@illuma/core 1.0.0 → 1.2.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/CHANGELOG.md CHANGED
@@ -7,40 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## Unreleased
9
9
 
10
- ## 1.7.0 - 2025-12-28
10
+ ## 1.2.1 - 2026-01-10
11
11
 
12
- ## 1.6.3 - 2025-12-27
12
+ ## 1.2.0 - 2026-01-10
13
13
 
14
- ## 1.6.2 - 2025-12-27
15
-
16
- ## 1.6.1 - 2025-12-27
17
-
18
- ## 1.6.0 - 2025-12-27
19
-
20
- ## 1.5.3 - 2025-12-10
21
-
22
- ## 1.5.2 - 2025-12-10
23
-
24
- ## 1.5.1 - 2025-12-10
25
-
26
- ## 1.5.0 - 2025-12-08
27
-
28
- ## 1.4.0 - 2025-12-08
29
-
30
- ## 1.3.0 - 2025-12-07
31
-
32
- ## 1.2.0 - 2025-11-26
33
-
34
- ## 1.1.2 - 2025-11-23
35
-
36
- ## 1.1.1 - 2025-11-23
37
-
38
- ## 1.1.0 - 2025-11-23
39
-
40
- ## 1.0.3 - 2025-11-22
41
-
42
- ## 1.0.2 - 2025-11-22
43
-
44
- ## 1.0.1 - 2025-11-22
45
-
46
- ## 1.0.0 - 2026-01-01
14
+ ## 1.1.0 - 2026-01-07
package/README.md CHANGED
@@ -23,6 +23,12 @@ A lightweight, type-safe dependency injection container for TypeScript. Zero dep
23
23
  npm install @illuma/core
24
24
  ```
25
25
 
26
+ ## 🤝 Compatibility
27
+
28
+ Anything that supports ES2015+ (ES6+).
29
+ Practically the library is compatible with Node.js (v14+) and all modern browsers.
30
+ For older environments, consider using a transpiler like Babel or TypeScript or provide polyfills as needed.
31
+
26
32
  ## 🚀 Quick Start
27
33
 
28
34
  ```typescript
package/dist/index.cjs CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/index.ts
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
+ ILLUMA_ERR_CODES: () => ERR_CODES,
24
25
  INJECTION_SYMBOL: () => INJECTION_SYMBOL,
25
26
  InjectionContext: () => InjectionContext,
26
27
  InjectionError: () => InjectionError,
