aveazul 0.1.5 → 1.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.
package/README.md CHANGED
@@ -1,10 +1,18 @@
1
1
  # AveAzul
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
8
16
 
9
17
  ## Installation
10
18
 
@@ -27,6 +35,26 @@ AveAzul.resolve([1, 2, 3])
27
35
  .filter((x) => x > 2)
28
36
  .then((result) => console.log(result)); // [4, 6]
29
37
 
38
+ // Wait for at least 2 promises to be fulfilled
39
+ const fetchUrls = [
40
+ fetch("https://api.example.com/data1"),
41
+ fetch("https://api.example.com/data2"),
42
+ fetch("https://api.example.com/data3"),
43
+ fetch("https://api.example.com/data4"),
44
+ ];
45
+ AveAzul.some(fetchUrls, 2).then((results) =>
46
+ console.log(`Got the first 2 successful results`)
47
+ );
48
+
49
+ // Process items sequentially with mapSeries
50
+ AveAzul.resolve([1, 2, 3])
51
+ .mapSeries(async (x) => {
52
+ // Each item is processed only after the previous one completes
53
+ await new Promise((resolve) => setTimeout(resolve, 100));
54
+ return x * 2;
55
+ })
56
+ .then((result) => console.log(result)); // [2, 4, 6]
57
+
30
58
  // Promisify callback-style functions
31
59
  const fs = require("fs");
32
60
  const readFile = AveAzul.promisify(fs.readFile);
@@ -71,6 +99,24 @@ AveAzul.all([getUser(1), getPosts(1), getComments(1)]).spread(
71
99
  );
72
100
  ```
73
101
 
102
+ ## Migration from Bluebird
103
+
104
+ For most applications, migrating from Bluebird to AveAzul should be as simple as:
105
+
106
+ ```javascript
107
+ // From
108
+ const Promise = require("bluebird");
109
+
110
+ // To
111
+ const Promise = require("aveazul");
112
+ ```
113
+
114
+ Key differences to be aware of:
115
+
116
+ - Some advanced debugging features are not available
117
+ - Performance characteristics may differ
118
+ - A few very specialized methods not available
119
+
74
120
  ## API
75
121
 
76
122
  ### Instance Methods
@@ -78,6 +124,7 @@ AveAzul.all([getUser(1), getPosts(1), getComments(1)]).spread(
78
124
  - `tap(fn)` - Execute side effects and return original value
79
125
  - `filter(fn)` - Filter array elements
80
126
  - `map(fn)` - Transform array elements
127
+ - `mapSeries(fn)` - Transform array elements sequentially
81
128
  - `return(value)` - Inject a new value
82
129
  - `each(fn)` - Iterate over array elements
83
130
  - `delay(ms)` - Delay resolution
@@ -86,16 +133,22 @@ AveAzul.all([getUser(1), getPosts(1), getComments(1)]).spread(
86
133
  - `spread(fn)` - Apply array values as arguments to function
87
134
  - `tapCatch(fn)` - Execute side effects on rejection
88
135
  - `reduce(fn, initialValue?)` - Reduce array elements
136
+ - `some(count)` - Resolves when a specified number of promises in the array have resolved
89
137
  - `throw(reason)` - Return rejected promise
90
138
  - `catchThrow(reason)` - Catch and throw new error
91
139
  - `catchReturn(value)` - Catch and return value
92
140
  - `get(propertyPath)` - Retrieve property value
93
141
  - `disposer(fn)` - Create a disposer for use with AveAzul.using() for resource cleanup
142
+ - `any()` - Resolves when any promise in the iterable resolves, rejecting if all reject
143
+ - `all()` - Like Promise.all(), resolves when all promises resolve, rejects if any reject
144
+ - `call(propertyName, ...args)` - Call a method on the resolved value with the provided arguments
145
+ - `asCallback(callback, options?)` - Register a Node-style callback that handles the resolution or rejection
94
146
 
95
147
  ### Static Methods
96
148
 
97
149
  - `delay(ms, value?)` - Resolve after specified time
98
150
  - `map(value, fn)` - Transform array elements
151
+ - `mapSeries(value, fn)` - Transform array elements one at a time in sequence
99
152
  - `try(fn)` - Wrap sync/async functions
100
153
  - `props(obj)` - Resolve object properties
101
154
  - `defer()` - Create a deferred promise
@@ -104,6 +157,7 @@ AveAzul.all([getUser(1), getPosts(1), getComments(1)]).spread(
104
157
  - `fromCallback(fn, options?)` - Alias for fromNode
105
158
  - `each(items, fn)` - Iterate over array elements
106
159
  - `reduce(array, fn, initialValue?)` - Reduce array elements
160
+ - `some(promises, count)` - Wait for a specified number of promises to be fulfilled
107
161
  - `method(fn)` - Creates a method that returns a promise resolving to the value returned by the original function
108
162
  - `throw(reason)` - Return rejected promise
109
163
  - `promisifyAll(target, options?)` - Convert all methods of an object/class to promises
@@ -127,12 +181,19 @@ npm install
127
181
  npm test
128
182
  npm run test:watch
129
183
  npm run test:coverage
184
+
185
+ # Test against Bluebird for compatibility
186
+ npm run jest:bluebird -- test/[name].test.js
130
187
  ```
