@zukhruf/single-flight 0.3.20 → 0.3.21
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 +26 -17
- package/dist/client/flight-client.d.ts +9 -1
- package/dist/client/flight-client.d.ts.map +1 -1
- package/dist/client/flight-client.js +86 -16
- package/dist/client/flight-client.js.map +1 -1
- package/dist/coordinator/flight-coordinator.d.ts +24 -12
- package/dist/coordinator/flight-coordinator.d.ts.map +1 -1
- package/dist/coordinator/flight-coordinator.js +92 -143
- package/dist/coordinator/flight-coordinator.js.map +1 -1
- package/dist/coordinator/flight-server.d.ts +7 -7
- package/dist/coordinator/flight-server.d.ts.map +1 -1
- package/dist/coordinator/flight-server.js +7 -13
- package/dist/coordinator/flight-server.js.map +1 -1
- package/dist/coordinator/flight.d.ts +13 -2
- package/dist/coordinator/flight.d.ts.map +1 -1
- package/dist/coordinator/flight.js +20 -4
- package/dist/coordinator/flight.js.map +1 -1
- package/dist/coordinator/parked-runs.d.ts +26 -0
- package/dist/coordinator/parked-runs.d.ts.map +1 -0
- package/dist/coordinator/parked-runs.js +65 -0
- package/dist/coordinator/parked-runs.js.map +1 -0
- package/dist/coordinator/party.d.ts +1 -1
- package/dist/coordinator/party.d.ts.map +1 -1
- package/dist/coordinator/session.d.ts.map +1 -1
- package/dist/coordinator/session.js +6 -3
- package/dist/coordinator/session.js.map +1 -1
- package/dist/key-lock/key-hold.d.ts +16 -0
- package/dist/key-lock/key-hold.d.ts.map +1 -0
- package/dist/key-lock/key-hold.js +24 -0
- package/dist/key-lock/key-hold.js.map +1 -0
- package/dist/key-lock/key-locks.d.ts +35 -0
- package/dist/key-lock/key-locks.d.ts.map +1 -0
- package/dist/key-lock/key-locks.js +67 -0
- package/dist/key-lock/key-locks.js.map +1 -0
- package/dist/protocol/flight-protocol.d.ts +2 -2
- package/dist/protocol/flight-protocol.d.ts.map +1 -1
- package/dist/single-flight.d.ts +4 -12
- package/dist/single-flight.d.ts.map +1 -1
- package/dist/single-flight.js +36 -29
- package/dist/single-flight.js.map +1 -1
- package/package.json +7 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"party.d.ts","sourceRoot":"","sources":["../../src/coordinator/party.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,2EAA2E;AAC3E,KAAK,MAAM,GAAG;KACX,EAAE,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE;QAAE,EAAE,EAAE,EAAE,CAAA;KAAE,CAAC,EAAE,IAAI,CAAC;CAC9E,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;AAExB,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,gFAAgF;IAChF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,8CAA8C;AAC9C,MAAM,MAAM,IAAI;AACd,
|
|
1
|
+
{"version":3,"file":"party.d.ts","sourceRoot":"","sources":["../../src/coordinator/party.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,2EAA2E;AAC3E,KAAK,MAAM,GAAG;KACX,EAAE,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE;QAAE,EAAE,EAAE,EAAE,CAAA;KAAE,CAAC,EAAE,IAAI,CAAC;CAC9E,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;AAExB,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,gFAAgF;IAChF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,8CAA8C;AAC9C,MAAM,MAAM,IAAI;AACd,4GAA4G;AAC1G;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACrC,+DAA+D;GAC7D;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,sHAAsH;AACtH,qBAAa,KAAK;;IAChB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAIR,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;IAKpC,IAAI,IAAI,IAAI,IAAI,CAEf;IAED,iHAAiH;IACjH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,CAAC,MAAM,EAAE,MAAM;IAKnB,gGAAgG;IAChG,MAAM,CAAC,MAAM,EAAE,MAAM;IAIrB,IAAI,CAAC,MAAM,EAAE,MAAM;IAKnB,iHAAiH;IACjH,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM;CAWpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/coordinator/session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,KAAK,KAAK,EAAS,MAAM,YAAY,CAAC;AAE/C;;;;GAIG;AACH,qBAAa,OAAQ,YAAW,KAAK;;gBAMjC,WAAW,EAAE,iBAAiB,EAC9B,UAAU,EAAE,UAAU,CAAC,cAAc,CAAC;IAYxC,KAAK,CAAC,QAAQ,EAAE,cAAc;IAI9B,MAAM,CAAC,EAAE,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/coordinator/session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,KAAK,KAAK,EAAS,MAAM,YAAY,CAAC;AAE/C;;;;GAIG;AACH,qBAAa,OAAQ,YAAW,KAAK;;gBAMjC,WAAW,EAAE,iBAAiB,EAC9B,UAAU,EAAE,UAAU,CAAC,cAAc,CAAC;IAYxC,KAAK,CAAC,QAAQ,EAAE,cAAc;IAI9B,MAAM,CAAC,EAAE,EAAE,MAAM;CAiElB"}
|
|
@@ -38,9 +38,12 @@ export class Session {
|
|
|
38
38
|
const party = this.#open(request.id);
|
|
39
39
|
if (!party)
|
|
40
40
|
return;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
if (request.flight !== undefined) {
|
|
42
|
+
return this.#coordinator.rejoin(party, request.key, request.flight);
|
|
43
|
+
}
|
|
44
|
+
void this.#coordinator
|
|
45
|
+
.run(party, request.key)
|
|
46
|
+
.catch(() => this.#phase.close());
|
|
44
47
|
return;
|
|
45
48
|
}
|
|
46
49
|
case 'land': {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/coordinator/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAGL,eAAe,EACf,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAc,KAAK,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;GAIG;AACH,MAAM,OAAO,OAAO;IACT,YAAY,CAAoB;IAChC,QAAQ,GAAG,IAAI,GAAG,EAAiB,CAAC;IAC7C,MAAM,CAAe;IAErB,YACE,WAA8B,EAC9B,UAAsC;QAEtC,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,MAAM,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;QACtC,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;YACnC,iHAAiH;YACjH,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;gBAAE,OAAO;YACxC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,QAAwB;QAC5B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,EAAU;QACf,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,CAAC,OAAwB;QAC9B,sEAAsE;QACtE,2DAA2D;QAC3D,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,QAAQ,OAAO,CAAC,EAAE,EAAE,CAAC;YACnB,KAAK,KAAK,CAAC,CAAC,CAAC;gBACX,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBACrC,IAAI,CAAC,KAAK;oBAAE,OAAO;gBACnB,
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/coordinator/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A held key lock of a flight this process leads. It lets the lock go only
|
|
3
|
+
* when the work has settled and the flight needs nothing more from this
|
|
4
|
+
* process: its landing was acknowledged, its lease was lost, or the client
|
|
5
|
+
* closed. Until both, the key stays this process's, so no other process starts
|
|
6
|
+
* the work beside it; a cancel or a dispose alone never frees it.
|
|
7
|
+
*/
|
|
8
|
+
export declare class KeyHold {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(lock: Disposable);
|
|
11
|
+
/** The work settled, or never runs. */
|
|
12
|
+
workEnded(): void;
|
|
13
|
+
/** The flight needs nothing more from this process. */
|
|
14
|
+
flightEnded(): void;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=key-hold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-hold.d.ts","sourceRoot":"","sources":["../../src/key-lock/key-hold.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,qBAAa,OAAO;;gBAIN,IAAI,EAAE,UAAU;IAM5B,uCAAuC;IACvC,SAAS,IAAI,IAAI;IAIjB,uDAAuD;IACvD,WAAW,IAAI,IAAI;CAGpB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Latch } from '@zukhruf/async';
|
|
2
|
+
/**
|
|
3
|
+
* A held key lock of a flight this process leads. It lets the lock go only
|
|
4
|
+
* when the work has settled and the flight needs nothing more from this
|
|
5
|
+
* process: its landing was acknowledged, its lease was lost, or the client
|
|
6
|
+
* closed. Until both, the key stays this process's, so no other process starts
|
|
7
|
+
* the work beside it; a cancel or a dispose alone never frees it.
|
|
8
|
+
*/
|
|
9
|
+
export class KeyHold {
|
|
10
|
+
#workEnded = new Latch();
|
|
11
|
+
#flightEnded = new Latch();
|
|
12
|
+
constructor(lock) {
|
|
13
|
+
void Promise.all([this.#workEnded.wait(), this.#flightEnded.wait()]).then(() => lock[Symbol.dispose]());
|
|
14
|
+
}
|
|
15
|
+
/** The work settled, or never runs. */
|
|
16
|
+
workEnded() {
|
|
17
|
+
this.#workEnded.open();
|
|
18
|
+
}
|
|
19
|
+
/** The flight needs nothing more from this process. */
|
|
20
|
+
flightEnded() {
|
|
21
|
+
this.#flightEnded.open();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=key-hold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-hold.js","sourceRoot":"","sources":["../../src/key-lock/key-hold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,OAAO,OAAO;IACT,UAAU,GAAG,IAAI,KAAK,EAAE,CAAC;IACzB,YAAY,GAAG,IAAI,KAAK,EAAE,CAAC;IAEpC,YAAY,IAAgB;QAC1B,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CACvE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAC7B,CAAC;IACJ,CAAC;IAED,uCAAuC;IACvC,SAAS;QACP,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IAED,uDAAuD;IACvD,WAAW;QACT,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kernel locks on the keys of one flight directory. The process that runs
|
|
3
|
+
* a key's work holds that key's lock, so the kernel keeps the key while the
|
|
4
|
+
* process lives, frozen or not, and frees it when the process dies.
|
|
5
|
+
*
|
|
6
|
+
* Each key has the file `execution-locks/<sha256 of the key>.lock`. The folder
|
|
7
|
+
* keeps the key `flight` away from the election's `flight.lock`. The digest
|
|
8
|
+
* keeps keys that differ only in case apart: APFS and NTFS treat `Sync` and
|
|
9
|
+
* `sync` as one file name. The files are never deleted: one per key ever led.
|
|
10
|
+
*/
|
|
11
|
+
export declare class KeyLocks {
|
|
12
|
+
#private;
|
|
13
|
+
constructor(directory: string, pollInterval: number);
|
|
14
|
+
/**
|
|
15
|
+
* Holds the lock of `key` until the result is disposed. The first try is
|
|
16
|
+
* synchronous, in the call; while another handle holds the lock, it tries
|
|
17
|
+
* again every poll interval. An abort closes the handle and rejects with
|
|
18
|
+
* `signal.reason`. A file fault rejects.
|
|
19
|
+
*/
|
|
20
|
+
acquire(key: string, signal: AbortSignal): Promise<Disposable>;
|
|
21
|
+
/**
|
|
22
|
+
* Whether a process holds the lock of `key` now. The answer can be stale as
|
|
23
|
+
* soon as it returns, and the check briefly takes a shared lock, so a
|
|
24
|
+
* `tryLock` at that moment fails once. A file fault throws.
|
|
25
|
+
*/
|
|
26
|
+
isHeld(key: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves `true` once no process holds the lock of `key`, and `false` once
|
|
29
|
+
* `wanted` answers false. Both are checked every poll interval, on a timer
|
|
30
|
+
* that never keeps the process alive. An abort rejects with
|
|
31
|
+
* `signal.reason`, and a file fault rejects.
|
|
32
|
+
*/
|
|
33
|
+
whenFree(key: string, wanted: () => boolean, signal: AbortSignal): Promise<boolean>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=key-locks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-locks.d.ts","sourceRoot":"","sources":["../../src/key-lock/key-locks.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,qBAAa,QAAQ;;gBAIP,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;IAKnD;;;;;OAKG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAapE;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI5B;;;;;OAKG;IACH,QAAQ,CACN,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,OAAO,EACrB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,OAAO,CAAC;CAcpB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdirSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { poll } from '@zukhruf/async';
|
|
5
|
+
import { FileLock } from '@zukhruf/fs';
|
|
6
|
+
/**
|
|
7
|
+
* The kernel locks on the keys of one flight directory. The process that runs
|
|
8
|
+
* a key's work holds that key's lock, so the kernel keeps the key while the
|
|
9
|
+
* process lives, frozen or not, and frees it when the process dies.
|
|
10
|
+
*
|
|
11
|
+
* Each key has the file `execution-locks/<sha256 of the key>.lock`. The folder
|
|
12
|
+
* keeps the key `flight` away from the election's `flight.lock`. The digest
|
|
13
|
+
* keeps keys that differ only in case apart: APFS and NTFS treat `Sync` and
|
|
14
|
+
* `sync` as one file name. The files are never deleted: one per key ever led.
|
|
15
|
+
*/
|
|
16
|
+
export class KeyLocks {
|
|
17
|
+
#folder;
|
|
18
|
+
#pollInterval;
|
|
19
|
+
constructor(directory, pollInterval) {
|
|
20
|
+
this.#folder = join(directory, 'execution-locks');
|
|
21
|
+
this.#pollInterval = pollInterval;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Holds the lock of `key` until the result is disposed. The first try is
|
|
25
|
+
* synchronous, in the call; while another handle holds the lock, it tries
|
|
26
|
+
* again every poll interval. An abort closes the handle and rejects with
|
|
27
|
+
* `signal.reason`. A file fault rejects.
|
|
28
|
+
*/
|
|
29
|
+
async acquire(key, signal) {
|
|
30
|
+
// A lead answer comes only after a connection, so the directory passed the local check.
|
|
31
|
+
mkdirSync(this.#folder, { recursive: true });
|
|
32
|
+
using opened = new DisposableStack();
|
|
33
|
+
const lock = opened.use(FileLock.open(this.#pathOf(key)));
|
|
34
|
+
await poll(async () => (lock.tryLock() ? lock : undefined), {
|
|
35
|
+
interval: this.#pollInterval,
|
|
36
|
+
signal,
|
|
37
|
+
});
|
|
38
|
+
opened.move();
|
|
39
|
+
return lock;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Whether a process holds the lock of `key` now. The answer can be stale as
|
|
43
|
+
* soon as it returns, and the check briefly takes a shared lock, so a
|
|
44
|
+
* `tryLock` at that moment fails once. A file fault throws.
|
|
45
|
+
*/
|
|
46
|
+
isHeld(key) {
|
|
47
|
+
return FileLock.check(this.#pathOf(key)) === 'locked';
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Resolves `true` once no process holds the lock of `key`, and `false` once
|
|
51
|
+
* `wanted` answers false. Both are checked every poll interval, on a timer
|
|
52
|
+
* that never keeps the process alive. An abort rejects with
|
|
53
|
+
* `signal.reason`, and a file fault rejects.
|
|
54
|
+
*/
|
|
55
|
+
whenFree(key, wanted, signal) {
|
|
56
|
+
return poll(async () => {
|
|
57
|
+
if (!wanted())
|
|
58
|
+
return false;
|
|
59
|
+
return this.isHeld(key) ? undefined : true;
|
|
60
|
+
}, { interval: this.#pollInterval, signal, ref: false });
|
|
61
|
+
}
|
|
62
|
+
#pathOf(key) {
|
|
63
|
+
const digest = createHash('sha256').update(key).digest('hex');
|
|
64
|
+
return join(this.#folder, `${digest}.lock`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=key-locks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-locks.js","sourceRoot":"","sources":["../../src/key-lock/key-locks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;;;;;;;GASG;AACH,MAAM,OAAO,QAAQ;IACV,OAAO,CAAS;IAChB,aAAa,CAAS;IAE/B,YAAY,SAAiB,EAAE,YAAoB;QACjD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;QAClD,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;IACpC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,MAAmB;QAC5C,wFAAwF;QACxF,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1D,MAAM,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE;YAC1D,QAAQ,EAAE,IAAI,CAAC,aAAa;YAC5B,MAAM;SACP,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,GAAW;QAChB,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,QAAQ,CAAC;IACxD,CAAC;IAED;;;;;OAKG;IACH,QAAQ,CACN,GAAW,EACX,MAAqB,EACrB,MAAmB;QAEnB,OAAO,IAAI,CACT,KAAK,IAAI,EAAE;YACT,IAAI,CAAC,MAAM,EAAE;gBAAE,OAAO,KAAK,CAAC;YAC5B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7C,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CACrD,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,GAAW;QACjB,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,MAAM,OAAO,CAAC,CAAC;IAC9C,CAAC;CACF"}
|
|
@@ -38,7 +38,7 @@ export type FlightRequest =
|
|
|
38
38
|
op: 'cancel';
|
|
39
39
|
id: string;
|
|
40
40
|
}
|
|
41
|
-
/** A leader
|
|
41
|
+
/** A leader claims its flight again after its connection was lost, at any time while its process lives. */
|
|
42
42
|
| {
|
|
43
43
|
op: 'reassert';
|
|
44
44
|
id: string;
|
|
@@ -72,7 +72,7 @@ export type FlightResponse =
|
|
|
72
72
|
op: 'ack';
|
|
73
73
|
id: string;
|
|
74
74
|
}
|
|
75
|
-
/** The flight of `id` is no longer its leader's: a
|
|
75
|
+
/** The flight of `id` is no longer its leader's: a flight of the key with a newer token took it, or it ended. */
|
|
76
76
|
| {
|
|
77
77
|
op: 'rejected';
|
|
78
78
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flight-protocol.d.ts","sourceRoot":"","sources":["../../src/protocol/flight-protocol.ts"],"names":[],"mappings":"AAIA,yFAAyF;AACzF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACjC;AAED,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAE/D,+EAA+E;AAC/E,MAAM,MAAM,aAAa;AACvB;;;;;;;GAOG;AACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AACzD,mHAAmH;GACjH;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE;AAC9C,6EAA6E;GAC3E;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AAC9B,
|
|
1
|
+
{"version":3,"file":"flight-protocol.d.ts","sourceRoot":"","sources":["../../src/protocol/flight-protocol.ts"],"names":[],"mappings":"AAIA,yFAAyF;AACzF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACjC;AAED,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAE/D,+EAA+E;AAC/E,MAAM,MAAM,aAAa;AACvB;;;;;;;GAOG;AACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AACzD,mHAAmH;GACjH;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE;AAC9C,6EAA6E;GAC3E;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AAC9B,2GAA2G;GACzG;IAAE,EAAE,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,MAAM,MAAM,cAAc;AACxB,wEAAwE;AACtE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAC3C,oFAAoF;GAClF;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE;AAChD,qGAAqG;GACnG;IAAE,EAAE,EAAE,aAAa,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AACnC,uDAAuD;GACrD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AAC3B,iHAAiH;GAC/G;IAAE,EAAE,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AAChC,2EAA2E;GACzE;IAAE,EAAE,EAAE,aAAa,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC,qHAAqH;AACrH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AAKD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,GACf,OAAO,IAAI,eAAe,CAM5B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,eAAe,GACvB,OAAO,IAAI,aAAa,CAsB1B;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,cAAc,CAiB5E"}
|
package/dist/single-flight.d.ts
CHANGED
|
@@ -3,10 +3,9 @@ import type { Codec } from './codec.ts';
|
|
|
3
3
|
export interface SingleFlightOptions<T> {
|
|
4
4
|
/**
|
|
5
5
|
* The local folder where the processes that share flights meet. They keep
|
|
6
|
-
* `flight.lock`, `flight.epoch`, `flight.
|
|
7
|
-
* there, so every process
|
|
8
|
-
*
|
|
9
|
-
* a flight across that group. There is no default: one shared default would
|
|
6
|
+
* `flight.lock`, `flight.epoch`, `flight.sock` and the folder
|
|
7
|
+
* `execution-locks` there, so every process that names the same folder is
|
|
8
|
+
* in one group, and callers of one key share a flight across that group. There is no default: one shared default would
|
|
10
9
|
* put every application on the host into one group, and unrelated
|
|
11
10
|
* applications that use the same key would join each other's flights. It
|
|
12
11
|
* must be on a local file system.
|
|
@@ -14,13 +13,6 @@ export interface SingleFlightOptions<T> {
|
|
|
14
13
|
directory: string;
|
|
15
14
|
/** Turns the flight's value into text for the processes that joined it, and back. */
|
|
16
15
|
codec: Codec<T>;
|
|
17
|
-
/**
|
|
18
|
-
* Milliseconds that a new coordinator starts no flight after the previous
|
|
19
|
-
* one stopped, so the leaders of flights in progress can claim them again.
|
|
20
|
-
* A coordinator after a clean shutdown of the one before has none. Must
|
|
21
|
-
* exceed how long a process takes to reconnect. Defaults to 500.
|
|
22
|
-
*/
|
|
23
|
-
graceWindow?: number;
|
|
24
16
|
}
|
|
25
17
|
export interface RunOptions {
|
|
26
18
|
/** Called once, when this caller joins a flight that another caller leads. */
|
|
@@ -42,7 +34,7 @@ export interface FlightValue<T> {
|
|
|
42
34
|
*/
|
|
43
35
|
export declare class SingleFlight<T> implements AsyncDisposable {
|
|
44
36
|
#private;
|
|
45
|
-
constructor({ directory, codec
|
|
37
|
+
constructor({ directory, codec }: SingleFlightOptions<T>);
|
|
46
38
|
/**
|
|
47
39
|
* Runs `work` for `key` as the leader of a new flight, or joins the flight
|
|
48
40
|
* of `key` in progress. `work` gets the flight's lease: its token, and a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"single-flight.d.ts","sourceRoot":"","sources":["../src/single-flight.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"single-flight.d.ts","sourceRoot":"","sources":["../src/single-flight.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAqBxC,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC;;;;;;;;OAQG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAClC,uGAAuG;IACvG,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAClC;AAED,mHAAmH;AACnH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAKD;;;;;;GAMG;AACH,qBAAa,YAAY,CAAC,CAAC,CAAE,YAAW,eAAe;;gBAMzC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAkCxD;;;;;;;;OAQG;IACG,GAAG,CACP,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACxC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAE,UAAe,GAClC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAmB1B,gGAAgG;IAC1F,CAAC,MAAM,CAAC,YAAY,CAAC;CAuC5B"}
|
package/dist/single-flight.js
CHANGED
|
@@ -8,8 +8,11 @@ import { LocalDirectoryConnector } from "./connection/local-directory-connector.
|
|
|
8
8
|
import { socketPathFor } from "./connection/socket-path.js";
|
|
9
9
|
import { FlightServer } from "./coordinator/flight-server.js";
|
|
10
10
|
import { FlightFailedError, FlightInterruptedError, failureOf, } from "./errors.js";
|
|
11
|
-
|
|
11
|
+
import { KeyLocks } from "./key-lock/key-locks.js";
|
|
12
|
+
/** Milliseconds between two attempts to reach the coordinator, to win its term, or to take a key's lock. */
|
|
12
13
|
const POLL_INTERVAL = 10;
|
|
14
|
+
/** The low bits of a token count grants within a term; the high bits are the term's epoch (`EpochTokenSource`). */
|
|
15
|
+
const SEQUENCE_BITS = 32n;
|
|
13
16
|
/**
|
|
14
17
|
* Callers of one key share the flight in progress, in this process and in
|
|
15
18
|
* every process that uses the same directory. One process of the group is
|
|
@@ -22,27 +25,26 @@ export class SingleFlight {
|
|
|
22
25
|
#codec;
|
|
23
26
|
/** The flight servers this process started; disposing closes them. */
|
|
24
27
|
#servers = new AsyncDisposableStack();
|
|
25
|
-
constructor({ directory, codec
|
|
28
|
+
constructor({ directory, codec }) {
|
|
26
29
|
this.#codec = codec;
|
|
27
30
|
const socketPath = socketPathFor(directory);
|
|
31
|
+
const election = flightElection(directory, POLL_INTERVAL);
|
|
32
|
+
const locks = new KeyLocks(directory, POLL_INTERVAL);
|
|
28
33
|
this.#client = new FlightClient(new ConnectionSupervisor(new LocalDirectoryConnector(directory, new ElectingConnector({
|
|
29
34
|
socketPath,
|
|
30
|
-
election
|
|
35
|
+
election,
|
|
31
36
|
pollInterval: POLL_INTERVAL,
|
|
32
37
|
serve: async (term) => {
|
|
33
38
|
// Until serving has fully started, a failure closes the new server, so no server outlives its term.
|
|
34
39
|
await using starting = new AsyncDisposableStack();
|
|
35
|
-
starting.adopt(await FlightServer.start(socketPath, term, {
|
|
36
|
-
// The first term of a directory has no flights to wait for,
|
|
37
|
-
// and neither has a term after a clean shutdown: the
|
|
38
|
-
// coordinator before it left no flight in progress.
|
|
39
|
-
graceWindow: term.epoch > 1n && !term.afterCleanShutdown
|
|
40
|
-
? graceWindow
|
|
41
|
-
: 0,
|
|
42
|
-
}), (started) => started.close());
|
|
40
|
+
starting.adopt(await FlightServer.start(socketPath, term, { locks }), (started) => started.close());
|
|
43
41
|
this.#servers.use(starting.move());
|
|
44
42
|
},
|
|
45
|
-
})))
|
|
43
|
+
}))), {
|
|
44
|
+
locks,
|
|
45
|
+
// The election writes a new term's epoch before its coordinator answers anything.
|
|
46
|
+
isCurrentTerm: async (token) => (await election.lastEpoch()) <= token.value >> SEQUENCE_BITS,
|
|
47
|
+
});
|
|
46
48
|
}
|
|
47
49
|
/**
|
|
48
50
|
* Runs `work` for `key` as the leader of a new flight, or joins the flight
|
|
@@ -77,26 +79,31 @@ export class SingleFlight {
|
|
|
77
79
|
/** Runs the work and lands its outcome. Never rejects, so a caller that stopped waiting leaves no rejection behind. */
|
|
78
80
|
async #fly(key, work, lead) {
|
|
79
81
|
const { token, signal: lost } = lead;
|
|
80
|
-
let text;
|
|
81
82
|
try {
|
|
82
|
-
text
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
let text;
|
|
84
|
+
try {
|
|
85
|
+
text = this.#codec.encode(await work({ token, signal: lost }));
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
lead.land({ failure: failureOf(error) });
|
|
89
|
+
// After a loss, every failure rejects with LeaseLostError: the outcome reaches no joiner (ADR 0007, as ADR 0010 amends it).
|
|
90
|
+
if (!lost.aborted || error === lost.reason)
|
|
91
|
+
return { error };
|
|
92
|
+
return { error: new LeaseLostError(key, { cause: error }) };
|
|
93
|
+
}
|
|
94
|
+
// A lost flight's joiners were told that it is interrupted, so this value reaches nobody.
|
|
95
|
+
if (lost.aborted)
|
|
96
|
+
return { error: lost.reason };
|
|
97
|
+
lead.land({ value: text });
|
|
98
|
+
try {
|
|
99
|
+
return { value: this.#codec.decode(text) };
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
88
102
|
return { error };
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
// A lost flight's joiners were told that it is interrupted, so this value reaches nobody.
|
|
92
|
-
if (lost.aborted)
|
|
93
|
-
return { error: lost.reason };
|
|
94
|
-
lead.land({ value: text });
|
|
95
|
-
try {
|
|
96
|
-
return { value: this.#codec.decode(text) };
|
|
103
|
+
}
|
|
97
104
|
}
|
|
98
|
-
|
|
99
|
-
|
|
105
|
+
finally {
|
|
106
|
+
lead.ended();
|
|
100
107
|
}
|
|
101
108
|
}
|
|
102
109
|
#read(key, outcome) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"single-flight.js","sourceRoot":"","sources":["../src/single-flight.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAa,MAAM,2BAA2B,CAAC;AAEpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,SAAS,GACV,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"single-flight.js","sourceRoot":"","sources":["../src/single-flight.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zukhruf/single-flight",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.21",
|
|
4
4
|
"description": "Callers of one key share the run already in flight, in one process or across the processes that use one directory, and get its outcome instead of a busy error.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
"inter-process"
|
|
22
22
|
],
|
|
23
23
|
"type": "module",
|
|
24
|
-
"types": "./dist/index.d.ts",
|
|
25
24
|
"exports": {
|
|
26
25
|
"./package.json": "./package.json",
|
|
27
26
|
".": {
|
|
@@ -41,13 +40,13 @@
|
|
|
41
40
|
"access": "public"
|
|
42
41
|
},
|
|
43
42
|
"dependencies": {
|
|
44
|
-
"@zukhruf/async": "^0.3.
|
|
45
|
-
"@zukhruf/election": "^0.3.
|
|
46
|
-
"@zukhruf/fencing": "^0.3.
|
|
47
|
-
"@zukhruf/fs": "^0.3.
|
|
48
|
-
"@zukhruf/lease": "^0.3.
|
|
43
|
+
"@zukhruf/async": "^0.3.21",
|
|
44
|
+
"@zukhruf/election": "^0.3.21",
|
|
45
|
+
"@zukhruf/fencing": "^0.3.21",
|
|
46
|
+
"@zukhruf/fs": "^0.3.21",
|
|
47
|
+
"@zukhruf/lease": "^0.3.21"
|
|
49
48
|
},
|
|
50
49
|
"devDependencies": {
|
|
51
|
-
"@zukhruf/mutex": "^0.3.
|
|
50
|
+
"@zukhruf/mutex": "^0.3.21"
|
|
52
51
|
}
|
|
53
52
|
}
|