@forgeax/engine-plugin 0.1.20 → 0.1.23

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.
Files changed (37) hide show
  1. package/README.md +94 -10
  2. package/dist/__tests__/composition-contract.test-d.d.ts +2 -0
  3. package/dist/__tests__/composition-contract.test-d.d.ts.map +1 -0
  4. package/dist/__tests__/composition.integration.test.d.ts +2 -0
  5. package/dist/__tests__/composition.integration.test.d.ts.map +1 -0
  6. package/dist/__tests__/public-api.test-d.d.ts +2 -0
  7. package/dist/__tests__/public-api.test-d.d.ts.map +1 -0
  8. package/dist/__tests__/realm-loader.integration.test.d.ts +2 -0
  9. package/dist/__tests__/realm-loader.integration.test.d.ts.map +1 -0
  10. package/dist/browser.d.ts +2 -0
  11. package/dist/browser.d.ts.map +1 -1
  12. package/dist/browser.mjs +548 -1
  13. package/dist/browser.mjs.map +1 -1
  14. package/dist/composition.d.ts +73 -0
  15. package/dist/composition.d.ts.map +1 -0
  16. package/dist/index.d.ts +2 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.mjs +560 -8
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/inspection.d.ts +26 -0
  21. package/dist/inspection.d.ts.map +1 -0
  22. package/dist/loader.d.ts +51 -3
  23. package/dist/loader.d.ts.map +1 -1
  24. package/dist/loader.mjs +12 -7
  25. package/dist/loader.mjs.map +1 -1
  26. package/package.json +2 -2
  27. package/src/__tests__/browser-entry.test.ts +1 -0
  28. package/src/__tests__/composition-contract.test-d.ts +74 -0
  29. package/src/__tests__/composition.integration.test.ts +1347 -0
  30. package/src/__tests__/public-api.test-d.ts +70 -0
  31. package/src/__tests__/realm-loader.integration.test.ts +56 -0
  32. package/src/browser.ts +18 -0
  33. package/src/composition.ts +642 -0
  34. package/src/index.ts +18 -0
  35. package/src/inspection.ts +160 -0
  36. package/src/loader.ts +82 -13
  37. package/dist/.tsbuildinfo +0 -1
package/dist/index.mjs CHANGED
@@ -7,6 +7,553 @@ function createContextCapabilityResolver(ctx) {
7
7
  return createCapabilityResolver((capability) => Reflect.get(ctx, capability.id));
8
8
  }
9
9
 
