@lppedd/di-wise-neo 0.3.2 → 0.4.0

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 CHANGED
@@ -1,16 +1,15 @@
1
1
  <!--suppress HtmlDeprecatedAttribute -->
2
+ <h1 align="center">di-wise-neo</h1>
3
+ <p align="center">Lightweight, type-safe, flexible dependency injection library for TypeScript and JavaScript</p>
2
4
  <div align="center">
3
- <h1>di-wise-neo</h1>
4
- <p>Lightweight, type-safe, flexible dependency injection library for TypeScript and JavaScript</p>
5
5
 
6
6
  [![test](https://img.shields.io/github/actions/workflow/status/lppedd/di-wise-neo/test.yml.svg?branch=main)](https://github.com/lppedd/di-wise-neo/actions/workflows/test.yml)
7
7
  [![npm](https://img.shields.io/npm/v/@lppedd/di-wise-neo?color=%23de1f1f&logo=npm)](https://www.npmjs.com/package/@lppedd/di-wise-neo)
8
8
  [![npm gzipped size](https://img.shields.io/bundlejs/size/@lppedd/di-wise-neo)](https://bundlejs.com/?q=@lppedd/di-wise-neo)
9
9
  [![license](https://img.shields.io/github/license/lppedd/di-wise-neo?color=blue)](https://github.com/lppedd/di-wise-neo/blob/main/LICENSE)
10
10
 
11
- <img src="./.github/images/neo-wall.jpg" alt="di-wise-neo" style="border: 3px solid black; border-radius: 15px;" />
12
- <div><sub>yes, I like The Matrix</sub></div>
13
11
  </div>
12
+ <img align="center" src="./.github/images/neo-wall.jpg" alt="di-wise-neo" style="border: 3px solid black; border-radius: 15px;" />
14
13
 
15
14
  > [!NOTE]
16
15
  >
@@ -23,7 +22,7 @@
23
22
  - [Why yet another library](#why-yet-another-library)
24
23
  - [Installation](#installation)
25
24
  - [API reference](#api-reference)
26
- - [Ergonomics](#ergonomics)
25
+ - [Ergonomics & Requirements](#ergonomics)
27
26
  - [Quickstart](#quickstart)
28
27
  - [Container scopes](#container-scopes)
29
28
  - [Token registration](#token-registration)
@@ -93,9 +92,10 @@ pnpm add @lppedd/di-wise-neo
93
92
  ```sh
94
93
  yarn add @lppedd/di-wise-neo
95
94
  ```
95
+
96
96
  ## API reference
97
97
 
98
- You can find the complete API reference at https://lppedd.github.io/di-wise-neo
98
+ You can find the complete API reference at [lppedd.github.io/di-wise-neo](https://lppedd.github.io/di-wise-neo)
99
99
 
100
100
  ## Ergonomics
101
101
 
@@ -103,6 +103,11 @@ You can find the complete API reference at https://lppedd.github.io/di-wise-neo
103
103
  - Does **not** use [reflect-metadata](https://www.npmjs.com/package/reflect-metadata) to drive decorators
104
104
  - **Can** be used from JavaScript with function-based injection
105
105
 
106
+ ### Requirements
107
+
108
+ - When using decorator-based injection, `experimentalDecorators` must be enabled in your `tsconfig.json` file
109
+ - The JavaScript environment must support features such as `Array.flat`, `WeakSet`, `WeakMap`, `Set`, and `Map`
110
+
106
111
  ## Quickstart
107
112
 
108
113
  ```ts
@@ -220,14 +225,14 @@ Alternatively, use an explicit `ClassProvider` object - useful when registering
220
225
  an interface or abstract type:
221
226
 
222
227
  ```ts
223
- const Store = createType<Store>("Store");
224
- container.register(Store, {
228
+ const IStore = createType<Store>("Store");
229
+ container.register(IStore, {
225
230
  useClass: SecretStore, // class SecretStore implements Store
226
231
  });
227
232
  ```
228
233
 
229
- Upon resolving `Store`, the container creates an instance of `SecretStore`,
230
- caching it according to the configured scope.
234
+ Upon resolving `IStore` - which represents the `Store` interface - the container
235
+ creates an instance of `SecretStore`, caching it according to the configured scope.
231
236
 
232
237
  ### FactoryProvider
233
238
 
@@ -245,7 +250,7 @@ according to the configured scope.
245
250
 
246
251
  ### ValueProvider
247
252
 
248
- A static value - always taken as-is and unaffected by scopes - can be registered using:
253
+ A fixed value - always taken as-is and unaffected by scopes - can be registered using:
249
254
 
250
255
  ```ts
251
256
  const PID = createType<number>("PID");
@@ -310,7 +315,7 @@ never been registered in the container.
310
315
 
311
316
  ```ts
312
317
  export class ExtensionContext {
313
- readonly stores /*: Store[] */ = injectAll(Store);
318
+ readonly stores /*: Store[] */ = injectAll(IStore);
314
319
 
315
320
  /* ... */
316
321
 
@@ -341,7 +346,7 @@ has never been registered in the container.
341
346
  ```ts
342
347
  export class ExtensionContext {
343
348
  // The type does not change compared to injectAll(T), but the call does not fail
344
- readonly stores /*: Store[] */ = optionalAll(Store);
349
+ readonly stores /*: Store[] */ = optionalAll(IStore);
345
350
 
346
351
  /* ... */
347
352
  }
@@ -366,7 +371,7 @@ export class ProcessManager {
366
371
  /* ... */
367
372
 
368
373
  // The method is called immediately after instance construction
369
- notifyListener(@Inject(ProcessListener) listeners: ProcessListener): void {
374
+ notifyListener(@Inject(ProcessListener) listener: ProcessListener): void {
370
375
  listener.processStarted(this.rootPID);
371
376
  }
372
377
  }
@@ -381,7 +386,7 @@ never been registered in the container.
381
386
 
382
387
  ```ts
383
388
  export class ExtensionContext {
384
- constructor(@InjectAll(Store) readonly stores: Store[]) {}
389
+ constructor(@InjectAll(IStore) readonly stores: Store[]) {}
385
390
 
386
391
  /* ... */
387
392
 
@@ -412,7 +417,7 @@ has never been registered in the container.
412
417
  ```ts
413
418
  export class ExtensionContext {
414
419
  // The type does not change compared to @InjectAll, but construction does not fail
415
- constructor(@OptionalAll(Store) readonly stores: Store[]) {}
420
+ constructor(@OptionalAll(IStore) readonly stores: Store[]) {}
416
421
 
417
422
  /* ... */
418
423
  }
@@ -423,13 +428,13 @@ export class ExtensionContext {
423
428
  Sometimes you may need to reference a token or class that is declared later in the file.
424
429
  Normally, attempting to do that would result in a `ReferenceError`:
425
430
 
426
- > ReferenceError: Cannot access 'Store' before initialization
431
+ > ReferenceError: Cannot access 'IStore' before initialization
427
432
 
428
433
  We can work around this problem by using the `forwardRef` helper function:
429
434
 
430
435
  ```ts
431
436
  export class ExtensionContext {
432
- constructor(@OptionalAll(forwardRef(() => Store)) readonly stores: Store[]) {}
437
+ constructor(@OptionalAll(forwardRef(() => IStore)) readonly stores: Store[]) {}
433
438
 
434
439
  /* ... */
435
440
  }
@@ -3,11 +3,11 @@
3
3
  */
4
4
  interface Type<A> {
5
5
  /**
6
- * Name of the type.
6
+ * The name of the type.
7
7
  */
8
8
  readonly name: string;
9
9
  /**
10
- * Create an intersection type from another type.
10
+ * Creates an intersection type from another type.
11
11
  *
12
12
  * @example
13
13
  * ```ts
@@ -19,7 +19,7 @@ interface Type<A> {
19
19
  */
20
20
  inter<B>(typeName: string, B: Type<B>): Type<A & B>;
21
21
  /**
22
- * Create a union type from another type.
22
+ * Creates a union type from another type.
23
23
  *
24
24
  * @example
25
25
  * ```ts
@@ -37,7 +37,6 @@ interface Type<A> {
37
37
  interface Constructor<Instance extends object> {
38
38
  new (...args: any[]): Instance;
39
39
  readonly name: string;
40
- readonly length: number;
41
40
  }
42
41
  /**
43
42
  * Token type.
@@ -48,11 +47,11 @@ type Token<Value = any> = [Value] extends [object] ? Type<Value> | Constructor<V
48
47
  */
49
48
  type Tokens<Value = any> = [Token<Value>, ...Token<Value>[]];
50
49
  /**
51
- * Create a type token.
50
+ * Creates a type token.
52
51
  *
53
52
  * @example
54
53
  * ```ts
55
- * const Spell = createType<Spell>("Spell");
54
+ * const ISpell = createType<Spell>("Spell");
56
55
  * ```
57
56
  *
58
57
  * @__NO_SIDE_EFFECTS__
@@ -65,31 +64,33 @@ declare function createType<T>(typeName: string): Type<T>;
65
64
  interface ClassProvider<Instance extends object> {
66
65
  readonly useClass: Constructor<Instance>;
67
66
  }
68
- /**
69
- * Provides a value for a token via another existing token.
70
- */
71
- interface ExistingProvider<Value> {
72
- readonly useExisting: Token<Value>;
73
- }
74
67
  /**
75
68
  * Provides a value for a token via a factory function.
76
69
  *
77
- * The factory function runs inside the injection context
78
- * and can thus access dependencies via {@link inject}.
70
+ * The factory function runs inside the injection context and can
71
+ * thus access dependencies via {@link inject}-like functions.
79
72
  */
80
73
  interface FactoryProvider<Value> {
81
74
  readonly useFactory: (...args: []) => Value;
82
75
  }
83
76
  /**
84
- * Provides a direct - already constructed - value for a token.
77
+ * Provides a static - already constructed - value for a token.
85
78
  */
86
79
  interface ValueProvider<T> {
87
80
  readonly useValue: T;
88
81
  }
82
+ /**
83
+ * Aliases another registered token.
84
+ *
85
+ * Resolving this token will return the value of the aliased one.
86
+ */
87
+ interface ExistingProvider<Value> {
88
+ readonly useExisting: Token<Value>;
89
+ }
89
90
  /**
90
91
  * A token provider.
91
92
  */
92
- type Provider<Value = any> = ClassProvider<Value & object> | ExistingProvider<Value> | FactoryProvider<Value> | ValueProvider<Value>;
93
+ type Provider<Value = any> = ClassProvider<Value & object> | FactoryProvider<Value> | ValueProvider<Value> | ExistingProvider<Value>;
93
94
 
94
95
  declare const Scope: {
95
96
  readonly Inherited: "Inherited";
@@ -212,35 +213,87 @@ interface Container {
212
213
  * Returns whether the token is registered in this container or in parent containers, if any.
213
214
  */
214
215
  isRegistered(token: Token): boolean;
216
+ /**
217
+ * Registers a concrete class, where the class acts as its own token.
218
+ *
219
+ * Tokens provided via the {@link Injectable} decorator applied to the class
220
+ * are also registered as aliases.
221
+ *
222
+ * The default registration scope is determined by the {@link Scoped} decorator,
223
+ * if present.
224
+ */
225
+ registerClass<Instance extends object>(Class: Constructor<Instance>): void;
226
+ /**
227
+ * Registers a concrete class with a token.
228
+ *
229
+ * The default registration scope is determined by the {@link Scoped} decorator
230
+ * applied to the class, if present, but it can be overridden by passing explicit
231
+ * registration options.
232
+ */
233
+ registerClass<Instance extends object, ProvidedInstance extends Instance>(token: Token<Instance>, Class: Constructor<ProvidedInstance>, options?: RegistrationOptions): void;
234
+ /**
235
+ * Registers a token whose value is produced by a factory function.
236
+ *
237
+ * The factory function runs inside the injection context and can
238
+ * thus access dependencies via {@link inject}-like functions.
239
+ */
240
+ registerFactory<Value, ProvidedValue extends Value>(token: Token<Value>, factory: (...args: []) => ProvidedValue, options?: RegistrationOptions): void;
241
+ /**
242
+ * Registers a token with a fixed value.
243
+ *
244
+ * The provided value is returned as-is when the token is resolved (scopes do not apply).
245
+ */
246
+ registerValue<Value, ProvidedValue extends Value>(token: Token<Value>, value: ProvidedValue): void;
247
+ /**
248
+ * Registers one or more tokens as aliases for a target token.
249
+ *
250
+ * When an alias is resolved, the target token is resolved instead.
251
+ */
252
+ registerAlias<Value, ProvidedValue extends Value>(targetToken: Token<ProvidedValue>, aliasTokens: Tokens<Value>): void;
215
253
  /**
216
254
  * Registers a {@link ClassProvider}, using the class itself as its token.
217
255
  *
218
- * Tokens provided to the {@link Injectable} decorator applied to the class
219
- * are also registered as aliases. The scope is determined by the {@link Scoped}
220
- * decorator, if present.
256
+ * Tokens provided via the {@link Injectable} decorator applied to the class
257
+ * are also registered as aliases.
258
+ *
259
+ * The scope is determined by the {@link Scoped} decorator, if present.
260
+ *
261
+ * @see registerClass
221
262
  */
222
- register<Instance extends object>(Class: Constructor<Instance>): this;
263
+ register<Instance extends object>(Class: Constructor<Instance>): Container;
223
264
  /**
224
265
  * Registers a {@link ClassProvider} with a token.
266
+ *
267
+ * The default registration scope is determined by the {@link Scoped} decorator
268
+ * applied to the provided class, if present, but it can be overridden by
269
+ * passing explicit registration options.
270
+ *
271
+ * @see registerClass
225
272
  */
226
- register<Instance extends object, ProviderInstance extends Instance>(token: Token<Instance>, provider: ClassProvider<ProviderInstance>, options?: RegistrationOptions): this;
273
+ register<Instance extends object, ProviderInstance extends Instance>(token: Token<Instance>, provider: ClassProvider<ProviderInstance>, options?: RegistrationOptions): Container;
227
274
  /**
228
275
  * Registers a {@link FactoryProvider} with a token.
276
+ *
277
+ * @see registerFactory
229
278
  */
230
- register<Value, ProviderValue extends Value>(token: Token<Value>, provider: FactoryProvider<ProviderValue>, options?: RegistrationOptions): this;
279
+ register<Value, ProviderValue extends Value>(token: Token<Value>, provider: FactoryProvider<ProviderValue>, options?: RegistrationOptions): Container;
231
280
  /**
232
281
  * Registers an {@link ExistingProvider} with a token.
233
282
  *
234
283
  * The token will alias the one set in `useExisting`.
284
+ *
285
+ * @see registerAlias
235
286
  */
236
- register<Value, ProviderValue extends Value>(token: Token<Value>, provider: ExistingProvider<ProviderValue>): this;
287
+ register<Value, ProviderValue extends Value>(token: Token<Value>, provider: ExistingProvider<ProviderValue>): Container;
237
288
  /**
238
289
  * Registers a {@link ValueProvider} with a token.
239
290
  *
240
291
  * Values provided via `useValue` are never cached (scopes do not apply)
241
292
  * and are simply returned as-is.
293
+ *
294
+ * @see registerValue
242
295
  */
243
- register<Value, ProviderValue extends Value>(token: Token<Value>, provider: ValueProvider<ProviderValue>): this;
296
+ register<Value, ProviderValue extends Value>(token: Token<Value>, provider: ValueProvider<ProviderValue>): Container;
244
297
  /**
245
298
  * Removes all registrations for the given token from the container's internal registry.
246
299
  *
@@ -364,8 +417,8 @@ interface Container {
364
417
  declare function createContainer(options?: Partial<ContainerOptions>): Container;
365
418
 
366
419
  /**
367
- * Class decorator for enabling auto-registration of an unregistered class
368
- * when first resolving it from the container.
420
+ * Class decorator that enables auto-registration of an unregistered class,
421
+ * when the class is first resolved from the container.
369
422
  *
370
423
  * @example
371
424
  * ```ts
@@ -387,9 +440,14 @@ interface TokenRef<Value = any> {
387
440
  readonly getRefToken: () => Token<Value>;
388
441
  }
389
442
  /**
390
- * Allows referencing a token that is declared later in the file by wrapping it in a function.
443
+ * Allows referencing tokens that are declared later in the file by wrapping them
444
+ * in a lazily evaluated function.
391
445
  */
392
446
  declare function forwardRef<Value>(token: () => Tokens<Value>): TokensRef<Value>;
447
+ /**
448
+ * Allows referencing a token that is declared later in the file by wrapping it
449
+ * in a lazily evaluated function.
450
+ */
393
451
  declare function forwardRef<Value>(token: () => Token<Value>): TokenRef<Value>;
394
452
 
395
453
  /**
@@ -424,8 +482,8 @@ declare function Inject<Value>(token: Token<Value>): ParameterDecorator;
424
482
  declare function Inject<Value>(tokens: TokenRef<Value>): ParameterDecorator;
425
483
 
426
484
  /**
427
- * Class decorator for registering additional aliasing tokens for the decorated type
428
- * when registering it.
485
+ * Class decorator that registers additional aliasing tokens for the decorated type,
486
+ * when the type is first registered in the container.
429
487
  *
430
488
  * The container uses {@link ExistingProvider} under the hood.
431
489
  *
@@ -437,8 +495,8 @@ declare function Inject<Value>(tokens: TokenRef<Value>): ParameterDecorator;
437
495
  */
438
496
  declare function Injectable<This extends object, Value extends This>(...tokens: Tokens<Value>): ClassDecorator;
439
497
  /**
440
- * Class decorator for registering additional aliasing tokens for the decorated type
441
- * when registering it.
498
+ * Class decorator that registers additional aliasing tokens for the decorated type,
499
+ * when the type is first registered in the container.
442
500
  *
443
501
  * The container uses {@link ExistingProvider} under the hood.
444
502
  *
@@ -718,7 +776,7 @@ declare const Injector: Type<Injector>;
718
776
  */
719
777
  interface MiddlewareComposer {
720
778
  /**
721
- * Add a middleware function to the composer.
779
+ * Adds a middleware function to the composer.
722
780
  */
723
781
  use<MethodKey extends keyof Container>(key: MethodKey, wrap: Container[MethodKey] extends Function ? (next: Container[MethodKey]) => Container[MethodKey] : never): MiddlewareComposer;
724
782
  }
@@ -744,7 +802,7 @@ interface Middleware {
744
802
  (composer: MiddlewareComposer, api: Readonly<Container>): void;
745
803
  }
746
804
  /**
747
- * Apply middleware functions to a container.
805
+ * Applies middleware functions to a container.
748
806
  *
749
807
  * Middlewares are applied in array order, but execute in reverse order.
750
808
  *
package/dist/cjs/index.js CHANGED
@@ -209,10 +209,6 @@ function isClassProvider(provider) {
209
209
  return "useClass" in provider;
210
210
  }
211
211
  // @internal
212
- function isExistingProvider(provider) {
213
- return "useExisting" in provider;
214
- }
215
- // @internal
216
212
  function isFactoryProvider(provider) {
217
213
  return "useFactory" in provider;
218
214
  }
@@ -220,6 +216,10 @@ function isFactoryProvider(provider) {
220
216
  function isValueProvider(provider) {
221
217
  return "useValue" in provider;
222
218
  }
219
+ // @internal
220
+ function isExistingProvider(provider) {
221
+ return "useExisting" in provider;
222
+ }
223
223
 
224
224
  const Scope = {
225
225
  Inherited: "Inherited",
@@ -231,11 +231,11 @@ const Scope = {
231
231
  /**
232
232
  * Type API.
233
233
  */ /**
234
- * Create a type token.
234
+ * Creates a type token.
235
235
  *
236
236
  * @example
237
237
  * ```ts
238
- * const Spell = createType<Spell>("Spell");
238
+ * const ISpell = createType<Spell>("Spell");
239
239
  * ```
240
240
  *
241
241
  * @__NO_SIDE_EFFECTS__
@@ -281,8 +281,8 @@ function getTypeName(value) {
281
281
  // @internal
282
282
  class TokenRegistry {
283
283
  constructor(parent){
284
- this.parent = parent;
285
284
  this.myMap = new Map();
285
+ this.myParent = parent;
286
286
  }
287
287
  get(token) {
288
288
  // To clarify, at(-1) means we take the last added registration for this token
@@ -335,7 +335,7 @@ class TokenRegistry {
335
335
  }
336
336
  getAllFromParent(token) {
337
337
  const registrations = this.myMap.get(token);
338
- return registrations || this.parent?.getAllFromParent(token);
338
+ return registrations || this.myParent?.getAllFromParent(token);
339
339
  }
340
340
  }
341
341
  // @internal
@@ -386,12 +386,11 @@ function isDisposable(value) {
386
386
 
387
387
  /**
388
388
  * The default implementation of a di-wise-neo {@link Container}.
389
- */ class DefaultContainer {
390
- constructor(myParent, options){
391
- this.myParent = myParent;
392
- // eslint-disable-next-line no-use-before-define
389
+ */ class ContainerImpl {
390
+ constructor(parent, options){
393
391
  this.myChildren = new Set();
394
392
  this.myDisposed = false;
393
+ this.myParent = parent;
395
394
  this.myOptions = {
396
395
  autoRegister: false,
397
396
  defaultScope: Scope.Inherited,
@@ -415,7 +414,7 @@ function isDisposable(value) {
415
414
  }
416
415
  createChild(options) {
417
416
  this.checkDisposed();
418
- const container = new DefaultContainer(this, {
417
+ const container = new ContainerImpl(this, {
419
418
  ...this.myOptions,
420
419
  ...options
421
420
  });
@@ -462,6 +461,36 @@ function isDisposable(value) {
462
461
  this.checkDisposed();
463
462
  return this.myTokenRegistry.get(token) !== undefined;
464
463
  }
464
+ registerClass(token, Class, options) {
465
+ // This mess will go away once/if we remove the register method
466
+ // in favor of the multiple specialized ones
467
+ if (Class) {
468
+ const ctor = Class ?? token;
469
+ this.register(token, {
470
+ useClass: ctor
471
+ }, options);
472
+ } else {
473
+ this.register(token);
474
+ }
475
+ }
476
+ registerFactory(token, factory, options) {
477
+ this.register(token, {
478
+ useFactory: factory
479
+ }, options);
480
+ }
481
+ registerValue(token, value) {
482
+ this.register(token, {
483
+ useValue: value
484
+ });
485
+ }
486
+ registerAlias(targetToken, aliasTokens) {
487
+ // De-duplicate tokens
488
+ for (const alias of new Set(aliasTokens)){
489
+ this.register(alias, {
490
+ useExisting: targetToken
491
+ });
492
+ }
493
+ }
465
494
  register(...args) {
466
495
  this.checkDisposed();
467
496
  if (args.length == 1) {
@@ -787,12 +816,12 @@ function isDisposable(value) {
787
816
  autoRegister: false,
788
817
  defaultScope: Scope.Inherited
789
818
  }) {
790
- return new DefaultContainer(undefined, options);
819
+ return new ContainerImpl(undefined, options);
791
820
  }
792
821
 
793
822
  /**
794
- * Class decorator for enabling auto-registration of an unregistered class
795
- * when first resolving it from the container.
823
+ * Class decorator that enables auto-registration of an unregistered class,
824
+ * when the class is first resolved from the container.
796
825
  *
797
826
  * @example
798
827
  * ```ts
@@ -992,7 +1021,7 @@ function OptionalAll(token) {
992
1021
  });
993
1022
 
994
1023
  /**
995
- * Apply middleware functions to a container.
1024
+ * Applies middleware functions to a container.
996
1025
  *
997
1026
  * Middlewares are applied in array order, but execute in reverse order.
998
1027
  *