Skip to content

Commit

Permalink
feat(cordis): initial support for internal tracing
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jul 31, 2024
1 parent 821d7b4 commit 871a1c2
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 31 deletions.
16 changes: 9 additions & 7 deletions packages/core/src/registry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineProperty, Dict } from 'cosmokit'
import { Awaitable, defineProperty, Dict } from 'cosmokit'
import { Context } from './context.ts'
import { ForkScope, MainScope } from './scope.ts'
import { resolveConfig, symbols } from './utils.ts'
Expand Down Expand Up @@ -51,15 +51,15 @@ export namespace Plugin {
}

export interface Function<C extends Context = Context, T = any> extends Base<T> {
(ctx: C, config: T): void
(ctx: C, config: T): Awaitable<void>
}

export interface Constructor<C extends Context = Context, T = any> extends Base<T> {
new (ctx: C, config: T): void
}

export interface Object<C extends Context = Context, T = any> extends Base<T> {
apply: (ctx: C, config: T) => void
apply: (ctx: C, config: T) => Awaitable<void>
}
}

Expand Down Expand Up @@ -91,7 +91,7 @@ class Registry<C extends Context = Context> {
})

this.context = ctx
const runtime = new MainScope(ctx, null!, config)
const runtime = new MainScope(ctx, null!, config, null, new Error().stack!.split('\n').slice(1))
ctx.scope = runtime
runtime.ctx = ctx
this.set(null!, runtime)
Expand Down Expand Up @@ -176,18 +176,20 @@ class Registry<C extends Context = Context> {
config = null
}

const stack = new Error().stack!.split('\n').slice(2)

// check duplication
let runtime = this.get(plugin)
if (runtime) {
if (!runtime.isForkable) {
this.context.emit(this.ctx, 'internal/warning', new Error(`duplicate plugin detected: ${plugin.name}`))
}
return runtime.fork(this.ctx, config, error)
return runtime.fork(this.ctx, config, error, stack)
}

runtime = new MainScope(this.ctx, plugin, config, error)
runtime = new MainScope(this.ctx, plugin, config, error, stack)
this.set(plugin, runtime)
return runtime.fork(this.ctx, config, error)
return runtime.fork(this.ctx, config, error, stack)
}
}

Expand Down
65 changes: 41 additions & 24 deletions packages/core/src/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export abstract class EffectScope<C extends Context = Context> {
abstract dispose(): boolean
abstract update(config: C['config'], forced?: boolean): void

constructor(public parent: C, public config: C['config']) {
constructor(public parent: C, public config: C['config'], public stack: string[]) {
this.uid = parent.registry ? parent.registry.counter : 0
this.ctx = this.context = parent.extend({ scope: this })
this.proxy = new Proxy({}, {
Expand Down Expand Up @@ -256,8 +256,8 @@ export abstract class EffectScope<C extends Context = Context> {
export class ForkScope<C extends Context = Context> extends EffectScope<C> {
dispose: () => boolean

constructor(parent: Context, public runtime: MainScope<C>, config: C['config'], error?: any) {
super(parent as C, config)
constructor(parent: Context, public runtime: MainScope<C>, config: C['config'], error: any, stack: string[]) {
super(parent as C, config, stack)

this.dispose = defineProperty(parent.scope.collect(`fork <${parent.runtime.name}>`, () => {
this.uid = null
Expand Down Expand Up @@ -300,8 +300,6 @@ export class ForkScope<C extends Context = Context> extends EffectScope<C> {
}

export class MainScope<C extends Context = Context> extends EffectScope<C> {
public value: any

runtime = this
schema: any
name?: string
Expand All @@ -311,8 +309,8 @@ export class MainScope<C extends Context = Context> extends EffectScope<C> {
isReusable?: boolean = false
isReactive?: boolean = false

constructor(ctx: C, public plugin: Plugin, config: any, error?: any) {
super(ctx, config)
constructor(ctx: C, public plugin: Plugin, config: any, error: any, stack: string[]) {
super(ctx, config, stack)
if (!plugin) {
this.name = 'root'
this.isActive = true
Expand All @@ -326,8 +324,8 @@ export class MainScope<C extends Context = Context> extends EffectScope<C> {
return this.forkables.length > 0
}

fork(parent: Context, config: any, error?: any) {
return new ForkScope(parent, this, config, error)
fork(parent: Context, config: any, error: any, stack: string[]) {
return new ForkScope(parent, this, config, error, stack)
}

dispose() {
Expand All @@ -351,22 +349,41 @@ export class MainScope<C extends Context = Context> extends EffectScope<C> {
}
}

private apply = (context: C, config: any) => {
if (typeof this.plugin !== 'function') {
return this.plugin.apply(context, config)
} else if (isConstructor(this.plugin)) {
// eslint-disable-next-line new-cap
const instance = new this.plugin(context, config)
const name = instance[Context.expose]
if (name) {
context.set(name, instance)
private apply = async (ctx: C, config: any) => {
const initialLine = new Error().stack!.split('\n')[2]
try {
if (typeof this.plugin !== 'function') {
return await this.plugin.apply(ctx, config)
} else if (isConstructor(this.plugin)) {
// eslint-disable-next-line new-cap
const instance = new this.plugin(ctx, config)
const name = instance[Context.expose]
if (name) {
ctx.set(name, instance)
}
if (instance['fork']) {
this.forkables.push(instance['fork'].bind(instance))
}
return instance
} else {
return await this.plugin(ctx, config)
}
if (instance['fork']) {
this.forkables.push(instance['fork'].bind(instance))
} catch (reason) {
// 'full' | 'short' | 'none' (default: 'short')
const { traceInternal } = ctx.root.config
if (reason instanceof Error && traceInternal !== 'full') {
const lines = reason.stack!.split('\n')
const index = lines.indexOf(initialLine)
if (index > 0) {
lines.splice(index - 1, Infinity)
if (traceInternal !== 'none') {
lines.push(' at MainScope.apply (cordis:internal)')
}
lines.push(...this.stack)
reason.stack = lines.join('\n')
}
}
return instance
} else {
return this.plugin(context, config)
throw reason
}
}

Expand All @@ -380,7 +397,7 @@ export class MainScope<C extends Context = Context> extends EffectScope<C> {
start() {
if (super.start()) return true
if (!this.isReusable && this.plugin) {
this.ensure(async () => this.value = this.apply(this.ctx, this._config))
this.ensure(async () => this.apply(this.ctx, this._config))
}
for (const fork of this.children) {
fork.start()
Expand Down

0 comments on commit 871a1c2

Please sign in to comment.