@codefast/di 0.3.15 → 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.
Files changed (58) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +4 -1
  3. package/dist/binding-scope.d.mts +2 -0
  4. package/dist/binding-scope.mjs +2 -0
  5. package/dist/binding-select.d.mts +6 -2
  6. package/dist/binding-select.mjs +4 -0
  7. package/dist/binding.d.mts +74 -7
  8. package/dist/binding.mjs +12 -0
  9. package/dist/constraints.d.mts +49 -9
  10. package/dist/constraints.mjs +71 -19
  11. package/dist/constructor-type.d.mts +6 -2
  12. package/dist/container.d.mts +15 -6
  13. package/dist/container.mjs +139 -55
  14. package/dist/decorators/inject.d.mts +27 -3
  15. package/dist/decorators/inject.mjs +28 -11
  16. package/dist/decorators/injectable.d.mts +13 -1
  17. package/dist/decorators/injectable.mjs +24 -14
  18. package/dist/decorators/lifecycle-decorators.d.mts +6 -0
  19. package/dist/decorators/lifecycle-decorators.mjs +9 -0
  20. package/dist/dependency-graph.d.mts +17 -2
  21. package/dist/dependency-graph.mjs +3 -0
  22. package/dist/environment.d.mts +27 -12
  23. package/dist/environment.mjs +12 -0
  24. package/dist/errors.d.mts +69 -8
  25. package/dist/errors.mjs +65 -1
  26. package/dist/graph-adapters/cytoscape.d.mts +12 -0
  27. package/dist/graph-adapters/cytoscape.mjs +3 -0
  28. package/dist/graph-adapters/dot.d.mts +3 -0
  29. package/dist/graph-adapters/dot.mjs +3 -0
  30. package/dist/graph-adapters/reactflow.d.mts +14 -2
  31. package/dist/graph-adapters/reactflow.mjs +3 -0
  32. package/dist/index.d.mts +4 -3
  33. package/dist/index.mjs +4 -3
  34. package/dist/inspector.d.mts +11 -3
  35. package/dist/inspector.mjs +8 -3
  36. package/dist/lifecycle.d.mts +8 -6
  37. package/dist/lifecycle.mjs +48 -52
  38. package/dist/metadata/metadata-keys.d.mts +42 -1
  39. package/dist/metadata/metadata-keys.mjs +32 -1
  40. package/dist/metadata/metadata-reader-token.d.mts +3 -0
  41. package/dist/metadata/metadata-reader-token.mjs +3 -0
  42. package/dist/metadata/metadata-types.d.mts +22 -6
  43. package/dist/metadata/symbol-metadata-reader.d.mts +6 -0
  44. package/dist/metadata/symbol-metadata-reader.mjs +37 -24
  45. package/dist/module.d.mts +25 -1
  46. package/dist/module.mjs +12 -0
  47. package/dist/registry.d.mts +8 -5
  48. package/dist/registry.mjs +38 -42
  49. package/dist/resolve-options.d.mts +4 -0
  50. package/dist/resolve-options.mjs +4 -0
  51. package/dist/resolver.d.mts +23 -8
  52. package/dist/resolver.mjs +67 -43
  53. package/dist/scope.d.mts +3 -0
  54. package/dist/scope.mjs +3 -0
  55. package/dist/token.d.mts +12 -0
  56. package/dist/token.mjs +9 -0
  57. package/dist/types.d.mts +44 -6
  58. package/package.json +11 -5
@@ -1,11 +1,11 @@
1
1
  import { effectiveBindingScope } from "./binding-scope.mjs";
2
- import { DisposedContainerError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError } from "./errors.mjs";
2
+ import { AsyncModuleLoadError, CircularDependencyError, DisposedContainerError, InternalError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError } from "./errors.mjs";
3
3
  import { DEFAULT_SLOT, generateBindingId } from "./binding.mjs";
4
4
  import { tokenName } from "./token.mjs";
5
5
  import { BindingRegistry } from "./registry.mjs";
6
6
  import { ScopeManager } from "./scope.mjs";
7
7
  import { LifecycleManager } from "./lifecycle.mjs";
