@loopback/context 3.8.2 → 3.9.3

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/binding-inspector.js +1 -1
  3. package/dist/binding-inspector.js.map +1 -1
  4. package/dist/binding-key.d.ts +5 -1
  5. package/dist/binding-key.js +104 -103
  6. package/dist/binding-key.js.map +1 -1
  7. package/dist/binding.d.ts +20 -2
  8. package/dist/binding.js +26 -16
  9. package/dist/binding.js.map +1 -1
  10. package/dist/context-view.d.ts +82 -0
  11. package/dist/context-view.js +8 -0
  12. package/dist/context-view.js.map +1 -1
  13. package/dist/context.d.ts +21 -3
  14. package/dist/context.js +32 -16
  15. package/dist/context.js.map +1 -1
  16. package/dist/inject-config.js +2 -1
  17. package/dist/inject-config.js.map +1 -1
  18. package/dist/inject.js +3 -0
  19. package/dist/inject.js.map +1 -1
  20. package/dist/resolution-session.d.ts +15 -0
  21. package/dist/resolution-session.js +40 -1
  22. package/dist/resolution-session.js.map +1 -1
  23. package/dist/resolver.js +6 -13
  24. package/dist/resolver.js.map +1 -1
  25. package/dist/unique-id.d.ts +14 -0
  26. package/dist/unique-id.js +26 -0
  27. package/dist/unique-id.js.map +1 -0
  28. package/dist/value-promise.d.ts +21 -0
  29. package/dist/value-promise.js +45 -16
  30. package/dist/value-promise.js.map +1 -1
  31. package/package.json +15 -11
  32. package/src/binding-inspector.ts +1 -1
  33. package/src/binding-key.ts +7 -3
  34. package/src/binding.ts +61 -19
  35. package/src/context-view.ts +120 -0
  36. package/src/context.ts +66 -19
  37. package/src/inject-config.ts +1 -1
  38. package/src/inject.ts +5 -0
  39. package/src/resolution-session.ts +48 -0
  40. package/src/resolver.ts +14 -17
  41. package/src/unique-id.ts +24 -0
  42. package/src/value-promise.ts +50 -18
@@ -394,3 +394,51 @@ export interface ResolutionContext<T = unknown> {
394
394
  */
395
395
  readonly options: ResolutionOptions;
396
396
  }
397
+
398
+ /**
399
+ * Error for context binding resolutions and dependency injections
400
+ */
401
+ export class ResolutionError extends Error {
402
+ constructor(
403
+ message: string,
404
+ readonly resolutionCtx: Partial<ResolutionContext>,
405
+ ) {
406
+ super(ResolutionError.buildMessage(message, resolutionCtx));
407
+ this.name = ResolutionError.name;
408
+ }
409
+
410
+ private static buildDetails(resolutionCtx: Partial<ResolutionContext>) {
411
+ return {
412
+ context: resolutionCtx.context?.name ?? '',
413
+ binding: resolutionCtx.binding?.key ?? '',
414
+ resolutionPath: resolutionCtx.options?.session?.getResolutionPath() ?? '',
415
+ };
416
+ }
417
+
418
+ /**
419
+ * Build the error message for the resolution to include more contextual data
420
+ * @param reason - Cause of the error
421
+ * @param resolutionCtx - Resolution context
422
+ */
423
+ private static buildMessage(
424
+ reason: string,
425
+ resolutionCtx: Partial<ResolutionContext>,
426
+ ) {
427
+ const info = this.describeResolutionContext(resolutionCtx);
428
+ const message = `${reason} (${info})`;
429
+ return message;
430
+ }
431
+
432
+ private static describeResolutionContext(
433
+ resolutionCtx: Partial<ResolutionContext>,
434
+ ) {
435
+ const details = ResolutionError.buildDetails(resolutionCtx);
436
+ const items: string[] = [];
437
+ for (const [name, val] of Object.entries(details)) {
438
+ if (val !== '') {
439
+ items.push(`${name}: ${val}`);
440
+ }
441
+ }
442
+ return items.join(', ');
443
+ }
444
+ }
package/src/resolver.ts CHANGED
@@ -15,7 +15,11 @@ import {
15
15
  describeInjectedProperties,
16
16
  Injection,
17
17
  } from './inject';
