aveazul 0.1.3 → 0.1.5

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.
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  AveAzul ("Blue Bird" in Spanish) is a Promise library that extends native Promises with Bluebird-like utility methods. Built on top of native Promises, it provides a familiar API for Node.js developers who are used to working with [Bluebird](https://github.com/petkaantonov/bluebird) ([npm](https://www.npmjs.com/package/bluebird)).
4
4
 
5
+ This library helps migrate legacy code that uses Bluebird specific APIs to native Promises with minimal changes. It aims to provide a drop-in replacement that maintains compatibility while leveraging native Promises.
6
+
7
+ Do you prefer Bluebird's API with native Promises? AveAzul gives you both - familiar Bluebird methods built on native Promise.
8
+
5
9
  ## Installation
6
10
 
7
11
  ```bash
@@ -11,29 +15,60 @@ npm install aveazul
11
15
  ## Usage
12
16
 
13
17
  ```javascript
14
- const AveAzul = require('aveazul');
18
+ const AveAzul = require("aveazul");
15
19
 
16
20
  // Basic Promise usage
17
21
  const promise = new AveAzul((resolve) => resolve(42));
18
- promise.then(value => console.log(value)); // 42
22
+ promise.then((value) => console.log(value)); // 42
19
23
 
20
24
  // Utility methods
21
25
  AveAzul.resolve([1, 2, 3])
22
- .map(x => x * 2)
23
- .filter(x => x > 2)
24
- .then(result => console.log(result)); // [4, 6]
26
+ .map((x) => x * 2)
27
+ .filter((x) => x > 2)
28
+ .then((result) => console.log(result)); // [4, 6]
25
29
 
26
30
  // Promisify callback-style functions
27
- const fs = require('fs');
31
+ const fs = require("fs");
28
32
  const readFile = AveAzul.promisify(fs.readFile);
29
- readFile('file.txt').then(content => console.log(content));
33
+ readFile("file.txt").then((content) => console.log(content));
34
+ // Properties from the original function are preserved
35
+ console.log(readFile.length); // Original function's length property
30
36
 
31
37
  // Promisify all methods of an object
32
38
  const obj = {
33
- method(cb) { cb(null, 'result'); }
39
+ method(cb) {
40
+ cb(null, "result");
41
+ },
34
42
  };
35
43
  AveAzul.promisifyAll(obj);
36
- obj.methodAsync().then(result => console.log(result)); // 'result'
44
+ obj.methodAsync().then((result) => console.log(result)); // 'result'
45
+
46
+ // Resource management with disposer and using
47
+ const getResource = () => {
48
+ return AveAzul.resolve({
49
+ data: "important data",
50
+ close: () => console.log("Resource closed!"),
51
+ }).disposer((resource) => resource.close());
52
+ };
53
+
54
+ AveAzul.using(getResource(), (resource) => {
55
+ console.log(resource.data); // "important data"
56
+ return AveAzul.resolve("operation completed");
57
+ }).then((result) => {
58
+ console.log(result); // "operation completed"
59
+ // Resource is automatically closed here, even if an error occurred
60
+ });
61
+
62
+ // Using spread to apply array results as arguments
63
+ AveAzul.all([getUser(1), getPosts(1), getComments(1)]).spread(
64
+ (user, posts, comments) => {
65
+ // Instead of using .then(([user, posts, comments]) => {...})
66
+ console.log(
67
+ `User ${user.name} has ${posts.length} posts and ${comments.length} comments`
68
+ );
69
+ return { user, activity: { posts, comments } };
70
+ }
71
+ );
37
72
  ```
38
73
 
39
74
  ## API
@@ -47,15 +82,15 @@ obj.methodAsync().then(result => console.log(result)); // 'result'
47
82
  - `each(fn)` - Iterate over array elements
48
83
  - `delay(ms)` - Delay resolution
49
84
  - `timeout(ms, message?)` - Reject after specified time
50
- - `try(fn)` - Wrap sync/async functions
51
85
  - `props(obj)` - Resolve object properties
52
- - `catchIf(predicate, fn)` - Catch specific errors
86
+ - `spread(fn)` - Apply array values as arguments to function
53
87
  - `tapCatch(fn)` - Execute side effects on rejection
54
88
  - `reduce(fn, initialValue?)` - Reduce array elements
55
89
  - `throw(reason)` - Return rejected promise
56
90
  - `catchThrow(reason)` - Catch and throw new error
57
91
  - `catchReturn(value)` - Catch and return value
58
92
  - `get(propertyPath)` - Retrieve property value
93
+ - `disposer(fn)` - Create a disposer for use with AveAzul.using() for resource cleanup
59
94
 
60
95
  ### Static Methods
61
96
 
@@ -64,11 +99,16 @@ obj.methodAsync().then(result => console.log(result)); // 'result'
64
99
  - `try(fn)` - Wrap sync/async functions
65
100
  - `props(obj)` - Resolve object properties
66
101
  - `defer()` - Create a deferred promise
67
- - `promisify(fn, options?)` - Convert callback-style functions to promises
102
+ - `promisify(fn, options?)` - Convert callback-style functions to promises (preserves original function properties)
103
+ - `fromNode(fn, options?)` - Convert Node-style callback functions to promise-returning functions
104
+ - `fromCallback(fn, options?)` - Alias for fromNode
68
105
  - `each(items, fn)` - Iterate over array elements
69
106
  - `reduce(array, fn, initialValue?)` - Reduce array elements
107
+ - `method(fn)` - Creates a method that returns a promise resolving to the value returned by the original function
70
108
  - `throw(reason)` - Return rejected promise
71
109
  - `promisifyAll(target, options?)` - Convert all methods of an object/class to promises
110
+ - `using(resources, fn)` - Manage resources with automatic cleanup
111
+ - `join(...values, handler?)` - Wait for multiple promises and pass their resolved values as separate arguments to the handler function. If no handler is provided, behaves like Promise.all
72
112
 
73
113
  ### PromisifyAll Options
74
114
 
@@ -76,8 +116,6 @@ obj.methodAsync().then(result => console.log(result)); // 'result'
76
116
  - `filter` - Filter function to determine which methods to promisify
77
117
  - `promisifier` - Custom function to handle promisification
78
118
  - `multiArgs` (default: false) - Whether to support multiple callback arguments
79
- - `excludeMain` (default: false) - Whether to exclude promisifying the main object/class
80
- - `context` - The context (this) to use when calling methods
81
119
 
82
120
  ## Development
83
121
 
package/lib/aveazul.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
 
3
3
  const xaa = require("xaa");
4
- const { promisify: nodePromisify } = require("node:util");
4
+ const { promisify } = require("./promisify");
5
+ const { promisifyAll } = require("./promisify-all");
6
+ const { Disposer } = require("./disposer");
7
+ const { using } = require("./using");
8
+ const { isPromise, triggerUncaughtException } = require("./util");
5
9
 
6
10
  /**
7
11
  * AveAzul ("Blue Bird" in Spanish) - Extended Promise class that provides Bluebird-like utility methods
@@ -28,8 +32,8 @@ class AveAzul extends Promise {
28
32
  * @returns {Promise} Promise that resolves with the original value
29
33
  */
30
34
  tap(fn) {
31
- return this.then(value => {
32
- fn(value);
35
+ return this.then(async (value) => {
36
+ await fn(value);
33
37
  return value;
34
38
  });
35
39
  }
@@ -41,7 +45,7 @@ class AveAzul extends Promise {
41
45
  * @returns {Promise} Promise that resolves with the filtered array
42
46
  */
43
47
  filter(fn) {
44
- return this.then(value => xaa.filter(value, fn));
48
+ return this.then((value) => xaa.filter(value, fn));
45
49
  }
46
50
 
47
51
  /**
@@ -51,7 +55,7 @@ class AveAzul extends Promise {
51
55
  * @returns {Promise} Promise that resolves with the mapped array
52
56
  */
53
57
  map(fn) {
54
- return this.then(value => xaa.map(value, fn));
58
+ return this.then((value) => xaa.map(value, fn));
55
59
  }
56
60
 
57
61
  /**
@@ -71,7 +75,18 @@ class AveAzul extends Promise {
71
75
  * @returns {Promise} Promise that resolves when iteration is complete
72
76
  */
73
77
  each(fn) {
74
- return this.then(value => xaa.each(value, fn));
78
+ return this.then(async (value) => {
79
+ const result = [];
80
+ for (let i = 0; i < value.length; i++) {
81
+ let x = value[i];
82
+ if (isPromise(x)) {
83
+ x = await x;
84
+ }
85
+ await fn(x, i, value.length);
86
+ result.push(x);
87
+ }
88
+ return result;
89
+ });
75
90
  }
76
91
 
77
92
  /**
@@ -80,7 +95,7 @@ class AveAzul extends Promise {
80
95
  * @returns {Promise} Promise that resolves after the delay
81
96
  */
82
97
  delay(ms) {
83
- return this.then(value => xaa.delay(ms, value));
98
+ return xaa.delay(ms);
84
99
  }
85
100
 
86
101
  /**
@@ -89,43 +104,8 @@ class AveAzul extends Promise {
89
104
  * @param {string} [message] - Optional error message
90
105
  * @returns {Promise} Promise that rejects if timeout occurs
91
106
  */
92
- timeout(ms, message = "Operation timed out") {
93
- return new AveAzul((resolve, reject) => {
94
- const timer = setTimeout(() => {
95
- reject(new Error(message));
96
- }, ms);
97
-
98
- this.then(
99
- value => {
100
- clearTimeout(timer);
101
- resolve(value);
102
- },
103
- err => {
104
- clearTimeout(timer);
105
- reject(err);
106
- }
107
- );
108
- });
109
- }
110
-
111
- /**
112
- * Bluebird-style try() for wrapping sync/async functions
113
- * @param {Function} fn - Function to execute
114
- * @returns {Promise} Promise that resolves with the function's return value
115
- */
116
- try(fn) {
117
- return new AveAzul((resolve, reject) => {
118
- try {
119
- const result = fn();
120
- if (result && typeof result.then === "function") {
121
- result.then(resolve, reject);
122
- } else {
123
- resolve(result);
124
- }
125
- } catch (err) {
126
- reject(err);
127
- }
128
- });
107
+ timeout(ms, message = "operation timed out") {
108
+ return AveAzul.resolve(xaa.timeout(ms, message).run(this));
129
109
  }
130
110
 
131
111
  /**
@@ -133,34 +113,18 @@ class AveAzul extends Promise {
133
113
  * @param {Object} obj - Object with promise values
134
114
  * @returns {Promise} Promise that resolves with an object of resolved values
135
115
  */
136
- props(obj) {
137
- const keys = Object.keys(obj);
138
- const values = keys.map(k => obj[k]);
139
-
140
- return AveAzul.all(values).then(results => {
141
- const resolved = {};
142
- keys.forEach((k, i) => {
143
- resolved[k] = results[i];
116
+ props() {
117
+ return this.then((value) => {
118
+ const keys = Object.keys(value);
119
+ const values = keys.map((k) => value[k]);
120
+
121
+ return AveAzul.all(values).then((results) => {
122
+ const resolved = {};
123
+ keys.forEach((k, i) => {
124
+ resolved[k] = results[i];
125
+ });
126
+ return resolved;
144
127
  });
145
- return resolved;
146
- });
147
- }
148
-
149
- /**
150
- * Bluebird-style catchIf() with predicate matching
151
- * @param {Function|Error} predicate - Error class or predicate function
152
- * @param {Function} fn - Handler function
153
- * @returns {Promise} Promise with conditional catch handler
154
- */
155
- catchIf(predicate, fn) {
156
- return this.catch(err => {
157
- if (
158
- (typeof predicate === "function" && !predicate.prototype && predicate(err)) ||
159
- (typeof predicate === "function" && predicate.prototype && err instanceof predicate)
160
- ) {
161
- return fn(err);
162
- }
163
- throw err;
164
128
  });
165
129
  }
166
130
 
@@ -170,7 +134,7 @@ class AveAzul extends Promise {
170
134
  * @returns {Promise} Promise that maintains the rejection
171
135
  */
172
136
  tapCatch(fn) {
173
- return this.catch(err => {
137
+ return this.catch((err) => {
174
138
  fn(err);
175
139
  throw err;
176
140
  });
@@ -184,7 +148,32 @@ class AveAzul extends Promise {
184
148
  * @returns {Promise} Promise that resolves with the final reduced value
185
149
  */
186
150
  reduce(fn, initialValue) {
187
- return this.then(array => AveAzul.reduce(array, fn, initialValue));
151
+ const hasInitial = arguments.length > 1;
152
+
153
+ return this.then(async (array) => {
154
+ const len = array.length;
155
+ let value;
156
+ let idx;
157
+ if (hasInitial) {
158
+ idx = 0;
159
+ value = initialValue;
160
+ } else {
161
+ idx = 1;
162
+ value = array[0];
163
+ }
164
+
165
+ value = isPromise(value) ? await value : value;
166
+
167
+ for (; idx < len; idx++) {
168
+ let x = array[idx];
169
+ if (isPromise(x)) {
170
+ x = await x;
171
+ }
172
+ value = await fn(value, x, idx, len);
173
+ }
174
+
175
+ return value;
176
+ });
188
177
  }
189
178
 
190
179
  /**
@@ -202,7 +191,9 @@ class AveAzul extends Promise {
202
191
  * @returns {Promise} Promise that rejects with the new reason
203
192
  */
204
193
  catchThrow(reason) {
205
- return this.catch(() => AveAzul.throw(reason));
194
+ return this.catch(() => {
195
+ throw reason;
196
+ });
206
197
  }
207
198
 
208
199
  /**
@@ -216,26 +207,50 @@ class AveAzul extends Promise {
216
207
 
217
208
  /**
218
209
  * Bluebird-style get() for retrieving a property value
219
- * @param {string|number} propertyPath - Path to the property (can be nested using dot notation)
210
+ * @param {string|number} key - Key to retrieve
220
211
  * @returns {Promise} Promise that resolves with the property value
221
212
  */
222
- get(propertyPath) {
223
- return this.then(value => {
224
- if (value == null) {
225
- throw new TypeError("Cannot read property '" + propertyPath + "' of " + value);
226
- }
213
+ get(key) {
214
+ return this.then((value) => value[key]);
215
+ }
227
216
 
228
- let result = value;
229
- const props = String(propertyPath).split(".");
217
+ /**
218
+ * Bluebird-style disposer() for resource cleanup
219
+ * @param {Function} fn - Cleanup function
220
+ * @returns {Disposer} Disposer object
221
+ */
222
+ disposer(fn) {
223
+ if (typeof fn !== "function") {
224
+ throw new TypeError("Expected a function");
225
+ }
226
+
227
+ return new Disposer(fn, this);
228
+ }
229
+
230
+ /**
231
+ * Bluebird-style spread() method for handling array arguments
232
+ * Similar to Bluebird's Promise.prototype.spread()
233
+ * @param {Function} fn - Function to apply to the array arguments
234
+ * @returns {Promise} Promise that resolves with the function's return value
235
+ */
236
+ spread(fn) {
237
+ if (typeof fn !== "function") {
238
+ return AveAzul.reject(
239
+ new TypeError("expecting a function but got " + fn)
240
+ );
241
+ }
230
242
 
231
- for (const prop of props) {
232
- if (result == null) {
233
- throw new TypeError("Cannot read property '" + prop + "' of " + result);
243
+ return this.then(async (args) => {
244
+ if (Array.isArray(args)) {
245
+ for (let i = 0; i < args.length; i++) {
246
+ if (isPromise(args[i])) {
247
+ args[i] = await args[i];
248
+ }
234
249
  }
235
- result = result[prop];
250
+ return fn(...args);
251
+ } else {
252
+ return fn(args);
236
253
  }
237
-
238
- return result;
239
254
  });
240
255
  }
241
256
  }
@@ -270,18 +285,18 @@ AveAzul.map = (value, fn) => AveAzul.resolve(xaa.map(value, fn));
270
285
  * @param {Function} fn - Function to execute
271
286
  * @returns {Promise} Promise that resolves with the function's return value
272
287
  */
273
- AveAzul.try = fn => AveAzul.resolve(xaa.wrap(fn));
288
+ AveAzul.try = (fn) => AveAzul.resolve(xaa.wrap(fn));
274
289
 
275
290
  /**
276
291
  * Bluebird-style props() for object properties
277
292
  * @param {Object} obj - Object with promise values
278
293
  * @returns {Promise} Promise that resolves with an object of resolved values
279
294
  */
280
- AveAzul.props = obj => {
295
+ AveAzul.props = (obj) => {
281
296
  const keys = Object.keys(obj);
282
- const values = keys.map(k => obj[k]);
297
+ const values = keys.map((k) => obj[k]);
283
298
 
284
- return AveAzul.all(values).then(results => {
299
+ return AveAzul.all(values).then((results) => {
285
300
  const resolved = {};
286
301
  keys.forEach((k, i) => {
287
302
  resolved[k] = results[i];
@@ -295,24 +310,7 @@ AveAzul.props = obj => {
295
310
  * @returns {Object} Deferred object with promise, resolve, and reject methods
296
311
  */
297
312
  AveAzul.defer = () => {
298
- const deferred = xaa.makeDefer();
299
- deferred.promise = AveAzul.resolve(deferred.promise);
300
- return deferred;
301
- };
302
-
303
- /**
304
- * Bluebird-style promisify() for converting callback-style functions to promises
305
- * @param {Function} fn - Function to promisify
306
- * @param {Object} [options] - Options object
307
- * @param {Object} [options.context] - `this` context to bind the function to
308
- * @returns {Function} Promisified function that returns an AveAzul promise
309
- */
310
- AveAzul.promisify = (fn, options = {}) => {
311
- const promisified = nodePromisify(fn);
312
- if (options.context) {
313
- return (...args) => AveAzul.resolve(promisified.apply(options.context, args));
314
- }
315
- return (...args) => AveAzul.resolve(promisified(...args));
313
+ return xaa.makeDefer(AveAzul);
316
314
  };
317
315
 
318
316
  /**
@@ -321,7 +319,9 @@ AveAzul.promisify = (fn, options = {}) => {
321
319
  * @param {Function} fn - Iterator function to call for each item
322
320
  * @returns {Promise} Promise that resolves when iteration is complete
323
321
  */
324
- AveAzul.each = (items, fn) => AveAzul.resolve(xaa.each(items, fn));
322
+ AveAzul.each = function (items, fn) {
323
+ return AveAzul.resolve(items).each(fn);
324
+ };
325
325
 
326
326
  /**
327
327
  * Bluebird-style reduce() for array reduction
@@ -330,124 +330,132 @@ AveAzul.each = (items, fn) => AveAzul.resolve(xaa.each(items, fn));
330
330
  * @param {*} [initialValue] - Optional initial value
331
331
  * @returns {Promise} Promise that resolves with the final reduced value
332
332
  */
333
- AveAzul.reduce = (array, fn, initialValue) => {
334
- const hasInitial = arguments.length > 2;
335
- const len = array.length;
336
-
337
- return AveAzul.resolve().then(async () => {
338
- let value = hasInitial ? initialValue : array[0];
339
- const start = hasInitial ? 0 : 1;
340
-
341
- for (let i = start; i < len; i++) {
342
- value = await fn(value, array[i], i, len);
343
- }
333
+ AveAzul.reduce = function (array, ...args) {
334
+ return AveAzul.resolve(array).reduce(...args);
335
+ };
344
336
 
345
- return value;
337
+ /**
338
+ * Bluebird-style promisify() for converting callback-based functions to promises
339
+ * @param {Function} fn - Function to promisify
340
+ * @param {Object} [options] - Options object
341
+ * @returns {Function} Promisified function
342
+ */
343
+ AveAzul.promisify = (fn, options) => {
344
+ return promisify(fn, {
345
+ ...options,
346
+ Promise: AveAzul,
346
347
  });
347
348
  };
348
349
 
349
350
  /**
350
- * Bluebird-style throw() that returns a rejected promise with the given reason
351
- * @param {*} reason - Value to reject the promise with
352
- * @returns {Promise} Promise that rejects with the given reason
351
+ * Bluebird-style promisifyAll() for converting callback-based functions to promises
352
+ * @param {Object} target - Object to promisify
353
+ * @param {Object} [options] - Options object
354
+ * @returns {Object} Object with promisified methods
353
355
  */
354
- AveAzul.throw = reason => AveAzul.reject(reason);
356
+ AveAzul.promisifyAll = (target, options) => {
357
+ return promisifyAll(target, { ...options, Promise: AveAzul });
358
+ };
355
359
 
356
360
  /**
357
- * Bluebird-style promisifyAll() for converting all methods of an object or class to promise-based versions
358
- * Similar to Bluebird's Promise.promisifyAll()
359
- *
360
- * @param {Object|Function} target - The object or class to promisify
361
- * @param {Object} [options] - Configuration options
362
- * @param {string} [options.suffix='Async'] - Suffix to append to promisified method names
363
- * @param {Function} [options.filter] - Filter function to determine which methods to promisify
364
- * @param {Function} [options.promisifier] - Custom function to handle promisification
365
- * @param {boolean} [options.multiArgs=false] - Whether to support multiple callback arguments
366
- * @param {boolean} [options.excludeMain=false] - Whether to exclude promisifying the main object/class
367
- * @param {Object} [options.context] - The context (this) to use when calling methods
368
- * @returns {Object|Function} The promisified object or class
369
- * @throws {TypeError} If target is null, undefined, or not an object/function
370
- *
371
- * @example
372
- * // Promisify an object
373
- * const obj = {
374
- * method(cb) { cb(null, 'result'); }
375
- * };
376
- * AveAzul.promisifyAll(obj);
377
- * const result = await obj.methodAsync();
378
- *
379
- * @example
380
- * // Promisify a class
381
- * class MyClass {
382
- * method(cb) { cb(null, 'result'); }
383
- * }
384
- * AveAzul.promisifyAll(MyClass);
385
- * const instance = new MyClass();
386
- * const result = await instance.methodAsync();
387
- *
388
- * @example
389
- * // With custom options
390
- * const obj = {
391
- * method(cb) { cb(null, 'result1', 'result2'); }
392
- * };
393
- * AveAzul.promisifyAll(obj, {
394
- * suffix: 'Promise',
395
- * multiArgs: true,
396
- * filter: (name) => name === 'method'
397
- * });
398
- * const [result1, result2] = await obj.methodPromise();
361
+ * Bluebird-style method() for creating a method that returns a promise
362
+ * @param {Function} fn - Function to create a method for
363
+ * @returns {Function} Method function that returns a promise
399
364
  */
400
- AveAzul.promisifyAll = (target, options = {}) => {
401
- const {
402
- suffix = 'Async',
403
- filter = (name, func, targetObj, passedOptions) => {
404
- return (
405
- typeof func === 'function' &&
406
- !func.name.startsWith('_') &&
407
- !func.name.startsWith('promisify') &&
408
- !func.name.startsWith('promisifyAll')
409
- );
410
- },
411
- promisifier = (fn, context, multiArgs) => {
412
- if (multiArgs) {
413
- return (...args) => {
414
- return new AveAzul((resolve, reject) => {
415
- args.push((err, ...results) => {
416
- if (err) reject(err);
417
- else resolve(results);
418
- });
419
- fn.apply(context, args);
420
- });
421
- };
365
+ AveAzul.method = (fn) => {
366
+ return function (...args) {
367
+ return new AveAzul((resolve, reject) => {
368
+ try {
369
+ const result = fn.call(this, ...args);
370
+ resolve(result);
371
+ } catch (error) {
372
+ reject(error);
422
373
  }
423
- return AveAzul.promisify(fn, { context });
424
- },
425
- multiArgs = false,
426
- excludeMain = false,
427
- context = target
428
- } = options;
429
-
430
- if (target == null || (typeof target !== 'object' && typeof target !== 'function')) {
431
- throw new TypeError('target must be an object');
432
- }
374
+ });
375
+ };
376
+ };
433
377
 
434
- const targetObj = target.prototype || target;
435
- const keys = Object.getOwnPropertyNames(targetObj);
378
+ /**
379
+ * Bluebird-style using() for resource management. There is only a static version of this method.
380
+ * After the handler finish and returns, regardless of whether it resolves or rejects, the resources will be disposed.
381
+ *
382
+ * @param {Disposer|Array<Disposer>} resources - Resource disposers, either an array of disposers or a variadic argument list
383
+ * @param {Function} handler - Handler function that will receive the resources as arguments
384
+ * @returns {Promise} Promise that resolves with handler result
385
+ */
386
+ AveAzul.using = (resources, ...args) => {
387
+ if (args.length === 0) {
388
+ throw new TypeError("resrouces and handler function required");
389
+ }
436
390
 
437
- for (const key of keys) {
438
- const func = targetObj[key];
439
- if (filter(key, func, targetObj, options)) {
440
- const promisifiedKey = key + suffix;
441
- targetObj[promisifiedKey] = promisifier(func, context, multiArgs);
391
+ if (Array.isArray(resources)) {
392
+ if (args.length > 1) {
393
+ throw new TypeError(
394
+ "only two arguments are allowed when passing an array of resources"
395
+ );
442
396
  }
397
+ return using(resources, args[0], AveAzul, true);
443
398
  }
399
+ const handler = args.pop();
400
+ return using([resources, ...args], handler, AveAzul, false);
401
+ };
444
402
 
445
- if (!excludeMain && typeof target === 'function') {
446
- target.promisify = AveAzul.promisify;
447
- target.promisifyAll = AveAzul.promisifyAll;
403
+ /**
404
+ * Bluebird-style join() for joining promises
405
+ *
406
+ * @param {...Promise} args - Promises to join
407
+ * @param {Function} handler - Handler function to apply to the joined results
408
+ * @returns {Promise} Promise that resolves with the handler's return value
409
+ */
410
+ AveAzul.join = function (...args) {
411
+ if (args.length > 1 && typeof args.at(-1) === "function") {
412
+ const handler = args.pop();
413
+ return AveAzul.all(args).then((results) => handler(...results));
414
+ } else {
415
+ return AveAzul.all(args);
448
416
  }
449
-
450
- return target;
451
417
  };
452
418
 
419
+ function fromCallback(fn, options) {
420
+ return new AveAzul((resolve, reject) => {
421
+ try {
422
+ fn((err, ...args) => {
423
+ if (err) {
424
+ reject(err);
425
+ } else {
426
+ if (options && options.multiArgs) {
427
+ resolve(args);
428
+ } else {
429
+ resolve(args[0]);
430
+ }
431
+ }
432
+ });
433
+ } catch (err) {
434
+ reject(err);
435
+ }
436
+ });
437
+ }
438
+
439
+ AveAzul.fromNode = fromCallback;
440
+
441
+ /**
442
+ * Bluebird-style fromCallback() for converting callback-based functions to promises
443
+ * @param {Function} fn - Function to convert
444
+ * @param {Object} [options] - Options object
445
+ * @returns {Promise} Promise that resolves with the function's return value
446
+ */
447
+ AveAzul.fromCallback = fromCallback;
448
+
449
+ /**
450
+ * @description
451
+ * When fatal error and AveAzul needs to crash the process,
452
+ * this method is used to throw the error.
453
+ *
454
+ * @param {Error} error - The error to throw.
455
+ */
456
+ AveAzul.___throwUncaughtError = triggerUncaughtException;
457
+
453
458
  module.exports = AveAzul;
459
+
460
+ const { setupNotImplemented } = require("./not-implemented");
461
+ setupNotImplemented(AveAzul);
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Disposer class for resource cleanup
5
+ * @private
6
+ */
7
+ class Disposer {
8
+ constructor(fn, promise) {
9
+ this._data = fn; // The cleanup function
10
+ this._promise = promise; // The promise that resolves to the resource
11
+ }
12
+ }
13
+
14
+ module.exports.Disposer = Disposer;
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+
3
+ function createNotImplemented(name) {
4
+ return function () {
5
+ const msg = name + " Not implemented in aveazul";
6
+ console.error(msg);
7
+ throw new Error(msg);
8
+ };
9
+ }
10
+
11
+ function createInstanceNotImplemented(AveAzul) {
12
+ const methods = [
13
+ "spread",
14
+ "error",
15
+ "bind",
16
+ "join",
17
+ "try",
18
+ "method",
19
+ "isFulfilled",
20
+ "isRejected",
21
+ "isPending",
22
+ "value",
23
+ "reason",
24
+ "props",
25
+ "any",
26
+ "some",
27
+ "map",
28
+ "reduce",
29
+ "filter",
30
+ "each",
31
+ "mapSeries",
32
+ "tap",
33
+ "tapCatch",
34
+ "catchThrow",
35
+ "catchReturn",
36
+ "get",
37
+ "throw",
38
+ "call",
39
+ ];
40
+
41
+ const proto = AveAzul.prototype;
42
+ const ret = [];
43
+ for (const method of methods) {
44
+ if (!proto[method]) {
45
+ ret.push(method);
46
+ proto[method] = createNotImplemented("instance " + method);
47
+ }
48
+ }
49
+ return ret;
50
+ }
51
+
52
+ function createStaticNotImplemented(AveAzul) {
53
+ const methods = [
54
+ "join",
55
+ "try",
56
+ "method",
57
+ "resolve",
58
+ "reject",
59
+ "all",
60
+ "props",
61
+ "any",
62
+ "some",
63
+ "map",
64
+ "filter",
65
+ "each",
66
+ "mapSeries",
67
+ "race",
68
+ "promisify",
69
+ "promisifyAll",
70
+ "fromNode",
71
+ "fromCallback",
72
+ "delay",
73
+ "coroutine",
74
+ "config",
75
+ ];
76
+ const ret = [];
77
+ for (const method of methods) {
78
+ if (!AveAzul[method]) {
79
+ ret.push(method);
80
+ AveAzul[method] = createNotImplemented("static " + method);
81
+ }
82
+ }
83
+ return ret;
84
+ }
85
+
86
+ function setupNotImplemented(AveAzul) {
87
+ const instance = createInstanceNotImplemented(AveAzul);
88
+ const staticMethods = createStaticNotImplemented(AveAzul);
89
+ AveAzul.__notImplementedInstance = instance;
90
+ AveAzul.__notImplementedStatic = staticMethods;
91
+ }
92
+
93
+ module.exports = {
94
+ createInstanceNotImplemented,
95
+ createStaticNotImplemented,
96
+ setupNotImplemented,
97
+ };
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+
3
+ const { promisify } = require("./promisify");
4
+ const {
5
+ isIdentifier,
6
+ isClass,
7
+ isConstructor,
8
+ isPromisified,
9
+ getObjectKeys,
10
+ isExcludedPrototype,
11
+ } = require("./util");
12
+
13
+ const defaultSuffix = "Async";
14
+
15
+ const defaultFilter = function (name) {
16
+ return isIdentifier(name) && name.charAt(0) !== "_" && name !== "constructor";
17
+ };
18
+
19
+ const defaultPromisifier = (fn, _defaultPromisifier, options) => {
20
+ return promisify(fn, {
21
+ ...options,
22
+ copyProps: false,
23
+ });
24
+ };
25
+
26
+ const excludedClasses = [Array, Object, Function];
27
+
28
+ // Helper function to determine if a class extends from any excluded class
29
+ function isExcludedClass(obj) {
30
+ if (excludedClasses.includes(obj)) {
31
+ return true;
32
+ }
33
+
34
+ // Check if obj extends from any excluded class using instanceof
35
+ if (typeof obj === "function" && obj.prototype) {
36
+ // Check if prototype is instance of any excluded class
37
+ for (const excludedClass of excludedClasses) {
38
+ if (obj.prototype instanceof excludedClass) {
39
+ return true;
40
+ }
41
+ }
42
+ }
43
+
44
+ return false;
45
+ }
46
+
47
+ function promisifyAll2(obj, options) {
48
+ if (isExcludedClass(obj)) {
49
+ return;
50
+ }
51
+
52
+ const allKeys = getObjectKeys(obj);
53
+
54
+ for (const key of allKeys) {
55
+ const value = obj[key];
56
+ const promisifiedKey = key + options.suffix;
57
+ const passesDefaultFilter =
58
+ options.filter === defaultFilter ? true : defaultFilter(key, value, obj);
59
+ if (
60
+ isConstructor(value) ||
61
+ typeof value !== "function" ||
62
+ isPromisified(value) ||
63
+ obj[promisifiedKey] ||
64
+ !options.filter(key, value, obj, passesDefaultFilter)
65
+ ) {
66
+ continue;
67
+ }
68
+
69
+ if (key.endsWith(options.suffix)) {
70
+ throw new TypeError(
71
+ "Cannot promisify an API that has normal methods with '%s'-suffix\u000a\u000a See http://goo.gl/MqrFmX\u000a".replace(
72
+ "%s",
73
+ options.suffix
74
+ )
75
+ );
76
+ }
77
+
78
+ obj[promisifiedKey] = options.promisifier(value, defaultPromisifier, {
79
+ context: obj,
80
+ copyProps: false,
81
+ multiArgs: options.multiArgs,
82
+ Promise: options.Promise,
83
+ });
84
+ }
85
+ }
86
+
87
+ function promisifyAll(target, _options) {
88
+ if (typeof target !== "function" && typeof target !== "object") {
89
+ throw new TypeError(
90
+ "the target of promisifyAll must be an object or a function"
91
+ );
92
+ }
93
+
94
+ const options = {
95
+ suffix: defaultSuffix,
96
+ filter: defaultFilter,
97
+ promisifier: defaultPromisifier,
98
+ Promise: global.Promise,
99
+ ..._options,
100
+ };
101
+
102
+ const suffix = options.suffix;
103
+
104
+ if (!isIdentifier(suffix)) {
105
+ throw new RangeError(
106
+ "suffix must be a valid identifier\u000a\u000a See http://goo.gl/MqrFmX\u000a"
107
+ );
108
+ }
109
+
110
+ const allKeys = getObjectKeys(target);
111
+
112
+ for (const key of allKeys) {
113
+ const value = target[key];
114
+ if (
115
+ value &&
116
+ key !== "constructor" &&
117
+ !key.startsWith("_") &&
118
+ isClass(value)
119
+ ) {
120
+ const proto = Object.getPrototypeOf(value);
121
+ if (!isExcludedPrototype(proto)) {
122
+ promisifyAll2(proto, options);
123
+ }
124
+
125
+ promisifyAll2(value, options);
126
+ }
127
+ }
128
+
129
+ promisifyAll2(target, options);
130
+ }
131
+
132
+ module.exports.promisifyAll = promisifyAll;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ const { copyOwnProperties, isPromisified } = require("./util");
4
+
5
+ module.exports.promisify = function promisify(fn, _options) {
6
+ if (typeof fn !== "function") {
7
+ throw new TypeError("expecting a function but got " + {}.toString.call(fn));
8
+ }
9
+
10
+ if (isPromisified(fn)) {
11
+ return fn;
12
+ }
13
+
14
+ const options = {
15
+ Promise: global.Promise,
16
+ multiArgs: false,
17
+ copyProps: true,
18
+ suffix: "",
19
+ ..._options,
20
+ };
21
+
22
+ const Promise = options.Promise;
23
+ const multiArgs = !!options.multiArgs;
24
+
25
+ const promisifiedFn = (...args) => {
26
+ return new Promise((resolve, reject) => {
27
+ // add a callback to the end of the arguments to transfer the result to the promise
28
+ args.push((err, ...values) => {
29
+ if (err) {
30
+ return reject(err);
31
+ }
32
+ if (multiArgs) {
33
+ resolve(values);
34
+ } else {
35
+ resolve(values[0]);
36
+ }
37
+ });
38
+
39
+ // call the original function with the updated args
40
+ fn.call(options.context, ...args);
41
+ });
42
+ };
43
+
44
+ if (options.copyProps) {
45
+ copyOwnProperties(fn, promisifiedFn);
46
+ }
47
+
48
+ Object.defineProperty(promisifiedFn, "__isPromisified__", {
49
+ value: true,
50
+ writable: false,
51
+ enumerable: false,
52
+ configurable: true,
53
+ });
54
+
55
+ Object.defineProperty(promisifiedFn, "length", {
56
+ value: fn.length,
57
+ writable: false,
58
+ enumerable: false,
59
+ configurable: false,
60
+ });
61
+
62
+ Object.defineProperty(promisifiedFn, "name", {
63
+ value: fn.name + options.suffix,
64
+ writable: false,
65
+ enumerable: false,
66
+ configurable: false,
67
+ });
68
+
69
+ return promisifiedFn;
70
+ };
package/lib/using.js ADDED
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+
3
+ const { Disposer } = require("./disposer");
4
+ const { isPromise } = require("./util");
5
+
6
+ const SYM_FN_DISPOSE = Symbol("fnDispose");
7
+ /**
8
+ * @description
9
+ * The using function is a utility function that allows you to acquire resources,
10
+ * process them, and then dispose of them in an error-safe manner.
11
+ *
12
+ * @param {Array} resources - An array of resources to acquire.
13
+ * @param {Function} handler - A function that will be called with the acquired resources.
14
+ * @param {Promise} Promise - The Promise implementation to use. AveAzul or Bluebird.
15
+ * @param {boolean} asArray - Whether to return the result as an array.
16
+ * @returns {Promise} A promise that resolves to the result of the handler function.
17
+ */
18
+ function using(resources, handler, Promise, asArray) {
19
+ if (typeof handler !== "function") {
20
+ throw new TypeError("handler must be a function");
21
+ }
22
+
23
+ // resources is guaranateed to be an array of disposer, promise like, or any value
24
+ // first process all resources by mapping the resources array:
25
+ // 1. if it's a disposer, get its promise and resolve its value
26
+ // 2. if it's a promise like, get its value
27
+ // 3. otherwise, return the value
28
+ // Expect Promise to be AveAzul or Bluebird that has map method
29
+ const acquisitionErrors = [];
30
+
31
+ const acquireResources = () => {
32
+ const promiseRes = resources.map((resource) => {
33
+ // if it's a promise-like, wait for its resolved value
34
+ if (isPromise(resource)) {
35
+ return { ___promise: resource };
36
+ }
37
+ return resource;
38
+ });
39
+
40
+ return Promise.map(promiseRes, async (resource) => {
41
+ if (
42
+ resource &&
43
+ (resource instanceof Disposer ||
44
+ (resource._promise && typeof resource._data === "function"))
45
+ ) {
46
+ try {
47
+ const res = await resource._promise;
48
+ resource._result = res;
49
+ resource[SYM_FN_DISPOSE] = resource._data;
50
+ } catch (error) {
51
+ acquisitionErrors.push(error);
52
+ resource._error = error;
53
+ }
54
+ return resource;
55
+ }
56
+
57
+ // if it's a promise like, wait for its resolved value
58
+ if (resource && resource.___promise) {
59
+ try {
60
+ const res = await resource.___promise;
61
+ resource._result = res;
62
+ } catch (error) {
63
+ acquisitionErrors.push(error);
64
+ resource._error = error;
65
+ }
66
+ return resource;
67
+ }
68
+
69
+ return { _result: resource };
70
+ });
71
+ };
72
+
73
+ const disposeResources = (processedResources) => {
74
+ const errors = [];
75
+ return Promise.each(processedResources, async (resource) => {
76
+ // dispose all resources that were acquired without errors
77
+ if (resource && resource[SYM_FN_DISPOSE]) {
78
+ try {
79
+ await resource[SYM_FN_DISPOSE](resource._result);
80
+ } catch (error) {
81
+ errors.push(error);
82
+ }
83
+ }
84
+ }).finally(() => {
85
+ if (errors.length > 0) {
86
+ Promise.___throwUncaughtError(
87
+ new AggregateError(errors, "cleanup resources failed", errors)
88
+ );
89
+ }
90
+ });
91
+ };
92
+
93
+ return acquireResources().then((processedResources) => {
94
+ if (acquisitionErrors.length > 0) {
95
+ return disposeResources(processedResources).tap(() => {
96
+ throw acquisitionErrors[0];
97
+ });
98
+ }
99
+
100
+ // now collect all the results into an array
101
+ const results = [];
102
+ for (const resource of processedResources) {
103
+ results.push(resource._result);
104
+ }
105
+
106
+ let handlerPromise;
107
+
108
+ try {
109
+ // now call the handler with the results
110
+ handlerPromise = Promise.resolve(
111
+ asArray ? handler(results) : handler(...results)
112
+ );
113
+ } catch (error) {
114
+ // catch sync error from handler
115
+ handlerPromise = Promise.reject(error);
116
+ }
117
+
118
+ return handlerPromise
119
+ .tap(() => {
120
+ return disposeResources(processedResources);
121
+ })
122
+ .tapCatch(() => {
123
+ return disposeResources(processedResources);
124
+ });
125
+ });
126
+ }
127
+
128
+ module.exports.using = using;
package/lib/util.js ADDED
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Determines if a function is a class (either ES6 class or ES5 constructor function)
5
+ * This function performs several checks to identify different class patterns:
6
+ * 1. ES6 classes with the 'class' keyword
7
+ * 2. Constructor functions (ES5 classes) with prototype methods
8
+ *
9
+ * @param {*} fn - The value to check
10
+ * @returns {boolean} - True if the function is a class, false otherwise
11
+ */
12
+ const thisAssignmentPattern = /this\s*\.\s*\S+\s*=/;
13
+ function isClass(fn) {
14
+ try {
15
+ if (typeof fn === "function") {
16
+ const keys = Object.getOwnPropertyNames(fn.prototype);
17
+
18
+ const fnStr = fn.toString();
19
+ const es6Class = fnStr.startsWith("class ") || /^class\s+/.test(fnStr);
20
+ const hasMethods = keys.length > 1;
21
+ const hasMethodsOtherThanConstructor =
22
+ keys.length > 0 && !(keys.length === 1 && keys[0] === "constructor");
23
+ const hasThisAssignmentAndStaticMethods =
24
+ thisAssignmentPattern.test(fnStr) &&
25
+ Object.getOwnPropertyNames(fn).length > 0;
26
+
27
+ if (
28
+ es6Class ||
29
+ hasMethods ||
30
+ hasMethodsOtherThanConstructor ||
31
+ hasThisAssignmentAndStaticMethods
32
+ ) {
33
+ return true;
34
+ }
35
+ }
36
+ return false;
37
+ } catch (e) {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ const rident = /^[a-z$_][a-z$_0-9]*$/i;
43
+ function isIdentifier(str) {
44
+ return rident.test(str);
45
+ }
46
+
47
+ function isConstructor(func) {
48
+ if (!func) {
49
+ return false;
50
+ }
51
+ const proto = func.prototype;
52
+ return (
53
+ !!proto &&
54
+ !!proto.constructor &&
55
+ !!proto.constructor.name &&
56
+ proto.constructor.name === func.name
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Prop filtering code copied from bluebird/js/release
62
+ */
63
+ const noCopyProps = [
64
+ "arity",
65
+ "length",
66
+ "name",
67
+ "arguments",
68
+ "caller",
69
+ "callee",
70
+ "prototype",
71
+ "__isPromisified__",
72
+ ];
73
+ const noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$");
74
+
75
+ function propsFilter(key) {
76
+ return !noCopyPropsPattern.test(key);
77
+ }
78
+
79
+ function copyOwnProperties(source, target, filter = propsFilter) {
80
+ const names = Object.getOwnPropertyNames(source);
81
+
82
+ for (const name of names) {
83
+ if (filter(name)) {
84
+ Object.defineProperty(
85
+ target,
86
+ name,
87
+ Object.getOwnPropertyDescriptor(source, name)
88
+ );
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Copied from bluebird/js/release/util.js
95
+ * @param {*} fn
96
+ * @returns {boolean}
97
+ */
98
+ function isPromisified(fn) {
99
+ try {
100
+ return fn.__isPromisified__ === true;
101
+ } catch (e) {
102
+ return false;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Determines if an object is a Promise instance
108
+ * @param {*} obj - The object to check
109
+ * @returns {boolean} - True if the object is a Promise instance, false otherwise
110
+ */
111
+ function isPromise(obj) {
112
+ return (
113
+ obj instanceof Promise ||
114
+ (obj != null &&
115
+ typeof obj === "object" &&
116
+ typeof obj.then === "function" &&
117
+ typeof obj.catch === "function")
118
+ );
119
+ }
120
+
121
+ // istanbul ignore next
122
+ const emptyFatArrow = () => {};
123
+ // istanbul ignore next
124
+ const emptyFunction = function () {};
125
+
126
+ const defaultExcluded = [
127
+ Object.getPrototypeOf(Array), // Array.prototype
128
+ Object.getPrototypeOf(Object), // Object.prototype
129
+ Object.getPrototypeOf(Function), // Function.prototype
130
+ Object.getPrototypeOf([]),
131
+ Object.getPrototypeOf({}),
132
+ Object.getPrototypeOf(emptyFatArrow),
133
+ Object.getPrototypeOf(emptyFunction),
134
+ ];
135
+
136
+ function isExcludedPrototype(proto) {
137
+ return defaultExcluded.includes(proto);
138
+ }
139
+
140
+ /**
141
+ * Gets all property keys from an object and its prototype chain, excluding standard
142
+ * prototypes like Object.prototype, Array.prototype, and Function.prototype
143
+ *
144
+ * @param {Object} target - The target object to get keys from
145
+ * @param {Array} [excludedPrototypes=[]] - An array of prototype objects to exclude keys from
146
+ * @returns {Array<string>} - Array of property keys
147
+ */
148
+ function getObjectKeys(target, excludedPrototypes = []) {
149
+ const excluded =
150
+ excludedPrototypes.length > 0 ? excludedPrototypes : defaultExcluded;
151
+
152
+ // Get own properties
153
+ const ownKeys = Object.getOwnPropertyNames(target);
154
+
155
+ // Get prototype properties, excluding those from excluded prototypes
156
+ let protoKeys = [];
157
+ let currentProto = Object.getPrototypeOf(target);
158
+
159
+ // Walk up the prototype chain until we hit null or an excluded prototype
160
+ while (currentProto && !excluded.includes(currentProto)) {
161
+ protoKeys = [...protoKeys, ...Object.getOwnPropertyNames(currentProto)];
162
+ currentProto = Object.getPrototypeOf(currentProto);
163
+ }
164
+
165
+ // Combine own properties and prototype properties
166
+ return [...protoKeys, ...ownKeys];
167
+ }
168
+
169
+ /**
170
+ * Triggers an uncaught exception in a safe way by scheduling it on the next event loop tick
171
+ * This is used for fatal errors that should crash the process
172
+ * @param {Error} error - The error to throw
173
+ */
174
+ function triggerUncaughtException(error) {
175
+ if (!(error instanceof Error)) {
176
+ error = new Error(String(error));
177
+ }
178
+
179
+ // Use setTimeout with 0ms delay to throw on the next event loop tick
180
+ // This ensures the current execution context completes first
181
+ setTimeout(() => {
182
+ throw error;
183
+ }, 0);
184
+ }
185
+
186
+ module.exports.copyOwnProperties = copyOwnProperties;
187
+ module.exports.isClass = isClass;
188
+ module.exports.isIdentifier = isIdentifier;
189
+ module.exports.isConstructor = isConstructor;
190
+ module.exports.isPromisified = isPromisified;
191
+ module.exports.isPromise = isPromise;
192
+ module.exports.triggerUncaughtException = triggerUncaughtException;
193
+ module.exports.getObjectKeys = getObjectKeys;
194
+ module.exports.isExcludedPrototype = isExcludedPrototype;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aveazul",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Bluebird-like APIs in extended native Promise",
5
5
  "main": "lib/aveazul.js",
6
6
  "homepage": "https://github.com/jchip/aveazul",
@@ -8,7 +8,12 @@
8
8
  "scripts": {
9
9
  "test": "jest test",
10
10
  "test:watch": "jest test --watch",
11
- "test:coverage": "jest test --coverage"
11
+ "test:coverage": "jest test --coverage",
12
+ "test:bluebird": "USE_BLUEBIRD=true jest test",
13
+ "test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --no-coverage",
14
+ "test:debug:bluebird": "USE_BLUEBIRD=true node --inspect-brk node_modules/.bin/jest --runInBand --no-coverage",
15
+ "jest": "jest --no-coverage",
16
+ "jest:bluebird": "USE_BLUEBIRD=true jest --no-coverage"
12
17
  },
13
18
  "author": "Joel Chen",
14
19
  "files": [
@@ -28,6 +33,8 @@
28
33
  "xaa": "^1.7.3"
29
34
  },
30
35
  "devDependencies": {
31
- "jest": "^29.7.0"
36
+ "bluebird": "^3.7.2",
37
+ "jest": "^29.7.0",
38
+ "rimraf": "^3.0.1"
32
39
  }
33
40
  }