131
188
 
189
+ ## Contributing
190
+
191
+ Contributions are welcome! Please feel free to submit a Pull Request.
192
+
132
193
  ## License
133
194
 
134
195
  Apache-2.0
135
196
 
136
197
  ## Author
137
198
 
138
- Joel Chen
199
+ Joel Chen, with assistant from Cursor Claude-3.7-sonnet
package/lib/aveazul.js CHANGED
@@ -8,7 +8,8 @@ const { using } = require("./using");
8
8
  const { isPromise, triggerUncaughtException } = require("./util");
9
9
 
10
10
  /**
11
- * AveAzul ("Blue Bird" in Spanish) - Extended Promise class that provides Bluebird-like utility methods
11
+ * @fileoverview
12
+ * AveAzul ("Blue Bird" in Spanish) - Extended Promise class that provides Bluebird like utility methods
12
13
  * This implementation is inspired by and provides similar APIs to the Bluebird Promise library,
13
14
  * but built on top of native Promises. The name is a Spanish play on words referencing Bluebird.
14
15
  * @extends Promise
@@ -54,8 +55,18 @@ class AveAzul extends Promise {
54
55
  * @param {Function} fn - Map function to apply to each element
55
56
  * @returns {Promise} Promise that resolves with the mapped array
56
57
  */
