@lppedd/di-wise-neo 0.7.2 → 0.8.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.
@@ -187,8 +187,6 @@ interface RegistrationOptions {
187
187
  * );
188
188
  * }
189
189
  * ```
190
- *
191
- * @__NO_SIDE_EFFECTS__
192
190
  */
193
191
  declare function build<Value>(factory: (...args: []) => Value): Type<Value>;
194
192
 
@@ -811,6 +809,17 @@ interface Injector {
811
809
  * or an empty array if the token is not registered in the container.
812
810
  */
813
811
  optionalAll<Value>(token: Token<Value>): NonNullable<Value>[];
812
+ /**
813
+ * Runs a function inside the injection context of this injector.
814
+ *
815
+ * Note that injection functions (`inject`, `injectAll`, `optional`, `optionalAll`)
816
+ * are only usable synchronously: they cannot be called from asynchronous callbacks
817
+ * or after any `await` points.
818
+ *
819
+ * @param fn The function to be run in the context of this injector.
820
+ * @returns The return value of the function, if any.
821
+ */
822
+ runInContext<ReturnType>(fn: () => ReturnType): ReturnType;
814
823
  }
815
824
  /**
816
825
  * Injector token for dynamic injections.
package/dist/cjs/index.js CHANGED
@@ -1,5 +1,32 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
+ /**
4
+ * Type API.
5
+ */ /**
6
+ * Creates a type token.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const ISpell = createType<Spell>("Spell");
11
+ * ```
12
+ *
13
+ * @__NO_SIDE_EFFECTS__
14
+ */ function createType(typeName) {
15
+ const type = {
16
+ name: `Type<${typeName}>`,
17
+ inter: createType,
18
+ union: createType,
19
+ toString () {
20
+ return type.name;
21
+ }
22
+ };
23
+ return type;
24
+ }
25
+ // @internal
26
+ function isConstructor(token) {
27
+ return typeof token === "function";
28
+ }
29
+
3
30
  // @internal