10
+ // src/composition.ts
11
+ var PluginCompositionErrorClass = class extends Error {
12
+ code;
13
+ expected;
14
+ hint;
15
+ detail;
16
+ constructor(args) {
17
+ super(`${args.code}: ${args.expected}`);
18
+ this.name = "PluginCompositionError";
19
+ this.code = args.code;
20
+ this.expected = args.expected;
21
+ this.hint = args.hint;
22
+ this.detail = args.detail;
23
+ }
24
+ };
25
+ var PluginCompositionError = PluginCompositionErrorClass;
26
+ function usePlugin(plugin, ...args) {
27
+ const config = args[0];
28
+ const options = args[1];
29
+ return {
30
+ plugin,
31
+ config,
32
+ ...options?.key === void 0 ? {} : { key: options.key }
33
+ };
34
+ }
35
+ var ChildOperationFailure = class extends Error {
36
+ constructor(child, reason, cleanupFailures = []) {
37
+ super(`child operation failed: ${pluginName(child.plugin)}`);
38
+ this.child = child;
39
+ this.reason = reason;
40
+ this.cleanupFailures = cleanupFailures;
41
+ }
42
+ child;
43
+ reason;
44
+ cleanupFailures;
45
+ };
46
+ var groupStateKey = /* @__PURE__ */ Symbol("forgeax.plugin.group.state");
47
+ function pluginName(plugin) {
48
+ return plugin.name ?? "anonymous";
49
+ }
50
+ function dependencyNames(plugin) {
51
+ const inject = plugin.inject;
52
+ if (inject === void 0) return [];
53
+ return Array.isArray(inject) ? inject : Object.keys(inject);
54
+ }
55
+ function providedNames(plugin) {
56
+ const provide = plugin.provide;
57
+ if (provide === void 0) return [];
58
+ return Array.isArray(provide) ? provide : [provide];
59
+ }
60
+ function keyFor(child) {
61
+ if (child.key !== void 0) return child.key;
62
+ return child.plugin;
63
+ }
64
+ function validateKeys(children) {
65
+ const keys = /* @__PURE__ */ new Map();
66
+ for (const child of children) {
67
+ const key = keyFor(child);
68
+ const previous = keys.get(key);
69
+ if (previous !== void 0) {
70
+ if (child.key === void 0 && previous.key === void 0) {
71
+ throw new PluginCompositionError({
72
+ code: "plugin-group-key-required",
73
+ expected: "repeated Plugin references to declare a stable child key",
74
+ hint: "assign a unique key to each repeated child before activation.",
75
+ detail: { plugin: pluginName(child.plugin) }
76
+ });
77
+ }
78
+ throw new PluginCompositionError({
79
+ code: "plugin-group-key-duplicate",
80
+ expected: "stable child key to be unique within its Group",
81
+ hint: "assign a unique key to each child declaration.",
82
+ detail: { key: String(child.key) }
83
+ });
84
+ }
85
+ keys.set(key, child);
86
+ }
87
+ }
88
+ function orderedChildren(ctx, children, groupOwnedServices) {
89
+ const providers = /* @__PURE__ */ new Map();
90
+ children.forEach((child, index) => {
91
+ for (const service of providedNames(child.plugin)) {
92
+ if (!providers.has(service)) providers.set(service, index);
93
+ }
94
+ });
95
+ const graph = children.map(() => []);
96
+ for (let index = 0; index < children.length; index += 1) {
97
+ const child = children[index];
98
+ if (child === void 0) continue;
99
+ for (const service of dependencyNames(child.plugin)) {
100
+ const provider = providers.get(service);
101
+ const externalServiceAvailable = !groupOwnedServices.has(service) && ctx.reflect.get(service, false) !== void 0;
102
+ if (provider === void 0 && !externalServiceAvailable) {
103
+ throw new PluginCompositionError({
104
+ code: "plugin-group-provider-missing",
105
+ expected: "an inject/provide dependency to be available before the child starts",
106
+ hint: "provide the service in the Group or install the owning plugin first.",
107
+ detail: { service }
108
+ });
109
+ }
110
+ if (provider !== void 0 && provider !== index) graph[index]?.push(provider);
111
+ if (provider === index) graph[index]?.push(index);
112
+ }
113
+ }
114
+ const state = children.map(() => 0);
115
+ const sorted = [];
116
+ const visit = (index, path) => {
117
+ if (state[index] === 1) {
118
+ const cycleStart = path.indexOf(index);
119
+ const cycle = [...path.slice(cycleStart), index].map((item) => {
120
+ const child = children[item];
121
+ return child === void 0 ? "unknown" : pluginName(child.plugin);
122
+ });
123
+ throw new PluginCompositionError({
124
+ code: "plugin-group-dependency-cycle",
125
+ expected: "an acyclic inject/provide dependency graph",
126
+ hint: "break the dependency cycle before activating the Group.",
127
+ detail: { path: cycle }
128
+ });
129
+ }
130
+ if (state[index] === 2) return;
131
+ state[index] = 1;
132
+ for (const dependency of graph[index] ?? []) visit(dependency, [...path, index]);
133
+ state[index] = 2;
134
+ sorted.push(index);
135
+ };
136
+ for (let index = 0; index < children.length; index += 1) visit(index, []);
137
+ return sorted.map((index) => children[index]).filter((child) => child !== void 0);
138
+ }
139
+ async function validateConfig(child) {
140
+ const schema = child.plugin.Config;
141
+ if (schema === void 0) return;
142
+ const result = await schema["~standard"].validate(child.config);
143
+ if ("issues" in result && result.issues) {
144
+ throw new PluginCompositionError({
145
+ code: "plugin-config-invalid",
146
+ expected: `${pluginName(child.plugin)} Plugin.Config to validate the child config`,
147
+ hint: "provide a valid config before the child side effect runs.",
148
+ detail: { plugin: pluginName(child.plugin) }
149
+ });
150
+ }
151
+ }
152
+ async function validateChildren(children) {
153
+ const results = await Promise.allSettled(children.map((child) => validateConfig(child)));
154
+ const failure = results.find(
155
+ (result) => result.status === "rejected"
156
+ );
157
+ if (failure !== void 0) throw failure.reason;
158
+ }
159
+ function childFailure(child, _reason) {
160
+ return new PluginCompositionError({
161
+ code: "plugin-group-child-failed",
162
+ expected: `${pluginName(child.plugin)} to activate under its owning Group`,
163
+ hint: "repair the child and retry the Group update.",
164
+ detail: { child: pluginName(child.plugin) }
165
+ });
166
+ }
167
+ function sameConfig(left, right) {
168
+ return sameDataValue(left, right, /* @__PURE__ */ new Set());
169
+ }
170
+ function sameDataValue(left, right, active) {
171
+ if (Object.is(left, right)) return true;
172
+ if (left === null || right === null || typeof left !== "object" || typeof right !== "object") {
173
+ return false;
174
+ }
175
+ const leftArray = Array.isArray(left);
176
+ if (leftArray !== Array.isArray(right)) return false;
177
+ const leftPrototype = Object.getPrototypeOf(left);
178
+ const rightPrototype = Object.getPrototypeOf(right);
179
+ if (leftArray) {
180
+ if (leftPrototype !== Array.prototype || rightPrototype !== Array.prototype) return false;
181
+ } else if (!((leftPrototype === Object.prototype || leftPrototype === null) && (rightPrototype === Object.prototype || rightPrototype === null) && leftPrototype === rightPrototype)) {
182
+ return false;
183
+ }
184
+ if (active.has(left)) return false;
185
+ active.add(left);
186
+ try {
187
+ const leftKeys = Reflect.ownKeys(left).filter((key) => !leftArray || key !== "length");
188
+ const rightKeys = Reflect.ownKeys(right).filter((key) => !leftArray || key !== "length");
189
+ if (leftKeys.length !== rightKeys.length) return false;
190
+ for (const key of leftKeys) {
191
+ if (!Object.hasOwn(right, key)) return false;
192
+ const leftDescriptor = Object.getOwnPropertyDescriptor(left, key);
193
+ const rightDescriptor = Object.getOwnPropertyDescriptor(right, key);
194
+ if (leftDescriptor === void 0 || rightDescriptor === void 0 || !("value" in leftDescriptor) || !("value" in rightDescriptor) || !sameDataValue(leftDescriptor.value, rightDescriptor.value, active)) {
195
+ return false;
196
+ }
197
+ }
198
+ return true;
199
+ } finally {
200
+ active.delete(left);
201
+ }
202
+ }
203
+ function startChild(ctx, child) {
204
+ let fiber;
205
+ try {
206
+ fiber = ctx.plugin(child.plugin, child.config);
207
+ return { key: keyFor(child), plugin: child.plugin, config: child.config, fiber };
208
+ } catch (reason) {
209
+ if (fiber !== void 0) void fiber.dispose();
210
+ if (reason instanceof PluginCompositionError) throw reason;
211
+ throw childFailure(child);
212
+ }
213
+ }
214
+ async function activateChild(ctx, child) {
215
+ const record = startChild(ctx, child);
216
+ await record.fiber.await();
217
+ return record;
218
+ }
219
+ function recordUse(record) {
220
+ return {
221
+ plugin: record.plugin,
222
+ config: record.config,
223
+ ...typeof record.key === "string" ? { key: record.key } : {}
224
+ };
225
+ }
226
+ function sharesProvidedService(left, right) {
227
+ const rightServices = new Set(providedNames(right));
228
+ return providedNames(left).some((service) => rightServices.has(service));
229
+ }
230
+ async function restoreRecord(ctx, record, config) {
231
+ try {
232
+ await record.fiber.update(config);
233
+ await record.fiber.await();
234
+ return { ...record, config };
235
+ } catch (updateReason) {
236
+ if (record.fiber.uid !== null) await record.fiber.dispose();
237
+ try {
238
+ return await activateChild(ctx, { ...recordUse(record), config });
239
+ } catch (restoreReason) {
240
+ throw restoreReason ?? updateReason;
241
+ }
242
+ }
243
+ }
244
+ async function settleRecords(records, abort) {
245
+ if (records.length === 0) return;
246
+ const outcomes = records.map(
247
+ (record, index) => record.fiber.await().then(
248
+ () => ({ index, reason: void 0 }),
249
+ (reason) => ({ index, reason })
250
+ )
251
+ );
252
+ const pending = new Set(outcomes);
253
+ const abortSignal = abort === void 0 ? void 0 : abort.then((reason) => ({ index: -1, reason, aborted: true }));
254
+ while (pending.size > 0) {
255
+ const outcome = await Promise.race([
256
+ ...pending,
257
+ ...abortSignal === void 0 ? [] : [abortSignal]
258
+ ]);
259
+ if ("aborted" in outcome) {
260
+ const cleanupFailures = await disposeRecordsInReverse(records);
261
+ await Promise.allSettled(outcomes);
262
+ if (outcome.reason instanceof ChildOperationFailure) {
263
+ throw new ChildOperationFailure(
264
+ outcome.reason.child,
265
+ outcome.reason.reason,
266
+ cleanupFailures
267
+ );
268
+ }
269
+ throw outcome.reason;
270
+ }
271
+ const completed = outcomes[outcome.index];
272
+ if (completed !== void 0) pending.delete(completed);
273
+ if (outcome.reason !== void 0) {
274
+ const record = records[outcome.index];
275
+ if (record !== void 0) {
276
+ const cleanupFailures = await disposeRecordsInReverse(records);
277
+ await Promise.allSettled(outcomes);
278
+ throw new ChildOperationFailure(recordUse(record), outcome.reason, cleanupFailures);
279
+ }
280
+ throw outcome.reason;
281
+ }
282
+ }
283
+ }
284
+ async function disposeRecordsInReverse(records) {
285
+ const failures = [];
286
+ for (const record of [...records].reverse()) {
287
+ try {
288
+ await record.fiber.dispose();
289
+ } catch (reason) {
290
+ failures.push(reason);
291
+ }
292
+ }
293
+ return failures;
294
+ }
295
+ async function reconcile(ctx, state, children) {
296
+ validateKeys(children);
297
+ const groupOwnedServices = /* @__PURE__ */ new Set();
298
+ for (const record of state.records.values()) {
299
+ for (const service of providedNames(record.plugin)) groupOwnedServices.add(service);
300
+ }
301
+ const ordered = orderedChildren(ctx, children, groupOwnedServices);
302
+ await validateChildren(ordered);
303
+ const desired = new Map(ordered.map((child) => [keyFor(child), child]));
304
+ const removed = [...state.records.values()].filter((record) => !desired.has(record.key));
305
+ const changed = [];
306
+ const candidateChildren = [];
307
+ const replacements = [];
308
+ const released = [];
309
+ const retired = [];
310
+ const candidates = [];
311
+ for (const child of ordered) {
312
+ const current = state.records.get(keyFor(child));
313
+ if (current === void 0) {
314
+ candidateChildren.push(child);
315
+ } else if (current.plugin !== child.plugin) {
316
+ candidateChildren.push(child);
317
+ replacements.push({ old: current, child });
318
+ } else if (!sameConfig(current.config, child.config)) {
319
+ changed.push({ record: current, child, config: current.config });
320
+ }
321
+ }
322
+ const desiredServiceProviders = candidateChildren.filter(
323
+ (child) => providedNames(child.plugin).length > 0
324
+ );
325
+ for (const record of removed) {
326
+ if (desiredServiceProviders.some((child) => sharesProvidedService(record.plugin, child.plugin))) {
327
+ released.push(record);
328
+ }
329
+ }
330
+ for (const { old, child } of replacements) {
331
+ if (sharesProvidedService(old.plugin, child.plugin) && !released.includes(old)) {
332
+ released.push(old);
333
+ }
334
+ }
335
+ try {
336
+ const releaseResults = await Promise.allSettled(
337
+ released.map((record) => record.fiber.dispose())
338
+ );
339
+ const releaseFailureIndex = releaseResults.findIndex(
340
+ (result) => result.status === "rejected"
341
+ );
342
+ if (releaseFailureIndex !== -1) {
343
+ const releaseFailure = releaseResults[releaseFailureIndex];
344
+ const releasedRecord = released[releaseFailureIndex];
345
+ if (releaseFailure?.status === "rejected" && releasedRecord !== void 0) {
346
+ throw new ChildOperationFailure(recordUse(releasedRecord), releaseFailure.reason);
347
+ }
348
+ throw releaseFailure;
349
+ }
350
+ const updateTasks = changed.map(({ record, child }) => {
351
+ return Promise.resolve().then(() => record.fiber.update(child.config)).then(() => record.fiber.await()).catch((reason) => {
352
+ throw new ChildOperationFailure(child, reason);
353
+ });
354
+ });
355
+ for (const child of candidateChildren) candidates.push(startChild(ctx, child));
356
+ const updateFailure = updateTasks.length === 0 ? void 0 : Promise.race(
357
+ updateTasks.map(
358
+ (task) => task.then(
359
+ () => new Promise(() => {
360
+ }),
361
+ (reason) => reason
362
+ )
363
+ )
364
+ );
365
+ const results = await Promise.allSettled([
366
+ settleRecords(candidates, updateFailure),
367
+ ...updateTasks
368
+ ]);
369
+ const failure = results.find(
370
+ (result) => result.status === "rejected"
371
+ );
372
+ if (failure !== void 0) throw failure.reason;
373
+ for (const record of removed) {
374
+ if (released.includes(record)) continue;
375
+ retired.push(record);
376
+ try {
377
+ await record.fiber.dispose();
378
+ } catch (retirementReason) {
379
+ throw new ChildOperationFailure(recordUse(record), retirementReason);
380
+ }
381
+ }
382
+ for (const { old } of replacements) {
383
+ if (released.includes(old)) continue;
384
+ retired.push(old);
385
+ try {
386
+ await old.fiber.dispose();
387
+ } catch (retirementReason) {
388
+ throw new ChildOperationFailure(recordUse(old), retirementReason);
389
+ }
390
+ }
391
+ } catch (reason) {
392
+ const operation = reason instanceof ChildOperationFailure ? reason : void 0;
393
+ const primary = operation?.reason ?? reason;
394
+ const rollbackFailures = operation?.cleanupFailures ? [...operation.cleanupFailures] : await disposeRecordsInReverse(candidates);
395
+ for (const { record, config } of changed.reverse()) {
396
+ try {
397
+ const restored = await restoreRecord(ctx, record, config);
398
+ state.records.set(record.key, restored);
399
+ } catch (restoreReason) {
400
+ rollbackFailures.push(restoreReason);
401
+ }
402
+ }
403
+ for (const record of released.reverse()) {
404
+ try {
405
+ const restored = await activateChild(ctx, recordUse(record));
406
+ state.records.set(record.key, restored);
407
+ } catch (restoreReason) {
408
+ rollbackFailures.push(restoreReason);
409
+ }
410
+ }
411
+ for (const record of retired.reverse()) {
412
+ try {
413
+ const restored = await activateChild(ctx, recordUse(record));
414
+ state.records.set(record.key, restored);
415
+ } catch (restoreReason) {
416
+ rollbackFailures.push(restoreReason);
417
+ }
418
+ }
419
+ const fallback = operation?.child ?? ordered[0];
420
+ const primaryError = primary instanceof PluginCompositionError ? primary : fallback === void 0 ? primary : childFailure(fallback);
421
+ if (rollbackFailures.length > 0) {
422
+ throw new AggregateError(
423
+ [primaryError, ...rollbackFailures],
424
+ "plugin Group reconciliation and rollback both failed"
425
+ );
426
+ }
427
+ throw primaryError;
428
+ }
429
+ for (const record of removed) state.records.delete(record.key);
430
+ for (const { old, child } of replacements) {
431
+ state.records.delete(old.key);
432
+ const next = candidates.find((record) => record.key === keyFor(child));
433
+ if (next !== void 0) state.records.set(next.key, next);
434
+ }
435
+ for (const record of candidates) state.records.set(record.key, record);
436
+ for (const { record, child } of changed) record.config = child.config;
437
+ }
438
+ function definePluginGroup(options) {
439
+ const group = {
440
+ name: options.name,
441
+ async apply(ctx, config) {
442
+ const owner = ctx.fiber;
443
+ let state = owner[groupStateKey];
444
+ if (state === void 0) {
445
+ state = { records: /* @__PURE__ */ new Map() };
446
+ owner[groupStateKey] = state;
447
+ }
448
+ ctx.effect(
449
+ () => async () => {
450
+ state.records.clear();
451
+ delete owner[groupStateKey];
452
+ },
453
+ "plugin-group-state"
454
+ );
455
+ ctx.on(
456
+ "internal/update",
457
+ (nextConfig, _noSave, _next) => reconcile(ctx, state, options.children(nextConfig))
458
+ );
459
+ await reconcile(ctx, state, options.children(config));
460
+ }
461
+ };
462
+ return group;
463
+ }
464
+
465
+ // src/inspection.ts
466
+ var FIBER_STATE_BY_CODE = [
467
+ "pending",
468
+ "loading",
469
+ "active",
470
+ "failed",
471
+ "disposed",
472
+ "unloading"
473
+ ];
474
+ function canonicalJson(value) {
475
+ if (value === void 0) return "undefined";
476
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
477
+ if (Array.isArray(value)) return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
478
+ return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
479
+ }
480
+ function configDigest(value) {
481
+ let hash = 2166136261;
482
+ for (const char of canonicalJson(value)) {
483
+ hash ^= char.codePointAt(0) ?? 0;
484
+ hash = Math.imul(hash, 16777619);
485
+ }
486
+ return `fnv1a:${(hash >>> 0).toString(16).padStart(8, "0")}`;
487
+ }
488
+ function requiredServices(inject) {
489
+ if (Array.isArray(inject)) {
490
+ return inject.filter((name) => typeof name === "string").sort();
491
+ }
492
+ if (inject !== null && typeof inject === "object") return Object.keys(inject).sort();
493
+ return [];
494
+ }
495
+ function providedServices(fiber) {
496
+ const names = /* @__PURE__ */ new Set();
497
+ const stores = [
498
+ fiber.store,
499
+ fiber.ctx.reflect.store
500
+ ];
501
+ for (const store of stores) {
502
+ if (store === void 0) continue;
503
+ for (const key of Reflect.ownKeys(store)) {
504
+ const value = store[key];
505
+ if (value !== null && typeof value === "object" && "fiber" in value && (value.fiber === fiber || value.fiber?.uid === fiber.uid) && "name" in value && typeof value.name === "string") {
506
+ names.add(value.name);
507
+ }
508
+ }
509
+ }
510
+ return [...names].sort();
511
+ }
512
+ function failureFromFiber(fiber) {
513
+ if (fiber.state !== 3) return void 0;
514
+ const cause = fiber._error;
515
+ if (cause !== null && typeof cause === "object" && typeof cause.code === "string" && typeof cause.expected === "string" && typeof cause.hint === "string" && cause.detail !== null && typeof cause.detail === "object") {
516
+ return {
517
+ code: cause.code,
518
+ expected: cause.expected,
519
+ hint: cause.hint,
520
+ detail: cause.detail
521
+ };
522
+ }
523
+ return {
524
+ code: "plugin-fiber-failed",
525
+ expected: "the native Plugin Fiber to settle in an active state",
526
+ hint: "Read the owning Entry, repair its module/config or required service, then reconcile again.",
527
+ detail: {
528
+ reason: cause instanceof Error ? cause.message : String(cause ?? "unknown failure")
529
+ }
530
+ };
531
+ }
532
+ function projectLiveEntry(loader, entry) {
533
+ const options = entry.options;
534
+ const fiber = entry.fiber;
535
+ const disabled = entry.disabled;
536
+ const state = disabled ? "disabled" : fiber === void 0 ? "missing" : FIBER_STATE_BY_CODE[fiber.state] ?? "unavailable";
537
+ const parent = entry.parent?.ctx.fiber.entry?.id;
538
+ const failure = fiber === void 0 ? void 0 : failureFromFiber(fiber);
539
+ return {
540
+ ...options,
541
+ entryId: entry.id,
542
+ module: options.name,
543
+ realm: loader.realm,
544
+ desiredState: disabled ? "disabled" : "enabled",
545
+ fiberState: state,
546
+ ...parent === void 0 ? {} : { parent },
547
+ requiredServices: requiredServices(fiber?.inject ?? options.inject),
548
+ providedServices: fiber === void 0 ? [] : providedServices(fiber),
549
+ configDigest: configDigest(options.config),
550
+ ...failure === void 0 ? {} : { failure }
551
+ };
552
+ }
553
+ function inspectCatalogPlugins(loader) {
554
+ return { live: [...loader.entries()].map((entry) => projectLiveEntry(loader, entry)) };
555
+ }
556
+
10
557
  // ../../node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.2/node_modules/@deepseek-ai/cosmokit/lib/index.js
