@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/es/index.mjs CHANGED
@@ -1,3 +1,30 @@
1
+ /**
2
+ * Type API.
3
+ */ /**
4
+ * Creates a type token.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const ISpell = createType<Spell>("Spell");
9
+ * ```
10
+ *
11
+ * @__NO_SIDE_EFFECTS__
12
+ */ function createType(typeName) {
13
+ const type = {
14
+ name: `Type<${typeName}>`,
15
+ inter: createType,
16
+ union: createType,
17
+ toString () {
18
+ return type.name;
19
+ }
20
+ };
21
+ return type;
22
+ }
23
+ // @internal
24
+ function isConstructor(token) {
25
+ return typeof token === "function";
26
+ }
27
+
1
28
  // @internal
2
29
  function assert(condition, message) {
3
30
  if (!condition) {
@@ -10,7 +37,8 @@ function expectNever(value) {
10
37
  }
11
38
  // @internal
12
39
  function throwUnregisteredError(token) {
13
- throw new Error(tag(`unregistered token ${token.name}`));
40
+ const type = isConstructor(token) ? "class" : "token";
41
+ throw new Error(tag(`unregistered ${type} ${token.name}`));
14
42
  }
15
43
  // @internal
16
44
  function throwExistingUnregisteredError(sourceToken, targetTokenOrError) {
@@ -29,23 +57,6 @@ function untag(message) {
29
57
  return message.startsWith("[di-wise-neo]") ? message.substring(13).trimStart() : message;
30
58
  }
31
59
 
32
- // @internal
33
- function createInjectionContext() {
34
- let current = null;
35
- function provide(next) {
36
- const prev = current;
37
- current = next;
38
- return ()=>current = prev;
39
- }
40
- function use() {
41
- return current;
42
- }
43
- return [
44
- provide,
45
- use
46
- ];
47
- }
48
-
49
60
  // @internal
50
61
  function invariant(condition) {
51
62
  if (!condition) {
@@ -115,18 +126,33 @@ function createResolution() {
115
126
  // @internal
116
127
  const [provideInjectionContext, useInjectionContext] = createInjectionContext();
117
128
  // @internal
118
- function ensureInjectionContext(fn) {
129
+ function ensureInjectionContext(name) {
119
130
  const context = useInjectionContext();
120
- assert(context, `${fn.name}() can only be invoked within an injection context`);
131
+ assert(context, `${name} can only be invoked within an injection context`);
121
132
  return context;
122
133
  }
134
+ function createInjectionContext() {
135
+ let current = null;
136
+ function provide(next) {
137
+ const prev = current;
138
+ current = next;
139
+ return ()=>current = prev;
140
+ }
141
+ function use() {
142
+ return current;
143
+ }
144
+ return [
145
+ provide,
146
+ use
147
+ ];
148
+ }
123
149
 
124
150
  function inject(token, name) {
125
- const context = ensureInjectionContext(inject);
151
+ const context = ensureInjectionContext("inject()");
126
152
  return context.container.resolve(token, name);
127
153
  }
128
154
  function injectBy(thisArg, token, name) {
129
- const context = ensureInjectionContext(injectBy);
155
+ const context = ensureInjectionContext("injectBy()");
130
156
  const resolution = context.resolution;
131
157
  const currentFrame = resolution.stack.peek();
132
158
  if (!currentFrame) {
@@ -144,7 +170,7 @@ function injectBy(thisArg, token, name) {
144
170
  }
145
171
 
146
172
  function injectAll(token) {
147
- const context = ensureInjectionContext(injectAll);
173
+ const context = ensureInjectionContext("injectAll()");
148
174
  return context.container.resolveAll(token);
149
175
  }
150
176
 
@@ -240,11 +266,11 @@ const classIdentityMap = new WeakMap();
240
266
  const metadataMap = new WeakMap();
241
267
 
242
268
  function optional(token, name) {
243
- const context = ensureInjectionContext(optional);
269
+ const context = ensureInjectionContext("optional()");
244
270
  return context.container.resolve(token, true, name);
245
271
  }
246
272
  function optionalBy(thisArg, token, name) {
247
- const context = ensureInjectionContext(optionalBy);
273
+ const context = ensureInjectionContext("optionalBy()");
248
274
  const resolution = context.resolution;
249
275
  const currentFrame = resolution.stack.peek();
250
276
  if (!currentFrame) {
@@ -262,7 +288,7 @@ function optionalBy(thisArg, token, name) {
262
288
  }
263
289
 
264
290
  function optionalAll(token) {
265
- const context = ensureInjectionContext(optionalAll);
291
+ const context = ensureInjectionContext("optionalAll()");
266
292
  return context.container.resolveAll(token, true);
267
293
  }
268
294
 
@@ -290,33 +316,6 @@ const Scope = {
290
316
  Container: "Container"
291
317
  };
292
318
 
293
- /**
294
- * Type API.
295
- */ /**
296
- * Creates a type token.
297
- *
298
- * @example
299
- * ```ts
300
- * const ISpell = createType<Spell>("Spell");
301
- * ```
302
- *
303
- * @__NO_SIDE_EFFECTS__
304
- */ function createType(typeName) {
305
- const type = {
306
- name: `Type<${typeName}>`,
307
- inter: createType,
308
- union: createType,
309
- toString () {
310
- return type.name;
311
- }
312
- };
313
- return type;
314
- }
315
- // @internal
316
- function isConstructor(token) {
317
- return typeof token === "function";
318
- }
319
-
320
319
  // @internal
321
320
  function getTypeName(value) {
322
321
  switch(typeof value){
@@ -428,26 +427,9 @@ class TokenRegistry {
428
427
  function isBuilder(provider) {
429
428
  return builders.has(provider);
430
429
  }
431
- /**
432
- * Create a one-off type token from a factory function.
433
- *
434
- * @example
435
- * ```ts
436
- * class Wizard {
437
- * wand = inject(
438
- * build(() => {
439
- * const wand = inject(Wand);
440
- * wand.owner = this;
441
- * // ...
442
- * return wand;
443
- * }),
444
- * );
445
- * }
446
- * ```
447
- *
448
- * @__NO_SIDE_EFFECTS__
449
- */ function build(factory) {
450
- const token = createType(`Build<${getTypeName(factory)}>`);
430
+ // @__NO_SIDE_EFFECTS__
431
+ function build(factory, name) {
432
+ const token = createType(name ?? `Build<${getTypeName(factory)}>`);
451
433
  const provider = {
452
434
  useFactory: factory
453
435
  };
@@ -638,7 +620,7 @@ function isDisposable(value) {
638
620
  if (isConstructor(token)) {
639
621
  return this.instantiateClass(token, localOptional);
640
622
  }
641
- return optionalOrName ? undefined : throwUnregisteredError(token);
623
+ return localOptional ? undefined : throwUnregisteredError(token);
642
624
  }
643
625
  resolveAll(token, optional) {
644
626
  this.checkDisposed();
@@ -1005,10 +987,22 @@ function updateParameterMetadata(decorator, target, propertyKey, parameterIndex,
1005
987
  updateFn(dependency);
1006
988
  }
1007
989
  }
990
+ // Checks that a constructor or method parameter has only one injection decorator.
991
+ // For example, if both `@Inject` and `@Optional` are used, it becomes difficult to
992
+ // understand which one "wins", unless the user is aware of the decorator evaluation order.
993
+ //
994
+ // @internal
995
+ function checkSingleDecorator(dependency, target, propertyKey, parameterIndex) {
996
+ assert(!dependency.appliedBy, ()=>{
997
+ const where = propertyKey === undefined ? `${target.name} constructor` : `${target.constructor.name}.${String(propertyKey)}`;
998
+ return `${where} parameter ${parameterIndex} declares multiple injection decorators, but only one is allowed`;
999
+ });
1000
+ }
1008
1001
 
1009
1002
  function Inject(token) {
1010
1003
  return function(target, propertyKey, parameterIndex) {
1011
1004
  updateParameterMetadata("Inject", target, propertyKey, parameterIndex, (dependency)=>{
1005
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1012
1006
  dependency.appliedBy = "Inject";
1013
1007
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1014
1008
  });
@@ -1038,6 +1032,7 @@ function Inject(token) {
1038
1032
  function InjectAll(token) {
1039
1033
  return function(target, propertyKey, parameterIndex) {
1040
1034
  updateParameterMetadata("InjectAll", target, propertyKey, parameterIndex, (dependency)=>{
1035
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1041
1036
  dependency.appliedBy = "InjectAll";
1042
1037
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1043
1038
  });
@@ -1085,6 +1080,7 @@ function InjectAll(token) {
1085
1080
  function Optional(token) {
1086
1081
  return function(target, propertyKey, parameterIndex) {
1087
1082
  updateParameterMetadata("Optional", target, propertyKey, parameterIndex, (dependency)=>{
1083
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1088
1084
  dependency.appliedBy = "Optional";
1089
1085
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1090
1086
  });
@@ -1094,6 +1090,7 @@ function Optional(token) {
1094
1090
  function OptionalAll(token) {
1095
1091
  return function(target, propertyKey, parameterIndex) {
1096
1092
  updateParameterMetadata("OptionalAll", target, propertyKey, parameterIndex, (dependency)=>{
1093
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1097
1094
  dependency.appliedBy = "OptionalAll";
1098
1095
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1099
1096
  });
@@ -1154,12 +1151,12 @@ function OptionalAll(token) {
1154
1151
  * const wizard = container.resolve(Wizard);
1155
1152
  * wizard.getWand(); // => Wand
1156
1153
  * ```
1157
- */ const Injector = /*@__PURE__*/ build(function Injector() {
1158
- const context = ensureInjectionContext(Injector);
1154
+ */ const Injector = /*@__PURE__*/ build(()=>{
1155
+ const context = ensureInjectionContext("Injector factory");
1159
1156
  const resolution = context.resolution;
1160
1157
  const dependentFrame = resolution.stack.peek();
1161
1158
  const dependentRef = dependentFrame && resolution.dependents.get(dependentFrame.provider);
1162
- function withCurrentContext(fn) {
1159
+ const runInContext = (fn)=>{
1163
1160
  if (useInjectionContext()) {
1164
1161
  return fn();
1165
1162
  }
@@ -1171,18 +1168,17 @@ function OptionalAll(token) {
1171
1168
  try {
1172
1169
  return fn();
1173
1170
  } finally{
1174
- for (const cleanup of cleanups){
1175
- cleanup?.();
1176
- }
1171
+ cleanups.forEach((cleanup)=>cleanup?.());
1177
1172
  }
1178
- }
1173
+ };
1179
1174
  return {
1180
- inject: (token, name)=>withCurrentContext(()=>inject(token, name)),
1181
- injectAll: (token)=>withCurrentContext(()=>injectAll(token)),
1182
- optional: (token, name)=>withCurrentContext(()=>optional(token, name)),
1183
- optionalAll: (token)=>withCurrentContext(()=>optionalAll(token))
1175
+ inject: (token, name)=>runInContext(()=>inject(token, name)),
1176
+ injectAll: (token)=>runInContext(()=>injectAll(token)),
1177
+ optional: (token, name)=>runInContext(()=>optional(token, name)),
1178
+ optionalAll: (token)=>runInContext(()=>optionalAll(token)),
1179
+ runInContext
1184
1180
  };
1185
- });
1181
+ }, "Injector");
1186
1182
 
1187
1183
  /**
1188
1184
  * Applies middleware functions to a container.