aveazul 0.1.5 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,22 @@
1
- # AveAzul
1
+ # AveAzul.js
2
2
 
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)).
3
+ AveAzul ("Blue Bird" in Spanish) serves as a near drop-in replacement for Bluebird. It's built on native Promise, by extending it with the familiar utility methods from 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.
5
+ ## Purpose
6
6
 
7
- Do you prefer Bluebird's API with native Promises? AveAzul gives you both - familiar Bluebird methods built on native Promise.
7
+ The primary goal is to help migrate legacy code, that uses Bluebird APIs extensively, to native Promises. While not a 100% drop-in replacement (some aspects of Bluebird simply can't be replicated), it offers a practical migration path with minimal changes for most cases.
8
+
9
+ Further, if you like Bluebird's API but want to use native Promises, AveAzul gives you both - familiar Bluebird methods built on native Promise.
10
+
11
+ ## Features
12
+
13
+ - Built on native Promises
14
+ - Implements most commonly used Bluebird methods
15
+ - Comprehensive test suite ensuring compatibility
16
+
17
+ ## Requirements
18
+
19
+ - node.js version >= 12
8
20
 
9
21
  ## Installation
10
22
 
@@ -27,6 +39,26 @@ AveAzul.resolve([1, 2, 3])
27
39
  .filter((x) => x > 2)
28
40
  .then((result) => console.log(result)); // [4, 6]
29
41
 
42
+ // Wait for at least 2 promises to be fulfilled
43
+ const fetchUrls = [
44
+ fetch("https://api.example.com/data1"),
45
+ fetch("https://api.example.com/data2"),
46
+ fetch("https://api.example.com/data3"),
47
+ fetch("https://api.example.com/data4"),
48
+ ];
49
+ AveAzul.some(fetchUrls, 2).then((results) =>
50
+ console.log(`Got the first 2 successful results`)
51
+ );
52
+
53
+ // Process items sequentially with mapSeries
54
+ AveAzul.resolve([1, 2, 3])
55
+ .mapSeries(async (x) => {
56
+ // Each item is processed only after the previous one completes
57
+ await new Promise((resolve) => setTimeout(resolve, 100));
58
+ return x * 2;
59
+ })
60
+ .then((result) => console.log(result)); // [2, 4, 6]
61
+
30
62
  // Promisify callback-style functions
31
63
  const fs = require("fs");
32
64
  const readFile = AveAzul.promisify(fs.readFile);
@@ -71,6 +103,24 @@ AveAzul.all([getUser(1), getPosts(1), getComments(1)]).spread(
71
103
  );
72
104
  ```
73
105
 
106
+ ## Migration from Bluebird
107
+
108
+ For most applications, migrating from Bluebird to AveAzul should be as simple as:
109
+
110
+ ```javascript
111
+ // From
112
+ const Promise = require("bluebird");
113
+
114
+ // To
115
+ const Promise = require("aveazul");
116
+ ```
117
+
118
+ Key differences to be aware of:
119
+
120
+ - Some advanced debugging features are not available
121
+ - Performance characteristics may differ
122
+ - A few very specialized methods not available
123
+
74
124
  ## API
75
125
 
76
126
  ### Instance Methods
@@ -78,6 +128,7 @@ AveAzul.all([getUser(1), getPosts(1), getComments(1)]).spread(
78
128
  - `tap(fn)` - Execute side effects and return original value
79
129
  - `filter(fn)` - Filter array elements
80
130
  - `map(fn)` - Transform array elements
131
+ - `mapSeries(fn)` - Transform array elements sequentially
81
132
  - `return(value)` - Inject a new value
82
133
  - `each(fn)` - Iterate over array elements
83
134
  - `delay(ms)` - Delay resolution
@@ -86,16 +137,23 @@ AveAzul.all([getUser(1), getPosts(1), getComments(1)]).spread(
86
137
  - `spread(fn)` - Apply array values as arguments to function
87
138
  - `tapCatch(fn)` - Execute side effects on rejection
88
139
  - `reduce(fn, initialValue?)` - Reduce array elements
140
+ - `some(count)` - Resolves when a specified number of promises in the array have resolved
89
141
  - `throw(reason)` - Return rejected promise
90
142
  - `catchThrow(reason)` - Catch and throw new error
91
143
  - `catchReturn(value)` - Catch and return value
92
144
  - `get(propertyPath)` - Retrieve property value
93
145
  - `disposer(fn)` - Create a disposer for use with AveAzul.using() for resource cleanup
146
+ - `any()` - Resolves when any promise in the iterable resolves, rejecting if all reject
147
+ - `all()` - Like Promise.all(), resolves when all promises resolve, rejects if any reject
148
+ - `call(propertyName, ...args)` - Call a method on the resolved value with the provided arguments
149
+ - `asCallback(callback, options?)` - Register a Node-style callback that handles the resolution or rejection
150
+ - `error(handler)` - Like catch(), but only catches operational errors, letting programmer errors bubble up
94
151
 
95
152
  ### Static Methods
96
153
 
97
154
  - `delay(ms, value?)` - Resolve after specified time
98
155
  - `map(value, fn)` - Transform array elements
156
+ - `mapSeries(value, fn)` - Transform array elements one at a time in sequence
99
157
  - `try(fn)` - Wrap sync/async functions
100
158
  - `props(obj)` - Resolve object properties
101
159
  - `defer()` - Create a deferred promise
@@ -104,12 +162,17 @@ AveAzul.all([getUser(1), getPosts(1), getComments(1)]).spread(
104
162
  - `fromCallback(fn, options?)` - Alias for fromNode
105
163
  - `each(items, fn)` - Iterate over array elements
106
164
  - `reduce(array, fn, initialValue?)` - Reduce array elements
165
+ - `some(promises, count)` - Wait for a specified number of promises to be fulfilled
107
166
  - `method(fn)` - Creates a method that returns a promise resolving to the value returned by the original function
108
167
  - `throw(reason)` - Return rejected promise
109
168
  - `promisifyAll(target, options?)` - Convert all methods of an object/class to promises
110
169
  - `using(resources, fn)` - Manage resources with automatic cleanup
111
170
  - `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
112
171
 
172
+ ### Error Types
173
+
174
+ - `AveAzul.OperationalError` - Error type for representing expected operational errors (network failures, validation errors, etc.)
175
+
113
176
  ### PromisifyAll Options
114
177
 
115
178
  - `suffix` (default: 'Async') - Suffix to append to promisified method names
@@ -127,12 +190,19 @@ npm install
127
190
  npm test
128
191
  npm run test:watch
129
192
  npm run test:coverage
193
+
194
+ # Test against Bluebird for compatibility
195
+ npm run jest:bluebird -- test/[name].test.js
130
196
  ```
131
197
 
198
+ ## Contributing
199
+
200
+ Contributions are welcome! Please feel free to submit a Pull Request.
201
+
132
202
  ## License
133
203
 
134
204
  Apache-2.0
135
205
 
136
206
  ## Author
137
207
 
138
- Joel Chen
208
+ Joel Chen, with assistant from Cursor Claude-3.7-sonnet
package/lib/any.js ADDED
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+
3
+ const { toArray, isPromise } = require("./util");
4
+ const { AggregateError } = require("@jchip/error");
5
+
6
+ function addStaticAny(AveAzul, force = false) {
7
+ if (force || !AveAzul.any) {
8
+ AveAzul.any = function (args) {
9
+ try {
10
+ args = toArray(args);
11
+ } catch (error) {
12
+ return AveAzul.reject(error);
13
+ }
14
+
15
+ if (args.length === 0) {
16
+ return AveAzul.reject(
17
+ new RangeError(
18
+ "Input array must contain at least 1 items but contains only 0 items"
19
+ )
20
+ );
21
+ }
22
+
23
+ return new AveAzul((resolve, reject) => {
24
+ const len = args.length;
25
+ let settled = false;
26
+ const errors = [];
27
+
28
+ const doFinish = (value) => {
29
+ if (settled) return;
30
+ settled = true;
31
+ resolve(value);
32
+ };
33
+
34
+ const addError = (err) => {
35
+ errors.push(err);
36
+ if (!settled && errors.length >= len) {
37
+ settled = true;
38
+ reject(new AggregateError(errors));
39
+ }
40
+ };
41
+
42
+ for (let i = 0; i < len; i++) {
43
+ const arg = args[i];
44
+ if (isPromise(arg)) {
45
+ arg.then(doFinish, addError);
46
+ } else {
47
+ doFinish(arg);
48
+ }
49
+ }
50
+ });
51
+ };
52
+ }
53
+ }
54
+
55
+ module.exports.addStaticAny = addStaticAny;
package/lib/aveazul.js CHANGED
@@ -5,10 +5,12 @@ const { promisify } = require("./promisify");
5
5
  const { promisifyAll } = require("./promisify-all");
6
6
  const { Disposer } = require("./disposer");
7
7
  const { using } = require("./using");
8
- const { isPromise, triggerUncaughtException } = require("./util");
9
-
8
+ const { isPromise, triggerUncaughtException, toArray } = require("./util");
9
+ const { AggregateError } = require("@jchip/error");
10
+ const { OperationalError, isOperationalError } = require("./operational-error");
10
11
  /**
11
- * AveAzul ("Blue Bird" in Spanish) - Extended Promise class that provides Bluebird-like utility methods
12
+ * @fileoverview
13
+ * AveAzul ("Blue Bird" in Spanish) - Extended Promise class that provides Bluebird like utility methods
12
14
  * This implementation is inspired by and provides similar APIs to the Bluebird Promise library,
13
15
  * but built on top of native Promises. The name is a Spanish play on words referencing Bluebird.
14
16
  * @extends Promise
@@ -54,8 +56,18 @@ class AveAzul extends Promise {
54
56
  * @param {Function} fn - Map function to apply to each element
55
57
  * @returns {Promise} Promise that resolves with the mapped array
56
58
  */
57
- map(fn) {
58
- return this.then((value) => xaa.map(value, fn));
59
+ map(fn, options = { concurrency: 50 }) {
60
+ return this.then((value) => xaa.map(value, fn, options));
61
+ }
62
+
63
+ /**
64
+ * Bluebird-style mapSeries() method for array operations
65
+ * Similar to Bluebird's Promise.prototype.mapSeries()
66
+ * @param {Function} fn - Map function to apply to each element
67
+ * @returns {Promise} Promise that resolves with the mapped array
68
+ */
69
+ mapSeries(fn) {
70
+ return this.map(fn, { concurrency: 1 });
59
71
  }
60
72
 
61
73
  /**
@@ -68,6 +80,17 @@ class AveAzul extends Promise {
68
80
  return this.then(() => value);
69
81
  }
70
82
 
83
+ /**
84
+ * Bluebird-style any() method for waiting for any promises to resolve
85
+ * @param {Array|Iterable} promises - Array or iterable of promises
86
+ * @returns {Promise} Promise that resolves with the first resolved promise
87
+ */
88
+ any() {
89
+ return this.then((args) => {
90
+ return AveAzul.any(toArray(args));
91
+ });
92
+ }
93
+
71
94
  /**
72
95
  * Bluebird-style each() method for array iteration
73
96
  * Similar to Bluebird's Promise.prototype.each()
@@ -105,7 +128,12 @@ class AveAzul extends Promise {
105
128
  * @returns {Promise} Promise that rejects if timeout occurs
106
129
  */
107
130
  timeout(ms, message = "operation timed out") {
108
- return AveAzul.resolve(xaa.timeout(ms, message).run(this));
131
+ return xaa
132
+ .timeout(ms, message, {
133
+ Promise: AveAzul,
134
+ TimeoutError: OperationalError,
135
+ })
136
+ .run(this);
109
137
  }
110
138
 
111
139
  /**
@@ -253,6 +281,136 @@ class AveAzul extends Promise {
253
281
  }
254
282
  });
255
283
  }
284
+
285
+ some(count) {
286
+ return this.then((args) => {
287
+ args = toArray(args);
288
+
289
+ return new AveAzul((resolve, reject) => {
290
+ // If too many promises are rejected so that the promise can never become fulfilled,
291
+ // it will be immediately rejected with an AggregateError of the rejection reasons
292
+ // in the order they were thrown in.
293
+ const errors = [];
294
+ // The fulfillment value is an array with count values
295
+ // in the order they were fulfilled.
296
+ const results = [];
297
+ const len = args.length;
298
+
299
+ let settled = false;
300
+
301
+ const addDone = (result) => {
302
+ if (settled) return;
303
+ results.push(result);
304
+ if (results.length >= count) {
305
+ settled = true;
306
+ // Resolve with exactly count results to match Bluebird's behavior
307
+ resolve(results.slice(0, count));
308
+ }
309
+ };
310
+
311
+ const addError = (err) => {
312
+ if (settled) return;
313
+ errors.push(err);
314
+ if (len - errors.length < count) {
315
+ settled = true;
316
+ reject(new AggregateError(errors, `aggregate error`));
317
+ }
318
+ };
319
+
320
+ for (let i = 0; i < len; i++) {
321
+ const x = args[i];
322
+ if (isPromise(x)) {
323
+ x.then(addDone, addError);
324
+ } else {
325
+ addDone(x);
326
+ }
327
+ }
328
+ });
329
+ });
330
+ }
331
+
332
+ /**
333
+ * Bluebird-style all() method for array operations
334
+ * Similar to Promise.all() but operates on the resolved value of this promise
335
+ * @returns {Promise} Promise that resolves when all items in the array resolve
336
+ */
337
+ all() {
338
+ return this.then((value) => {
339
+ return AveAzul.all(toArray(value));
340
+ });
341
+ }
342
+
343
+ /**
344
+ * Bluebird-style asCallback() method
345
+ * Attaches a callback to the promise and returns the promise.
346
+ * The callback is invoked when the promise is resolved or rejected.
347
+ *
348
+ * @param {Function} cb - Node.js-style callback function (err, value)
349
+ * @param {Object} [options] - Additional options
350
+ * @param {boolean} [options.spread=false] - Pass array values as arguments to callback
351
+ * @returns {Promise} The same promise instance
352
+ */
353
+ asCallback(cb, options = {}) {
354
+ if (typeof cb !== "function") {
355
+ return this;
356
+ }
357
+
358
+ const spread = options && options.spread === true;
359
+
360
+ this.then(
361
+ (value) => {
362
+ try {
363
+ if (spread && Array.isArray(value)) {
364
+ cb(null, ...value);
365
+ } else {
366
+ cb(null, value);
367
+ }
368
+ } catch (err) {
369
+ AveAzul.___throwUncaughtError(err);
370
+ }
371
+ },
372
+ (reason) => {
373
+ try {
374
+ cb(reason);
375
+ } catch (err) {
376
+ AveAzul.___throwUncaughtError(err);
377
+ }
378
+ }
379
+ );
380
+
381
+ return this;
382
+ }
383
+
384
+ nodeify(cb, options) {
385
+ return this.asCallback(cb, options);
386
+ }
387
+
388
+ /**
389
+ * Bluebird-style call() method for calling a method on the resolved value
390
+ * @param {string} methodName - Name of the method to call
391
+ * @param {...any} args - Arguments to pass to the method
392
+ * @returns {Promise} Promise that resolves with the method's return value
393
+ */
394
+ call(methodName, ...args) {
395
+ return this.then(function (obj) {
396
+ return obj[methodName].call(obj, ...args);
397
+ });
398
+ }
399
+
400
+ /**
401
+ * Catches only operational errors and passes them to the handler.
402
+ * Programmer errors (non-operational) are rethrown.
403
+ * @param {Function} handler - Function to handle operational errors
404
+ * @returns {Promise} - Promise with the error handled or rethrown
405
+ */
406
+ error(handler) {
407
+ return this.catch((err) => {
408
+ if (isOperationalError(err)) {
409
+ return handler(err);
410
+ }
411
+ throw err;
412
+ });
413
+ }
256
414
  }
257
415
 
258
416
  /**
@@ -278,7 +436,16 @@ AveAzul.delay = (ms, value) => {
278
436
  * @param {Function} fn - Map function to apply to each element
279
437
  * @returns {Promise} Promise that resolves with the mapped array
280
438
  */
281
- AveAzul.map = (value, fn) => AveAzul.resolve(xaa.map(value, fn));
439
+ AveAzul.map = (value, fn, options = { concurrency: 50 }) =>
440
+ AveAzul.resolve(value).map(fn, options);
441
+
442
+ /**
443
+ * Bluebird-style mapSeries() for array operations
444
+ * @param {Array} value - Array to map over
445
+ * @param {Function} fn - Map function to apply to each element
446
+ * @returns {Promise} Promise that resolves with the mapped array
447
+ */
448
+ AveAzul.mapSeries = (value, fn) => AveAzul.map(value, fn, { concurrency: 1 });
282
449
 
283
450
  /**
284
451
  * Bluebird-style try() for wrapping sync/async functions
@@ -408,7 +575,7 @@ AveAzul.using = (resources, ...args) => {
408
575
  * @returns {Promise} Promise that resolves with the handler's return value
409
576
  */
410
577
  AveAzul.join = function (...args) {
411
- if (args.length > 1 && typeof args.at(-1) === "function") {
578
+ if (args.length > 1 && typeof args[args.length - 1] === "function") {
412
579
  const handler = args.pop();
413
580
  return AveAzul.all(args).then((results) => handler(...results));
414
581
  } else {
@@ -455,7 +622,24 @@ AveAzul.fromCallback = fromCallback;
455
622
  */
456
623
  AveAzul.___throwUncaughtError = triggerUncaughtException;
457
624
 
458
- module.exports = AveAzul;
625
+ /**
626
+ * Bluebird-style some() for waiting for some promises to resolve
627
+ * @param {Array|Iterable} promises - Array or iterable of promises
628
+ * @param {number} count - Number of promises that need to resolve
629
+ * @returns {Promise} Promise that resolves when count promises have resolved
630
+ */
631
+ AveAzul.some = function (promises, count) {
632
+ return AveAzul.resolve(promises).some(count);
633
+ };
459
634
 
635
+ const { addStaticAny } = require("./any");
636
+ addStaticAny(AveAzul);
637
+
638
+ // Setup the not implemented methods
460
639
  const { setupNotImplemented } = require("./not-implemented");
461
640
  setupNotImplemented(AveAzul);
641
+
642
+ // Add these static properties after the class definition
643
+ AveAzul.OperationalError = OperationalError;
644
+
645
+ module.exports = AveAzul;
@@ -10,17 +10,18 @@ function createNotImplemented(name) {
10
10
 
11
11
  function createInstanceNotImplemented(AveAzul) {
12
12
  const methods = [
13
+ "then",
13
14
  "spread",
14
- "error",
15
+ "catch",
16
+ "finally",
15
17
  "bind",
16
- "join",
17
- "try",
18
- "method",
19
18
  "isFulfilled",
20
19
  "isRejected",
21
20
  "isPending",
21
+ "isCancelled",
22
22
  "value",
23
23
  "reason",
24
+ "all",
24
25
  "props",
25
26
  "any",
26
27
  "some",
@@ -29,13 +30,22 @@ function createInstanceNotImplemented(AveAzul) {
29
30
  "filter",
30
31
  "each",
31
32
  "mapSeries",
33
+ "disposer",
34
+ "asCallback",
35
+ "delay",
36
+ "timeout",
37
+ "cancel",
32
38
  "tap",
33
39
  "tapCatch",
34
- "catchThrow",
35
- "catchReturn",
40
+ "call",
36
41
  "get",
42
+ "return",
37
43
  "throw",
38
- "call",
44
+ "catchReturn",
45
+ "catchThrow",
46
+ "reflect",
47
+ "suppressUnhandledRejections",
48
+ "done",
39
49
  ];
40
50
 
41
51
  const proto = AveAzul.prototype;
@@ -56,22 +66,25 @@ function createStaticNotImplemented(AveAzul) {
56
66
  "method",
57
67
  "resolve",
58
68
  "reject",
59
- "all",
60
69
  "props",
61
70
  "any",
62
71
  "some",
63
72
  "map",
73
+ "reduce",
64
74
  "filter",
65
75
  "each",
66
76
  "mapSeries",
67
77
  "race",
78
+ "using",
68
79
  "promisify",
69
80
  "promisifyAll",
70
- "fromNode",
71
81
  "fromCallback",
72
82
  "delay",
73
83
  "coroutine",
74
- "config",
84
+ "coroutine.addYieldHandler",
85
+ "getNewLibraryCopy",
86
+ "noConflict",
87
+ "setScheduler",
75
88
  ];
76
89
  const ret = [];
77
90
  for (const method of methods) {
@@ -84,9 +97,9 @@ function createStaticNotImplemented(AveAzul) {
84
97
  }
85
98
 
86
99
  function setupNotImplemented(AveAzul) {
87
- const instance = createInstanceNotImplemented(AveAzul);
100
+ const instanceMethods = createInstanceNotImplemented(AveAzul);
88
101
  const staticMethods = createStaticNotImplemented(AveAzul);
89
- AveAzul.__notImplementedInstance = instance;
102
+ AveAzul.__notImplementedInstance = instanceMethods;
90
103
  AveAzul.__notImplementedStatic = staticMethods;
91
104
  }
92
105
 
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * OperationalError class for representing errors that are expected during normal operation
5
+ * Similar to Bluebird's OperationalError
6
+ */
7
+ class OperationalError extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = "OperationalError";
11
+ this.isOperational = true;
12
+
13
+ // Capture stack trace
14
+ if (Error.captureStackTrace) {
15
+ Error.captureStackTrace(this, this.constructor);
16
+ }
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Check if an error is an operational error
22
+ * @param {*} error - Error to check
23
+ * @returns {boolean} True if the error is operational
24
+ */
25
+ function isOperationalError(error) {
26
+ if (!error || typeof error !== "object") return false;
27
+ return error instanceof OperationalError || error.isOperational === true;
28
+ }
29
+
30
+ /**
31
+ * Check if an error is a programmer error (unexpected, likely a bug)
32
+ * @param {*} error - Error to check
33
+ * @returns {boolean} True if the error is a programmer error
34
+ */
35
+ function isProgrammerError(error) {
36
+ if (!error || typeof error !== "object") return false;
37
+ return !isOperationalError(error);
38
+ }
39
+
40
+ // Only export the OperationalError class
41
+ module.exports = {
42
+ OperationalError,
43
+ // Internal utilities used by AveAzul.prototype.error
44
+ isOperationalError,
45
+ isProgrammerError,
46
+ };
@@ -76,7 +76,7 @@ function promisifyAll2(obj, options) {
76
76
  }
77
77
 
78
78
  obj[promisifiedKey] = options.promisifier(value, defaultPromisifier, {
79
- context: obj,
79
+ // context: obj, // promisified function should get the binded object using this
80
80
  copyProps: false,
81
81
  multiArgs: options.multiArgs,
82
82
  Promise: options.Promise,
package/lib/promisify.js CHANGED
@@ -22,7 +22,7 @@ module.exports.promisify = function promisify(fn, _options) {
22
22
  const Promise = options.Promise;
23
23
  const multiArgs = !!options.multiArgs;
24
24
 
25
- const promisifiedFn = (...args) => {
25
+ const promisifiedFn = function (...args) {
26
26
  return new Promise((resolve, reject) => {
27
27
  // add a callback to the end of the arguments to transfer the result to the promise
28
28
  args.push((err, ...values) => {
@@ -37,7 +37,7 @@ module.exports.promisify = function promisify(fn, _options) {
37
37
  });
38
38
 
39
39
  // call the original function with the updated args
40
- fn.call(options.context, ...args);
40
+ fn.call(options.context || this, ...args);
41
41
  });
42
42
  };
43
43
 
package/lib/using.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { Disposer } = require("./disposer");
4
4
  const { isPromise } = require("./util");
5
+ const { AggregateError } = require("@jchip/error");
5
6
 
6
7
  const SYM_FN_DISPOSE = Symbol("fnDispose");
7
8
  /**
@@ -28,6 +29,25 @@ function using(resources, handler, Promise, asArray) {
28
29
  // Expect Promise to be AveAzul or Bluebird that has map method
29
30
  const acquisitionErrors = [];
30
31
 
32
+ // Helper function to process a disposer
33
+ const processDisposer = async (resource, disposer) => {
34
+ try {
35
+ const res = await disposer._promise;
36
+ resource._result = res;
37
+ resource[SYM_FN_DISPOSE] = disposer._data;
38
+ } catch (error) {
39
+ acquisitionErrors.push(error);
40
+ resource._error = error;
41
+ }
42
+ return resource;
43
+ };
44
+
45
+ // Helper to check if something is a disposer
46
+ const isDisposer = (obj) =>
47
+ obj &&
48
+ (obj instanceof Disposer ||
49
+ (obj._promise && typeof obj._data === "function"));
50
+
31
51
  const acquireResources = () => {
32
52
  const promiseRes = resources.map((resource) => {
33
53
  // if it's a promise-like, wait for its resolved value
@@ -38,27 +58,21 @@ function using(resources, handler, Promise, asArray) {
38
58
  });
39
59
 
40
60
  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;
61
+ // If it's directly a disposer
62
+ if (isDisposer(resource)) {
63
+ return processDisposer(resource, resource);
55
64
  }
56
65
 
57
66
  // if it's a promise like, wait for its resolved value
58
67
  if (resource && resource.___promise) {
59
68
  try {
60
69
  const res = await resource.___promise;
61
- resource._result = res;
70
+ // Check if the resolved value is a disposer
71
+ if (isDisposer(res)) {
72
+ return processDisposer(resource, res);
73
+ } else {
74
+ resource._result = res;
75
+ }
62
76
  } catch (error) {
63
77
  acquisitionErrors.push(error);
64
78
  resource._error = error;
package/lib/util.js CHANGED
@@ -183,6 +183,23 @@ function triggerUncaughtException(error) {
183
183
  }, 0);
184
184
  }
185
185
 
186
+ function toArray(args) {
187
+ if (!Array.isArray(args)) {
188
+ // Check if args is iterable
189
+ if (args != null && typeof args[Symbol.iterator] === "function") {
190
+ // Convert iterable to array, must do this to get the length, in order
191
+ // to detect if too many errors occurred and completion is impossible.
192
+ args = Array.from(args);
193
+ } else {
194
+ throw new TypeError(
195
+ "expecting an array or an iterable object but got " + args
196
+ );
197
+ }
198
+ }
199
+
200
+ return args;
201
+ }
202
+
186
203
  module.exports.copyOwnProperties = copyOwnProperties;
187
204
  module.exports.isClass = isClass;
188
205
  module.exports.isIdentifier = isIdentifier;
@@ -192,3 +209,4 @@ module.exports.isPromise = isPromise;
192
209
  module.exports.triggerUncaughtException = triggerUncaughtException;
193
210
  module.exports.getObjectKeys = getObjectKeys;
194
211
  module.exports.isExcludedPrototype = isExcludedPrototype;
212
+ module.exports.toArray = toArray;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aveazul",
3
- "version": "0.1.5",
4
- "description": "Bluebird-like APIs in extended native Promise",
3
+ "version": "1.0.1",
4
+ "description": "Bluebird drop-in replacement built on native Promise",
5
5
  "main": "lib/aveazul.js",
6
6
  "homepage": "https://github.com/jchip/aveazul",
7
7
  "license": "Apache-2.0",
@@ -16,6 +16,9 @@
16
16
  "jest:bluebird": "USE_BLUEBIRD=true jest --no-coverage"
17
17
  },
18
18
  "author": "Joel Chen",
19
+ "contributors": [
20
+ "Claude (AI assistant)"
21
+ ],
19
22
  "files": [
20
23
  "lib"
21
24
  ],
@@ -23,18 +26,51 @@
23
26
  "promise",
24
27
  "async",
25
28
  "bluebird",
26
- "nodejs"
29
+ "nodejs",
30
+ "node.js",
31
+ "promises",
32
+ "promise-library",
33
+ "native-promise",
34
+ "es6-promise",
35
+ "migration",
36
+ "promisify",
37
+ "utility",
38
+ "using",
39
+ "disposer",
40
+ "spread",
41
+ "promise-map",
42
+ "async-map",
43
+ "p-map",
44
+ "map-series",
45
+ "async-await",
46
+ "promise-chain",
47
+ "promise-utils",
48
+ "asynchronous",
49
+ "callback",
50
+ "promisifyall",
51
+ "timeout",
52
+ "delay",
53
+ "any",
54
+ "props",
55
+ "filter",
56
+ "reduce",
57
+ "aveazul",
58
+ "aveazul.js"
27
59
  ],
28
60
  "repository": {
29
61
  "type": "git",
30
62
  "url": "git+https://github.com/jchip/aveazul.git"
31
63
  },
32
64
  "dependencies": {
33
- "xaa": "^1.7.3"
65
+ "@jchip/error": "^1.0.3",
66
+ "xaa": "^1.8.0"
34
67
  },
35
68
  "devDependencies": {
36
69
  "bluebird": "^3.7.2",
37
- "jest": "^29.7.0",
70
+ "jest": "^28.0.0",
38
71
  "rimraf": "^3.0.1"
72
+ },
73
+ "engines": {
74
+ "node": ">=12.0.0"
39
75
  }
40
76
  }