11
558
  function isNullable(value) {
12
559
  return value === null || value === void 0;
@@ -871,18 +1418,23 @@ function isToolPlugin(value) {
871
1418
  }
872
1419
 
873
1420
  // src/loader.ts
874
- var CatalogLoaderError = class extends Error {
1421
+ var CatalogLoaderErrorClass = class extends Error {
875
1422
  code;
876
1423
  expected;
877
1424
  hint;
878
1425
  detail;
879
- constructor(code, expected, hint, detail) {
880
- super(`${code}: ${expected}`);
1426
+ constructor(args) {
1427
+ super(`${args.code}: ${args.expected}`);
881
1428
  this.name = "CatalogLoaderError";
882
- this.code = code;
883
- this.expected = expected;
884
- this.hint = hint;
885
- this.detail = detail;
1429
+ this.code = args.code;
1430
+ this.expected = args.expected;
1431
+ this.hint = args.hint;
1432
+ this.detail = args.detail;
1433
+ }
1434
+ };
1435
+ var CatalogLoaderError = class extends CatalogLoaderErrorClass {
1436
+ constructor(code, expected, hint, detail) {
1437
+ super({ code, expected, hint, detail });
886
1438
  }
887
1439
  };
888
1440
  var CatalogLoader = class extends Loader {
@@ -985,6 +1537,6 @@ function projectPluginEntries(entries, realm, inheritedRealm = "engine") {
985
1537
  return projected;
986
1538
  }
987
1539
 
988
- export { CatalogLoader, CatalogLoaderError, bootstrapCatalogLoader, createContextCapabilityResolver, defineToolPlugin, installCatalogLoader, isToolPlugin, projectPluginEntries };
1540
+ export { CatalogLoader, CatalogLoaderError, PluginCompositionError, bootstrapCatalogLoader, createContextCapabilityResolver, definePluginGroup, defineToolPlugin, inspectCatalogPlugins, installCatalogLoader, isToolPlugin, projectPluginEntries, usePlugin };
989
1541
  //# sourceMappingURL=index.mjs.map
990
1542
  //# sourceMappingURL=index.mjs.map