@kubun/plugin-p2p 0.10.0 → 0.11.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.
Files changed (136) hide show
  1. package/lib/context/delegation.js +130 -1
  2. package/lib/context/group.js +1319 -1
  3. package/lib/context/hub.js +150 -1
  4. package/lib/context/join.js +80 -1
  5. package/lib/context/sync.js +19 -1
  6. package/lib/context/types.d.ts +12 -3
  7. package/lib/context/types.js +28 -1
  8. package/lib/groups/admin-roster.d.ts +130 -0
  9. package/lib/groups/admin-roster.js +184 -0
  10. package/lib/groups/broadcast-codec.js +6 -1
  11. package/lib/groups/broadcast.d.ts +122 -38
  12. package/lib/groups/broadcast.js +627 -1
  13. package/lib/groups/circle-projection.d.ts +122 -0
  14. package/lib/groups/circle-projection.js +288 -0
  15. package/lib/groups/circle-reducers.d.ts +178 -0
  16. package/lib/groups/circle-reducers.js +198 -0
  17. package/lib/groups/events.d.ts +7 -2
  18. package/lib/groups/events.js +10 -1
  19. package/lib/groups/group-anchor.d.ts +63 -0
  20. package/lib/groups/group-anchor.js +107 -0
  21. package/lib/groups/group-crypto.d.ts +19 -0
  22. package/lib/groups/group-crypto.js +85 -0
  23. package/lib/groups/group-handle-registry.d.ts +69 -2
  24. package/lib/groups/group-handle-registry.js +243 -1
  25. package/lib/groups/group-handlers.d.ts +29 -0
  26. package/lib/groups/group-handlers.js +92 -0
  27. package/lib/groups/group-health-monitor.d.ts +56 -0
  28. package/lib/groups/group-health-monitor.js +139 -0
  29. package/lib/groups/group-mls.d.ts +35 -0
  30. package/lib/groups/group-mls.js +164 -0
  31. package/lib/groups/group-peer-manager.d.ts +108 -0
  32. package/lib/groups/group-peer-manager.js +344 -0
  33. package/lib/groups/group-protocols.d.ts +297 -0
  34. package/lib/groups/group-protocols.js +174 -0
  35. package/lib/groups/invite-payload.d.ts +1 -1
  36. package/lib/groups/invite-payload.js +26 -1
  37. package/lib/groups/join-utils.d.ts +1 -0
  38. package/lib/groups/join-utils.js +104 -1
  39. package/lib/groups/ledger-affected-events.d.ts +15 -0
  40. package/lib/groups/ledger-affected-events.js +96 -0
  41. package/lib/groups/ledger-entry.d.ts +37 -0
  42. package/lib/groups/ledger-entry.js +56 -0
  43. package/lib/groups/ledger-fold.d.ts +56 -0
  44. package/lib/groups/ledger-fold.js +53 -0
  45. package/lib/groups/manager.d.ts +29 -71
  46. package/lib/groups/manager.js +281 -1
  47. package/lib/groups/mls-codec.d.ts +1 -1
  48. package/lib/groups/mls-codec.js +18 -1
  49. package/lib/groups/mls-encryptor.d.ts +2 -2
  50. package/lib/groups/mls-encryptor.js +30 -1
  51. package/lib/groups/mls-group-handle.d.ts +1 -1
  52. package/lib/groups/mls-group-handle.js +10 -1
  53. package/lib/groups/mls-json.js +17 -1
  54. package/lib/groups/mls-receive-errors.d.ts +29 -0
  55. package/lib/groups/mls-receive-errors.js +42 -0
  56. package/lib/groups/mls-state.d.ts +1 -1
  57. package/lib/groups/mls-state.js +56 -1
  58. package/lib/groups/store-received-grant.d.ts +4 -1
  59. package/lib/groups/store-received-grant.js +116 -1
  60. package/lib/groups/store-received-revocation.d.ts +3 -3
  61. package/lib/groups/store-received-revocation.js +82 -1
  62. package/lib/hub/ack-backoff.d.ts +22 -0
  63. package/lib/hub/ack-backoff.js +88 -0
  64. package/lib/hub/did-observing-transport.d.ts +13 -2
  65. package/lib/hub/did-observing-transport.js +126 -1
  66. package/lib/hub/errors.d.ts +4 -8
  67. package/lib/hub/errors.js +33 -1
  68. package/lib/hub/http-client.d.ts +28 -3
  69. package/lib/hub/http-client.js +32 -1
  70. package/lib/hub/hub-like.d.ts +24 -0
  71. package/lib/hub/hub-like.js +535 -0
  72. package/lib/hub/manager.d.ts +5 -4
  73. package/lib/hub/manager.js +198 -1
  74. package/lib/hub/wiring.d.ts +28 -41
  75. package/lib/hub/wiring.js +132 -1
  76. package/lib/index.d.ts +29 -13
  77. package/lib/index.js +392 -1
  78. package/lib/protocol.d.ts +8 -1
  79. package/lib/protocol.js +251 -1
  80. package/lib/schema.d.ts +2 -1
  81. package/lib/schema.js +471 -2
  82. package/lib/sync/authorize.d.ts +25 -0
  83. package/lib/sync/authorize.js +73 -0
  84. package/lib/sync/broadcast-queue.js +183 -1
  85. package/lib/sync/broadcast-sender.js +179 -1
  86. package/lib/sync/catalog-scope.js +68 -1
  87. package/lib/sync/errors.d.ts +12 -0
  88. package/lib/sync/errors.js +19 -0
  89. package/lib/sync/forwarder.js +277 -1
  90. package/lib/sync/handlers.d.ts +3 -22
  91. package/lib/sync/handlers.js +160 -1
  92. package/lib/sync/hub-tunnel-sync-listener.d.ts +9 -3
  93. package/lib/sync/hub-tunnel-sync-listener.js +172 -1
  94. package/lib/sync/hub-tunnel-sync-provider.d.ts +14 -14
  95. package/lib/sync/hub-tunnel-sync-provider.js +64 -1
  96. package/lib/sync/merkle-apply.d.ts +5 -0
  97. package/lib/sync/merkle-apply.js +194 -1
  98. package/lib/sync/merkle-channel.d.ts +19 -0
  99. package/lib/sync/merkle-channel.js +182 -1
  100. package/lib/sync/merkle-tree.js +120 -1
  101. package/lib/sync/peer-registry.d.ts +8 -1
  102. package/lib/sync/peer-registry.js +107 -1
  103. package/lib/sync/receive-access-gate.js +24 -1
  104. package/lib/sync/scope-resolver.js +41 -1
  105. package/lib/sync/sync-client.d.ts +5 -3
  106. package/lib/sync/sync-client.js +192 -1
  107. package/lib/sync/sync-manager.d.ts +2 -2
  108. package/lib/sync/sync-manager.js +272 -1
  109. package/lib/types.d.ts +41 -0
  110. package/lib/types.js +2 -1
  111. package/lib/util/mutex.js +10 -1
  112. package/lib/utils.d.ts +5 -0
  113. package/lib/utils.js +21 -0
  114. package/package.json +48 -41
  115. package/lib/groups/broadcast-service.d.ts +0 -189
  116. package/lib/groups/broadcast-service.js +0 -1
  117. package/lib/groups/wire-frame.d.ts +0 -32
  118. package/lib/groups/wire-frame.js +0 -1
  119. package/lib/hub/connection-pool.d.ts +0 -43
  120. package/lib/hub/connection-pool.js +0 -1
  121. package/lib/hub/forward-remote-broadcast.d.ts +0 -15
  122. package/lib/hub/forward-remote-broadcast.js +0 -1
  123. package/lib/hub/group-channel.d.ts +0 -67
  124. package/lib/hub/group-channel.js +0 -1
  125. package/lib/hub/hub-connection.d.ts +0 -96
  126. package/lib/hub/hub-connection.js +0 -1
  127. package/lib/hub/receive-handler.d.ts +0 -33
  128. package/lib/hub/receive-handler.js +0 -1
  129. package/lib/hub/relay-manager.d.ts +0 -127
  130. package/lib/hub/relay-manager.js +0 -1
  131. package/lib/hub/send-handler.d.ts +0 -40
  132. package/lib/hub/send-handler.js +0 -1
  133. package/lib/hub/tunnel-inbox.d.ts +0 -20
  134. package/lib/hub/tunnel-inbox.js +0 -1
  135. package/lib/hub/wait-for-gate.d.ts +0 -14
  136. package/lib/hub/wait-for-gate.js +0 -1
