UNPKG

@cordisjs/core

Version:

Meta-Framework for Modern JavaScript Applications

1,224 lines (1,212 loc) 41 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/context.ts import { defineProperty as defineProperty6 } from "cosmokit"; // src/events.ts import { defineProperty as defineProperty3, remove } from "cosmokit"; // src/reflect.ts import { defineProperty as defineProperty2, isNullable } from "cosmokit"; // src/utils.ts import { defineProperty } from "cosmokit"; var symbols = { // internal symbols shadow: Symbol.for("cordis.shadow"), receiver: Symbol.for("cordis.receiver"), original: Symbol.for("cordis.original"), // context symbols store: Symbol.for("cordis.store"), events: Symbol.for("cordis.events"), static: Symbol.for("cordis.static"), filter: Symbol.for("cordis.filter"), expose: Symbol.for("cordis.expose"), isolate: Symbol.for("cordis.isolate"), internal: Symbol.for("cordis.internal"), intercept: Symbol.for("cordis.intercept"), // service symbols setup: Symbol.for("cordis.setup"), invoke: Symbol.for("cordis.invoke"), extend: Symbol.for("cordis.extend"), tracker: Symbol.for("cordis.tracker"), provide: Symbol.for("cordis.provide"), immediate: Symbol.for("cordis.immediate") }; var GeneratorFunction = function* () { }.constructor; var AsyncGeneratorFunction = async function* () { }.constructor; function isConstructor(func) { if (!func.prototype) return false; if (func instanceof GeneratorFunction) return false; if (AsyncGeneratorFunction !== Function && func instanceof AsyncGeneratorFunction) return false; return true; } __name(isConstructor, "isConstructor"); function resolveConfig(plugin, config) { const schema = plugin["Config"] || plugin["schema"]; if (schema && plugin["schema"] !== false) config = schema(config); return config ?? {}; } __name(resolveConfig, "resolveConfig"); function isUnproxyable(value) { return [Map, Set, Date, Promise].some((constructor) => value instanceof constructor); } __name(isUnproxyable, "isUnproxyable"); function joinPrototype(proto1, proto2) { if (proto1 === Object.prototype) return proto2; const result = Object.create(joinPrototype(Object.getPrototypeOf(proto1), proto2)); for (const key of Reflect.ownKeys(proto1)) { Object.defineProperty(result, key, Object.getOwnPropertyDescriptor(proto1, key)); } return result; } __name(joinPrototype, "joinPrototype"); function isObject(value) { return value && (typeof value === "object" || typeof value === "function"); } __name(isObject, "isObject"); function getTraceable(ctx, value, noTrap) { if (!isObject(value)) return value; if (Object.hasOwn(value, symbols.shadow)) { return Object.getPrototypeOf(value); } const tracker = value[symbols.tracker]; if (!tracker) return value; return createTraceable(ctx, value, tracker, noTrap); } __name(getTraceable, "getTraceable"); function withProps(target, props) { if (!props) return target; return new Proxy(target, { get: /* @__PURE__ */ __name((target2, prop, receiver) => { if (prop in props && prop !== "constructor") return Reflect.get(props, prop, receiver); return Reflect.get(target2, prop, receiver); }, "get"), set: /* @__PURE__ */ __name((target2, prop, value, receiver) => { if (prop in props && prop !== "constructor") return Reflect.set(props, prop, value, receiver); return Reflect.set(target2, prop, value, receiver); }, "set") }); } __name(withProps, "withProps"); function withProp(target, prop, value) { return withProps(target, Object.defineProperty(/* @__PURE__ */ Object.create(null), prop, { value, writable: false })); } __name(withProp, "withProp"); function createShadow(ctx, target, property, receiver) { if (!property) return receiver; const origin = Reflect.getOwnPropertyDescriptor(target, property)?.value; if (!origin) return receiver; return withProp(receiver, property, ctx.extend({ [symbols.shadow]: origin })); } __name(createShadow, "createShadow"); function createShadowMethod(ctx, value, outer, shadow) { return new Proxy(value, { apply: /* @__PURE__ */ __name((target, thisArg, args) => { if (thisArg === outer) thisArg = shadow; args = args.map((arg) => { if (typeof arg !== "function" || arg[symbols.original]) return arg; return new Proxy(arg, { get: /* @__PURE__ */ __name((target2, prop, receiver) => { if (prop === symbols.original) return target2; const value2 = Reflect.get(target2, prop, receiver); if (prop === "toString" && value2 === Function.prototype.toString) { return function(...args2) { return Reflect.apply(value2, this === receiver ? target2 : this, args2); }; } return value2; }, "get"), apply: /* @__PURE__ */ __name((target2, thisArg2, args2) => { return Reflect.apply(target2, getTraceable(ctx, thisArg2), args2.map((arg2) => getTraceable(ctx, arg2))); }, "apply"), construct: /* @__PURE__ */ __name((target2, args2, newTarget) => { return Reflect.construct(target2, args2.map((arg2) => getTraceable(ctx, arg2)), newTarget); }, "construct") }); }); return getTraceable(ctx, Reflect.apply(target, thisArg, args)); }, "apply") }); } __name(createShadowMethod, "createShadowMethod"); function createTraceable(ctx, value, tracker, noTrap) { if (ctx[symbols.shadow]) { ctx = Object.getPrototypeOf(ctx); } const proxy = new Proxy(value, { get: /* @__PURE__ */ __name((target, prop, receiver) => { if (prop === symbols.original) return target; if (prop === tracker.property) return ctx; if (typeof prop === "symbol") { return Reflect.get(target, prop, receiver); } if (tracker.associate && ctx[symbols.internal][`${tracker.associate}.${prop}`]) { return Reflect.get(ctx, `${tracker.associate}.${prop}`, withProp(ctx, symbols.receiver, receiver)); } const shadow = createShadow(ctx, target, tracker.property, receiver); const innerValue = Reflect.get(target, prop, shadow); const innerTracker = innerValue?.[symbols.tracker]; if (innerTracker) { return createTraceable(ctx, innerValue, innerTracker); } else if (!noTrap && typeof innerValue === "function") { return createShadowMethod(ctx, innerValue, receiver, shadow); } else { return innerValue; } }, "get"), set: /* @__PURE__ */ __name((target, prop, value2, receiver) => { if (prop === symbols.original) return false; if (prop === tracker.property) return false; if (typeof prop === "symbol") { return Reflect.set(target, prop, value2, receiver); } if (tracker.associate && ctx[symbols.internal][`${tracker.associate}.${prop}`]) { return Reflect.set(ctx, `${tracker.associate}.${prop}`, value2, withProp(ctx, symbols.receiver, receiver)); } const shadow = createShadow(ctx, target, tracker.property, receiver); return Reflect.set(target, prop, value2, shadow); }, "set"), apply: /* @__PURE__ */ __name((target, thisArg, args) => { return applyTraceable(proxy, target, thisArg, args); }, "apply") }); return proxy; } __name(createTraceable, "createTraceable"); function applyTraceable(proxy, value, thisArg, args) { if (!value[symbols.invoke]) return Reflect.apply(value, thisArg, args); return value[symbols.invoke].apply(proxy, args); } __name(applyTraceable, "applyTraceable"); function createCallable(name, proto, tracker) { const self = /* @__PURE__ */ __name(function(...args) { const proxy = createTraceable(self["ctx"], self, tracker); return applyTraceable(proxy, self, this, args); }, "self"); defineProperty(self, "name", name); return Object.setPrototypeOf(self, proto); } __name(createCallable, "createCallable"); // src/reflect.ts var ReflectService = class _ReflectService { constructor(ctx) { this.ctx = ctx; defineProperty2(this, symbols.tracker, { associate: "reflect", property: "ctx" }); this._mixin("reflect", ["get", "set", "provide", "accessor", "mixin", "alias"]); this._mixin("scope", ["config", "runtime", "effect", "collect", "accept", "decline"]); this._mixin("registry", ["using", "inject", "plugin"]); this._mixin("lifecycle", ["on", "once", "parallel", "emit", "serial", "bail", "start", "stop"]); } static { __name(this, "ReflectService"); } static resolveInject(ctx, name) { let internal = ctx[symbols.internal][name]; while (internal?.type === "alias") { name = internal.name; internal = ctx[symbols.internal][name]; } return [name, internal]; } static checkInject(ctx, name, error) { ctx = ctx[symbols.shadow] ?? ctx; if (["prototype", "then", "registry", "lifecycle"].includes(name)) return; if (name[0] === "$" || name[0] === "_") return; if (!ctx.runtime.plugin) return; if (ctx.bail(ctx, "internal/inject", name)) return; const lines = error.stack.split("\n"); lines.splice(1, 1); error.stack = lines.join("\n"); ctx.emit(ctx, "internal/warning", error); } static handler = { get: /* @__PURE__ */ __name((target, prop, ctx) => { if (typeof prop !== "string") return Reflect.get(target, prop, ctx); if (Reflect.has(target, prop)) { return getTraceable(ctx, Reflect.get(target, prop, ctx), true); } const [name, internal] = _ReflectService.resolveInject(target, prop); const error = new Error(`property ${name} is not registered, declare it as \`inject\` to suppress this warning`); if (!internal) { _ReflectService.checkInject(ctx, name, error); return Reflect.get(target, name, ctx); } else if (internal.type === "accessor") { return internal.get.call(ctx, ctx[symbols.receiver]); } else { if (!internal.builtin) _ReflectService.checkInject(ctx, name, error); return ctx.reflect.get(name); } }, "get"), set: /* @__PURE__ */ __name((target, prop, value, ctx) => { if (typeof prop !== "string") return Reflect.set(target, prop, value, ctx); const [name, internal] = _ReflectService.resolveInject(target, prop); if (!internal) { return Reflect.set(target, name, value, ctx); } if (internal.type === "accessor") { if (!internal.set) return false; return internal.set.call(ctx, value, ctx[symbols.receiver]); } else { ctx.reflect.set(name, value); return true; } }, "set"), has: /* @__PURE__ */ __name((target, prop) => { if (typeof prop !== "string") return Reflect.has(target, prop); if (Reflect.has(target, prop)) return true; const [, internal] = _ReflectService.resolveInject(target, prop); return !!internal; }, "has") }; get(name) { const internal = this.ctx[symbols.internal][name]; if (internal?.type !== "service") return; const key = this.ctx[symbols.isolate][name]; const value = this.ctx[symbols.store][key]?.value; return getTraceable(this.ctx, value); } set(name, value) { this.provide(name); const key = this.ctx[symbols.isolate][name]; const oldValue = this.ctx[symbols.store][key]?.value; value ??= void 0; let dispose = /* @__PURE__ */ __name(() => { }, "dispose"); if (oldValue === value) return dispose; if (!isNullable(value) && !isNullable(oldValue)) { throw new Error(`service ${name} has been registered`); } const ctx = this.ctx; if (!isNullable(value)) { dispose = ctx.effect(() => () => { ctx.set(name, void 0); }); } if (isUnproxyable(value)) { ctx.emit(ctx, "internal/warning", new Error(`service ${name} is an unproxyable object, which may lead to unexpected behavior`)); } const self = Object.create(ctx); self[symbols.filter] = (ctx2) => { return ctx[symbols.isolate][name] === ctx2[symbols.isolate][name]; }; ctx.emit(self, "internal/before-service", name, value); ctx[symbols.store][key] = { value, source: ctx }; ctx.emit(self, "internal/service", name, oldValue); return dispose; } provide(name, value, builtin) { const internal = this.ctx.root[symbols.internal]; if (name in internal) return; const key = Symbol(name); internal[name] = { type: "service", builtin }; this.ctx.root[symbols.isolate][name] = key; if (!isObject(value)) return; this.ctx[symbols.store][key] = { value, source: null }; defineProperty2(value, symbols.tracker, { associate: name, property: "ctx" }); } _accessor(name, options) { const internal = this.ctx.root[symbols.internal]; if (name in internal) return () => { }; internal[name] = { type: "accessor", ...options }; return () => delete this.ctx.root[symbols.isolate][name]; } accessor(name, options) { this.ctx.scope.effect(() => { return this._accessor(name, options); }); } alias(name, aliases) { const internal = this.ctx.root[symbols.internal]; if (name in internal) return; for (const key of aliases) { internal[key] ||= { type: "alias", name }; } } _mixin(source, mixins) { const entries = Array.isArray(mixins) ? mixins.map((key) => [key, key]) : Object.entries(mixins); const getTarget = typeof source === "string" ? (ctx) => ctx[source] : () => source; const disposables = entries.map(([key, value]) => { return this._accessor(value, { get(receiver) { const service = getTarget(this); if (isNullable(service)) return service; const mixin = receiver ? withProps(receiver, service) : service; const value2 = Reflect.get(service, key, mixin); if (typeof value2 !== "function") return value2; return value2.bind(mixin ?? service); }, set(value2, receiver) { const service = getTarget(this); const mixin = receiver ? withProps(receiver, service) : service; return Reflect.set(service, key, value2, mixin); } }); }); return () => disposables.forEach((dispose) => dispose()); } mixin(source, mixins) { this.ctx.scope.effect(() => { return this._mixin(source, mixins); }); } trace(value) { return getTraceable(this.ctx, value); } bind(callback) { return new Proxy(callback, { apply: /* @__PURE__ */ __name((target, thisArg, args) => { return target.apply(this.trace(thisArg), args.map((arg) => this.trace(arg))); }, "apply") }); } }; var reflect_default = ReflectService; // src/events.ts function isBailed(value) { return value !== null && value !== false && value !== void 0; } __name(isBailed, "isBailed"); var Lifecycle = class { constructor(ctx) { this.ctx = ctx; defineProperty3(this, symbols.tracker, { associate: "lifecycle", property: "ctx" }); defineProperty3(this.on("internal/listener", function(name, listener, options) { const method = options.prepend ? "unshift" : "push"; if (name === "ready") { if (!this.lifecycle.isActive) return; this.scope.ensure(async () => listener()); return () => false; } else if (name === "dispose") { this.scope.disposables[method](listener); defineProperty3(listener, "name", "event <dispose>"); return () => remove(this.scope.disposables, listener); } else if (name === "fork") { this.scope.runtime.forkables[method](listener); return this.scope.collect("event <fork>", () => remove(this.scope.runtime.forkables, listener)); } }), Context.static, ctx.scope); for (const level of ["info", "error", "warning"]) { defineProperty3(this.on(`internal/${level}`, (format, ...param) => { if (this._hooks[`internal/${level}`].length > 1) return; console.info(format, ...param); }), Context.static, ctx.scope); } defineProperty3(this.on("internal/before-service", function(name) { for (const runtime of this.registry.values()) { if (!runtime.inject[name]?.required) continue; const scopes = runtime.isReusable ? runtime.children : [runtime]; for (const scope of scopes) { if (!this[symbols.filter](scope.ctx)) continue; scope.updateStatus(); scope.reset(); } } }, { global: true }), Context.static, ctx.scope); defineProperty3(this.on("internal/service", function(name) { for (const runtime of this.registry.values()) { if (!runtime.inject[name]?.required) continue; const scopes = runtime.isReusable ? runtime.children : [runtime]; for (const scope of scopes) { if (!this[symbols.filter](scope.ctx)) continue; scope.start(); } } }, { global: true }), Context.static, ctx.scope); const checkInject = /* @__PURE__ */ __name((scope, name) => { if (!scope.runtime.plugin) return false; for (const key in scope.runtime.inject) { if (name === reflect_default.resolveInject(scope.ctx, key)[0]) return true; } return checkInject(scope.parent.scope, name); }, "checkInject"); defineProperty3(this.on("internal/inject", function(name) { return checkInject(this.scope, name); }, { global: true }), Context.static, ctx.scope); } static { __name(this, "Lifecycle"); } isActive = false; _tasks = /* @__PURE__ */ new Set(); _hooks = {}; async flush() { while (this._tasks.size) { await Promise.all(Array.from(this._tasks)); } } filterHooks(hooks, thisArg) { thisArg = getTraceable(this.ctx, thisArg); return hooks.slice().filter((hook) => { const filter = thisArg?.[Context.filter]; return hook.global || !filter || filter.call(thisArg, hook.ctx); }); } *dispatch(type, args) { const thisArg = typeof args[0] === "object" || typeof args[0] === "function" ? args.shift() : null; const name = args.shift(); if (name !== "internal/event") { this.emit("internal/event", type, name, args, thisArg); } for (const hook of this.filterHooks(this._hooks[name] || [], thisArg)) { yield hook.callback.apply(thisArg, args); } } async parallel(...args) { await Promise.all(this.dispatch("emit", args)); } emit(...args) { Array.from(this.dispatch("emit", args)); } async serial(...args) { for await (const result of this.dispatch("serial", args)) { if (isBailed(result)) return result; } } bail(...args) { for (const result of this.dispatch("bail", args)) { if (isBailed(result)) return result; } } register(label, hooks, callback, options) { const method = options.prepend ? "unshift" : "push"; hooks[method]({ ctx: this.ctx, callback, ...options }); return this.ctx.state.collect(label, () => this.unregister(hooks, callback)); } unregister(hooks, callback) { const index = hooks.findIndex((hook) => hook.callback === callback); if (index >= 0) { hooks.splice(index, 1); return true; } } on(name, listener, options) { if (typeof options !== "object") { options = { prepend: options }; } this.ctx.scope.assertActive(); listener = this.ctx.reflect.bind(listener); const result = this.bail(this.ctx, "internal/listener", name, listener, options); if (result) return result; const hooks = this._hooks[name] ||= []; const label = typeof name === "string" ? `event <${name}>` : "event (Symbol)"; return this.register(label, hooks, listener, options); } once(name, listener, options) { const dispose = this.on(name, function(...args) { dispose(); return listener.apply(this, args); }, options); return dispose; } async start() { this.isActive = true; const hooks = this._hooks.ready || []; while (hooks.length) { const { ctx, callback } = hooks.shift(); ctx.scope.ensure(async () => callback()); } await this.flush(); } async stop() { this.isActive = false; this.ctx.scope.reset(); } }; var events_default = Lifecycle; // src/registry.ts import { defineProperty as defineProperty5 } from "cosmokit"; // src/scope.ts import { deepEqual, defineProperty as defineProperty4, isNullable as isNullable2, remove as remove2 } from "cosmokit"; var ScopeStatus = /* @__PURE__ */ ((ScopeStatus2) => { ScopeStatus2[ScopeStatus2["PENDING"] = 0] = "PENDING"; ScopeStatus2[ScopeStatus2["LOADING"] = 1] = "LOADING"; ScopeStatus2[ScopeStatus2["ACTIVE"] = 2] = "ACTIVE"; ScopeStatus2[ScopeStatus2["FAILED"] = 3] = "FAILED"; ScopeStatus2[ScopeStatus2["DISPOSED"] = 4] = "DISPOSED"; return ScopeStatus2; })(ScopeStatus || {}); var CordisError = class _CordisError extends Error { constructor(code, message) { super(message ?? _CordisError.Code[code]); this.code = code; } static { __name(this, "CordisError"); } }; ((CordisError2) => { CordisError2.Code = { INACTIVE_EFFECT: "cannot create effect on inactive context" }; })(CordisError || (CordisError = {})); var EffectScope = class { constructor(parent, config) { this.parent = parent; this.config = config; this.uid = parent.registry ? parent.registry.counter : 0; this.ctx = this.context = parent.extend({ scope: this }); this.proxy = new Proxy({}, { get: /* @__PURE__ */ __name((target, key) => Reflect.get(this.config, key), "get") }); } static { __name(this, "EffectScope"); } uid; ctx; disposables = []; error; status = 0 /* PENDING */; isActive = false; // Same as `this.ctx`, but with a more specific type. context; proxy; acceptors = []; tasks = /* @__PURE__ */ new Set(); hasError = false; get _config() { return this.runtime.isReactive ? this.proxy : this.config; } assertActive() { if (this.uid !== null || this.isActive) return; throw new CordisError("INACTIVE_EFFECT"); } effect(callback, config) { this.assertActive(); const result = isConstructor(callback) ? new callback(this.ctx, config) : callback(this.ctx, config); let disposed = false; const original = typeof result === "function" ? result : result.dispose.bind(result); const wrapped = /* @__PURE__ */ __name((...args) => { if (disposed) return; disposed = true; remove2(this.disposables, wrapped); return original(...args); }, "wrapped"); this.disposables.push(wrapped); if (typeof result === "function") return wrapped; result.dispose = wrapped; return result; } collect(label, callback) { const dispose = defineProperty4(() => { remove2(this.disposables, dispose); return callback(); }, "name", label); this.disposables.push(dispose); return dispose; } restart() { this.reset(); this.error = null; this.hasError = false; this.status = 0 /* PENDING */; this.start(); } _getStatus() { if (this.uid === null) return 4 /* DISPOSED */; if (this.hasError) return 3 /* FAILED */; if (this.tasks.size) return 1 /* LOADING */; if (this.ready) return 2 /* ACTIVE */; return 0 /* PENDING */; } updateStatus(callback) { const oldValue = this.status; callback?.(); this.status = this._getStatus(); if (oldValue !== this.status) { this.context.emit("internal/status", this, oldValue); } } ensure(callback) { const task = callback().catch((reason) => { this.context.emit(this.ctx, "internal/error", reason); this.cancel(reason); }).finally(() => { this.updateStatus(() => this.tasks.delete(task)); this.context.events._tasks.delete(task); }); this.updateStatus(() => this.tasks.add(task)); this.context.events._tasks.add(task); } cancel(reason) { this.error = reason; this.updateStatus(() => this.hasError = true); this.reset(); } get ready() { return Object.entries(this.runtime.inject).every(([name, inject]) => { return !inject.required || !isNullable2(this.ctx.get(name)); }); } reset() { this.isActive = false; this.disposables = this.disposables.splice(0).filter((dispose) => { if (this.uid !== null && dispose[Context.static] === this) return true; (async () => dispose())().catch((reason) => { this.context.emit(this.ctx, "internal/error", reason); }); }); } init(error) { if (!this.config) { this.cancel(error); } else { this.start(); } } start() { if (!this.ready || this.isActive || this.uid === null) return true; this.isActive = true; this.updateStatus(() => this.hasError = false); } accept(...args) { const keys = Array.isArray(args[0]) ? args.shift() : null; const acceptor = { keys, callback: args[0], ...args[1] }; return this.effect(() => { this.acceptors.push(acceptor); if (acceptor.immediate) acceptor.callback?.(this.config); return () => remove2(this.acceptors, acceptor); }); } decline(keys) { return this.accept(keys, () => true); } checkUpdate(resolved, forced) { if (forced || !this.config) return [true, true]; if (forced === false) return [false, false]; const modified = /* @__PURE__ */ Object.create(null); const checkPropertyUpdate = /* @__PURE__ */ __name((key) => { const result = modified[key] ??= !deepEqual(this.config[key], resolved[key]); hasUpdate ||= result; return result; }, "checkPropertyUpdate"); const ignored = /* @__PURE__ */ new Set(); let hasUpdate = false, shouldRestart = false; let fallback = this.runtime.isReactive || null; for (const { keys, callback, passive } of this.acceptors) { if (!keys) { fallback ||= !passive; } else if (passive) { keys?.forEach((key) => ignored.add(key)); } else { let hasUpdate2 = false; for (const key of keys) { hasUpdate2 ||= checkPropertyUpdate(key); } if (!hasUpdate2) continue; } const result = callback?.(resolved); if (result) shouldRestart = true; } for (const key in { ...this.config, ...resolved }) { if (fallback === false) continue; if (!(key in modified) && !ignored.has(key)) { const hasUpdate2 = checkPropertyUpdate(key); if (fallback === null) shouldRestart ||= hasUpdate2; } } return [hasUpdate, shouldRestart]; } }; var ForkScope = class extends EffectScope { constructor(parent, runtime, config, error) { super(parent, config); this.runtime = runtime; this.dispose = defineProperty4(parent.scope.collect(`fork <${parent.runtime.name}>`, () => { this.uid = null; this.reset(); this.context.emit("internal/fork", this); const result = remove2(runtime.disposables, this.dispose); if (remove2(runtime.children, this) && !runtime.children.length) { parent.registry.delete(runtime.plugin); } return result; }), Context.static, runtime); runtime.children.push(this); runtime.disposables.push(this.dispose); this.context.emit("internal/fork", this); this.init(error); } static { __name(this, "ForkScope"); } dispose; start() { if (super.start()) return true; for (const fork of this.runtime.forkables) { this.ensure(async () => fork(this.context, this._config)); } } update(config, forced) { const oldConfig = this.config; const state = this.runtime.isForkable ? this : this.runtime; if (state.config !== oldConfig) return; let resolved; try { resolved = resolveConfig(this.runtime.plugin, config); } catch (error) { this.context.emit("internal/error", error); return this.cancel(error); } const [hasUpdate, shouldRestart] = state.checkUpdate(resolved, forced); this.context.emit("internal/before-update", this, config); this.config = resolved; state.config = resolved; if (hasUpdate) { this.context.emit("internal/update", this, oldConfig); } if (shouldRestart) state.restart(); } }; var MainScope = class extends EffectScope { constructor(ctx, plugin, config, error) { super(ctx, config); this.plugin = plugin; if (!plugin) { this.name = "root"; this.isActive = true; } else { this.setup(); this.init(error); } } static { __name(this, "MainScope"); } value; runtime = this; schema; name; inject = /* @__PURE__ */ Object.create(null); forkables = []; children = []; isReusable = false; isReactive = false; get isForkable() { return this.forkables.length > 0; } fork(parent, config, error) { return new ForkScope(parent, this, config, error); } dispose() { this.uid = null; this.reset(); this.context.emit("internal/runtime", this); return true; } setup() { const { name } = this.plugin; if (name && name !== "apply") this.name = name; this.schema = this.plugin["Config"] || this.plugin["schema"]; this.inject = Inject.resolve(this.plugin["using"] || this.plugin["inject"]); this.isReusable = this.plugin["reusable"]; this.isReactive = this.plugin["reactive"]; this.context.emit("internal/runtime", this); if (this.isReusable) { this.forkables.push(this.apply); } } apply = /* @__PURE__ */ __name((context, config) => { if (typeof this.plugin !== "function") { return this.plugin.apply(context, config); } else if (isConstructor(this.plugin)) { const instance = new this.plugin(context, config); const name = instance[Context.expose]; if (name) { context.set(name, instance); } if (instance["fork"]) { this.forkables.push(instance["fork"].bind(instance)); } return instance; } else { return this.plugin(context, config); } }, "apply"); reset() { super.reset(); for (const fork of this.children) { fork.reset(); } } start() { if (super.start()) return true; if (!this.isReusable && this.plugin) { this.ensure(async () => this.value = this.apply(this.ctx, this._config)); } for (const fork of this.children) { fork.start(); } } update(config, forced) { if (this.isForkable) { const warning = new Error(`attempting to update forkable plugin "${this.plugin.name}", which may lead to unexpected behavior`); this.context.emit(this.ctx, "internal/warning", warning); } const oldConfig = this.config; let resolved; try { resolved = resolveConfig(this.runtime.plugin || this.context.constructor, config); } catch (error) { this.context.emit("internal/error", error); return this.cancel(error); } const [hasUpdate, shouldRestart] = this.checkUpdate(resolved, forced); const state = this.children.find((fork) => fork.config === oldConfig); this.config = resolved; if (state) { this.context.emit("internal/before-update", state, config); state.config = resolved; if (hasUpdate) { this.context.emit("internal/update", state, oldConfig); } } if (shouldRestart) this.restart(); } }; // src/registry.ts function isApplicable(object) { return object && typeof object === "object" && typeof object.apply === "function"; } __name(isApplicable, "isApplicable"); function Inject(inject) { return function(value, ctx) { if (ctx.kind === "class") { value.inject = inject; } else if (ctx.kind === "method") { ctx.addInitializer(function() { const property = this[symbols.tracker]?.property; if (!property) throw new Error("missing context tracker"); this[property].inject(inject, (ctx2) => { value.call(withProps(this, { [property]: ctx2 })); }); }); } else { throw new Error("@Inject can only be used on class or class methods"); } }; } __name(Inject, "Inject"); ((Inject2) => { function resolve(inject) { if (!inject) return {}; if (Array.isArray(inject)) { return Object.fromEntries(inject.map((name) => [name, { required: true }])); } const { required, optional, ...rest } = inject; if (Array.isArray(required)) { Object.assign(rest, Object.fromEntries(required.map((name) => [name, { required: true }]))); } if (Array.isArray(optional)) { Object.assign(rest, Object.fromEntries(optional.map((name) => [name, { required: false }]))); } return rest; } Inject2.resolve = resolve; __name(resolve, "resolve"); })(Inject || (Inject = {})); var Registry = class { constructor(ctx, config) { this.ctx = ctx; defineProperty5(this, symbols.tracker, { associate: "registry", property: "ctx" }); this.context = ctx; const runtime = new MainScope(ctx, null, config); ctx.scope = runtime; runtime.ctx = ctx; this.set(null, runtime); } static { __name(this, "Registry"); } _counter = 0; _internal = /* @__PURE__ */ new Map(); context; get counter() { return ++this._counter; } get size() { return this._internal.size; } resolve(plugin, assert = false) { if (plugin === null) return plugin; if (typeof plugin === "function") return plugin; if (isApplicable(plugin)) return plugin.apply; if (assert) throw new Error('invalid plugin, expect function or object with an "apply" method, received ' + typeof plugin); } get(plugin) { const key = this.resolve(plugin); return key && this._internal.get(key); } has(plugin) { const key = this.resolve(plugin); return !!key && this._internal.has(key); } set(plugin, state) { const key = this.resolve(plugin); this._internal.set(key, state); } delete(plugin) { const key = this.resolve(plugin); const runtime = key && this._internal.get(key); if (!runtime) return; this._internal.delete(key); runtime.dispose(); return runtime; } keys() { return this._internal.keys(); } values() { return this._internal.values(); } entries() { return this._internal.entries(); } forEach(callback) { return this._internal.forEach(callback); } using(inject, callback) { return this.inject(inject, callback); } inject(inject, callback) { return this.plugin({ inject, apply: callback, name: callback.name }); } plugin(plugin, config, error) { this.resolve(plugin, true); this.ctx.scope.assertActive(); if (!error) { try { config = resolveConfig(plugin, config); } catch (reason) { this.context.emit(this.ctx, "internal/error", reason); error = reason; config = null; } } 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); } runtime = new MainScope(this.ctx, plugin, config, error); this.set(plugin, runtime); return runtime.fork(this.ctx, config, error); } }; var registry_default = Registry; // src/context.ts var Context = class _Context { static { __name(this, "Context"); } static store = symbols.store; static events = symbols.events; static static = symbols.static; static filter = symbols.filter; static expose = symbols.expose; static isolate = symbols.isolate; static internal = symbols.internal; static intercept = symbols.intercept; static origin = "ctx"; static current = "ctx"; static is(value) { return !!value?.[_Context.is]; } static { _Context.is[Symbol.toPrimitive] = () => Symbol.for("cordis.is"); _Context.prototype[_Context.is] = true; } /** @deprecated use `Service.traceable` instead */ static associate(object, name) { return object; } constructor(config) { config = resolveConfig(this.constructor, config); this[symbols.store] = /* @__PURE__ */ Object.create(null); this[symbols.isolate] = /* @__PURE__ */ Object.create(null); this[symbols.internal] = /* @__PURE__ */ Object.create(null); this[symbols.intercept] = /* @__PURE__ */ Object.create(null); const self = new Proxy(this, reflect_default.handler); self.root = self; self.reflect = new reflect_default(self); self.registry = new registry_default(self, config); self.lifecycle = new events_default(self); const attach = /* @__PURE__ */ __name((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); defineProperty6(self[internal[key]["key"]], "ctx", self); } }, "attach"); 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 = {}) { 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, label) { const shadow = Object.create(this[symbols.isolate]); shadow[name] = label ?? Symbol(name); return this.extend({ [symbols.isolate]: shadow }); } intercept(name, config) { const intercept = Object.create(this[symbols.intercept]); intercept[name] = config; return this.extend({ [symbols.intercept]: intercept }); } }; Context.prototype[Context.internal] = /* @__PURE__ */ Object.create(null); // src/service.ts import { defineProperty as defineProperty7 } from "cosmokit"; var Service = class _Service { static { __name(this, "Service"); } static setup = symbols.setup; static invoke = symbols.invoke; static extend = symbols.extend; static tracker = symbols.tracker; static provide = symbols.provide; static immediate = symbols.immediate; start() { } stop() { } ctx; name; config; constructor(...args) { let _ctx, name, immediate, config; if (Context.is(args[0])) { _ctx = args[0]; if (typeof args[1] === "string") { name = args[1]; immediate = args[2]; } else { config = args[1]; } } else { config = args[0]; } name ??= this.constructor[symbols.provide]; immediate ??= this.constructor[symbols.immediate]; let self = this; const tracker = { associate: name, property: "ctx" }; if (self[symbols.invoke]) { self = createCallable(name, joinPrototype(Object.getPrototypeOf(this), Function.prototype), tracker); } if (_ctx) { self.ctx = _ctx; } else { self[symbols.setup](); } self.name = name; self.config = config; defineProperty7(self, symbols.tracker, tracker); self.ctx.provide(name); self.ctx.runtime.name = name; if (immediate) { if (_ctx) self[symbols.expose] = name; else self.ctx.set(name, self); } self.ctx.on("ready", async () => { await Promise.resolve(); await self.start(); if (!immediate) self.ctx.set(name, self); }); self.ctx.on("dispose", () => self.stop()); return self; } [symbols.filter](ctx) { return ctx[symbols.isolate][this.name] === this.ctx[symbols.isolate][this.name]; } [symbols.setup]() { this.ctx = new Context(); } [symbols.extend](props) { let self; if (this[_Service.invoke]) { self = createCallable(this.name, this, this[symbols.tracker]); } else { self = Object.create(this); } return Object.assign(self, props); } static [Symbol.hasInstance](instance) { let constructor = instance.constructor; while (constructor) { constructor = constructor.prototype?.constructor; if (constructor === this) return true; constructor = Object.getPrototypeOf(constructor); } return false; } }; export { Context, CordisError, EffectScope, ForkScope, Inject, events_default as Lifecycle, MainScope, reflect_default as ReflectService, registry_default as Registry, ScopeStatus, Service, createCallable, getTraceable, isBailed, isConstructor, isObject, isUnproxyable, joinPrototype, resolveConfig, symbols, withProps }; //# sourceMappingURL=index.mjs.map