57
- map(fn) {
58
- return this.then((value) => xaa.map(value, fn));
58
+ map(fn, options = { concurrency: 50 }) {
59
+ return this.then((value) => xaa.map(value, fn, options));
60
+ }
61
+
62
+ /**
63
+ * Bluebird-style mapSeries() method for array operations
64
+ * Similar to Bluebird's Promise.prototype.mapSeries()
65
+ * @param {Function} fn - Map function to apply to each element
66
+ * @returns {Promise} Promise that resolves with the mapped array
67
+ */
68
+ mapSeries(fn) {
69
+ return this.map(fn, { concurrency: 1 });
59
70
  }
60
71
 
61
72
  /**
@@ -68,6 +79,30 @@ class AveAzul extends Promise {
68
79
  return this.then(() => value);
69
80
  }
70
81
 
82
+ /**
83
+ * Bluebird-style any() method for waiting for any promises to resolve
84
+ * @param {Array|Iterable} promises - Array or iterable of promises
85
+ * @returns {Promise} Promise that resolves with the first resolved promise
86
+ */
87
+ any() {
88
+ return this.then((args) => {
89
+ if (!Array.isArray(args)) {
90
+ // Check if args is iterable
91
+ if (args != null && typeof args[Symbol.iterator] === "function") {
92
+ // Convert iterable to array, must do this to get the length, in order
93
+ // to detect if too many errors occurred and completion is impossible.
94
+ args = Array.from(args);
95
+ } else {
96
+ throw new TypeError(
97
+ "expecting an array or an iterable object but got " + args
98
+ );
99
+ }
100
+ }
101
+
102
+ return AveAzul.any(args);
103
+ });
104
+ }
105
+
71
106
  /**
72
107
  * Bluebird-style each() method for array iteration
73
108
  * Similar to Bluebird's Promise.prototype.each()
@@ -253,6 +288,138 @@ class AveAzul extends Promise {
253
288
  }
254
289
  });
255
290
  }
291
+
292
+ some(count) {
293
+ return this.then((args) => {
294
+ if (!Array.isArray(args)) {
295
+ // Check if args is iterable
296
+ if (args != null && typeof args[Symbol.iterator] === "function") {
297
+ // Convert iterable to array, must do this to get the length, in order
298
+ // to detect if too many errors occurred and completion is impossible.
299
+ args = Array.from(args);
300
+ } else {
301
+ throw new TypeError(
302
+ "expecting an array or an iterable object but got " + args
303
+ );
304
+ }
305
+ }
306
+
307
+ return new AveAzul((resolve, reject) => {
308
+ // If too many promises are rejected so that the promise can never become fulfilled,
309
+ // it will be immediately rejected with an AggregateError of the rejection reasons
310
+ // in the order they were thrown in.
311
+ const errors = [];
312
+ // The fulfillment value is an array with count values
313
+ // in the order they were fulfilled.
314
+ const results = [];
315
+ const len = args.length;
316
+
317
+ const addDone = (result) => {
318
+ results.push(result);
319
+ if (results.length >= count) {
320
+ // Resolve with exactly count results to match Bluebird's behavior
321
+ resolve(results.slice(0, count));
322
+ }
323
+ };
324
+
325
+ const addError = (err) => {
326
+ errors.push(err);
327
+ if (len - errors.length < count) {
328
+ reject(new AggregateError(errors, `aggregate error`));
329
+ }
330
+ };
331
+
332
+ for (let i = 0; i < len; i++) {
333
+ const x = args[i];
334
+ if (isPromise(x)) {
335
+ x.then(addDone, addError);
336
+ } else {
337
+ addDone(x);
338
+ }
339
+ }
340
+ });
341
+ });
342
+ }
343
+
344
+ /**
345
+ * Bluebird-style all() method for array operations
346
+ * Similar to Promise.all() but operates on the resolved value of this promise
347
+ * @returns {Promise} Promise that resolves when all items in the array resolve
348
+ */
349
+ all() {
350
+ return this.then((value) => {
351
+ if (!Array.isArray(value)) {
352
+ // Check if value is iterable
353
+ if (value != null && typeof value[Symbol.iterator] === "function") {
354
+ // Convert iterable to array
355
+ value = Array.from(value);
356
+ } else {
357
+ throw new TypeError(
358
+ "expecting an array or an iterable object but got " + value
359
+ );
360
+ }
361
+ }
362
+
363
+ return AveAzul.all(value);
364
+ });
365
+ }
366
+
367
+ /**
368
+ * Bluebird-style asCallback() method
369
+ * Attaches a callback to the promise and returns the promise.
370
+ * The callback is invoked when the promise is resolved or rejected.
371
+ *
372
+ * @param {Function} cb - Node.js-style callback function (err, value)
373
+ * @param {Object} [options] - Additional options
374
+ * @param {boolean} [options.spread=false] - Pass array values as arguments to callback
375
+ * @returns {Promise} The same promise instance
376
+ */
377
+ asCallback(cb, options = {}) {
378
+ if (typeof cb !== "function") {
379
+ return this;
380
+ }
381
+
382
+ const spread = options && options.spread === true;
383
+
384
+ this.then(
385
+ (value) => {
386
+ try {
387
+ if (spread && Array.isArray(value)) {
388
+ cb(null, ...value);
389
+ } else {
390
+ cb(null, value);
391
+ }
392
+ } catch (err) {
393
+ AveAzul.___throwUncaughtError(err);
394
+ }
395
+ },
396
+ (reason) => {
397
+ try {
398
+ cb(reason);
399
+ } catch (err) {
400
+ AveAzul.___throwUncaughtError(err);
401
+ }
402
+ }
403
+ );
404
+
405
+ return this;
406
+ }
407
+
408
+ nodeify(cb, options) {
409
+ return this.asCallback(cb, options);
410
+ }
411
+
412
+ /**
413
+ * Bluebird-style call() method for calling a method on the resolved value
414
+ * @param {string} methodName - Name of the method to call
415
+ * @param {...any} args - Arguments to pass to the method
416
+ * @returns {Promise} Promise that resolves with the method's return value
417
+ */
418
+ call(methodName, ...args) {
419
+ return this.then(function (obj) {
420
+ return obj[methodName].call(obj, ...args);
421
+ });
422
+ }
256
423
  }
257
424
 
258
425
  /**
@@ -278,7 +445,16 @@ AveAzul.delay = (ms, value) => {
278
445
  * @param {Function} fn - Map function to apply to each element
279
446
  * @returns {Promise} Promise that resolves with the mapped array
280
447
  */
