xflight 1.0.0 → 2.0.2

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,9 +1,177 @@
1
1
  # xflight
2
2
 
3
+ **Avoid redundant async calls by sharing inflight promises for the same key.**
4
+
5
+ ## Description
6
+
7
+ `xflight` is a lightweight Node.js utility that manages inflight promises by key. It ensures that only one asynchronous operation per key is running at a time, returning the same promise for concurrent requests. This is useful for avoiding duplicate network or resource-intensive calls.
8
+
9
+ ## Features
10
+ - Prevents duplicate async operations for the same key
11
+ - Tracks start and check times for inflight items
12
+ - Cleans up after promise resolution or rejection
13
+ - 100% test coverage
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install xflight
19
+ ```
20
+
21
+ ## Requirements
22
+
23
+ This package requires **Node.js >= 20**.
24
+
25
+ ## Usage
26
+
3
27
  ```js
4
28
  const Xflight = require("xflight");
5
29
  const xfl = new Xflight();
6
- xfl.promise(url, () => {
7
- return fetch(url);
30
+
31
+ function fetchData(url) {
32
+ return xfl.promise(url, () => fetch(url));
33
+ }
34
+
35
+ // Multiple calls with the same URL will share the same promise if still pending
36
+ fetchData("https://api.example.com/data").then(console.log);
37
+ fetchData("https://api.example.com/data").then(console.log);
38
+ ```
39
+
40
+ ## Usage in ESM and CommonJS
41
+
42
+ ### ESM (ECMAScript Modules)
43
+ ```ts
44
+ import Inflight from "xflight";
45
+
46
+ const inflight = new Inflight();
47
+ const key = "resource-1";
48
+
49
+ // Deduplicate async calls
50
+ const resultPromise = inflight.promise(key, () => fetch("https://api.example.com/data"));
51
+ ```
52
+
53
+ ### CommonJS
54
+ ```js
55
+ const Inflight = require("xflight").default;
56
+
57
+ const inflight = new Inflight();
58
+ // ... use as shown in examples above
59
+ ```
60
+
61
+ ## API
62
+
63
+ ### `new Xflight([PromiseImpl])`
64
+ - `PromiseImpl` (optional): Custom Promise implementation (e.g., Bluebird, Aveazul, or native Promise).
65
+
66
+ **Note:** By default, the constructor will try to use Bluebird or Aveazul as the Promise implementation if they are available. If you want to always use the native Promise and skip these checks, pass the global Promise as the argument:
67
+
68
+ ```ts
69
+ const inflight = new Inflight(Promise);
70
+ ```
71
+
72
+ ### Methods
73
+
74
+ #### `promise(key, promiseFactory)`
75
+ - `key`: Unique identifier for the inflight operation.
76
+ - `promiseFactory`: Function that returns a promise.
77
+ - **Returns:** The promise from `promiseFactory`, or the existing inflight promise for the key.
78
+
79
+ #### `add(key, value, [now])`
80
+ - Manually add an inflight item. `value` should be a promise.
81
+
82
+ #### `get(key)`
83
+ - Get the current inflight promise for a key, or `undefined`.
84
+
85
+ #### `remove(key)`
86
+ - Remove an inflight item by key.
87
+
88
+ #### `isEmpty`
89
+ - Boolean: true if no inflight items.
90
+
91
+ #### `count`
92
+ - Number of inflight items.
93
+
94
+ #### Timing Methods
95
+ - `getStartTime(key)`: Get start time (ms since epoch) for a key.
96
+ - `time(key, [now])` / `elapseTime(key, [now])`: Elapsed time since start.
97
+ - `getCheckTime(key)`: Get last check time for a key.
98
+ - `lastCheckTime(key, [now])` / `elapseCheckTime(key, [now])`: Elapsed time since last check.
99
+ - `resetCheckTime(key, [now])`: Reset last check time to now.
100
+
101
+ ## Testing
102
+
103
+ To run tests:
104
+
105
+ ```bash
106
+ npm test
107
+ ```
108
+
109
+ Test coverage is enforced at 100% using `nyc`.
110
+
111
+
112
+ ## Examples
113
+
114
+ ### Basic Usage with `promise`
115
+ ```ts
116
+ import Inflight from "xflight";
117
+
118
+ const inflight = new Inflight();
119
+ const key = "resource-1";
120
+
121
+ // Deduplicate async calls
122
+ const resultPromise = inflight.promise(key, () => fetch("https://api.example.com/data"));
123
+ // or, for clarity:
124
+ const resultPromise2 = inflight.promise(key, function promiseFactory() {
125
+ return fetch("https://api.example.com/data");
8
126
  });
9
127
  ```
128
+
129
+ ### Manually Add and Get an Inflight Promise
130
+ ```ts
131
+ const promise = new Promise((resolve) => setTimeout(() => resolve("done"), 100));
132
+ inflight.add("manual-key", promise);
133
+ const inflightPromise = inflight.get("manual-key"); // Promise<string> | undefined
134
+ ```
135
+
136
+ ### Remove an Inflight Item
137
+ ```ts
138
+ inflight.remove("manual-key");
139
+ ```
140
+
141
+ ### Check if Inflight is Empty and Get Count
142
+ ```ts
143
+ console.log(inflight.isEmpty); // true or false
144
+ console.log(inflight.count); // number of inflight items
145
+ ```
146
+
147
+ ### Timing Methods
148
+ ```ts
149
+ inflight.add("timed-key", new Promise(() => {}));
150
+ const start = inflight.getStartTime("timed-key");
151
+ const elapsed = inflight.time("timed-key");
152
+ const elapsedAlias = inflight.elapseTime("timed-key");
153
+ ```
154
+
155
+ ### Check Time Methods
156
+ ```ts
157
+ const check = inflight.getCheckTime("timed-key");
158
+ const sinceLastCheck = inflight.lastCheckTime("timed-key");
159
+ const sinceLastCheckAlias = inflight.elapseCheckTime("timed-key");
160
+ ```
161
+
162
+ ### Reset Check Time
163
+ ```ts
164
+ // Reset for a specific key
165
+ inflight.resetCheckTime("timed-key");
166
+ // Reset for all inflight items
167
+ inflight.resetCheckTime();
168
+ ```
169
+
170
+
171
+ ## License
172
+
173
+ Apache-2.0
174
+
175
+ ---
176
+
177
+ © Joel Chen
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_assert_1 = __importDefault(require("node:assert"));
7
+ const optional_require_1 = require("optional-require");
8
+ const getBluebirdPromiseImpl = () => (0, optional_require_1.optionalRequire)("bluebird", { default: globalThis.Promise });
9
+ const getAveazulPromiseImpl = () => (0, optional_require_1.optionalRequire)("aveazul", { default: globalThis.Promise });
10
+ class Inflight {
11
+ _count = 0;
12
+ _inflights = {};
13
+ Promise;
14
+ constructor(xPromise) {
15
+ this.Promise = xPromise || getBluebirdPromiseImpl() || getAveazulPromiseImpl();
16
+ }
17
+ promise(key, func) {
18
+ const f = this._inflights[key];
19
+ if (f) {
20
+ return f.value;
21
+ }
22
+ const remove = () => this.remove(key);
23
+ try {
24
+ const p = func();
25
+ (0, node_assert_1.default)(p && typeof p.then === "function", `xflight: func for key ${key} didn't return a promise`);
26
+ this.add(key, p).then(remove, remove);
27
+ return p;
28
+ }
29
+ catch (err) {
30
+ return this.Promise.reject(err);
31
+ }
32
+ }
33
+ add(key, value, now) {
34
+ (0, node_assert_1.default)(this._inflights[key] === undefined, `xflight: item ${key} already exist`);
35
+ this._count++;
36
+ now = now || Date.now();
37
+ this._inflights[key] = { start: now, lastXTime: now, value };
38
+ return value;
39
+ }
40
+ get(key) {
41
+ const x = this._inflights[key];
42
+ return x && x.value;
43
+ }
44
+ remove(key) {
45
+ (0, node_assert_1.default)(this._inflights[key] !== undefined, `xflight: removing non-existing item ${key}`);
46
+ (0, node_assert_1.default)(this._count > 0, `xflight: removing item ${key} but list is empty - count ${this._count}`);
47
+ this._count--;
48
+ if (this._count === 0) {
49
+ this._inflights = {};
50
+ }
51
+ else {
52
+ this._inflights[key] = undefined;
53
+ }
54
+ }
55
+ get isEmpty() {
56
+ return this._count === 0;
57
+ }
58
+ get count() {
59
+ return this._count;
60
+ }
61
+ getStartTime(key) {
62
+ const x = this._inflights[key];
63
+ return x && x.start;
64
+ }
65
+ time(key, now) {
66
+ const x = this._inflights[key];
67
+ if (x) {
68
+ return (now || Date.now()) - x.start;
69
+ }
70
+ return -1;
71
+ }
72
+ elapseTime(key, now) {
73
+ return this.time(key, now);
74
+ }
75
+ getCheckTime(key) {
76
+ const x = this._inflights[key];
77
+ return x && x.lastXTime;
78
+ }
79
+ lastCheckTime(key, now) {
80
+ const x = this._inflights[key];
81
+ if (x) {
82
+ const t = (now || Date.now()) - x.lastXTime;
83
+ return t;
84
+ }
85
+ return -1;
86
+ }
87
+ elapseCheckTime(key, now) {
88
+ return this.lastCheckTime(key, now);
89
+ }
90
+ resetCheckTime(key, now) {
91
+ if (key) {
92
+ const x = this._inflights[key];
93
+ if (x) {
94
+ x.lastXTime = now || Date.now();
95
+ }
96
+ }
97
+ else {
98
+ // If no key is provided, reset all
99
+ Object.values(this._inflights).forEach(x => {
100
+ if (x)
101
+ x.lastXTime = now || Date.now();
102
+ });
103
+ }
104
+ return this;
105
+ }
106
+ }
107
+ exports.default = Inflight;
108
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["index.js"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;"}
@@ -0,0 +1,20 @@
1
+ declare class Inflight<T = any> {
2
+ private _count;
3
+ private _inflights;
4
+ Promise: PromiseConstructor;
5
+ constructor(xPromise?: PromiseConstructor);
6
+ promise(key: string, func: () => Promise<T>): Promise<T>;
7
+ add(key: string, value: Promise<T>, now?: number): Promise<T>;
8
+ get(key: string): T | undefined;
9
+ remove(key: string): void;
10
+ get isEmpty(): boolean;
11
+ get count(): number;
12
+ getStartTime(key: string): number | undefined;
13
+ time(key: string, now?: number): number;
14
+ elapseTime(key: string, now?: number): number;
15
+ getCheckTime(key: string): number | undefined;
16
+ lastCheckTime(key: string, now?: number): number;
17
+ elapseCheckTime(key: string, now?: number): number;
18
+ resetCheckTime(key?: string, now?: number): this;
19
+ }
20
+ export default Inflight;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"root":["../src/index.ts"],"version":"5.8.3"}
@@ -0,0 +1,20 @@
1
+ declare class Inflight<T = any> {
2
+ private _count;
3
+ private _inflights;
4
+ Promise: PromiseConstructor;
5
+ constructor(xPromise?: PromiseConstructor);
6
+ promise(key: string, func: () => Promise<T>): Promise<T>;
7
+ add(key: string, value: Promise<T>, now?: number): Promise<T>;
8
+ get(key: string): T | undefined;
9
+ remove(key: string): void;
10
+ get isEmpty(): boolean;
11
+ get count(): number;
12
+ getStartTime(key: string): number | undefined;
13
+ time(key: string, now?: number): number;
14
+ elapseTime(key: string, now?: number): number;
15
+ getCheckTime(key: string): number | undefined;
16
+ lastCheckTime(key: string, now?: number): number;
17
+ elapseCheckTime(key: string, now?: number): number;
18
+ resetCheckTime(key?: string, now?: number): this;
19
+ }
20
+ export default Inflight;
@@ -0,0 +1,103 @@
1
+ import assert from "node:assert";
2
+ import { optionalRequire } from "optional-require";
3
+ const getBluebirdPromiseImpl = () => optionalRequire("bluebird", { default: globalThis.Promise });
4
+ const getAveazulPromiseImpl = () => optionalRequire("aveazul", { default: globalThis.Promise });
5
+ class Inflight {
6
+ _count = 0;
7
+ _inflights = {};
8
+ Promise;
9
+ constructor(xPromise) {
10
+ this.Promise = xPromise || getBluebirdPromiseImpl() || getAveazulPromiseImpl();
11
+ }
12
+ promise(key, func) {
13
+ const f = this._inflights[key];
14
+ if (f) {
15
+ return f.value;
16
+ }
17
+ const remove = () => this.remove(key);
18
+ try {
19
+ const p = func();
20
+ assert(p && typeof p.then === "function", `xflight: func for key ${key} didn't return a promise`);
21
+ this.add(key, p).then(remove, remove);
22
+ return p;
23
+ }
24
+ catch (err) {
25
+ return this.Promise.reject(err);
26
+ }
27
+ }
28
+ add(key, value, now) {
29
+ assert(this._inflights[key] === undefined, `xflight: item ${key} already exist`);
30
+ this._count++;
31
+ now = now || Date.now();
32
+ this._inflights[key] = { start: now, lastXTime: now, value };
33
+ return value;
34
+ }
35
+ get(key) {
36
+ const x = this._inflights[key];
37
+ return x && x.value;
38
+ }
39
+ remove(key) {
40
+ assert(this._inflights[key] !== undefined, `xflight: removing non-existing item ${key}`);
41
+ assert(this._count > 0, `xflight: removing item ${key} but list is empty - count ${this._count}`);
42
+ this._count--;
43
+ if (this._count === 0) {
44
+ this._inflights = {};
45
+ }
46
+ else {
47
+ this._inflights[key] = undefined;
48
+ }
49
+ }
50
+ get isEmpty() {
51
+ return this._count === 0;
52
+ }
53
+ get count() {
54
+ return this._count;
55
+ }
56
+ getStartTime(key) {
57
+ const x = this._inflights[key];
58
+ return x && x.start;
59
+ }
60
+ time(key, now) {
61
+ const x = this._inflights[key];
62
+ if (x) {
63
+ return (now || Date.now()) - x.start;
64
+ }
65
+ return -1;
66
+ }
67
+ elapseTime(key, now) {
68
+ return this.time(key, now);
69
+ }
70
+ getCheckTime(key) {
71
+ const x = this._inflights[key];
72
+ return x && x.lastXTime;
73
+ }
74
+ lastCheckTime(key, now) {
75
+ const x = this._inflights[key];
76
+ if (x) {
77
+ const t = (now || Date.now()) - x.lastXTime;
78
+ return t;
79
+ }
80
+ return -1;
81
+ }
82
+ elapseCheckTime(key, now) {
83
+ return this.lastCheckTime(key, now);
84
+ }
85
+ resetCheckTime(key, now) {
86
+ if (key) {
87
+ const x = this._inflights[key];
88
+ if (x) {
89
+ x.lastXTime = now || Date.now();
90
+ }
91
+ }
92
+ else {
93
+ // If no key is provided, reset all
94
+ Object.values(this._inflights).forEach(x => {
95
+ if (x)
96
+ x.lastXTime = now || Date.now();
97
+ });
98
+ }
99
+ return this;
100
+ }
101
+ }
102
+ export default Inflight;
103
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"root":["../src/index.ts"],"version":"5.8.3"}
package/package.json CHANGED
@@ -1,17 +1,28 @@
1
1
  {
2
2
  "name": "xflight",
3
- "version": "1.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Handle inflight promise to avoid async duplication",
5
- "main": "lib/index.js",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist-esm/esm/index.js",
9
+ "require": "./dist-cjs/cjs/index.cjs"
10
+ }
11
+ },
6
12
  "scripts": {
7
- "test": "clap check"
13
+ "test": "vitest run",
14
+ "test:ui": "vitest --ui",
15
+ "coverage": "vitest run --coverage",
16
+ "build": "rm -rf dist-* && tsc --build tsconfig.esm.json && tsc --build tsconfig.cjs.json && ts2mjs --cjs --remove-source --skip-ts dist-cjs"
8
17
  },
