@lppedd/di-wise-neo 0.13.0 → 0.14.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.
@@ -1,5 +1,54 @@
1
+ declare const Scope: {
2
+ /**
3
+ * Creates a new value every time the token is resolved.
4
+ */
5
+ readonly Transient: "Transient";
6
+ /**
7
+ * Creates and caches a single value per token resolution graph.
8
+ *
9
+ * The same value is reused during a single resolution request and is subsequently discarded.
10
+ */
11
+ readonly Resolution: "Resolution";
12
+ /**
13
+ * Creates and caches a single value per container.
14
+ *
15
+ * If the value is not found in the current container, it is looked up in the parent container,
16
+ * and so on. It effectively behaves like a _singleton_ scope but allows container-specific overrides.
17
+ */
18
+ readonly Container: "Container";
19
+ };
20
+ type Scope = (typeof Scope)[keyof typeof Scope];
21
+
22
+ /**
23
+ * Token registration options.
24
+ */
25
+ interface RegistrationOptions {
26
+ /**
27
+ * The scope of the registration.
28
+ */
29
+ readonly scope?: Scope;
30
+ }
1
31
  /**
2
- * Type API.
32
+ * Create a one-off type token from a factory function.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * class Wizard {
37
+ * wand = inject(
38
+ * build(() => {
39
+ * const wand = inject(Wand);
40
+ * wand.owner = this;
41
+ * // ...
42
+ * return wand;
43
+ * }),
44
+ * );
45
+ * }
46
+ * ```
47
+ */
48
+ declare function build<Value>(factory: (...args: []) => Value): Type<Value>;
49
+
50
+ /**
51
+ * An injectable type `T`.
3
52
  */
4
53
  interface Type<T> {
5
54
  /**
@@ -9,11 +58,24 @@ interface Type<T> {
9
58
  /**
10
59
  * Ensures that different `Type<T>` types are not structurally compatible.
11
60
  *
12
- * This property is never used at runtime.
61
+ * This property is always `undefined` and is never used at runtime.
13
62
  *
14
63
  * @private
15
64
  */
16
- readonly __type?: T;
65
+ readonly __type: T | undefined;
66
+ }
67
+ /**
68
+ * An injectable type `T` with a default {@link Provider} and optional default registration options.
69
+ */
70
+ interface ProviderType<T> extends Type<T> {
71
+ /**
72
+ * The type's default provider.
73
+ */
74
+ readonly provider: Provider<T>;
75
+ /**
76
+ * The type's default registration options.
77
+ */
78
+ readonly options?: RegistrationOptions;
17
79
  }
18
80
  /**
19
81
  * Constructor type.
@@ -25,7 +87,7 @@ interface Constructor<Instance extends object> {
25
87
  /**
26
88
  * Token type.
27
89
  */
28
- type Token<Value = any> = [Value] extends [object] ? Type<Value> | Constructor<Value> : Type<Value>;
90
+ type Token<Value = any> = [Value] extends [object] ? Type<Value> | Constructor<Value & object> : Type<Value>;
29
91
  /**
30
92
  * Describes a {@link Token} array with at least one element.
31
93
  */
@@ -36,11 +98,26 @@ type Tokens<Value = any> = [Token<Value>, ...Token<Value>[]];
36
98
  * @example
37
99
  * ```ts
38
100
  * const ISpell = createType<Spell>("Spell");
101
+ * container.register(ISpell, {
102
+ * useFactory: () => getDefaultSpell(),
103
+ * });
39
104
  * ```
40
- *
41
- * @__NO_SIDE_EFFECTS__
42
105
  */
43
106
  declare function createType<T>(typeName: string): Type<T>;
107
+ /**
108
+ * Creates a type token with a default {@link Provider} and optional default registration options.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * // Notice how we pass in a Provider directly at type creation site
113
+ * const ISpell = createType<Spell>("Spell", {
114
+ * useFactory: () => getDefaultSpell(),
115
+ * });
116
+ *
117
+ * container.register(ISpell);
118
+ * ```
119
+ */
120
+ declare function createType<T>(typeName: string, provider: Provider<T>, options?: RegistrationOptions): ProviderType<T>;
44
121
 
45
122
  /**
46
123
  * Provides a class instance for a token via a class constructor.
@@ -166,55 +243,6 @@ interface ExistingProvider<Value> {
166
243
  */
167
244
  type Provider<Value = any> = ClassProvider<Value & object> | FactoryProvider<Value> | ValueProvider<Value> | ExistingProvider<Value>;
168
245
 
