@codefast/di 0.3.16-canary.0 → 0.3.16-canary.1

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/dist/resolver.mjs CHANGED
@@ -19,6 +19,12 @@ const ROOT_CONSTRAINT_CONTEXT = {
19
19
  * @since 0.3.16-canary.0
20
20
  */
21
21
  var DependencyResolver = class {
22
+ _registry;
23
+ _scope;
24
+ _lifecycle;
25
+ _metadataReader;
26
+ _container;
27
+ _parent;
22
28
  _frameByBindingId = /* @__PURE__ */ new Map();
23
29
  _syncResolutionContextPool = [];
24
30
  _deepCycleMarks = /* @__PURE__ */ new Map();
@@ -87,6 +93,21 @@ var DependencyResolver = class {
87
93
  }
88
94
  if (this._parent !== void 0) return this._parent._findBinding(token, hint, resolutionPath, materializationStack);
89
95
  }
96
+ /**
97
+ * Binding lookup aligned with `resolve` — used by `Container.validate` without instantiating.
98
+ */
99
+ peekBindingForValidate(token, hint) {
100
+ return this._findBinding(token, hint, [], []);
101
+ }
102
+ /**
103
+ * Mirrors {@link DependencyResolver.resolveAll} candidate selection only (no instantiation).
104
+ */
105
+ peekCandidateBindingsForValidate(token, hint) {
106
+ if (hint?.name !== void 0 && hint.tag === void 0 && (hint.tags?.length ?? 0) === 0) return this._getSimpleNamedBindingsFromChain(token, hint.name);
107
+ const allBindings = this._getAllBindingsFromChain(token);
108
+ if (allBindings.length === 0) return [];
109
+ return selectAllBindings(allBindings, hint, this._makeConstraintContext([], [], hint));
110
+ }
90
111
  resolveFromContext(token, resolutionPath, materializationStack) {
91
112
  const fastBinding = this._registry.getFastDefault(token);
92
113
  if (fastBinding !== void 0) {
@@ -124,16 +145,16 @@ var DependencyResolver = class {
124
145
  if (this._scope.hasScoped(binding.id)) return this._scope.getScoped(binding.id);
125
146
  }
126
147
  const frame = this._getMaterializationFrame(binding);
127
- const tName = frame.tokenName;
148
+ const tokenDisplayName = frame.tokenName;
128
149
  const pathWithSet = resolutionPath;
129
150
  let resolutionSet = pathWithSet[RESOLUTION_SET_KEY];
130
151
  if (resolutionSet === void 0 && resolutionPath.length >= RESOLUTION_SET_THRESHOLD) {
131
152
  resolutionSet = new Set(resolutionPath);
132
153
  pathWithSet[RESOLUTION_SET_KEY] = resolutionSet;
133
154
  }
134
- if (resolutionSet !== void 0 ? resolutionSet.has(tName) : resolutionPath.includes(tName)) throw new CircularDependencyError([...resolutionPath, tName]);
135
- resolutionPath.push(tName);
136
- resolutionSet?.add(tName);
155
+ if (resolutionSet !== void 0 ? resolutionSet.has(tokenDisplayName) : resolutionPath.includes(tokenDisplayName)) throw new CircularDependencyError([...resolutionPath, tokenDisplayName]);
156
+ resolutionPath.push(tokenDisplayName);
157
+ resolutionSet?.add(tokenDisplayName);
137
158
  materializationStack.push(frame);
138
159
  const needsActivation = this._needsActivation(binding);
139
160
  if (!needsActivation && scope === "transient" && binding.kind === "dynamic") {
@@ -143,18 +164,18 @@ var DependencyResolver = class {
143
164
  if (dynamicResult instanceof Promise) throw new AsyncResolutionError(tokenName(binding.token), tokenName(binding.token));
144
165
  materializationStack.pop();
145
166
  resolutionPath.pop();
146
- resolutionSet?.delete(tName);
167
+ resolutionSet?.delete(tokenDisplayName);
147
168
  return dynamicResult;
148
169
  } catch (error) {
149
170
  materializationStack.pop();
150
171
  resolutionPath.pop();
151
- resolutionSet?.delete(tName);
172
+ resolutionSet?.delete(tokenDisplayName);
152
173
  throw error;
153
174
  }
154
175
  }
155
176
  try {
156
177
  const resolutionCtx = needsActivation || this._requiresResolutionContext(binding) ? this._acquireSyncResolutionContext(resolutionPath, materializationStack, hint) : void 0;
157
- const instance = this._instantiateSync(binding, resolutionCtx, resolutionPath, materializationStack, hint);
178
+ const instance = this._instantiateSync(binding, resolutionCtx, resolutionPath, materializationStack);
158
179
  let shouldActivate = needsActivation;
159
180
  if (binding.kind === "class" && this._classHasPostConstruct.get(binding.target) === void 0) {
160
181
  this._refreshClassPostConstructCache(binding.target);
@@ -168,17 +189,17 @@ var DependencyResolver = class {
168
189
  } finally {
169
190
  materializationStack.pop();
170
191
  resolutionPath.pop();
171
- resolutionSet?.delete(tName);
192
+ resolutionSet?.delete(tokenDisplayName);
172
193
  }
173
194
  }
174
- _instantiateSync(binding, ctx, resolutionPath, materializationStack, hint) {
195
+ _instantiateSync(binding, ctx, resolutionPath, materializationStack) {
175
196
  switch (binding.kind) {
176
197
  case "constant": return binding.value;
177
198
  case "dynamic": {
178
199
  if (ctx === void 0) throw new InternalError("dynamic binding requires resolution context");
179
- const r = binding.factory(ctx);
180
- if (r instanceof Promise) throw new AsyncResolutionError(tokenName(binding.token), tokenName(binding.token));
181
- return r;
200
+ const factoryResult = binding.factory(ctx);
201
+ if (factoryResult instanceof Promise) throw new AsyncResolutionError(tokenName(binding.token), tokenName(binding.token));
202
+ return factoryResult;
182
203
  }
183
204
  case "dynamic-async": throw new AsyncResolutionError(tokenName(binding.token), tokenName(binding.token));
184
205
  case "class": {
@@ -186,7 +207,7 @@ var DependencyResolver = class {
186
207
  return this._instantiateClass(binding.target, deps);
187
208
  }
188
209
  case "resolved": {
189
- const deps = this._resolveDescriptorDeps(binding.deps, resolutionPath, materializationStack, hint);
210
+ const deps = this._resolveDescriptorDeps(binding.deps, resolutionPath, materializationStack);
190
211
  const r = binding.factory(...deps);
191
212
  if (r instanceof Promise) throw new AsyncResolutionError(tokenName(binding.token), tokenName(binding.token));
192
213
  return r;
@@ -226,7 +247,7 @@ var DependencyResolver = class {
226
247
  }
227
248
  return deps;
228
249
  }
229
- _resolveDescriptorDeps(deps, resolutionPath, materializationStack, _hint) {
250
+ _resolveDescriptorDeps(deps, resolutionPath, materializationStack) {
230
251
  const resolved = new Array(deps.length);
231
252
  for (let index = 0; index < deps.length; index += 1) {
232
253
  const dep = deps[index];
@@ -329,7 +350,7 @@ var DependencyResolver = class {
329
350
  try {
330
351
  if (scope === "singleton") {
331
352
  const createSingletonPromise = async () => {
332
- const instance = await this._instantiateAsync(binding, resolutionCtx, resolutionPath, materializationStack, hint);
353
+ const instance = await this._instantiateAsync(binding, resolutionCtx, resolutionPath, materializationStack);
333
354
  let shouldActivate = needsActivation;
334
355
  if (binding.kind === "class" && this._classHasPostConstruct.get(binding.target) === void 0) {
335
356
  this._refreshClassPostConstructCache(binding.target);
@@ -341,14 +362,14 @@ var DependencyResolver = class {
341
362
  this._scope.clearInflight(binding.id);
342
363
  return activated;
343
364
  };
344
- const p = createSingletonPromise().catch((err) => {
365
+ const singletonPromise = createSingletonPromise().catch((err) => {
345
366
  this._scope.clearInflight(binding.id);
346
367
  throw err;
347
368
  });
348
- this._scope.setInflight(binding.id, p);
349
- return await p;
369
+ this._scope.setInflight(binding.id, singletonPromise);
370
+ return await singletonPromise;
350
371
  }
351
- const instance = await this._instantiateAsync(binding, resolutionCtx, resolutionPath, materializationStack, hint);
372
+ const instance = await this._instantiateAsync(binding, resolutionCtx, resolutionPath, materializationStack);
352
373
  let shouldActivate = needsActivation;
353
374
  if (binding.kind === "class" && this._classHasPostConstruct.get(binding.target) === void 0) {
354
375
  this._refreshClassPostConstructCache(binding.target);
@@ -364,7 +385,7 @@ var DependencyResolver = class {
364
385
  resolutionSet?.delete(tName);
365
386
  }
366
387
  }
367
- async _instantiateAsync(binding, ctx, resolutionPath, materializationStack, hint) {
388
+ async _instantiateAsync(binding, ctx, resolutionPath, materializationStack) {
368
389
  switch (binding.kind) {
369
390
  case "constant": return binding.value;
370
391
  case "dynamic": {
@@ -381,12 +402,12 @@ var DependencyResolver = class {
381
402
  }
382
403
  case "resolved": {
383
404
  if (ctx === void 0) throw new InternalError("resolved binding requires resolution context");
384
- const deps = await this._resolveDescriptorDepsAsync(binding.deps, resolutionPath, materializationStack, hint);
405
+ const deps = await this._resolveDescriptorDepsAsync(binding.deps, resolutionPath, materializationStack);
385
406
  const r = binding.factory(...deps);
386
407
  return r instanceof Promise ? r : Promise.resolve(r);
387
408
  }
388
409
  case "resolved-async": {
389
- const deps = await this._resolveDescriptorDepsAsync(binding.deps, resolutionPath, materializationStack, hint);
410
+ const deps = await this._resolveDescriptorDepsAsync(binding.deps, resolutionPath, materializationStack);
390
411
  return binding.factory(...deps);
391
412
  }
392
413
  case "alias": throw new InternalError("alias should have been followed before instantiation");
@@ -418,7 +439,7 @@ var DependencyResolver = class {
418
439
  }
419
440
  return Promise.all(pending);
420
441
  }
421
- async _resolveDescriptorDepsAsync(deps, resolutionPath, materializationStack, _hint) {
442
+ async _resolveDescriptorDepsAsync(deps, resolutionPath, materializationStack) {
422
443
  const pending = new Array(deps.length);
423
444
  const shouldCloneContext = deps.length > 1;
424
445
  for (let index = 0; index < deps.length; index += 1) {
@@ -539,14 +560,14 @@ var DependencyResolver = class {
539
560
  _resolveTransientDynamicSyncFromContext(binding, resolutionPath, materializationStack) {
540
561
  if (resolutionPath.length < RESOLUTION_SET_THRESHOLD) {
541
562
  const frame = this._getMaterializationFrame(binding);
542
- const tName = frame.tokenName;
543
- if (resolutionPath.includes(tName)) throw new CircularDependencyError([...resolutionPath, tName]);
544
- resolutionPath.push(tName);
563
+ const tokenDisplayName = frame.tokenName;
564
+ if (resolutionPath.includes(tokenDisplayName)) throw new CircularDependencyError([...resolutionPath, tokenDisplayName]);
565
+ resolutionPath.push(tokenDisplayName);
545
566
  materializationStack.push(frame);
546
567
  const resolutionCtx = this._acquireSyncResolutionContext(resolutionPath, materializationStack, void 0);
547
568
  try {
548
569
  const dynamicResult = binding.factory(resolutionCtx);
549
- if (dynamicResult instanceof Promise) throw new AsyncResolutionError(tName, tName);
570
+ if (dynamicResult instanceof Promise) throw new AsyncResolutionError(tokenDisplayName, tokenDisplayName);
550
571
  return dynamicResult;
551
572
  } finally {
552
573
  materializationStack.pop();
@@ -577,33 +598,33 @@ var DependencyResolver = class {
577
598
  }
578
599
  _resolveTransientDynamicSyncSlow(binding, resolutionPath, materializationStack) {
579
600
  const frame = this._getMaterializationFrame(binding);
580
- const tName = frame.tokenName;
601
+ const tokenDisplayName = frame.tokenName;
581
602
  const pathWithSet = resolutionPath;
582
603
  let resolutionSet = pathWithSet[RESOLUTION_SET_KEY];
583
604
  if (resolutionSet === void 0) {
584
605
  resolutionSet = new Set(resolutionPath);
585
606
  pathWithSet[RESOLUTION_SET_KEY] = resolutionSet;
586
607
  }
587
- if (resolutionSet.has(tName)) throw new CircularDependencyError([...resolutionPath, tName]);
588
- resolutionPath.push(tName);
589
- resolutionSet.add(tName);
608
+ if (resolutionSet.has(tokenDisplayName)) throw new CircularDependencyError([...resolutionPath, tokenDisplayName]);
609
+ resolutionPath.push(tokenDisplayName);
610
+ resolutionSet.add(tokenDisplayName);
590
611
  materializationStack.push(frame);
591
612
  const resolutionCtx = this._acquireSyncResolutionContext(resolutionPath, materializationStack, void 0);
592
613
  try {
593
614
  const dynamicResult = binding.factory(resolutionCtx);
594
- if (dynamicResult instanceof Promise) throw new AsyncResolutionError(tName, tName);
615
+ if (dynamicResult instanceof Promise) throw new AsyncResolutionError(tokenDisplayName, tokenDisplayName);
595
616
  return dynamicResult;
596
617
  } finally {
597
618
  materializationStack.pop();
598
619
  resolutionPath.pop();
599
- resolutionSet.delete(tName);
620
+ resolutionSet.delete(tokenDisplayName);
600
621
  }
601
622
  }
602
623
  _resolveTransientDynamicAsyncFromContext(binding, resolutionPath, materializationStack) {
603
624
  if (resolutionPath.length < RESOLUTION_SET_THRESHOLD) {
604
- const tName = tokenName(binding.token);
605
- if (resolutionPath.includes(tName)) return Promise.reject(new CircularDependencyError([...resolutionPath, tName]));
606
- resolutionPath.push(tName);
625
+ const tokenDisplayName = tokenName(binding.token);
626
+ if (resolutionPath.includes(tokenDisplayName)) return Promise.reject(new CircularDependencyError([...resolutionPath, tokenDisplayName]));
627
+ resolutionPath.push(tokenDisplayName);
607
628
  let ctx;
608
629
  let isOwnerLevel;
609
630
  if (this._deepAsyncCtxPath === resolutionPath) {
@@ -629,8 +650,8 @@ var DependencyResolver = class {
629
650
  try {
630
651
  if (binding.kind === "dynamic-async") factoryPromise = binding.factory(ctx);
631
652
  else {
632
- const r = binding.factory(ctx);
633
- factoryPromise = r instanceof Promise ? r : Promise.resolve(r);
653
+ const factoryResult = binding.factory(ctx);
654
+ factoryPromise = factoryResult instanceof Promise ? factoryResult : Promise.resolve(factoryResult);
634
655
  }
635
656
  } catch (err) {
636
657
  resolutionPath.pop();
@@ -651,16 +672,16 @@ var DependencyResolver = class {
651
672
  }
652
673
  async _resolveTransientDynamicAsyncSlow(binding, resolutionPath, materializationStack) {
653
674
  const frame = this._getMaterializationFrame(binding);
654
- const tName = frame.tokenName;
675
+ const tokenDisplayName = frame.tokenName;
655
676
  const pathWithSet = resolutionPath;
656
677
  let resolutionSet = pathWithSet[RESOLUTION_SET_KEY];
657
678
  if (resolutionSet === void 0) {
658
679
  resolutionSet = new Set(resolutionPath);
659
680
  pathWithSet[RESOLUTION_SET_KEY] = resolutionSet;
660
681
  }
661
- if (resolutionSet.has(tName)) throw new CircularDependencyError([...resolutionPath, tName]);
662
- resolutionPath.push(tName);
663
- resolutionSet.add(tName);
682
+ if (resolutionSet.has(tokenDisplayName)) throw new CircularDependencyError([...resolutionPath, tokenDisplayName]);
683
+ resolutionPath.push(tokenDisplayName);
684
+ resolutionSet.add(tokenDisplayName);
664
685
  materializationStack.push(frame);
665
686
  const resolutionCtx = new DefaultResolutionContext(this, resolutionPath, materializationStack, void 0);
666
687
  try {
@@ -670,7 +691,7 @@ var DependencyResolver = class {
670
691
  } finally {
671
692
  materializationStack.pop();
672
693
  resolutionPath.pop();
673
- resolutionSet.delete(tName);
694
+ resolutionSet.delete(tokenDisplayName);
674
695
  }
675
696
  }
676
697
  _resolveCandidateSync(binding, hint, resolutionPath, materializationStack) {
package/dist/types.d.mts CHANGED
@@ -36,6 +36,10 @@ type DeactivationHandler<Value> = (instance: Value) => void | Promise<void>;
36
36
  */
37
37
  interface ResolveOptions {
38
38
  name?: string;
39
+ /**
40
+ * Single-tag shorthand — semantics match including one pair in `tags`.
41
+ * Enables fast-path lookup alongside `tags`.
42
+ */
39
43
  tag?: readonly [tag: string, value: unknown];
40
44
  tags?: ReadonlyArray<readonly [tag: string, value: unknown]>;
41
45
  }
@@ -56,10 +60,10 @@ interface MaterializationFrame {
56
60
  * @since 0.3.16-canary.0
57
61
  */
58
62
  interface ConstraintContext {
59
- readonly resolutionPath: readonly string[];
60
- readonly materializationStack: readonly MaterializationFrame[];
63
+ readonly resolutionPath: ReadonlyArray<string>;
64
+ readonly materializationStack: ReadonlyArray<MaterializationFrame>;
61
65
  readonly parent: MaterializationFrame | undefined;
62
- readonly ancestors: readonly MaterializationFrame[];
66
+ readonly ancestors: ReadonlyArray<MaterializationFrame>;
63
67
  readonly currentResolveHint: ResolveOptions | undefined;
64
68
  }
65
69
  /**
@@ -70,8 +74,8 @@ interface ResolutionContext {
70
74
  resolveAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Value>;
71
75
  resolveOptional<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Value | undefined;
72
76
  resolveOptionalAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Value | undefined>;
73
- resolveAll<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Value[];
74
- resolveAllAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Value[]>;
77
+ resolveAll<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Array<Value>;
78
+ resolveAllAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Array<Value>>;
75
79
  readonly graph: ConstraintContext;
76
80
  }
77
81
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codefast/di",
3
- "version": "0.3.16-canary.0",
3
+ "version": "0.3.16-canary.1",
4
4
  "description": "Lightweight dependency injection primitives for Codefast",
5
5
  "keywords": [
6
6
  "codefast",
@@ -170,14 +170,16 @@
170
170
  "access": "public"
171
171
  },
172
172
  "devDependencies": {
173
- "@types/node": "^25.6.0",
174
- "@typescript/native-preview": "7.0.0-dev.20260504.1",
173
+ "@types/node": "^25.6.2",
174
+ "@typescript/native-preview": "7.0.0-dev.20260509.2",
175
175
  "@vitest/coverage-v8": "^4.1.5",
176
176
  "expect-type": "^1.3.0",
177
+ "tsdown": "^0.22.0",
178
+ "tsx": "^4.21.0",
177
179
  "typescript": "^6.0.3",
178
180
  "unplugin-swc": "^1.5.9",
179
181
  "vitest": "^4.1.5",
180
- "@codefast/typescript-config": "0.3.16-canary.0"
182
+ "@codefast/typescript-config": "0.3.16-canary.1"
181
183
  },
182
184
  "engines": {
183
185
  "node": ">=22.0.0"
@@ -186,9 +188,13 @@
186
188
  "build": "tsdown",
187
189
  "check-types": "tsgo --noEmit",
188
190
  "clean": "rm -rf dist",
189
- "examples": "for file in examples/*/*.ts; do echo \"=== Running $file ===\"; tsx \"$file\"; echo \"\n\" || exit 1; done",
191
+ "examples": "for file in examples/*/*.ts; do echo \"=== Running $file ===\"; node --import tsx/esm \"$file\"; echo \"\n\" || exit 1; done",
190
192
  "test": "vitest run",
191
193
  "test:coverage": "vitest run --coverage",
194
+ "test:e2e": "vitest run tests/e2e",
195
+ "test:integration": "vitest run tests/integration",
196
+ "test:type": "vitest run tests/types",
197
+ "test:unit": "vitest run tests/unit",
192
198
  "test:watch": "vitest"
193
199
  }
194
200
  }