4
31
  function assert(condition, message) {
5
32
  if (!condition) {
@@ -12,7 +39,8 @@ function expectNever(value) {
12
39
  }
13
40
  // @internal
14
41
  function throwUnregisteredError(token) {
15
- throw new Error(tag(`unregistered token ${token.name}`));
42
+ const type = isConstructor(token) ? "class" : "token";
43
+ throw new Error(tag(`unregistered ${type} ${token.name}`));
16
44
  }
17
45
  // @internal
18
46
  function throwExistingUnregisteredError(sourceToken, targetTokenOrError) {
@@ -31,23 +59,6 @@ function untag(message) {
31
59
  return message.startsWith("[di-wise-neo]") ? message.substring(13).trimStart() : message;
32
60
  }
33
61
 
34
- // @internal
35
- function createInjectionContext() {
36
- let current = null;
37
- function provide(next) {
38
- const prev = current;
39
- current = next;
40
- return ()=>current = prev;
41
- }
42
- function use() {
43
- return current;
44
- }
45
- return [
46
- provide,
47
- use
48
- ];
49
- }
50
-
51
62
  // @internal
52
63
  function invariant(condition) {
53
64
  if (!condition) {
@@ -117,18 +128,33 @@ function createResolution() {
117
128
  // @internal
118
129
  const [provideInjectionContext, useInjectionContext] = createInjectionContext();
119
130
  // @internal
120
- function ensureInjectionContext(fn) {
131
+ function ensureInjectionContext(name) {
121
132
  const context = useInjectionContext();
122
- assert(context, `${fn.name}() can only be invoked within an injection context`);
133
+ assert(context, `${name} can only be invoked within an injection context`);
123
134
  return context;
124
135
  }
136
+ function createInjectionContext() {
137
+ let current = null;
138
+ function provide(next) {
139
+ const prev = current;
140
+ current = next;
141
+ return ()=>current = prev;
142
+ }
143
+ function use() {
144
+ return current;
145
+ }
146
+ return [
147
+ provide,
148
+ use
149
+ ];
150
+ }
125
151
 
126
152
  function inject(token, name) {
127
- const context = ensureInjectionContext(inject);
153
+ const context = ensureInjectionContext("inject()");
128
154
  return context.container.resolve(token, name);
129
155
  }
130
156
  function injectBy(thisArg, token, name) {
131
- const context = ensureInjectionContext(injectBy);
157
+ const context = ensureInjectionContext("injectBy()");
132
158
  const resolution = context.resolution;
133
159
  const currentFrame = resolution.stack.peek();
134
160
  if (!currentFrame) {
@@ -146,7 +172,7 @@ function injectBy(thisArg, token, name) {
146
172
  }
147
173
 
148
174
  function injectAll(token) {
149
- const context = ensureInjectionContext(injectAll);
175
+ const context = ensureInjectionContext("injectAll()");
150
176
  return context.container.resolveAll(token);
151
177
  }
152
178
 
@@ -242,11 +268,11 @@ const classIdentityMap = new WeakMap();
242
268
  const metadataMap = new WeakMap();
243
269
 
244
270
  function optional(token, name) {
245
- const context = ensureInjectionContext(optional);
271
+ const context = ensureInjectionContext("optional()");
246
272
  return context.container.resolve(token, true, name);
247
273
  }
248
274
  function optionalBy(thisArg, token, name) {
249
- const context = ensureInjectionContext(optionalBy);
275
+ const context = ensureInjectionContext("optionalBy()");
250
276
  const resolution = context.resolution;
251
277
  const currentFrame = resolution.stack.peek();
252
278
  if (!currentFrame) {
@@ -264,7 +290,7 @@ function optionalBy(thisArg, token, name) {
264
290
  }
265
291
 
266
292
  function optionalAll(token) {
267
- const context = ensureInjectionContext(optionalAll);
293
+ const context = ensureInjectionContext("optionalAll()");
268
294
  return context.container.resolveAll(token, true);
269
295
  }
270
296
 
@@ -292,33 +318,6 @@ const Scope = {
292
318
  Container: "Container"
293
319
  };
294
320
 
295
- /**
296
- * Type API.
297
- */ /**
298
- * Creates a type token.
299
- *
300
- * @example
301
- * ```ts
302
- * const ISpell = createType<Spell>("Spell");
303
- * ```
304
- *
305
- * @__NO_SIDE_EFFECTS__
306
- */ function createType(typeName) {
307
- const type = {
308
- name: `Type<${typeName}>`,
309
- inter: createType,
310
- union: createType,
311
- toString () {
312
- return type.name;
313
- }
314
- };
315
- return type;
316
- }
317
- // @internal
318
- function isConstructor(token) {
319
- return typeof token === "function";
320
- }
321
-
322
321
  // @internal
323
322
  function getTypeName(value) {
324
323
  switch(typeof value){
@@ -430,26 +429,9 @@ class TokenRegistry {
430
429
  function isBuilder(provider) {
431
430
  return builders.has(provider);
432
431
  }
433
- /**
434
- * Create a one-off type token from a factory function.
435
- *
436
- * @example
437
- * ```ts
438
- * class Wizard {
439
- * wand = inject(
440
- * build(() => {
441
- * const wand = inject(Wand);
442
- * wand.owner = this;
443
- * // ...
444
- * return wand;
445
- * }),
446
- * );
447
- * }
448
- * ```
449
- *
450
- * @__NO_SIDE_EFFECTS__
451
- */ function build(factory) {
452
- const token = createType(`Build<${getTypeName(factory)}>`);
432
+ // @__NO_SIDE_EFFECTS__
433
+ function build(factory, name) {
434
+ const token = createType(name ?? `Build<${getTypeName(factory)}>`);
453
435
  const provider = {
454
436
  useFactory: factory
455
437
  };
@@ -640,7 +622,7 @@ function isDisposable(value) {
640
622
  if (isConstructor(token)) {
641
623
  return this.instantiateClass(token, localOptional);
642
624
  }
643
- return optionalOrName ? undefined : throwUnregisteredError(token);
625
+ return localOptional ? undefined : throwUnregisteredError(token);
644
626
  }
645
627
  resolveAll(token, optional) {
646
628
  this.checkDisposed();
@@ -1007,10 +989,22 @@ function updateParameterMetadata(decorator, target, propertyKey, parameterIndex,
1007
989
  updateFn(dependency);
1008
990
  }
1009
991
  }
992
+ // Checks that a constructor or method parameter has only one injection decorator.
993
+ // For example, if both `@Inject` and `@Optional` are used, it becomes difficult to
994
+ // understand which one "wins", unless the user is aware of the decorator evaluation order.
995
+ //
996
+ // @internal
997
+ function checkSingleDecorator(dependency, target, propertyKey, parameterIndex) {
998
+ assert(!dependency.appliedBy, ()=>{
999
+ const where = propertyKey === undefined ? `${target.name} constructor` : `${target.constructor.name}.${String(propertyKey)}`;
1000
+ return `${where} parameter ${parameterIndex} declares multiple injection decorators, but only one is allowed`;
1001
+ });
1002
+ }
1010
1003
 
1011
1004
  function Inject(token) {
1012
1005
  return function(target, propertyKey, parameterIndex) {
1013
1006
  updateParameterMetadata("Inject", target, propertyKey, parameterIndex, (dependency)=>{
1007
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1014
1008
  dependency.appliedBy = "Inject";
1015
1009
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1016
1010
  });
@@ -1040,6 +1034,7 @@ function Inject(token) {
1040
1034
  function InjectAll(token) {
1041
1035
  return function(target, propertyKey, parameterIndex) {
1042
1036
  updateParameterMetadata("InjectAll", target, propertyKey, parameterIndex, (dependency)=>{
1037
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1043
1038
  dependency.appliedBy = "InjectAll";
1044
1039
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1045
1040
  });
@@ -1087,6 +1082,7 @@ function InjectAll(token) {
1087
1082
  function Optional(token) {
1088
1083
  return function(target, propertyKey, parameterIndex) {
1089
1084
  updateParameterMetadata("Optional", target, propertyKey, parameterIndex, (dependency)=>{
1085
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1090
1086
  dependency.appliedBy = "Optional";
1091
1087
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1092
1088
  });
@@ -1096,6 +1092,7 @@ function Optional(token) {
1096
1092
  function OptionalAll(token) {
1097
1093
  return function(target, propertyKey, parameterIndex) {
1098
1094
  updateParameterMetadata("OptionalAll", target, propertyKey, parameterIndex, (dependency)=>{
1095
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1099
1096
  dependency.appliedBy = "OptionalAll";
1100
1097
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1101
1098
  });
@@ -1156,12 +1153,12 @@ function OptionalAll(token) {
1156
1153
  * const wizard = container.resolve(Wizard);
1157
1154
  * wizard.getWand(); // => Wand
1158
1155
  * ```
1159
- */ const Injector = /*@__PURE__*/ build(function Injector() {
1160
- const context = ensureInjectionContext(Injector);
1156
+ */ const Injector = /*@__PURE__*/ build(()=>{
1157
+ const context = ensureInjectionContext("Injector factory");
1161
1158
  const resolution = context.resolution;
1162
1159
  const dependentFrame = resolution.stack.peek();
1163
1160
  const dependentRef = dependentFrame && resolution.dependents.get(dependentFrame.provider);
1164
- function withCurrentContext(fn) {
1161
+ const runInContext = (fn)=>{
1165
1162
  if (useInjectionContext()) {
1166
1163
  return fn();
1167
1164
  }
@@ -1173,18 +1170,17 @@ function OptionalAll(token) {
1173
1170
  try {
1174
1171
  return fn();
1175
1172
  } finally{
1176
- for (const cleanup of cleanups){
1177
- cleanup?.();
1178
- }
1173
+ cleanups.forEach((cleanup)=>cleanup?.());
1179
1174
  }
1180
- }
1175
+ };
1181
1176
  return {
1182
- inject: (token, name)=>withCurrentContext(()=>inject(token, name)),
1183
- injectAll: (token)=>withCurrentContext(()=>injectAll(token)),
1184
- optional: (token, name)=>withCurrentContext(()=>optional(token, name)),
1185
- optionalAll: (token)=>withCurrentContext(()=>optionalAll(token))
1177
+ inject: (token, name)=>runInContext(()=>inject(token, name)),
1178
+ injectAll: (token)=>runInContext(()=>injectAll(token)),
1179
+ optional: (token, name)=>runInContext(()=>optional(token, name)),
1180
+ optionalAll: (token)=>runInContext(()=>optionalAll(token)),
1181
+ runInContext
1186
1182
  };
1187
- });
1183
+ }, "Injector");
1188
1184
 
1189
1185
  /**
1190
1186
  * Applies middleware functions to a container.