169
- declare const Scope: {
170
- /**
171
- * Creates a new value every time the token is resolved.
172
- */
173
- readonly Transient: "Transient";
174
- /**
175
- * Creates and caches a single value per token resolution graph.
176
- *
177
- * The same value is reused during a single resolution request and is subsequently discarded.
178
- */
179
- readonly Resolution: "Resolution";
180
- /**
181
- * Creates and caches a single value per container.
182
- *
183
- * If the value is not found in the current container, it is looked up in the parent container,
184
- * and so on. It effectively behaves like a _singleton_ scope but allows container-specific overrides.
185
- */
186
- readonly Container: "Container";
187
- };
188
- type Scope = (typeof Scope)[keyof typeof Scope];
189
-
190
- /**
191
- * Token registration options.
192
- */
193
- interface RegistrationOptions {
194
- /**
195
- * The scope of the registration.
196
- */
197
- readonly scope?: Scope;
198
- }
199
- /**
200
- * Create a one-off type token from a factory function.
201
- *
202
- * @example
203
- * ```ts
204
- * class Wizard {
205
- * wand = inject(
206
- * build(() => {
207
- * const wand = inject(Wand);
208
- * wand.owner = this;
209
- * // ...
210
- * return wand;
211
- * }),
212
- * );
213
- * }
214
- * ```
215
- */
216
- declare function build<Value>(factory: (...args: []) => Value): Type<Value>;
217
-
218
246
  /**
219
247
  * Container creation options.
220
248
  */
@@ -308,6 +336,10 @@ interface Container {
308
336
  * or by the {@link ContainerOptions.defaultScope} value.
309
337
  */
310
338
  register<Instance extends object>(Class: Constructor<Instance>): Container;
339
+ /**
340
+ * Registers a token type with a default {@link Provider} and optional default registration options.
341
+ */
342
+ register<Value>(token: ProviderType<Value>): Container;
311
343
  /**
312
344
  * Registers a {@link ClassProvider} with a token.
313
345
  *
@@ -1056,4 +1088,4 @@ declare function optionalAll<Instance extends object>(Class: Constructor<Instanc
1056
1088
  declare function optionalAll<Value>(token: Token<Value>): Value[];
1057
1089
 
1058
1090
  export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Named, Optional, OptionalAll, Scope, Scoped, applyMiddleware, build, createContainer, createType, forwardRef, inject, injectAll, injectBy, optional, optionalAll, optionalBy, setClassIdentityMapping };
1059
- export type { ClassProvider, Constructor, Container, ContainerOptions, ExistingProvider, FactoryProvider, Middleware, MiddlewareComposer, Provider, RegistrationOptions, Token, TokenRef, Tokens, TokensRef, Type, ValueProvider };
1091
+ export type { ClassProvider, Constructor, Container, ContainerOptions, ExistingProvider, FactoryProvider, Middleware, MiddlewareComposer, Provider, ProviderType, RegistrationOptions, Token, TokenRef, Tokens, TokensRef, Type, ValueProvider };
package/dist/cjs/index.js CHANGED
@@ -337,22 +337,20 @@ const Scope = {
337
337
  */ Container: "Container"
338
338
  };
339
339
 
