ditox 3.2.0 → 3.3.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/README.md +76 -27
- package/dist/index.cjs +531 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{umd/index.d.ts → index.d.cts} +191 -196
- package/dist/{browser/index.d.ts → index.d.ts} +191 -196
- package/dist/index.js +514 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +537 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +22 -20
- package/src/container.test.ts +77 -33
- package/src/container.ts +26 -11
- package/src/index.ts +22 -24
- package/src/modules.test.ts +190 -37
- package/src/modules.ts +22 -5
- package/src/removal-compatibility.test.ts +259 -0
- package/src/removal.test.ts +190 -0
- package/src/tokens.test.ts +9 -8
- package/src/tokens.ts +2 -2
- package/src/utils.test.ts +18 -17
- package/src/utils.ts +21 -21
- package/dist/browser/index.js +0 -2
- package/dist/browser/index.js.map +0 -1
- package/dist/cjs/container.d.ts +0 -93
- package/dist/cjs/container.js +0 -216
- package/dist/cjs/container.js.map +0 -1
- package/dist/cjs/index.d.ts +0 -7
- package/dist/cjs/index.js +0 -24
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/modules.d.ts +0 -121
- package/dist/cjs/modules.js +0 -143
- package/dist/cjs/modules.js.map +0 -1
- package/dist/cjs/tokens.d.ts +0 -56
- package/dist/cjs/tokens.js +0 -20
- package/dist/cjs/tokens.js.map +0 -1
- package/dist/cjs/utils.d.ts +0 -137
- package/dist/cjs/utils.js +0 -156
- package/dist/cjs/utils.js.map +0 -1
- package/dist/esm/container.d.ts +0 -93
- package/dist/esm/container.js +0 -211
- package/dist/esm/container.js.map +0 -1
- package/dist/esm/index.d.ts +0 -7
- package/dist/esm/index.js +0 -5
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/modules.d.ts +0 -121
- package/dist/esm/modules.js +0 -136
- package/dist/esm/modules.js.map +0 -1
- package/dist/esm/tokens.d.ts +0 -56
- package/dist/esm/tokens.js +0 -15
- package/dist/esm/tokens.js.map +0 -1
- package/dist/esm/utils.d.ts +0 -137
- package/dist/esm/utils.js +0 -145
- package/dist/esm/utils.js.map +0 -1
- package/dist/umd/index.js +0 -2
- package/dist/umd/index.js.map +0 -1
package/src/container.test.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { describe, expect, it, test, vi } from 'vitest';
|
|
1
2
|
import {
|
|
2
3
|
CONTAINER,
|
|
3
4
|
createContainer,
|
|
5
|
+
FACTORIES_MAP,
|
|
4
6
|
PARENT_CONTAINERS,
|
|
7
|
+
RESOLVER,
|
|
5
8
|
ResolverError,
|
|
6
9
|
} from './container';
|
|
7
|
-
import {optional, token} from './tokens';
|
|
8
|
-
import {injectable} from './utils';
|
|
10
|
+
import { optional, token } from './tokens';
|
|
11
|
+
import { injectable } from './utils';
|
|
9
12
|
|
|
10
13
|
const NUMBER = token<number>('number');
|
|
11
14
|
const STRING = token<string>('string');
|
|
@@ -28,7 +31,7 @@ describe('Container', () => {
|
|
|
28
31
|
it('should bind a result of a factory and prevent invoking it', () => {
|
|
29
32
|
const container = createContainer();
|
|
30
33
|
|
|
31
|
-
const factory =
|
|
34
|
+
const factory = vi.fn(() => 1);
|
|
32
35
|
container.bindFactory(NUMBER, factory);
|
|
33
36
|
container.bindValue(NUMBER, 2);
|
|
34
37
|
|
|
@@ -54,7 +57,7 @@ describe('Container', () => {
|
|
|
54
57
|
const container = createContainer();
|
|
55
58
|
|
|
56
59
|
let containerArg;
|
|
57
|
-
const factory =
|
|
60
|
+
const factory = vi.fn((arg) => {
|
|
58
61
|
containerArg = arg;
|
|
59
62
|
return 1;
|
|
60
63
|
});
|
|
@@ -68,8 +71,8 @@ describe('Container', () => {
|
|
|
68
71
|
it('should rebind a factory in case it was bound', () => {
|
|
69
72
|
const container = createContainer();
|
|
70
73
|
|
|
71
|
-
const factory1 =
|
|
72
|
-
const factory2 =
|
|
74
|
+
const factory1 = vi.fn(() => 1);
|
|
75
|
+
const factory2 = vi.fn(() => 2);
|
|
73
76
|
container.bindFactory(NUMBER, factory1);
|
|
74
77
|
container.bindFactory(NUMBER, factory2);
|
|
75
78
|
|
|
@@ -87,7 +90,7 @@ describe('Container', () => {
|
|
|
87
90
|
container.bindValue(START, 20);
|
|
88
91
|
|
|
89
92
|
let counter = 0;
|
|
90
|
-
const factory =
|
|
93
|
+
const factory = vi.fn((start) => start + ++counter);
|
|
91
94
|
parent.bindFactory(NUMBER, injectable(factory, START), {
|
|
92
95
|
scope: 'singleton',
|
|
93
96
|
});
|
|
@@ -113,7 +116,7 @@ describe('Container', () => {
|
|
|
113
116
|
container.bindValue(START, 20);
|
|
114
117
|
|
|
115
118
|
let counter = 0;
|
|
116
|
-
const factory =
|
|
119
|
+
const factory = vi.fn((start) => start + ++counter);
|
|
117
120
|
parent.bindFactory(NUMBER, injectable(factory, START), {
|
|
118
121
|
scope: 'scoped',
|
|
119
122
|
});
|
|
@@ -129,7 +132,7 @@ describe('Container', () => {
|
|
|
129
132
|
it('should inherit a factory with "scoped" scope', async () => {
|
|
130
133
|
const parentParent = createContainer();
|
|
131
134
|
let counter = 0;
|
|
132
|
-
const factory =
|
|
135
|
+
const factory = vi.fn(() => ++counter);
|
|
133
136
|
|
|
134
137
|
parentParent.bindFactory(NUMBER, factory);
|
|
135
138
|
|
|
@@ -163,7 +166,7 @@ describe('Container', () => {
|
|
|
163
166
|
container.bindValue(START, 20);
|
|
164
167
|
|
|
165
168
|
let counter = 0;
|
|
166
|
-
const factory =
|
|
169
|
+
const factory = vi.fn((start) => start + ++counter);
|
|
167
170
|
parent.bindFactory(NUMBER, injectable(factory, START));
|
|
168
171
|
|
|
169
172
|
expect(container.get(NUMBER)).toBe(11);
|
|
@@ -190,7 +193,7 @@ describe('Container', () => {
|
|
|
190
193
|
container3.bindValue(START, 30);
|
|
191
194
|
|
|
192
195
|
let counter = 0;
|
|
193
|
-
const factory =
|
|
196
|
+
const factory = vi.fn((start) => start + ++counter);
|
|
194
197
|
parent.bindFactory(NUMBER, injectable(factory, START), {
|
|
195
198
|
scope: 'scoped',
|
|
196
199
|
});
|
|
@@ -235,7 +238,7 @@ describe('Container', () => {
|
|
|
235
238
|
container.bindValue(START, 20);
|
|
236
239
|
|
|
237
240
|
let counter = 0;
|
|
238
|
-
const factory =
|
|
241
|
+
const factory = vi.fn((start) => start + ++counter);
|
|
239
242
|
parent.bindFactory(NUMBER, injectable(factory, START), {
|
|
240
243
|
scope: 'transient',
|
|
241
244
|
});
|
|
@@ -251,9 +254,9 @@ describe('Container', () => {
|
|
|
251
254
|
it('should bind a factory with "onRemoved" callback', () => {
|
|
252
255
|
const container = createContainer();
|
|
253
256
|
|
|
254
|
-
const factory =
|
|
255
|
-
const callback =
|
|
256
|
-
container.bindFactory(NUMBER, factory, {onRemoved: callback});
|
|
257
|
+
const factory = vi.fn(() => 1);
|
|
258
|
+
const callback = vi.fn();
|
|
259
|
+
container.bindFactory(NUMBER, factory, { onRemoved: callback });
|
|
257
260
|
|
|
258
261
|
expect(container.get(NUMBER)).toBe(1);
|
|
259
262
|
container.remove(NUMBER);
|
|
@@ -284,9 +287,9 @@ describe('Container', () => {
|
|
|
284
287
|
it('should remove "singleton" factory silently in case its value has never been resolved', () => {
|
|
285
288
|
const container = createContainer();
|
|
286
289
|
|
|
287
|
-
const factory =
|
|
288
|
-
const onRemoved =
|
|
289
|
-
container.bindFactory(NUMBER, factory, {scope: 'singleton', onRemoved});
|
|
290
|
+
const factory = vi.fn(() => 1);
|
|
291
|
+
const onRemoved = vi.fn();
|
|
292
|
+
container.bindFactory(NUMBER, factory, { scope: 'singleton', onRemoved });
|
|
290
293
|
container.remove(NUMBER);
|
|
291
294
|
|
|
292
295
|
expect(container.get(NUMBER)).toBeUndefined();
|
|
@@ -297,9 +300,9 @@ describe('Container', () => {
|
|
|
297
300
|
it('should remove "singleton" factory with calling "onRemoved" in case its value has been resolved', () => {
|
|
298
301
|
const container = createContainer();
|
|
299
302
|
|
|
300
|
-
const factory =
|
|
301
|
-
const onRemoved =
|
|
302
|
-
container.bindFactory(NUMBER, factory, {scope: 'singleton', onRemoved});
|
|
303
|
+
const factory = vi.fn(() => 100);
|
|
304
|
+
const onRemoved = vi.fn();
|
|
305
|
+
container.bindFactory(NUMBER, factory, { scope: 'singleton', onRemoved });
|
|
303
306
|
|
|
304
307
|
expect(container.get(NUMBER)).toBe(100);
|
|
305
308
|
container.remove(NUMBER);
|
|
@@ -315,9 +318,9 @@ describe('Container', () => {
|
|
|
315
318
|
const container = createContainer(parent);
|
|
316
319
|
|
|
317
320
|
let count = 1;
|
|
318
|
-
const factory =
|
|
319
|
-
const onRemoved =
|
|
320
|
-
parent.bindFactory(NUMBER, factory, {scope: 'scoped', onRemoved});
|
|
321
|
+
const factory = vi.fn(() => count++);
|
|
322
|
+
const onRemoved = vi.fn();
|
|
323
|
+
parent.bindFactory(NUMBER, factory, { scope: 'scoped', onRemoved });
|
|
321
324
|
|
|
322
325
|
expect(parent.get(NUMBER)).toBe(1);
|
|
323
326
|
expect(container.get(NUMBER)).toBe(1);
|
|
@@ -332,8 +335,8 @@ describe('Container', () => {
|
|
|
332
335
|
it('should remove "transient" factory in case its value has never been resolved', () => {
|
|
333
336
|
const container = createContainer();
|
|
334
337
|
|
|
335
|
-
const factory =
|
|
336
|
-
container.bindFactory(NUMBER, factory, {scope: 'transient'});
|
|
338
|
+
const factory = vi.fn(() => 1);
|
|
339
|
+
container.bindFactory(NUMBER, factory, { scope: 'transient' });
|
|
337
340
|
container.remove(NUMBER);
|
|
338
341
|
|
|
339
342
|
expect(container.get(NUMBER)).toBeUndefined();
|
|
@@ -343,8 +346,8 @@ describe('Container', () => {
|
|
|
343
346
|
it('should remove "transient" factory in case its value has been resolved', () => {
|
|
344
347
|
const container = createContainer();
|
|
345
348
|
|
|
346
|
-
const factory =
|
|
347
|
-
container.bindFactory(NUMBER, factory, {scope: 'transient'});
|
|
349
|
+
const factory = vi.fn(() => 1);
|
|
350
|
+
container.bindFactory(NUMBER, factory, { scope: 'transient' });
|
|
348
351
|
expect(container.get(NUMBER)).toBe(1);
|
|
349
352
|
|
|
350
353
|
container.remove(NUMBER);
|
|
@@ -365,6 +368,32 @@ describe('Container', () => {
|
|
|
365
368
|
});
|
|
366
369
|
|
|
367
370
|
describe('removeAll()', () => {
|
|
371
|
+
it('should restore internal tokens after a callback throws and allow reuse', () => {
|
|
372
|
+
const parent = createContainer();
|
|
373
|
+
const container = createContainer(parent);
|
|
374
|
+
const child = createContainer(container);
|
|
375
|
+
parent.bindValue(STRING, 'parent');
|
|
376
|
+
container.bindFactory(NUMBER, () => 1, {
|
|
377
|
+
onRemoved() {
|
|
378
|
+
throw new Error('cleanup failed');
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
container.resolve(NUMBER);
|
|
382
|
+
|
|
383
|
+
expect(() => container.removeAll()).toThrow('cleanup failed');
|
|
384
|
+
expect(container.resolve(CONTAINER)).toBe(container);
|
|
385
|
+
expect(container.resolve(PARENT_CONTAINERS)).toEqual([parent]);
|
|
386
|
+
expect(container.resolve(RESOLVER)).toBeTypeOf('function');
|
|
387
|
+
expect(container.hasToken(FACTORIES_MAP)).toBe(true);
|
|
388
|
+
expect(child.resolve(STRING)).toBe('parent');
|
|
389
|
+
const onRemoved = vi.fn();
|
|
390
|
+
container.bindFactory(NUMBER, () => 2, { onRemoved });
|
|
391
|
+
expect(child.resolve(NUMBER)).toBe(2);
|
|
392
|
+
container.removeAll();
|
|
393
|
+
expect(onRemoved).toHaveBeenCalledExactlyOnceWith(2);
|
|
394
|
+
expect(child.get(NUMBER)).toBeUndefined();
|
|
395
|
+
});
|
|
396
|
+
|
|
368
397
|
it('should remove all values and factories', () => {
|
|
369
398
|
const container = createContainer();
|
|
370
399
|
|
|
@@ -382,8 +411,8 @@ describe('Container', () => {
|
|
|
382
411
|
const F1 = token('f1');
|
|
383
412
|
const F2 = token('f2');
|
|
384
413
|
|
|
385
|
-
const unbind1 =
|
|
386
|
-
const unbind2 =
|
|
414
|
+
const unbind1 = vi.fn();
|
|
415
|
+
const unbind2 = vi.fn();
|
|
387
416
|
|
|
388
417
|
const container = createContainer();
|
|
389
418
|
container.bindFactory(F1, () => 10, {
|
|
@@ -419,7 +448,7 @@ describe('Container', () => {
|
|
|
419
448
|
|
|
420
449
|
describe('hasToken()', () => {
|
|
421
450
|
it('should check if a container hierarchy has the token', () => {
|
|
422
|
-
const factory =
|
|
451
|
+
const factory = vi.fn();
|
|
423
452
|
|
|
424
453
|
const token1 = token();
|
|
425
454
|
const token2 = token();
|
|
@@ -621,8 +650,8 @@ describe('Container', () => {
|
|
|
621
650
|
|
|
622
651
|
it('should resolve a value by shared tokens', () => {
|
|
623
652
|
const key = 'token-' + Date.now();
|
|
624
|
-
const t1 = token({key});
|
|
625
|
-
const t2 = token({key});
|
|
653
|
+
const t1 = token({ key });
|
|
654
|
+
const t2 = token({ key });
|
|
626
655
|
expect(t1).not.toBe(t2);
|
|
627
656
|
|
|
628
657
|
const container = createContainer();
|
|
@@ -687,5 +716,20 @@ describe('Container', () => {
|
|
|
687
716
|
|
|
688
717
|
expect(() => child.resolve(NUMBER)).toThrow(ResolverError);
|
|
689
718
|
});
|
|
719
|
+
|
|
720
|
+
it('should NOT resolve a token from a parent that does NOT provide a RESOLVER token', () => {
|
|
721
|
+
const T1 = token<number>('T1');
|
|
722
|
+
|
|
723
|
+
const customParent = {
|
|
724
|
+
hasToken: () => true,
|
|
725
|
+
get: (t: any) => (t === RESOLVER ? undefined : undefined),
|
|
726
|
+
resolve: () => {
|
|
727
|
+
throw new Error('Not implemented');
|
|
728
|
+
},
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
const container = createContainer(customParent);
|
|
732
|
+
expect(container.get(T1)).toBeUndefined();
|
|
733
|
+
});
|
|
690
734
|
});
|
|
691
735
|
});
|
package/src/container.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Token, token} from './tokens';
|
|
1
|
+
import { Token, token } from './tokens';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* ResolverError is thrown by the resolver when a token is not found in a container.
|
|
@@ -75,6 +75,8 @@ export type Container = {
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Removes all bindings in the container.
|
|
78
|
+
* Continues after cleanup failures and rethrows the first thrown value unchanged
|
|
79
|
+
* after clearing bindings and restoring internal tokens. Later failures are discarded.
|
|
78
80
|
*/
|
|
79
81
|
removeAll(): void;
|
|
80
82
|
};
|
|
@@ -178,7 +180,7 @@ export function createContainer(
|
|
|
178
180
|
return;
|
|
179
181
|
}
|
|
180
182
|
|
|
181
|
-
factories.set(token.symbol, {factory, options});
|
|
183
|
+
factories.set(token.symbol, { factory, options });
|
|
182
184
|
},
|
|
183
185
|
|
|
184
186
|
remove<T>(token: Token<T>): void {
|
|
@@ -196,15 +198,28 @@ export function createContainer(
|
|
|
196
198
|
},
|
|
197
199
|
|
|
198
200
|
removeAll(): void {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
201
|
+
// A separate flag preserves thrown values such as undefined or false.
|
|
202
|
+
let hasError = false;
|
|
203
|
+
let firstError: unknown;
|
|
204
|
+
try {
|
|
205
|
+
factories.forEach((context, tokenSymbol) => {
|
|
206
|
+
try {
|
|
207
|
+
if (context.options) {
|
|
208
|
+
executeOnRemoved(tokenSymbol, context.options);
|
|
209
|
+
}
|
|
210
|
+
} catch (error) {
|
|
211
|
+
if (!hasError) {
|
|
212
|
+
hasError = true;
|
|
213
|
+
firstError = error;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
} finally {
|
|
218
|
+
values.clear();
|
|
219
|
+
factories.clear();
|
|
220
|
+
bindInternalTokens();
|
|
221
|
+
}
|
|
222
|
+
if (hasError) throw firstError;
|
|
208
223
|
},
|
|
209
224
|
|
|
210
225
|
hasToken(token: Token<unknown>): boolean {
|
package/src/index.ts
CHANGED
|
@@ -1,34 +1,32 @@
|
|
|
1
|
-
export {token, optional} from './tokens';
|
|
2
|
-
export type {RequiredToken, OptionalToken, Token} from './tokens';
|
|
3
|
-
|
|
4
|
-
export {ResolverError, createContainer} from './container';
|
|
5
1
|
export type {
|
|
6
|
-
FactoryScope,
|
|
7
|
-
FactoryOptions,
|
|
8
2
|
Container,
|
|
9
3
|
ContainerResolver,
|
|
4
|
+
FactoryOptions,
|
|
5
|
+
FactoryScope,
|
|
10
6
|
} from './container';
|
|
11
|
-
|
|
12
|
-
export {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
injectable,
|
|
20
|
-
injectableClass,
|
|
21
|
-
} from './utils';
|
|
22
|
-
|
|
7
|
+
export { createContainer, ResolverError } from './container';
|
|
8
|
+
export type {
|
|
9
|
+
AnyModuleDeclaration,
|
|
10
|
+
BindModuleOptions,
|
|
11
|
+
Module,
|
|
12
|
+
ModuleBindingEntry,
|
|
13
|
+
ModuleDeclaration,
|
|
14
|
+
} from './modules';
|
|
23
15
|
export {
|
|
24
16
|
bindModule,
|
|
25
17
|
bindModules,
|
|
26
18
|
declareModule,
|
|
27
19
|
declareModuleBindings,
|
|
28
20
|
} from './modules';
|
|
29
|
-
export type {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
export type { OptionalToken, RequiredToken, Token } from './tokens';
|
|
22
|
+
export { optional, token } from './tokens';
|
|
23
|
+
export {
|
|
24
|
+
bindMultiValue,
|
|
25
|
+
injectable,
|
|
26
|
+
injectableClass,
|
|
27
|
+
isToken,
|
|
28
|
+
resolveValue,
|
|
29
|
+
resolveValues,
|
|
30
|
+
tryResolveValue,
|
|
31
|
+
tryResolveValues,
|
|
32
|
+
} from './utils';
|