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 +75 -5
- package/lib/any.js +55 -0
- package/lib/aveazul.js +193 -9
- package/lib/not-implemented.js +25 -12
- package/lib/operational-error.js +46 -0
- package/lib/promisify-all.js +1 -1
- package/lib/promisify.js +2 -2
- package/lib/using.js +29 -15
- package/lib/util.js +18 -0
- package/package.json +41 -5
package/README.md
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
# AveAzul
|
|
1
|
+
# AveAzul.js
|
|
2
2
|
|
|
3
|
-
AveAzul ("Blue Bird" in Spanish)
|
|
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
|
-
|
|
5
|
+
## Purpose
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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;
|
package/lib/not-implemented.js
CHANGED
|
@@ -10,17 +10,18 @@ function createNotImplemented(name) {
|
|
|
10
10
|
|
|
11
11
|
function createInstanceNotImplemented(AveAzul) {
|
|
12
12
|
const methods = [
|
|
13
|
+
"then",
|
|
13
14
|
"spread",
|
|
14
|
-
"
|
|
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
|
-
"
|
|
35
|
-
"catchReturn",
|
|
40
|
+
"call",
|
|
36
41
|
"get",
|
|
42
|
+
"return",
|
|
37
43
|
"throw",
|
|
38
|
-
"
|
|
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
|
-
"
|
|
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
|
|
100
|
+
const instanceMethods = createInstanceNotImplemented(AveAzul);
|
|
88
101
|
const staticMethods = createStaticNotImplemented(AveAzul);
|
|
89
|
-
AveAzul.__notImplementedInstance =
|
|
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
|
+
};
|
package/lib/promisify-all.js
CHANGED
|
@@ -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
|
-
|
|
42
|
-
|
|
43
|
-
(resource
|
|
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
|
-
|
|
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
|
|
4
|
-
"description": "Bluebird-
|
|
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
|
-
"
|
|
65
|
+
"@jchip/error": "^1.0.3",
|
|
66
|
+
"xaa": "^1.8.0"
|
|
34
67
|
},
|
|
35
68
|
"devDependencies": {
|
|
36
69
|
"bluebird": "^3.7.2",
|
|
37
|
-
"jest": "^
|
|
70
|
+
"jest": "^28.0.0",
|
|
38
71
|
"rimraf": "^3.0.1"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=12.0.0"
|
|
39
75
|
}
|
|
40
76
|
}
|