@navios/di 0.6.1 → 0.7.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 +47 -0
- package/lib/browser/index.d.mts +15 -84
- package/lib/browser/index.d.mts.map +1 -1
- package/lib/browser/index.mjs +121 -484
- package/lib/browser/index.mjs.map +1 -1
- package/lib/{index-DW3K5sOX.d.cts → container-BuAutHGg.d.mts} +17 -62
- package/lib/container-BuAutHGg.d.mts.map +1 -0
- package/lib/{testing-DIaIRiJz.cjs → container-DnzgpfBe.cjs} +81 -459
- package/lib/container-DnzgpfBe.cjs.map +1 -0
- package/lib/{testing-BG_fa9TJ.mjs → container-Pb_Y4Z4x.mjs} +81 -446
- package/lib/container-Pb_Y4Z4x.mjs.map +1 -0
- package/lib/{index-7jfWsiG4.d.mts → container-oGTgX2iX.d.cts} +12 -67
- package/lib/container-oGTgX2iX.d.cts.map +1 -0
- package/lib/index.cjs +44 -52
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +6 -25
- package/lib/index.d.cts.map +1 -1
- package/lib/index.d.mts +6 -25
- package/lib/index.d.mts.map +1 -1
- package/lib/index.mjs +2 -2
- package/lib/testing/index.cjs +343 -4
- package/lib/testing/index.cjs.map +1 -0
- package/lib/testing/index.d.cts +65 -2
- package/lib/testing/index.d.cts.map +1 -0
- package/lib/testing/index.d.mts +65 -2
- package/lib/testing/index.d.mts.map +1 -0
- package/lib/testing/index.mjs +341 -2
- package/lib/testing/index.mjs.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/async-local-storage.browser.spec.mts +18 -92
- package/src/__tests__/container.spec.mts +93 -0
- package/src/__tests__/e2e.browser.spec.mts +7 -15
- package/src/__tests__/library-findings.spec.mts +23 -21
- package/src/browser.mts +4 -9
- package/src/container/scoped-container.mts +14 -8
- package/src/index.mts +5 -8
- package/src/internal/context/async-local-storage.browser.mts +19 -0
- package/src/internal/context/async-local-storage.mts +46 -98
- package/src/internal/context/async-local-storage.types.mts +7 -0
- package/src/internal/context/resolution-context.mts +23 -5
- package/src/internal/context/sync-local-storage.mts +3 -1
- package/src/internal/core/instance-resolver.mts +8 -1
- package/src/internal/lifecycle/circular-detector.mts +15 -0
- package/src/token/registry.mts +21 -0
- package/tsdown.config.mts +12 -1
- package/vitest.config.mts +25 -3
- package/lib/index-7jfWsiG4.d.mts.map +0 -1
- package/lib/index-DW3K5sOX.d.cts.map +0 -1
- package/lib/testing-BG_fa9TJ.mjs.map +0 -1
- package/lib/testing-DIaIRiJz.cjs.map +0 -1
package/lib/testing/index.mjs
CHANGED
|
@@ -1,3 +1,342 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as Injectable, C as getInjectableToken, L as InjectableType, N as globalRegistry, R as InjectableScope, t as _Container$1 } from "../container-Pb_Y4Z4x.mjs";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
//#region src/testing/test-container.mts
|
|
4
|
+
function applyDecs2203RFactory() {
|
|
5
|
+
function createAddInitializerMethod(initializers, decoratorFinishedRef) {
|
|
6
|
+
return function addInitializer(initializer) {
|
|
7
|
+
assertNotFinished(decoratorFinishedRef, "addInitializer");
|
|
8
|
+
assertCallable(initializer, "An initializer");
|
|
9
|
+
initializers.push(initializer);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value) {
|
|
13
|
+
var kindStr;
|
|
14
|
+
switch (kind) {
|
|
15
|
+
case 1:
|
|
16
|
+
kindStr = "accessor";
|
|
17
|
+
break;
|
|
18
|
+
case 2:
|
|
19
|
+
kindStr = "method";
|
|
20
|
+
break;
|
|
21
|
+
case 3:
|
|
22
|
+
kindStr = "getter";
|
|
23
|
+
break;
|
|
24
|
+
case 4:
|
|
25
|
+
kindStr = "setter";
|
|
26
|
+
break;
|
|
27
|
+
default: kindStr = "field";
|
|
28
|
+
}
|
|
29
|
+
var ctx = {
|
|
30
|
+
kind: kindStr,
|
|
31
|
+
name: isPrivate ? "#" + name : name,
|
|
32
|
+
static: isStatic,
|
|
33
|
+
private: isPrivate,
|
|
34
|
+
metadata
|
|
35
|
+
};
|
|
36
|
+
var decoratorFinishedRef = { v: false };
|
|
37
|
+
ctx.addInitializer = createAddInitializerMethod(initializers, decoratorFinishedRef);
|
|
38
|
+
var get, set;
|
|
39
|
+
if (kind === 0) if (isPrivate) {
|
|
40
|
+
get = desc.get;
|
|
41
|
+
set = desc.set;
|
|
42
|
+
} else {
|
|
43
|
+
get = function() {
|
|
44
|
+
return this[name];
|
|
45
|
+
};
|
|
46
|
+
set = function(v) {
|
|
47
|
+
this[name] = v;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
else if (kind === 2) get = function() {
|
|
51
|
+
return desc.value;
|
|
52
|
+
};
|
|
53
|
+
else {
|
|
54
|
+
if (kind === 1 || kind === 3) get = function() {
|
|
55
|
+
return desc.get.call(this);
|
|
56
|
+
};
|
|
57
|
+
if (kind === 1 || kind === 4) set = function(v) {
|
|
58
|
+
desc.set.call(this, v);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
ctx.access = get && set ? {
|
|
62
|
+
get,
|
|
63
|
+
set
|
|
64
|
+
} : get ? { get } : { set };
|
|
65
|
+
try {
|
|
66
|
+
return dec(value, ctx);
|
|
67
|
+
} finally {
|
|
68
|
+
decoratorFinishedRef.v = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function assertNotFinished(decoratorFinishedRef, fnName) {
|
|
72
|
+
if (decoratorFinishedRef.v) throw new Error("attempted to call " + fnName + " after decoration was finished");
|
|
73
|
+
}
|
|
74
|
+
function assertCallable(fn, hint) {
|
|
75
|
+
if (typeof fn !== "function") throw new TypeError(hint + " must be a function");
|
|
76
|
+
}
|
|
77
|
+
function assertValidReturnValue(kind, value) {
|
|
78
|
+
var type = typeof value;
|
|
79
|
+
if (kind === 1) {
|
|
80
|
+
if (type !== "object" || value === null) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0");
|
|
81
|
+
if (value.get !== void 0) assertCallable(value.get, "accessor.get");
|
|
82
|
+
if (value.set !== void 0) assertCallable(value.set, "accessor.set");
|
|
83
|
+
if (value.init !== void 0) assertCallable(value.init, "accessor.init");
|
|
84
|
+
} else if (type !== "function") {
|
|
85
|
+
var hint;
|
|
86
|
+
if (kind === 0) hint = "field";
|
|
87
|
+
else if (kind === 10) hint = "class";
|
|
88
|
+
else hint = "method";
|
|
89
|
+
throw new TypeError(hint + " decorators must return a function or void 0");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata) {
|
|
93
|
+
var decs = decInfo[0];
|
|
94
|
+
var desc, init, value;
|
|
95
|
+
if (isPrivate) if (kind === 0 || kind === 1) desc = {
|
|
96
|
+
get: decInfo[3],
|
|
97
|
+
set: decInfo[4]
|
|
98
|
+
};
|
|
99
|
+
else if (kind === 3) desc = { get: decInfo[3] };
|
|
100
|
+
else if (kind === 4) desc = { set: decInfo[3] };
|
|
101
|
+
else desc = { value: decInfo[3] };
|
|
102
|
+
else if (kind !== 0) desc = Object.getOwnPropertyDescriptor(base, name);
|
|
103
|
+
if (kind === 1) value = {
|
|
104
|
+
get: desc.get,
|
|
105
|
+
set: desc.set
|
|
106
|
+
};
|
|
107
|
+
else if (kind === 2) value = desc.value;
|
|
108
|
+
else if (kind === 3) value = desc.get;
|
|
109
|
+
else if (kind === 4) value = desc.set;
|
|
110
|
+
var newValue, get, set;
|
|
111
|
+
if (typeof decs === "function") {
|
|
112
|
+
newValue = memberDec(decs, name, desc, initializers, kind, isStatic, isPrivate, metadata, value);
|
|
113
|
+
if (newValue !== void 0) {
|
|
114
|
+
assertValidReturnValue(kind, newValue);
|
|
115
|
+
if (kind === 0) init = newValue;
|
|
116
|
+
else if (kind === 1) {
|
|
117
|
+
init = newValue.init;
|
|
118
|
+
get = newValue.get || value.get;
|
|
119
|
+
set = newValue.set || value.set;
|
|
120
|
+
value = {
|
|
121
|
+
get,
|
|
122
|
+
set
|
|
123
|
+
};
|
|
124
|
+
} else value = newValue;
|
|
125
|
+
}
|
|
126
|
+
} else for (var i = decs.length - 1; i >= 0; i--) {
|
|
127
|
+
var dec = decs[i];
|
|
128
|
+
newValue = memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value);
|
|
129
|
+
if (newValue !== void 0) {
|
|
130
|
+
assertValidReturnValue(kind, newValue);
|
|
131
|
+
var newInit;
|
|
132
|
+
if (kind === 0) newInit = newValue;
|
|
133
|
+
else if (kind === 1) {
|
|
134
|
+
newInit = newValue.init;
|
|
135
|
+
get = newValue.get || value.get;
|
|
136
|
+
set = newValue.set || value.set;
|
|
137
|
+
value = {
|
|
138
|
+
get,
|
|
139
|
+
set
|
|
140
|
+
};
|
|
141
|
+
} else value = newValue;
|
|
142
|
+
if (newInit !== void 0) if (init === void 0) init = newInit;
|
|
143
|
+
else if (typeof init === "function") init = [init, newInit];
|
|
144
|
+
else init.push(newInit);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (kind === 0 || kind === 1) {
|
|
148
|
+
if (init === void 0) init = function(instance, init$1) {
|
|
149
|
+
return init$1;
|
|
150
|
+
};
|
|
151
|
+
else if (typeof init !== "function") {
|
|
152
|
+
var ownInitializers = init;
|
|
153
|
+
init = function(instance, init$1) {
|
|
154
|
+
var value$1 = init$1;
|
|
155
|
+
for (var i$1 = 0; i$1 < ownInitializers.length; i$1++) value$1 = ownInitializers[i$1].call(instance, value$1);
|
|
156
|
+
return value$1;
|
|
157
|
+
};
|
|
158
|
+
} else {
|
|
159
|
+
var originalInitializer = init;
|
|
160
|
+
init = function(instance, init$1) {
|
|
161
|
+
return originalInitializer.call(instance, init$1);
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
ret.push(init);
|
|
165
|
+
}
|
|
166
|
+
if (kind !== 0) {
|
|
167
|
+
if (kind === 1) {
|
|
168
|
+
desc.get = value.get;
|
|
169
|
+
desc.set = value.set;
|
|
170
|
+
} else if (kind === 2) desc.value = value;
|
|
171
|
+
else if (kind === 3) desc.get = value;
|
|
172
|
+
else if (kind === 4) desc.set = value;
|
|
173
|
+
if (isPrivate) if (kind === 1) {
|
|
174
|
+
ret.push(function(instance, args) {
|
|
175
|
+
return value.get.call(instance, args);
|
|
176
|
+
});
|
|
177
|
+
ret.push(function(instance, args) {
|
|
178
|
+
return value.set.call(instance, args);
|
|
179
|
+
});
|
|
180
|
+
} else if (kind === 2) ret.push(value);
|
|
181
|
+
else ret.push(function(instance, args) {
|
|
182
|
+
return value.call(instance, args);
|
|
183
|
+
});
|
|
184
|
+
else Object.defineProperty(base, name, desc);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function applyMemberDecs(Class, decInfos, metadata) {
|
|
188
|
+
var ret = [];
|
|
189
|
+
var protoInitializers;
|
|
190
|
+
var staticInitializers;
|
|
191
|
+
var existingProtoNonFields = /* @__PURE__ */ new Map();
|
|
192
|
+
var existingStaticNonFields = /* @__PURE__ */ new Map();
|
|
193
|
+
for (var i = 0; i < decInfos.length; i++) {
|
|
194
|
+
var decInfo = decInfos[i];
|
|
195
|
+
if (!Array.isArray(decInfo)) continue;
|
|
196
|
+
var kind = decInfo[1];
|
|
197
|
+
var name = decInfo[2];
|
|
198
|
+
var isPrivate = decInfo.length > 3;
|
|
199
|
+
var isStatic = kind >= 5;
|
|
200
|
+
var base;
|
|
201
|
+
var initializers;
|
|
202
|
+
if (isStatic) {
|
|
203
|
+
base = Class;
|
|
204
|
+
kind = kind - 5;
|
|
205
|
+
staticInitializers = staticInitializers || [];
|
|
206
|
+
initializers = staticInitializers;
|
|
207
|
+
} else {
|
|
208
|
+
base = Class.prototype;
|
|
209
|
+
protoInitializers = protoInitializers || [];
|
|
210
|
+
initializers = protoInitializers;
|
|
211
|
+
}
|
|
212
|
+
if (kind !== 0 && !isPrivate) {
|
|
213
|
+
var existingNonFields = isStatic ? existingStaticNonFields : existingProtoNonFields;
|
|
214
|
+
var existingKind = existingNonFields.get(name) || 0;
|
|
215
|
+
if (existingKind === true || existingKind === 3 && kind !== 4 || existingKind === 4 && kind !== 3) throw new Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + name);
|
|
216
|
+
else if (!existingKind && kind > 2) existingNonFields.set(name, kind);
|
|
217
|
+
else existingNonFields.set(name, true);
|
|
218
|
+
}
|
|
219
|
+
applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata);
|
|
220
|
+
}
|
|
221
|
+
pushInitializers(ret, protoInitializers);
|
|
222
|
+
pushInitializers(ret, staticInitializers);
|
|
223
|
+
return ret;
|
|
224
|
+
}
|
|
225
|
+
function pushInitializers(ret, initializers) {
|
|
226
|
+
if (initializers) ret.push(function(instance) {
|
|
227
|
+
for (var i = 0; i < initializers.length; i++) initializers[i].call(instance);
|
|
228
|
+
return instance;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
function applyClassDecs(targetClass, classDecs, metadata) {
|
|
232
|
+
if (classDecs.length > 0) {
|
|
233
|
+
var initializers = [];
|
|
234
|
+
var newClass = targetClass;
|
|
235
|
+
var name = targetClass.name;
|
|
236
|
+
for (var i = classDecs.length - 1; i >= 0; i--) {
|
|
237
|
+
var decoratorFinishedRef = { v: false };
|
|
238
|
+
try {
|
|
239
|
+
var nextNewClass = classDecs[i](newClass, {
|
|
240
|
+
kind: "class",
|
|
241
|
+
name,
|
|
242
|
+
addInitializer: createAddInitializerMethod(initializers, decoratorFinishedRef),
|
|
243
|
+
metadata
|
|
244
|
+
});
|
|
245
|
+
} finally {
|
|
246
|
+
decoratorFinishedRef.v = true;
|
|
247
|
+
}
|
|
248
|
+
if (nextNewClass !== void 0) {
|
|
249
|
+
assertValidReturnValue(10, nextNewClass);
|
|
250
|
+
newClass = nextNewClass;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return [defineMetadata(newClass, metadata), function() {
|
|
254
|
+
for (var i$1 = 0; i$1 < initializers.length; i$1++) initializers[i$1].call(newClass);
|
|
255
|
+
}];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function defineMetadata(Class, metadata) {
|
|
259
|
+
return Object.defineProperty(Class, Symbol.metadata || Symbol.for("Symbol.metadata"), {
|
|
260
|
+
configurable: true,
|
|
261
|
+
enumerable: true,
|
|
262
|
+
value: metadata
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
return function applyDecs2203R(targetClass, memberDecs, classDecs, parentClass) {
|
|
266
|
+
if (parentClass !== void 0) var parentMetadata = parentClass[Symbol.metadata || Symbol.for("Symbol.metadata")];
|
|
267
|
+
var metadata = Object.create(parentMetadata === void 0 ? null : parentMetadata);
|
|
268
|
+
var e = applyMemberDecs(targetClass, memberDecs, metadata);
|
|
269
|
+
if (!classDecs.length) defineMetadata(targetClass, metadata);
|
|
270
|
+
return {
|
|
271
|
+
e,
|
|
272
|
+
get c() {
|
|
273
|
+
return applyClassDecs(targetClass, classDecs, metadata);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
279
|
+
return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
|
|
280
|
+
}
|
|
281
|
+
var _dec, _initClass, _Container;
|
|
282
|
+
/**
|
|
283
|
+
* A binding builder for the TestContainer that allows chaining binding operations.
|
|
284
|
+
*/ var TestBindingBuilder = class {
|
|
285
|
+
container;
|
|
286
|
+
token;
|
|
287
|
+
constructor(container, token) {
|
|
288
|
+
this.container = container;
|
|
289
|
+
this.token = token;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Binds the token to a specific value.
|
|
293
|
+
* This is useful for testing with mock values or constants.
|
|
294
|
+
* @param value The value to bind to the token
|
|
295
|
+
*/ toValue(value) {
|
|
296
|
+
const instanceName = this.container.getServiceLocator().getInstanceIdentifier(this.token);
|
|
297
|
+
this.container.getServiceLocator().getManager().storeCreatedHolder(instanceName, value, InjectableType.Class, InjectableScope.Singleton);
|
|
298
|
+
return this.container;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Binds the token to a class constructor.
|
|
302
|
+
* @param target The class constructor to bind to
|
|
303
|
+
*/ toClass(target) {
|
|
304
|
+
this.container["registry"].set(this.token, InjectableScope.Singleton, target, InjectableType.Class);
|
|
305
|
+
return this.container;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
let _TestContainer;
|
|
309
|
+
_dec = Injectable();
|
|
310
|
+
var TestContainer = class extends (_Container = _Container$1) {
|
|
311
|
+
static {
|
|
312
|
+
({c: [_TestContainer, _initClass]} = _apply_decs_2203_r(this, [], [_dec], _Container));
|
|
313
|
+
}
|
|
314
|
+
constructor(registry = globalRegistry, logger = null, injectors = void 0) {
|
|
315
|
+
super(registry, logger, injectors);
|
|
316
|
+
}
|
|
317
|
+
bind(token) {
|
|
318
|
+
let realToken = token;
|
|
319
|
+
if (typeof token === "function") realToken = getInjectableToken(token);
|
|
320
|
+
return new TestBindingBuilder(this, realToken);
|
|
321
|
+
}
|
|
322
|
+
bindValue(token, value) {
|
|
323
|
+
return this.bind(token).toValue(value);
|
|
324
|
+
}
|
|
325
|
+
bindClass(token, target) {
|
|
326
|
+
return this.bind(token).toClass(target);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Creates a new TestContainer instance with the same configuration.
|
|
330
|
+
* This is useful for creating isolated test containers.
|
|
331
|
+
* @returns A new TestContainer instance
|
|
332
|
+
*/ createChild() {
|
|
333
|
+
return new _TestContainer(this.registry, this.logger, this.injectors);
|
|
334
|
+
}
|
|
335
|
+
static {
|
|
336
|
+
_initClass();
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
//#endregion
|
|
341
|
+
export { TestBindingBuilder, _TestContainer as TestContainer };
|
|
342
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["Container","Injectable","InjectableScope","InjectableType","globalRegistry","getInjectableToken","TestBindingBuilder","container","token","toValue","value","instanceName","getServiceLocator","getInstanceIdentifier","getManager","storeCreatedHolder","Class","Singleton","toClass","target","set","TestContainer","registry","logger","injectors","undefined","bind","realToken","bindValue","bindClass","createChild"],"sources":["../../src/testing/test-container.mts"],"sourcesContent":["import type { ClassType, InjectionToken } from '../token/injection-token.mjs'\nimport type { Registry } from '../token/registry.mjs'\nimport type { Injectors } from '../utils/index.mjs'\n\nimport { Container } from '../container/container.mjs'\nimport { Injectable } from '../decorators/injectable.decorator.mjs'\nimport { InjectableScope, InjectableType } from '../enums/index.mjs'\nimport { globalRegistry } from '../token/registry.mjs'\nimport { getInjectableToken } from '../utils/index.mjs'\n\n/**\n * A binding builder for the TestContainer that allows chaining binding operations.\n */\nexport class TestBindingBuilder<T> {\n constructor(\n private readonly container: TestContainer,\n private readonly token: InjectionToken<T, any>,\n ) {}\n\n /**\n * Binds the token to a specific value.\n * This is useful for testing with mock values or constants.\n * @param value The value to bind to the token\n */\n toValue(value: T): TestContainer {\n const instanceName = this.container\n .getServiceLocator()\n .getInstanceIdentifier(this.token)\n this.container\n .getServiceLocator()\n .getManager()\n .storeCreatedHolder(\n instanceName,\n value,\n InjectableType.Class,\n InjectableScope.Singleton,\n )\n return this.container\n }\n\n /**\n * Binds the token to a class constructor.\n * @param target The class constructor to bind to\n */\n toClass(target: ClassType): TestContainer {\n this.container['registry'].set(\n this.token,\n InjectableScope.Singleton,\n target,\n InjectableType.Class,\n )\n return this.container\n }\n}\n\n/**\n * TestContainer extends the base Container with additional methods useful for testing.\n * It provides a simplified API for binding values and classes during test setup.\n */\n@Injectable()\nexport class TestContainer extends Container {\n constructor(\n registry: Registry = globalRegistry,\n logger: Console | null = null,\n injectors: Injectors = undefined as any,\n ) {\n super(registry, logger, injectors)\n }\n\n /**\n * Creates a binding builder for the given token.\n * This allows chaining binding operations like bind(Token).toValue(value).\n * @param token The injection token to bind\n * @returns A TestBindingBuilder for chaining binding operations\n */\n bind<T>(token: ClassType): TestBindingBuilder<T>\n bind<T>(token: InjectionToken<T, any>): TestBindingBuilder<T>\n bind(token: any): TestBindingBuilder<any> {\n let realToken = token\n if (typeof token === 'function') {\n realToken = getInjectableToken(token)\n }\n return new TestBindingBuilder(this, realToken)\n }\n\n /**\n * Binds a value directly to a token.\n * This is a convenience method equivalent to bind(token).toValue(value).\n * @param token The injection token to bind\n * @param value The value to bind to the token\n * @returns The TestContainer instance for chaining\n */\n bindValue<T>(token: ClassType, value: T): TestContainer\n bindValue<T>(token: InjectionToken<T, any>, value: T): TestContainer\n bindValue(token: any, value: any): TestContainer {\n return this.bind(token).toValue(value)\n }\n\n /**\n * Binds a class to a token.\n * This is a convenience method equivalent to bind(token).toClass(target).\n * @param token The injection token to bind\n * @param target The class constructor to bind to\n * @returns The TestContainer instance for chaining\n */\n bindClass(token: ClassType, target: ClassType): TestContainer\n bindClass<T>(token: InjectionToken<T, any>, target: ClassType): TestContainer\n bindClass(token: any, target: any): TestContainer {\n return this.bind(token).toClass(target)\n }\n\n /**\n * Creates a new TestContainer instance with the same configuration.\n * This is useful for creating isolated test containers.\n * @returns A new TestContainer instance\n */\n createChild(): TestContainer {\n return new TestContainer(this.registry, this.logger, this.injectors)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBA4DmCA;;;GA/CnC,IAAaM,qBAAb,MAAaA;;;CACX,YACE,WACA,OACA;OAFiBC,YAAAA;OACAC,QAAAA;;;;;;IAQnBC,QAAQC,OAAyB;EAC/B,MAAMC,eAAe,KAAKJ,UACvBK,mBAAiB,CACjBC,sBAAsB,KAAKL,MAAK;AACnC,OAAKD,UACFK,mBAAiB,CACjBE,YAAU,CACVC,mBACCJ,cACAD,OACAP,eAAea,OACfd,gBAAgBe,UAAS;AAE7B,SAAO,KAAKV;;;;;IAOdW,QAAQC,QAAkC;AACxC,OAAKZ,UAAU,YAAYa,IACzB,KAAKZ,OACLN,gBAAgBe,WAChBE,QACAhB,eAAea,MAAK;AAEtB,SAAO,KAAKT;;;;OAQfN,YAAAA;AACM,IAAMoB,gBAAN,eAA4BrB,aAAAA,cAAAA;;4EAAAA,WAAAA;;CACjC,YACEsB,WAAqBlB,gBACrBmB,SAAyB,MACzBC,YAAuBC,QACvB;AACA,QAAMH,UAAUC,QAAQC,UAAAA;;CAW1BE,KAAKlB,OAAqC;EACxC,IAAImB,YAAYnB;AAChB,MAAI,OAAOA,UAAU,WACnBmB,aAAYtB,mBAAmBG,MAAAA;AAEjC,SAAO,IAAIF,mBAAmB,MAAMqB,UAAAA;;CAYtCC,UAAUpB,OAAYE,OAA2B;AAC/C,SAAO,KAAKgB,KAAKlB,MAAAA,CAAOC,QAAQC,MAAAA;;CAYlCmB,UAAUrB,OAAYW,QAA4B;AAChD,SAAO,KAAKO,KAAKlB,MAAAA,CAAOU,QAAQC,OAAAA;;;;;;IAQlCW,cAA6B;AAC3B,SAAO,IAAIT,eAAc,KAAKC,UAAU,KAAKC,QAAQ,KAAKC,UAAS"}
|
package/package.json
CHANGED
|
@@ -6,51 +6,47 @@
|
|
|
6
6
|
* 2. All DI functionality works correctly with the polyfill
|
|
7
7
|
* 3. The resolution context pattern works for synchronous operations
|
|
8
8
|
*
|
|
9
|
-
* Note: We
|
|
10
|
-
*
|
|
9
|
+
* Note: We directly import the browser implementation to test it
|
|
10
|
+
* in isolation, simulating how it would work in a browser bundle.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { describe, expect, it } from 'vitest'
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
createAsyncLocalStorage,
|
|
17
17
|
isUsingNativeAsyncLocalStorage,
|
|
18
|
-
|
|
19
|
-
} from '../internal/context/async-local-storage.mjs'
|
|
18
|
+
} from '../internal/context/async-local-storage.browser.mjs'
|
|
20
19
|
import {
|
|
21
20
|
getCurrentResolutionContext,
|
|
22
21
|
withResolutionContext,
|
|
23
|
-
withoutResolutionContext,
|
|
24
22
|
} from '../internal/context/resolution-context.mjs'
|
|
25
|
-
import
|
|
26
|
-
import { InstanceStatus } from '../internal/holder/instance-holder.mjs'
|
|
27
|
-
import { InjectableScope } from '../enums/index.mjs'
|
|
28
|
-
|
|
29
|
-
// Force sync mode for all tests in this file to simulate browser behavior
|
|
30
|
-
beforeAll(() => {
|
|
31
|
-
__testing__.forceSyncMode()
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
afterAll(() => {
|
|
35
|
-
__testing__.reset()
|
|
36
|
-
})
|
|
23
|
+
import { SyncLocalStorage } from '../internal/context/sync-local-storage.mjs'
|
|
37
24
|
|
|
38
25
|
// ============================================================================
|
|
39
26
|
// SECTION 1: Environment Detection
|
|
40
27
|
// ============================================================================
|
|
41
28
|
|
|
42
|
-
describe('Browser Environment Detection
|
|
43
|
-
it('should use sync polyfill
|
|
44
|
-
// After forcing sync mode, native AsyncLocalStorage should not be used
|
|
29
|
+
describe('Browser Environment Detection', () => {
|
|
30
|
+
it('should use sync polyfill in browser build', () => {
|
|
45
31
|
expect(isUsingNativeAsyncLocalStorage()).toBe(false)
|
|
46
32
|
})
|
|
47
33
|
|
|
34
|
+
it('should create a SyncLocalStorage instance', () => {
|
|
35
|
+
const storage = createAsyncLocalStorage<{ value: number }>()
|
|
36
|
+
expect(storage).toBeInstanceOf(SyncLocalStorage)
|
|
37
|
+
})
|
|
38
|
+
|
|
48
39
|
it('should create a working storage instance', () => {
|
|
49
40
|
const storage = createAsyncLocalStorage<{ value: number }>()
|
|
50
41
|
expect(storage).toBeDefined()
|
|
51
42
|
expect(typeof storage.run).toBe('function')
|
|
52
43
|
expect(typeof storage.getStore).toBe('function')
|
|
53
44
|
})
|
|
45
|
+
|
|
46
|
+
it('should create a resolution context instance', () => {
|
|
47
|
+
const resolutionContext = getCurrentResolutionContext()
|
|
48
|
+
expect(resolutionContext).toBeUndefined()
|
|
49
|
+
})
|
|
54
50
|
})
|
|
55
51
|
|
|
56
52
|
// ============================================================================
|
|
@@ -111,77 +107,7 @@ describe('SyncLocalStorage in Browser', () => {
|
|
|
111
107
|
})
|
|
112
108
|
|
|
113
109
|
// ============================================================================
|
|
114
|
-
// SECTION 3:
|
|
115
|
-
// ============================================================================
|
|
116
|
-
|
|
117
|
-
describe('Resolution Context in Browser', () => {
|
|
118
|
-
function createMockHolder(name: string): InstanceHolder {
|
|
119
|
-
return {
|
|
120
|
-
status: InstanceStatus.Creating,
|
|
121
|
-
name,
|
|
122
|
-
instance: null,
|
|
123
|
-
creationPromise: null,
|
|
124
|
-
destroyPromise: null,
|
|
125
|
-
type: class {} as unknown as InstanceHolder['type'],
|
|
126
|
-
scope: InjectableScope.Singleton,
|
|
127
|
-
deps: new Set(),
|
|
128
|
-
destroyListeners: [],
|
|
129
|
-
createdAt: Date.now(),
|
|
130
|
-
waitingFor: new Set(),
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
it('should track resolution context correctly', () => {
|
|
135
|
-
const holderA = createMockHolder('ServiceA')
|
|
136
|
-
const getHolder = () => undefined
|
|
137
|
-
|
|
138
|
-
expect(getCurrentResolutionContext()).toBeUndefined()
|
|
139
|
-
|
|
140
|
-
withResolutionContext(holderA, getHolder, () => {
|
|
141
|
-
const ctx = getCurrentResolutionContext()
|
|
142
|
-
expect(ctx).toBeDefined()
|
|
143
|
-
expect(ctx?.waiterHolder).toBe(holderA)
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
expect(getCurrentResolutionContext()).toBeUndefined()
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it('should handle nested resolution contexts', () => {
|
|
150
|
-
const holderA = createMockHolder('ServiceA')
|
|
151
|
-
const holderB = createMockHolder('ServiceB')
|
|
152
|
-
const getHolder = () => undefined
|
|
153
|
-
|
|
154
|
-
withResolutionContext(holderA, getHolder, () => {
|
|
155
|
-
expect(getCurrentResolutionContext()?.waiterHolder.name).toBe('ServiceA')
|
|
156
|
-
|
|
157
|
-
withResolutionContext(holderB, getHolder, () => {
|
|
158
|
-
expect(getCurrentResolutionContext()?.waiterHolder.name).toBe(
|
|
159
|
-
'ServiceB',
|
|
160
|
-
)
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
expect(getCurrentResolutionContext()?.waiterHolder.name).toBe('ServiceA')
|
|
164
|
-
})
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it('should clear context with withoutResolutionContext', () => {
|
|
168
|
-
const holderA = createMockHolder('ServiceA')
|
|
169
|
-
const getHolder = () => undefined
|
|
170
|
-
|
|
171
|
-
withResolutionContext(holderA, getHolder, () => {
|
|
172
|
-
expect(getCurrentResolutionContext()).toBeDefined()
|
|
173
|
-
|
|
174
|
-
withoutResolutionContext(() => {
|
|
175
|
-
expect(getCurrentResolutionContext()).toBeUndefined()
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
expect(getCurrentResolutionContext()).toBeDefined()
|
|
179
|
-
})
|
|
180
|
-
})
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
// ============================================================================
|
|
184
|
-
// SECTION 4: Async Limitations in Browser
|
|
110
|
+
// SECTION 3: Async Limitations in Browser
|
|
185
111
|
// ============================================================================
|
|
186
112
|
|
|
187
113
|
describe('Async Limitations in Browser (expected behavior)', () => {
|
|
@@ -1317,6 +1317,99 @@ describe('Container', () => {
|
|
|
1317
1317
|
await expect(container.get(ServiceWithUnregistered)).rejects.toThrow()
|
|
1318
1318
|
})
|
|
1319
1319
|
})
|
|
1320
|
+
|
|
1321
|
+
describe('failed service retry', () => {
|
|
1322
|
+
it('should allow retrying service creation after initialization failure', async () => {
|
|
1323
|
+
let callCount = 0
|
|
1324
|
+
|
|
1325
|
+
@Factory({ registry })
|
|
1326
|
+
class FailingFactory implements Factorable<TestService> {
|
|
1327
|
+
async create() {
|
|
1328
|
+
callCount++
|
|
1329
|
+
if (callCount === 1) {
|
|
1330
|
+
throw new Error('First attempt fails')
|
|
1331
|
+
}
|
|
1332
|
+
return new TestService()
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
@Injectable({ registry })
|
|
1337
|
+
class TestService {
|
|
1338
|
+
public value = 'success'
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// First attempt should fail
|
|
1342
|
+
await expect(container.get(FailingFactory)).rejects.toThrow(
|
|
1343
|
+
'First attempt fails',
|
|
1344
|
+
)
|
|
1345
|
+
|
|
1346
|
+
// Second attempt should succeed (service should be removed from storage after failure)
|
|
1347
|
+
const instance = await container.get(FailingFactory)
|
|
1348
|
+
expect(instance).toBeInstanceOf(TestService)
|
|
1349
|
+
expect(instance.value).toBe('success')
|
|
1350
|
+
expect(callCount).toBe(2)
|
|
1351
|
+
})
|
|
1352
|
+
|
|
1353
|
+
it('should allow retrying injectable service creation after constructor throws', async () => {
|
|
1354
|
+
let callCount = 0
|
|
1355
|
+
|
|
1356
|
+
@Injectable({ registry })
|
|
1357
|
+
class FailingService {
|
|
1358
|
+
public value: string
|
|
1359
|
+
|
|
1360
|
+
constructor() {
|
|
1361
|
+
callCount++
|
|
1362
|
+
if (callCount === 1) {
|
|
1363
|
+
throw new Error('Constructor fails on first attempt')
|
|
1364
|
+
}
|
|
1365
|
+
this.value = 'success'
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
// First attempt should fail
|
|
1370
|
+
await expect(container.get(FailingService)).rejects.toThrow(
|
|
1371
|
+
'Constructor fails on first attempt',
|
|
1372
|
+
)
|
|
1373
|
+
|
|
1374
|
+
// Second attempt should succeed
|
|
1375
|
+
const instance = await container.get(FailingService)
|
|
1376
|
+
expect(instance).toBeInstanceOf(FailingService)
|
|
1377
|
+
expect(instance.value).toBe('success')
|
|
1378
|
+
expect(callCount).toBe(2)
|
|
1379
|
+
})
|
|
1380
|
+
|
|
1381
|
+
it('should allow retrying request-scoped service creation after failure', async () => {
|
|
1382
|
+
let callCount = 0
|
|
1383
|
+
|
|
1384
|
+
@Injectable({ registry, scope: InjectableScope.Request })
|
|
1385
|
+
class FailingRequestService {
|
|
1386
|
+
public value: string
|
|
1387
|
+
|
|
1388
|
+
constructor() {
|
|
1389
|
+
callCount++
|
|
1390
|
+
if (callCount === 1) {
|
|
1391
|
+
throw new Error('Request service fails on first attempt')
|
|
1392
|
+
}
|
|
1393
|
+
this.value = 'success'
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
const scoped = container.beginRequest('retry-test-request')
|
|
1398
|
+
|
|
1399
|
+
// First attempt should fail
|
|
1400
|
+
await expect(scoped.get(FailingRequestService)).rejects.toThrow(
|
|
1401
|
+
'Request service fails on first attempt',
|
|
1402
|
+
)
|
|
1403
|
+
|
|
1404
|
+
// Second attempt should succeed within the same request
|
|
1405
|
+
const instance = await scoped.get(FailingRequestService)
|
|
1406
|
+
expect(instance).toBeInstanceOf(FailingRequestService)
|
|
1407
|
+
expect(instance.value).toBe('success')
|
|
1408
|
+
expect(callCount).toBe(2)
|
|
1409
|
+
|
|
1410
|
+
await scoped.endRequest()
|
|
1411
|
+
})
|
|
1412
|
+
})
|
|
1320
1413
|
})
|
|
1321
1414
|
describe('custom injectors', () => {
|
|
1322
1415
|
it('should work with custom injectors', async () => {
|
|
@@ -1,33 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* E2E tests for @navios/di running with SyncLocalStorage (browser mode).
|
|
3
3
|
*
|
|
4
|
-
* This file
|
|
5
|
-
*
|
|
4
|
+
* This file tests the browser implementation by directly importing from
|
|
5
|
+
* the browser-specific async-local-storage module.
|
|
6
6
|
*
|
|
7
7
|
* The key difference: In browser mode, AsyncLocalStorage context does NOT
|
|
8
8
|
* propagate across async boundaries. This affects circular dependency detection
|
|
9
9
|
* for async operations, but synchronous DI operations should work identically.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
13
|
+
|
|
14
|
+
import type { OnServiceDestroy } from '../interfaces/on-service-destroy.interface.mjs'
|
|
15
|
+
import type { OnServiceInit } from '../interfaces/on-service-init.interface.mjs'
|
|
13
16
|
|
|
14
17
|
import { Container } from '../container/container.mjs'
|
|
15
18
|
import { Injectable } from '../decorators/injectable.decorator.mjs'
|
|
16
19
|
import { InjectableScope } from '../enums/index.mjs'
|
|
17
|
-
import
|
|
18
|
-
import type { OnServiceInit } from '../interfaces/on-service-init.interface.mjs'
|
|
20
|
+
import { isUsingNativeAsyncLocalStorage } from '../internal/context/async-local-storage.browser.mjs'
|
|
19
21
|
import { Registry } from '../token/registry.mjs'
|
|
20
22
|
import { getInjectors } from '../utils/get-injectors.mjs'
|
|
21
|
-
import { __testing__, isUsingNativeAsyncLocalStorage } from '../internal/context/async-local-storage.mjs'
|
|
22
|
-
|
|
23
|
-
// Force sync mode for all tests in this file
|
|
24
|
-
beforeAll(() => {
|
|
25
|
-
__testing__.forceSyncMode()
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
afterAll(() => {
|
|
29
|
-
__testing__.reset()
|
|
30
|
-
})
|
|
31
23
|
|
|
32
24
|
// ============================================================================
|
|
33
25
|
// TEST UTILITIES
|