9
18
  "repository": {
10
19
  "type": "git",
11
20
  "url": "https://github.com/jchip/xflight.git"
12
21
  },
13
22
  "files": [
14
- "lib"
23
+ "lib",
24
+ "dist-esm",
25
+ "dist-cjs"
15
26
  ],
16
27
  "keywords": [
17
28
  "inflight",
@@ -20,10 +31,16 @@
20
31
  "author": "Joel Chen",
21
32
  "license": "Apache-2.0",
22
33
  "devDependencies": {
23
- "electrode-archetype-njs-module-dev": "^3.0.0"
34
+ "@types/node": "^22.15.29",
35
+ "@vitest/coverage-v8": "^3.2.0",
36
+ "@xarc/run": "^1.1.2",
37
+ "tsx": "^4.19.4",
38
+ "typescript": "^5.8.3",
39
+ "vitest": "^3.2.0",
40
+ "ts2mjs": "github:jchip/ts2mjs#main"
24
41
  },
25
42
  "dependencies": {
26
- "optional-require": "^1.0.0"
43
+ "optional-require": "^2.0.1"
27
44
  },
28
45
  "nyc": {
29
46
  "all": true,
@@ -45,5 +62,8 @@
45
62
  "functions": 100,
46
63
  "lines": 100,
47
64
  "cache": true
65
+ },
66
+ "engines": {
67
+ "node": ">=20"
48
68
  }
49
69
  }