18
- import {ResolutionOptions, ResolutionSession} from './resolution-session';
18
+ import {
19
+ ResolutionError,
20
+ ResolutionOptions,
21
+ ResolutionSession,
22
+ } from './resolution-session';
19
23
  import {
20
24
  BoundValue,
21
25
  Constructor,
@@ -230,10 +234,11 @@ export function resolveInjectedArguments(
230
234
  return extraArgs[nonInjectedIndex++];
231
235
  } else {
232
236
  const name = getTargetName(target, method, ix);
233
- throw new Error(
234
- `Cannot resolve injected arguments for ${name}: ` +
235
- `The arguments[${ix}] is not decorated for dependency injection, ` +
236
- `but a value is not supplied`,
237
+ throw new ResolutionError(
238
+ `The argument '${name}' is not decorated for dependency injection ` +
239
+ 'but no value was supplied by the caller. Did you forget to apply ' +
240
+ '@inject() to the argument?',
241
+ {context: ctx, options: {session}},
237
242
  );
238
243
  }
239
244
  }
@@ -270,20 +275,12 @@ export function resolveInjectedProperties(
270
275
  }
271
276
  const injectedProperties = describeInjectedProperties(constructor.prototype);
272
277
 
273
- return resolveMap(injectedProperties, (injection, p) => {
274
- if (!injection.bindingSelector && !injection.resolve) {
275
- const name = getTargetName(constructor, p);
276
- throw new Error(
277
- `Cannot resolve injected property ${name}: ` +
278
- `The property ${p} is not decorated for dependency injection.`,
279
- );
280
- }
281
-
282
- return resolve(
278
+ return resolveMap(injectedProperties, injection =>
279
+ resolve(
283
280
  ctx,
284
281
  injection,
285
282
  // Clone the session so that multiple properties can be resolved in parallel
286
283
  ResolutionSession.fork(session),
287
- );
288
- });
284
+ ),
285
+ );
289
286
  }
@@ -0,0 +1,24 @@
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
+ // Node module: @loopback/context
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import hyperid from 'hyperid';
7
+
8
+ /**
9
+ * Generate a (globally) unique identifier in a very fast way.
10
+ * Please note the ids ARE NOT formatted as UUID and have variable length.
11
+ * The format of generated values may change in the future.
12
+ *
13
+ * @internal
14
+ */
15
+ export const generateUniqueId = hyperid({
16
+ fixedLength: false,
17
+ urlSafe: true,
18
+ });
19
+
20
+ /**
21
+ * A regular expression for testing values generated by generateUniqueId.
22
+ * @internal
23
+ */
24
+ export const UNIQUE_ID_PATTERN = /[A-Za-z0-9-_]+-\d+/;
@@ -187,36 +187,63 @@ export function resolveList<T, V>(
187
187
  * @param action - A function that returns a promise or a value
188
188
  * @param finalAction - A function to be called once the action
189
189
  * is fulfilled or rejected (synchronously or asynchronously)
190
+ *
191
+ * @typeParam T - Type for the return value
190
192
  */
191
193
  export function tryWithFinally<T>(
192
194
  action: () => ValueOrPromise<T>,
193
195
  finalAction: () => void,
196
+ ): ValueOrPromise<T> {
197
+ return tryCatchFinally(action, undefined, finalAction);
198
+ }
199
+
200
+ /**
201
+ * Try to run an action that returns a promise or a value with error and final
202
+ * actions to mimic `try {} catch(err) {} finally {}` for a value or promise.
203
+ *
204
+ * @param action - A function that returns a promise or a value
205
+ * @param errorAction - A function to be called once the action
206
+ * is rejected (synchronously or asynchronously). It must either return a new
207
+ * value or throw an error.
208
+ * @param finalAction - A function to be called once the action
209
+ * is fulfilled or rejected (synchronously or asynchronously)
210
+ *
211
+ * @typeParam T - Type for the return value
212
+ */
213
+ export function tryCatchFinally<T>(
214
+ action: () => ValueOrPromise<T>,
215
+ errorAction: (err: unknown) => T | never = err => {
216
+ throw err;
217
+ },
218
+ finalAction: () => void = () => {},
194
219
  ): ValueOrPromise<T> {
195
220
  let result: ValueOrPromise<T>;
196
221
  try {
197
222
  result = action();
198
223
  } catch (err) {
199
- finalAction();
200
- throw err;
224
+ result = reject(err);
201
225
  }
202
226
  if (isPromiseLike(result)) {
203
- // Once (promise.finally)[https://github.com/tc39/proposal-promise-finally
204
- // is supported, the following can be simplifed as
205
- // `result = result.finally(finalAction);`
206
- result = result.then(
207
- val => {
208
- finalAction();
209
- return val;
210
- },
211
- err => {
212
- finalAction();
213
- throw err;
214
- },
215
- );
216
- } else {
217
- finalAction();
227
+ return result.then(resolve, reject);
228
+ }
229
+
230
+ return resolve(result);
231
+
232
+ function resolve(value: T) {
233
+ try {
234
+ return value;
235
+ } finally {
236
+ finalAction();
237
+ }
238
+ }
239
+
240
+ function reject(err: unknown): T | never {
241
+ try {
242
+ return errorAction(err);
243
+ } finally {
244
+ finalAction();
245
+ }
218
246
  }
219
- return result;
220
247
  }
221
248
 
222
249
  /**
@@ -274,6 +301,9 @@ export function transformValueOrPromise<T, V>(
274
301
 
275
302
  /**
276
303
  * A utility to generate uuid v4
304
+ *
305
+ * @deprecated Use [uuid](https://www.npmjs.com/package/uuid) or
306
+ * [hyperid](https://www.npmjs.com/package/hyperid) instead.
277
307
  */
278
308
  export function uuid() {
279
309
  return uuidv4();
@@ -281,5 +311,7 @@ export function uuid() {
281
311
 
282
312
  /**
283
313
  * A regular expression for testing uuid v4 PATTERN
314
+ * @deprecated This pattern is an internal helper used by unit-tests, we are no
315
+ * longer using it.
284
316
  */
285
317
  export const UUID_PATTERN = /[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}/i;