281
- AveAzul.map = (value, fn) => AveAzul.resolve(xaa.map(value, fn));
448
+ AveAzul.map = (value, fn, options = { concurrency: 50 }) =>
449
+ AveAzul.resolve(value).map(fn, options);
450
+
451
+ /**
452
+ * Bluebird-style mapSeries() for array operations
453
+ * @param {Array} value - Array to map over
454
+ * @param {Function} fn - Map function to apply to each element
455
+ * @returns {Promise} Promise that resolves with the mapped array
456
+ */
457
+ AveAzul.mapSeries = (value, fn) => AveAzul.map(value, fn, { concurrency: 1 });
282
458
 
283
459
  /**
284
460
  * Bluebird-style try() for wrapping sync/async functions
@@ -455,7 +631,18 @@ AveAzul.fromCallback = fromCallback;
455
631
  */
456
632
  AveAzul.___throwUncaughtError = triggerUncaughtException;
457
633
 
458
- module.exports = AveAzul;
634
+ /**
635
+ * Bluebird-style some() for waiting for some promises to resolve
636
+ * @param {Array|Iterable} promises - Array or iterable of promises
637
+ * @param {number} count - Number of promises that need to resolve
638
+ * @returns {Promise} Promise that resolves when count promises have resolved
639
+ */
640
+ AveAzul.some = function (promises, count) {
641
+ return AveAzul.resolve(promises).some(count);
642
+ };
459
643
 
644
+ // Setup the not implemented methods
460
645
  const { setupNotImplemented } = require("./not-implemented");
461
646
  setupNotImplemented(AveAzul);
647
+
648
+ module.exports = AveAzul;
@@ -10,17 +10,19 @@ function createNotImplemented(name) {
10
10
 
11
11
  function createInstanceNotImplemented(AveAzul) {
12
12
  const methods = [
13
+ "then",
13
14
  "spread",
15
+ "catch",
14
16
  "error",
17
+ "finally",
15
18
  "bind",
16
- "join",
17
- "try",
18
- "method",
19
19
  "isFulfilled",
20
20
  "isRejected",
21
21
  "isPending",
22
+ "isCancelled",
22
23
  "value",
23
24
  "reason",
25
+ "all",
24
26
  "props",
25
27
  "any",
26
28
  "some",
@@ -29,13 +31,22 @@ function createInstanceNotImplemented(AveAzul) {
29
31
  "filter",
30
32
  "each",
31
33
  "mapSeries",
34
+ "disposer",
35
+ "asCallback",
36
+ "delay",
37
+ "timeout",
38
+ "cancel",
32
39
  "tap",
33
40
  "tapCatch",
34
- "catchThrow",
35
- "catchReturn",
41
+ "call",
36
42
  "get",
43
+ "return",
37
44
  "throw",
38
- "call",
45
+ "catchReturn",
46
+ "catchThrow",
47
+ "reflect",
48
+ "suppressUnhandledRejections",
49
+ "done",
39
50
  ];
40
51
 
41
52
  const proto = AveAzul.prototype;
@@ -56,22 +67,25 @@ function createStaticNotImplemented(AveAzul) {
56
67
  "method",
57
68
  "resolve",
58
69
  "reject",
59
- "all",
60
70
  "props",
61
71
  "any",
62
72
  "some",
63
73
  "map",
74
+ "reduce",
64
75
  "filter",
65
76
  "each",
66
77
  "mapSeries",
67
78
  "race",
79
+ "using",
68
80
  "promisify",
69
81
  "promisifyAll",
70
- "fromNode",
71
82
  "fromCallback",
72
83
  "delay",
73
84
  "coroutine",
74
- "config",
85
+ "coroutine.addYieldHandler",
86
+ "getNewLibraryCopy",
87
+ "noConflict",
88
+ "setScheduler",
75
89
  ];
76
90
  const ret = [];
77
91
  for (const method of methods) {
@@ -84,9 +98,9 @@ function createStaticNotImplemented(AveAzul) {
84
98
  }
85
99
 
86
100
  function setupNotImplemented(AveAzul) {
87
- const instance = createInstanceNotImplemented(AveAzul);
101
+ const instanceMethods = createInstanceNotImplemented(AveAzul);
88
102
  const staticMethods = createStaticNotImplemented(AveAzul);
89
- AveAzul.__notImplementedInstance = instance;
103
+ AveAzul.__notImplementedInstance = instanceMethods;
90
104
  AveAzul.__notImplementedStatic = staticMethods;
91
105
  }
92
106
 
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.0",
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,7 +26,34 @@
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"
27
57
  ],
28
58
  "repository": {
29
59
  "type": "git",