package/lib/index.js DELETED
@@ -1,113 +0,0 @@
1
- "use strict";
2
-
3
- const assert = require("assert");
4
- const Promise = require("optional-require")(require)("bluebird", { default: global.Promise });
5
-
6
- class Inflight {
7
- constructor(xPromise) {
8
- this._count = 0;
9
- this._inflights = {};
10
- this.Promise = xPromise || Promise;
11
- }
12
-
13
- promise(key, func) {
14
- const f = this._inflights[key];
15
- if (f) {
16
- return f.value;
17
- }
18
-
19
- const remove = () => this.remove(key);
20
-
21
- try {
22
- const p = func();
23
- assert(p && p.then, `xflight: func for key ${key} didn't return a promise`);
24
- this.add(key, p).then(remove, remove);
25
- return p;
26
- } catch (err) {
27
- return this.Promise.reject(err);
28
- }
29
- }
30
-
31
- add(key, value, now) {
32
- assert(this._inflights[key] === undefined, `xflight: item ${key} already exist`);
33
- this._count++;
34
- now = now || Date.now();
35
- this._inflights[key] = { start: now, lastXTime: now, value };
36
-
37
- return value;
38
- }
39
-
40
- get(key) {
41
- const x = this._inflights[key];
42
- return x && x.value;
43
- }
44
-
45
- remove(key) {
46
- assert(this._inflights[key] !== undefined, `xflight: removing non-existing item ${key}`);
47
- assert(
48
- this._count > 0,
49
- `xflight: removing item ${key} but list is empty - count ${this._count}`
50
- );
51
-
52
- this._count--;
53
-
54
- if (this._count === 0) {
55
- this._inflights = {};
56
- } else {
57
- this._inflights[key] = undefined;
58
- }
59
- }
60
-
61
- get isEmpty() {
62
- return this._count === 0;
63
- }
64
-
65
- get count() {
66
- return this._count;
67
- }
68
-
69
- getStartTime(key) {
70
- const x = this._inflights[key];
71
- return x && x.start;
72
- }
73
-
74
- time(key, now) {
75
- const x = this._inflights[key];
76
- if (x) {
77
- return (now || Date.now()) - x.start;
78
- }
79
- return -1;
80
- }
81
-
82
- elapseTime(key, now) {
83
- return this.time(key, now);
84
- }
85
-
86
- getCheckTime(key) {
87
- const x = this._inflights[key];
88
- return x && x.lastXTime;
89
- }
90
-
91
- lastCheckTime(key, now) {
92
- const x = this._inflights[key];
93
- if (x) {
94
- const t = (now || Date.now()) - x.lastXTime;
95
- return t;
96
- }
97
- return -1;
98
- }
99
-
100
- elapseCheckTime(key, now) {
101
- return this.lastCheckTime(key, now);
102
- }
103
-
104
- resetCheckTime(key, now) {
105
- const x = this._inflights[key];
106
- if (x) {
107
- x.lastXTime = now || Date.now();
108
- }
109
- return this;
110
- }
111
- }
112
-
113
- module.exports = Inflight;