@@ -34,6 +35,7 @@ __export(index_exports, {
34
35
  extractToken: () => extractToken,
35
36
  getInjectableToken: () => getInjectableToken,
36
37
  injectAsync: () => injectAsync,
38
+ injectDefer: () => injectDefer,
37
39
  injectEntryAsync: () => injectEntryAsync,
38
40
  injectGroupAsync: () => injectGroupAsync,
39
41
  isConstructor: () => isConstructor,
@@ -45,6 +47,29 @@ __export(index_exports, {
45
47
  module.exports = __toCommonJS(index_exports);
46
48
 
47
49
  // src/lib/errors.ts
50
+ var ERR_CODES = {
51
+ // Provider errors
52
+ DUPLICATE_PROVIDER: 100,
53
+ DUPLICATE_FACTORY: 101,
54
+ INVALID_CTOR: 102,
55
+ INVALID_PROVIDER: 103,
56
+ // Alias errors
57
+ INVALID_ALIAS: 200,
58
+ LOOP_ALIAS: 201,
59
+ // Bootstrap errors
60
+ NOT_BOOTSTRAPPED: 300,
61
+ BOOTSTRAPPED: 301,
62
+ DOUBLE_BOOTSTRAP: 302,
63
+ // Retrieval errors
64
+ NOT_FOUND: 400,
65
+ CIRCULAR_DEPENDENCY: 401,
66
+ // Instantiation errors
67
+ UNTRACKED: 500,
68
+ OUTSIDE_CONTEXT: 501,
69
+ CALLED_UTILS_OUTSIDE_CONTEXT: 502,
70
+ INSTANCE_ACCESS_FAILED: 503,
71
+ ACCESS_FAILED: 504
72
+ };
48
73
  var InjectionError = class _InjectionError extends Error {
49
74
  static {
50
75
  __name(this, "InjectionError");
@@ -56,66 +81,70 @@ var InjectionError = class _InjectionError extends Error {
56
81
  }
57
82
  // Provider errors
58
83
  static duplicate(token) {
59
- return new _InjectionError(100, `Duplicate provider for token "${token.toString()}" detected.`);
84
+ return new _InjectionError(ERR_CODES.DUPLICATE_PROVIDER, `Duplicate provider for token "${token.toString()}" detected.`);
60
85
  }
61
86
  static duplicateFactory(token) {
62
- return new _InjectionError(101, `Tried to re-provide factory for token "${token.toString()}" detected.`);
87
+ return new _InjectionError(ERR_CODES.DUPLICATE_FACTORY, `Tried to re-provide factory for token "${token.toString()}" detected.`);
63
88
  }
64
89
  static invalidCtor(ctor) {
65
- return new _InjectionError(102, `Cannot use constructor for token "${ctor.name}". Please make sure to use @nodeInjectable() decorator`);
90
+ return new _InjectionError(ERR_CODES.INVALID_CTOR, `Cannot use constructor for token "${ctor.name}". Please make sure to use @nodeInjectable() decorator`);
66
91
  }
67
92
  static invalidProvider(provider) {
68
- return new _InjectionError(103, `Cannot use provider as it is neither a NodeToken nor MultiNodeToken nor a valid constructor.:
93
+ return new _InjectionError(ERR_CODES.INVALID_PROVIDER, `Cannot use provider as it is neither a NodeToken nor MultiNodeToken nor a valid constructor.:
69
94
  ${provider}`);
70
95
  }
71
96
  // Alias errors
72
97
  static invalidAlias(alias) {
73
98
  const aliasStr = typeof alias === "function" ? alias.name || "Unknown" : String(alias);
74
- return new _InjectionError(200, `Invalid alias target "${aliasStr}". Alias must be a NodeToken, MultiNodeToken, or a class decorated with @NodeInjectable().`);
99
+ return new _InjectionError(ERR_CODES.INVALID_ALIAS, `Invalid alias target "${aliasStr}". Alias must be a NodeToken, MultiNodeToken, or a class decorated with @NodeInjectable().`);
75
100
  }
76
101
  static loopAlias(alias) {
77
- return new _InjectionError(201, `Token "${alias.toString()}" cannot alias itself in a loop.`);
102
+ return new _InjectionError(ERR_CODES.LOOP_ALIAS, `Token "${alias.toString()}" cannot alias itself in a loop.`);
78
103
  }
79
104
  // Bootstrap errors
80
105
  static notBootstrapped() {
81
- return new _InjectionError(300, "Cannot retrieve providers before the container has been bootstrapped.");
106
+ return new _InjectionError(ERR_CODES.NOT_BOOTSTRAPPED, "Cannot retrieve providers before the container has been bootstrapped.");
82
107
  }
83
108
  static bootstrapped() {
84
- return new _InjectionError(301, "Cannot modify providers after the container has been bootstrapped.");
109
+ return new _InjectionError(ERR_CODES.BOOTSTRAPPED, "Cannot modify providers after the container has been bootstrapped.");
85
110
  }
86
111
  static doubleBootstrap() {
87
- return new _InjectionError(302, "Container has already been bootstrapped and cannot be bootstrapped again.");
112
+ return new _InjectionError(ERR_CODES.DOUBLE_BOOTSTRAP, "Container has already been bootstrapped and cannot be bootstrapped again.");
88
113
  }
89
114
  // Retrieval errors
90
115
  static notFound(token) {
91
- return new _InjectionError(400, `No provider found for "${token.toString()}".`);
116
+ return new _InjectionError(ERR_CODES.NOT_FOUND, `No provider found for "${token.toString()}".`);
92
117
  }
93
118
  static circularDependency(provider, path) {
94
119
  const providerStr = provider instanceof NodeBase ? provider.toString() : provider.name;
95
120
  const pathStr = path.map((p) => p instanceof NodeBase ? p.toString() : p.name).join(" -> ");
96
- return new _InjectionError(401, `Circular dependency detected while resolving "${providerStr}":
121
+ return new _InjectionError(ERR_CODES.CIRCULAR_DEPENDENCY, `Circular dependency detected while resolving "${providerStr}":
97
122
  ${pathStr}`);
98
123
  }
99
124
  // Instantiation errors
100
125
  static untracked(token, parent) {
101
126
  const tokenStr = token instanceof NodeBase ? token.toString() : token.name;
102
127
  const parentStr = parent instanceof NodeBase ? parent.toString() : parent.name;
103
- return new _InjectionError(500, `Cannot instantiate ${parentStr} because it depends on untracked injection ${tokenStr}. Please make sure all injections are properly tracked.`);
128
+ return new _InjectionError(ERR_CODES.UNTRACKED, `Cannot instantiate ${parentStr} because it depends on untracked injection ${tokenStr}. Please make sure all injections are properly tracked.`);
104
129
  }
105
130
  static outsideContext(token) {
106
131
  const tokenStr = token instanceof NodeBase ? token.toString() : token.name;
107
- return new _InjectionError(501, `Cannot inject "${tokenStr}" outside of an injection context.`);
132
+ return new _InjectionError(ERR_CODES.OUTSIDE_CONTEXT, `Cannot inject "${tokenStr}" outside of an injection context.`);
108
133
  }
109
134
  static calledUtilsOutsideContext() {
110
- return new _InjectionError(502, "Cannot call injection utilities outside of an injection context.");
135
+ return new _InjectionError(ERR_CODES.CALLED_UTILS_OUTSIDE_CONTEXT, "Cannot call injection utilities outside of an injection context.");
111
136
  }
112
137
  static instanceAccessFailed(token) {
113
- return new _InjectionError(503, `Failed to access instance for token "${token.toString()}". It was not properly instantiated.`);
138
+ return new _InjectionError(ERR_CODES.INSTANCE_ACCESS_FAILED, `Failed to access instance for token "${token.toString()}". It was not properly instantiated.`);
114
139
  }
115
140
  static accessFailed() {
116
- return new _InjectionError(504, "Failed to access the requested instance due to an unknown error.");
141
+ return new _InjectionError(ERR_CODES.ACCESS_FAILED, "Failed to access the requested instance due to an unknown error.");
117
142
  }
118
143
  };
144
+ function isNotFoundError(error) {
145
+ return error instanceof InjectionError && error.code === ERR_CODES.NOT_FOUND;
146
+ }
147
+ __name(isNotFoundError, "isNotFoundError");
119
148
 
120
149
  // src/lib/api/token.ts
121
150
  var NodeBase = class {
@@ -335,6 +364,13 @@ var InjectionContext = class _InjectionContext {
335
364
  }
336
365
  };
337
366
 
367
+ // src/lib/api/proxy.ts
368
+ var SHAPE_SHIFTER = new Proxy(() => {
369
+ }, {
370
+ get: /* @__PURE__ */ __name(() => SHAPE_SHIFTER, "get"),
371
+ apply: /* @__PURE__ */ __name(() => SHAPE_SHIFTER, "apply")
372
+ });
373
+
338
374
  // src/lib/api/injection.ts
339
375
  function nodeInject(provider, options) {
340
376
  let token = provider;
@@ -351,7 +387,7 @@ function nodeInject(provider, options) {
351
387
  if (InjectionContext.injector) {
352
388
  return InjectionContext.injector(token, options?.optional);
353
389
  }
354
- return injection;
390
+ return SHAPE_SHIFTER;
355
391
  }
356
392
  __name(nodeInject, "nodeInject");
357
393
 
@@ -471,23 +507,29 @@ var TreeRootNode = class {
471
507
  static {
472
508
  __name(this, "TreeRootNode");
473
509
  }
510
+ instant;
474
511
  _deps = /* @__PURE__ */ new Set();
475
512
  _treePool = /* @__PURE__ */ new Map();
513
+ constructor(instant = true) {
514
+ this.instant = instant;
515
+ }
476
516
  get dependencies() {
477
517
  return this._deps;
478
518
  }
479
519
  addDependency(node) {
480
520
  this._deps.add(node);
481
521
  }
482
- instantiate() {
522
+ build() {
483
523
  for (const dep of this._deps) {
484
- dep.instantiate(this._treePool);
485
524
  if ("token" in dep.proto) this._treePool.set(dep.proto.token, dep);
525
+ if (this.instant) dep.instantiate(this._treePool);
526
+ else dep.collectPool(this._treePool);
486
527
  }
487
528
  }
488
529
  find(token) {
489
530
  const node = this._treePool.get(token);
490
531
  if (!node) return null;
532
+ if (!this.instant) node.instantiate(this._treePool);
491
533
  return node;
492
534
  }
493
535
  toString() {
@@ -518,6 +560,11 @@ var TreeNodeSingle = class {
518
560
  else this._deps.set(node.proto.token, node);
519
561
  node.allocations++;
520
562
  }
563
+ collectPool(pool) {
564
+ for (const node of this._deps.values()) node.collectPool(pool);
565
+ for (const dep of this._transparent) dep.collectPool(pool);
566
+ pool.set(this.proto.token, this);
567
+ }
521
568
  instantiate(pool) {
522
569
  if (this._resolved) return;
523
570
  for (const node of this._deps.values()) node.instantiate(pool);
@@ -555,6 +602,10 @@ var TreeNodeTransparent = class _TreeNodeTransparent {
555
602
  else this._deps.set(node.proto.token, node);
556
603
  node.allocations++;
557
604
  }
605
+ collectPool(pool) {
606
+ for (const node of this._deps.values()) node.collectPool(pool);
607
+ for (const dep of this._transparent) dep.collectPool(pool);
608
+ }
558
609
  instantiate(pool) {
559
610
  if (this._resolved) return;
560
611
  for (const dep of this._transparent) dep.instantiate(pool);
@@ -579,6 +630,10 @@ var TreeNodeMulti = class _TreeNodeMulti {
579
630
  constructor(proto) {
580
631
  this.proto = proto;
581
632
  }
633
+ collectPool(pool) {
634
+ for (const dep of this._deps) dep.collectPool(pool);
635
+ pool.set(this.proto.token, this);
636
+ }
582
637
  instantiate(pool) {
583
638
  if (this._resolved) return;
584
639
  for (const dep of this._deps) {
@@ -755,6 +810,37 @@ var InjectorImpl = class {
755
810
  };
756
811
  var Injector = new NodeToken("Injector");
757
812
 
813
+ // src/lib/utils/defer.ts
814
+ function injectDefer(provider, options) {
815
+ const injector = nodeInject(Injector);
816
+ let token = provider;
817
+ if (isInjectable(provider)) token = getInjectableToken(provider);
818
+ if (!isNodeBase(token)) throw InjectionError.invalidProvider(String(token));
819
+ let resolved = false;
820
+ let instance = SHAPE_SHIFTER;
821
+ return () => {
822
+ if (resolved) return instance;
823
+ if (options?.optional) {
824
+ try {
825
+ instance = injector.get(token);
826
+ resolved = true;
827
+ return instance;
828
+ } catch (e) {
829
+ if (isNotFoundError(e)) {
830
+ resolved = true;
831
+ instance = null;
832
+ return instance;
833
+ }
834
+ throw e;
835
+ }
836
+ }
837
+ instance = injector.get(token);
838
+ resolved = true;
839
+ return instance;
840
+ };
841
+ }
842
+ __name(injectDefer, "injectDefer");
843
+
758
844
  // src/lib/utils/inheritance.ts
759
845
  function injectGroupAsync(fn, opts) {
760
846
  const { container: parent } = nodeInject(Injector);
@@ -949,7 +1035,7 @@ var NodeContainer = class extends Illuma {
949
1035
  return parentNode.findNode(token);
950
1036
  }
951
1037
  _buildInjectionTree() {
952
- const root = new TreeRootNode();
1038
+ const root = new TreeRootNode(this._opts?.instant);
953
1039
  const cache = /* @__PURE__ */ new Map();
954
1040
  const nodes = [
955
1041
  ...this._protoNodes.values(),
@@ -994,7 +1080,7 @@ var NodeContainer = class extends Illuma {
994
1080
  value: new InjectorImpl(this)
995
1081
  });
996
1082
  this._rootNode = this._buildInjectionTree();
997
- this._rootNode.instantiate();
1083
+ this._rootNode.build();
998
1084
  this._bootstrapped = true;
999
1085
  const end = performance.now();
1000
1086
  const duration = end - start;
@@ -1065,6 +1151,7 @@ var NodeContainer = class extends Illuma {
1065
1151
  };
1066
1152
  // Annotate the CommonJS export names for ESM import in node:
1067
1153
  0 && (module.exports = {
1154
+ ILLUMA_ERR_CODES,
1068
1155
  INJECTION_SYMBOL,
1069
1156
  InjectionContext,
1070
1157
  InjectionError,
@@ -1078,6 +1165,7 @@ var NodeContainer = class extends Illuma {
1078
1165
  extractToken,
1079
1166
  getInjectableToken,
1080
1167
  injectAsync,
1168
+ injectDefer,
1081
1169
  injectEntryAsync,
1082
1170
  injectGroupAsync,
1083
1171
  isConstructor,