@frontmcp/di 1.10.9-rc.1 → 1.10.9-rc.2
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.
- package/esm/index.mjs +4 -1175
- package/esm/package.json +2 -2
- package/index.js +4 -1227
- package/package.json +2 -2
package/esm/index.mjs
CHANGED
|
@@ -1,1175 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
function
|
|
3
|
-
|
|
4
|
-
return {
|
|
5
|
-
// Use Symbol.for() to create globally shared symbols that survive across
|
|
6
|
-
// multiple module instances (e.g., SDK loaded from both app and plugin paths).
|
|
7
|
-
type: (name) => /* @__PURE__ */ Symbol.for(`${prefix}:type:${name}`),
|
|
8
|
-
meta: (name) => /* @__PURE__ */ Symbol.for(`${prefix}:meta:${name}`)
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
var DiTokens = createTokenFactory({ prefix: "DI" });
|
|
12
|
-
|
|
13
|
-
// libs/di/src/tokens/di.constants.ts
|
|
14
|
-
var DESIGN_PARAMTYPES = "design:paramtypes";
|
|
15
|
-
var META_ASYNC_WITH = /* @__PURE__ */ Symbol("di:async-with");
|
|
16
|
-
var META_ASYNC_WITH_TOKENS = /* @__PURE__ */ Symbol("di:async-with-tokens");
|
|
17
|
-
|
|
18
|
-
// libs/di/src/records/provider.record.ts
|
|
19
|
-
var ProviderKind = /* @__PURE__ */ ((ProviderKind2) => {
|
|
20
|
-
ProviderKind2["CLASS_TOKEN"] = "CLASS_TOKEN";
|
|
21
|
-
ProviderKind2["CLASS"] = "CLASS";
|
|
22
|
-
ProviderKind2["VALUE"] = "VALUE";
|
|
23
|
-
ProviderKind2["FACTORY"] = "FACTORY";
|
|
24
|
-
ProviderKind2["INJECTED"] = "INJECTED";
|
|
25
|
-
return ProviderKind2;
|
|
26
|
-
})(ProviderKind || {});
|
|
27
|
-
|
|
28
|
-
// libs/di/src/metadata/provider.metadata.ts
|
|
29
|
-
var ProviderScope = /* @__PURE__ */ ((ProviderScope2) => {
|
|
30
|
-
ProviderScope2["GLOBAL"] = "global";
|
|
31
|
-
ProviderScope2["CONTEXT"] = "context";
|
|
32
|
-
ProviderScope2["SESSION"] = "session";
|
|
33
|
-
ProviderScope2["REQUEST"] = "request";
|
|
34
|
-
return ProviderScope2;
|
|
35
|
-
})(ProviderScope || {});
|
|
36
|
-
|
|
37
|
-
// libs/di/src/metadata/provider.schema.ts
|
|
38
|
-
import { z } from "@frontmcp/lazy-zod";
|
|
39
|
-
var providerMetadataSchema = z.object({
|
|
40
|
-
id: z.string().optional(),
|
|
41
|
-
name: z.string().min(1),
|
|
42
|
-
description: z.string().optional(),
|
|
43
|
-
scope: z.nativeEnum(ProviderScope).optional().default("global" /* GLOBAL */)
|
|
44
|
-
}).passthrough();
|
|
45
|
-
|
|
46
|
-
// libs/di/src/utils/metadata.utils.ts
|
|
47
|
-
import "reflect-metadata";
|
|
48
|
-
function getMetadata(key, target, propertyKey) {
|
|
49
|
-
return propertyKey !== void 0 ? Reflect.getMetadata(key, target, propertyKey) : Reflect.getMetadata(key, target);
|
|
50
|
-
}
|
|
51
|
-
function setMetadata(key, value, target, propertyKey) {
|
|
52
|
-
if (propertyKey !== void 0) {
|
|
53
|
-
Reflect.defineMetadata(key, value, target, propertyKey);
|
|
54
|
-
} else {
|
|
55
|
-
Reflect.defineMetadata(key, value, target);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function hasAsyncWith(klass) {
|
|
59
|
-
return !!getMetadata(META_ASYNC_WITH, klass);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// libs/di/src/utils/token.utils.ts
|
|
63
|
-
var tokenName = (t) => typeof t === "function" && !!t.prototype ? t.name : t?.name ?? "[ref]";
|
|
64
|
-
var isClass = (x) => typeof x === "function" && !!x.prototype;
|
|
65
|
-
var isPromise = (v) => !!v && typeof v.then === "function";
|
|
66
|
-
function getAsyncWithTokens(klass) {
|
|
67
|
-
const resolver = getMetadata(META_ASYNC_WITH_TOKENS, klass);
|
|
68
|
-
if (!resolver) return null;
|
|
69
|
-
const arr = resolver() ?? [];
|
|
70
|
-
return arr.filter(isClass);
|
|
71
|
-
}
|
|
72
|
-
function readWithParamTypes(klass, forWhat) {
|
|
73
|
-
const lazy = getAsyncWithTokens(klass);
|
|
74
|
-
if (lazy) return lazy;
|
|
75
|
-
const meta = getMetadata(DESIGN_PARAMTYPES, klass, "with");
|
|
76
|
-
if (meta === void 0) {
|
|
77
|
-
const where = `${klass.name}.with(...)`;
|
|
78
|
-
const hint = "Add @AsyncWith(() => [DepA, DepB]) to lazily declare deps, or decorate 'with' so TypeScript emits design:paramtypes.";
|
|
79
|
-
throw new Error(`Cannot discover ${forWhat} deps for ${where}. ${hint}`);
|
|
80
|
-
}
|
|
81
|
-
const tdzIdx = meta.findIndex((t) => t === void 0);
|
|
82
|
-
if (tdzIdx !== -1) {
|
|
83
|
-
const where = `${klass.name}.with(...)`;
|
|
84
|
-
throw new Error(
|
|
85
|
-
`Unresolved dependency at param #${tdzIdx} in ${where} (ESM circular import / TDZ).
|
|
86
|
-
Fix: add @AsyncWith(() => [/* deps */]) on ${klass.name} to avoid TDZ, or refactor the import cycle.`
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
return meta.filter(isClass);
|
|
90
|
-
}
|
|
91
|
-
function depsOfClass(klass, phase) {
|
|
92
|
-
if (hasAsyncWith(klass)) return readWithParamTypes(klass, phase);
|
|
93
|
-
const ctorMeta = getMetadata(DESIGN_PARAMTYPES, klass) ?? [];
|
|
94
|
-
const tdzIdx = ctorMeta.findIndex((t) => t === void 0);
|
|
95
|
-
if (tdzIdx !== -1) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
`Unresolved constructor dependency in ${klass.name} at param #${tdzIdx} (ESM circular import / TDZ).
|
|
98
|
-
Fix: convert this provider to @AsyncWith(() => [/* deps */]) and use a static with(...), or break the import cycle.`
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
return ctorMeta.filter(isClass);
|
|
102
|
-
}
|
|
103
|
-
function depsOfFunc(fn, phase) {
|
|
104
|
-
const [_ignoreInput, ...fnMeta] = getMetadata(DESIGN_PARAMTYPES, fn) ?? [];
|
|
105
|
-
const tdzIdx = fnMeta.findIndex((t) => t === void 0);
|
|
106
|
-
if (tdzIdx !== -1) {
|
|
107
|
-
throw new Error(`Unresolved function dependency in ${fn.name} at param #${tdzIdx} (ESM circular import / TDZ).
|
|
108
|
-
`);
|
|
109
|
-
}
|
|
110
|
-
return fnMeta.filter(isClass);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// libs/di/src/utils/provider.utils.ts
|
|
114
|
-
function createProviderNormalizer(options) {
|
|
115
|
-
const { tokens } = options;
|
|
116
|
-
return function normalizeProvider(item) {
|
|
117
|
-
if (isClass(item)) {
|
|
118
|
-
const isProviderClass = getMetadata(tokens.type, item);
|
|
119
|
-
if (isProviderClass) {
|
|
120
|
-
const metadata = {
|
|
121
|
-
name: getMetadata(tokens.name, item) ?? item.name ?? "UnknownProvider",
|
|
122
|
-
scope: getMetadata(tokens.scope, item),
|
|
123
|
-
description: tokens.description ? getMetadata(tokens.description, item) : void 0,
|
|
124
|
-
id: tokens.id ? getMetadata(tokens.id, item) : void 0
|
|
125
|
-
};
|
|
126
|
-
return {
|
|
127
|
-
kind: "CLASS_TOKEN" /* CLASS_TOKEN */,
|
|
128
|
-
provide: item,
|
|
129
|
-
metadata
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
const staticMeta = item.metadata;
|
|
133
|
-
return {
|
|
134
|
-
kind: "CLASS_TOKEN" /* CLASS_TOKEN */,
|
|
135
|
-
provide: item,
|
|
136
|
-
metadata: staticMeta ?? { name: item.name ?? "UnknownProvider" }
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
const obj = item;
|
|
140
|
-
if ("useValue" in obj || "useFactory" in obj || "useClass" in obj) {
|
|
141
|
-
if (!obj.provide) {
|
|
142
|
-
const name = obj.name ?? "[object]";
|
|
143
|
-
throw new Error(`Provider '${name}' is missing 'provide'.`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if ("useValue" in obj) {
|
|
147
|
-
return {
|
|
148
|
-
kind: "VALUE" /* VALUE */,
|
|
149
|
-
provide: obj.provide,
|
|
150
|
-
useValue: obj.useValue,
|
|
151
|
-
metadata: obj.metadata ?? { name: "ValueProvider" }
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
if ("useFactory" in obj) {
|
|
155
|
-
return {
|
|
156
|
-
kind: "FACTORY" /* FACTORY */,
|
|
157
|
-
provide: obj.provide,
|
|
158
|
-
inject: obj.inject ?? (() => []),
|
|
159
|
-
useFactory: obj.useFactory,
|
|
160
|
-
metadata: obj.metadata ?? { name: "FactoryProvider" }
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
if ("useClass" in obj) {
|
|
164
|
-
return {
|
|
165
|
-
kind: "CLASS" /* CLASS */,
|
|
166
|
-
provide: obj.provide,
|
|
167
|
-
useClass: obj.useClass,
|
|
168
|
-
metadata: obj.metadata ?? { name: obj.useClass?.name ?? "ClassProvider" }
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
let itemStr;
|
|
172
|
-
try {
|
|
173
|
-
itemStr = JSON.stringify(item);
|
|
174
|
-
} catch {
|
|
175
|
-
itemStr = String(item);
|
|
176
|
-
}
|
|
177
|
-
throw new Error(`Cannot normalize provider: unrecognized format ${itemStr}`);
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
function providerDiscoveryDeps(rec, localTokens, depsOfClassFn) {
|
|
181
|
-
switch (rec.kind) {
|
|
182
|
-
case "VALUE" /* VALUE */:
|
|
183
|
-
case "INJECTED" /* INJECTED */:
|
|
184
|
-
return [];
|
|
185
|
-
case "FACTORY" /* FACTORY */: {
|
|
186
|
-
const all = [...rec.inject()];
|
|
187
|
-
return all.filter((d) => localTokens.has(d));
|
|
188
|
-
}
|
|
189
|
-
case "CLASS" /* CLASS */:
|
|
190
|
-
return depsOfClassFn(rec.useClass, "discovery");
|
|
191
|
-
case "CLASS_TOKEN" /* CLASS_TOKEN */:
|
|
192
|
-
return depsOfClassFn(rec.provide, "discovery");
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
function providerInvocationTokens(rec, depsOfClassFn) {
|
|
196
|
-
switch (rec.kind) {
|
|
197
|
-
case "VALUE" /* VALUE */:
|
|
198
|
-
case "INJECTED" /* INJECTED */:
|
|
199
|
-
return [];
|
|
200
|
-
case "FACTORY" /* FACTORY */:
|
|
201
|
-
return [...rec.inject()];
|
|
202
|
-
case "CLASS" /* CLASS */:
|
|
203
|
-
return depsOfClassFn(rec.useClass, "invocation");
|
|
204
|
-
case "CLASS_TOKEN" /* CLASS_TOKEN */:
|
|
205
|
-
return depsOfClassFn(rec.provide, "invocation");
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// libs/di/src/registry/registry.base.ts
|
|
210
|
-
var RegistryAbstract = class {
|
|
211
|
-
/** Default timeout for async operations in milliseconds */
|
|
212
|
-
asyncTimeoutMs = 3e4;
|
|
213
|
-
/** Promise that resolves when the registry is fully initialized */
|
|
214
|
-
ready;
|
|
215
|
-
/** Reference to parent provider registry for dependency resolution */
|
|
216
|
-
providers;
|
|
217
|
-
/** Metadata used for initialization */
|
|
218
|
-
list;
|
|
219
|
-
/** All tokens that are provided (graph nodes) */
|
|
220
|
-
tokens;
|
|
221
|
-
/** Record definition by token */
|
|
222
|
-
defs;
|
|
223
|
-
/** Dependency graph by token */
|
|
224
|
-
graph;
|
|
225
|
-
/** Instantiated entries by token */
|
|
226
|
-
instances = /* @__PURE__ */ new Map();
|
|
227
|
-
/**
|
|
228
|
-
* Create a new registry.
|
|
229
|
-
*
|
|
230
|
-
* @param name - Registry kind name for identification
|
|
231
|
-
* @param providers - Parent provider registry
|
|
232
|
-
* @param metadata - Initialization metadata
|
|
233
|
-
* @param auto - Whether to automatically build and initialize
|
|
234
|
-
*/
|
|
235
|
-
constructor(name, providers, metadata, auto = true) {
|
|
236
|
-
if (providers && typeof providers.addRegistry === "function") {
|
|
237
|
-
providers.addRegistry(name, this);
|
|
238
|
-
}
|
|
239
|
-
this.providers = providers;
|
|
240
|
-
this.list = metadata;
|
|
241
|
-
const { tokens, defs, graph } = this.buildMap(metadata);
|
|
242
|
-
this.tokens = tokens;
|
|
243
|
-
this.defs = defs;
|
|
244
|
-
this.graph = graph;
|
|
245
|
-
if (auto) {
|
|
246
|
-
this.buildGraph();
|
|
247
|
-
this.ready = this.initialize();
|
|
248
|
-
} else {
|
|
249
|
-
this.ready = Promise.resolve();
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Check if the registry has any entries.
|
|
254
|
-
*/
|
|
255
|
-
hasAny() {
|
|
256
|
-
return this.instances.size > 0;
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Get all instances as a readonly map.
|
|
260
|
-
*/
|
|
261
|
-
getAllInstances() {
|
|
262
|
-
return this.instances;
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
// libs/di/src/registry/container.ts
|
|
267
|
-
import "reflect-metadata";
|
|
268
|
-
var DiContainer = class extends RegistryAbstract {
|
|
269
|
-
/** Topological order for initialization (deps first) */
|
|
270
|
-
order = /* @__PURE__ */ new Set();
|
|
271
|
-
/** Provider normalizer function */
|
|
272
|
-
normalizeProvider;
|
|
273
|
-
/** Parent container for hierarchical lookup */
|
|
274
|
-
parentContainer;
|
|
275
|
-
/**
|
|
276
|
-
* Create a new DI container.
|
|
277
|
-
*
|
|
278
|
-
* @param providers - Array of provider definitions
|
|
279
|
-
* @param parent - Optional parent container for hierarchical lookup
|
|
280
|
-
* @param options - Container configuration options
|
|
281
|
-
*/
|
|
282
|
-
constructor(providers, parent, options = {}) {
|
|
283
|
-
const normalizer = options.providerTokens ? createProviderNormalizer({ tokens: options.providerTokens }) : createBasicNormalizer();
|
|
284
|
-
super("DiContainer", parent, providers, false);
|
|
285
|
-
this.normalizeProvider = normalizer;
|
|
286
|
-
this.parentContainer = parent;
|
|
287
|
-
if (options.asyncTimeoutMs) {
|
|
288
|
-
this.asyncTimeoutMs = options.asyncTimeoutMs;
|
|
289
|
-
}
|
|
290
|
-
this.buildGraph();
|
|
291
|
-
this.topoSort();
|
|
292
|
-
this.ready = this.initialize();
|
|
293
|
-
}
|
|
294
|
-
/* -------------------- Hierarchy helpers -------------------- */
|
|
295
|
-
/**
|
|
296
|
-
* Walk up the registry chain to find a def for a token.
|
|
297
|
-
*/
|
|
298
|
-
lookupDefInHierarchy(token) {
|
|
299
|
-
if (this.defs.has(token)) {
|
|
300
|
-
return { registry: this, rec: this.defs.get(token) };
|
|
301
|
-
}
|
|
302
|
-
return this.parentContainer?.lookupDefInHierarchy(token);
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Resolve a GLOBAL-scoped dependency from the hierarchy.
|
|
306
|
-
*/
|
|
307
|
-
resolveDefaultFromHierarchy(token) {
|
|
308
|
-
const found = this.lookupDefInHierarchy(token);
|
|
309
|
-
if (!found) {
|
|
310
|
-
throw new Error(`Cannot resolve token ${tokenName(token)}: not registered in hierarchy.`);
|
|
311
|
-
}
|
|
312
|
-
const { registry, rec } = found;
|
|
313
|
-
const sc = registry.getProviderScope(rec);
|
|
314
|
-
if (sc !== "global" /* GLOBAL */) {
|
|
315
|
-
throw new Error(
|
|
316
|
-
`Dependency ${tokenName(token)} is scoped (${sc}) in ${registry.constructor.name}; cannot use as GLOBAL.`
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
const inst = registry.instances.get(token);
|
|
320
|
-
if (inst === void 0) {
|
|
321
|
-
throw new Error(`Dependency ${tokenName(token)} (GLOBAL) is not instantiated in ${registry.constructor.name}`);
|
|
322
|
-
}
|
|
323
|
-
return inst;
|
|
324
|
-
}
|
|
325
|
-
/* -------------------- Build phase -------------------- */
|
|
326
|
-
buildMap(list) {
|
|
327
|
-
const tokens = /* @__PURE__ */ new Set();
|
|
328
|
-
const defs = /* @__PURE__ */ new Map();
|
|
329
|
-
const graph = /* @__PURE__ */ new Map();
|
|
330
|
-
const normalize = this.normalizeProvider ?? createBasicNormalizer();
|
|
331
|
-
for (const raw of list) {
|
|
332
|
-
const rec = normalize(raw);
|
|
333
|
-
const provide = rec.provide;
|
|
334
|
-
tokens.add(provide);
|
|
335
|
-
defs.set(provide, rec);
|
|
336
|
-
graph.set(provide, /* @__PURE__ */ new Set());
|
|
337
|
-
}
|
|
338
|
-
return { tokens, defs, graph };
|
|
339
|
-
}
|
|
340
|
-
buildGraph() {
|
|
341
|
-
for (const token of this.tokens) {
|
|
342
|
-
const rec = this.defs.get(token);
|
|
343
|
-
const deps = this.discoveryDeps(rec);
|
|
344
|
-
for (const d of deps) {
|
|
345
|
-
const isLocal = this.tokens.has(d);
|
|
346
|
-
const up = isLocal ? void 0 : this.lookupDefInHierarchy(d);
|
|
347
|
-
if (!isLocal && !up) {
|
|
348
|
-
throw new Error(
|
|
349
|
-
`Provider ${tokenName(token)} depends on ${tokenName(d)}, which is not registered (local or parent).`
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
|
-
const depRec = isLocal ? this.defs.get(d) : up.rec;
|
|
353
|
-
const depScope = isLocal ? this.getProviderScope(depRec) : up.registry.getProviderScope(depRec);
|
|
354
|
-
if (this.getProviderScope(rec) === "global" /* GLOBAL */ && depScope !== "global" /* GLOBAL */) {
|
|
355
|
-
throw new Error(
|
|
356
|
-
`Invalid dependency: GLOBAL-scoped provider ${tokenName(
|
|
357
|
-
token
|
|
358
|
-
)} cannot depend on scoped provider ${tokenName(d)}.`
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
if (isLocal) {
|
|
362
|
-
this.graph.get(token).add(d);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Topological sort with cycle detection.
|
|
369
|
-
*/
|
|
370
|
-
topoSort() {
|
|
371
|
-
const WHITE = 0, GRAY = 1, BLACK = 2;
|
|
372
|
-
const color = /* @__PURE__ */ new Map();
|
|
373
|
-
const order = /* @__PURE__ */ new Set();
|
|
374
|
-
const path = [];
|
|
375
|
-
const dfs = (n) => {
|
|
376
|
-
color.set(n, GRAY);
|
|
377
|
-
path.push(n);
|
|
378
|
-
for (const dep of this.graph.get(n) ?? []) {
|
|
379
|
-
const c = color.get(dep) ?? WHITE;
|
|
380
|
-
if (c === GRAY) {
|
|
381
|
-
const idx = path.findIndex((p) => p === dep);
|
|
382
|
-
const cycle = [...path.slice(idx), dep].map((t) => tokenName(t)).join(" -> ");
|
|
383
|
-
throw new Error(`Dependency cycle detected: ${cycle}`);
|
|
384
|
-
}
|
|
385
|
-
if (c === WHITE) dfs(dep);
|
|
386
|
-
}
|
|
387
|
-
color.set(n, BLACK);
|
|
388
|
-
order.add(n);
|
|
389
|
-
path.pop();
|
|
390
|
-
};
|
|
391
|
-
for (const n of this.graph.keys()) {
|
|
392
|
-
if ((color.get(n) ?? WHITE) === WHITE) dfs(n);
|
|
393
|
-
}
|
|
394
|
-
this.order = order;
|
|
395
|
-
}
|
|
396
|
-
async initialize(opts) {
|
|
397
|
-
const force = !!opts?.force;
|
|
398
|
-
const only = opts?.onlyTokens ? new Set(opts.onlyTokens) : null;
|
|
399
|
-
for (const token of this.order) {
|
|
400
|
-
const rec = this.defs.get(token);
|
|
401
|
-
if (this.getProviderScope(rec) !== "global" /* GLOBAL */) continue;
|
|
402
|
-
if (only && !only.has(token)) continue;
|
|
403
|
-
if (!force && this.instances.has(token)) continue;
|
|
404
|
-
if (force && this.instances.has(token)) {
|
|
405
|
-
this.instances.delete(token);
|
|
406
|
-
}
|
|
407
|
-
try {
|
|
408
|
-
await this.instantiateOne(token, rec);
|
|
409
|
-
} catch (e) {
|
|
410
|
-
const msg = e?.message ?? e;
|
|
411
|
-
throw new Error(`Failed constructing ${tokenName(token)}: ${msg}`);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
/* -------------------- Instantiation -------------------- */
|
|
416
|
-
async withTimeout(p, ms, label) {
|
|
417
|
-
if (ms <= 0 || !Number.isFinite(ms)) return p;
|
|
418
|
-
let timer;
|
|
419
|
-
try {
|
|
420
|
-
const tm = new Promise(
|
|
421
|
-
(_, rej) => timer = setTimeout(() => rej(new Error(`Timeout after ${ms}ms: ${label}`)), ms)
|
|
422
|
-
);
|
|
423
|
-
return await Promise.race([p, tm]);
|
|
424
|
-
} finally {
|
|
425
|
-
clearTimeout(timer);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
async resolveFactoryArg(t, store) {
|
|
429
|
-
if (store.has(t)) return store.get(t);
|
|
430
|
-
if (this.instances.has(t)) return this.instances.get(t);
|
|
431
|
-
const rec = this.defs.get(t);
|
|
432
|
-
if (rec) {
|
|
433
|
-
const sc = this.getProviderScope(rec);
|
|
434
|
-
if (sc === "global" /* GLOBAL */) {
|
|
435
|
-
if (!this.instances.has(t)) {
|
|
436
|
-
throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated`);
|
|
437
|
-
}
|
|
438
|
-
return this.instances.get(t);
|
|
439
|
-
} else {
|
|
440
|
-
await this.buildIntoStore(t, rec, store);
|
|
441
|
-
return store.get(t);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
const up = this.lookupDefInHierarchy(t);
|
|
445
|
-
if (up) {
|
|
446
|
-
const sc = up.registry.getProviderScope(up.rec);
|
|
447
|
-
if (sc === "global" /* GLOBAL */) {
|
|
448
|
-
const inst = up.registry.instances.get(t);
|
|
449
|
-
if (inst === void 0) {
|
|
450
|
-
throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated in parent`);
|
|
451
|
-
}
|
|
452
|
-
return inst;
|
|
453
|
-
} else {
|
|
454
|
-
await up.registry.buildIntoStore(t, up.rec, store);
|
|
455
|
-
return store.get(t);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
if (isClass(t)) {
|
|
459
|
-
if (hasAsyncWith(t)) {
|
|
460
|
-
const out = t.with();
|
|
461
|
-
return isPromise(out) ? await this.withTimeout(out, this.asyncTimeoutMs, `${t.name}.with(...)`) : out;
|
|
462
|
-
}
|
|
463
|
-
const instance = new t();
|
|
464
|
-
const init = instance?.init;
|
|
465
|
-
if (typeof init === "function") {
|
|
466
|
-
const ret = init.call(instance);
|
|
467
|
-
if (isPromise(ret)) await this.withTimeout(ret, this.asyncTimeoutMs, `${t.name}.init()`);
|
|
468
|
-
}
|
|
469
|
-
return instance;
|
|
470
|
-
}
|
|
471
|
-
throw new Error(`Cannot resolve token ${tokenName(t)}: not registered in hierarchy and not constructable.`);
|
|
472
|
-
}
|
|
473
|
-
async instantiateOne(token, rec) {
|
|
474
|
-
switch (rec.kind) {
|
|
475
|
-
case "VALUE" /* VALUE */: {
|
|
476
|
-
this.instances.set(token, rec.useValue);
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
case "FACTORY" /* FACTORY */: {
|
|
480
|
-
const deps = this.invocationTokens(token, rec);
|
|
481
|
-
const args = [];
|
|
482
|
-
for (const d of deps) {
|
|
483
|
-
args.push(await this.resolveFactoryArg(d, this.instances));
|
|
484
|
-
}
|
|
485
|
-
const out = rec.useFactory(...args);
|
|
486
|
-
const val = isPromise(out) ? await this.withTimeout(out, this.asyncTimeoutMs, `${tokenName(token)}.useFactory(...)`) : out;
|
|
487
|
-
this.instances.set(token, val);
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
case "CLASS" /* CLASS */: {
|
|
491
|
-
const depTokens = this.invocationTokens(token, rec);
|
|
492
|
-
const deps = depTokens.map((t) => this.resolveDefaultFromHierarchy(t));
|
|
493
|
-
const klass = rec.useClass;
|
|
494
|
-
if (hasAsyncWith(klass)) {
|
|
495
|
-
const out = klass.with(...deps);
|
|
496
|
-
const val = isPromise(out) ? await this.withTimeout(out, this.asyncTimeoutMs, `${klass.name}.with(...)`) : out;
|
|
497
|
-
this.instances.set(token, val);
|
|
498
|
-
} else {
|
|
499
|
-
const instance = new klass(...deps);
|
|
500
|
-
const init = instance?.init;
|
|
501
|
-
if (typeof init === "function") {
|
|
502
|
-
const ret = init.call(instance);
|
|
503
|
-
if (isPromise(ret)) await this.withTimeout(ret, this.asyncTimeoutMs, `${klass.name}.init()`);
|
|
504
|
-
}
|
|
505
|
-
this.instances.set(token, instance);
|
|
506
|
-
}
|
|
507
|
-
return;
|
|
508
|
-
}
|
|
509
|
-
case "CLASS_TOKEN" /* CLASS_TOKEN */: {
|
|
510
|
-
const depTokens = this.invocationTokens(token, rec);
|
|
511
|
-
const deps = depTokens.map((t) => this.resolveDefaultFromHierarchy(t));
|
|
512
|
-
const klass = rec.provide;
|
|
513
|
-
if (hasAsyncWith(klass)) {
|
|
514
|
-
const out = klass.with(...deps);
|
|
515
|
-
const val = isPromise(out) ? await this.withTimeout(out, this.asyncTimeoutMs, `${klass.name}.with(...)`) : out;
|
|
516
|
-
this.instances.set(token, val);
|
|
517
|
-
} else {
|
|
518
|
-
const instance = new klass(...deps);
|
|
519
|
-
const init = instance?.init;
|
|
520
|
-
if (typeof init === "function") {
|
|
521
|
-
const ret = init.call(instance);
|
|
522
|
-
if (isPromise(ret)) await this.withTimeout(ret, this.asyncTimeoutMs, `${klass.name}.init()`);
|
|
523
|
-
}
|
|
524
|
-
this.instances.set(token, instance);
|
|
525
|
-
}
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
/**
|
|
531
|
-
* Build a scoped provider into the given store.
|
|
532
|
-
*/
|
|
533
|
-
async buildIntoStore(token, rec, store) {
|
|
534
|
-
if (store.has(token)) return;
|
|
535
|
-
try {
|
|
536
|
-
switch (rec.kind) {
|
|
537
|
-
case "VALUE" /* VALUE */: {
|
|
538
|
-
store.set(token, rec.useValue);
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
case "FACTORY" /* FACTORY */: {
|
|
542
|
-
const deps = this.invocationTokens(token, rec);
|
|
543
|
-
const args = [];
|
|
544
|
-
for (const d of deps) {
|
|
545
|
-
args.push(await this.resolveFactoryArg(d, store));
|
|
546
|
-
}
|
|
547
|
-
const out = rec.useFactory(...args);
|
|
548
|
-
const val = isPromise(out) ? await this.withTimeout(out, this.asyncTimeoutMs, `${tokenName(token)}.useFactory(...)`) : out;
|
|
549
|
-
store.set(token, val);
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
case "CLASS" /* CLASS */:
|
|
553
|
-
case "CLASS_TOKEN" /* CLASS_TOKEN */: {
|
|
554
|
-
const depsTokens = this.invocationTokens(token, rec);
|
|
555
|
-
const deps = [];
|
|
556
|
-
for (const d of depsTokens) {
|
|
557
|
-
deps.push(await this.resolveManagedForClass(d, store));
|
|
558
|
-
}
|
|
559
|
-
const klass = rec.kind === "CLASS" /* CLASS */ ? rec.useClass : rec.provide;
|
|
560
|
-
if (hasAsyncWith(klass)) {
|
|
561
|
-
const out = klass.with(...deps);
|
|
562
|
-
const val = isPromise(out) ? await this.withTimeout(out, this.asyncTimeoutMs, `${klass.name}.with(...)`) : out;
|
|
563
|
-
store.set(token, val);
|
|
564
|
-
} else {
|
|
565
|
-
const instance = new klass(...deps);
|
|
566
|
-
const init = instance?.init;
|
|
567
|
-
if (typeof init === "function") {
|
|
568
|
-
const ret = init.call(instance);
|
|
569
|
-
if (isPromise(ret)) await this.withTimeout(ret, this.asyncTimeoutMs, `${klass.name}.init()`);
|
|
570
|
-
}
|
|
571
|
-
store.set(token, instance);
|
|
572
|
-
}
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
} catch (e) {
|
|
577
|
-
const msg = e?.message ?? e;
|
|
578
|
-
throw new Error(`Failed constructing (scoped) ${tokenName(token)}: ${msg}`);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
async resolveManagedForClass(d, store) {
|
|
582
|
-
if (store.has(d)) return store.get(d);
|
|
583
|
-
if (this.instances.has(d)) return this.instances.get(d);
|
|
584
|
-
const depRec = this.defs.get(d);
|
|
585
|
-
if (depRec) {
|
|
586
|
-
const sc = this.getProviderScope(depRec);
|
|
587
|
-
if (sc === "global" /* GLOBAL */) {
|
|
588
|
-
const v = this.instances.get(d);
|
|
589
|
-
if (v === void 0) throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated`);
|
|
590
|
-
return v;
|
|
591
|
-
} else {
|
|
592
|
-
await this.buildIntoStore(d, depRec, store);
|
|
593
|
-
return store.get(d);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
const up = this.lookupDefInHierarchy(d);
|
|
597
|
-
if (up) {
|
|
598
|
-
const sc = up.registry.getProviderScope(up.rec);
|
|
599
|
-
if (sc === "global" /* GLOBAL */) {
|
|
600
|
-
const v = up.registry.instances.get(d);
|
|
601
|
-
if (v === void 0) throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated in parent`);
|
|
602
|
-
return v;
|
|
603
|
-
} else {
|
|
604
|
-
await up.registry.buildIntoStore(d, up.rec, store);
|
|
605
|
-
return store.get(d);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
throw new Error(`${tokenName(d)} is not instantiated`);
|
|
609
|
-
}
|
|
610
|
-
/* -------------------- Public API -------------------- */
|
|
611
|
-
/**
|
|
612
|
-
* Get a GLOBAL-scoped provider by token.
|
|
613
|
-
*
|
|
614
|
-
* @throws If the provider is scoped or not registered
|
|
615
|
-
*/
|
|
616
|
-
get(token) {
|
|
617
|
-
if (this.instances.has(token)) return this.instances.get(token);
|
|
618
|
-
const rec = this.defs.get(token);
|
|
619
|
-
if (rec && this.getProviderScope(rec) !== "global" /* GLOBAL */) {
|
|
620
|
-
throw new Error(
|
|
621
|
-
`Provider ${tokenName(token)} is scoped (${this.getProviderScope(rec)}). Use getScoped(token, views).`
|
|
622
|
-
);
|
|
623
|
-
}
|
|
624
|
-
if (this.parentContainer) return this.parentContainer.get(token);
|
|
625
|
-
throw new Error(`Provider ${tokenName(token)} is not available in local or parent registries`);
|
|
626
|
-
}
|
|
627
|
-
/**
|
|
628
|
-
* Resolve a dependency by token or class.
|
|
629
|
-
* If not registered, attempts to construct the class directly.
|
|
630
|
-
*/
|
|
631
|
-
resolve(cls) {
|
|
632
|
-
const found = this.lookupDefInHierarchy(cls);
|
|
633
|
-
if (found) {
|
|
634
|
-
const { registry, rec } = found;
|
|
635
|
-
const sc = registry.getProviderScope(rec);
|
|
636
|
-
if (sc !== "global" /* GLOBAL */) {
|
|
637
|
-
throw new Error(
|
|
638
|
-
`Provider ${tokenName(cls)} is scoped (${sc}). Use getScoped(token, views) or buildViews(...).`
|
|
639
|
-
);
|
|
640
|
-
}
|
|
641
|
-
if (!registry.instances.has(cls)) {
|
|
642
|
-
throw new Error(
|
|
643
|
-
`Provider ${tokenName(cls)} (GLOBAL scope) is not instantiated. Call await container.ready first.`
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
return registry.instances.get(cls);
|
|
647
|
-
}
|
|
648
|
-
if (isClass(cls)) {
|
|
649
|
-
const instance = new cls();
|
|
650
|
-
const init = instance?.init;
|
|
651
|
-
if (typeof init === "function") {
|
|
652
|
-
const result = init.call(instance);
|
|
653
|
-
if (isPromise(result)) {
|
|
654
|
-
throw new Error(
|
|
655
|
-
`Cannot use resolve() with async init() for ${cls.name ?? "unknown"}. Use get() after initialize() instead.`
|
|
656
|
-
);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
return instance;
|
|
660
|
-
}
|
|
661
|
-
throw new Error(
|
|
662
|
-
`Cannot resolve ${tokenName(cls)}: not a registered GLOBAL provider and not a constructable class.`
|
|
663
|
-
);
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* Try to get a provider, returning undefined if not found.
|
|
667
|
-
*/
|
|
668
|
-
tryGet(token) {
|
|
669
|
-
try {
|
|
670
|
-
return this.get(token);
|
|
671
|
-
} catch {
|
|
672
|
-
return void 0;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
/**
|
|
676
|
-
* Normalize deprecated scopes to their modern equivalents.
|
|
677
|
-
*/
|
|
678
|
-
normalizeScope(scope) {
|
|
679
|
-
switch (scope) {
|
|
680
|
-
case "session" /* SESSION */:
|
|
681
|
-
case "request" /* REQUEST */:
|
|
682
|
-
return "context" /* CONTEXT */;
|
|
683
|
-
default:
|
|
684
|
-
return scope;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* Get the scope of a provider record.
|
|
689
|
-
*/
|
|
690
|
-
getProviderScope(rec) {
|
|
691
|
-
const rawScope = rec.metadata?.scope ?? "global" /* GLOBAL */;
|
|
692
|
-
return this.normalizeScope(rawScope);
|
|
693
|
-
}
|
|
694
|
-
/**
|
|
695
|
-
* Get all discovered dependencies for a provider record.
|
|
696
|
-
*/
|
|
697
|
-
discoveryDeps(rec) {
|
|
698
|
-
return providerDiscoveryDeps(rec, this.tokens, (k, phase) => depsOfClass(k, phase));
|
|
699
|
-
}
|
|
700
|
-
/**
|
|
701
|
-
* Get invocation tokens for a provider record.
|
|
702
|
-
*/
|
|
703
|
-
invocationTokens(_token, rec) {
|
|
704
|
-
return providerInvocationTokens(rec, (k, phase) => depsOfClass(k, phase));
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Return the singleton map as a read-only view.
|
|
708
|
-
*/
|
|
709
|
-
getAllSingletons() {
|
|
710
|
-
return this.instances;
|
|
711
|
-
}
|
|
712
|
-
/**
|
|
713
|
-
* Build provider views for different scopes.
|
|
714
|
-
*
|
|
715
|
-
* @param sessionKey - Unique key for this context/session
|
|
716
|
-
* @param contextProviders - Optional pre-built CONTEXT providers
|
|
717
|
-
* @returns Views with global and context provider maps
|
|
718
|
-
*/
|
|
719
|
-
async buildViews(sessionKey, contextProviders) {
|
|
720
|
-
const global = this.getAllSingletons();
|
|
721
|
-
const contextStore = new Map(contextProviders);
|
|
722
|
-
for (const token of this.order) {
|
|
723
|
-
const rec = this.defs.get(token);
|
|
724
|
-
if (!rec) continue;
|
|
725
|
-
if (this.getProviderScope(rec) !== "context" /* CONTEXT */) continue;
|
|
726
|
-
if (contextStore.has(token)) continue;
|
|
727
|
-
await this.buildIntoStoreWithViews(token, rec, contextStore, global);
|
|
728
|
-
}
|
|
729
|
-
return { global, context: contextStore };
|
|
730
|
-
}
|
|
731
|
-
async buildIntoStoreWithViews(token, rec, contextStore, globalStore) {
|
|
732
|
-
if (contextStore.has(token)) return;
|
|
733
|
-
try {
|
|
734
|
-
switch (rec.kind) {
|
|
735
|
-
case "VALUE" /* VALUE */: {
|
|
736
|
-
contextStore.set(token, rec.useValue);
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
case "FACTORY" /* FACTORY */: {
|
|
740
|
-
const deps = this.invocationTokens(token, rec);
|
|
741
|
-
const args = [];
|
|
742
|
-
for (const d of deps) {
|
|
743
|
-
args.push(await this.resolveFromViews(d, contextStore, globalStore));
|
|
744
|
-
}
|
|
745
|
-
const out = rec.useFactory(...args);
|
|
746
|
-
const val = isPromise(out) ? await this.withTimeout(out, this.asyncTimeoutMs, `${tokenName(token)}.useFactory(...)`) : out;
|
|
747
|
-
contextStore.set(token, val);
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
case "CLASS" /* CLASS */:
|
|
751
|
-
case "CLASS_TOKEN" /* CLASS_TOKEN */: {
|
|
752
|
-
const depsTokens = this.invocationTokens(token, rec);
|
|
753
|
-
const deps = [];
|
|
754
|
-
for (const d of depsTokens) {
|
|
755
|
-
deps.push(await this.resolveFromViews(d, contextStore, globalStore));
|
|
756
|
-
}
|
|
757
|
-
const klass = rec.kind === "CLASS" /* CLASS */ ? rec.useClass : rec.provide;
|
|
758
|
-
if (hasAsyncWith(klass)) {
|
|
759
|
-
const out = klass.with(...deps);
|
|
760
|
-
const val = isPromise(out) ? await this.withTimeout(out, this.asyncTimeoutMs, `${klass.name}.with(...)`) : out;
|
|
761
|
-
contextStore.set(token, val);
|
|
762
|
-
} else {
|
|
763
|
-
const instance = new klass(...deps);
|
|
764
|
-
const init = instance?.init;
|
|
765
|
-
if (typeof init === "function") {
|
|
766
|
-
const ret = init.call(instance);
|
|
767
|
-
if (isPromise(ret)) await this.withTimeout(ret, this.asyncTimeoutMs, `${klass.name}.init()`);
|
|
768
|
-
}
|
|
769
|
-
contextStore.set(token, instance);
|
|
770
|
-
}
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
} catch (e) {
|
|
775
|
-
const msg = e?.message ?? e;
|
|
776
|
-
throw new Error(`Failed constructing (context-scoped) ${tokenName(token)}: ${msg}`);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
async resolveFromViews(token, contextStore, globalStore) {
|
|
780
|
-
if (contextStore.has(token)) return contextStore.get(token);
|
|
781
|
-
if (globalStore.has(token)) return globalStore.get(token);
|
|
782
|
-
if (this.instances.has(token)) return this.instances.get(token);
|
|
783
|
-
const rec = this.defs.get(token);
|
|
784
|
-
if (rec) {
|
|
785
|
-
const scope = this.getProviderScope(rec);
|
|
786
|
-
if (scope === "global" /* GLOBAL */) {
|
|
787
|
-
throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated`);
|
|
788
|
-
} else {
|
|
789
|
-
await this.buildIntoStoreWithViews(token, rec, contextStore, globalStore);
|
|
790
|
-
return contextStore.get(token);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
const up = this.lookupDefInHierarchy(token);
|
|
794
|
-
if (up) {
|
|
795
|
-
const sc = up.registry.getProviderScope(up.rec);
|
|
796
|
-
if (sc === "global" /* GLOBAL */) {
|
|
797
|
-
const v = up.registry.instances.get(token);
|
|
798
|
-
if (v !== void 0) return v;
|
|
799
|
-
throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated in parent`);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
throw new Error(`Cannot resolve dependency ${tokenName(token)} from views`);
|
|
803
|
-
}
|
|
804
|
-
/**
|
|
805
|
-
* Get a provider from views.
|
|
806
|
-
*/
|
|
807
|
-
getScoped(token, views) {
|
|
808
|
-
if (views.context.has(token)) return views.context.get(token);
|
|
809
|
-
if (views.global.has(token)) return views.global.get(token);
|
|
810
|
-
if (this.instances.has(token)) return this.instances.get(token);
|
|
811
|
-
throw new Error(`Provider ${tokenName(token)} not found in views. Ensure it was built via buildViews().`);
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* Inject a pre-instantiated provider.
|
|
815
|
-
*/
|
|
816
|
-
injectProvider(injected) {
|
|
817
|
-
const rec = {
|
|
818
|
-
...injected,
|
|
819
|
-
kind: "INJECTED" /* INJECTED */
|
|
820
|
-
};
|
|
821
|
-
this.tokens.add(rec.provide);
|
|
822
|
-
this.defs.set(rec.provide, rec);
|
|
823
|
-
this.graph.set(rec.provide, /* @__PURE__ */ new Set());
|
|
824
|
-
this.instances.set(rec.provide, rec.value);
|
|
825
|
-
}
|
|
826
|
-
};
|
|
827
|
-
function createBasicNormalizer() {
|
|
828
|
-
return function normalizeProvider(item) {
|
|
829
|
-
if (isClass(item)) {
|
|
830
|
-
const staticMeta = item.metadata;
|
|
831
|
-
return {
|
|
832
|
-
kind: "CLASS_TOKEN" /* CLASS_TOKEN */,
|
|
833
|
-
provide: item,
|
|
834
|
-
metadata: staticMeta ?? { name: item.name ?? "UnknownProvider" }
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
const obj = item;
|
|
838
|
-
if ("useValue" in obj) {
|
|
839
|
-
if (!obj.provide) {
|
|
840
|
-
throw new Error(`Provider with useValue must have a 'provide' field`);
|
|
841
|
-
}
|
|
842
|
-
return {
|
|
843
|
-
kind: "VALUE" /* VALUE */,
|
|
844
|
-
provide: obj.provide,
|
|
845
|
-
useValue: obj.useValue,
|
|
846
|
-
metadata: obj.metadata ?? { name: "ValueProvider" }
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
if ("useFactory" in obj) {
|
|
850
|
-
if (!obj.provide) {
|
|
851
|
-
throw new Error(`Provider with useFactory must have a 'provide' field`);
|
|
852
|
-
}
|
|
853
|
-
return {
|
|
854
|
-
kind: "FACTORY" /* FACTORY */,
|
|
855
|
-
provide: obj.provide,
|
|
856
|
-
inject: obj.inject ?? (() => []),
|
|
857
|
-
useFactory: obj.useFactory,
|
|
858
|
-
metadata: obj.metadata ?? { name: "FactoryProvider" }
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
if ("useClass" in obj) {
|
|
862
|
-
if (!obj.provide) {
|
|
863
|
-
throw new Error(`Provider with useClass must have a 'provide' field`);
|
|
864
|
-
}
|
|
865
|
-
return {
|
|
866
|
-
kind: "CLASS" /* CLASS */,
|
|
867
|
-
provide: obj.provide,
|
|
868
|
-
useClass: obj.useClass,
|
|
869
|
-
metadata: obj.metadata ?? { name: obj.useClass?.name ?? "ClassProvider" }
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
throw new Error(`Cannot normalize provider: unrecognized format`);
|
|
873
|
-
};
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// libs/di/src/registry/indexed.registry.ts
|
|
877
|
-
var IndexedRegistry = class extends RegistryAbstract {
|
|
878
|
-
/* -------------------- Local vs. Adopted -------------------- */
|
|
879
|
-
/** Entries created by this registry */
|
|
880
|
-
localRows = [];
|
|
881
|
-
/** Entries adopted from child registries */
|
|
882
|
-
adopted = /* @__PURE__ */ new Map();
|
|
883
|
-
/** Set of child registries */
|
|
884
|
-
children = /* @__PURE__ */ new Set();
|
|
885
|
-
/** Unsubscribe functions for child subscriptions */
|
|
886
|
-
childSubscriptions = /* @__PURE__ */ new Map();
|
|
887
|
-
/* -------------------- Common Indexes -------------------- */
|
|
888
|
-
/** O(1) lookup by qualified ID */
|
|
889
|
-
byQualifiedId = /* @__PURE__ */ new Map();
|
|
890
|
-
/** O(1) lookup by base name (array for conflicts) */
|
|
891
|
-
byName = /* @__PURE__ */ new Map();
|
|
892
|
-
/** O(1) lookup by owner key */
|
|
893
|
-
byOwner = /* @__PURE__ */ new Map();
|
|
894
|
-
/** O(1) lookup by owner:name composite */
|
|
895
|
-
byOwnerAndName = /* @__PURE__ */ new Map();
|
|
896
|
-
/* -------------------- Change Events -------------------- */
|
|
897
|
-
/** Version counter for change tracking */
|
|
898
|
-
version = 0;
|
|
899
|
-
/** Event emitter for change notifications */
|
|
900
|
-
emitter;
|
|
901
|
-
/**
|
|
902
|
-
* Create an event emitter for this registry.
|
|
903
|
-
* Override to use custom emitter implementation.
|
|
904
|
-
*/
|
|
905
|
-
createEmitter() {
|
|
906
|
-
const handlers = /* @__PURE__ */ new Set();
|
|
907
|
-
return {
|
|
908
|
-
emit: (event) => handlers.forEach((h) => h(event)),
|
|
909
|
-
on: (handler) => {
|
|
910
|
-
handlers.add(handler);
|
|
911
|
-
return () => handlers.delete(handler);
|
|
912
|
-
}
|
|
913
|
-
};
|
|
914
|
-
}
|
|
915
|
-
constructor(name, providers, metadata, auto = true) {
|
|
916
|
-
super(name, providers, metadata, auto);
|
|
917
|
-
this.emitter = this.createEmitter();
|
|
918
|
-
}
|
|
919
|
-
/* -------------------- Adoption -------------------- */
|
|
920
|
-
/**
|
|
921
|
-
* Adopt entries from a child registry.
|
|
922
|
-
*
|
|
923
|
-
* @param child - Child registry to adopt from
|
|
924
|
-
* @param childOwner - Owner reference for lineage
|
|
925
|
-
*/
|
|
926
|
-
adoptFromChild(child, childOwner) {
|
|
927
|
-
const childRows = child.listAllIndexed();
|
|
928
|
-
const adoptedRows = childRows.map((r) => this.relineage(r, childOwner.lineage));
|
|
929
|
-
this.adopted.set(child, adoptedRows);
|
|
930
|
-
this.children.add(child);
|
|
931
|
-
const unsubscribe = child.subscribe({ immediate: false }, () => {
|
|
932
|
-
const latest = child.listAllIndexed().map((r) => this.relineage(r, childOwner.lineage));
|
|
933
|
-
this.adopted.set(child, latest);
|
|
934
|
-
this.reindex();
|
|
935
|
-
this.bump("reset");
|
|
936
|
-
});
|
|
937
|
-
this.childSubscriptions.set(child, unsubscribe);
|
|
938
|
-
this.reindex();
|
|
939
|
-
this.bump("reset");
|
|
940
|
-
}
|
|
941
|
-
/**
|
|
942
|
-
* Remove a child registry.
|
|
943
|
-
*/
|
|
944
|
-
removeChild(child) {
|
|
945
|
-
const unsub = this.childSubscriptions.get(child);
|
|
946
|
-
if (unsub) unsub();
|
|
947
|
-
this.childSubscriptions.delete(child);
|
|
948
|
-
this.children.delete(child);
|
|
949
|
-
this.adopted.delete(child);
|
|
950
|
-
this.reindex();
|
|
951
|
-
this.bump("reset");
|
|
952
|
-
}
|
|
953
|
-
/* -------------------- Lineage -------------------- */
|
|
954
|
-
/**
|
|
955
|
-
* Relineage an entry with a new prefix.
|
|
956
|
-
* Creates a new indexed entry with updated lineage.
|
|
957
|
-
*/
|
|
958
|
-
relineage(row, prepend) {
|
|
959
|
-
const merged = [...prepend, ...row.lineage];
|
|
960
|
-
const lineage = this.dedupLineage(merged);
|
|
961
|
-
const ownerKey = this.ownerKeyOf(lineage);
|
|
962
|
-
const qualifiedName = this.qualifiedNameOf(row.baseName, lineage);
|
|
963
|
-
const qualifiedId = `${ownerKey}:${row.token.toString()}`;
|
|
964
|
-
return {
|
|
965
|
-
...row,
|
|
966
|
-
lineage,
|
|
967
|
-
ownerKey,
|
|
968
|
-
qualifiedName,
|
|
969
|
-
qualifiedId
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* Remove adjacent duplicate segments from lineage.
|
|
974
|
-
*/
|
|
975
|
-
dedupLineage(lineage) {
|
|
976
|
-
const result = [];
|
|
977
|
-
for (const seg of lineage) {
|
|
978
|
-
const last = result[result.length - 1];
|
|
979
|
-
if (!last || last.type !== seg.type || last.id !== seg.id) {
|
|
980
|
-
result.push(seg);
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
return result;
|
|
984
|
-
}
|
|
985
|
-
/**
|
|
986
|
-
* Generate owner key from lineage.
|
|
987
|
-
*/
|
|
988
|
-
ownerKeyOf(lineage) {
|
|
989
|
-
return lineage.map((s) => `${s.type}:${s.id}`).join("/");
|
|
990
|
-
}
|
|
991
|
-
/**
|
|
992
|
-
* Generate qualified name from base name and lineage.
|
|
993
|
-
*/
|
|
994
|
-
qualifiedNameOf(baseName, lineage) {
|
|
995
|
-
const prefix = lineage.map((s) => s.name || s.id).join("/");
|
|
996
|
-
return prefix ? `${prefix}/${baseName}` : baseName;
|
|
997
|
-
}
|
|
998
|
-
/* -------------------- Indexing -------------------- */
|
|
999
|
-
/**
|
|
1000
|
-
* Rebuild all indexes from local and adopted entries.
|
|
1001
|
-
*/
|
|
1002
|
-
reindex() {
|
|
1003
|
-
const allRows = [...this.localRows];
|
|
1004
|
-
for (const rows of this.adopted.values()) {
|
|
1005
|
-
allRows.push(...rows);
|
|
1006
|
-
}
|
|
1007
|
-
this.byQualifiedId.clear();
|
|
1008
|
-
this.byName.clear();
|
|
1009
|
-
this.byOwner.clear();
|
|
1010
|
-
this.byOwnerAndName.clear();
|
|
1011
|
-
for (const row of allRows) {
|
|
1012
|
-
this.byQualifiedId.set(row.qualifiedId, row);
|
|
1013
|
-
const nameList = this.byName.get(row.baseName) ?? [];
|
|
1014
|
-
nameList.push(row);
|
|
1015
|
-
this.byName.set(row.baseName, nameList);
|
|
1016
|
-
const ownerList = this.byOwner.get(row.ownerKey) ?? [];
|
|
1017
|
-
ownerList.push(row);
|
|
1018
|
-
this.byOwner.set(row.ownerKey, ownerList);
|
|
1019
|
-
const ownerNameKey = `${row.ownerKey}:${row.baseName}`;
|
|
1020
|
-
this.byOwnerAndName.set(ownerNameKey, row);
|
|
1021
|
-
}
|
|
1022
|
-
this.buildIndexes(allRows);
|
|
1023
|
-
}
|
|
1024
|
-
/* -------------------- Change Events -------------------- */
|
|
1025
|
-
/**
|
|
1026
|
-
* Subscribe to change events.
|
|
1027
|
-
*/
|
|
1028
|
-
subscribe(opts, callback) {
|
|
1029
|
-
if (opts.immediate) {
|
|
1030
|
-
callback({
|
|
1031
|
-
kind: "reset",
|
|
1032
|
-
changeScope: "global",
|
|
1033
|
-
version: this.version,
|
|
1034
|
-
snapshot: this.listAllIndexed()
|
|
1035
|
-
});
|
|
1036
|
-
}
|
|
1037
|
-
return this.emitter.on((e) => {
|
|
1038
|
-
if (opts.filter) {
|
|
1039
|
-
const filtered = e.snapshot.filter((row) => opts.filter(row.instance));
|
|
1040
|
-
callback({ ...e, snapshot: filtered });
|
|
1041
|
-
} else {
|
|
1042
|
-
callback(e);
|
|
1043
|
-
}
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
/**
|
|
1047
|
-
* Emit a change event.
|
|
1048
|
-
*/
|
|
1049
|
-
bump(kind) {
|
|
1050
|
-
const version = ++this.version;
|
|
1051
|
-
this.emitter.emit({
|
|
1052
|
-
kind,
|
|
1053
|
-
changeScope: "global",
|
|
1054
|
-
version,
|
|
1055
|
-
snapshot: this.listAllIndexed()
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
|
-
/* -------------------- Lookups -------------------- */
|
|
1059
|
-
/**
|
|
1060
|
-
* Find by base name (returns first match).
|
|
1061
|
-
*/
|
|
1062
|
-
findByName(name) {
|
|
1063
|
-
const rows = this.byName.get(name);
|
|
1064
|
-
return rows?.[0]?.instance;
|
|
1065
|
-
}
|
|
1066
|
-
/**
|
|
1067
|
-
* Find all by base name.
|
|
1068
|
-
*/
|
|
1069
|
-
findAllByName(name) {
|
|
1070
|
-
const rows = this.byName.get(name);
|
|
1071
|
-
return rows?.map((r) => r.instance) ?? [];
|
|
1072
|
-
}
|
|
1073
|
-
/**
|
|
1074
|
-
* Find by qualified ID.
|
|
1075
|
-
*/
|
|
1076
|
-
findByQualifiedId(qualifiedId) {
|
|
1077
|
-
return this.byQualifiedId.get(qualifiedId)?.instance;
|
|
1078
|
-
}
|
|
1079
|
-
/**
|
|
1080
|
-
* List all indexed entries by owner.
|
|
1081
|
-
*/
|
|
1082
|
-
listByOwner(ownerKey) {
|
|
1083
|
-
return this.byOwner.get(ownerKey) ?? [];
|
|
1084
|
-
}
|
|
1085
|
-
/**
|
|
1086
|
-
* List all indexed entries.
|
|
1087
|
-
*/
|
|
1088
|
-
listAllIndexed() {
|
|
1089
|
-
const all = [...this.localRows];
|
|
1090
|
-
for (const rows of this.adopted.values()) {
|
|
1091
|
-
all.push(...rows);
|
|
1092
|
-
}
|
|
1093
|
-
return all;
|
|
1094
|
-
}
|
|
1095
|
-
/**
|
|
1096
|
-
* List all instances.
|
|
1097
|
-
*/
|
|
1098
|
-
listAllInstances() {
|
|
1099
|
-
return this.listAllIndexed().map((r) => r.instance);
|
|
1100
|
-
}
|
|
1101
|
-
/**
|
|
1102
|
-
* Check if registry has any entries.
|
|
1103
|
-
*/
|
|
1104
|
-
hasAny() {
|
|
1105
|
-
return this.localRows.length > 0 || this.adopted.size > 0;
|
|
1106
|
-
}
|
|
1107
|
-
/* -------------------- Cleanup -------------------- */
|
|
1108
|
-
/**
|
|
1109
|
-
* Dispose of this registry and clean up subscriptions.
|
|
1110
|
-
*/
|
|
1111
|
-
dispose() {
|
|
1112
|
-
for (const unsub of this.childSubscriptions.values()) {
|
|
1113
|
-
unsub();
|
|
1114
|
-
}
|
|
1115
|
-
this.childSubscriptions.clear();
|
|
1116
|
-
this.children.clear();
|
|
1117
|
-
this.adopted.clear();
|
|
1118
|
-
}
|
|
1119
|
-
};
|
|
1120
|
-
|
|
1121
|
-
// libs/di/src/registry/simple.registry.ts
|
|
1122
|
-
var SimpleRegistry = class extends RegistryAbstract {
|
|
1123
|
-
constructor(name, providers, metadata, auto = true) {
|
|
1124
|
-
super(name, providers, metadata, auto);
|
|
1125
|
-
}
|
|
1126
|
-
/**
|
|
1127
|
-
* Get all entries as an array.
|
|
1128
|
-
*/
|
|
1129
|
-
getAll() {
|
|
1130
|
-
return [...this.instances.values()];
|
|
1131
|
-
}
|
|
1132
|
-
/**
|
|
1133
|
-
* Get entry count.
|
|
1134
|
-
*/
|
|
1135
|
-
count() {
|
|
1136
|
-
return this.instances.size;
|
|
1137
|
-
}
|
|
1138
|
-
};
|
|
1139
|
-
|
|
1140
|
-
// libs/di/src/decorators/async-with.decorator.ts
|
|
1141
|
-
import "reflect-metadata";
|
|
1142
|
-
function AsyncWith(tokensFactory) {
|
|
1143
|
-
return (target) => {
|
|
1144
|
-
Reflect.defineMetadata(META_ASYNC_WITH, true, target);
|
|
1145
|
-
Reflect.defineMetadata(META_ASYNC_WITH_TOKENS, tokensFactory, target);
|
|
1146
|
-
};
|
|
1147
|
-
}
|
|
1148
|
-
export {
|
|
1149
|
-
AsyncWith,
|
|
1150
|
-
DESIGN_PARAMTYPES,
|
|
1151
|
-
DiContainer,
|
|
1152
|
-
DiTokens,
|
|
1153
|
-
IndexedRegistry,
|
|
1154
|
-
META_ASYNC_WITH,
|
|
1155
|
-
META_ASYNC_WITH_TOKENS,
|
|
1156
|
-
ProviderKind,
|
|
1157
|
-
ProviderScope,
|
|
1158
|
-
RegistryAbstract,
|
|
1159
|
-
SimpleRegistry,
|
|
1160
|
-
createProviderNormalizer,
|
|
1161
|
-
createTokenFactory,
|
|
1162
|
-
depsOfClass,
|
|
1163
|
-
depsOfFunc,
|
|
1164
|
-
getAsyncWithTokens,
|
|
1165
|
-
getMetadata,
|
|
1166
|
-
hasAsyncWith,
|
|
1167
|
-
isClass,
|
|
1168
|
-
isPromise,
|
|
1169
|
-
providerDiscoveryDeps,
|
|
1170
|
-
providerInvocationTokens,
|
|
1171
|
-
providerMetadataSchema,
|
|
1172
|
-
readWithParamTypes,
|
|
1173
|
-
setMetadata,
|
|
1174
|
-
tokenName
|
|
1175
|
-
};
|
|
1
|
+
function createTokenFactory(options={}){const prefix=options.prefix??"DI";return{type:name=>Symbol.for(`${prefix}:type:${name}`),meta:name=>Symbol.for(`${prefix}:meta:${name}`)}}var DiTokens=createTokenFactory({prefix:"DI"});var DESIGN_PARAMTYPES="design:paramtypes";var META_ASYNC_WITH=Symbol("di:async-with");var META_ASYNC_WITH_TOKENS=Symbol("di:async-with-tokens");var ProviderKind=(ProviderKind2=>{ProviderKind2["CLASS_TOKEN"]="CLASS_TOKEN";ProviderKind2["CLASS"]="CLASS";ProviderKind2["VALUE"]="VALUE";ProviderKind2["FACTORY"]="FACTORY";ProviderKind2["INJECTED"]="INJECTED";return ProviderKind2})(ProviderKind||{});var ProviderScope=(ProviderScope2=>{ProviderScope2["GLOBAL"]="global";ProviderScope2["CONTEXT"]="context";ProviderScope2["SESSION"]="session";ProviderScope2["REQUEST"]="request";return ProviderScope2})(ProviderScope||{});import{z}from"@frontmcp/lazy-zod";var providerMetadataSchema=z.object({id:z.string().optional(),name:z.string().min(1),description:z.string().optional(),scope:z.nativeEnum(ProviderScope).optional().default("global")}).passthrough();import"reflect-metadata";function getMetadata(key,target,propertyKey){return propertyKey!==void 0?Reflect.getMetadata(key,target,propertyKey):Reflect.getMetadata(key,target)}function setMetadata(key,value,target,propertyKey){if(propertyKey!==void 0){Reflect.defineMetadata(key,value,target,propertyKey)}else{Reflect.defineMetadata(key,value,target)}}function hasAsyncWith(klass){return!!getMetadata(META_ASYNC_WITH,klass)}var tokenName=t=>typeof t==="function"&&!!t.prototype?t.name:t?.name??"[ref]";var isClass=x=>typeof x==="function"&&!!x.prototype;var isPromise=v=>!!v&&typeof v.then==="function";function getAsyncWithTokens(klass){const resolver=getMetadata(META_ASYNC_WITH_TOKENS,klass);if(!resolver)return null;const arr=resolver()??[];return arr.filter(isClass)}function readWithParamTypes(klass,forWhat){const lazy=getAsyncWithTokens(klass);if(lazy)return lazy;const meta=getMetadata(DESIGN_PARAMTYPES,klass,"with");if(meta===void 0){const where=`${klass.name}.with(...)`;const hint="Add @AsyncWith(() => [DepA, DepB]) to lazily declare deps, or decorate 'with' so TypeScript emits design:paramtypes.";throw new Error(`Cannot discover ${forWhat} deps for ${where}. ${hint}`)}const tdzIdx=meta.findIndex(t=>t===void 0);if(tdzIdx!==-1){const where=`${klass.name}.with(...)`;throw new Error(`Unresolved dependency at param #${tdzIdx} in ${where} (ESM circular import / TDZ).
|
|
2
|
+
Fix: add @AsyncWith(() => [/* deps */]) on ${klass.name} to avoid TDZ, or refactor the import cycle.`)}return meta.filter(isClass)}function depsOfClass(klass,phase){if(hasAsyncWith(klass))return readWithParamTypes(klass,phase);const ctorMeta=getMetadata(DESIGN_PARAMTYPES,klass)??[];const tdzIdx=ctorMeta.findIndex(t=>t===void 0);if(tdzIdx!==-1){throw new Error(`Unresolved constructor dependency in ${klass.name} at param #${tdzIdx} (ESM circular import / TDZ).
|
|
3
|
+
Fix: convert this provider to @AsyncWith(() => [/* deps */]) and use a static with(...), or break the import cycle.`)}return ctorMeta.filter(isClass)}function depsOfFunc(fn,phase){const[_ignoreInput,...fnMeta]=getMetadata(DESIGN_PARAMTYPES,fn)??[];const tdzIdx=fnMeta.findIndex(t=>t===void 0);if(tdzIdx!==-1){throw new Error(`Unresolved function dependency in ${fn.name} at param #${tdzIdx} (ESM circular import / TDZ).
|
|
4
|
+
`)}return fnMeta.filter(isClass)}function createProviderNormalizer(options){const{tokens}=options;return function normalizeProvider(item){if(isClass(item)){const isProviderClass=getMetadata(tokens.type,item);if(isProviderClass){const metadata={name:getMetadata(tokens.name,item)??item.name??"UnknownProvider",scope:getMetadata(tokens.scope,item),description:tokens.description?getMetadata(tokens.description,item):void 0,id:tokens.id?getMetadata(tokens.id,item):void 0};return{kind:"CLASS_TOKEN",provide:item,metadata}}const staticMeta=item.metadata;return{kind:"CLASS_TOKEN",provide:item,metadata:staticMeta??{name:item.name??"UnknownProvider"}}}const obj=item;if("useValue"in obj||"useFactory"in obj||"useClass"in obj){if(!obj.provide){const name=obj.name??"[object]";throw new Error(`Provider '${name}' is missing 'provide'.`)}}if("useValue"in obj){return{kind:"VALUE",provide:obj.provide,useValue:obj.useValue,metadata:obj.metadata??{name:"ValueProvider"}}}if("useFactory"in obj){return{kind:"FACTORY",provide:obj.provide,inject:obj.inject??(()=>[]),useFactory:obj.useFactory,metadata:obj.metadata??{name:"FactoryProvider"}}}if("useClass"in obj){return{kind:"CLASS",provide:obj.provide,useClass:obj.useClass,metadata:obj.metadata??{name:obj.useClass?.name??"ClassProvider"}}}let itemStr;try{itemStr=JSON.stringify(item)}catch{itemStr=String(item)}throw new Error(`Cannot normalize provider: unrecognized format ${itemStr}`)}}function providerDiscoveryDeps(rec,localTokens,depsOfClassFn){switch(rec.kind){case"VALUE":case"INJECTED":return[];case"FACTORY":{const all=[...rec.inject()];return all.filter(d=>localTokens.has(d))}case"CLASS":return depsOfClassFn(rec.useClass,"discovery");case"CLASS_TOKEN":return depsOfClassFn(rec.provide,"discovery")}}function providerInvocationTokens(rec,depsOfClassFn){switch(rec.kind){case"VALUE":case"INJECTED":return[];case"FACTORY":return[...rec.inject()];case"CLASS":return depsOfClassFn(rec.useClass,"invocation");case"CLASS_TOKEN":return depsOfClassFn(rec.provide,"invocation")}}var RegistryAbstract=class{asyncTimeoutMs=3e4;ready;providers;list;tokens;defs;graph;instances=new Map;constructor(name,providers,metadata,auto=true){if(providers&&typeof providers.addRegistry==="function"){providers.addRegistry(name,this)}this.providers=providers;this.list=metadata;const{tokens,defs,graph}=this.buildMap(metadata);this.tokens=tokens;this.defs=defs;this.graph=graph;if(auto){this.buildGraph();this.ready=this.initialize()}else{this.ready=Promise.resolve()}}hasAny(){return this.instances.size>0}getAllInstances(){return this.instances}};import"reflect-metadata";var DiContainer=class extends RegistryAbstract{order=new Set;normalizeProvider;parentContainer;constructor(providers,parent,options={}){const normalizer=options.providerTokens?createProviderNormalizer({tokens:options.providerTokens}):createBasicNormalizer();super("DiContainer",parent,providers,false);this.normalizeProvider=normalizer;this.parentContainer=parent;if(options.asyncTimeoutMs){this.asyncTimeoutMs=options.asyncTimeoutMs}this.buildGraph();this.topoSort();this.ready=this.initialize()}lookupDefInHierarchy(token){if(this.defs.has(token)){return{registry:this,rec:this.defs.get(token)}}return this.parentContainer?.lookupDefInHierarchy(token)}resolveDefaultFromHierarchy(token){const found=this.lookupDefInHierarchy(token);if(!found){throw new Error(`Cannot resolve token ${tokenName(token)}: not registered in hierarchy.`)}const{registry,rec}=found;const sc=registry.getProviderScope(rec);if(sc!=="global"){throw new Error(`Dependency ${tokenName(token)} is scoped (${sc}) in ${registry.constructor.name}; cannot use as GLOBAL.`)}const inst=registry.instances.get(token);if(inst===void 0){throw new Error(`Dependency ${tokenName(token)} (GLOBAL) is not instantiated in ${registry.constructor.name}`)}return inst}buildMap(list){const tokens=new Set;const defs=new Map;const graph=new Map;const normalize=this.normalizeProvider??createBasicNormalizer();for(const raw of list){const rec=normalize(raw);const provide=rec.provide;tokens.add(provide);defs.set(provide,rec);graph.set(provide,new Set)}return{tokens,defs,graph}}buildGraph(){for(const token of this.tokens){const rec=this.defs.get(token);const deps=this.discoveryDeps(rec);for(const d of deps){const isLocal=this.tokens.has(d);const up=isLocal?void 0:this.lookupDefInHierarchy(d);if(!isLocal&&!up){throw new Error(`Provider ${tokenName(token)} depends on ${tokenName(d)}, which is not registered (local or parent).`)}const depRec=isLocal?this.defs.get(d):up.rec;const depScope=isLocal?this.getProviderScope(depRec):up.registry.getProviderScope(depRec);if(this.getProviderScope(rec)==="global"&&depScope!=="global"){throw new Error(`Invalid dependency: GLOBAL-scoped provider ${tokenName(token)} cannot depend on scoped provider ${tokenName(d)}.`)}if(isLocal){this.graph.get(token).add(d)}}}}topoSort(){const WHITE=0,GRAY=1,BLACK=2;const color=new Map;const order=new Set;const path=[];const dfs=n=>{color.set(n,GRAY);path.push(n);for(const dep of this.graph.get(n)??[]){const c=color.get(dep)??WHITE;if(c===GRAY){const idx=path.findIndex(p=>p===dep);const cycle=[...path.slice(idx),dep].map(t=>tokenName(t)).join(" -> ");throw new Error(`Dependency cycle detected: ${cycle}`)}if(c===WHITE)dfs(dep)}color.set(n,BLACK);order.add(n);path.pop()};for(const n of this.graph.keys()){if((color.get(n)??WHITE)===WHITE)dfs(n)}this.order=order}async initialize(opts){const force=!!opts?.force;const only=opts?.onlyTokens?new Set(opts.onlyTokens):null;for(const token of this.order){const rec=this.defs.get(token);if(this.getProviderScope(rec)!=="global")continue;if(only&&!only.has(token))continue;if(!force&&this.instances.has(token))continue;if(force&&this.instances.has(token)){this.instances.delete(token)}try{await this.instantiateOne(token,rec)}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing ${tokenName(token)}: ${msg}`)}}}async withTimeout(p,ms,label){if(ms<=0||!Number.isFinite(ms))return p;let timer;try{const tm=new Promise((_,rej)=>timer=setTimeout(()=>rej(new Error(`Timeout after ${ms}ms: ${label}`)),ms));return await Promise.race([p,tm])}finally{clearTimeout(timer)}}async resolveFactoryArg(t,store){if(store.has(t))return store.get(t);if(this.instances.has(t))return this.instances.get(t);const rec=this.defs.get(t);if(rec){const sc=this.getProviderScope(rec);if(sc==="global"){if(!this.instances.has(t)){throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated`)}return this.instances.get(t)}else{await this.buildIntoStore(t,rec,store);return store.get(t)}}const up=this.lookupDefInHierarchy(t);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const inst=up.registry.instances.get(t);if(inst===void 0){throw new Error(`Dependency ${tokenName(t)} (GLOBAL scope) is not instantiated in parent`)}return inst}else{await up.registry.buildIntoStore(t,up.rec,store);return store.get(t)}}if(isClass(t)){if(hasAsyncWith(t)){const out=t.with();return isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${t.name}.with(...)`):out}const instance=new t;const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${t.name}.init()`)}return instance}throw new Error(`Cannot resolve token ${tokenName(t)}: not registered in hierarchy and not constructable.`)}async instantiateOne(token,rec){switch(rec.kind){case"VALUE":{this.instances.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFactoryArg(d,this.instances))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;this.instances.set(token,val);return}case"CLASS":{const depTokens=this.invocationTokens(token,rec);const deps=depTokens.map(t=>this.resolveDefaultFromHierarchy(t));const klass=rec.useClass;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;this.instances.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}this.instances.set(token,instance)}return}case"CLASS_TOKEN":{const depTokens=this.invocationTokens(token,rec);const deps=depTokens.map(t=>this.resolveDefaultFromHierarchy(t));const klass=rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;this.instances.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}this.instances.set(token,instance)}return}}}async buildIntoStore(token,rec,store){if(store.has(token))return;try{switch(rec.kind){case"VALUE":{store.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFactoryArg(d,store))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;store.set(token,val);return}case"CLASS":case"CLASS_TOKEN":{const depsTokens=this.invocationTokens(token,rec);const deps=[];for(const d of depsTokens){deps.push(await this.resolveManagedForClass(d,store))}const klass=rec.kind==="CLASS"?rec.useClass:rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;store.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}store.set(token,instance)}return}}}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing (scoped) ${tokenName(token)}: ${msg}`)}}async resolveManagedForClass(d,store){if(store.has(d))return store.get(d);if(this.instances.has(d))return this.instances.get(d);const depRec=this.defs.get(d);if(depRec){const sc=this.getProviderScope(depRec);if(sc==="global"){const v=this.instances.get(d);if(v===void 0)throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated`);return v}else{await this.buildIntoStore(d,depRec,store);return store.get(d)}}const up=this.lookupDefInHierarchy(d);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const v=up.registry.instances.get(d);if(v===void 0)throw new Error(`${tokenName(d)} (GLOBAL scope) is not instantiated in parent`);return v}else{await up.registry.buildIntoStore(d,up.rec,store);return store.get(d)}}throw new Error(`${tokenName(d)} is not instantiated`)}get(token){if(this.instances.has(token))return this.instances.get(token);const rec=this.defs.get(token);if(rec&&this.getProviderScope(rec)!=="global"){throw new Error(`Provider ${tokenName(token)} is scoped (${this.getProviderScope(rec)}). Use getScoped(token, views).`)}if(this.parentContainer)return this.parentContainer.get(token);throw new Error(`Provider ${tokenName(token)} is not available in local or parent registries`)}resolve(cls){const found=this.lookupDefInHierarchy(cls);if(found){const{registry,rec}=found;const sc=registry.getProviderScope(rec);if(sc!=="global"){throw new Error(`Provider ${tokenName(cls)} is scoped (${sc}). Use getScoped(token, views) or buildViews(...).`)}if(!registry.instances.has(cls)){throw new Error(`Provider ${tokenName(cls)} (GLOBAL scope) is not instantiated. Call await container.ready first.`)}return registry.instances.get(cls)}if(isClass(cls)){const instance=new cls;const init=instance?.init;if(typeof init==="function"){const result=init.call(instance);if(isPromise(result)){throw new Error(`Cannot use resolve() with async init() for ${cls.name??"unknown"}. Use get() after initialize() instead.`)}}return instance}throw new Error(`Cannot resolve ${tokenName(cls)}: not a registered GLOBAL provider and not a constructable class.`)}tryGet(token){try{return this.get(token)}catch{return void 0}}normalizeScope(scope){switch(scope){case"session":case"request":return"context";default:return scope}}getProviderScope(rec){const rawScope=rec.metadata?.scope??"global";return this.normalizeScope(rawScope)}discoveryDeps(rec){return providerDiscoveryDeps(rec,this.tokens,(k,phase)=>depsOfClass(k,phase))}invocationTokens(_token,rec){return providerInvocationTokens(rec,(k,phase)=>depsOfClass(k,phase))}getAllSingletons(){return this.instances}async buildViews(sessionKey,contextProviders){const global=this.getAllSingletons();const contextStore=new Map(contextProviders);for(const token of this.order){const rec=this.defs.get(token);if(!rec)continue;if(this.getProviderScope(rec)!=="context")continue;if(contextStore.has(token))continue;await this.buildIntoStoreWithViews(token,rec,contextStore,global)}return{global,context:contextStore}}async buildIntoStoreWithViews(token,rec,contextStore,globalStore){if(contextStore.has(token))return;try{switch(rec.kind){case"VALUE":{contextStore.set(token,rec.useValue);return}case"FACTORY":{const deps=this.invocationTokens(token,rec);const args=[];for(const d of deps){args.push(await this.resolveFromViews(d,contextStore,globalStore))}const out=rec.useFactory(...args);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${tokenName(token)}.useFactory(...)`):out;contextStore.set(token,val);return}case"CLASS":case"CLASS_TOKEN":{const depsTokens=this.invocationTokens(token,rec);const deps=[];for(const d of depsTokens){deps.push(await this.resolveFromViews(d,contextStore,globalStore))}const klass=rec.kind==="CLASS"?rec.useClass:rec.provide;if(hasAsyncWith(klass)){const out=klass.with(...deps);const val=isPromise(out)?await this.withTimeout(out,this.asyncTimeoutMs,`${klass.name}.with(...)`):out;contextStore.set(token,val)}else{const instance=new klass(...deps);const init=instance?.init;if(typeof init==="function"){const ret=init.call(instance);if(isPromise(ret))await this.withTimeout(ret,this.asyncTimeoutMs,`${klass.name}.init()`)}contextStore.set(token,instance)}return}}}catch(e){const msg=e?.message??e;throw new Error(`Failed constructing (context-scoped) ${tokenName(token)}: ${msg}`)}}async resolveFromViews(token,contextStore,globalStore){if(contextStore.has(token))return contextStore.get(token);if(globalStore.has(token))return globalStore.get(token);if(this.instances.has(token))return this.instances.get(token);const rec=this.defs.get(token);if(rec){const scope=this.getProviderScope(rec);if(scope==="global"){throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated`)}else{await this.buildIntoStoreWithViews(token,rec,contextStore,globalStore);return contextStore.get(token)}}const up=this.lookupDefInHierarchy(token);if(up){const sc=up.registry.getProviderScope(up.rec);if(sc==="global"){const v=up.registry.instances.get(token);if(v!==void 0)return v;throw new Error(`GLOBAL dependency ${tokenName(token)} is not instantiated in parent`)}}throw new Error(`Cannot resolve dependency ${tokenName(token)} from views`)}getScoped(token,views){if(views.context.has(token))return views.context.get(token);if(views.global.has(token))return views.global.get(token);if(this.instances.has(token))return this.instances.get(token);throw new Error(`Provider ${tokenName(token)} not found in views. Ensure it was built via buildViews().`)}injectProvider(injected){const rec={...injected,kind:"INJECTED"};this.tokens.add(rec.provide);this.defs.set(rec.provide,rec);this.graph.set(rec.provide,new Set);this.instances.set(rec.provide,rec.value)}};function createBasicNormalizer(){return function normalizeProvider(item){if(isClass(item)){const staticMeta=item.metadata;return{kind:"CLASS_TOKEN",provide:item,metadata:staticMeta??{name:item.name??"UnknownProvider"}}}const obj=item;if("useValue"in obj){if(!obj.provide){throw new Error(`Provider with useValue must have a 'provide' field`)}return{kind:"VALUE",provide:obj.provide,useValue:obj.useValue,metadata:obj.metadata??{name:"ValueProvider"}}}if("useFactory"in obj){if(!obj.provide){throw new Error(`Provider with useFactory must have a 'provide' field`)}return{kind:"FACTORY",provide:obj.provide,inject:obj.inject??(()=>[]),useFactory:obj.useFactory,metadata:obj.metadata??{name:"FactoryProvider"}}}if("useClass"in obj){if(!obj.provide){throw new Error(`Provider with useClass must have a 'provide' field`)}return{kind:"CLASS",provide:obj.provide,useClass:obj.useClass,metadata:obj.metadata??{name:obj.useClass?.name??"ClassProvider"}}}throw new Error(`Cannot normalize provider: unrecognized format`)}}var IndexedRegistry=class extends RegistryAbstract{localRows=[];adopted=new Map;children=new Set;childSubscriptions=new Map;byQualifiedId=new Map;byName=new Map;byOwner=new Map;byOwnerAndName=new Map;version=0;emitter;createEmitter(){const handlers=new Set;return{emit:event=>handlers.forEach(h=>h(event)),on:handler=>{handlers.add(handler);return()=>handlers.delete(handler)}}}constructor(name,providers,metadata,auto=true){super(name,providers,metadata,auto);this.emitter=this.createEmitter()}adoptFromChild(child,childOwner){const childRows=child.listAllIndexed();const adoptedRows=childRows.map(r=>this.relineage(r,childOwner.lineage));this.adopted.set(child,adoptedRows);this.children.add(child);const unsubscribe=child.subscribe({immediate:false},()=>{const latest=child.listAllIndexed().map(r=>this.relineage(r,childOwner.lineage));this.adopted.set(child,latest);this.reindex();this.bump("reset")});this.childSubscriptions.set(child,unsubscribe);this.reindex();this.bump("reset")}removeChild(child){const unsub=this.childSubscriptions.get(child);if(unsub)unsub();this.childSubscriptions.delete(child);this.children.delete(child);this.adopted.delete(child);this.reindex();this.bump("reset")}relineage(row,prepend){const merged=[...prepend,...row.lineage];const lineage=this.dedupLineage(merged);const ownerKey=this.ownerKeyOf(lineage);const qualifiedName=this.qualifiedNameOf(row.baseName,lineage);const qualifiedId=`${ownerKey}:${row.token.toString()}`;return{...row,lineage,ownerKey,qualifiedName,qualifiedId}}dedupLineage(lineage){const result=[];for(const seg of lineage){const last=result[result.length-1];if(!last||last.type!==seg.type||last.id!==seg.id){result.push(seg)}}return result}ownerKeyOf(lineage){return lineage.map(s=>`${s.type}:${s.id}`).join("/")}qualifiedNameOf(baseName,lineage){const prefix=lineage.map(s=>s.name||s.id).join("/");return prefix?`${prefix}/${baseName}`:baseName}reindex(){const allRows=[...this.localRows];for(const rows of this.adopted.values()){allRows.push(...rows)}this.byQualifiedId.clear();this.byName.clear();this.byOwner.clear();this.byOwnerAndName.clear();for(const row of allRows){this.byQualifiedId.set(row.qualifiedId,row);const nameList=this.byName.get(row.baseName)??[];nameList.push(row);this.byName.set(row.baseName,nameList);const ownerList=this.byOwner.get(row.ownerKey)??[];ownerList.push(row);this.byOwner.set(row.ownerKey,ownerList);const ownerNameKey=`${row.ownerKey}:${row.baseName}`;this.byOwnerAndName.set(ownerNameKey,row)}this.buildIndexes(allRows)}subscribe(opts,callback){if(opts.immediate){callback({kind:"reset",changeScope:"global",version:this.version,snapshot:this.listAllIndexed()})}return this.emitter.on(e=>{if(opts.filter){const filtered=e.snapshot.filter(row=>opts.filter(row.instance));callback({...e,snapshot:filtered})}else{callback(e)}})}bump(kind){const version=++this.version;this.emitter.emit({kind,changeScope:"global",version,snapshot:this.listAllIndexed()})}findByName(name){const rows=this.byName.get(name);return rows?.[0]?.instance}findAllByName(name){const rows=this.byName.get(name);return rows?.map(r=>r.instance)??[]}findByQualifiedId(qualifiedId){return this.byQualifiedId.get(qualifiedId)?.instance}listByOwner(ownerKey){return this.byOwner.get(ownerKey)??[]}listAllIndexed(){const all=[...this.localRows];for(const rows of this.adopted.values()){all.push(...rows)}return all}listAllInstances(){return this.listAllIndexed().map(r=>r.instance)}hasAny(){return this.localRows.length>0||this.adopted.size>0}dispose(){for(const unsub of this.childSubscriptions.values()){unsub()}this.childSubscriptions.clear();this.children.clear();this.adopted.clear()}};var SimpleRegistry=class extends RegistryAbstract{constructor(name,providers,metadata,auto=true){super(name,providers,metadata,auto)}getAll(){return[...this.instances.values()]}count(){return this.instances.size}};import"reflect-metadata";function AsyncWith(tokensFactory){return target=>{Reflect.defineMetadata(META_ASYNC_WITH,true,target);Reflect.defineMetadata(META_ASYNC_WITH_TOKENS,tokensFactory,target)}}export{AsyncWith,DESIGN_PARAMTYPES,DiContainer,DiTokens,IndexedRegistry,META_ASYNC_WITH,META_ASYNC_WITH_TOKENS,ProviderKind,ProviderScope,RegistryAbstract,SimpleRegistry,createProviderNormalizer,createTokenFactory,depsOfClass,depsOfFunc,getAsyncWithTokens,getMetadata,hasAsyncWith,isClass,isPromise,providerDiscoveryDeps,providerInvocationTokens,providerMetadataSchema,readWithParamTypes,setMetadata,tokenName};
|