@forgeax/engine-plugin 0.0.0-dev.8d955ade1c79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,989 @@
1
+ import { Service, Inject, composeError, Context } from '@deepseek-ai/cordis';
2
+ import { createCapabilityResolver } from '@forgeax/engine-tool-runtime';
3
+
4
+ // ../../node_modules/.pnpm/@deepseek-ai+cordis-plugin-loader@1.0.2_patch_hash=6e77a3b82171afbdac249c0e31290cc06386_1a14a168fdebad14b739afc92f305e1c/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js
5
+
6
+ // ../../node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.2/node_modules/@deepseek-ai/cosmokit/lib/index.js
7
+ function isNullable(value) {
8
+ return value === null || value === void 0;
9
+ }
10
+ function isNonNullable(value) {
11
+ return !isNullable(value);
12
+ }
13
+ function mapValues(object, transform) {
14
+ return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
15
+ }
16
+ function defineProperty(object, key, value) {
17
+ return Object.defineProperty(object, key, {
18
+ writable: true,
19
+ value,
20
+ enumerable: false
21
+ });
22
+ }
23
+ function is(type, value) {
24
+ if (arguments.length === 1) return (value2) => is(type, value2);
25
+ return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
26
+ }
27
+ function isArrayBufferLike(value) {
28
+ return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
29
+ }
30
+ function isArrayBufferSource(value) {
31
+ return isArrayBufferLike(value) || ArrayBuffer.isView(value);
32
+ }
33
+ var Binary;
34
+ (function(Binary2) {
35
+ Binary2.is = isArrayBufferLike;
36
+ Binary2.isSource = isArrayBufferSource;
37
+ function fromSource(source) {
38
+ if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
39
+ else return source;
40
+ }
41
+ Binary2.fromSource = fromSource;
42
+ function toBase64(source) {
43
+ source = fromSource(source);
44
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
45
+ let binary = "";
46
+ const bytes = new Uint8Array(source);
47
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
48
+ return btoa(binary);
49
+ }
50
+ Binary2.toBase64 = toBase64;
51
+ function fromBase64(source) {
52
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
53
+ return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
54
+ }
55
+ Binary2.fromBase64 = fromBase64;
56
+ function toHex(source) {
57
+ source = fromSource(source);
58
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
59
+ return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
60
+ }
61
+ Binary2.toHex = toHex;
62
+ function fromHex(source) {
63
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
64
+ const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
65
+ const buffer = [];
66
+ for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
67
+ return Uint8Array.from(buffer).buffer;
68
+ }
69
+ Binary2.fromHex = fromHex;
70
+ })(Binary || (Binary = {}));
71
+ Binary.fromBase64;
72
+ Binary.toBase64;
73
+ Binary.fromHex;
74
+ Binary.toHex;
75
+ function deepEqual(a, b, strict) {
76
+ if (a === b) return true;
77
+ if (isNullable(a) && isNullable(b)) return true;
78
+ if (typeof a !== typeof b) return false;
79
+ if (typeof a !== "object") return false;
80
+ if (!a || !b) return false;
81
+ function check(test, then) {
82
+ return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
83
+ }
84
+ return check(Array.isArray, (a2, b2) => a2.length === b2.length && a2.every((item, index) => deepEqual(item, b2[index]))) ?? check(is("Date"), (a2, b2) => a2.valueOf() === b2.valueOf()) ?? check(is("RegExp"), (a2, b2) => a2.source === b2.source && a2.flags === b2.flags) ?? check(isArrayBufferLike, (a2, b2) => {
85
+ if (a2.byteLength !== b2.byteLength) return false;
86
+ const viewA = new Uint8Array(a2);
87
+ const viewB = new Uint8Array(b2);
88
+ for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
89
+ return true;
90
+ }) ?? Object.keys({
91
+ ...a,
92
+ ...b
93
+ }).every((key) => deepEqual(a[key], b[key]));
94
+ }
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/.pnpm/@deepseek-ai+cordis-plugin-loader@1.0.2_patch_hash=6e77a3b82171afbdac249c0e31290cc06386_1a14a168fdebad14b739afc92f305e1c/node_modules/@deepseek-ai/cordis-plugin-loader/lib/index.js
166
+ var ModuleLoader;
167
+ (function(ModuleLoader2) {
168
+ function fromInternal() {
169
+ }
170
+ ModuleLoader2.fromInternal = fromInternal;
171
+ })(ModuleLoader || (ModuleLoader = {}));
172
+ var EntryGroup = class {
173
+ ctx;
174
+ tree;
175
+ static key = /* @__PURE__ */ Symbol.for("cordis.group");
176
+ data = [];
177
+ constructor(ctx, tree) {
178
+ this.ctx = ctx;
179
+ this.tree = tree;
180
+ const entry = ctx.fiber.entry;
181
+ if (entry) entry.subgroup = this;
182
+ }
183
+ get context() {
184
+ return this.ctx;
185
+ }
186
+ async create(options) {
187
+ const id = this.tree.ensureId(options);
188
+ const existing = this.tree.store[id];
189
+ const entry = existing ?? (this.tree.store[id] = new Entry(this.ctx.loader));
190
+ const previousParent = entry.parent;
191
+ entry.parent = this;
192
+ try {
193
+ await entry.update(options, true, true);
194
+ } catch (error) {
195
+ if (existing) entry.parent = previousParent;
196
+ else delete this.tree.store[id];
197
+ throw error;
198
+ }
199
+ return entry.id;
200
+ }
201
+ unlink(options) {
202
+ const config = this.data;
203
+ const index = config.indexOf(options);
204
+ if (index >= 0) config.splice(index, 1);
205
+ }
206
+ async remove(id, isDispose = false) {
207
+ const entry = this.tree.store[id];
208
+ if (!entry) return;
209
+ await entry._dispose();
210
+ if (!isDispose) this.unlink(entry.options);
211
+ delete this.tree.store[id];
212
+ this.context.emit("loader/partial-dispose", entry, entry.options, false);
213
+ }
214
+ async update(config) {
215
+ const oldConfig = this.data;
216
+ const seen = /* @__PURE__ */ new Set();
217
+ for (const options of config) {
218
+ const id = this.tree.ensureId(options);
219
+ if (seen.has(id)) throw new TypeError(`duplicate loader entry id: ${id}`);
220
+ seen.add(id);
221
+ }
222
+ const oldMap = Object.fromEntries(oldConfig.map((options) => [options.id, options]));
223
+ const newMap = Object.fromEntries(config.map((options) => [options.id, options]));
224
+ try {
225
+ const outcomes = await Promise.allSettled(config.map((options) => this.create(options)));
226
+ if (this.ctx.fiber.uid === null) return;
227
+ const failures = outcomes.filter((outcome) => outcome.status === "rejected").map((outcome) => outcome.reason);
228
+ if (failures.length === 1) throw failures[0];
229
+ if (failures.length > 1) throw new AggregateError(failures, "loader entries failed to apply");
230
+ for (const id of Object.keys(oldMap)) if (!newMap[id]) await this.remove(id, true);
231
+ this.data = config;
232
+ } catch (error) {
233
+ const rollbackErrors = [];
234
+ for (const id of Object.keys(newMap).reverse()) {
235
+ if (oldMap[id]) continue;
236
+ try {
237
+ await this.remove(id, true);
238
+ } catch (rollbackError) {
239
+ rollbackErrors.push(rollbackError);
240
+ }
241
+ }
242
+ for (const options of oldConfig) try {
243
+ await this.create(options);
244
+ } catch (rollbackError) {
245
+ rollbackErrors.push(rollbackError);
246
+ }
247
+ this.data = oldConfig;
248
+ if (rollbackErrors.length) throw new AggregateError([error, ...rollbackErrors], "loader entry rollback failed");
249
+ throw error;
250
+ }
251
+ }
252
+ async stop() {
253
+ for (const options of this.data) await this.remove(options.id, true);
254
+ }
255
+ };
256
+ var Group = class extends EntryGroup {
257
+ ctx;
258
+ config;
259
+ static initial = [];
260
+ static [EntryGroup.key] = true;
261
+ constructor(ctx, config) {
262
+ super(ctx, ctx.fiber.entry.parent.tree);
263
+ this.ctx = ctx;
264
+ this.config = config;
265
+ ctx.on("internal/update", (config2) => this.update(config2));
266
+ }
267
+ async *[Service.init]() {
268
+ yield () => this.stop();
269
+ await this.update(this.config);
270
+ }
271
+ };
272
+ var __rewriteRelativeImportExtension = function(path, preserveJsx) {
273
+ if (typeof path === "string" && /^\.\.?\//.test(path)) return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
274
+ return tsx ? ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
275
+ });
276
+ return path;
277
+ };
278
+ var EntryTree = class EntryTree2 {
279
+ static sep = ":";
280
+ ctx;
281
+ enableLogs;
282
+ root;
283
+ store = /* @__PURE__ */ Object.create(null);
284
+ constructor(ctx) {
285
+ this.ctx = ctx.extend({ baseUrl: ctx.baseUrl });
286
+ this.root = new EntryGroup(this.ctx, this);
287
+ const entry = this.ctx.fiber.entry;
288
+ if (entry) entry.subtree = this;
289
+ }
290
+ get context() {
291
+ return this.ctx;
292
+ }
293
+ /** Iterate entries in this tree and any nested subtrees. */
294
+ *entries() {
295
+ for (const entry of Object.values(this.store)) {
296
+ yield entry;
297
+ if (!entry.subtree) continue;
298
+ yield* entry.subtree.entries();
299
+ }
300
+ }
301
+ /** Return pending import and lifecycle tasks owned by this tree. */
302
+ getTasks() {
303
+ return [...this.entries()].map((entry) => entry._initTask || entry.fiber?.inertia).filter(isNonNullable);
304
+ }
305
+ /**
306
+ * Wait until this tree has no active import or lifecycle tasks.
307
+ * @throws a settled fiber failure, or an aggregate when several fibers failed.
308
+ */
309
+ async await() {
310
+ while (true) {
311
+ const tasks = this.getTasks();
312
+ if (tasks.length) {
313
+ await Promise.allSettled(tasks);
314
+ continue;
315
+ }
316
+ const failures = (await Promise.allSettled([...this.entries()].map((entry) => entry._await()))).filter((outcome) => outcome.status === "rejected").map((outcome) => outcome.reason);
317
+ if (failures.length === 1) throw failures[0];
318
+ if (failures.length > 1) throw new AggregateError(failures, "loader fibers failed");
319
+ this.ctx.reflect.notify(["loader"]);
320
+ if (!this.getTasks().length) return;
321
+ }
322
+ }
323
+ ensureId(options) {
324
+ if (!options.id) do
325
+ options.id = Math.random().toString(16).slice(2, 10);
326
+ while (this.store[options.id]);
327
+ return options.id;
328
+ }
329
+ /** Resolve an entry by id, including nested ids separated by `EntryTree.sep`. */
330
+ resolve(id) {
331
+ const parts = id.split(EntryTree2.sep);
332
+ let tree = this;
333
+ const final = parts.pop();
334
+ for (const part of parts) {
335
+ tree = tree.store[part]?.subtree;
336
+ if (!tree) throw new Error(`cannot resolve entry ${id}`);
337
+ }
338
+ const entry = tree.store[final];
339
+ if (!entry) throw new Error(`cannot resolve entry ${id}`);
340
+ return entry;
341
+ }
342
+ resolveGroup(id) {
343
+ if (!id) return this.root;
344
+ const entry = this.resolve(id);
345
+ if (!entry.subgroup) throw new Error(`entry ${id} is not a group`);
346
+ return entry.subgroup;
347
+ }
348
+ /** Create an entry in the root group or a nested group. */
349
+ async create(options, parent = null, position = Infinity) {
350
+ const group = this.resolveGroup(parent);
351
+ const id = await group.create(options);
352
+ const entry = this.resolve(id);
353
+ group.data.splice(position, 0, entry.options);
354
+ group.tree.write();
355
+ return id;
356
+ }
357
+ /** Stop and remove an entry from its parent group. */
358
+ async remove(id) {
359
+ const entry = this.resolve(id);
360
+ await entry.parent.remove(id);
361
+ entry.parent.tree.write();
362
+ }
363
+ /** Update an entry and optionally move it to another group. */
364
+ async update(id, options, parent, position) {
365
+ const entry = this.resolve(id);
366
+ const source = entry.parent;
367
+ const sourceIndex = source.data.indexOf(entry.options);
368
+ let target = source;
369
+ if (parent !== void 0) {
370
+ target = this.resolveGroup(parent);
371
+ source.unlink(entry.options);
372
+ target.data.splice(position ?? Infinity, 0, entry.options);
373
+ entry.parent = target;
374
+ }
375
+ try {
376
+ await entry.update(options, false, true);
377
+ } catch (error) {
378
+ if (parent !== void 0) {
379
+ target.unlink(entry.options);
380
+ source.data.splice(sourceIndex < 0 ? source.data.length : sourceIndex, 0, entry.options);
381
+ entry.parent = source;
382
+ try {
383
+ await entry.update({}, false, true);
384
+ } catch (rollbackError) {
385
+ throw new AggregateError([error, rollbackError], `failed to roll back loader entry move ${id}`);
386
+ }
387
+ }
388
+ throw error;
389
+ }
390
+ source.tree.write();
391
+ if (target !== source) target.tree.write();
392
+ }
393
+ /** Import a plugin module from a specifier or `cordis:` builtin. */
394
+ import(name, getOuterStack) {
395
+ if (name.startsWith("cordis:")) return this.ctx.loader.builtins[name.slice(7)];
396
+ return composeError(async (info) => {
397
+ info.offset += 3;
398
+ if (this.ctx.loader.internal) return await this.ctx.loader.internal.import(name, this.ctx.baseUrl, {});
399
+ else if (name.startsWith(".")) return await import(__rewriteRelativeImportExtension(
400
+ /* @vite-ignore */
401
+ new URL(name, this.ctx.baseUrl).href
402
+ ));
403
+ else return await import(__rewriteRelativeImportExtension(
404
+ /* @vite-ignore */
405
+ name
406
+ ));
407
+ }, getOuterStack);
408
+ }
409
+ };
410
+ var evaluate = new Function("ctx", "expr", `
411
+ with (ctx) {
412
+ return eval(expr)
413
+ }
414
+ `);
415
+ function interpolate(ctx, value) {
416
+ if (isJsExpr(value)) return evaluate(ctx, value.__jsExpr);
417
+ else if (!value || typeof value !== "object") return value;
418
+ else if (Array.isArray(value)) return value.map((item) => interpolate(ctx, item));
419
+ else return mapValues(value, (item) => interpolate(ctx, item));
420
+ }
421
+ function isJsExpr(value) {
422
+ return value instanceof Object && "__jsExpr" in value;
423
+ }
424
+ function updateError(stage, options, cause) {
425
+ const detail = cause instanceof Error ? cause.message : String(cause);
426
+ return new Error(`failed to ${stage} loader entry ${options.id} (${options.name}): ${detail}`, { cause });
427
+ }
428
+ function takeEntries(object, keys) {
429
+ const result = [];
430
+ for (const key of keys) {
431
+ if (!(key in object)) continue;
432
+ result.push([key, object[key]]);
433
+ delete object[key];
434
+ }
435
+ return result;
436
+ }
437
+ function sortKeys(object, prepend = ["id", "name"], append = ["config"]) {
438
+ const part1 = takeEntries(object, prepend);
439
+ const part2 = takeEntries(object, append);
440
+ const rest = takeEntries(object, Object.keys(object)).sort(([a], [b]) => a.localeCompare(b));
441
+ return Object.assign(object, Object.fromEntries([
442
+ ...part1,
443
+ ...rest,
444
+ ...part2
445
+ ]));
446
+ }
447
+ function replaceKeys(target, source) {
448
+ for (const key of Object.keys(target)) Reflect.deleteProperty(target, key);
449
+ return Object.assign(target, source);
450
+ }
451
+ var Entry = class Entry2 {
452
+ loader;
453
+ static key = /* @__PURE__ */ Symbol.for("cordis.entry");
454
+ ctx;
455
+ fiber;
456
+ parent;
457
+ options = {};
458
+ subgroup;
459
+ subtree;
460
+ _initTask;
461
+ _disposing = 0;
462
+ constructor(loader) {
463
+ this.loader = loader;
464
+ this.ctx = loader.ctx.extend({ [Entry2.key]: this });
465
+ this.context.emit("loader/entry-init", this);
466
+ }
467
+ get context() {
468
+ return this.ctx;
469
+ }
470
+ get id() {
471
+ let id = this.options.id;
472
+ if (this.parent.tree.ctx.fiber.entry) id = this.parent.tree.ctx.fiber.entry.id + EntryTree.sep + id;
473
+ return id;
474
+ }
475
+ /** True when this entry or any owning parent entry is disabled. */
476
+ get disabled() {
477
+ return this._disabled(this.options);
478
+ }
479
+ _disabled(options) {
480
+ if (options.group) return false;
481
+ if (this.disabledOf(options)) return true;
482
+ let entry = this.parent.ctx.fiber.entry;
483
+ while (entry) {
484
+ if (this.disabledOf(entry.options)) return true;
485
+ entry = entry.parent.ctx.fiber.entry;
486
+ }
487
+ return false;
488
+ }
489
+ /**
490
+ * Effective disabled state: a `!!js` expression evaluates against the loader
491
+ * context. The raw node stays in the options, so write-back keeps the form.
492
+ */
493
+ disabledOf(options) {
494
+ return isJsExpr(options.disabled) ? Boolean(this.evaluate(options.disabled.__jsExpr)) : Boolean(options.disabled);
495
+ }
496
+ evaluate(expr) {
497
+ return evaluate(this.ctx, expr);
498
+ }
499
+ async _patchContext(diff) {
500
+ await this.context.waterfall("loader/patch-context", this, async () => {
501
+ Object.setPrototypeOf(this.ctx, this.parent.ctx);
502
+ if (this.fiber?.uid && (diff.includes("config") || this.options.group)) await this.fiber.update(this.options.config, true);
503
+ });
504
+ }
505
+ async refresh() {
506
+ if (this.fiber) return;
507
+ if (this.disabled) return;
508
+ await this.init();
509
+ }
510
+ async _dispose(fiber = this.fiber) {
511
+ if (!fiber) return;
512
+ if (this.fiber === fiber) this.fiber = void 0;
513
+ this._disposing += 1;
514
+ try {
515
+ await fiber.dispose();
516
+ } finally {
517
+ this._disposing -= 1;
518
+ }
519
+ }
520
+ /** Merge new options, restart as needed, and persist through the parent tree. */
521
+ async update(options, create = false, force = false) {
522
+ const previousOptions = this.options;
523
+ const legacy = { ...previousOptions };
524
+ const candidate = create ? options : { ...previousOptions };
525
+ if (!create) for (const [key, value] of Object.entries(options)) if (isNullable(value)) delete candidate[key];
526
+ else candidate[key] = value;
527
+ sortKeys(candidate);
528
+ const diff = Object.keys({
529
+ ...candidate,
530
+ ...legacy
531
+ }).filter((key) => !deepEqual(candidate[key], legacy[key]));
532
+ if (!diff.length && !force) return;
533
+ const commit = () => {
534
+ if (create) return;
535
+ this.options = replaceKeys(previousOptions, candidate);
536
+ };
537
+ const previous = this.fiber;
538
+ if (!previous?.uid) {
539
+ this.fiber = void 0;
540
+ this.options = candidate;
541
+ try {
542
+ if (!this._disabled(candidate)) await this.init();
543
+ } catch (error) {
544
+ this.options = previousOptions;
545
+ throw error;
546
+ }
547
+ commit();
548
+ return;
549
+ }
550
+ if (this._disabled(candidate)) {
551
+ this.options = candidate;
552
+ try {
553
+ await this._dispose(previous);
554
+ } catch (error) {
555
+ this.options = previousOptions;
556
+ throw updateError("dispose", candidate, error);
557
+ }
558
+ commit();
559
+ this.context.emit("loader/partial-dispose", this, legacy, true);
560
+ return;
561
+ }
562
+ if (!diff.some((key) => key === "name" || key === "inject" || key === "group")) {
563
+ this.options = candidate;
564
+ try {
565
+ await this._patchContext(diff);
566
+ } catch (error) {
567
+ this.options = previousOptions;
568
+ try {
569
+ await this._patchContext(diff);
570
+ } catch (rollbackError) {
571
+ throw updateError("rollback", legacy, new AggregateError([error, rollbackError]));
572
+ }
573
+ this.context.emit("loader/partial-dispose", this, candidate, true);
574
+ throw updateError("apply", candidate, error);
575
+ }
576
+ commit();
577
+ this.context.emit("loader/partial-dispose", this, legacy, true);
578
+ return;
579
+ }
580
+ let plugin;
581
+ try {
582
+ plugin = diff.includes("name") ? this.loader.unwrapExports(await this.parent.tree.import(candidate.name, this.getOuterStack)) : previous.runtime.callback;
583
+ } catch (error) {
584
+ throw updateError("import", candidate, error);
585
+ }
586
+ const previousPlugin = previous.runtime.callback;
587
+ this.options = candidate;
588
+ try {
589
+ await this._dispose(previous);
590
+ } catch (error) {
591
+ this.options = previousOptions;
592
+ throw updateError("dispose", candidate, error);
593
+ }
594
+ try {
595
+ await this._start(plugin);
596
+ } catch (error) {
597
+ this.options = previousOptions;
598
+ try {
599
+ await this._start(previousPlugin);
600
+ } catch (rollbackError) {
601
+ throw updateError("rollback", legacy, new AggregateError([error, rollbackError]));
602
+ }
603
+ this.context.emit("loader/partial-dispose", this, candidate, true);
604
+ throw updateError("apply", candidate, error);
605
+ }
606
+ commit();
607
+ this.context.emit("loader/partial-dispose", this, legacy, true);
608
+ }
609
+ getOuterStack = () => {
610
+ let entry = this;
611
+ const result = [];
612
+ do {
613
+ result.push(` at ${entry.parent.tree.ctx.baseUrl}#${entry.options.id}`);
614
+ entry = entry.parent.ctx.fiber.entry;
615
+ } while (entry);
616
+ return result;
617
+ };
618
+ /** Import and start the configured plugin if it is not already running. */
619
+ async init() {
620
+ try {
621
+ await (this._initTask ??= this._init());
622
+ } finally {
623
+ this._initTask = void 0;
624
+ if (!this.loader.getTasks().length) this.ctx.reflect.notify(["loader"]);
625
+ }
626
+ await this._await();
627
+ }
628
+ async _await() {
629
+ try {
630
+ await this.fiber?.await();
631
+ } catch (error) {
632
+ throw updateError("apply", this.options, error);
633
+ }
634
+ }
635
+ async _init() {
636
+ let plugin;
637
+ try {
638
+ plugin = this.loader.unwrapExports(await this.parent.tree.import(this.options.name, this.getOuterStack));
639
+ } catch (error) {
640
+ throw updateError("import", this.options, error);
641
+ }
642
+ try {
643
+ await this._start(plugin);
644
+ } catch (error) {
645
+ throw updateError("apply", this.options, error);
646
+ }
647
+ }
648
+ async _start(plugin) {
649
+ let fiber;
650
+ try {
651
+ await this._patchContext([]);
652
+ this.loader.showLog(this, "apply");
653
+ fiber = this.fiber = this.ctx.registry.plugin(plugin, this.options.config, this.getOuterStack);
654
+ await fiber.await();
655
+ } catch (error) {
656
+ await this._dispose(fiber);
657
+ throw error;
658
+ }
659
+ }
660
+ };
661
+ function swap(target, source) {
662
+ for (const key of Reflect.ownKeys(target)) Reflect.deleteProperty(target, key);
663
+ for (const key of Reflect.ownKeys(source || {})) Reflect.defineProperty(target, key, Reflect.getOwnPropertyDescriptor(source, key));
664
+ }
665
+ var Realm = class {
666
+ store = /* @__PURE__ */ Object.create(null);
667
+ access(key, create = false) {
668
+ if (create) return this.store[key] ??= /* @__PURE__ */ Symbol(`${key}${this.suffix}`);
669
+ else return this.store[key] ?? /* @__PURE__ */ Symbol(`${key}${this.suffix}`);
670
+ }
671
+ delete(key) {
672
+ delete this.store[key];
673
+ }
674
+ get size() {
675
+ return Object.keys(this.store).length;
676
+ }
677
+ };
678
+ var LocalRealm = class extends Realm {
679
+ entry;
680
+ constructor(entry) {
681
+ super();
682
+ this.entry = entry;
683
+ }
684
+ get suffix() {
685
+ return "#" + this.entry.options.id;
686
+ }
687
+ };
688
+ var GlobalRealm = class extends Realm {
689
+ label;
690
+ constructor(label) {
691
+ super();
692
+ this.label = label;
693
+ }
694
+ get suffix() {
695
+ return "@" + this.label;
696
+ }
697
+ };
698
+ function isolate(ctx) {
699
+ const realms = /* @__PURE__ */ Object.create(null);
700
+ const delims = /* @__PURE__ */ Object.create(null);
701
+ function access(entry, name, create = false) {
702
+ let realm;
703
+ const label = entry.options.isolate?.[name];
704
+ if (!label) return;
705
+ if (label === true) realm = entry.realm ??= new LocalRealm(entry);
706
+ else if (create) realm = realms[label] ??= new GlobalRealm(label);
707
+ else realm = realms[label];
708
+ return realm?.access(name, create);
709
+ }
710
+ ctx.on("loader/entry-init", (entry) => {
711
+ entry.ctx[Context.intercept] = Object.create(entry.ctx[Context.intercept]);
712
+ entry.ctx[Context.isolate] = Object.create(entry.ctx[Context.isolate]);
713
+ });
714
+ ctx.on("loader/patch-context", async (entry, next) => {
715
+ const newMap = Object.create(entry.parent.ctx[Context.isolate]);
716
+ for (const name of Object.keys(entry.options.isolate ?? {})) newMap[name] = access(entry, name, true);
717
+ const diff = /* @__PURE__ */ Object.create(null);
718
+ const oldMap = entry.ctx[Context.isolate];
719
+ for (const name in {
720
+ ...newMap,
721
+ ...delims
722
+ }) {
723
+ if (newMap[name] === oldMap[name]) continue;
724
+ const delim = delims[name] ??= /* @__PURE__ */ Symbol(`delim:${name}`);
725
+ entry.ctx[delim] = /* @__PURE__ */ Symbol(`${name}#${entry.id}`);
726
+ for (const symbol of [oldMap[name], newMap[name]]) {
727
+ const impl = symbol && entry.ctx.reflect.store[symbol];
728
+ if (!impl) continue;
729
+ if (!impl.fiber) {
730
+ entry.ctx.logger.warn(/* @__PURE__ */ new Error(`expected service ${name} to be implemented`));
731
+ continue;
732
+ }
733
+ diff[name] = [
734
+ oldMap[name],
735
+ newMap[name],
736
+ entry.ctx[delim],
737
+ impl.fiber.ctx[delim]
738
+ ];
739
+ if (entry.ctx[delim] !== impl.fiber.ctx[delim]) break;
740
+ }
741
+ }
742
+ Object.setPrototypeOf(entry.ctx[Context.isolate], entry.parent.ctx[Context.isolate]);
743
+ Object.setPrototypeOf(entry.ctx[Context.intercept], entry.parent.ctx[Context.intercept]);
744
+ swap(entry.ctx[Context.isolate], newMap);
745
+ swap(entry.ctx[Context.intercept], entry.options.intercept);
746
+ await next();
747
+ for (const [symbol1, symbol2, flag1, flag2] of Object.values(diff)) if (flag1 === flag2 && entry.ctx.reflect.store[symbol1] && !entry.ctx.reflect.store[symbol2]) {
748
+ entry.ctx.reflect.store[symbol2] = entry.ctx.reflect.store[symbol1];
749
+ delete entry.ctx.reflect.store[symbol1];
750
+ }
751
+ ctx.reflect.notify(Object.keys(diff), (ctx2, name) => {
752
+ const [symbol1, symbol2, flag1, flag2] = diff[name];
753
+ const symbol3 = ctx2[Context.isolate][name];
754
+ const flag3 = ctx2[delims[name]];
755
+ return (symbol1 === symbol3 || symbol2 === symbol3) && flag1 === flag3 !== (flag1 === flag2);
756
+ });
757
+ for (const name in delims) if (!Reflect.ownKeys(newMap).includes(name)) delete entry.ctx[delims[name]];
758
+ });
759
+ ctx.on("loader/partial-dispose", (entry, legacy, active) => {
760
+ for (const [name, label] of Object.entries(legacy.isolate ?? {})) {
761
+ if (label === true) continue;
762
+ if (active && entry.options.isolate?.[name] === label) continue;
763
+ const realm = realms[label];
764
+ if (!realm) continue;
765
+ for (const entry2 of ctx.loader.entries()) if (entry2.options.isolate?.[name] === realm.label) return;
766
+ realm.delete(name);
767
+ if (!realm.size) delete realms[realm.label];
768
+ }
769
+ });
770
+ }
771
+ var Loader = class extends EntryTree {
772
+ config;
773
+ envData = { startTime: Date.now() };
774
+ name = "loader";
775
+ internal = ModuleLoader.fromInternal();
776
+ builtins = /* @__PURE__ */ Object.create(null);
777
+ constructor(ctx, config = {}) {
778
+ super(ctx);
779
+ this.config = config;
780
+ if (config.baseUrl) this.ctx.baseUrl = config.baseUrl;
781
+ const self = this;
782
+ defineProperty(this, Service.tracker, {
783
+ associate: "loader",
784
+ property: "ctx",
785
+ noShadow: true
786
+ });
787
+ ctx.reflect.provide("loader", this, this[Service.check]);
788
+ ctx.on("internal/config", function(_config, next) {
789
+ const config2 = next();
790
+ if (!this.entry || this.parent.fiber?.entry === this.entry) return config2;
791
+ if (this.runtime?.callback?.[EntryGroup.key]) return config2;
792
+ return interpolate(this.ctx, config2);
793
+ }, { global: true });
794
+ ctx.on("internal/update", async function(config2, noSave, next) {
795
+ if (!this.entry || noSave || this.parent.fiber?.entry === this.entry) return next();
796
+ await next();
797
+ const unparse = this.runtime?.Config?.["simplify"];
798
+ this.entry.options.config = unparse ? unparse(config2) : config2;
799
+ this.entry.parent.tree.write();
800
+ }, {
801
+ global: true,
802
+ prepend: true
803
+ });
804
+ ctx.on("internal/update", function(config2, _, next) {
805
+ if (!this.entry || this.parent.fiber?.entry === this.entry) return next();
806
+ self.showLog(this.entry, "reload");
807
+ return next();
808
+ }, { global: true });
809
+ ctx.on("internal/plugin", (fiber) => {
810
+ if (fiber.parent[Entry.key] && !fiber.entry) {
811
+ fiber.entry = fiber.parent[Entry.key];
812
+ Inject.resolve(fiber.entry.options.inject, fiber.inject);
813
+ }
814
+ if (fiber.uid) return;
815
+ if (!fiber.entry) return;
816
+ if (fiber.parent.fiber?.entry === fiber.entry) return;
817
+ if (!ctx.registry.has(fiber.runtime.callback)) return;
818
+ const treeOwner = fiber.entry.parent.tree.ctx.fiber;
819
+ if (!treeOwner.uid || treeOwner.state === 5) return;
820
+ if (fiber.entry._disposing) return;
821
+ this.showLog(fiber.entry, "unload");
822
+ if (fiber.entry.disabled) return;
823
+ fiber.entry.options.disabled = true;
824
+ fiber.entry.parent.tree.write();
825
+ });
826
+ ctx.plugin(isolate);
827
+ }
828
+ write() {
829
+ }
830
+ [Service.check]() {
831
+ if (Service.prototype[Service.resolveConfig].call(this).await && this.getTasks().length) return false;
832
+ return true;
833
+ }
834
+ showLog(entry, type) {
835
+ if (entry.options.group || !entry.parent.tree.enableLogs) return;
836
+ this.ctx.root.logger?.("loader").info("%s plugin %C", type, entry.options.name);
837
+ }
838
+ /** Return the loader entry id that owns `fiber`, if any. */
839
+ locate(fiber = this.ctx.fiber) {
840
+ while (1) {
841
+ if (fiber.entry) return fiber.entry.id;
842
+ const next = fiber.parent.fiber;
843
+ if (fiber === next) return;
844
+ fiber = next;
845
+ }
846
+ }
847
+ /** Hook for hosts that can restart the process on full-reload requests. */
848
+ exit() {
849
+ }
850
+ /** Normalize ESM/CJS/default export shapes before applying a plugin. */
851
+ unwrapExports(exports) {
852
+ if (isNullable(exports)) return exports;
853
+ exports = exports.default ?? exports;
854
+ if (!exports.__esModule) return exports;
855
+ return exports.default ?? exports;
856
+ }
857
+ };
858
+
859
+ // src/tool-plugin.ts
860
+ function defineToolPlugin(plugin, tools) {
861
+ return { plugin, tools: [...tools] };
862
+ }
863
+ function isToolPlugin(value) {
864
+ if (typeof value !== "object" || value === null) return false;
865
+ const candidate = value;
866
+ return candidate.plugin !== void 0 && Array.isArray(candidate.tools);
867
+ }
868
+ function createContextCapabilityResolver(ctx) {
869
+ return createCapabilityResolver((capability) => Reflect.get(ctx, capability.id));
870
+ }
871
+
872
+ // src/loader.ts
873
+ var CatalogLoaderError = class extends Error {
874
+ code;
875
+ expected;
876
+ hint;
877
+ detail;
878
+ constructor(code, expected, hint, detail) {
879
+ super(`${code}: ${expected}`);
880
+ this.name = "CatalogLoaderError";
881
+ this.code = code;
882
+ this.expected = expected;
883
+ this.hint = hint;
884
+ this.detail = detail;
885
+ }
886
+ };
887
+ var CatalogLoader = class extends Loader {
888
+ catalog;
889
+ realm;
890
+ constructor(ctx, config) {
891
+ super(ctx, config.baseUrl === void 0 ? {} : { baseUrl: config.baseUrl });
892
+ this.catalog = config.catalog;
893
+ this.realm = config.realm;
894
+ this.internal = void 0;
895
+ this.builtins.group = Group;
896
+ }
897
+ import(name, getOuterStack) {
898
+ if (name.startsWith("cordis:")) return super.import(name, getOuterStack);
899
+ const record = this.catalog.get(name);
900
+ if (record === void 0) {
901
+ throw new CatalogLoaderError(
902
+ "plugin-catalog-missing",
903
+ `plugin ${name} to exist in the generated catalog`,
904
+ "Install the package, add the Entry to forge.json, and rebuild the generated catalog.",
905
+ { name, realm: this.realm }
906
+ );
907
+ }
908
+ if (record.realm !== this.realm) {
909
+ throw new CatalogLoaderError(
910
+ "plugin-realm-mismatch",
911
+ `plugin ${name} to target the ${this.realm} realm`,
912
+ "Use a realm-specific plugin export and Entry.",
913
+ { actual: record.realm, expected: this.realm, name }
914
+ );
915
+ }
916
+ return record.load();
917
+ }
918
+ unwrapExports(exports) {
919
+ const value = super.unwrapExports(exports);
920
+ return isToolPlugin(value) ? value.plugin : value;
921
+ }
922
+ };
923
+ async function installCatalogLoader(ctx, catalog, realm) {
924
+ const fiber = await ctx.plugin(CatalogLoader, { catalog, realm });
925
+ return { fiber, loader: ctx.loader };
926
+ }
927
+ async function bootstrapCatalogLoader(ctx, catalog, realm, options) {
928
+ if (!options.supportedRealms.includes(realm)) {
929
+ return {
930
+ ok: false,
931
+ error: new CatalogLoaderError(
932
+ "plugin-realm-unsupported",
933
+ `the ${realm} realm to be supported by this host`,
934
+ "Select a realm advertised by the capability matrix before module evaluation.",
935
+ { realm, supportedRealms: options.supportedRealms }
936
+ )
937
+ };
938
+ }
939
+ const handle = await installCatalogLoader(ctx, catalog, realm);
940
+ return {
941
+ ok: true,
942
+ value: { ...handle, catalogDigest: options.catalogDigest, realm }
943
+ };
944
+ }
945
+ function effectiveRealm(entry, inherited) {
946
+ return entry.realm ?? inherited;
947
+ }
948
+ function assertSingleRealmGroup(entry, inheritedRealm) {
949
+ const realm = effectiveRealm(entry, inheritedRealm);
950
+ if (!entry.group) return;
951
+ const children = entry.config ?? [];
952
+ for (const child of children) {
953
+ const childRealm = effectiveRealm(child, realm);
954
+ if (childRealm !== realm) {
955
+ throw new CatalogLoaderError(
956
+ "plugin-entry-realm-mixed",
957
+ `group ${entry.id} to contain entries for only the ${realm} physical realm`,
958
+ "Split Host and Engine capabilities into separate top-level groups.",
959
+ { actual: childRealm, expected: realm, group: entry.id }
960
+ );
961
+ }
962
+ assertSingleRealmGroup(child, realm);
963
+ }
964
+ }
965
+ function projectEntry(entry, inherited) {
966
+ const realm = effectiveRealm(entry, inherited);
967
+ const config = entry.group ? projectPluginEntries(entry.config ?? [], realm, realm) : entry.config;
968
+ return {
969
+ id: entry.id,
970
+ name: entry.name,
971
+ ...config === void 0 ? {} : { config },
972
+ ...entry.group == null ? {} : { group: entry.group },
973
+ ...entry.disabled == null ? {} : { disabled: entry.disabled },
974
+ ...entry.inject == null ? {} : { inject: entry.inject }
975
+ };
976
+ }
977
+ function projectPluginEntries(entries, realm, inheritedRealm = "engine") {
978
+ const projected = [];
979
+ for (const entry of entries) {
980
+ const current = effectiveRealm(entry, inheritedRealm);
981
+ assertSingleRealmGroup(entry, inheritedRealm);
982
+ if (current === realm) projected.push(projectEntry(entry, current));
983
+ }
984
+ return projected;
985
+ }
986
+
987
+ export { CatalogLoader, CatalogLoaderError, Entry, EntryGroup, EntryTree, Group, Loader, bootstrapCatalogLoader, createContextCapabilityResolver, defineToolPlugin, installCatalogLoader, isToolPlugin, projectPluginEntries };
988
+ //# sourceMappingURL=loader.mjs.map
989
+ //# sourceMappingURL=loader.mjs.map