@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 +3 -35
- package/README.md +6 -0
- package/dist/index.cjs +109 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -76
- package/dist/index.d.ts +120 -76
- package/dist/index.js +107 -21
- package/dist/index.js.map +1 -1
- package/dist/{injection-Y_bVmBSk.d.ts → injection-CJGqGWJ6.d.cts} +1 -1
- package/dist/{injection-CSxu56ds.d.cts → injection-D22uAh1O.d.ts} +1 -1
- package/dist/{plugin-container-CwkVlVS4.d.ts → plugin-container-CUw26ZhP.d.ts} +21 -18
- package/dist/{plugin-container-D8Zwpigq.d.cts → plugin-container-n0FIqLbh.d.cts} +21 -18
- package/dist/plugins.d.cts +3 -3
- package/dist/plugins.d.ts +3 -3
- package/dist/testkit.cjs +70 -21
- package/dist/testkit.cjs.map +1 -1
- package/dist/testkit.d.cts +2 -2
- package/dist/testkit.d.ts +2 -2
- package/dist/testkit.js +70 -21
- package/dist/testkit.js.map +1 -1
- package/dist/{providers-D9YA8L_g.d.cts → token-BvQrvm-Q.d.cts} +73 -73
- package/dist/{providers-D9YA8L_g.d.ts → token-BvQrvm-Q.d.ts} +73 -73
- package/package.json +1 -1
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.
|
|
10
|
+
## 1.2.1 - 2026-01-10
|
|
11
11
|
|
|
12
|
-
## 1.
|
|
12
|
+
## 1.2.0 - 2026-01-10
|
|
13
13
|
|
|
14
|
-
## 1.
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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.
|
|
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,
|