340
- /**
341
- * Type API.
342
- */ /**
343
- * Creates a type token.
344
- *
345
- * @example
346
- * ```ts
347
- * const ISpell = createType<Spell>("Spell");
348
- * ```
349
- *
350
- * @__NO_SIDE_EFFECTS__
351
- */ function createType(typeName) {
340
+ // @internal
341
+ // @__NO_SIDE_EFFECTS__
342
+ function createType(typeName, provider, options) {
352
343
  const name = `Type<${typeName}>`;
353
- return {
354
- name: name,
355
- toString: ()=>name
344
+ const toString = ()=>name;
345
+ return provider //
346
+ ? {
347
+ name,
348
+ provider,
349
+ options,
350
+ toString
351
+ } : {
352
+ name,
353
+ toString
356
354
  };
357
355
  }
358
356
  // @internal
@@ -573,70 +571,14 @@ function isDisposable(value) {
573
571
  register(...args) {
574
572
  this.checkDisposed();
575
573
  if (args.length === 1) {
576
- const Class = args[0];
577
- const metadata = getMetadata(Class);
578
- const name = metadata.name;
579
- const registration = {
580
- name: name,
581
- // The provider is of type ClassProvider, initialized by getMetadata
582
- provider: metadata.provider,
583
- options: {
584
- scope: metadata.scope?.value ?? this.myOptions.defaultScope
585
- },
586
- dependencies: metadata.dependencies
587
- };
588
- // Register the class itself
589
- this.myTokenRegistry.set(Class, registration);
590
- // Register the additional tokens specified via class decorators.
591
- // These tokens will point to the original Class token and will have the same scope.
592
- for (const token of metadata.tokensRef.getRefTokens()){
593
- this.myTokenRegistry.set(token, {
594
- name: name,
595
- provider: {
596
- useExisting: [
597
- Class,
598
- name
599
- ]
600
- }
601
- });
602
- }
603
- // Eager-instantiate only if the class is container-scoped
604
- if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
605
- this.resolveProviderValue(Class, registration);
606
- }
607
- } else {
608
- const [token, provider, options] = args;
609
- const name = provider.name;
610
- check(name === undefined || name.trim(), `name qualifier for token ${getTokenName(token)} must not be empty`);
611
- if (isClassProvider(provider)) {
612
- const metadata = getMetadata(provider.useClass);
613
- const registration = {
614
- // An explicit provider name overrides what is specified via @Named
615
- name: metadata.name ?? name,
616
- provider: metadata.provider,
617
- options: {
618
- // Explicit registration options override what is specified via class decorators (e.g., @Scoped)
619
- scope: metadata.scope?.value ?? this.myOptions.defaultScope,
620
- ...options
621
- },
622
- dependencies: metadata.dependencies
623
- };
624
- this.myTokenRegistry.set(token, registration);
625
- // Eager-instantiate only if the provided class is container-scoped
626
- if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
627
- this.resolveProviderValue(token, registration);
628
- }
574
+ const [token] = args;
575
+ if (isConstructor(token)) {
576
+ this.registerClass(token);
629
577
  } else {
630
- if (isExistingProvider(provider)) {
631
- const [targetToken] = this.getTargetToken(provider);
632
- check(token !== targetToken, `token ${getTokenName(token)} cannot alias itself via useExisting`);
633
- }
634
- this.myTokenRegistry.set(token, {
635
- name: name,
636
- provider: provider,
637
- options: options
638
- });
578
+ this.registerToken(token, token.provider, token.options);
639
579
  }
580
+ } else {
581
+ this.registerToken(...args);
640
582
  }
641
583
  return this;
642
584
  }
@@ -693,6 +635,71 @@ function isDisposable(value) {
693
635
  // Allow values to be GCed
694
636
  disposedRefs.clear();
695
637
  }
638
+ registerClass(Class) {
639
+ const metadata = getMetadata(Class);
640
+ const name = metadata.name;
641
+ const registration = {
642
+ name: name,
643
+ // The provider is of type ClassProvider, initialized by getMetadata
644
+ provider: metadata.provider,
645
+ options: {
646
+ scope: metadata.scope?.value ?? this.myOptions.defaultScope
647
+ },
648
+ dependencies: metadata.dependencies
649
+ };
650
+ // Register the class itself
651
+ this.myTokenRegistry.set(Class, registration);
652
+ // Register the additional tokens specified via class decorators.
653
+ // These tokens will point to the original Class token and will have the same scope.
654
+ for (const token of metadata.tokensRef.getRefTokens()){
655
+ this.myTokenRegistry.set(token, {
656
+ name: name,
657
+ provider: {
658
+ useExisting: [
659
+ Class,
660
+ name
661
+ ]
662
+ }
663
+ });
664
+ }
665
+ // Eager-instantiate only if the class is container-scoped
666
+ if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
667
+ this.resolveProviderValue(Class, registration);
668
+ }
669
+ }
670
+ registerToken(token, provider, options) {
671
+ const name = provider.name;
672
+ check(name === undefined || name.trim(), `name qualifier for token ${getTokenName(token)} must not be empty`);
673
+ if (isClassProvider(provider)) {
674
+ const metadata = getMetadata(provider.useClass);
675
+ const registration = {
676
+ // An explicit provider name overrides what is specified via @Named
677
+ name: metadata.name ?? name,
678
+ provider: metadata.provider,
679
+ options: {
680
+ // Explicit registration options override what is specified via class decorators (e.g., @Scoped)
681
+ scope: metadata.scope?.value ?? this.myOptions.defaultScope,
682
+ ...options
683
+ },
684
+ dependencies: metadata.dependencies
685
+ };
686
+ this.myTokenRegistry.set(token, registration);
687
+ // Eager-instantiate only if the provided class is container-scoped
688
+ if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
689
+ this.resolveProviderValue(token, registration);
690
+ }
691
+ } else {
692
+ if (isExistingProvider(provider)) {
693
+ const [targetToken] = this.getTargetToken(provider);
694
+ check(token !== targetToken, `token ${getTokenName(token)} cannot alias itself via useExisting`);
695
+ }
696
+ this.myTokenRegistry.set(token, {
697
+ name: name,
698
+ provider: provider,
699
+ options: options
700
+ });
701
+ }
702
+ }
696
703
  resolveToken(token, name, optional) {
697
704
  let registration = this.myTokenRegistry.get(token, name);
698
705
  if (!registration && isConstructor(token)) {