@@ -1,127 +0,0 @@
1
- import type { Client } from '@enkaku/client';
2
- import { EventEmitter } from '@enkaku/event';
3
- import type { HubProtocol } from '@enkaku/hub-protocol';
4
- import type { Logger } from '@kubun/logger';
5
- import type { P2PStoreAPI } from '@kubun/store-p2p';
6
- import type { GroupBroadcastMessage } from '../groups/broadcast.js';
7
- import type { BroadcastService } from '../groups/broadcast-service.js';
8
- import { type OnServerDIDObserved } from './connection-pool.js';
9
- import type { ServerDIDMismatchObserver, ServerDIDObserver } from './did-observing-transport.js';
10
- import { GroupChannel, type GroupChannelParams } from './group-channel.js';
11
- import { HubConnection, type HubConnectionParams } from './hub-connection.js';
12
- export type CreateHubClientOptions = {
13
- /**
14
- * Per-call observer fired once when the first signed response arrives. Set
15
- * by `HubConnectionPool` so the pool can route the observed server DID
16
- * through its `onServerDIDObserved` handler with the hub URL bound. Factories
17
- * that don't honour this opt simply skip TOFU capture for pool-spawned
18
- * clients.
19
- */
20
- onServerDID?: ServerDIDObserver;
21
- /**
22
- * Pinned `server_did` resolved by the pool from the local hub row at spawn
23
- * time. When set, the underlying `DIDObservingTransport` enforces it: a
24
- * non-matching first response raises `HubServerDIDMismatchError` and no
25
- * message is delivered to the inner Client (hard-fail path). When unset,
26
- * the wrapper falls back to TOFU capture via `onServerDID`.
27
- */
28
- expectedServerDID?: string;
29
- /**
30
- * Side-channel notifier fired when a pinned-DID mismatch is detected. Lets
31
- * the pool log + evict on top of the typed error that propagates through
32
- * the read loop.
33
- */
34
- onMismatch?: ServerDIDMismatchObserver;
35
- };
36
- export type CreateHubClient = (hubURL: string, opts?: CreateHubClientOptions) => Promise<Client<HubProtocol>>;
37
- export type CreateGroupChannel = (params: GroupChannelParams) => GroupChannel;
38
- export type CreateHubConnection = (params: HubConnectionParams) => HubConnection;
39
- export type RelayEvent = {
40
- type: 'connected';
41
- hubURL: string;
42
- groupIDs: Array<string>;
43
- } | {
44
- type: 'disconnected';
45
- hubURL: string;
46
- groupIDs: Array<string>;
47
- reason?: string;
48
- } | {
49
- type: 'error';
50
- hubURL: string;
51
- groupIDs: Array<string>;
52
- error: unknown;
53
- } | {
54
- type: 'reconnecting';
55
- hubURL: string;
56
- groupIDs: Array<string>;
57
- attempt: number;
58
- delayMs: number;
59
- };
60
- type RelayManagerEvents = {
61
- relay: RelayEvent;
62
- };
63
- export type HubRelayManagerParams = {
64
- p2pStore: P2PStoreAPI;
65
- broadcastService: BroadcastService;
66
- deviceID: string;
67
- createHubClient: CreateHubClient;
68
- logger?: Logger;
69
- ackFlushMs?: number;
70
- ackFlushMax?: number;
71
- backoffBaseMs?: number;
72
- backoffMaxMs?: number;
73
- backoffJitter?: number;
74
- broadcastTimeoutMs?: number;
75
- createGroupChannel?: CreateGroupChannel;
76
- createHubConnection?: CreateHubConnection;
77
- /**
78
- * Forwarded to the underlying `HubConnectionPool`. Fired with the observed
79
- * `serverDID` after the first signed response from a freshly-spawned client.
80
- * Used by the hub manager to apply the TOFU capture conflict matrix (the
81
- * `null → DID-A` upgrade). Errors are caught and logged by the pool;
82
- * pinned-mismatch enforcement is a separate path inside the wrapper.
83
- */
84
- onServerDIDObserved?: OnServerDIDObserved;
85
- };
86
- export declare class HubRelayManager {
87
- #private;
88
- readonly relayEvents: EventEmitter<RelayManagerEvents>;
89
- constructor(params: HubRelayManagerParams);
90
- /**
91
- * Open channels for each group in parallel. Per-group failures are logged
92
- * and swallowed: this method never rejects, so a single bad group does not
93
- * block the others from starting. Callers who need per-group readiness
94
- * signals must check `addGroup` / `broadcast` results individually.
95
- */
96
- start(groupIDs: Array<string>): Promise<void>;
97
- addGroup(groupID: string): Promise<void>;
98
- /**
99
- * Post-join binding-add hook. Single-hub regime — when the group already
100
- * has an active channel:
101
- * - same hubURL → debug log, no-op (idempotent re-bind);
102
- * - different hubURL → warn, no-op (extra binding deferred until
103
- * multi-hub support lands).
104
- * When the group has no active channel yet (e.g. binding was created
105
- * before relay setup ran), open one for the supplied hub.
106
- */
107
- addBinding(groupID: string, hubURL: string): Promise<void>;
108
- /**
109
- * Post-join binding-remove hook. No-op when the unbound hub is not the
110
- * group's active hub (single-hub regime: unbinding a non-active hub leaves
111
- * the channel intact). When it matches, behaves like `removeGroup`.
112
- */
113
- removeBinding(groupID: string, hubURL: string): Promise<void>;
114
- removeGroup(groupID: string): Promise<void>;
115
- broadcast(groupID: string, message: GroupBroadcastMessage, opts?: {
116
- timeoutMs?: number;
117
- }): Promise<void>;
118
- /**
119
- * Fan out a pre-produced MLS handshake Commit to a group's existing members
120
- * via its active channel. Mirrors {@link broadcast}.
121
- */
122
- sendCommit(groupID: string, commitBytes: Uint8Array, opts?: {
123
- timeoutMs?: number;
124
- }): Promise<void>;
125
- stop(): Promise<void>;
126
- }
127
- export {};
@@ -1 +0,0 @@
1
- import{EventEmitter as e}from"@enkaku/event";import{HubConnectionPool as t}from"./connection-pool.js";import{GroupChannel as s}from"./group-channel.js";import{HubConnection as r}from"./hub-connection.js";export class HubRelayManager{#e;#t;#s;#r;#o;#a;#i;#n;#l;#c;#u;#h;#p;#g=new Map;#d=new Map;#b=new Map;#f=new Map;relayEvents;constructor(o){this.#e=o.p2pStore,this.#t=o.broadcastService,this.#s=o.deviceID,this.#o=o.logger,this.#a=o.ackFlushMs??500,this.#i=o.ackFlushMax??10,this.#n=o.backoffBaseMs??1e3,this.#l=o.backoffMaxMs??6e4,this.#c=o.backoffJitter??.25,this.#u=o.broadcastTimeoutMs;let a=o.p2pStore;this.#r=new t({createHubClient:o.createHubClient,logger:o.logger,onServerDIDObserved:o.onServerDIDObserved,resolvePinnedDID:async e=>{let t=await a.getHubByURL(e);return t?.server_did??null}}),this.#h=o.createGroupChannel??(e=>new s(e)),this.#p=o.createHubConnection??(e=>new r(e)),this.relayEvents=new e}async #M(e){let t=this.#g.get(e);if(null!=t)return t;let s=this.#p({hubURL:e,deviceID:this.#s,pool:this.#r,logger:this.#o,ackFlushMs:this.#a,ackFlushMax:this.#i,backoffBaseMs:this.#n,backoffMaxMs:this.#l,backoffJitter:this.#c});try{await s.open()}catch(e){try{await s.dispose()}catch{}throw e}let r=s.events.on("lifecycle",async t=>{let s=Object.freeze([...this.#b.entries()].filter(([,t])=>t===e).map(([e])=>e));await this.relayEvents.emit("relay",{...t,hubURL:e,groupIDs:s})});return this.#f.set(e,r),this.#g.set(e,s),s}async start(e){(await Promise.allSettled(e.map(e=>this.addGroup(e)))).forEach((t,s)=>{"rejected"===t.status&&this.#o?.error("addGroup failed during start",{groupID:e[s],error:t.reason})})}async addGroup(e){if(this.#d.has(e))return void this.#o?.debug("group already added",{groupID:e});if(null==await this.#e.getGroup(e))throw Error(`Group ${e} not found`);let t=await this.#e.listHubsByGroupID(e);0===t.length?this.#o?.warn("group has no hub bindings, skipping",{groupID:e}):(t.length>1&&this.#o?.warn("multi-hub not yet supported, using first",{groupID:e,hubURLs:t.map(e=>e.url)}),await this.#m(e,t[0].url))}async #m(e,t){let s=await this.#M(t),r=this.#h({groupID:e,deviceID:this.#s,hubConnection:s,broadcastService:this.#t,p2pStore:this.#e,logger:this.#o});await r.open(),this.#d.set(e,r),this.#b.set(e,t)}async addBinding(e,t){let s=this.#b.get(e);null!=s?s===t?this.#o?.debug("binding already active",{groupID:e,hubURL:t}):this.#o?.warn("multi-hub not yet supported, ignoring extra binding",{groupID:e,activeHubURL:s,newHubURL:t}):await this.#m(e,t)}async removeBinding(e,t){this.#b.get(e)!==t?this.#o?.debug("binding not active, ignoring removal",{groupID:e,hubURL:t}):await this.removeGroup(e)}async removeGroup(e){let t=this.#d.get(e);null!=t&&(await t.close(),this.#d.delete(e),this.#b.delete(e))}async broadcast(e,t,s){let r=this.#d.get(e);if(null==r)throw Error(`Group ${e} is not active in HubRelayManager`);let o=s?.timeoutMs??this.#u;await r.broadcast(t,{timeoutMs:o})}async sendCommit(e,t,s){let r=this.#d.get(e);if(null==r)throw Error(`Group ${e} is not active in HubRelayManager`);let o=s?.timeoutMs??this.#u;await r.sendCommit(t,{timeoutMs:o})}async stop(){for(let e of this.#f.values())e();this.#f.clear();let e=[...this.#d.values()];this.#d.clear(),this.#b.clear(),(await Promise.allSettled(e.map(e=>e.close()))).forEach(e=>{"rejected"===e.status&&this.#o?.warn("GroupChannel close failed",{error:e.reason})});let t=[...this.#g.values()];this.#g.clear(),(await Promise.allSettled(t.map(e=>e.close()))).forEach(e=>{"rejected"===e.status&&this.#o?.warn("HubConnection close failed",{error:e.reason})}),await this.#r.disposeAll()}}
@@ -1,40 +0,0 @@
1
- import type { Logger } from '@kubun/logger';
2
- import type { GroupBroadcastMessage } from '../groups/broadcast.js';
3
- import type { BroadcastService } from '../groups/broadcast-service.js';
4
- export type HandleSendBroadcastParams = {
5
- groupID: string;
6
- message: GroupBroadcastMessage;
7
- broadcastService: BroadcastService;
8
- /**
9
- * Wire-send callback. Receives the groupID and the base64-encoded encrypted
10
- * MLS application message, and is expected to fan it out via the underlying
11
- * hub transport (e.g. `HubConnection.broadcast`). Decoupling from the hub
12
- * client lets callers serialize the send through a per-group mutex shared
13
- * with the receive path.
14
- */
15
- send: (groupID: string, payloadB64: string) => Promise<void>;
16
- logger?: Logger;
17
- };
18
- /**
19
- * Encrypt + send a broadcast for a group. MLS state persistence is handled
20
- * by the registry inside `BroadcastService.prepareSend`. Wedge protection
21
- * (failed send → no advance of persisted state) is preserved because send
22
- * runs INSIDE the registry's per-group lock and a throw skips persist.
23
- */
24
- export declare function handleSendBroadcast(params: HandleSendBroadcastParams): Promise<void>;
25
- export type HandleSendCommitParams = {
26
- groupID: string;
27
- /** Framed MLSMessage(Commit) bytes from the committer's `GroupManager` call. */
28
- commitBytes: Uint8Array;
29
- broadcastService: BroadcastService;
30
- /** Same wire-send callback as {@link HandleSendBroadcastParams.send}. */
31
- send: (groupID: string, payloadB64: string) => Promise<void>;
32
- logger?: Logger;
33
- };
34
- /**
35
- * Fan out a pre-produced MLS handshake Commit to a group's existing members.
36
- * Unlike {@link handleSendBroadcast} this does not encrypt — the Commit is
37
- * already a self-protected `PrivateMessage`; `prepareSendCommit` just tags it
38
- * `mls` on the wire.
39
- */
40
- export declare function handleSendCommit(params: HandleSendCommitParams): Promise<void>;
@@ -1 +0,0 @@
1
- import{toB64 as r}from"@enkaku/codec";export async function handleSendBroadcast(e){let{groupID:o,message:t,broadcastService:a,send:n,logger:d}=e;try{await a.prepareSend({groupID:o,message:t,send:e=>n(o,r(e))})}catch(r){throw d?.error("hub/group/send failed",{groupID:o,error:r}),r}}export async function handleSendCommit(e){let{groupID:o,commitBytes:t,broadcastService:a,send:n,logger:d}=e;try{await a.prepareSendCommit({groupID:o,commitBytes:t,send:e=>n(o,r(e))})}catch(r){throw d?.error("hub/group/send (mls commit) failed",{groupID:o,error:r}),r}}
@@ -1,20 +0,0 @@
1
- import type { StoredMessage } from '@enkaku/hub-protocol';
2
- /**
3
- * FIFO queue used by `HubConnection.tunnelMessageStream` to deliver synthetic /
4
- * forwarded `StoredMessage` values to tunnel transports. Callers `push` messages;
5
- * each consumer drains via an `AsyncIterator` returned from
6
- * {@link TunnelInbox.iterator}. Disposal is idempotent: once `close` runs,
7
- * subsequent pushes are dropped and pending iteration resolves to `done`.
8
- *
9
- * Multiple sequential iterators are supported (one consumer at a time, but
10
- * iterators may be created and returned across the inbox's lifetime). Each
11
- * iterator tracks its own pending waiter so `iterator.return()` drains only
12
- * that iterator's waiter — leaving queued messages and other consumers'
13
- * waiters intact for future iterators.
14
- */
15
- export declare class TunnelInbox {
16
- #private;
17
- push(message: StoredMessage): void;
18
- close(): void;
19
- iterator(onReturn?: () => void): AsyncIterator<StoredMessage>;
20
- }
@@ -1 +0,0 @@
1
- export class TunnelInbox{#e=[];#s=[];#t=!1;push(e){if(this.#t)return;let s=this.#s.shift();null!=s?s.resolve({value:e,done:!1}):this.#e.push(e)}close(){if(!this.#t)for(this.#t=!0;this.#s.length>0;){let e=this.#s.shift();e?.resolve({value:void 0,done:!0})}}iterator(e){let s=!1,t=new Set,i=()=>{if(!s){for(let e of(s=!0,t)){let s=this.#s.indexOf(e);s>=0&&this.#s.splice(s,1),e.resolve({value:void 0,done:!0})}t.clear(),e?.()}};return{next:()=>{let e;return s?Promise.resolve({value:void 0,done:!0}):this.#e.length>0?Promise.resolve({value:this.#e.shift(),done:!1}):this.#t?(i(),Promise.resolve({value:void 0,done:!0})):new Promise(s=>{e={resolve:s},t.add(e),this.#s.push(e)}).then(s=>(null!=e&&t.delete(e),!0===s.done&&i(),s))},return:()=>(i(),Promise.resolve({value:void 0,done:!0}))}}}
@@ -1,14 +0,0 @@
1
- /**
2
- * Wait for a reconnect gate (or similar boolean-style gate) to clear, with an
3
- * optional timeout. If `gate` is null the call returns immediately without
4
- * allocating any timer or signal — this is the hot path while the connection
5
- * is healthy. If `timeoutMs` is undefined we simply await the gate without
6
- * arming a timer (callers with no timeout can also omit `onTimeout` to skip
7
- * the closure allocation). Both set: race the gate against
8
- * `AbortSignal.timeout`; the first to settle wins.
9
- *
10
- * `onTimeout` is invoked lazily — only when the timeout actually fires — so
11
- * callers can capture mutable state (e.g. current reconnect attempt) at the
12
- * moment of failure rather than at call time.
13
- */
14
- export declare function waitForGate(gate: Promise<void> | null, timeoutMs: number | undefined, onTimeout?: () => Error): Promise<void>;
@@ -1 +0,0 @@
1
- export async function waitForGate(e,t,r){if(null==e)return;if(null==t)return void await e;if(null==r)throw Error("waitForGate: onTimeout required when timeoutMs is set");let i=AbortSignal.timeout(t),n=null,o=new Promise((e,t)=>{i.aborted?t(r()):(n=()=>{t(r())},i.addEventListener("abort",n,{once:!0}))});try{await Promise.race([e,o])}finally{null!=n&&i.removeEventListener("abort",n)}}