@loopback/context 4.0.0-alpha.6 → 4.0.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.
Files changed (137) hide show
  1. package/LICENSE +25 -0
  2. package/README.md +116 -0
  3. package/dist/binding-config.d.ts +40 -0
  4. package/dist/binding-config.js +33 -0
  5. package/dist/binding-config.js.map +1 -0
  6. package/dist/binding-decorator.d.ts +45 -0
  7. package/dist/binding-decorator.js +118 -0
  8. package/dist/binding-decorator.js.map +1 -0
  9. package/dist/binding-filter.d.ts +108 -0
  10. package/dist/binding-filter.js +162 -0
  11. package/dist/binding-filter.js.map +1 -0
  12. package/dist/binding-inspector.d.ts +150 -0
  13. package/dist/binding-inspector.js +249 -0
  14. package/dist/binding-inspector.js.map +1 -0
  15. package/dist/binding-key.d.ts +66 -0
  16. package/dist/binding-key.js +121 -0
  17. package/dist/binding-key.js.map +1 -0
  18. package/dist/binding-sorter.d.ts +71 -0
  19. package/dist/binding-sorter.js +89 -0
  20. package/dist/binding-sorter.js.map +1 -0
  21. package/dist/binding.d.ts +577 -0
  22. package/dist/binding.js +788 -0
  23. package/dist/binding.js.map +1 -0
  24. package/dist/context-event.d.ts +23 -0
  25. package/dist/context-event.js +7 -0
  26. package/dist/context-event.js.map +1 -0
  27. package/dist/context-observer.d.ts +36 -0
  28. package/dist/context-observer.js +7 -0
  29. package/dist/context-observer.js.map +1 -0
  30. package/dist/context-subscription.d.ts +147 -0
  31. package/dist/context-subscription.js +317 -0
  32. package/dist/context-subscription.js.map +1 -0
  33. package/dist/context-tag-indexer.d.ts +42 -0
  34. package/dist/context-tag-indexer.js +135 -0
  35. package/dist/context-tag-indexer.js.map +1 -0
  36. package/dist/context-view.d.ts +209 -0
  37. package/dist/context-view.js +240 -0
  38. package/dist/context-view.js.map +1 -0
  39. package/dist/context.d.ts +513 -0
  40. package/dist/context.js +717 -0
  41. package/dist/context.js.map +1 -0
  42. package/dist/index.d.ts +52 -0
  43. package/dist/index.js +60 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/inject-config.d.ts +67 -0
  46. package/dist/inject-config.js +181 -0
  47. package/dist/inject-config.js.map +1 -0
  48. package/dist/inject.d.ts +250 -0
  49. package/dist/inject.js +535 -0
  50. package/dist/inject.js.map +1 -0
  51. package/dist/interception-proxy.d.ts +76 -0
  52. package/dist/interception-proxy.js +67 -0
  53. package/dist/interception-proxy.js.map +1 -0
  54. package/dist/interceptor-chain.d.ts +121 -0
  55. package/dist/interceptor-chain.js +148 -0
  56. package/dist/interceptor-chain.js.map +1 -0
  57. package/dist/interceptor.d.ts +138 -0
  58. package/dist/interceptor.js +299 -0
  59. package/dist/interceptor.js.map +1 -0
  60. package/dist/invocation.d.ts +101 -0
  61. package/dist/invocation.js +163 -0
  62. package/dist/invocation.js.map +1 -0
  63. package/dist/json-types.d.ts +28 -0
  64. package/dist/json-types.js +7 -0
  65. package/dist/json-types.js.map +1 -0
  66. package/dist/keys.d.ts +65 -0
  67. package/dist/keys.js +74 -0
  68. package/dist/keys.js.map +1 -0
  69. package/dist/provider.d.ts +31 -0
  70. package/dist/provider.js +7 -0
  71. package/dist/provider.js.map +1 -0
  72. package/dist/resolution-session.d.ts +180 -0
  73. package/dist/resolution-session.js +274 -0
  74. package/dist/resolution-session.js.map +1 -0
  75. package/dist/resolver.d.ts +46 -0
  76. package/dist/resolver.js +203 -0
  77. package/dist/resolver.js.map +1 -0
  78. package/dist/unique-id.d.ts +14 -0
  79. package/dist/unique-id.js +26 -0
  80. package/dist/unique-id.js.map +1 -0
  81. package/dist/value-promise.d.ts +134 -0
  82. package/dist/value-promise.js +277 -0
  83. package/dist/value-promise.js.map +1 -0
  84. package/package.json +49 -36
  85. package/src/binding-config.ts +73 -0
  86. package/src/binding-decorator.ts +136 -0
  87. package/src/binding-filter.ts +250 -0
  88. package/src/binding-inspector.ts +371 -0
  89. package/src/binding-key.ts +136 -0
  90. package/src/binding-sorter.ts +124 -0
  91. package/src/binding.ts +1107 -0
  92. package/src/context-event.ts +30 -0
  93. package/src/context-observer.ts +50 -0
  94. package/src/context-subscription.ts +402 -0
  95. package/src/context-tag-indexer.ts +147 -0
  96. package/src/context-view.ts +440 -0
  97. package/src/context.ts +1079 -0
  98. package/src/index.ts +58 -0
  99. package/src/inject-config.ts +239 -0
  100. package/src/inject.ts +796 -0
  101. package/src/interception-proxy.ts +127 -0
  102. package/src/interceptor-chain.ts +268 -0
  103. package/src/interceptor.ts +430 -0
  104. package/src/invocation.ts +269 -0
  105. package/src/json-types.ts +35 -0
  106. package/src/keys.ts +85 -0
  107. package/src/provider.ts +37 -0
  108. package/src/resolution-session.ts +414 -0
  109. package/src/resolver.ts +282 -0
  110. package/src/unique-id.ts +24 -0
  111. package/src/value-promise.ts +318 -0
  112. package/index.d.ts +0 -6
  113. package/index.js +0 -9
  114. package/lib/binding.d.ts +0 -75
  115. package/lib/binding.js +0 -102
  116. package/lib/context.d.ts +0 -14
  117. package/lib/context.js +0 -96
  118. package/lib/index.d.ts +0 -5
  119. package/lib/index.js +0 -13
  120. package/lib/inject.d.ts +0 -24
  121. package/lib/inject.js +0 -43
  122. package/lib/isPromise.d.ts +0 -1
  123. package/lib/isPromise.js +0 -14
  124. package/lib/resolver.d.ts +0 -26
  125. package/lib/resolver.js +0 -72
  126. package/lib6/binding.d.ts +0 -75
  127. package/lib6/binding.js +0 -102
  128. package/lib6/context.d.ts +0 -14
  129. package/lib6/context.js +0 -96
  130. package/lib6/index.d.ts +0 -5
  131. package/lib6/index.js +0 -13
  132. package/lib6/inject.d.ts +0 -24
  133. package/lib6/inject.js +0 -43
  134. package/lib6/isPromise.d.ts +0 -1
  135. package/lib6/isPromise.js +0 -14
  136. package/lib6/resolver.d.ts +0 -26
  137. package/lib6/resolver.js +0 -72
