1 | ;
|
2 | // Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
|
3 | // Node module: @loopback/context
|
4 | // This file is licensed under the MIT License.
|
5 | // License text available at https://opensource.org/licenses/MIT
|
6 | Object.defineProperty(exports, "__esModule", { value: true });
|
7 | exports.UUID_PATTERN = exports.uuid = exports.transformValueOrPromise = exports.resolveUntil = exports.tryCatchFinally = exports.tryWithFinally = exports.resolveList = exports.resolveMap = exports.getDeepProperty = exports.isPromiseLike = void 0;
|
8 | /**
|
9 | * This module contains types for values and/or promises as well as a set of
|
10 | * utility methods to handle values and/or promises.
|
11 | */
|
12 | const uuid_1 = require("uuid");
|
13 | /**
|
14 | * Check whether a value is a Promise-like instance.
|
15 | * Recognizes both native promises and third-party promise libraries.
|
16 | *
|
17 | * @param value - The value to check.
|
18 | */
|
19 | function isPromiseLike(value) {
|
20 | if (!value)
|
21 | return false;
|
22 | if (typeof value !== 'object' && typeof value !== 'function')
|
23 | return false;
|
24 | return typeof value.then === 'function';
|
25 | }
|
26 | exports.isPromiseLike = isPromiseLike;
|
27 | /**
|
28 | * Get nested properties of an object by path
|
29 | * @param value - Value of the source object
|
30 | * @param path - Path to the property
|
31 | */
|
32 | function getDeepProperty(value, path) {
|
33 | let result = value;
|
34 | const props = path.split('.').filter(Boolean);
|
35 | for (const p of props) {
|
36 | if (result == null) {
|
37 | return undefined;
|
38 | }
|
39 | result = result[p];
|
40 | }
|
41 | return result;
|
42 | }
|
43 | exports.getDeepProperty = getDeepProperty;
|
44 | /**
|
45 | * Resolve entries of an object into a new object with the same keys. If one or
|
46 | * more entries of the source object are resolved to a promise by the `resolver`
|
47 | * function, this method returns a promise which will be resolved to the new
|
48 | * object with fully resolved entries.
|
49 | *
|
50 | * @example
|
51 | *
|
52 | * - Example 1: resolve all entries synchronously
|
53 | * ```ts
|
54 | * const result = resolveMap({a: 'x', b: 'y'}, v => v.toUpperCase());
|
55 | * ```
|
56 | * The `result` will be `{a: 'X', b: 'Y'}`.
|
57 | *
|
58 | * - Example 2: resolve one or more entries asynchronously
|
59 | * ```ts
|
60 | * const result = resolveMap({a: 'x', b: 'y'}, v =>
|
61 | * Promise.resolve(v.toUpperCase()),
|
62 | * );
|
63 | * ```
|
64 | * The `result` will be a promise of `{a: 'X', b: 'Y'}`.
|
65 | *
|
66 | * @param map - The original object containing the source entries
|
67 | * @param resolver - A function resolves an entry to a value or promise. It will
|
68 | * be invoked with the property value, the property name, and the source object.
|
69 | */
|
70 | function resolveMap(map, resolver) {
|
71 | const result = {};
|
72 | let asyncResolvers = undefined;
|
73 | const setter = (key) => (val) => {
|
74 | if (val !== undefined) {
|
75 | // Only set the value if it's not undefined so that the default value
|
76 | // for a key will be honored
|
77 | result[key] = val;
|
78 | }
|
79 | };
|
80 | for (const key in map) {
|
81 | const valueOrPromise = resolver(map[key], key, map);
|
82 | if (isPromiseLike(valueOrPromise)) {
|
83 | if (!asyncResolvers)
|
84 | asyncResolvers = [];
|
85 | asyncResolvers.push(valueOrPromise.then(setter(key)));
|
86 | }
|
87 | else {
|
88 | if (valueOrPromise !== undefined) {
|
89 | // Only set the value if it's not undefined so that the default value
|
90 | // for a key will be honored
|
91 | result[key] = valueOrPromise;
|
92 | }
|
93 | }
|
94 | }
|
95 | if (asyncResolvers) {
|
96 | return Promise.all(asyncResolvers).then(() => result);
|
97 | }
|
98 | else {
|
99 | return result;
|
100 | }
|
101 | }
|
102 | exports.resolveMap = resolveMap;
|
103 | /**
|
104 | * Resolve entries of an array into a new array with the same indexes. If one or
|
105 | * more entries of the source array are resolved to a promise by the `resolver`
|
106 | * function, this method returns a promise which will be resolved to the new
|
107 | * array with fully resolved entries.
|
108 | *
|
109 | * @example
|
110 | *
|
111 | * - Example 1: resolve all entries synchronously
|
112 | * ```ts
|
113 | * const result = resolveList(['a', 'b'], v => v.toUpperCase());
|
114 | * ```
|
115 | * The `result` will be `['A', 'B']`.
|
116 | *
|
117 | * - Example 2: resolve one or more entries asynchronously
|
118 | * ```ts
|
119 | * const result = resolveList(['a', 'b'], v =>
|
120 | * Promise.resolve(v.toUpperCase()),
|
121 | * );
|
122 | * ```
|
123 | * The `result` will be a promise of `['A', 'B']`.
|
124 | *
|
125 | * @param list - The original array containing the source entries
|
126 | * @param resolver - A function resolves an entry to a value or promise. It will
|
127 | * be invoked with the property value, the property index, and the source array.
|
128 | */
|
129 | function resolveList(list, resolver) {
|
130 | const result = new Array(list.length);
|
131 | let asyncResolvers = undefined;
|
132 | const setter = (index) => (val) => {
|
133 | result[index] = val;
|
134 | };
|
135 | for (let ix = 0; ix < list.length; ix++) {
|
136 | const valueOrPromise = resolver(list[ix], ix, list);
|
137 | if (isPromiseLike(valueOrPromise)) {
|
138 | if (!asyncResolvers)
|
139 | asyncResolvers = [];
|
140 | asyncResolvers.push(valueOrPromise.then(setter(ix)));
|
141 | }
|
142 | else {
|
143 | result[ix] = valueOrPromise;
|
144 | }
|
145 | }
|
146 | if (asyncResolvers) {
|
147 | return Promise.all(asyncResolvers).then(() => result);
|
148 | }
|
149 | else {
|
150 | return result;
|
151 | }
|
152 | }
|
153 | exports.resolveList = resolveList;
|
154 | /**
|
155 | * Try to run an action that returns a promise or a value
|
156 | * @param action - A function that returns a promise or a value
|
157 | * @param finalAction - A function to be called once the action
|
158 | * is fulfilled or rejected (synchronously or asynchronously)
|
159 | *
|
160 | * @typeParam T - Type for the return value
|
161 | */
|
162 | function tryWithFinally(action, finalAction) {
|
163 | return tryCatchFinally(action, undefined, finalAction);
|
164 | }
|
165 | exports.tryWithFinally = tryWithFinally;
|
166 | /**
|
167 | * Try to run an action that returns a promise or a value with error and final
|
168 | * actions to mimic `try {} catch(err) {} finally {}` for a value or promise.
|
169 | *
|
170 | * @param action - A function that returns a promise or a value
|
171 | * @param errorAction - A function to be called once the action
|
172 | * is rejected (synchronously or asynchronously). It must either return a new
|
173 | * value or throw an error.
|
174 | * @param finalAction - A function to be called once the action
|
175 | * is fulfilled or rejected (synchronously or asynchronously)
|
176 | *
|
177 | * @typeParam T - Type for the return value
|
178 | */
|
179 | function tryCatchFinally(action, errorAction = err => {
|
180 | throw err;
|
181 | }, finalAction = () => { }) {
|
182 | let result;
|
183 | try {
|
184 | result = action();
|
185 | }
|
186 | catch (err) {
|
187 | result = reject(err);
|
188 | }
|
189 | if (isPromiseLike(result)) {
|
190 | return result.then(resolve, reject);
|
191 | }
|
192 | return resolve(result);
|
193 | function resolve(value) {
|
194 | try {
|
195 | return value;
|
196 | }
|
197 | finally {
|
198 | finalAction();
|
199 | }
|
200 | }
|
201 | function reject(err) {
|
202 | try {
|
203 | return errorAction(err);
|
204 | }
|
205 | finally {
|
206 | finalAction();
|
207 | }
|
208 | }
|
209 | }
|
210 | exports.tryCatchFinally = tryCatchFinally;
|
211 | /**
|
212 | * Resolve an iterator of source values into a result until the evaluator
|
213 | * returns `true`
|
214 | * @param source - The iterator of source values
|
215 | * @param resolver - The resolve function that maps the source value to a result
|
216 | * @param evaluator - The evaluate function that decides when to stop
|
217 | */
|
218 | function resolveUntil(source, resolver, evaluator) {
|
219 | // Do iteration in loop for synchronous values to avoid stack overflow
|
220 | // eslint-disable-next-line no-constant-condition
|
221 | while (true) {
|
222 | const next = source.next();
|
223 | if (next.done)
|
224 | return undefined; // End of the iterator
|
225 | const sourceVal = next.value;
|
226 | const valueOrPromise = resolver(sourceVal);
|
227 | if (isPromiseLike(valueOrPromise)) {
|
228 | return valueOrPromise.then(v => {
|
229 | if (evaluator(sourceVal, v)) {
|
230 | return v;
|
231 | }
|
232 | else {
|
233 | return resolveUntil(source, resolver, evaluator);
|
234 | }
|
235 | });
|
236 | }
|
237 | else {
|
238 | if (evaluator(sourceVal, valueOrPromise)) {
|
239 | return valueOrPromise;
|
240 | }
|
241 | // Continue with the while loop
|
242 | }
|
243 | }
|
244 | }
|
245 | exports.resolveUntil = resolveUntil;
|
246 | /**
|
247 | * Transform a value or promise with a function that produces a new value or
|
248 | * promise
|
249 | * @param valueOrPromise - The value or promise
|
250 | * @param transformer - A function that maps the source value to a value or promise
|
251 | */
|
252 | function transformValueOrPromise(valueOrPromise, transformer) {
|
253 | if (isPromiseLike(valueOrPromise)) {
|
254 | return valueOrPromise.then(transformer);
|
255 | }
|
256 | else {
|
257 | return transformer(valueOrPromise);
|
258 | }
|
259 | }
|
260 | exports.transformValueOrPromise = transformValueOrPromise;
|
261 | /**
|
262 | * A utility to generate uuid v4
|
263 | *
|
264 | * @deprecated Use `generateUniqueId`, [uuid](https://www.npmjs.com/package/uuid)
|
265 | * or [hyperid](https://www.npmjs.com/package/hyperid) instead.
|
266 | */
|
267 | function uuid() {
|
268 | return (0, uuid_1.v4)();
|
269 | }
|
270 | exports.uuid = uuid;
|
271 | /**
|
272 | * A regular expression for testing uuid v4 PATTERN
|
273 | * @deprecated This pattern is an internal helper used by unit-tests, we are no
|
274 | * longer using it.
|
275 | */
|
276 | exports.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;
|
277 | //# sourceMappingURL=value-promise.js.map |
\ | No newline at end of file |