8
- import { slotKeyToResolveOptions } from "./resolve-options.mjs";
8
+ import { injectableSlotToResolveOptions, slotKeyToResolveOptions } from "./resolve-options.mjs";
9
9
  import { DependencyResolver } from "./resolver.mjs";
10
10
  import { Inspector } from "./inspector.mjs";
11
11
  import { buildDependencyGraph } from "./dependency-graph.mjs";
@@ -142,6 +142,7 @@ var DefaultContainer = class DefaultContainer {
142
142
  _loadSyncModules(modules) {
143
143
  const toLoad = this._collectModuleDeps(modules);
144
144
  for (const mod of toLoad) {
145
+ if (!isSyncModule(mod)) throw new AsyncModuleLoadError(mod.name);
145
146
  const ref = mod;
146
147
  const existing = this._moduleRefs.get(ref);
147
148
  if (existing !== void 0) {
@@ -332,43 +333,116 @@ var DefaultContainer = class DefaultContainer {
332
333
  const reader = this._getMetadataReader();
333
334
  const allBindings = this._registry.allBindings();
334
335
  for (const binding of allBindings) {
335
- if (binding.kind === "alias" || binding.kind === "constant" || binding.kind === "dynamic" || binding.kind === "dynamic-async") continue;
336
- const scope = effectiveBindingScope(binding);
337
- if (scope !== "singleton") continue;
338
- const deps = this._getDepsForBinding(binding, reader);
339
- for (const dep of deps) this._validateDep(tokenName(binding.token), scope, dep, [tokenName(binding.token)], reader);
336
+ if (!this._isSingletonStaticAnalyzableBinding(binding)) continue;
337
+ this._validateSingletonBindingGraph(binding, reader);
340
338
  }
341
339
  }
342
- _getDepsForBinding(binding, reader) {
343
- const result = [];
344
- if (binding.kind === "class") {
345
- const meta = reader.getConstructorMetadata(binding.target);
346
- if (meta !== void 0) for (const param of meta.params) {
347
- const depBindings = this._registry.getAll(param.token);
348
- for (const db of depBindings) if (db.kind !== "alias") result.push({
349
- token: param.token,
350
- scope: effectiveBindingScope(db)
340
+ _isSingletonStaticAnalyzableBinding(binding) {
341
+ if (effectiveBindingScope(binding) !== "singleton") return false;
342
+ return binding.kind === "class" || binding.kind === "resolved" || binding.kind === "resolved-async";
343
+ }
344
+ /**
345
+ * DFS over explicit constructor / `toResolved*` dependency edges. Follows `toAlias` chains to the
346
+ * terminal binding for scope checks (SPEC §6.9). Skips `toDynamic*` subtrees (opaque).
347
+ */
348
+ _validateSingletonBindingGraph(root, reader) {
349
+ const rootName = tokenName(root.token);
350
+ const dfs = (current, pathNames, pathBindingIds) => {
351
+ if (pathBindingIds.has(current.id)) return;
352
+ const extendedPathIds = new Set(pathBindingIds);
353
+ extendedPathIds.add(current.id);
354
+ for (const edge of this._collectStaticDependencyEdges(current, reader)) {
355
+ const { terminal, depTokenName } = edge;
356
+ const depScope = this._validationScopeFromTerminal(terminal);
357
+ if (depScope === "opaque") continue;
358
+ if (depScope === "scoped" || depScope === "transient") throw new ScopeViolationError({
359
+ consumerToken: rootName,
360
+ consumerScope: "singleton",
361
+ dependencyToken: depTokenName,
362
+ dependencyScope: depScope,
363
+ path: [...pathNames, depTokenName]
351
364
  });
365
+ if (terminal.kind === "class" || terminal.kind === "resolved" || terminal.kind === "resolved-async") dfs(terminal, [...pathNames, depTokenName], extendedPathIds);
352
366
  }
353
- } else if (binding.kind === "resolved" || binding.kind === "resolved-async") for (const dep of binding.deps) {
354
- const depBindings = this._registry.getAll(dep.token);
355
- for (const db of depBindings) if (db.kind !== "alias") result.push({
356
- token: dep.token,
357
- scope: effectiveBindingScope(db)
358
- });
367
+ };
368
+ dfs(root, [rootName], /* @__PURE__ */ new Set());
369
+ }
370
+ _validationScopeFromTerminal(terminal) {
371
+ switch (terminal.kind) {
372
+ case "constant": return "singleton";
373
+ case "dynamic":
374
+ case "dynamic-async": return "opaque";
375
+ case "class":
376
+ case "resolved":
377
+ case "resolved-async": return terminal.scope;
378
+ case "alias": throw new InternalError("validate: expected terminal binding after alias resolution");
379
+ default: return terminal;
359
380
  }
360
- return result;
361
381
  }
362
- _validateDep(consumerToken, consumerScope, dep, path, _reader) {
363
- if (consumerScope === "singleton") {
364
- if (dep.scope === "scoped" || dep.scope === "transient") throw new ScopeViolationError({
365
- consumerToken,
366
- consumerScope,
367
- dependencyToken: tokenName(dep.token),
368
- dependencyScope: dep.scope,
369
- path
382
+ _followAliasChainToTerminal(binding, hint) {
383
+ const cyclePath = [];
384
+ const seenAliasIds = /* @__PURE__ */ new Set();
385
+ let current = binding;
386
+ while (current !== void 0 && current.kind === "alias") {
387
+ if (seenAliasIds.has(current.id)) throw new CircularDependencyError(cyclePath);
388
+ seenAliasIds.add(current.id);
389
+ cyclePath.push(tokenName(current.token));
390
+ const nextToken = current.target;
391
+ const next = this._resolver.peekBindingForValidate(nextToken, hint);
392
+ if (next === void 0) return;
393
+ current = next.binding;
394
+ }
395
+ return current;
396
+ }
397
+ _collectStaticDependencyEdges(binding, reader) {
398
+ const edges = [];
399
+ const pushTerminal = (terminal, displayName) => {
400
+ if (terminal === void 0) return;
401
+ edges.push({
402
+ terminal,
403
+ depTokenName: displayName
370
404
  });
405
+ };
406
+ if (binding.kind === "class") {
407
+ const meta = reader.getConstructorMetadata(binding.target);
408
+ if (meta === void 0) return edges;
409
+ for (const param of meta.params) {
410
+ const paramHint = injectableSlotToResolveOptions(param);
411
+ if (param.optional) continue;
412
+ const tokenRef = param.token;
413
+ if (param.multi) {
414
+ const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef, paramHint);
415
+ for (const cand of candidates) {
416
+ const term = this._followAliasChainToTerminal(cand, paramHint);
417
+ pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
418
+ }
419
+ continue;
420
+ }
421
+ const found = paramHint === void 0 ? this._resolver.peekBindingForValidate(tokenRef, void 0) : this._resolver.peekBindingForValidate(tokenRef, paramHint);
422
+ if (found === void 0) continue;
423
+ const term = this._followAliasChainToTerminal(found.binding, paramHint);
424
+ pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
425
+ }
426
+ return edges;
371
427
  }
428
+ if (binding.kind === "resolved" || binding.kind === "resolved-async") for (const dep of binding.deps) {
429
+ const depHint = injectableSlotToResolveOptions(dep);
430
+ if (dep.optional) continue;
431
+ const tokenRef = dep.token;
432
+ if (dep.multi) {
433
+ const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef, depHint);
434
+ for (const cand of candidates) {
435
+ const term = this._followAliasChainToTerminal(cand, depHint);
436
+ pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
437
+ }
438
+ continue;
439
+ }
440
+ const found = depHint === void 0 ? this._resolver.peekBindingForValidate(tokenRef, void 0) : this._resolver.peekBindingForValidate(tokenRef, depHint);
441
+ if (found === void 0) continue;
442
+ const term = this._followAliasChainToTerminal(found.binding, depHint);
443
+ pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
444
+ }
445
+ return edges;
372
446
  }
373
447
  has(token, hint) {
374
448
  this._assertNotDisposed();
@@ -394,7 +468,19 @@ var DefaultContainer = class DefaultContainer {
394
468
  if (this._disposed) throw new DisposedContainerError();
395
469
  }
396
470
  };
471
+ function updateSlotTag(slot, tag, value) {
472
+ const existing = [...slot.tags];
473
+ const idx = existing.findIndex(([k]) => k === tag);
474
+ if (idx !== -1) existing[idx] = [tag, value];
475
+ else existing.push([tag, value]);
476
+ return {
477
+ ...slot,
478
+ tags: existing
479
+ };
480
+ }
397
481
  var BindToBuilderImpl = class {
482
+ _token;
483
+ _commit;
398
484
  constructor(_token, _commit) {
399
485
  this._token = _token;
400
486
  this._commit = _commit;
@@ -454,6 +540,9 @@ var BindToBuilderImpl = class {
454
540
  }
455
541
  };
456
542
  var BindingBuilderImpl = class {
543
+ _token;
544
+ _partial;
545
+ _commit;
457
546
  _slot;
458
547
  _predicate;
459
548
  _committed;
@@ -492,14 +581,7 @@ var BindingBuilderImpl = class {
492
581
  return this;
493
582
  }
494
583
  whenTagged(tag, value) {
495
- const existing = [...this._slot.tags];
496
- const idx = existing.findIndex(([k]) => k === tag);
497
- if (idx !== -1) existing[idx] = [tag, value];
498
- else existing.push([tag, value]);
499
- this._slot = {
500
- ...this._slot,
501
- tags: existing
502
- };
584
+ this._slot = updateSlotTag(this._slot, tag, value);
503
585
  this._doCommit();
504
586
  return this;
505
587
  }
@@ -535,6 +617,13 @@ var BindingBuilderImpl = class {
535
617
  }
536
618
  };
537
619
  var ScopeBuilderImpl = class {
620
+ _token;
621
+ _partial;
622
+ _slot;
623
+ _predicate;
624
+ _commit;
625
+ _scope;
626
+ _initialPreviousId;
538
627
  _onActivation;
539
628
  _onDeactivation;
540
629
  _committed;
@@ -576,6 +665,9 @@ var ScopeBuilderImpl = class {
576
665
  }
577
666
  };
578
667
  var ConstantBindingBuilderImpl = class {
668
+ _token;
669
+ _value;
670
+ _commit;
579
671
  _slot;
580
672
  _predicate;
581
673
  _onActivation;
@@ -620,14 +712,7 @@ var ConstantBindingBuilderImpl = class {
620
712
  return this;
621
713
  }
622
714
  whenTagged(tag, value) {
623
- const existing = [...this._slot.tags];
624
- const idx = existing.findIndex(([k]) => k === tag);
625
- if (idx !== -1) existing[idx] = [tag, value];
626
- else existing.push([tag, value]);
627
- this._slot = {
628
- ...this._slot,
629
- tags: existing
630
- };
715
+ this._slot = updateSlotTag(this._slot, tag, value);
631
716
  this._doCommit();
632
717
  return this;
633
718
  }
@@ -649,6 +734,9 @@ var ConstantBindingBuilderImpl = class {
649
734
  }
650
735
  };
651
736
  var AliasBindingBuilderImpl = class {
737
+ _token;
738
+ _target;
739
+ _commit;
652
740
  _slot;
653
741
  _predicate;
654
742
  _committed;
@@ -688,14 +776,7 @@ var AliasBindingBuilderImpl = class {
688
776
  return this;
689
777
  }
690
778
  whenTagged(tag, value) {
691
- const existing = [...this._slot.tags];
692
- const idx = existing.findIndex(([k]) => k === tag);
693
- if (idx !== -1) existing[idx] = [tag, value];
694
- else existing.push([tag, value]);
695
- this._slot = {
696
- ...this._slot,
697
- tags: existing
698
- };
779
+ this._slot = updateSlotTag(this._slot, tag, value);
699
780
  this._doCommit();
700
781
  return this;
701
782
  }
@@ -706,6 +787,9 @@ var AliasBindingBuilderImpl = class {
706
787
  return this._committed;
707
788
  }
708
789
  };
790
+ /**
791
+ * @since 0.3.16-canary.0
792
+ */
709
793
  const Container = {
710
794
  create() {
711
795
  return new DefaultContainer();
@@ -2,10 +2,16 @@ import { Constructor } from "../constructor-type.mjs";
2
2
  import { Token } from "../token.mjs";
3
3
 
4
4
  //#region src/decorators/inject.d.ts
5
+ /**
6
+ * @since 0.3.16-canary.0
7
+ */
5
8
  interface InjectOptions {
6
9
  name?: string;
7
10
  tags?: ReadonlyArray<readonly [tag: string, value: unknown]>;
8
11
  }
12
+ /**
13
+ * @since 0.3.16-canary.0
14
+ */
9
15
  interface InjectionDescriptor<Value = unknown> {
10
16
  readonly token: Token<Value> | Constructor<Value>;
11
17
  readonly optional: boolean;
@@ -13,12 +19,30 @@ interface InjectionDescriptor<Value = unknown> {
13
19
  readonly name?: string;
14
20
  readonly tags?: ReadonlyArray<readonly [string, unknown]>;
15
21
  }
22
+ /**
23
+ * @since 0.3.16-canary.0
24
+ */
16
25
  type InjectableDependency<Value = unknown> = Token<Value> | Constructor<Value> | InjectionDescriptor<Value>;
26
+ /**
27
+ * @since 0.3.16-canary.0
28
+ */
17
29
  declare function isInjectionDescriptor(value: unknown): value is InjectionDescriptor;
30
+ /**
31
+ * @since 0.3.16-canary.0
32
+ */
18
33
  declare function normalizeToDescriptor(dep: InjectableDependency): InjectionDescriptor;
19
34
  type ClassAccessorDecorator<This, Value> = (target: ClassAccessorDecoratorTarget<This, Value>, context: ClassAccessorDecoratorContext<This, Value>) => ClassAccessorDecoratorResult<This, Value> | void;
20
- declare function inject<const Value>(t: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value> & ClassAccessorDecorator<unknown, Value>;
21
- declare function optional<const Value>(t: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value | undefined>;
22
- declare function injectAll<const Value>(t: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value[]>;
35
+ /**
36
+ * @since 0.3.16-canary.0
37
+ */
38
+ declare function inject<const Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value> & ClassAccessorDecorator<unknown, Value>;
39
+ /**
40
+ * @since 0.3.16-canary.0
41
+ */
42
+ declare function optional<const Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value | undefined>;
43
+ /**
44
+ * @since 0.3.16-canary.0
45
+ */
46
+ declare function injectAll<const Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Array<Value>>;
23
47
  //#endregion
24
48
  export { InjectOptions, InjectableDependency, InjectionDescriptor, inject, injectAll, isInjectionDescriptor, normalizeToDescriptor, optional };
@@ -1,14 +1,20 @@
1
- import { MissingContainerContextError } from "../errors.mjs";
1
+ import { InternalError, MissingContainerContextError } from "../errors.mjs";
2
2
  import { getActiveContainer } from "../environment.mjs";
3
3
  import { injectableSlotToResolveOptions } from "../resolve-options.mjs";
4
- import { INJECT_ACCESSOR_KEY } from "../metadata/metadata-keys.mjs";
4
+ import { INJECT_ACCESSOR_KEY, accessorMetadataByMetadataObjectMap } from "../metadata/metadata-keys.mjs";
5
5
  //#region src/decorators/inject.ts
6
+ /**
7
+ * @since 0.3.16-canary.0
8
+ */
6
9
  function isInjectionDescriptor(value) {
7
10
  if (value === null || value === void 0) return false;
8
11
  const type = typeof value;
9
12
  if (type !== "object" && type !== "function") return false;
10
13
  return "token" in value && "optional" in value && "multi" in value && typeof value.optional === "boolean" && typeof value.multi === "boolean";
11
14
  }
15
+ /**
16
+ * @since 0.3.16-canary.0
17
+ */
12
18
  function normalizeToDescriptor(dep) {
13
19
  if (isInjectionDescriptor(dep)) return materializeInjectionDescriptor(dep);
14
20
  return {
@@ -48,9 +54,9 @@ function materializeInjectionDescriptor(dep) {
48
54
  };
49
55
  return base;
50
56
  }
51
- function buildInjectionDescriptor(t, options) {
57
+ function buildInjectionDescriptor(token, options) {
52
58
  const base = {
53
- token: t,
59
+ token,
54
60
  optional: false,
55
61
  multi: false
56
62
  };
@@ -69,15 +75,20 @@ function buildInjectionDescriptor(t, options) {
69
75
  };
70
76
  return base;
71
77
  }
72
- function inject(t, options) {
73
- const descriptor = buildInjectionDescriptor(t, options);
78
+ /**
79
+ * @since 0.3.16-canary.0
80
+ */
81
+ function inject(token, options) {
82
+ const descriptor = buildInjectionDescriptor(token, options);
74
83
  const decoratorFn = (_target, context) => {
84
+ if (context.static === true) throw new InternalError("@inject() on static accessors is not supported; only instance accessors participate in runWithContainer-based property injection.");
75
85
  const meta = context.metadata;
76
86
  if (!Array.isArray(meta[INJECT_ACCESSOR_KEY])) meta[INJECT_ACCESSOR_KEY] = [];
77
87
  meta[INJECT_ACCESSOR_KEY].push({
78
88
  key: context.name,
79
89
  descriptor
80
90
  });
91
+ accessorMetadataByMetadataObjectMap.set(context.metadata, meta[INJECT_ACCESSOR_KEY]);
81
92
  context.addInitializer(function() {
82
93
  const container = getActiveContainer();
83
94
  if (container === void 0) throw new MissingContainerContextError(String(context.name));
@@ -85,7 +96,7 @@ function inject(t, options) {
85
96
  ...options.name !== void 0 ? { name: options.name } : {},
86
97
  ...options.tags !== void 0 ? { tags: options.tags } : {}
87
98
  });
88
- const value = descriptor.optional ? container.resolveOptional(t, hint) : container.resolve(t, hint);
99
+ const value = descriptor.optional ? container.resolveOptional(token, hint) : container.resolve(token, hint);
89
100
  context.access.set(this, value);
90
101
  });
91
102
  return {};
@@ -100,9 +111,12 @@ function inject(t, options) {
100
111
  Object.defineProperties(decoratorFn, props);
101
112
  return decoratorFn;
102
113
  }
103
- function optional(t, options) {
114
+ /**
115
+ * @since 0.3.16-canary.0
116
+ */
117
+ function optional(token, options) {
104
118
  const base = {
105
- token: t,
119
+ token,
106
120
  optional: true,
107
121
  multi: false
108
122
  };
@@ -121,9 +135,12 @@ function optional(t, options) {
121
135
  };
122
136
  return base;
123
137
  }
124
- function injectAll(t, options) {
138
+ /**
139
+ * @since 0.3.16-canary.0
140
+ */
141
+ function injectAll(token, options) {
125
142
  const base = {
126
- token: t,
143
+ token,
127
144
  optional: false,
128
145
  multi: true
129
146
  };
@@ -3,6 +3,9 @@ import { BindingScope } from "../types.mjs";
3
3
  import { InjectableDependency } from "./inject.mjs";
4
4
 
5
5
  //#region src/decorators/injectable.d.ts
6
+ /**
7
+ * @since 0.3.16-canary.0
8
+ */
6
9
  interface AutoRegisterRegistry {
7
10
  register(target: Constructor, scope: BindingScope): void;
8
11
  entries(): ReadonlyArray<{
@@ -10,11 +13,20 @@ interface AutoRegisterRegistry {
10
13
  scope: BindingScope;
11
14
  }>;
12
15
  }
16
+ /**
17
+ * @since 0.3.16-canary.0
18
+ */
13
19
  declare function createAutoRegisterRegistry(): AutoRegisterRegistry;
20
+ /**
21
+ * @since 0.3.16-canary.0
22
+ */
14
23
  interface InjectableOptions {
15
24
  autoRegister?: AutoRegisterRegistry;
16
25
  scope?: BindingScope;
17
26
  }
18
- declare function injectable(deps?: readonly InjectableDependency[], options?: InjectableOptions): (target: unknown, context: ClassDecoratorContext) => void;
27
+ /**
28
+ * @since 0.3.16-canary.0
29
+ */
30
+ declare function injectable(deps?: ReadonlyArray<InjectableDependency>, options?: InjectableOptions): (target: unknown, context: ClassDecoratorContext) => void;
19
31
  //#endregion
20
32
  export { AutoRegisterRegistry, type InjectableDependency, InjectableOptions, createAutoRegisterRegistry, injectable };
@@ -1,49 +1,59 @@
1
- import { INJECTABLE_KEY, constructorMetadataMap } from "../metadata/metadata-keys.mjs";
1
+ import { INJECTABLE_KEY, INJECT_ACCESSOR_KEY, accessorMetadataByConstructorMap, constructorMetadataMap } from "../metadata/metadata-keys.mjs";
2
2
  import { normalizeToDescriptor } from "./inject.mjs";
3
3
  //#region src/decorators/injectable.ts
4
+ /**
5
+ * @since 0.3.16-canary.0
6
+ */
4
7
  function createAutoRegisterRegistry() {
5
- const _entries = [];
8
+ const _registeredEntries = [];
6
9
  return {
7
10
  register(target, scope) {
8
- _entries.push({
11
+ _registeredEntries.push({
9
12
  target,
10
13
  scope
11
14
  });
12
15
  },
13
16
  entries() {
14
- return _entries;
17
+ return _registeredEntries;
15
18
  }
16
19
  };
17
20
  }
21
+ /**
22
+ * @since 0.3.16-canary.0
23
+ */
18
24
  function injectable(deps, options) {
19
25
  return function(target, context) {
20
- const constructorMeta = { params: (deps ?? []).map((dep, index) => {
21
- const descriptor = normalizeToDescriptor(dep);
22
- const base = {
26
+ const constructorMetadata = { params: (deps ?? []).map((dependency, index) => {
27
+ const descriptor = normalizeToDescriptor(dependency);
28
+ const baseParameterMetadata = {
23
29
  index,
24
30
  token: descriptor.token,
25
31
  optional: descriptor.optional,
26
32
  multi: descriptor.multi
27
33
  };
28
34
  if (descriptor.name !== void 0 && descriptor.tags !== void 0) return {
29
- ...base,
35
+ ...baseParameterMetadata,
30
36
  name: descriptor.name,
31
37
  tags: descriptor.tags
32
38
  };
33
39
  if (descriptor.name !== void 0) return {
34
- ...base,
40
+ ...baseParameterMetadata,
35
41
  name: descriptor.name
36
42
  };
37
43
  if (descriptor.tags !== void 0) return {
38
- ...base,
44
+ ...baseParameterMetadata,
39
45
  tags: descriptor.tags
40
46
  };
41
- return base;
47
+ return baseParameterMetadata;
42
48
  }) };
43
- constructorMetadataMap.set(target, constructorMeta);
49
+ constructorMetadataMap.set(target, constructorMetadata);
44
50
  try {
45
- const meta = context.metadata;
46
- if (meta !== null && typeof meta === "object") meta[INJECTABLE_KEY] = constructorMeta;
51
+ const metadata = context.metadata;
52
+ if (metadata !== null && typeof metadata === "object") {
53
+ const accessorList = metadata[INJECT_ACCESSOR_KEY];
54
+ if (Array.isArray(accessorList) && accessorList.length > 0) accessorMetadataByConstructorMap.set(target, accessorList);
55
+ metadata[INJECTABLE_KEY] = constructorMetadata;
56
+ }
47
57
  } catch {}
48
58
  if (options?.autoRegister !== void 0) {
49
59
  const scope = options.scope ?? "transient";
@@ -1,5 +1,11 @@
1
1
  //#region src/decorators/lifecycle-decorators.d.ts
2
+ /**
3
+ * @since 0.3.16-canary.0
4
+ */
2
5
  declare function postConstruct(): (target: unknown, context: ClassMethodDecoratorContext) => void;
6
+ /**
7
+ * @since 0.3.16-canary.0
8
+ */
3
9
  declare function preDestroy(): (target: unknown, context: ClassMethodDecoratorContext) => void;
4
10
  //#endregion
5
11
  export { postConstruct, preDestroy };
@@ -1,3 +1,4 @@
1
+ import { InternalError } from "../errors.mjs";
1
2
  import { LIFECYCLE_KEY, lifecycleByConstructorMetadataMap, lifecycleMetadataMap } from "../metadata/metadata-keys.mjs";
2
3
  //#region src/decorators/lifecycle-decorators.ts
3
4
  function appendUniqueMethod(metadata, phase, methodName) {
@@ -20,8 +21,12 @@ function registerByConstructor(target, phase, methodName) {
20
21
  preDestroy: phase === "preDestroy" ? [methodName] : []
21
22
  });
22
23
  }
24
+ /**
25
+ * @since 0.3.16-canary.0
26
+ */
23
27
  function postConstruct() {
24
28
  return function(target, context) {
29
+ if (context.static === true) throw new InternalError("@postConstruct() applies to instance methods only; static methods are not invoked during instance lifecycle.");
25
30
  const methodName = String(context.name);
26
31
  registerByConstructor(target, "postConstruct", methodName);
27
32
  const existing = lifecycleMetadataMap.get(context.metadata);
@@ -53,8 +58,12 @@ function postConstruct() {
53
58
  } catch {}
54
59
  };
55
60
  }
61
+ /**
62
+ * @since 0.3.16-canary.0
63
+ */
56
64
  function preDestroy() {
57
65
  return function(target, context) {
66
+ if (context.static === true) throw new InternalError("@preDestroy() applies to instance methods only; static methods are not invoked during instance teardown.");
58
67
  const methodName = String(context.name);
59
68
  registerByConstructor(target, "preDestroy", methodName);
60
69
  const existing = lifecycleMetadataMap.get(context.metadata);
@@ -3,6 +3,9 @@ import { BindingRegistry } from "./registry.mjs";
3
3
  import { MetadataReader } from "./metadata/metadata-types.mjs";
4
4
 
5
5
  //#region src/dependency-graph.d.ts
6
+ /**
7
+ * @since 0.3.16-canary.0
8
+ */
6
9
  interface GraphNode {
7
10
  readonly id: string;
8
11
  readonly tokenName: string;
@@ -10,19 +13,31 @@ interface GraphNode {
10
13
  readonly scope: BindingScope;
11
14
  readonly fromParent: boolean;
12
15
  }
16
+ /**
17
+ * @since 0.3.16-canary.0
18
+ */
13
19
  interface GraphEdge {
14
20
  readonly from: string;
15
21
  readonly to: string;
16
22
  readonly label?: string;
17
23
  }
24
+ /**
25
+ * @since 0.3.16-canary.0
26
+ */
18
27
  interface ContainerGraphJson {
19
- readonly nodes: readonly GraphNode[];
20
- readonly edges: readonly GraphEdge[];
28
+ readonly nodes: ReadonlyArray<GraphNode>;
29
+ readonly edges: ReadonlyArray<GraphEdge>;
21
30
  readonly includesParent: boolean;
22
31
  }
32
+ /**
33
+ * @since 0.3.16-canary.0
34
+ */
23
35
  interface GraphOptions {
24
36
  readonly includeParent?: boolean;
25
37
  }
38
+ /**
39
+ * @since 0.3.16-canary.0
40
+ */
26
41
  declare function buildDependencyGraph(registry: BindingRegistry, metadataReader: MetadataReader, options: GraphOptions | undefined, parentRegistry?: BindingRegistry): ContainerGraphJson;
27
42
  //#endregion
28
43
  export { ContainerGraphJson, GraphEdge, GraphNode, GraphOptions, buildDependencyGraph };
@@ -1,6 +1,9 @@
1
1
  import { effectiveBindingScope } from "./binding-scope.mjs";
2
2
  import { tokenName } from "./token.mjs";
3
3
  //#region src/dependency-graph.ts
4
+ /**
5
+ * @since 0.3.16-canary.0
6
+ */
4
7
  function buildDependencyGraph(registry, metadataReader, options, parentRegistry) {
5
8
  const nodes = [];
6
9
  const edges = [];