@@ -0,0 +1,282 @@
1
+ // Copyright IBM Corp. 2017,2019. 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 {DecoratorFactory} from '@loopback/metadata';
7
+ import assert from 'assert';
8
+ import debugModule from 'debug';
9
+ import {isBindingAddress} from './binding-filter';
10
+ import {BindingAddress} from './binding-key';
11
+ import {Context} from './context';
12
+ import {
13
+ describeInjectedArguments,
14
+ describeInjectedProperties,
15
+ Injection,
16
+ } from './inject';
17
+ import {
18
+ ResolutionError,
19
+ ResolutionOptions,
20
+ ResolutionSession,
21
+ } from './resolution-session';
22
+ import {
23
+ BoundValue,
24
+ Constructor,
25
+ MapObject,
26
+ resolveList,
27
+ resolveMap,
28
+ transformValueOrPromise,
29
+ ValueOrPromise,
30
+ } from './value-promise';
31
+
32
+ const debug = debugModule('loopback:context:resolver');
33
+ const getTargetName = DecoratorFactory.getTargetName;
34
+
35
+ /**
36
+ * Create an instance of a class which constructor has arguments
37
+ * decorated with `@inject`.
38
+ *
39
+ * The function returns a class when all dependencies were
40
+ * resolved synchronously, or a Promise otherwise.
41
+ *
42
+ * @param ctor - The class constructor to call.
43
+ * @param ctx - The context containing values for `@inject` resolution
44
+ * @param session - Optional session for binding and dependency resolution
45
+ * @param nonInjectedArgs - Optional array of args for non-injected parameters
46
+ */
47
+ export function instantiateClass<T>(
48
+ ctor: Constructor<T>,
49
+ ctx: Context,
50
+ session?: ResolutionSession,
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ nonInjectedArgs?: any[],
53
+ ): ValueOrPromise<T> {
54
+ /* istanbul ignore if */
55
+ if (debug.enabled) {
56
+ debug('Instantiating %s', getTargetName(ctor));
57
+ if (nonInjectedArgs?.length) {
58
+ debug('Non-injected arguments:', nonInjectedArgs);
59
+ }
60
+ }
61
+ const argsOrPromise = resolveInjectedArguments(
62
+ ctor,
63
+ '',
64
+ ctx,
65
+ session,
66
+ nonInjectedArgs,
67
+ );
68
+ const propertiesOrPromise = resolveInjectedProperties(ctor, ctx, session);
69
+ const inst: ValueOrPromise<T> = transformValueOrPromise(
70
+ argsOrPromise,
71
+ args => {
72
+ /* istanbul ignore if */
73
+ if (debug.enabled) {
74
+ debug('Injected arguments for %s():', ctor.name, args);
75
+ }
76
+ return new ctor(...args);
77
+ },
78
+ );
79
+ return transformValueOrPromise(propertiesOrPromise, props => {
80
+ /* istanbul ignore if */
81
+ if (debug.enabled) {
82
+ debug('Injected properties for %s:', ctor.name, props);
83
+ }
84
+ return transformValueOrPromise(inst, obj => Object.assign(obj, props));
85
+ });
86
+ }
87
+
88
+ /**
89
+ * If the scope of current binding is `SINGLETON`, reset the context
90
+ * to be the one that owns the current binding to make sure a singleton
91
+ * does not have dependencies injected from child contexts unless the
92
+ * injection is for method (excluding constructor) parameters.
93
+ */
94
+ function resolveContext(
95
+ ctx: Context,
96
+ injection: Readonly<Injection>,
97
+ session?: ResolutionSession,
98
+ ) {
99
+ const currentBinding = session?.currentBinding;
100
+ if (currentBinding == null) {
101
+ // No current binding
102
+ return ctx;
103
+ }
104
+
105
+ const isConstructorOrPropertyInjection =
106
+ // constructor injection
107
+ !injection.member ||
108
+ // property injection
109
+ typeof injection.methodDescriptorOrParameterIndex !== 'number';
110
+
111
+ if (isConstructorOrPropertyInjection) {
112
+ // Set context to the resolution context of the current binding for
113
+ // constructor or property injections against a singleton
114
+ ctx = ctx.getResolutionContext(currentBinding)!;
115
+ }
116
+ return ctx;
117
+ }
118
+
119
+ /**
120
+ * Resolve the value or promise for a given injection
121
+ * @param ctx - Context
122
+ * @param injection - Descriptor of the injection
123
+ * @param session - Optional session for binding and dependency resolution
124
+ */
125
+ function resolve<T>(
126
+ ctx: Context,
127
+ injection: Readonly<Injection>,
128
+ session?: ResolutionSession,
129
+ ): ValueOrPromise<T> {
130
+ /* istanbul ignore if */
131
+ if (debug.enabled) {
132
+ debug(
133
+ 'Resolving an injection:',
134
+ ResolutionSession.describeInjection(injection),
135
+ );
136
+ }
137
+
138
+ ctx = resolveContext(ctx, injection, session);
139
+ const resolved = ResolutionSession.runWithInjection(
140
+ s => {
141
+ if (injection.resolve) {
142
+ // A custom resolve function is provided
143
+ return injection.resolve(ctx, injection, s);
144
+ } else {
145
+ // Default to resolve the value from the context by binding key
146
+ assert(
147
+ isBindingAddress(injection.bindingSelector),
148
+ 'The binding selector must be an address (string or BindingKey)',
149
+ );
150
+ const key = injection.bindingSelector as BindingAddress;
151
+ const options: ResolutionOptions = {
152
+ session: s,
153
+ ...injection.metadata,
154
+ };
155
+ return ctx.getValueOrPromise(key, options);
156
+ }
157
+ },
158
+ injection,
159
+ session,
160
+ );
161
+ return resolved;
162
+ }
163
+
164
+ /**
165
+ * Given a function with arguments decorated with `@inject`,
166
+ * return the list of arguments resolved using the values
167
+ * bound in `ctx`.
168
+
169
+ * The function returns an argument array when all dependencies were
170
+ * resolved synchronously, or a Promise otherwise.
171
+ *
172
+ * @param target - The class for constructor injection or prototype for method
173
+ * injection
174
+ * @param method - The method name. If set to '', the constructor will
175
+ * be used.
176
+ * @param ctx - The context containing values for `@inject` resolution
177
+ * @param session - Optional session for binding and dependency resolution
178
+ * @param nonInjectedArgs - Optional array of args for non-injected parameters
179
+ */
180
+ export function resolveInjectedArguments(
181
+ target: object,
182
+ method: string,
183
+ ctx: Context,
184
+ session?: ResolutionSession,
185
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
186
+ nonInjectedArgs?: any[],
187
+ ): ValueOrPromise<BoundValue[]> {
188
+ /* istanbul ignore if */
189
+ if (debug.enabled) {
190
+ debug('Resolving injected arguments for %s', getTargetName(target, method));
191
+ }
192
+ const targetWithMethods = <{[method: string]: Function}>target;
193
+ if (method) {
194
+ assert(
195
+ typeof targetWithMethods[method] === 'function',
196
+ `Method ${method} not found`,
197
+ );
198
+ }
199
+ // NOTE: the array may be sparse, i.e.
200
+ // Object.keys(injectedArgs).length !== injectedArgs.length
201
+ // Example value:
202
+ // [ , 'key1', , 'key2']
203
+ const injectedArgs = describeInjectedArguments(target, method);
204
+ const extraArgs = nonInjectedArgs ?? [];
205
+
206
+ let argLength = DecoratorFactory.getNumberOfParameters(target, method);
207
+
208
+ // Please note `injectedArgs` contains `undefined` for non-injected args
209
+ const numberOfInjected = injectedArgs.filter(i => i != null).length;
210
+ if (argLength < numberOfInjected + extraArgs.length) {
211
+ /**
212
+ * `Function.prototype.length` excludes the rest parameter and only includes
213
+ * parameters before the first one with a default value. For example,
214
+ * `hello(@inject('name') name: string = 'John')` gives 0 for argLength
215
+ */
216
+ argLength = numberOfInjected + extraArgs.length;
217
+ }
218
+
219
+ let nonInjectedIndex = 0;
220
+ return resolveList(new Array(argLength), (val, ix) => {
221
+ // The `val` argument is not used as the resolver only uses `injectedArgs`
222
+ // and `extraArgs` to return the new value
223
+ const injection = ix < injectedArgs.length ? injectedArgs[ix] : undefined;
224
+ if (
225
+ injection == null ||
226
+ (!injection.bindingSelector && !injection.resolve)
227
+ ) {
228
+ if (nonInjectedIndex < extraArgs.length) {
229
+ // Set the argument from the non-injected list
230
+ return extraArgs[nonInjectedIndex++];
231
+ } else {
232
+ const name = getTargetName(target, method, ix);
233
+ throw new ResolutionError(
234
+ `The argument '${name}' is not decorated for dependency injection ` +
235
+ 'but no value was supplied by the caller. Did you forget to apply ' +
236
+ '@inject() to the argument?',
237
+ {context: ctx, options: {session}},
238
+ );
239
+ }
240
+ }
241
+
242
+ return resolve(
243
+ ctx,
244
+ injection,
245
+ // Clone the session so that multiple arguments can be resolved in parallel
246
+ ResolutionSession.fork(session),
247
+ );
248
+ });
249
+ }
250
+
251
+ /**
252
+ * Given a class with properties decorated with `@inject`,
253
+ * return the map of properties resolved using the values
254
+ * bound in `ctx`.
255
+
256
+ * The function returns an argument array when all dependencies were
257
+ * resolved synchronously, or a Promise otherwise.
258
+ *
259
+ * @param constructor - The class for which properties should be resolved.
260
+ * @param ctx - The context containing values for `@inject` resolution
261
+ * @param session - Optional session for binding and dependency resolution
262
+ */
263
+ export function resolveInjectedProperties(
264
+ constructor: Function,
265
+ ctx: Context,
266
+ session?: ResolutionSession,
267
+ ): ValueOrPromise<MapObject<BoundValue>> {
268
+ /* istanbul ignore if */
269
+ if (debug.enabled) {
270
+ debug('Resolving injected properties for %s', getTargetName(constructor));
271
+ }
272
+ const injectedProperties = describeInjectedProperties(constructor.prototype);
273
+
274
+ return resolveMap(injectedProperties, injection =>
275
+ resolve(
276
+ ctx,
277
+ injection,
278
+ // Clone the session so that multiple properties can be resolved in parallel
279
+ ResolutionSession.fork(session),
280
+ ),
281
+ );
282
+ }
@@ -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+/;
@@ -0,0 +1,318 @@
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
+ /**
7
+ * This module contains types for values and/or promises as well as a set of
8
+ * utility methods to handle values and/or promises.
9
+ */
10
+
11
+ import {v4 as uuidv4} from 'uuid';
12
+ /**
13
+ * A class constructor accepting arbitrary arguments.
14
+ */
15
+ export type Constructor<T> =
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ new (...args: any[]) => T;
18
+
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ export type BoundValue = any;
21
+
22
+ /**
23
+ * Representing a value or promise. This type is used to represent results of
24
+ * synchronous/asynchronous resolution of values.
25
+ *
26
+ * Note that we are using PromiseLike instead of native Promise to describe
27
+ * the asynchronous variant. This allows producers of async values to use
28
+ * any Promise implementation (e.g. Bluebird) instead of native Promises
29
+ * provided by JavaScript runtime.
30
+ */
31
+ export type ValueOrPromise<T> = T | PromiseLike<T>;
32
+
33
+ export type MapObject<T> = Record<string, T>;
34
+
35
+ /**
36
+ * Check whether a value is a Promise-like instance.
37
+ * Recognizes both native promises and third-party promise libraries.
38
+ *
39
+ * @param value - The value to check.
40
+ */
41
+ export function isPromiseLike<T>(
42
+ value: T | PromiseLike<T> | undefined,
43
+ ): value is PromiseLike<T> {
44
+ if (!value) return false;
45
+ if (typeof value !== 'object' && typeof value !== 'function') return false;
46
+ return typeof (value as PromiseLike<T>).then === 'function';
47
+ }
48
+
49
+ /**
50
+ * Get nested properties of an object by path
51
+ * @param value - Value of the source object
52
+ * @param path - Path to the property
53
+ */
54
+ export function getDeepProperty<OUT = BoundValue, IN = BoundValue>(
55
+ value: IN,
56
+ path: string,
57
+ ): OUT | undefined {
58
+ let result: BoundValue = value;
59
+ const props = path.split('.').filter(Boolean);
60
+ for (const p of props) {
61
+ if (result == null) {
62
+ return undefined;
63
+ }
64
+ result = result[p];
65
+ }
66
+ return <OUT>result;
67
+ }
68
+
69
+ /**
70
+ * Resolve entries of an object into a new object with the same keys. If one or
71
+ * more entries of the source object are resolved to a promise by the `resolver`
72
+ * function, this method returns a promise which will be resolved to the new
73
+ * object with fully resolved entries.
74
+ *
75
+ * @example
76
+ *
77
+ * - Example 1: resolve all entries synchronously
78
+ * ```ts
79
+ * const result = resolveMap({a: 'x', b: 'y'}, v => v.toUpperCase());
80
+ * ```
81
+ * The `result` will be `{a: 'X', b: 'Y'}`.
82
+ *
83
+ * - Example 2: resolve one or more entries asynchronously
84
+ * ```ts
85
+ * const result = resolveMap({a: 'x', b: 'y'}, v =>
86
+ * Promise.resolve(v.toUpperCase()),
87
+ * );
88
+ * ```
89
+ * The `result` will be a promise of `{a: 'X', b: 'Y'}`.
90
+ *
91
+ * @param map - The original object containing the source entries
92
+ * @param resolver - A function resolves an entry to a value or promise. It will
93
+ * be invoked with the property value, the property name, and the source object.
94
+ */
95
+ export function resolveMap<T, V>(
96
+ map: MapObject<T>,
97
+ resolver: (val: T, key: string, values: MapObject<T>) => ValueOrPromise<V>,
98
+ ): ValueOrPromise<MapObject<V>> {
99
+ const result: MapObject<V> = {};
100
+ let asyncResolvers: PromiseLike<void>[] | undefined = undefined;
101
+
102
+ const setter = (key: string) => (val: V) => {
103
+ if (val !== undefined) {
104
+ // Only set the value if it's not undefined so that the default value
105
+ // for a key will be honored
106
+ result[key] = val;
107
+ }
108
+ };
109
+
110
+ for (const key in map) {
111
+ const valueOrPromise = resolver(map[key], key, map);
112
+ if (isPromiseLike(valueOrPromise)) {
113
+ if (!asyncResolvers) asyncResolvers = [];
114
+ asyncResolvers.push(valueOrPromise.then(setter(key)));
115
+ } else {
116
+ if (valueOrPromise !== undefined) {
117
+ // Only set the value if it's not undefined so that the default value
118
+ // for a key will be honored
119
+ result[key] = valueOrPromise;
120
+ }
121
+ }
122
+ }
123
+
124
+ if (asyncResolvers) {
125
+ return Promise.all(asyncResolvers).then(() => result);
126
+ } else {
127
+ return result;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Resolve entries of an array into a new array with the same indexes. If one or
133
+ * more entries of the source array are resolved to a promise by the `resolver`
134
+ * function, this method returns a promise which will be resolved to the new
135
+ * array with fully resolved entries.
136
+ *
137
+ * @example
138
+ *
139
+ * - Example 1: resolve all entries synchronously
140
+ * ```ts
141
+ * const result = resolveList(['a', 'b'], v => v.toUpperCase());
142
+ * ```
143
+ * The `result` will be `['A', 'B']`.
144
+ *
145
+ * - Example 2: resolve one or more entries asynchronously
146
+ * ```ts
147
+ * const result = resolveList(['a', 'b'], v =>
148
+ * Promise.resolve(v.toUpperCase()),
149
+ * );
150
+ * ```
151
+ * The `result` will be a promise of `['A', 'B']`.
152
+ *
153
+ * @param list - The original array containing the source entries
154
+ * @param resolver - A function resolves an entry to a value or promise. It will
155
+ * be invoked with the property value, the property index, and the source array.
156
+ */
157
+ export function resolveList<T, V>(
158
+ list: T[],
159
+ resolver: (val: T, index: number, values: T[]) => ValueOrPromise<V>,
160
+ ): ValueOrPromise<V[]> {
161
+ const result: V[] = new Array<V>(list.length);
162
+ let asyncResolvers: PromiseLike<void>[] | undefined = undefined;
163
+
164
+ const setter = (index: number) => (val: V) => {
165
+ result[index] = val;
166
+ };
167
+
168
+ for (let ix = 0; ix < list.length; ix++) {
169
+ const valueOrPromise = resolver(list[ix], ix, list);
170
+ if (isPromiseLike(valueOrPromise)) {
171
+ if (!asyncResolvers) asyncResolvers = [];
172
+ asyncResolvers.push(valueOrPromise.then(setter(ix)));
173
+ } else {
174
+ result[ix] = valueOrPromise;
175
+ }
176
+ }
177
+
178
+ if (asyncResolvers) {
179
+ return Promise.all(asyncResolvers).then(() => result);
180
+ } else {
181
+ return result;
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Try to run an action that returns a promise or a value
187
+ * @param action - A function that returns a promise or a value
188
+ * @param finalAction - A function to be called once the action
189
+ * is fulfilled or rejected (synchronously or asynchronously)
190
+ *
191
+ * @typeParam T - Type for the return value
192
+ */
193
+ export function tryWithFinally<T>(
194
+ action: () => ValueOrPromise<T>,
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 = () => {},
219
+ ): ValueOrPromise<T> {
220
+ let result: ValueOrPromise<T>;
221
+ try {
222
+ result = action();
223
+ } catch (err) {
224
+ result = reject(err);
225
+ }
226
+ if (isPromiseLike(result)) {
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
+ }
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Resolve an iterator of source values into a result until the evaluator
251
+ * returns `true`
252
+ * @param source - The iterator of source values
253
+ * @param resolver - The resolve function that maps the source value to a result
254
+ * @param evaluator - The evaluate function that decides when to stop
255
+ */
256
+ export function resolveUntil<T, V>(
257
+ source: Iterator<T>,
258
+ resolver: (sourceVal: T) => ValueOrPromise<V | undefined>,
259
+ evaluator: (sourceVal: T, targetVal: V | undefined) => boolean,
260
+ ): ValueOrPromise<V | undefined> {
261
+ // Do iteration in loop for synchronous values to avoid stack overflow
262
+ // eslint-disable-next-line no-constant-condition
263
+ while (true) {
264
+ const next = source.next();
265
+ if (next.done) return undefined; // End of the iterator
266
+ const sourceVal = next.value;
267
+ const valueOrPromise = resolver(sourceVal);
268
+ if (isPromiseLike(valueOrPromise)) {
269
+ return valueOrPromise.then(v => {
270
+ if (evaluator(sourceVal, v)) {
271
+ return v;
272
+ } else {
273
+ return resolveUntil(source, resolver, evaluator);
274
+ }
275
+ });
276
+ } else {
277
+ if (evaluator(sourceVal, valueOrPromise)) {
278
+ return valueOrPromise;
279
+ }
280
+ // Continue with the while loop
281
+ }
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Transform a value or promise with a function that produces a new value or
287
+ * promise
288
+ * @param valueOrPromise - The value or promise
289
+ * @param transformer - A function that maps the source value to a value or promise
290
+ */
291
+ export function transformValueOrPromise<T, V>(
292
+ valueOrPromise: ValueOrPromise<T>,
293
+ transformer: (val: T) => ValueOrPromise<V>,
294
+ ): ValueOrPromise<V> {
295
+ if (isPromiseLike(valueOrPromise)) {
296
+ return valueOrPromise.then(transformer);
297
+ } else {
298
+ return transformer(valueOrPromise);
299
+ }
300
+ }
301
+
302
+ /**
303
+ * A utility to generate uuid v4
304
+ *
305
+ * @deprecated Use `generateUniqueId`, [uuid](https://www.npmjs.com/package/uuid)
306
+ * or [hyperid](https://www.npmjs.com/package/hyperid) instead.
307
+ */
308
+ export function uuid() {
309
+ return uuidv4();
310
+ }
311
+
312
+ /**
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.
316
+ */
317
+ export const UUID_PATTERN =
318
+ /[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}/i;
package/index.d.ts DELETED
@@ -1,6 +0,0 @@
1
- // Copyright IBM Corp. 2017. 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
- export * from './lib';
package/index.js DELETED
@@ -1,9 +0,0 @@
1
- // Copyright IBM Corp. 2017. All Rights Reserved.
2
- // Node module: @loopback/testlab
3
- // This file is licensed under the MIT License.
4
- // License text available at https://opensource.org/licenses/MIT
5
-
6
- const nodeMajorVersion = +process.versions.node.split('.')[0];
7
- module.exports = nodeMajorVersion >= 7 ?
8
- require('./lib') :
9
- require('./lib6');