UNPKG

@cordisjs/core

Version:

Meta-Framework for Modern JavaScript Applications

156 lines (131 loc) 4.7 kB
import { defineProperty, Dict } from 'cosmokit' import Lifecycle from './events.ts' import ReflectService from './reflect.ts' import Registry from './registry.ts' import { getTraceable, resolveConfig, symbols } from './utils.ts' export { Lifecycle, ReflectService, Registry } export namespace Context { export type Parameterized<C, T = any> = C & { config: T } /** @deprecated use `string[]` instead */ export interface MixinOptions { methods?: string[] accessors?: string[] prototype?: {} } export interface Item<C extends Context> { value?: any source: C } export type Internal = Internal.Service | Internal.Accessor | Internal.Alias export namespace Internal { export interface Service { type: 'service' builtin?: boolean prototype?: {} } export interface Accessor { type: 'accessor' get: (this: Context, receiver: any) => any set?: (this: Context, value: any, receiver: any) => boolean } export interface Alias { type: 'alias' name: string } } } // https://github.com/typescript-eslint/typescript-eslint/issues/6720 // eslint-disable-next-line @typescript-eslint/no-unused-vars export interface Intercept<C extends Context = Context> {} export interface Context { [Context.store]: Dict<Context.Item<this>, symbol> [Context.isolate]: Dict<symbol> [Context.intercept]: Intercept<this> [Context.internal]: Dict<Context.Internal> root: this lifecycle: Lifecycle reflect: ReflectService registry: Registry<this> config: any } export class Context { static readonly store: unique symbol = symbols.store as any static readonly events: unique symbol = symbols.events as any static readonly static: unique symbol = symbols.static as any static readonly filter: unique symbol = symbols.filter as any static readonly expose: unique symbol = symbols.expose as any static readonly isolate: unique symbol = symbols.isolate as any static readonly internal: unique symbol = symbols.internal as any static readonly intercept: unique symbol = symbols.intercept as any static readonly origin = 'ctx' static readonly current = 'ctx' static is<C extends Context>(value: any): value is C { return !!value?.[Context.is as any] } static { Context.is[Symbol.toPrimitive] = () => Symbol.for('cordis.is') Context.prototype[Context.is as any] = true } /** @deprecated use `Service.traceable` instead */ static associate<T extends {}>(object: T, name: string) { return object } constructor(config?: any) { config = resolveConfig(this.constructor, config) this[symbols.store] = Object.create(null) this[symbols.isolate] = Object.create(null) this[symbols.internal] = Object.create(null) this[symbols.intercept] = Object.create(null) const self: Context = new Proxy(this, ReflectService.handler) self.root = self self.reflect = new ReflectService(self) self.registry = new Registry(self, config) self.lifecycle = new Lifecycle(self) const attach = (internal: Context[typeof symbols.internal]) => { if (!internal) return attach(Object.getPrototypeOf(internal)) for (const key of Object.getOwnPropertyNames(internal)) { const constructor = internal[key]['prototype']?.constructor if (!constructor) continue self[internal[key]['key']] = new constructor(self, config) defineProperty(self[internal[key]['key']], 'ctx', self) } } attach(this[symbols.internal]) return self } [Symbol.for('nodejs.util.inspect.custom')]() { return `Context <${this.name}>` } get name() { let runtime = this.runtime while (runtime && !runtime.name) { runtime = runtime.parent.runtime } return runtime?.name! } get events() { return this.lifecycle } /** @deprecated */ get state() { return this.scope } extend(meta = {}): this { const source = Reflect.getOwnPropertyDescriptor(this, symbols.shadow)?.value const self = Object.assign(Object.create(getTraceable(this, this)), meta) if (!source) return self return Object.assign(Object.create(self), { [symbols.shadow]: source }) } isolate(name: string, label?: symbol) { const shadow = Object.create(this[symbols.isolate]) shadow[name] = label ?? Symbol(name) return this.extend({ [symbols.isolate]: shadow }) } intercept<K extends keyof Intercept>(name: K, config: Intercept[K]) { const intercept = Object.create(this[symbols.intercept]) intercept[name] = config return this.extend({ [symbols.intercept]: intercept }) } } Context.prototype[Context.internal] = Object.create(null)