| 1 | // generated by crates/tui/extension-host/build.mjs from src/shims/skills.ts; do not edit. |
| 2 | |
| 3 | // node_modules/@deepseek-ai/cosmokit/lib/index.js |
| 4 | function isNullable(value) { |
| 5 | return value === null || value === void 0; |
| 6 | } |
| 7 | function defineProperty(object, key, value) { |
| 8 | return Object.defineProperty(object, key, { |
| 9 | writable: true, |
| 10 | value, |
| 11 | enumerable: false |
| 12 | }); |
| 13 | } |
| 14 | function is(type, value) { |
| 15 | if (arguments.length === 1) return (value2) => is(type, value2); |
| 16 | return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type; |
| 17 | } |
| 18 | function isArrayBufferLike(value) { |
| 19 | return is("ArrayBuffer", value) || is("SharedArrayBuffer", value); |
| 20 | } |
| 21 | function isArrayBufferSource(value) { |
| 22 | return isArrayBufferLike(value) || ArrayBuffer.isView(value); |
| 23 | } |
| 24 | var Binary; |
| 25 | (function(Binary2) { |
| 26 | Binary2.is = isArrayBufferLike; |
| 27 | Binary2.isSource = isArrayBufferSource; |
| 28 | function fromSource(source) { |
| 29 | if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength); |
| 30 | else return source; |
| 31 | } |
| 32 | Binary2.fromSource = fromSource; |
| 33 | function toBase64(source) { |
| 34 | source = fromSource(source); |
| 35 | if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64"); |
| 36 | let binary = ""; |
| 37 | const bytes = new Uint8Array(source); |
| 38 | for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]); |
| 39 | return btoa(binary); |
| 40 | } |
| 41 | Binary2.toBase64 = toBase64; |
| 42 | function fromBase64(source) { |
| 43 | if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64")); |
| 44 | return Uint8Array.from(atob(source), (c) => c.charCodeAt(0)); |
| 45 | } |
| 46 | Binary2.fromBase64 = fromBase64; |
| 47 | function toHex(source) { |
| 48 | source = fromSource(source); |
| 49 | if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex"); |
| 50 | return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join(""); |
| 51 | } |
| 52 | Binary2.toHex = toHex; |
| 53 | function fromHex(source) { |
| 54 | if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex")); |
| 55 | const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1); |
| 56 | const buffer = []; |
| 57 | for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16)); |
| 58 | return Uint8Array.from(buffer).buffer; |
| 59 | } |
| 60 | Binary2.fromHex = fromHex; |
| 61 | })(Binary || (Binary = {})); |
| 62 | var base64ToArrayBuffer = Binary.fromBase64; |
| 63 | var arrayBufferToBase64 = Binary.toBase64; |
| 64 | var hexToArrayBuffer = Binary.fromHex; |
| 65 | var arrayBufferToHex = Binary.toHex; |
| 66 | function tokenize(source, delimiters, delimiter) { |
| 67 | const output = []; |
| 68 | let state = 0; |
| 69 | for (let i = 0; i < source.length; i++) { |
| 70 | const code = source.charCodeAt(i); |
| 71 | if (code >= 65 && code <= 90) { |
| 72 | if (state === 1) { |
| 73 | const next = source.charCodeAt(i + 1); |
| 74 | if (next >= 97 && next <= 122) output.push(delimiter); |
| 75 | output.push(code + 32); |
| 76 | } else { |
| 77 | if (state !== 0) output.push(delimiter); |
| 78 | output.push(code + 32); |
| 79 | } |
| 80 | state = 1; |
| 81 | } else if (code >= 97 && code <= 122) { |
| 82 | output.push(code); |
| 83 | state = 2; |
| 84 | } else if (delimiters.includes(code)) { |
| 85 | if (state !== 0) output.push(delimiter); |
| 86 | state = 0; |
| 87 | } else output.push(code); |
| 88 | } |
| 89 | return String.fromCharCode(...output); |
| 90 | } |
| 91 | function paramCase(source) { |
| 92 | return tokenize(source, [45, 95], 45); |
| 93 | } |
| 94 | var hyphenate = paramCase; |
| 95 | var Time; |
| 96 | (function(Time2) { |
| 97 | Time2.millisecond = 1; |
| 98 | Time2.second = 1e3; |
| 99 | Time2.minute = Time2.second * 60; |
| 100 | Time2.hour = Time2.minute * 60; |
| 101 | Time2.day = Time2.hour * 24; |
| 102 | Time2.week = Time2.day * 7; |
| 103 | let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset(); |
| 104 | function setTimezoneOffset(offset) { |
| 105 | timezoneOffset = offset; |
| 106 | } |
| 107 | Time2.setTimezoneOffset = setTimezoneOffset; |
| 108 | function getTimezoneOffset() { |
| 109 | return timezoneOffset; |
| 110 | } |
| 111 | Time2.getTimezoneOffset = getTimezoneOffset; |
| 112 | function getDateNumber(date = /* @__PURE__ */ new Date(), offset) { |
| 113 | if (typeof date === "number") date = new Date(date); |
| 114 | if (offset === void 0) offset = timezoneOffset; |
| 115 | return Math.floor((date.valueOf() / Time2.minute - offset) / 1440); |
| 116 | } |
| 117 | Time2.getDateNumber = getDateNumber; |
| 118 | function fromDateNumber(value, offset) { |
| 119 | const date = new Date(value * Time2.day); |
| 120 | if (offset === void 0) offset = timezoneOffset; |
| 121 | return new Date(+date + offset * Time2.minute); |
| 122 | } |
| 123 | Time2.fromDateNumber = fromDateNumber; |
| 124 | const numeric = /\d+(?:\.\d+)?/.source; |
| 125 | const timeRegExp = new RegExp(`^${[ |
| 126 | "w(?:eek(?:s)?)?", |
| 127 | "d(?:ay(?:s)?)?", |
| 128 | "h(?:our(?:s)?)?", |
| 129 | "m(?:in(?:ute)?(?:s)?)?", |
| 130 | "s(?:ec(?:ond)?(?:s)?)?" |
| 131 | ].map((unit) => `(${numeric}${unit})?`).join("")}$`); |
| 132 | function parseTime(source) { |
| 133 | const capture = timeRegExp.exec(source); |
| 134 | if (!capture) return 0; |
| 135 | return (parseFloat(capture[1]) * Time2.week || 0) + (parseFloat(capture[2]) * Time2.day || 0) + (parseFloat(capture[3]) * Time2.hour || 0) + (parseFloat(capture[4]) * Time2.minute || 0) + (parseFloat(capture[5]) * Time2.second || 0); |
| 136 | } |
| 137 | Time2.parseTime = parseTime; |
| 138 | function parseDate(date) { |
| 139 | const parsed = parseTime(date); |
| 140 | if (parsed) date = Date.now() + parsed; |
| 141 | else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`; |
| 142 | else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`; |
| 143 | return date ? new Date(date) : /* @__PURE__ */ new Date(); |
| 144 | } |
| 145 | Time2.parseDate = parseDate; |
| 146 | function format(ms) { |
| 147 | const abs = Math.abs(ms); |
| 148 | if (abs >= Time2.day - Time2.hour / 2) return Math.round(ms / Time2.day) + "d"; |
| 149 | else if (abs >= Time2.hour - Time2.minute / 2) return Math.round(ms / Time2.hour) + "h"; |
| 150 | else if (abs >= Time2.minute - Time2.second / 2) return Math.round(ms / Time2.minute) + "m"; |
| 151 | else if (abs >= Time2.second) return Math.round(ms / Time2.second) + "s"; |
| 152 | return ms + "ms"; |
| 153 | } |
| 154 | Time2.format = format; |
| 155 | function toDigits(source, length = 2) { |
| 156 | return source.toString().padStart(length, "0"); |
| 157 | } |
| 158 | Time2.toDigits = toDigits; |
| 159 | function template(template2, time = /* @__PURE__ */ new Date()) { |
| 160 | return template2.replace("yyyy", time.getFullYear().toString()).replace("yy", time.getFullYear().toString().slice(2)).replace("MM", toDigits(time.getMonth() + 1)).replace("dd", toDigits(time.getDate())).replace("hh", toDigits(time.getHours())).replace("mm", toDigits(time.getMinutes())).replace("ss", toDigits(time.getSeconds())).replace("SSS", toDigits(time.getMilliseconds(), 3)); |
| 161 | } |
| 162 | Time2.template = template; |
| 163 | })(Time || (Time = {})); |
| 164 | |
| 165 | // node_modules/@deepseek-ai/cordis/lib/index.js |
| 166 | var DisposableList = class { |
| 167 | sn = 0; |
| 168 | map = /* @__PURE__ */ new Map(); |
| 169 | weak = /* @__PURE__ */ new WeakMap(); |
| 170 | get length() { |
| 171 | return this.map.size; |
| 172 | } |
| 173 | push(value) { |
| 174 | const sn = ++this.sn; |
| 175 | this.map.set(sn, value); |
| 176 | this.weak.set(value, sn); |
| 177 | return () => this.map.delete(sn); |
| 178 | } |
| 179 | delete(value) { |
| 180 | const sn = this.weak.get(value); |
| 181 | if (!sn) return false; |
| 182 | return this.map.delete(sn); |
| 183 | } |
| 184 | clear() { |
| 185 | const values = [...this.map.values()]; |
| 186 | this.map.clear(); |
| 187 | return values.reverse(); |
| 188 | } |
| 189 | [Symbol.iterator]() { |
| 190 | return this.map.values(); |
| 191 | } |
| 192 | [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() { |
| 193 | return [...this]; |
| 194 | } |
| 195 | }; |
| 196 | var symbols = { |
| 197 | shadow: /* @__PURE__ */ Symbol.for("cordis.shadow"), |
| 198 | receiver: /* @__PURE__ */ Symbol.for("cordis.receiver"), |
| 199 | original: /* @__PURE__ */ Symbol.for("cordis.original"), |
| 200 | metadata: /* @__PURE__ */ Symbol.for("cordis.metadata"), |
| 201 | initHooks: /* @__PURE__ */ Symbol.for("cordis.initHooks"), |
| 202 | checkProto: /* @__PURE__ */ Symbol.for("cordis.checkProto"), |
| 203 | effect: /* @__PURE__ */ Symbol.for("cordis.effect"), |
| 204 | filter: /* @__PURE__ */ Symbol.for("cordis.filter"), |
| 205 | isolate: /* @__PURE__ */ Symbol.for("cordis.isolate"), |
| 206 | intercept: /* @__PURE__ */ Symbol.for("cordis.intercept"), |
| 207 | init: /* @__PURE__ */ Symbol.for("cordis.init"), |
| 208 | check: /* @__PURE__ */ Symbol.for("cordis.check"), |
| 209 | config: /* @__PURE__ */ Symbol.for("cordis.config"), |
| 210 | invoke: /* @__PURE__ */ Symbol.for("cordis.invoke"), |
| 211 | extend: /* @__PURE__ */ Symbol.for("cordis.extend"), |
| 212 | tracker: /* @__PURE__ */ Symbol.for("cordis.tracker"), |
| 213 | resolveConfig: /* @__PURE__ */ Symbol.for("cordis.resolveConfig") |
| 214 | }; |
| 215 | var GeneratorFunction = function* () { |
| 216 | }.constructor; |
| 217 | var AsyncGeneratorFunction = async function* () { |
| 218 | }.constructor; |
| 219 | function isConstructor(func) { |
| 220 | if (!func.prototype) return false; |
| 221 | if (func instanceof GeneratorFunction) return false; |
| 222 | if (AsyncGeneratorFunction !== Function && func instanceof AsyncGeneratorFunction) return false; |
| 223 | return true; |
| 224 | } |
| 225 | function joinPrototype(proto1, proto2) { |
| 226 | if (proto1 === Object.prototype) return proto2; |
| 227 | const result = Object.create(joinPrototype(Object.getPrototypeOf(proto1), proto2)); |
| 228 | for (const key of Reflect.ownKeys(proto1)) Object.defineProperty(result, key, Object.getOwnPropertyDescriptor(proto1, key)); |
| 229 | return result; |
| 230 | } |
| 231 | function isObject(value) { |
| 232 | return value && (typeof value === "object" || typeof value === "function"); |
| 233 | } |
| 234 | function getPropertyDescriptor(target, prop) { |
| 235 | let proto = target; |
| 236 | while (proto) { |
| 237 | const desc = Reflect.getOwnPropertyDescriptor(proto, prop); |
| 238 | if (desc) return desc; |
| 239 | proto = Object.getPrototypeOf(proto); |
| 240 | } |
| 241 | } |
| 242 | function getTraceable(ctx, value) { |
| 243 | if (!isObject(value)) return value; |
| 244 | if (Object.hasOwn(value, symbols.shadow)) return Object.getPrototypeOf(value); |
| 245 | const tracker = value[symbols.tracker]; |
| 246 | if (!tracker) return value; |
| 247 | return createTraceable(ctx, value, tracker); |
| 248 | } |
| 249 | function withProps(target, props) { |
| 250 | if (!props) return target; |
| 251 | return new Proxy(target, { |
| 252 | get: (target2, prop, receiver) => { |
| 253 | if (prop in props && prop !== "constructor") return Reflect.get(props, prop, receiver); |
| 254 | return Reflect.get(target2, prop, receiver); |
| 255 | }, |
| 256 | set: (target2, prop, value, receiver) => { |
| 257 | if (prop in props && prop !== "constructor") return Reflect.set(props, prop, value, receiver); |
| 258 | return Reflect.set(target2, prop, value, receiver); |
| 259 | } |
| 260 | }); |
| 261 | } |
| 262 | function withProp(target, prop, value) { |
| 263 | return withProps(target, Object.defineProperty(/* @__PURE__ */ Object.create(null), prop, { |
| 264 | value, |
| 265 | writable: false |
| 266 | })); |
| 267 | } |
| 268 | function createShadow(ctx, target, property, receiver) { |
| 269 | if (!property) return receiver; |
| 270 | const origin = Reflect.getOwnPropertyDescriptor(target, property)?.value; |
| 271 | if (!origin) return receiver; |
| 272 | return withProp(receiver, property, ctx.extend({ [symbols.shadow]: origin })); |
| 273 | } |
| 274 | function createShadowMethod(ctx, value, outer, shadow) { |
| 275 | return new Proxy(value, { apply: (target, thisArg, args) => { |
| 276 | if (thisArg === outer) thisArg = shadow; |
| 277 | return getTraceable(ctx, Reflect.apply(target, thisArg, args)); |
| 278 | } }); |
| 279 | } |
| 280 | function createTraceable(ctx, value, tracker) { |
| 281 | if (ctx[symbols.shadow] && !tracker.noShadow) ctx = Object.getPrototypeOf(ctx); |
| 282 | const proxy = new Proxy(value, { |
| 283 | get: (target, prop, receiver) => { |
| 284 | if (prop === symbols.original) return target; |
| 285 | if (prop === tracker.property) return ctx; |
| 286 | if (typeof prop === "symbol") return Reflect.get(target, prop, receiver); |
| 287 | if (tracker.associate && ctx.reflect.props[`${tracker.associate}.${prop}`]) return Reflect.get(ctx, `${tracker.associate}.${prop}`, withProp(ctx, symbols.receiver, receiver)); |
| 288 | let shadow, innerValue; |
| 289 | const desc = getPropertyDescriptor(target, prop); |
| 290 | if (desc && "value" in desc) innerValue = desc.value; |
| 291 | else { |
| 292 | shadow = createShadow(ctx, target, tracker.property, receiver); |
| 293 | innerValue = Reflect.get(target, prop, shadow); |
| 294 | } |
| 295 | const innerTracker = innerValue?.[symbols.tracker]; |
| 296 | if (innerTracker) return createTraceable(ctx, innerValue, innerTracker); |
| 297 | else if (!tracker.noShadow && typeof innerValue === "function") { |
| 298 | shadow ??= createShadow(ctx, target, tracker.property, receiver); |
| 299 | return createShadowMethod(ctx, innerValue, receiver, shadow); |
| 300 | } else return innerValue; |
| 301 | }, |
| 302 | set: (target, prop, value2, receiver) => { |
| 303 | if (prop === symbols.original) return false; |
| 304 | if (prop === tracker.property) return false; |
| 305 | if (typeof prop === "symbol") return Reflect.set(target, prop, value2, receiver); |
| 306 | if (tracker.associate && ctx.reflect.props[`${tracker.associate}.${prop}`]) return Reflect.set(ctx, `${tracker.associate}.${prop}`, value2, withProp(ctx, symbols.receiver, receiver)); |
| 307 | const shadow = createShadow(ctx, target, tracker.property, receiver); |
| 308 | return Reflect.set(target, prop, value2, shadow); |
| 309 | }, |
| 310 | apply: (target, thisArg, args) => { |
| 311 | return applyTraceable(proxy, target, thisArg, args); |
| 312 | } |
| 313 | }); |
| 314 | return proxy; |
| 315 | } |
| 316 | function applyTraceable(proxy, value, thisArg, args) { |
| 317 | if (!value[symbols.invoke]) return Reflect.apply(value, thisArg, args); |
| 318 | return value[symbols.invoke].apply(proxy, args); |
| 319 | } |
| 320 | function createCallable(name, proto, tracker) { |
| 321 | const self = function(...args) { |
| 322 | return applyTraceable(createTraceable(self["ctx"], self, tracker), self, this, args); |
| 323 | }; |
| 324 | defineProperty(self, "name", name); |
| 325 | return Object.setPrototypeOf(self, proto); |
| 326 | } |
| 327 | function handleError(info, reason, getOuterStack) { |
| 328 | const innerLines = info.error.stack.split("\n"); |
| 329 | if (typeof reason?.stack !== "string") { |
| 330 | const outerError = new Error(reason); |
| 331 | const lines2 = outerError.stack.split("\n"); |
| 332 | lines2.splice(1, Infinity, ...getOuterStack()); |
| 333 | outerError.stack = lines2.join("\n"); |
| 334 | throw outerError; |
| 335 | } |
| 336 | const lines = reason.stack.split("\n"); |
| 337 | let index = lines.indexOf(innerLines[2]); |
| 338 | if (index === -1) throw reason; |
| 339 | index -= info.offset; |
| 340 | while (index > 0) { |
| 341 | if (!lines[index - 1].endsWith(" (<anonymous>)")) break; |
| 342 | index -= 1; |
| 343 | } |
| 344 | lines.splice(index, Infinity, ...getOuterStack()); |
| 345 | reason.stack = lines.join("\n"); |
| 346 | throw reason; |
| 347 | } |
| 348 | function composeError(callback, getOuterStack = buildOuterStack()) { |
| 349 | const info = { |
| 350 | offset: 1, |
| 351 | error: /* @__PURE__ */ new Error() |
| 352 | }; |
| 353 | try { |
| 354 | const result = callback(info); |
| 355 | if (isObject(result) && "then" in result) return result.then(void 0, (reason) => handleError(info, reason, getOuterStack)); |
| 356 | else return result; |
| 357 | } catch (reason) { |
| 358 | handleError(info, reason, getOuterStack); |
| 359 | } |
| 360 | } |
| 361 | function buildOuterStack(offset = 0) { |
| 362 | const outerError = /* @__PURE__ */ new Error(); |
| 363 | return () => outerError.stack.split("\n").slice(3 + offset); |
| 364 | } |
| 365 | function isBailed(value) { |
| 366 | return value !== null && value !== false && value !== void 0; |
| 367 | } |
| 368 | var EventsService = class { |
| 369 | ctx; |
| 370 | _hooks = {}; |
| 371 | constructor(ctx) { |
| 372 | this.ctx = ctx; |
| 373 | defineProperty(this, symbols.tracker, { |
| 374 | property: "ctx", |
| 375 | noShadow: true |
| 376 | }); |
| 377 | this.on("internal/listener", function(name, listener, options) { |
| 378 | if (name === "internal/update" && !options.global) return (this.fiber._hooks["internal/update"] ??= new DisposableList())[options.prepend ? "unshift" : "push"](listener); |
| 379 | }); |
| 380 | this.on("internal/update", function(config, noSave, next) { |
| 381 | const cbs = [...this._hooks["internal/update"] || []]; |
| 382 | const _next = () => { |
| 383 | return (cbs.shift() ?? next).call(this, config, noSave, _next); |
| 384 | }; |
| 385 | return _next(); |
| 386 | }, { |
| 387 | global: true, |
| 388 | prepend: true |
| 389 | }); |
| 390 | } |
| 391 | /** |
| 392 | * Resolve listeners for one dispatch and apply context filtering. |
| 393 | * |
| 394 | * @param type — the dispatch mode, reported on `internal/dispatch`. |
| 395 | * @param args — the raw dispatch arguments; consumed up to the event name. |
| 396 | * @returns the matching listener callbacks, bound to the dispatch `this`. |
| 397 | */ |
| 398 | dispatch(type, args) { |
| 399 | const thisArg = typeof args[0] === "object" || typeof args[0] === "function" ? args.shift() : null; |
| 400 | const name = args.shift(); |
| 401 | if (!name.startsWith("internal/")) this.emit("internal/dispatch", type, name, args, thisArg); |
| 402 | const filter = thisArg?.[Context.filter]; |
| 403 | return (this._hooks[name] || []).filter((hook) => hook.global || !filter || filter.call(thisArg, hook.ctx)).map((hook) => hook.callback.bind(thisArg)); |
| 404 | } |
| 405 | /** |
| 406 | * Run listeners concurrently and wait for all of them. |
| 407 | * |
| 408 | * @param args — optional `this`, the event name, then listener arguments. |
| 409 | * @returns a promise resolving once every listener has settled. |
| 410 | */ |
| 411 | async parallel(...args) { |
| 412 | const errors = (await Promise.allSettled(this.dispatch("emit", args).map(async (cb) => cb(...args)))).filter((result) => result.status === "rejected"); |
| 413 | if (errors.length) throw new AggregateError(errors.map((error) => error.reason)); |
| 414 | } |
| 415 | /** |
| 416 | * Run listeners synchronously without waiting for returned promises. |
| 417 | * |
| 418 | * @param args — optional `this`, the event name, then listener arguments. |
| 419 | */ |
| 420 | emit(...args) { |
| 421 | this.dispatch("emit", args).map((cb) => cb(...args)); |
| 422 | } |
| 423 | /** |
| 424 | * Run listeners in order, awaiting each, until one returns a bail value. |
| 425 | * |
| 426 | * @param args — optional `this`, the event name, then listener arguments. |
| 427 | * @returns the first bail value (see {@link isBailed}), if any. |
| 428 | */ |
| 429 | async serial(...args) { |
| 430 | for (const cb of this.dispatch("serial", args)) { |
| 431 | const result = await cb(...args); |
| 432 | if (isBailed(result)) return result; |
| 433 | } |
| 434 | } |
| 435 | /** |
| 436 | * Run listeners synchronously until one returns a bail value. |
| 437 | * |
| 438 | * @param args — optional `this`, the event name, then listener arguments. |
| 439 | * @returns the first bail value (see {@link isBailed}), if any. |
| 440 | */ |
| 441 | bail(...args) { |
| 442 | for (const cb of this.dispatch("bail", args)) { |
| 443 | const result = cb(...args); |
| 444 | if (isBailed(result)) return result; |
| 445 | } |
| 446 | } |
| 447 | /** |
| 448 | * Compose listeners around the final `next` callback. |
| 449 | * |
| 450 | * The last dispatch argument is treated as the innermost `next`. Listeners |
| 451 | * run outermost-first; a listener that does not call `next()` vetoes the |
| 452 | * rest of the chain, including the built-in behavior. |
| 453 | * |
| 454 | * @param args — optional `this`, the event name, listener arguments, then `next`. |
| 455 | * @returns the outermost listener's return value. |
| 456 | */ |
| 457 | waterfall(...args) { |
| 458 | const cbs = this.dispatch("waterfall", args); |
| 459 | const inner = args.pop(); |
| 460 | const next = () => { |
| 461 | return (cbs.shift() ?? inner)(...args); |
| 462 | }; |
| 463 | args.push(next); |
| 464 | return next(); |
| 465 | } |
| 466 | /** |
| 467 | * Store a listener record as an effect on the current fiber. |
| 468 | * |
| 469 | * @param label — effect label shown in fiber diagnostics. |
| 470 | * @param hooks — the listener list for one event. |
| 471 | * @param callback — the listener to store. |
| 472 | * @param options — placement and filtering options. |
| 473 | * @returns a disposer that unregisters the listener. |
| 474 | */ |
| 475 | register(label, hooks, callback, options) { |
| 476 | const method = options.prepend ? "unshift" : "push"; |
| 477 | return this.ctx.fiber.effect(() => { |
| 478 | hooks[method]({ |
| 479 | ctx: this.ctx, |
| 480 | callback, |
| 481 | ...options |
| 482 | }); |
| 483 | return () => this.unregister(hooks, callback); |
| 484 | }, label); |
| 485 | } |
| 486 | /** |
| 487 | * Remove a stored listener record. |
| 488 | * |
| 489 | * @param hooks — the listener list for one event. |
| 490 | * @param callback — the listener to remove. |
| 491 | * @returns `true` if the listener was found and removed. |
| 492 | */ |
| 493 | unregister(hooks, callback) { |
| 494 | const index = hooks.findIndex((hook) => hook.callback === callback); |
| 495 | if (index >= 0) { |
| 496 | hooks.splice(index, 1); |
| 497 | return true; |
| 498 | } |
| 499 | } |
| 500 | /** |
| 501 | * Register an event listener owned by the current fiber. |
| 502 | * |
| 503 | * The listener is removed automatically when the fiber unloads. Throws |
| 504 | * `CordisError('INACTIVE_EFFECT')` if the fiber is already disposed. |
| 505 | * |
| 506 | * @param name — the event name to listen for. |
| 507 | * @param listener — called with the dispatch arguments. |
| 508 | * @param options — listener options; a boolean is shorthand for `prepend`. |
| 509 | * @returns a disposer removing the listener; `true` if it was still registered. |
| 510 | */ |
| 511 | on(name, listener, options) { |
| 512 | if (typeof options !== "object") options = { prepend: options }; |
| 513 | this.ctx.fiber.assertActive(); |
| 514 | listener = this.ctx.reflect.bind(listener); |
| 515 | const result = this.bail(this.ctx, "internal/listener", name, listener, options); |
| 516 | if (result) return result; |
| 517 | const hooks = this._hooks[name] ||= []; |
| 518 | const label = `ctx.on(${typeof name === "string" ? JSON.stringify(name) : name.toString()})`; |
| 519 | return this.register(label, hooks, listener, options); |
| 520 | } |
| 521 | /** |
| 522 | * Register an event listener that disposes itself after the first call. |
| 523 | * |
| 524 | * @param name — the event name to listen for. |
| 525 | * @param listener — called at most once with the dispatch arguments. |
| 526 | * @param options — listener options; a boolean is shorthand for `prepend`. |
| 527 | * @returns a disposer removing the listener; `true` if it was still registered. |
| 528 | */ |
| 529 | once(name, listener, options) { |
| 530 | const dispose = this.on(name, function(...args) { |
| 531 | dispose(); |
| 532 | return listener.apply(this, args); |
| 533 | }, options); |
| 534 | return dispose; |
| 535 | } |
| 536 | }; |
| 537 | var defaultFormatters = { |
| 538 | s: (value) => String(value), |
| 539 | d: (value) => Math.trunc(Number(value)), |
| 540 | i: (value) => Math.trunc(Number(value)), |
| 541 | f: (value) => Number(value), |
| 542 | o: (value) => JSON.stringify(value), |
| 543 | O: (value) => JSON.stringify(value), |
| 544 | c: () => "", |
| 545 | C: (value, exporter, message) => { |
| 546 | return Logger.color(exporter, Logger.code(message.name, exporter.colors), value); |
| 547 | } |
| 548 | }; |
| 549 | function isAggregateError(error) { |
| 550 | return error instanceof Error && Array.isArray(error["errors"]); |
| 551 | } |
| 552 | var Logger = class { |
| 553 | service; |
| 554 | static color(exporter, code, value, decoration = "") { |
| 555 | if (!exporter.colors) return "" + value; |
| 556 | return `\x1B[3${code < 8 ? code : "8;5;" + code}${exporter.colors >= 2 ? decoration : ""}m${value}\x1B[0m`; |
| 557 | } |
| 558 | static code(name, level) { |
| 559 | let hash = 0; |
| 560 | for (let i = 0; i < name.length; i++) { |
| 561 | hash = (hash << 3) - hash + name.charCodeAt(i) + 13; |
| 562 | hash |= 0; |
| 563 | } |
| 564 | const colors = !level ? [] : level >= 2 ? c256 : c16; |
| 565 | return colors[Math.abs(hash) % colors.length]; |
| 566 | } |
| 567 | static format(exporter, message) { |
| 568 | const args = message.args.slice(); |
| 569 | if (args[0] instanceof Error) { |
| 570 | args[0] = args[0].stack || args[0].message; |
| 571 | args.unshift("%s"); |
| 572 | } else if (typeof args[0] !== "string") args.unshift("%o"); |
| 573 | let format = args.shift(); |
| 574 | format = format.replace(/%([a-zA-Z%])/g, (match, char) => { |
| 575 | if (match === "%%") return "%"; |
| 576 | const formatter = exporter.formatters?.[char] ?? defaultFormatters[char]; |
| 577 | if (typeof formatter === "function") return formatter(args.shift(), exporter, message); |
| 578 | return match; |
| 579 | }); |
| 580 | const oFormatter = exporter.formatters?.o ?? defaultFormatters.o; |
| 581 | for (let arg of args) { |
| 582 | if (typeof arg === "object" && arg) arg = oFormatter(arg, exporter, message); |
| 583 | format += " " + arg; |
| 584 | } |
| 585 | const { maxLength = 10240 } = exporter; |
| 586 | return format.split(/\r?\n/g).map((line) => { |
| 587 | return line.slice(0, maxLength) + (line.length > maxLength ? "..." : ""); |
| 588 | }).join("\n"); |
| 589 | } |
| 590 | constructor(options, service) { |
| 591 | this.service = service; |
| 592 | Object.assign(this, options); |
| 593 | this.error = this._method("error", 0); |
| 594 | this.info = this._method("info", 1); |
| 595 | this.warn = this._method("warn", 2); |
| 596 | this.debug = this._method("debug", 3); |
| 597 | } |
| 598 | _method(type, level) { |
| 599 | return (...args) => { |
| 600 | if (args.length === 1 && args[0] instanceof Error) { |
| 601 | if (args[0].cause) this[type](args[0].cause); |
| 602 | else if (isAggregateError(args[0])) { |
| 603 | args[0].errors.forEach((error) => this[type](error)); |
| 604 | return; |
| 605 | } |
| 606 | } |
| 607 | const sn = ++this.service._snMessage; |
| 608 | const ts = Date.now(); |
| 609 | for (const exporter of this.service.exporters.values()) { |
| 610 | if ((exporter.levels?.[this.name] ?? exporter.levels?.default ?? this.level ?? 1) < level) continue; |
| 611 | const message = { |
| 612 | sn, |
| 613 | ts, |
| 614 | type, |
| 615 | level, |
| 616 | name: this.name, |
| 617 | ...this.meta, |
| 618 | args |
| 619 | }; |
| 620 | exporter.export(message); |
| 621 | } |
| 622 | }; |
| 623 | } |
| 624 | }; |
| 625 | var c16 = [ |
| 626 | 6, |
| 627 | 2, |
| 628 | 3, |
| 629 | 4, |
| 630 | 5, |
| 631 | 1 |
| 632 | ]; |
| 633 | var c256 = [ |
| 634 | 20, |
| 635 | 21, |
| 636 | 26, |
| 637 | 27, |
| 638 | 32, |
| 639 | 33, |
| 640 | 38, |
| 641 | 39, |
| 642 | 40, |
| 643 | 41, |
| 644 | 42, |
| 645 | 43, |
| 646 | 44, |
| 647 | 45, |
| 648 | 56, |
| 649 | 57, |
| 650 | 62, |
| 651 | 63, |
| 652 | 68, |
| 653 | 69, |
| 654 | 74, |
| 655 | 75, |
| 656 | 76, |
| 657 | 77, |
| 658 | 78, |
| 659 | 79, |
| 660 | 80, |
| 661 | 81, |
| 662 | 92, |
| 663 | 93, |
| 664 | 98, |
| 665 | 99, |
| 666 | 112, |
| 667 | 113, |
| 668 | 129, |
| 669 | 134, |
| 670 | 135, |
| 671 | 148, |
| 672 | 149, |
| 673 | 160, |
| 674 | 161, |
| 675 | 162, |
| 676 | 163, |
| 677 | 164, |
| 678 | 165, |
| 679 | 166, |
| 680 | 167, |
| 681 | 168, |
| 682 | 169, |
| 683 | 170, |
| 684 | 171, |
| 685 | 172, |
| 686 | 173, |
| 687 | 178, |
| 688 | 179, |
| 689 | 184, |
| 690 | 185, |
| 691 | 196, |
| 692 | 197, |
| 693 | 198, |
| 694 | 199, |
| 695 | 200, |
| 696 | 201, |
| 697 | 202, |
| 698 | 203, |
| 699 | 204, |
| 700 | 205, |
| 701 | 206, |
| 702 | 207, |
| 703 | 208, |
| 704 | 209, |
| 705 | 214, |
| 706 | 215, |
| 707 | 220, |
| 708 | 221 |
| 709 | ]; |
| 710 | var LoggerService = class LoggerService2 { |
| 711 | bufferSize = 1e3; |
| 712 | buffer = []; |
| 713 | ctx; |
| 714 | _snMessage = 0; |
| 715 | _snExporter = 0; |
| 716 | exporters = /* @__PURE__ */ new Map(); |
| 717 | constructor(ctx) { |
| 718 | const tracker = { |
| 719 | property: "ctx", |
| 720 | noShadow: true |
| 721 | }; |
| 722 | const self = createCallable("logger", joinPrototype(Object.getPrototypeOf(this), Function.prototype), tracker); |
| 723 | Object.assign(self, this); |
| 724 | self.ctx = ctx; |
| 725 | defineProperty(self, symbols.tracker, tracker); |
| 726 | self.exporter({ |
| 727 | colors: 3, |
| 728 | export: (message) => { |
| 729 | self.buffer.push(message); |
| 730 | if (self.buffer.length > self.bufferSize) self.buffer = self.buffer.slice(-self.bufferSize); |
| 731 | } |
| 732 | }); |
| 733 | return self; |
| 734 | } |
| 735 | /** |
| 736 | * Register an exporter and dispose it with the current fiber. |
| 737 | * |
| 738 | * @param exporter — the sink that receives structured log messages. |
| 739 | * @returns a disposer that removes the exporter. |
| 740 | */ |
| 741 | exporter(exporter) { |
| 742 | return this.ctx.effect(() => { |
| 743 | const id = ++this._snExporter; |
| 744 | this.exporters.set(id, exporter); |
| 745 | return () => this.exporters.delete(id); |
| 746 | }, "ctx.logger.exporter()"); |
| 747 | } |
| 748 | _resolveConfig() { |
| 749 | let intercept = this.ctx[symbols.intercept]; |
| 750 | const configs = []; |
| 751 | while ("logger" in intercept) { |
| 752 | if (Object.hasOwn(intercept, "logger")) configs.unshift(intercept["logger"]); |
| 753 | intercept = Object.getPrototypeOf(intercept); |
| 754 | } |
| 755 | return Object.assign({}, ...configs); |
| 756 | } |
| 757 | [symbols.invoke](name) { |
| 758 | const config = this._resolveConfig(); |
| 759 | const fiber = (this.ctx[symbols.shadow] ?? this.ctx).fiber; |
| 760 | name ??= config.name; |
| 761 | name ??= hyphenate(fiber.name); |
| 762 | return new Logger({ |
| 763 | name, |
| 764 | level: config.level, |
| 765 | meta: { fiber: new WeakRef(fiber) } |
| 766 | }, this); |
| 767 | } |
| 768 | static { |
| 769 | for (const type of [ |
| 770 | "error", |
| 771 | "info", |
| 772 | "warn", |
| 773 | "debug" |
| 774 | ]) LoggerService2.prototype[type] = function(...args) { |
| 775 | return this()[type](...args); |
| 776 | }; |
| 777 | } |
| 778 | }; |
| 779 | function enhanceError(error) { |
| 780 | const lines = error.stack.split("\n"); |
| 781 | lines.splice(0, 2, `Error: ${error.message}`); |
| 782 | error.stack = lines.join("\n"); |
| 783 | return error; |
| 784 | } |
| 785 | var RESERVED_WORDS = ["prototype", "then"]; |
| 786 | function isSpecialProperty(prop) { |
| 787 | return typeof prop === "symbol" || RESERVED_WORDS.includes(prop) || parseInt(prop).toString() === prop || prop.startsWith("_"); |
| 788 | } |
| 789 | var ReflectService = class { |
| 790 | ctx; |
| 791 | /** Proxy traps implementing service resolution for every context object. */ |
| 792 | static handler = { |
| 793 | get: (target, prop, ctx) => { |
| 794 | if (isSpecialProperty(prop)) return Reflect.get(target, prop, ctx); |
| 795 | if (Reflect.has(target, prop)) return getTraceable(ctx, Reflect.get(target, prop, ctx)); |
| 796 | const error = /* @__PURE__ */ new Error(`cannot get property "${prop}" without inject`); |
| 797 | try { |
| 798 | const def = target.reflect.props[prop]; |
| 799 | if (def?.type === "accessor") return def.get.call(ctx, ctx[symbols.receiver], error); |
| 800 | if (!ctx.fiber.runtime) return ctx.reflect.get(prop, false); |
| 801 | return ctx.events.waterfall("internal/get", ctx, prop, error, () => { |
| 802 | const key = target[symbols.isolate][prop]; |
| 803 | let fiber = (ctx[symbols.shadow] ?? ctx).fiber; |
| 804 | while (true) { |
| 805 | const impl = fiber.store?.[prop]; |
| 806 | if (impl) return getTraceable(ctx, impl.value); |
| 807 | if (prop in fiber.inject) { |
| 808 | error.message = `cannot get required service "${prop}" in inactive context`; |
| 809 | throw error; |
| 810 | } |
| 811 | if (!fiber.runtime) throw error; |
| 812 | if (fiber.parent[symbols.isolate][prop] !== key) throw error; |
| 813 | fiber = fiber.parent.fiber; |
| 814 | } |
| 815 | }); |
| 816 | } catch (e) { |
| 817 | throw e === error ? enhanceError(e) : e; |
| 818 | } |
| 819 | }, |
| 820 | set: (target, prop, value, ctx) => { |
| 821 | if (isSpecialProperty(prop)) return Reflect.set(target, prop, value, ctx); |
| 822 | const error = /* @__PURE__ */ new Error(`cannot set property "${prop}" without provide`); |
| 823 | const def = target.reflect.props[prop]; |
| 824 | if (!def) { |
| 825 | if (!ctx.fiber.runtime) return Reflect.set(target, prop, value, ctx); |
| 826 | throw enhanceError(error); |
| 827 | } |
| 828 | try { |
| 829 | if (def.type === "accessor") { |
| 830 | if (!def.set) return false; |
| 831 | return def.set.call(ctx, value, ctx[symbols.receiver], error); |
| 832 | } |
| 833 | return ctx.events.waterfall("internal/set", ctx, prop, value, error, () => { |
| 834 | return ctx.reflect.set(prop, value, error); |
| 835 | }); |
| 836 | } catch (e) { |
| 837 | throw e === error ? enhanceError(e) : e; |
| 838 | } |
| 839 | }, |
| 840 | has: (target, prop) => { |
| 841 | if (isSpecialProperty(prop)) return Reflect.has(target, prop); |
| 842 | if (Reflect.has(target, prop)) return true; |
| 843 | return !!target.reflect.props[prop]; |
| 844 | } |
| 845 | }; |
| 846 | /** Service implementations, keyed by isolation label. */ |
| 847 | store = /* @__PURE__ */ Object.create(null); |
| 848 | /** Declared context properties (services and accessors), by name. */ |
| 849 | props = /* @__PURE__ */ Object.create(null); |
| 850 | constructor(ctx) { |
| 851 | this.ctx = ctx; |
| 852 | defineProperty(this, symbols.tracker, { |
| 853 | property: "ctx", |
| 854 | noShadow: true |
| 855 | }); |
| 856 | this.mixin("reflect", [ |
| 857 | "get", |
| 858 | "set", |
| 859 | "provide", |
| 860 | "accessor", |
| 861 | "mixin" |
| 862 | ]); |
| 863 | this.mixin("fiber", ["runtime", "effect"]); |
| 864 | this.mixin("registry", ["inject", "plugin"]); |
| 865 | this.mixin("events", [ |
| 866 | "on", |
| 867 | "once", |
| 868 | "parallel", |
| 869 | "emit", |
| 870 | "serial", |
| 871 | "bail", |
| 872 | "waterfall" |
| 873 | ]); |
| 874 | } |
| 875 | /** |
| 876 | * Read a service from the store without the inject requirement. |
| 877 | * |
| 878 | * @param name — the service name. |
| 879 | * @param strict — when `true`, only return implementations whose providing |
| 880 | * fiber is currently active. |
| 881 | * @returns the service value, or `undefined` when not (yet) provided. |
| 882 | */ |
| 883 | get(name, strict = true) { |
| 884 | return getTraceable(this.ctx, this._getImpl(name, strict)?.value); |
| 885 | } |
| 886 | _getImpl(name, strict = true) { |
| 887 | const key = this.ctx[symbols.isolate][name]; |
| 888 | const impl = key && this.store[key]; |
| 889 | if (!impl) return; |
| 890 | if (strict && impl.fiber.state !== 2) return; |
| 891 | return impl; |
| 892 | } |
| 893 | /** |
| 894 | * Overwrite a provided service's value. |
| 895 | * |
| 896 | * @param name — the service name. |
| 897 | * @param value — the new service value. |
| 898 | * @param error — carrier for the caller stack in diagnostics. |
| 899 | * @returns `true` on success. |
| 900 | * @throws when `name` was never provided, or was provided by another fiber. |
| 901 | */ |
| 902 | set(name, value, error) { |
| 903 | const key = this.ctx[symbols.isolate][name]; |
| 904 | const impl = this.store[key]; |
| 905 | if (!impl) throw new Error(`cannot set property "${name}" without provide`); |
| 906 | if (impl.fiber !== this.ctx.fiber) throw new Error(`cannot set property "${name}" in multiple fibers`); |
| 907 | impl.value = value; |
| 908 | return true; |
| 909 | } |
| 910 | /** |
| 911 | * Register a service implementation owned by the current fiber. |
| 912 | * |
| 913 | * See the `ctx.provide()` overload above for the full contract. |
| 914 | * |
| 915 | * @param name — the service name. |
| 916 | * @param value — the service value. |
| 917 | * @param check — optional availability predicate for dependents. |
| 918 | * @returns a disposer that unregisters the service. |
| 919 | */ |
| 920 | provide(name, value, check) { |
| 921 | return this.ctx.fiber.effect(() => { |
| 922 | if (!this.props[name]) this.props[name] ??= { type: "service" }; |
| 923 | else if (this.props[name].type !== "service") throw new Error(`property "${name}" is already declared as ${this.props[name].type}`); |
| 924 | this.props[name] = { type: "service" }; |
| 925 | this.ctx.root[symbols.isolate][name] ??= Symbol(name); |
| 926 | const key = this.ctx[symbols.isolate][name]; |
| 927 | const impl = { |
| 928 | name, |
| 929 | value, |
| 930 | fiber: this.ctx.fiber, |
| 931 | check |
| 932 | }; |
| 933 | if (this.store[key]) throw new Error(`service "${name}" has been registered at <${this.store[key].fiber.name}>`); |
| 934 | this.store[key] = impl; |
| 935 | this.ctx.fiber.store[name] = impl; |
| 936 | if (this.ctx.fiber.state === 2) this.notify([name]); |
| 937 | return async () => { |
| 938 | delete this.store[key]; |
| 939 | const fibers = this.notify([name]); |
| 940 | await Promise.allSettled(fibers.map((fiber) => fiber.await())); |
| 941 | delete this.ctx.fiber.store[name]; |
| 942 | }; |
| 943 | }, `ctx.provide(${JSON.stringify(name)})`); |
| 944 | } |
| 945 | /** |
| 946 | * Re-evaluate every fiber that requires one of the given services. |
| 947 | * |
| 948 | * @param names — the service names that changed. |
| 949 | * @param filter — restricts notification to matching isolation scopes. |
| 950 | * @returns the fibers whose dependency state was refreshed. |
| 951 | */ |
| 952 | notify(names, filter = (ctx, name) => ctx[symbols.isolate][name] === this.ctx[symbols.isolate][name]) { |
| 953 | const fibers = []; |
| 954 | for (const runtime of this.ctx.registry.values()) for (const fiber of runtime.fibers) { |
| 955 | let hasUpdate = false; |
| 956 | for (const name of names) { |
| 957 | if (!(name in fiber.inject)) continue; |
| 958 | if (!filter(fiber.ctx, name)) continue; |
| 959 | hasUpdate = true; |
| 960 | fiber._checkImpl(name); |
| 961 | } |
| 962 | if (!hasUpdate) continue; |
| 963 | fiber._refresh(); |
| 964 | fibers.push(fiber); |
| 965 | } |
| 966 | for (const name of names) { |
| 967 | const self = Object.create(this.ctx); |
| 968 | self[symbols.filter] = (target) => filter(target, name); |
| 969 | this.ctx.events.emit(self, "internal/service", name, this._getImpl(name, false)?.value); |
| 970 | } |
| 971 | return fibers; |
| 972 | } |
| 973 | /** |
| 974 | * Define a computed context property backed by get/set hooks. |
| 975 | * |
| 976 | * @param name — the context property name. |
| 977 | * @param options — the `get` hook and optional `set` hook. |
| 978 | * @returns a disposer that removes the accessor. |
| 979 | */ |
| 980 | accessor(name, options) { |
| 981 | return this.ctx.fiber.effect(() => { |
| 982 | if (name in this.props) throw new Error(`property "${name}" is already declared as ${this.props[name].type}`); |
| 983 | this.props[name] = { |
| 984 | type: "accessor", |
| 985 | ...options |
| 986 | }; |
| 987 | return () => delete this.props[name]; |
| 988 | }, `ctx.accessor(${JSON.stringify(name)})`); |
| 989 | } |
| 990 | /** |
| 991 | * Expose selected members of a service directly on `ctx`. |
| 992 | * |
| 993 | * See the `ctx.mixin()` overload above for the full contract. |
| 994 | * |
| 995 | * @param source — a context property name or a source object. |
| 996 | * @param mixins — keys to forward, or a source-key → ctx-key map. |
| 997 | * @returns a disposer that removes all created accessors. |
| 998 | */ |
| 999 | mixin(source, mixins) { |
| 1000 | const self = this; |
| 1001 | return this.ctx.fiber.effect(function* () { |
| 1002 | const entries = Array.isArray(mixins) ? mixins.map((key) => [key, key]) : Object.entries(mixins); |
| 1003 | const getTarget = (ctx, error) => { |
| 1004 | return ctx[source]; |
| 1005 | }; |
| 1006 | for (const [key, value] of entries) yield self.accessor(value, { |
| 1007 | get(receiver, error) { |
| 1008 | const service = getTarget(this, error); |
| 1009 | if (isNullable(service)) return service; |
| 1010 | const mixin = receiver ? withProps(receiver, service) : service; |
| 1011 | const value2 = Reflect.get(service, key, mixin); |
| 1012 | if (typeof value2 !== "function") return value2; |
| 1013 | return value2.bind(mixin ?? service); |
| 1014 | }, |
| 1015 | set(value2, receiver, error) { |
| 1016 | const service = getTarget(this, error); |
| 1017 | const mixin = receiver ? withProps(receiver, service) : service; |
| 1018 | return Reflect.set(service, key, value2, mixin); |
| 1019 | } |
| 1020 | }); |
| 1021 | }, `ctx.mixin(${JSON.stringify(source)})`); |
| 1022 | } |
| 1023 | /** |
| 1024 | * Attach this context's tracing wrapper to a value. |
| 1025 | * |
| 1026 | * @param value — the value to wrap. |
| 1027 | * @returns the traceable wrapper (or the value itself when not applicable). |
| 1028 | */ |
| 1029 | trace(value) { |
| 1030 | return getTraceable(this.ctx, value); |
| 1031 | } |
| 1032 | /** |
| 1033 | * Wrap a callback so calls trace `this` and arguments to this context. |
| 1034 | * |
| 1035 | * @param callback — the function to wrap. |
| 1036 | * @returns a proxy delegating to `callback` with traced values. |
| 1037 | */ |
| 1038 | bind(callback) { |
| 1039 | return new Proxy(callback, { |
| 1040 | apply: (target, thisArg, args) => { |
| 1041 | return Reflect.apply(target, this.trace(thisArg), args.map((arg) => this.trace(arg))); |
| 1042 | }, |
| 1043 | construct: (target, args, newTarget) => { |
| 1044 | return Reflect.construct(target, args.map((arg) => this.trace(arg)), newTarget); |
| 1045 | } |
| 1046 | }); |
| 1047 | } |
| 1048 | }; |
| 1049 | var kValidationError = /* @__PURE__ */ Symbol.for("ValidationError"); |
| 1050 | var ValidationError = class extends TypeError { |
| 1051 | name = "ValidationError"; |
| 1052 | /** |
| 1053 | * Build the aggregated message from schema issues. |
| 1054 | * |
| 1055 | * @param issues — the standard-schema issues, one message line each. |
| 1056 | */ |
| 1057 | constructor(issues) { |
| 1058 | super(`invalid config: |
| 1059 | ` + issues.map((issue) => { |
| 1060 | if (issue.path) return ` - ${issue.message} (at ${issue.path.join(".")})`; |
| 1061 | else return ` - ${issue.message}`; |
| 1062 | }).join("\n")); |
| 1063 | } |
| 1064 | }; |
| 1065 | Object.defineProperty(ValidationError.prototype, kValidationError, { value: true }); |
| 1066 | function resolveConfig(runtime, config) { |
| 1067 | if (!runtime.Config) return config; |
| 1068 | const result = runtime.Config["~standard"].validate(config); |
| 1069 | if ("then" in result) throw new TypeError("Async config validation is not supported"); |
| 1070 | if (result.issues) throw new ValidationError(result.issues); |
| 1071 | else return result.value; |
| 1072 | } |
| 1073 | var effectInertia = /* @__PURE__ */ new WeakMap(); |
| 1074 | function runDisposable(dispose) { |
| 1075 | const result = dispose(); |
| 1076 | return effectInertia.get(dispose)?.() ?? result; |
| 1077 | } |
| 1078 | function emitPluginDisposed(context, fiber) { |
| 1079 | const args = ["internal/plugin", fiber]; |
| 1080 | let callbacks; |
| 1081 | try { |
| 1082 | callbacks = context.events.dispatch("emit", args); |
| 1083 | } catch (error) { |
| 1084 | context.logger.error(error); |
| 1085 | return; |
| 1086 | } |
| 1087 | for (const callback of callbacks) try { |
| 1088 | const returned = callback(...args); |
| 1089 | Promise.resolve(returned).catch((error) => context.logger.error(error)); |
| 1090 | } catch (error) { |
| 1091 | context.logger.error(error); |
| 1092 | } |
| 1093 | } |
| 1094 | var CordisError = class CordisError2 extends Error { |
| 1095 | code; |
| 1096 | /** |
| 1097 | * @param code — the stable error code; also the default message. |
| 1098 | * @param message — optional human-readable override. |
| 1099 | */ |
| 1100 | constructor(code, message) { |
| 1101 | super(message ?? CordisError2.Code[code]); |
| 1102 | this.code = code; |
| 1103 | } |
| 1104 | }; |
| 1105 | (function(CordisError3) { |
| 1106 | CordisError3.Code = { INACTIVE_EFFECT: "cannot create effect on inactive context" }; |
| 1107 | })(CordisError || (CordisError = {})); |
| 1108 | var INACTIVE = "__INACTIVE__"; |
| 1109 | var Fiber = class { |
| 1110 | parent; |
| 1111 | inject; |
| 1112 | runtime; |
| 1113 | /** Unique id within the registry; 0 for the root fiber, `null` once disposed. */ |
| 1114 | uid; |
| 1115 | /** The context this fiber's plugin runs in (extends the parent context). */ |
| 1116 | ctx; |
| 1117 | /** The validated plugin config (updated by `update()`). */ |
| 1118 | config; |
| 1119 | /** The raw plugin config, re-resolved before each activation. */ |
| 1120 | _config; |
| 1121 | /** Current lifecycle state; transitions emit `internal/status`. */ |
| 1122 | state = 0; |
| 1123 | /** Dispose this fiber: unload the plugin, then settle once cleanup finished. */ |
| 1124 | dispose; |
| 1125 | /** Snapshot of required service implementations while loaded; `undefined` otherwise. */ |
| 1126 | store; |
| 1127 | /** The in-flight load/unload transition, if one is currently running. */ |
| 1128 | inertia; |
| 1129 | _hooks = /* @__PURE__ */ Object.create(null); |
| 1130 | _disposables = new DisposableList(); |
| 1131 | context; |
| 1132 | _error; |
| 1133 | _runner; |
| 1134 | _store = /* @__PURE__ */ Object.create(null); |
| 1135 | /** |
| 1136 | * Create a fiber. Plugin authors normally obtain fibers from `ctx.plugin()` |
| 1137 | * rather than constructing them directly. |
| 1138 | * |
| 1139 | * @param parent — the context the plugin was loaded from. |
| 1140 | * @param config — raw config, validated against the runtime's schema. |
| 1141 | * @param inject — resolved dependency map (service name → intercept config). |
| 1142 | * @param runtime — the shared plugin runtime, or `null` for the root fiber. |
| 1143 | * @param getOuterStack — captures the caller stack for effect diagnostics. |
| 1144 | */ |
| 1145 | constructor(parent, config, inject, runtime, getOuterStack) { |
| 1146 | this.parent = parent; |
| 1147 | this.inject = inject; |
| 1148 | this.runtime = runtime; |
| 1149 | this._config = config; |
| 1150 | const collect = (dispose) => { |
| 1151 | this._disposables.push(dispose); |
| 1152 | }; |
| 1153 | if (runtime) { |
| 1154 | this.uid = parent.registry.counter; |
| 1155 | this.ctx = this.context = parent.extend({ fiber: this }); |
| 1156 | const injectEntries = Object.entries(this.inject); |
| 1157 | if (injectEntries.length) { |
| 1158 | this.ctx[Context.intercept] = Object.create(parent[Context.intercept]); |
| 1159 | for (const [name, config2] of injectEntries) { |
| 1160 | if (isNullable(config2)) continue; |
| 1161 | this.ctx[Context.intercept][name] = config2; |
| 1162 | } |
| 1163 | } |
| 1164 | this._runner = { |
| 1165 | epoch: INACTIVE, |
| 1166 | getOuterStack, |
| 1167 | execute: function() { |
| 1168 | if (isConstructor(runtime.callback)) { |
| 1169 | const instance = new runtime.callback(this.ctx, this.config); |
| 1170 | for (const hook of instance?.[symbols.initHooks] ?? []) hook(); |
| 1171 | return instance?.[symbols.init]?.(); |
| 1172 | } else return runtime.callback(this.ctx, this.config); |
| 1173 | }, |
| 1174 | collect |
| 1175 | }; |
| 1176 | this.dispose = parent.fiber.effect(() => { |
| 1177 | const remove = runtime.fibers.push(this); |
| 1178 | return async () => { |
| 1179 | this.uid = null; |
| 1180 | emitPluginDisposed(this.context, this); |
| 1181 | if (this.ctx.registry.has(runtime.callback)) { |
| 1182 | remove(); |
| 1183 | if (!runtime.fibers.length) this.ctx.registry.delete(runtime.callback); |
| 1184 | } |
| 1185 | this._setEpoch(INACTIVE); |
| 1186 | if (!this.inertia) this._updateState(() => { |
| 1187 | this.inertia = this._unload(); |
| 1188 | return 5; |
| 1189 | }); |
| 1190 | while (this.inertia) await this.inertia; |
| 1191 | }; |
| 1192 | }, "ctx.plugin()"); |
| 1193 | try { |
| 1194 | this.context.emit("internal/plugin", this); |
| 1195 | } catch (error) { |
| 1196 | Promise.resolve(this.dispose()).catch((reason) => this.ctx.logger.error(reason)); |
| 1197 | throw error; |
| 1198 | } |
| 1199 | if (this.uid !== null && parent.fiber.state !== 5) { |
| 1200 | for (const name of Object.keys(this.inject)) this._checkImpl(name); |
| 1201 | this._refresh(); |
| 1202 | } |
| 1203 | } else { |
| 1204 | this.uid = 0; |
| 1205 | this.ctx = this.context = parent; |
| 1206 | this.state = 2; |
| 1207 | this.store = /* @__PURE__ */ Object.create(null); |
| 1208 | this._runner = { |
| 1209 | epoch: "", |
| 1210 | getOuterStack, |
| 1211 | execute: () => { |
| 1212 | }, |
| 1213 | collect |
| 1214 | }; |
| 1215 | this.dispose = () => this.restart(); |
| 1216 | } |
| 1217 | } |
| 1218 | /** The plugin's display name, inherited from the nearest named ancestor, else `'root'`. */ |
| 1219 | get name() { |
| 1220 | let fiber = this; |
| 1221 | do { |
| 1222 | if (fiber.runtime?.name) return fiber.runtime.name; |
| 1223 | fiber = fiber.parent.fiber; |
| 1224 | } while (fiber !== fiber.parent.fiber); |
| 1225 | return "root"; |
| 1226 | } |
| 1227 | /** |
| 1228 | * Throw if the fiber has already been disposed. |
| 1229 | * |
| 1230 | * @returns nothing when the fiber is still active. |
| 1231 | * @throws {CordisError} `INACTIVE_EFFECT` when the fiber's uid has been cleared. |
| 1232 | */ |
| 1233 | assertActive() { |
| 1234 | if (this.uid !== null) return; |
| 1235 | throw new CordisError("INACTIVE_EFFECT"); |
| 1236 | } |
| 1237 | _execute(runner) { |
| 1238 | const oldEpoch = runner.epoch; |
| 1239 | return composeError((info) => { |
| 1240 | const safeCollect = (dispose) => { |
| 1241 | if (typeof dispose === "function") runner.collect(dispose); |
| 1242 | else if (!isNullable(dispose)) throw new TypeError("Invalid effect"); |
| 1243 | }; |
| 1244 | const effect = runner.execute.call(this); |
| 1245 | if (typeof effect === "function") return runner.collect(effect); |
| 1246 | else if (isNullable(effect)) { |
| 1247 | } else if (!isObject(effect)) throw new TypeError("Invalid effect"); |
| 1248 | else if ("then" in effect) return effect.then(safeCollect); |
| 1249 | else if (Symbol.iterator in effect) { |
| 1250 | info.error = /* @__PURE__ */ new Error(); |
| 1251 | const iter = effect[Symbol.iterator](); |
| 1252 | while (true) { |
| 1253 | const result = iter.next(); |
| 1254 | safeCollect(result.value); |
| 1255 | if (result.done) return; |
| 1256 | } |
| 1257 | } else if (Symbol.asyncIterator in effect) { |
| 1258 | const iter = effect[Symbol.asyncIterator](); |
| 1259 | return (async () => { |
| 1260 | await Promise.resolve(); |
| 1261 | info.error = /* @__PURE__ */ new Error(); |
| 1262 | while (true) { |
| 1263 | if (runner.epoch !== oldEpoch) return; |
| 1264 | const result = await iter.next(); |
| 1265 | safeCollect(result.value); |
| 1266 | if (result.done) return; |
| 1267 | } |
| 1268 | })(); |
| 1269 | } else throw new TypeError("Invalid effect"); |
| 1270 | }, runner.getOuterStack); |
| 1271 | } |
| 1272 | effect(execute, label = "anonymous") { |
| 1273 | this.assertActive(); |
| 1274 | if (this.state === 5) throw new CordisError("INACTIVE_EFFECT"); |
| 1275 | const disposables = []; |
| 1276 | let disposing = false; |
| 1277 | let disposalTask; |
| 1278 | const dispose = () => { |
| 1279 | if (disposing) return disposalTask; |
| 1280 | disposing = true; |
| 1281 | let task2; |
| 1282 | for (const disposable of disposables.splice(0).reverse()) if (task2) task2 = task2.then(() => runDisposable(disposable)); |
| 1283 | else { |
| 1284 | const result = runDisposable(disposable); |
| 1285 | if (isObject(result) && "then" in result) task2 = result; |
| 1286 | } |
| 1287 | return disposalTask = task2; |
| 1288 | }; |
| 1289 | const meta = { |
| 1290 | label, |
| 1291 | children: [] |
| 1292 | }; |
| 1293 | const runner = { |
| 1294 | execute, |
| 1295 | epoch: true, |
| 1296 | collect: (dispose2) => { |
| 1297 | disposables.push(dispose2); |
| 1298 | this._disposables.delete(dispose2); |
| 1299 | if (dispose2[symbols.effect]) meta.children.push(dispose2[symbols.effect]); |
| 1300 | }, |
| 1301 | getOuterStack: buildOuterStack() |
| 1302 | }; |
| 1303 | let task; |
| 1304 | let executing = true; |
| 1305 | let resolveSetup; |
| 1306 | let rejectSetup; |
| 1307 | let setupBarrier; |
| 1308 | let setupFailed = false; |
| 1309 | let inFlight; |
| 1310 | let removeWrapper = () => false; |
| 1311 | const waitForSetup = () => { |
| 1312 | setupBarrier ??= new Promise((resolve, reject) => { |
| 1313 | resolveSetup = resolve; |
| 1314 | rejectSetup = reject; |
| 1315 | }); |
| 1316 | return setupBarrier; |
| 1317 | }; |
| 1318 | const disposeAfter = (setup) => { |
| 1319 | return Promise.resolve(setup).then(() => dispose(), async (reason) => { |
| 1320 | await dispose(); |
| 1321 | throw reason; |
| 1322 | }); |
| 1323 | }; |
| 1324 | const finalizeDisposal = (callback) => { |
| 1325 | let result; |
| 1326 | try { |
| 1327 | result = callback(); |
| 1328 | } catch (error) { |
| 1329 | removeWrapper(); |
| 1330 | throw error; |
| 1331 | } |
| 1332 | if (isObject(result) && "then" in result) { |
| 1333 | const pending = Promise.resolve(result).finally(() => { |
| 1334 | removeWrapper(); |
| 1335 | if (inFlight === pending) inFlight = void 0; |
| 1336 | }); |
| 1337 | return inFlight = pending; |
| 1338 | } |
| 1339 | removeWrapper(); |
| 1340 | return result; |
| 1341 | }; |
| 1342 | const wrapper = defineProperty(() => { |
| 1343 | if (!runner.epoch) return setupFailed ? inFlight : void 0; |
| 1344 | runner.epoch = false; |
| 1345 | return finalizeDisposal(() => { |
| 1346 | if (executing) return disposeAfter(waitForSetup()); |
| 1347 | return task ? disposeAfter(task) : dispose(); |
| 1348 | }); |
| 1349 | }, symbols.effect, meta); |
| 1350 | effectInertia.set(wrapper, () => inFlight); |
| 1351 | removeWrapper = this._disposables.push(wrapper); |
| 1352 | try { |
| 1353 | task = this._execute(runner); |
| 1354 | } catch (reason) { |
| 1355 | executing = false; |
| 1356 | setupFailed = true; |
| 1357 | runner.epoch = false; |
| 1358 | let cleanup; |
| 1359 | try { |
| 1360 | cleanup = finalizeDisposal(dispose); |
| 1361 | } finally { |
| 1362 | rejectSetup?.(reason); |
| 1363 | } |
| 1364 | if (isObject(cleanup) && "then" in cleanup) cleanup.catch((error) => this.ctx.logger.error(error)); |
| 1365 | throw reason; |
| 1366 | } |
| 1367 | executing = false; |
| 1368 | if (setupBarrier) Promise.resolve(task).then(resolveSetup, rejectSetup); |
| 1369 | task?.catch(() => { |
| 1370 | if (!runner.epoch) return dispose(); |
| 1371 | return finalizeDisposal(dispose); |
| 1372 | }).catch((error) => this.ctx.logger.error(error)); |
| 1373 | const disposeAsync = () => { |
| 1374 | if (!runner.epoch) return; |
| 1375 | runner.epoch = false; |
| 1376 | return finalizeDisposal(dispose); |
| 1377 | }; |
| 1378 | wrapper.then = async (onFulfilled, onRejected) => { |
| 1379 | return Promise.resolve(task).then(() => disposeAsync).then(onFulfilled, onRejected); |
| 1380 | }; |
| 1381 | return wrapper; |
| 1382 | } |
| 1383 | /** |
| 1384 | * Return metadata for currently registered effects. |
| 1385 | * |
| 1386 | * @returns one {@link EffectMeta} tree per labeled live effect. |
| 1387 | */ |
| 1388 | getEffects() { |
| 1389 | return [...this._disposables].map((dispose) => dispose[symbols.effect]).filter(Boolean); |
| 1390 | } |
| 1391 | _getState() { |
| 1392 | if (this.uid === null) return 4; |
| 1393 | if (this._error) return 3; |
| 1394 | if (this._runner.epoch !== INACTIVE) return 2; |
| 1395 | return 0; |
| 1396 | } |
| 1397 | _updateState(callback) { |
| 1398 | const oldState = this.state; |
| 1399 | this.state = callback() ?? this._getState(); |
| 1400 | if (oldState === this.state) return; |
| 1401 | this.context.emit("internal/status", this, oldState); |
| 1402 | if (oldState !== 2 && this.state !== 2) return; |
| 1403 | for (const key of Reflect.ownKeys(this.ctx.reflect.store)) { |
| 1404 | const impl = this.ctx.reflect.store[key]; |
| 1405 | if (impl.fiber !== this) continue; |
| 1406 | this.ctx.reflect.notify([impl.name]); |
| 1407 | } |
| 1408 | } |
| 1409 | _checkImpl(name) { |
| 1410 | const impl = this.ctx.reflect._getImpl(name, true); |
| 1411 | if (!impl) return delete this._store[name]; |
| 1412 | try { |
| 1413 | if (impl.check && !impl.check.call(getTraceable(this.ctx, impl.value))) return delete this._store[name]; |
| 1414 | } catch (error) { |
| 1415 | impl.fiber.ctx.logger.error(error); |
| 1416 | return delete this._store[name]; |
| 1417 | } |
| 1418 | this._store[name] = impl; |
| 1419 | } |
| 1420 | _refresh() { |
| 1421 | let epoch = false; |
| 1422 | epoch = ""; |
| 1423 | for (const name of Object.keys(this.inject)) { |
| 1424 | const impl = this._store[name]; |
| 1425 | if (!impl) { |
| 1426 | epoch = INACTIVE; |
| 1427 | break; |
| 1428 | } |
| 1429 | epoch += ":" + impl.fiber.uid; |
| 1430 | } |
| 1431 | this._setEpoch(epoch); |
| 1432 | } |
| 1433 | _setEpoch(epoch) { |
| 1434 | const oldEpoch = this._runner.epoch; |
| 1435 | if (epoch === oldEpoch) return; |
| 1436 | this._runner.epoch = epoch; |
| 1437 | if (this.inertia) return; |
| 1438 | this._updateState(() => { |
| 1439 | if (epoch !== INACTIVE && oldEpoch === INACTIVE) { |
| 1440 | this.inertia = this._reload(); |
| 1441 | return 1; |
| 1442 | } else { |
| 1443 | this.inertia = this._unload(); |
| 1444 | return 5; |
| 1445 | } |
| 1446 | }); |
| 1447 | } |
| 1448 | _resolveConfig(config) { |
| 1449 | config = this.context.waterfall(this, "internal/config", config, () => config); |
| 1450 | return this.runtime ? resolveConfig(this.runtime, config) : config; |
| 1451 | } |
| 1452 | async _reload() { |
| 1453 | this.store = { ...this._store }; |
| 1454 | const oldEpoch = this._runner.epoch; |
| 1455 | try { |
| 1456 | await Promise.resolve(); |
| 1457 | if (this._runner.epoch === oldEpoch) { |
| 1458 | this.config = this._resolveConfig(this._config); |
| 1459 | await this._execute(this._runner); |
| 1460 | this._error = void 0; |
| 1461 | } |
| 1462 | } catch (reason) { |
| 1463 | this.ctx.logger.error(reason); |
| 1464 | this._error = reason; |
| 1465 | this._runner.epoch = INACTIVE; |
| 1466 | } |
| 1467 | this._updateState(() => { |
| 1468 | if (this._runner.epoch === oldEpoch) this.inertia = void 0; |
| 1469 | else { |
| 1470 | this.inertia = this._unload(); |
| 1471 | return 5; |
| 1472 | } |
| 1473 | }); |
| 1474 | } |
| 1475 | async _unload() { |
| 1476 | await Promise.all(this._disposables.clear().map(async (dispose) => { |
| 1477 | try { |
| 1478 | await composeError(async (info) => { |
| 1479 | await Promise.resolve(); |
| 1480 | info.error = /* @__PURE__ */ new Error(); |
| 1481 | await runDisposable(dispose); |
| 1482 | }, this._runner.getOuterStack); |
| 1483 | } catch (reason) { |
| 1484 | this.ctx.logger.error(reason); |
| 1485 | } |
| 1486 | })); |
| 1487 | this.store = void 0; |
| 1488 | this._updateState(() => { |
| 1489 | if (this._runner.epoch === INACTIVE) this.inertia = void 0; |
| 1490 | else { |
| 1491 | this.inertia = this._reload(); |
| 1492 | return 1; |
| 1493 | } |
| 1494 | }); |
| 1495 | } |
| 1496 | /** |
| 1497 | * Wait for current lifecycle work and rethrow startup errors. |
| 1498 | * |
| 1499 | * @returns this fiber, once it has settled into a stable state. |
| 1500 | * @throws the config-validation or plugin-startup error, if any. |
| 1501 | */ |
| 1502 | async await() { |
| 1503 | while (this.inertia) await this.inertia; |
| 1504 | if (this._error) throw this._error; |
| 1505 | return this; |
| 1506 | } |
| 1507 | /** |
| 1508 | * Dispose and immediately reload this plugin with its current config. |
| 1509 | * |
| 1510 | * @returns a promise resolving once the reload settled. |
| 1511 | * @throws {CordisError} `INACTIVE_EFFECT` when the fiber is already disposed. |
| 1512 | */ |
| 1513 | async restart() { |
| 1514 | this.assertActive(); |
| 1515 | this._setEpoch(INACTIVE); |
| 1516 | this._refresh(); |
| 1517 | await this.await(); |
| 1518 | } |
| 1519 | /** |
| 1520 | * Validate and apply new config, then restart the plugin. |
| 1521 | * |
| 1522 | * Runs the `internal/update` waterfall first, so update hooks (and HMR) |
| 1523 | * can veto or replace the restart. |
| 1524 | * |
| 1525 | * @param config — the new raw config; validated before anything restarts. |
| 1526 | * @param noSave — hint for persistence hooks not to write the change back. |
| 1527 | * @returns nothing; the restart runs behind the `internal/update` waterfall. |
| 1528 | * @throws {ValidationError} when the new config fails validation. |
| 1529 | */ |
| 1530 | update(config, noSave = false) { |
| 1531 | this.assertActive(); |
| 1532 | this._config = config; |
| 1533 | if (this.state !== 2) { |
| 1534 | this._error = void 0; |
| 1535 | this._setEpoch(INACTIVE); |
| 1536 | this._refresh(); |
| 1537 | return; |
| 1538 | } |
| 1539 | config = this._resolveConfig(config); |
| 1540 | this.context.waterfall(this, "internal/update", config, noSave, () => { |
| 1541 | this.config = config; |
| 1542 | this._error = void 0; |
| 1543 | return this.restart(); |
| 1544 | }); |
| 1545 | } |
| 1546 | }; |
| 1547 | function isApplicable(object) { |
| 1548 | return object && typeof object === "object" && typeof object.apply === "function"; |
| 1549 | } |
| 1550 | function Inject(name, config) { |
| 1551 | return function(value, decorator) { |
| 1552 | if (decorator.kind === "class") { |
| 1553 | if (!Object.hasOwn(value, "inject")) { |
| 1554 | defineProperty(value, "inject", Object.create(Object.getPrototypeOf(value).inject ?? null)); |
| 1555 | defineProperty(value.inject, symbols.checkProto, true); |
| 1556 | } |
| 1557 | value.inject[name] = config; |
| 1558 | } else if (decorator.kind === "method") { |
| 1559 | const inject = (value[symbols.metadata] ??= {}).inject ??= /* @__PURE__ */ Object.create(null); |
| 1560 | inject[name] = config; |
| 1561 | decorator.addInitializer(function() { |
| 1562 | const property = this[symbols.tracker]?.property; |
| 1563 | (this[symbols.initHooks] ??= []).push(() => { |
| 1564 | this.ctx.inject(inject, (ctx) => { |
| 1565 | return value.call(property ? withProps(this, { [property]: ctx }) : this); |
| 1566 | }); |
| 1567 | }); |
| 1568 | }); |
| 1569 | } else throw new Error("@Inject() can only be used on class or class methods"); |
| 1570 | }; |
| 1571 | } |
| 1572 | (function(Inject2) { |
| 1573 | function resolve(inject, result = /* @__PURE__ */ Object.create(null)) { |
| 1574 | if (!inject) return result; |
| 1575 | if (Array.isArray(inject)) for (const name of inject) result[name] = null; |
| 1576 | else if (Reflect.has(inject, symbols.checkProto)) { |
| 1577 | Object.assign(result, resolve(Object.getPrototypeOf(inject))); |
| 1578 | for (const name of Object.keys(inject)) result[name] = inject[name] ?? null; |
| 1579 | } else for (const name of Object.keys(inject)) result[name] = inject[name] ?? null; |
| 1580 | return result; |
| 1581 | } |
| 1582 | Inject2.resolve = resolve; |
| 1583 | })(Inject || (Inject = {})); |
| 1584 | var RegistryService = class { |
| 1585 | ctx; |
| 1586 | _counter = 0; |
| 1587 | _internal = /* @__PURE__ */ new Map(); |
| 1588 | constructor(ctx) { |
| 1589 | this.ctx = ctx; |
| 1590 | defineProperty(this, symbols.tracker, { |
| 1591 | property: "ctx", |
| 1592 | noShadow: true |
| 1593 | }); |
| 1594 | } |
| 1595 | /** Allocate the next fiber uid (increments on every read). */ |
| 1596 | get counter() { |
| 1597 | return ++this._counter; |
| 1598 | } |
| 1599 | /** Number of registered plugin runtimes. */ |
| 1600 | get size() { |
| 1601 | return this._internal.size; |
| 1602 | } |
| 1603 | /** |
| 1604 | * Resolve a supported plugin shape to its executable callback. |
| 1605 | * |
| 1606 | * @param plugin — a function, class, or `{ apply }` object plugin. |
| 1607 | * @returns the callback identifying the plugin, or `undefined` if invalid. |
| 1608 | */ |
| 1609 | resolve(plugin) { |
| 1610 | try { |
| 1611 | if (typeof plugin === "function") return plugin; |
| 1612 | if (isApplicable(plugin)) return plugin.apply; |
| 1613 | } catch { |
| 1614 | } |
| 1615 | } |
| 1616 | /** |
| 1617 | * Look up the runtime record for a plugin. |
| 1618 | * |
| 1619 | * @param plugin — any supported plugin shape. |
| 1620 | * @returns the runtime, or `undefined` when the plugin is not registered. |
| 1621 | */ |
| 1622 | get(plugin) { |
| 1623 | const key = this.resolve(plugin); |
| 1624 | return key && this._internal.get(key); |
| 1625 | } |
| 1626 | /** |
| 1627 | * Check whether a plugin has a registered runtime. |
| 1628 | * |
| 1629 | * @param plugin — any supported plugin shape. |
| 1630 | * @returns `true` when at least one fiber of the plugin exists. |
| 1631 | */ |
| 1632 | has(plugin) { |
| 1633 | const key = this.resolve(plugin); |
| 1634 | return !!key && this._internal.has(key); |
| 1635 | } |
| 1636 | /** |
| 1637 | * Dispose every running fiber for a plugin and remove its runtime record. |
| 1638 | * |
| 1639 | * @param plugin — any supported plugin shape. |
| 1640 | * @returns the removed runtime, or `undefined` when none was registered. |
| 1641 | */ |
| 1642 | delete(plugin) { |
| 1643 | const key = this.resolve(plugin); |
| 1644 | const runtime = key && this._internal.get(key); |
| 1645 | if (!runtime) return; |
| 1646 | this._internal.delete(key); |
| 1647 | for (const fiber of runtime.fibers) fiber.dispose(); |
| 1648 | return runtime; |
| 1649 | } |
| 1650 | /** Iterate the registered plugin callbacks. */ |
| 1651 | keys() { |
| 1652 | return this._internal.keys(); |
| 1653 | } |
| 1654 | /** Iterate the registered plugin runtimes. */ |
| 1655 | values() { |
| 1656 | return this._internal.values(); |
| 1657 | } |
| 1658 | /** Iterate `[callback, runtime]` pairs. */ |
| 1659 | entries() { |
| 1660 | return this._internal.entries(); |
| 1661 | } |
| 1662 | /** |
| 1663 | * Visit every registered runtime. |
| 1664 | * |
| 1665 | * @param callback — receives each runtime and its identifying callback. |
| 1666 | */ |
| 1667 | forEach(callback) { |
| 1668 | return this._internal.forEach(callback); |
| 1669 | } |
| 1670 | /** |
| 1671 | * Start a callback once the requested dependencies are available. |
| 1672 | * |
| 1673 | * @param inject — required services, as an array or a name → config map. |
| 1674 | * @param callback — plugin body called with `(ctx, config)`. |
| 1675 | * @returns the fiber; awaiting it settles once loading finished. |
| 1676 | */ |
| 1677 | inject(inject, callback) { |
| 1678 | return this.plugin({ |
| 1679 | inject, |
| 1680 | apply: callback, |
| 1681 | name: callback.name |
| 1682 | }); |
| 1683 | } |
| 1684 | /** |
| 1685 | * Start a plugin in the current context and return its fiber. |
| 1686 | * |
| 1687 | * Creates (or reuses) the plugin's runtime record, then starts a new fiber |
| 1688 | * under the current context. Throws if `plugin` is not a supported shape or |
| 1689 | * if the current fiber is already disposed. |
| 1690 | * |
| 1691 | * @param plugin — a function, class, or `{ apply }` object plugin. |
| 1692 | * @param config — the plugin config, validated against its `Config` schema. |
| 1693 | * @param getOuterStack — captures the caller stack for effect diagnostics. |
| 1694 | * @returns the fiber; awaiting it settles once loading finished. |
| 1695 | */ |
| 1696 | plugin(plugin, config, getOuterStack = buildOuterStack()) { |
| 1697 | const callback = this.resolve(plugin); |
| 1698 | if (!callback) throw new Error('invalid plugin, expect function or object with an "apply" method, received ' + typeof plugin); |
| 1699 | this.ctx.fiber.assertActive(); |
| 1700 | let runtime = this._internal.get(callback); |
| 1701 | if (!runtime) { |
| 1702 | let name = plugin.name; |
| 1703 | if (name === "apply") name = void 0; |
| 1704 | runtime = { |
| 1705 | name, |
| 1706 | callback, |
| 1707 | fibers: new DisposableList(), |
| 1708 | Config: plugin.Config |
| 1709 | }; |
| 1710 | this._internal.set(callback, runtime); |
| 1711 | } |
| 1712 | const fiber = new Fiber(this.ctx, config, Inject.resolve(plugin.inject), runtime, getOuterStack); |
| 1713 | const wrapped = Object.create(fiber); |
| 1714 | wrapped.then = (onFulfilled, onRejected) => { |
| 1715 | return fiber.await().then(onFulfilled, onRejected); |
| 1716 | }; |
| 1717 | return wrapped; |
| 1718 | } |
| 1719 | }; |
| 1720 | var Context = class Context2 { |
| 1721 | /** Symbol key under which a disposer exposes its {@link EffectMeta} diagnostics tree. */ |
| 1722 | static effect = symbols.effect; |
| 1723 | /** Symbol key for a context's listener filter, consulted on every event dispatch. */ |
| 1724 | static filter = symbols.filter; |
| 1725 | /** Symbol key of the isolation map (see the `Context[symbols.isolate]` property). */ |
| 1726 | static isolate = symbols.isolate; |
| 1727 | /** Symbol key of the intercept map (see the `Context[symbols.intercept]` property). */ |
| 1728 | static intercept = symbols.intercept; |
| 1729 | /** |
| 1730 | * Returns true for Cordis context proxies and context prototypes. |
| 1731 | * |
| 1732 | * Works across realms and across multiple copies of cordis, because the |
| 1733 | * brand is keyed by a global symbol rather than by `instanceof`. |
| 1734 | * |
| 1735 | * @param value — the value to test. |
| 1736 | * @returns `true` if `value` is a Cordis context, narrowing its type. |
| 1737 | */ |
| 1738 | static is(value) { |
| 1739 | return !!value?.[Context2.is]; |
| 1740 | } |
| 1741 | static { |
| 1742 | Context2.is[Symbol.toPrimitive] = () => /* @__PURE__ */ Symbol.for("cordis.is"); |
| 1743 | Context2.prototype[Context2.is] = true; |
| 1744 | } |
| 1745 | /** Create the root context and install the built-in services. */ |
| 1746 | constructor() { |
| 1747 | this[symbols.isolate] = /* @__PURE__ */ Object.create(null); |
| 1748 | this[symbols.intercept] = /* @__PURE__ */ Object.create(null); |
| 1749 | const self = new Proxy(this, ReflectService.handler); |
| 1750 | this.root = self; |
| 1751 | this.baseUrl = void 0; |
| 1752 | this.fiber = new Fiber(self, {}, /* @__PURE__ */ Object.create(null), null, () => []); |
| 1753 | this.reflect = new ReflectService(self); |
| 1754 | this.registry = new RegistryService(self); |
| 1755 | this.events = new EventsService(self); |
| 1756 | this.logger = new LoggerService(self); |
| 1757 | this.fiber._disposables.clear(); |
| 1758 | return self; |
| 1759 | } |
| 1760 | [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() { |
| 1761 | return `Context <${this.fiber.name}>`; |
| 1762 | } |
| 1763 | /** |
| 1764 | * Create a child context with extra metadata on top of the current scope. |
| 1765 | * |
| 1766 | * The child prototypally inherits every property of this context; own |
| 1767 | * properties of `meta` shadow the inherited ones. The parent is not mutated. |
| 1768 | * |
| 1769 | * @param meta — own properties (including symbol keys) to define on the child. |
| 1770 | * @returns a child context inheriting from this one. |
| 1771 | */ |
| 1772 | extend(meta = {}) { |
| 1773 | const shadow = Reflect.getOwnPropertyDescriptor(this, symbols.shadow)?.value; |
| 1774 | const self = Object.create(getTraceable(this, this)); |
| 1775 | for (const prop of Reflect.ownKeys(meta)) Object.defineProperty(self, prop, Reflect.getOwnPropertyDescriptor(meta, prop)); |
| 1776 | if (!shadow) return self; |
| 1777 | return Object.assign(Object.create(self), { [symbols.shadow]: shadow }); |
| 1778 | } |
| 1779 | /** |
| 1780 | * Create a child context with an independent service scope for `name`. |
| 1781 | * |
| 1782 | * Below the returned context, reads and writes of the service `name` |
| 1783 | * resolve against the new label instead of the parent's, so a different |
| 1784 | * implementation can be provided without affecting the parent scope. |
| 1785 | * Passing the same `label` to two `isolate()` calls joins their scopes. |
| 1786 | * |
| 1787 | * @param name — the service name to isolate. |
| 1788 | * @param label — scope label to join; defaults to a fresh unique symbol. |
| 1789 | * @returns a child context whose `name` service resolves in the new scope. |
| 1790 | */ |
| 1791 | isolate(name, label) { |
| 1792 | const shadow = Object.create(this[symbols.isolate]); |
| 1793 | shadow[name] = label ?? Symbol(name); |
| 1794 | return this.extend({ [symbols.isolate]: shadow }); |
| 1795 | } |
| 1796 | intercept(name, config) { |
| 1797 | const intercept = Object.create(this[symbols.intercept]); |
| 1798 | intercept[name] = config; |
| 1799 | return this.extend({ [symbols.intercept]: intercept }); |
| 1800 | } |
| 1801 | }; |
| 1802 | var Service = class Service2 { |
| 1803 | ctx; |
| 1804 | /** Symbol key of an instance method run after construction (class plugins). */ |
| 1805 | static init = symbols.init; |
| 1806 | /** Symbol key of the availability predicate passed to `ctx.provide()`. */ |
| 1807 | static check = symbols.check; |
| 1808 | /** Symbol key of the phantom intercept-config type parameter. */ |
| 1809 | static config = symbols.config; |
| 1810 | /** Symbol key of the call body making a service callable (e.g. `ctx.logger()`). */ |
| 1811 | static invoke = symbols.invoke; |
| 1812 | /** Symbol key of the helper deriving an extended service instance. */ |
| 1813 | static extend = symbols.extend; |
| 1814 | /** Symbol key of the tracker metadata used for context tracing. */ |
| 1815 | static tracker = symbols.tracker; |
| 1816 | /** Symbol key of the intercept-config resolution helper below. */ |
| 1817 | static resolveConfig = symbols.resolveConfig; |
| 1818 | /** The service name this instance is registered under. */ |
| 1819 | name; |
| 1820 | /** |
| 1821 | * Register this instance as `name` in the current context. |
| 1822 | * |
| 1823 | * Calls `ctx.reflect.provide(name, this, this[Service.check])`, so the |
| 1824 | * service is unregistered automatically when the owning fiber unloads. |
| 1825 | * Services with a `[Service.invoke]` body return a callable instance. |
| 1826 | * |
| 1827 | * @param ctx — the context to register in (stored as `this.ctx`). |
| 1828 | * @param name — the service name; defaults to the static `provide` field. |
| 1829 | */ |
| 1830 | constructor(ctx, name) { |
| 1831 | this.ctx = ctx; |
| 1832 | name ??= this.constructor["provide"]; |
| 1833 | let self = this; |
| 1834 | const tracker = { |
| 1835 | associate: name, |
| 1836 | property: "ctx" |
| 1837 | }; |
| 1838 | if (self[symbols.invoke]) self = createCallable(name, joinPrototype(Object.getPrototypeOf(this), Function.prototype), tracker); |
| 1839 | self.ctx = ctx; |
| 1840 | self.name = name; |
| 1841 | defineProperty(self, symbols.tracker, tracker); |
| 1842 | self.ctx.reflect.provide(name, self, this[symbols.check]); |
| 1843 | return self; |
| 1844 | } |
| 1845 | [symbols.filter](ctx) { |
| 1846 | return ctx[symbols.isolate][this.name] === this.ctx[symbols.isolate][this.name]; |
| 1847 | } |
| 1848 | [symbols.extend](props) { |
| 1849 | let self; |
| 1850 | if (this[Service2.invoke]) self = createCallable(this.name, this, this[symbols.tracker]); |
| 1851 | else self = Object.create(this); |
| 1852 | return Object.assign(self, props); |
| 1853 | } |
| 1854 | /** |
| 1855 | * Merge intercept config from ancestors with optional base and head values. |
| 1856 | * |
| 1857 | * Entries added closer to the root apply first; `base` is prepended and |
| 1858 | * `head` appended. Uses `Config.merge` when the service declares one, |
| 1859 | * otherwise a shallow `Object.assign`. |
| 1860 | * |
| 1861 | * @param base — lowest-precedence config merged before all intercepts. |
| 1862 | * @param head — highest-precedence config merged after all intercepts. |
| 1863 | * @returns the merged config. |
| 1864 | */ |
| 1865 | [symbols.resolveConfig](base, head) { |
| 1866 | let intercept = this.ctx[Context.intercept]; |
| 1867 | const configs = []; |
| 1868 | while (this.name in intercept) { |
| 1869 | if (Object.hasOwn(intercept, this.name)) configs.unshift(intercept[this.name]); |
| 1870 | intercept = Object.getPrototypeOf(intercept); |
| 1871 | } |
| 1872 | if (base) configs.unshift(base); |
| 1873 | if (head) configs.push(head); |
| 1874 | if (this["Config"]?.merge) return this["Config"].merge(...configs); |
| 1875 | else return Object.assign({}, ...configs); |
| 1876 | } |
| 1877 | static [Symbol.hasInstance](instance) { |
| 1878 | if (!instance) return false; |
| 1879 | let constructor = instance.constructor; |
| 1880 | while (constructor) { |
| 1881 | constructor = constructor.prototype?.constructor; |
| 1882 | if (constructor === this) return true; |
| 1883 | constructor &&= Object.getPrototypeOf(constructor); |
| 1884 | } |
| 1885 | return false; |
| 1886 | } |
| 1887 | }; |
| 1888 | |
| 1889 | // src/shims/owned.ts |
| 1890 | function describeError(error) { |
| 1891 | return error instanceof Error ? `${error.name}: ${error.message}` : String(error); |
| 1892 | } |
| 1893 | var OwnedRegistrations = class { |
| 1894 | /** Admitted entries by core handle. */ |
| 1895 | byHandle = /* @__PURE__ */ new Map(); |
| 1896 | rpc; |
| 1897 | kind; |
| 1898 | /** The owner's own index of this kind, for the leak report at deactivation. */ |
| 1899 | ownedBy; |
| 1900 | warn; |
| 1901 | constructor(rpc, kind, ownedBy, warn) { |
| 1902 | this.rpc = rpc; |
| 1903 | this.kind = kind; |
| 1904 | this.ownedBy = ownedBy; |
| 1905 | this.warn = warn; |
| 1906 | } |
| 1907 | /** Called inside the owner's effect; returns the effect's cleanup. */ |
| 1908 | add(entry, spec) { |
| 1909 | const owner = entry.owner; |
| 1910 | if (owner.state !== "activating" && owner.state !== "active") throw new Error("registration owner was disposed"); |
| 1911 | const registration = this.rpc.request("registry/register", { owner: owner.ref, ...owner.scope ? { scope: owner.scope } : {}, kind: this.kind, spec }).then( |
| 1912 | (result) => { |
| 1913 | if (typeof result?.handle === "number") { |
| 1914 | entry.handle = result.handle; |
| 1915 | if (entry.disposed || owner.state !== "activating" && owner.state !== "active") { |
| 1916 | return this.unregister(entry); |
| 1917 | } |
| 1918 | this.ownedBy(owner).set(result.handle, entry); |
| 1919 | this.byHandle.set(result.handle, entry); |
| 1920 | } else { |
| 1921 | const reason = typeof result?.refused === "string" ? result.refused : "refused without a reason"; |
| 1922 | owner.refusals.push(`${this.kind} \`${entry.name}\` refused: ${reason}`); |
| 1923 | if (owner.state === "active") this.warn(`${this.kind} \`${entry.name}\` refused: ${reason}`, owner); |
| 1924 | } |
| 1925 | }, |
| 1926 | (error) => { |
| 1927 | owner.refusals.push(`${this.kind} \`${entry.name}\` registration failed: ${describeError(error)}`); |
| 1928 | } |
| 1929 | ).finally(() => owner.pendingRegistrations.delete(registration)); |
| 1930 | owner.pendingRegistrations.add(registration); |
| 1931 | return () => { |
| 1932 | if (entry.disposed) return; |
| 1933 | entry.disposed = true; |
| 1934 | if (entry.handle !== void 0) void this.unregister(entry); |
| 1935 | }; |
| 1936 | } |
| 1937 | /** Undo exactly this entry. The core revokes first, so a failure here is expected after revocation. */ |
| 1938 | unregister(entry) { |
| 1939 | const handle = entry.handle; |
| 1940 | const owner = entry.owner; |
| 1941 | this.ownedBy(owner).delete(handle); |
| 1942 | this.byHandle.delete(handle); |
| 1943 | const cleanup = this.rpc.request("registry/unregister", { owner: owner.ref, handle }).then(() => void 0, () => void 0).finally(() => owner.pendingRegistrations.delete(cleanup)); |
| 1944 | owner.pendingRegistrations.add(cleanup); |
| 1945 | return cleanup; |
| 1946 | } |
| 1947 | /** Drop the host's index of everything `owner` registered (after its teardown). */ |
| 1948 | forget(owner) { |
| 1949 | for (const entry of this.ownedBy(owner).values()) this.byHandle.delete(entry.handle); |
| 1950 | } |
| 1951 | }; |
| 1952 | |
| 1953 | // src/shims/skills.ts |
| 1954 | var MAX_SKILL_ROOTS_PER_OWNER = 8; |
| 1955 | var MAX_SKILL_ROOTS_PER_HOST = 64; |
| 1956 | function normalizeSkillRoot(value) { |
| 1957 | if (typeof value !== "object" || value === null || Array.isArray(value) || Object.keys(value).some((key) => key !== "path")) throw new TypeError("skill root supports only path"); |
| 1958 | const { path } = value; |
| 1959 | if (typeof path !== "string" || !path || Buffer.byteLength(path, "utf8") > 512 || /[\\:\u0000-\u001f\u007f-\u009f]/u.test(path) || path.split("/").some((part) => !part || part === "." || part === "..")) { |
| 1960 | throw new TypeError("skill root path must be a bounded bundle-relative path with normal slash-separated components"); |
| 1961 | } |
| 1962 | return Object.freeze({ path }); |
| 1963 | } |
| 1964 | var SkillRoots = class { |
| 1965 | registrations; |
| 1966 | owners = /* @__PURE__ */ new Map(); |
| 1967 | count = 0; |
| 1968 | constructor(rpc, ownedBy, warn) { |
| 1969 | this.registrations = new OwnedRegistrations(rpc, "skill_root", ownedBy, warn); |
| 1970 | } |
| 1971 | register(owner, definition) { |
| 1972 | if (owner.state !== "activating" && owner.state !== "active") throw new Error("skill owner is not live"); |
| 1973 | const { path } = normalizeSkillRoot(definition); |
| 1974 | const roots = this.owners.get(owner) ?? /* @__PURE__ */ new Map(); |
| 1975 | if (roots.has(path)) throw new Error("skill root is already registered; dispose it before registering it again"); |
| 1976 | if (roots.size >= MAX_SKILL_ROOTS_PER_OWNER || this.count >= MAX_SKILL_ROOTS_PER_HOST) throw new RangeError("skill root owner or host registration limit reached"); |
| 1977 | const undo = this.registrations.add({ owner, name: path, disposed: false }, { name: path, description: "" }); |
| 1978 | const dispose = () => { |
| 1979 | if (roots.get(path) !== dispose) return; |
| 1980 | roots.delete(path); |
| 1981 | this.count--; |
| 1982 | if (!roots.size) this.owners.delete(owner); |
| 1983 | undo(); |
| 1984 | }; |
| 1985 | roots.set(path, dispose); |
| 1986 | this.owners.set(owner, roots); |
| 1987 | this.count++; |
| 1988 | return dispose; |
| 1989 | } |
| 1990 | forget(owner) { |
| 1991 | for (const dispose of [...this.owners.get(owner)?.values() ?? []]) dispose(); |
| 1992 | this.registrations.forget(owner); |
| 1993 | } |
| 1994 | }; |
| 1995 | function defineSkillsService(host) { |
| 1996 | class SkillsShim extends Service { |
| 1997 | constructor(ctx) { |
| 1998 | super(ctx, "skills"); |
| 1999 | } |
| 2000 | registerRoot(definition) { |
| 2001 | const ctx = this.ctx; |
| 2002 | const owner = host.ownerOf(ctx); |
| 2003 | if (!owner) throw new Error("skills.registerRoot called outside an extension owner"); |
| 2004 | const root = normalizeSkillRoot(definition); |
| 2005 | return ctx.effect(() => host.skillRoots.register(owner, root), `skills.registerRoot(${JSON.stringify(root.path)})`); |
| 2006 | } |
| 2007 | } |
| 2008 | Object.freeze(SkillsShim.prototype); |
| 2009 | return SkillsShim; |
| 2010 | } |
| 2011 | export { |
| 2012 | MAX_SKILL_ROOTS_PER_HOST, |
| 2013 | MAX_SKILL_ROOTS_PER_OWNER, |
| 2014 | SkillRoots, |
| 2015 | defineSkillsService, |
| 2016 | normalizeSkillRoot |
| 2017 | }; |
| 2018 |