@masons/runtime-broker 0.1.1 → 0.2.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/dist/broker/broker-daemon.d.ts +7 -0
- package/dist/broker/broker-daemon.d.ts.map +1 -1
- package/dist/broker/broker-daemon.js +247 -30
- package/dist/broker/connector-ws.d.ts +4 -0
- package/dist/broker/connector-ws.d.ts.map +1 -1
- package/dist/broker/connector-ws.js +12 -0
- package/dist/broker/delivery-cursor-file.d.ts +8 -0
- package/dist/broker/delivery-cursor-file.d.ts.map +1 -0
- package/dist/broker/delivery-cursor-file.js +71 -0
- package/dist/broker/entry.d.ts.map +1 -1
- package/dist/broker/entry.js +4 -1
- package/dist/broker/ipc-server.d.ts +11 -2
- package/dist/broker/ipc-server.d.ts.map +1 -1
- package/dist/broker/ipc-server.js +32 -7
- package/dist/broker/paths.d.ts +1 -0
- package/dist/broker/paths.d.ts.map +1 -1
- package/dist/broker/paths.js +1 -0
- package/dist/broker/reconnecting-buffer.d.ts +1 -0
- package/dist/broker/reconnecting-buffer.d.ts.map +1 -1
- package/dist/broker/runtime-endpoint-port.d.ts +2 -0
- package/dist/broker/runtime-endpoint-port.d.ts.map +1 -1
- package/dist/broker/runtime-inbound-routed-emitter.d.ts +22 -0
- package/dist/broker/runtime-inbound-routed-emitter.d.ts.map +1 -0
- package/dist/broker/runtime-inbound-routed-emitter.js +147 -0
- package/dist/broker/runtime-inbound-routed-event-types.d.ts +10 -0
- package/dist/broker/runtime-inbound-routed-event-types.d.ts.map +1 -0
- package/dist/broker/runtime-inbound-routed-event-types.js +1 -0
- package/dist/broker/undispatched-changed-event-types.d.ts +1 -0
- package/dist/broker/undispatched-changed-event-types.d.ts.map +1 -1
- package/dist/broker/undispatched-inbox.d.ts +1 -0
- package/dist/broker/undispatched-inbox.d.ts.map +1 -1
- package/dist/broker/version-handshake.d.ts +5 -1
- package/dist/broker/version-handshake.d.ts.map +1 -1
- package/dist/broker/version-handshake.js +3 -2
- package/dist/broker-client/broker-client.d.ts +14 -0
- package/dist/broker-client/broker-client.d.ts.map +1 -1
- package/dist/broker-client/broker-client.js +57 -0
- package/dist/connector-client.d.ts +5 -0
- package/dist/connector-client.d.ts.map +1 -1
- package/dist/connector-client.js +47 -5
- package/dist/runtime-endpoint-client.d.ts +4 -0
- package/dist/runtime-endpoint-client.d.ts.map +1 -1
- package/dist/runtime-endpoint-client.js +33 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@ import type { NetworkPresenceChangedEvent } from "./network-presence-changed-eve
|
|
|
11
11
|
import type { BrokerPaths } from "./paths.js";
|
|
12
12
|
import { type BufferedMessage } from "./reconnecting-buffer.js";
|
|
13
13
|
import type { RuntimeEndpointPort } from "./runtime-endpoint-port.js";
|
|
14
|
+
import type { RuntimeInboundRoutedEvent } from "./runtime-inbound-routed-event-types.js";
|
|
14
15
|
import type { ServicesEventClient } from "./services-event-client.js";
|
|
15
16
|
import type { SpawnDriverRegistry } from "./spawn-driver.js";
|
|
16
17
|
import { type UndispatchedChangedEvent } from "./undispatched-changed-event-types.js";
|
|
@@ -49,6 +50,11 @@ export interface BrokerDaemonOptions {
|
|
|
49
50
|
networkPresenceChangedBackoffInitialMs?: number;
|
|
50
51
|
networkPresenceChangedBackoffMaxMs?: number;
|
|
51
52
|
networkPresenceChangedMaxRetries?: number;
|
|
53
|
+
runtimeInboundRoutedPost?: (event: RuntimeInboundRoutedEvent) => Promise<import("./runtime-endpoint-port.js").EmitOutcome>;
|
|
54
|
+
runtimeInboundRoutedCapacity?: number;
|
|
55
|
+
runtimeInboundRoutedBackoffInitialMs?: number;
|
|
56
|
+
runtimeInboundRoutedBackoffMaxMs?: number;
|
|
57
|
+
runtimeInboundRoutedMaxRetries?: number;
|
|
52
58
|
}
|
|
53
59
|
export interface RunningBroker {
|
|
54
60
|
bearerToken: string;
|
|
@@ -64,6 +70,7 @@ export interface RunningBroker {
|
|
|
64
70
|
replyCorrelationCacheSize(endpoint_id: string): number;
|
|
65
71
|
undispatchedChangedQueueSize(): number;
|
|
66
72
|
networkPresenceChangedQueueSize(): number;
|
|
73
|
+
runtimeInboundRoutedQueueSize(): number;
|
|
67
74
|
}
|
|
68
75
|
export declare function startBrokerDaemon(opts: BrokerDaemonOptions): Promise<RunningBroker>;
|
|
69
76
|
export declare function readPluginPidHeader(req: IncomingMessage): number | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broker-daemon.d.ts","sourceRoot":"","sources":["../../src/broker/broker-daemon.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAqB,KAAK,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"broker-daemon.d.ts","sourceRoot":"","sources":["../../src/broker/broker-daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { spawn as spawnChild } from "node:child_process";
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { basename } from "node:path";
|
|
4
4
|
import { createControlEventDispatcher, } from "./control-event-dispatcher.js";
|
|
5
|
+
import { readDeliveryCursorFile, writeDeliveryCursorFile, } from "./delivery-cursor-file.js";
|
|
5
6
|
import { deleteDiscoveryFile, mintBearerToken, writeDiscoveryFile, } from "./discovery-file.js";
|
|
6
7
|
import { EndpointRegistry } from "./endpoint-registry.js";
|
|
7
8
|
import { DEFAULT_GRACE_MS, transition, } from "./endpoint-state-machine.js";
|
|
@@ -13,12 +14,14 @@ import { isPluginAlive as defaultIsPluginAlive } from "./plugin-liveness.js";
|
|
|
13
14
|
import { createReceivedMessageCorrelationCache, DEFAULT_TTL_MS as DEFAULT_REPLY_CORRELATION_TTL_MS, } from "./received-message-correlation-cache.js";
|
|
14
15
|
import { createReconnectingBufferManager, } from "./reconnecting-buffer.js";
|
|
15
16
|
import { RoutingTable } from "./routing-table.js";
|
|
17
|
+
import { createRuntimeInboundRoutedEmitter, postRuntimeInboundRoutedViaPort, } from "./runtime-inbound-routed-emitter.js";
|
|
16
18
|
import { createSpawnCorrelationManager, createSpawnRateLimiter, } from "./spawn-correlation.js";
|
|
17
19
|
import { TASK_HINT_MAX_LENGTH, updateTaskHint } from "./task-hint-handler.js";
|
|
18
20
|
import { createTransitionStateRetryQueue } from "./transition-state-retry-queue.js";
|
|
19
21
|
import { CONTENT_PREVIEW_MAX_CODEPOINTS, truncateContentPreview, } from "./undispatched-changed-event-types.js";
|
|
20
22
|
import { createUndispatchedChangedEmitter, postUndispatchedChangedViaPort, } from "./undispatched-emitter.js";
|
|
21
23
|
import { createUndispatchedInbox, } from "./undispatched-inbox.js";
|
|
24
|
+
const REMOTE_SPAWN_CAPABILITY = "remote_spawn_v1";
|
|
22
25
|
function derivePublicDisplayLabel(body) {
|
|
23
26
|
const parts = [
|
|
24
27
|
body.kind,
|
|
@@ -40,6 +43,11 @@ export async function startBrokerDaemon(opts) {
|
|
|
40
43
|
(async () => {
|
|
41
44
|
});
|
|
42
45
|
void _asNodeIdReservedForA3;
|
|
46
|
+
const persistedDeliveryCursor = readDeliveryCursorFile(paths.deliveryCursorFile) ?? 0;
|
|
47
|
+
connector.setDeliveryCursor(persistedDeliveryCursor);
|
|
48
|
+
logger.info("delivery_cursor_loaded", {
|
|
49
|
+
lastKnownSeq: persistedDeliveryCursor,
|
|
50
|
+
});
|
|
43
51
|
logger.info("connecting_to_connector");
|
|
44
52
|
await connector.connect();
|
|
45
53
|
logger.info("connector_connected");
|
|
@@ -86,7 +94,8 @@ export async function startBrokerDaemon(opts) {
|
|
|
86
94
|
}
|
|
87
95
|
}, replyCorrelationSweepInterval);
|
|
88
96
|
replyCorrelationSweepTimer.unref?.();
|
|
89
|
-
const
|
|
97
|
+
const postUndispatchedChanged = opts.undispatchedChangedPost ?? postUndispatchedChangedViaPort(apiPort);
|
|
98
|
+
const undispatchedEmitter = createUndispatchedChangedEmitter(postUndispatchedChanged, logger, {
|
|
90
99
|
capacity: opts.undispatchedChangedCapacity,
|
|
91
100
|
backoffInitialMs: opts.undispatchedChangedBackoffInitialMs,
|
|
92
101
|
backoffMaxMs: opts.undispatchedChangedBackoffMaxMs,
|
|
@@ -105,6 +114,79 @@ export async function startBrokerDaemon(opts) {
|
|
|
105
114
|
const emitNetworkPresence = (event) => {
|
|
106
115
|
networkPresenceEmitter.enqueue(event);
|
|
107
116
|
};
|
|
117
|
+
const postRuntimeInboundRouted = opts.runtimeInboundRoutedPost ?? postRuntimeInboundRoutedViaPort(apiPort);
|
|
118
|
+
const runtimeInboundRoutedEmitter = createRuntimeInboundRoutedEmitter(postRuntimeInboundRouted, logger, {
|
|
119
|
+
capacity: opts.runtimeInboundRoutedCapacity,
|
|
120
|
+
backoffInitialMs: opts.runtimeInboundRoutedBackoffInitialMs,
|
|
121
|
+
backoffMaxMs: opts.runtimeInboundRoutedBackoffMaxMs,
|
|
122
|
+
maxRetries: opts.runtimeInboundRoutedMaxRetries,
|
|
123
|
+
});
|
|
124
|
+
const directPostUndispatched = async (event, opts = {}) => {
|
|
125
|
+
let outcome;
|
|
126
|
+
try {
|
|
127
|
+
outcome = await postUndispatchedChanged(event);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
outcome = {
|
|
131
|
+
ok: false,
|
|
132
|
+
terminal: false,
|
|
133
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (outcome.ok)
|
|
137
|
+
return true;
|
|
138
|
+
logger.warn("undispatched_changed_direct_post_failed", {
|
|
139
|
+
action: event.action,
|
|
140
|
+
undispatched_id: event.undispatched_id,
|
|
141
|
+
terminal: outcome.terminal,
|
|
142
|
+
status: outcome.status,
|
|
143
|
+
detail: outcome.detail,
|
|
144
|
+
});
|
|
145
|
+
if (!outcome.terminal && opts.queueOnTransientFailure !== false) {
|
|
146
|
+
undispatchedEmitter.enqueue(event);
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
};
|
|
150
|
+
const directPostRuntimeInboundRouted = async (event, opts = {}) => {
|
|
151
|
+
let outcome;
|
|
152
|
+
try {
|
|
153
|
+
outcome = await postRuntimeInboundRouted(event);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
outcome = {
|
|
157
|
+
ok: false,
|
|
158
|
+
terminal: false,
|
|
159
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (outcome.ok)
|
|
163
|
+
return true;
|
|
164
|
+
logger.warn("runtime_inbound_routed_direct_post_failed", {
|
|
165
|
+
source_message_id: event.source_message_id,
|
|
166
|
+
routed_to_endpoint_id: event.routed_to_endpoint_id,
|
|
167
|
+
terminal: outcome.terminal,
|
|
168
|
+
status: outcome.status,
|
|
169
|
+
detail: outcome.detail,
|
|
170
|
+
});
|
|
171
|
+
if (!outcome.terminal && opts.queueOnTransientFailure !== false) {
|
|
172
|
+
runtimeInboundRoutedEmitter.enqueue(event);
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
};
|
|
176
|
+
const markDeliverySeqAccepted = (seq) => {
|
|
177
|
+
if (typeof seq !== "number")
|
|
178
|
+
return true;
|
|
179
|
+
const before = connector.getDeliveryCursor();
|
|
180
|
+
const prepared = connector.prepareDeliverySeqAccepted(seq);
|
|
181
|
+
if (typeof prepared === "number" && prepared !== before) {
|
|
182
|
+
writeDeliveryCursorFile(paths.deliveryCursorFile, prepared);
|
|
183
|
+
}
|
|
184
|
+
const after = connector.markDeliverySeqAccepted(seq);
|
|
185
|
+
if (after !== prepared) {
|
|
186
|
+
throw new Error("delivery cursor changed during durable commit");
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
};
|
|
108
190
|
const recordInboundCorrelation = (endpoint_id, metadata) => {
|
|
109
191
|
const c = metadata && typeof metadata.correlation_id === "string"
|
|
110
192
|
? metadata.correlation_id
|
|
@@ -113,6 +195,7 @@ export async function startBrokerDaemon(opts) {
|
|
|
113
195
|
return;
|
|
114
196
|
replyCorrelationCache.record(endpoint_id, c);
|
|
115
197
|
};
|
|
198
|
+
const channels = new Map();
|
|
116
199
|
let networkPresence = "offline";
|
|
117
200
|
let presenceGraceTimer;
|
|
118
201
|
const applyPresenceTransition = (event) => {
|
|
@@ -150,6 +233,15 @@ export async function startBrokerDaemon(opts) {
|
|
|
150
233
|
ts: Date.now(),
|
|
151
234
|
reason: effect.reason,
|
|
152
235
|
});
|
|
236
|
+
if (channels.size > 0) {
|
|
237
|
+
for (const ws of channels.values()) {
|
|
238
|
+
pushToPlugin(ws, {
|
|
239
|
+
event: "presence_changed",
|
|
240
|
+
presence: effect.presence,
|
|
241
|
+
...(effect.reason ? { reason: effect.reason } : {}),
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
153
245
|
if (effect.presence === "online") {
|
|
154
246
|
void retryQueue.flush(async (entry) => {
|
|
155
247
|
await apiPort.transitionState(entry.endpoint_id, entry.params);
|
|
@@ -166,7 +258,6 @@ export async function startBrokerDaemon(opts) {
|
|
|
166
258
|
connector.on("disconnected", () => {
|
|
167
259
|
applyPresenceTransition({ type: "connector_disconnected" });
|
|
168
260
|
});
|
|
169
|
-
const channels = new Map();
|
|
170
261
|
const wsEndpoints = new WeakMap();
|
|
171
262
|
const heartbeatTimers = new Map();
|
|
172
263
|
const displacedWs = new WeakSet();
|
|
@@ -276,6 +367,9 @@ export async function startBrokerDaemon(opts) {
|
|
|
276
367
|
content: msg.envelope.content,
|
|
277
368
|
contentType: msg.envelope.contentType,
|
|
278
369
|
metadata: msg.envelope.metadata,
|
|
370
|
+
...(msg.envelope.sourceMessageId !== undefined && {
|
|
371
|
+
sourceMessageId: msg.envelope.sourceMessageId,
|
|
372
|
+
}),
|
|
279
373
|
});
|
|
280
374
|
}
|
|
281
375
|
}
|
|
@@ -317,6 +411,9 @@ export async function startBrokerDaemon(opts) {
|
|
|
317
411
|
version: 1,
|
|
318
412
|
action: "remove",
|
|
319
413
|
undispatched_id: event.undispatched_id,
|
|
414
|
+
...(taken.source_message_id !== undefined && {
|
|
415
|
+
source_message_id: taken.source_message_id,
|
|
416
|
+
}),
|
|
320
417
|
remove_reason: "lost_at_capacity_during_redispatch_bounce",
|
|
321
418
|
});
|
|
322
419
|
}
|
|
@@ -335,6 +432,9 @@ export async function startBrokerDaemon(opts) {
|
|
|
335
432
|
dispatched_from: event.undispatched_id,
|
|
336
433
|
dispatched_by: "passport",
|
|
337
434
|
},
|
|
435
|
+
...(taken.source_message_id !== undefined && {
|
|
436
|
+
sourceMessageId: taken.source_message_id,
|
|
437
|
+
}),
|
|
338
438
|
};
|
|
339
439
|
recordInboundCorrelation(target.endpoint_id, stamped.metadata);
|
|
340
440
|
if (target.state === "active") {
|
|
@@ -350,6 +450,9 @@ export async function startBrokerDaemon(opts) {
|
|
|
350
450
|
content: taken.content,
|
|
351
451
|
contentType: taken.content_type,
|
|
352
452
|
metadata: stamped.metadata,
|
|
453
|
+
...(stamped.sourceMessageId !== undefined && {
|
|
454
|
+
sourceMessageId: stamped.sourceMessageId,
|
|
455
|
+
}),
|
|
353
456
|
},
|
|
354
457
|
});
|
|
355
458
|
}
|
|
@@ -363,6 +466,9 @@ export async function startBrokerDaemon(opts) {
|
|
|
363
466
|
version: 1,
|
|
364
467
|
action: "dispatch",
|
|
365
468
|
undispatched_id: event.undispatched_id,
|
|
469
|
+
...(taken.source_message_id !== undefined && {
|
|
470
|
+
source_message_id: taken.source_message_id,
|
|
471
|
+
}),
|
|
366
472
|
dispatched_to_endpoint_id: event.target_endpoint_id,
|
|
367
473
|
});
|
|
368
474
|
return { ok: true };
|
|
@@ -452,6 +558,13 @@ export async function startBrokerDaemon(opts) {
|
|
|
452
558
|
throw new BrokerHttpError(400, "ipc_channel_missing", `no open IPC channel for plugin_pid=${body.plugin_pid}; ` +
|
|
453
559
|
"open WS /v1/stream with x-plugin-pid header first");
|
|
454
560
|
}
|
|
561
|
+
const spawnDriver = spawnDriverRegistry?.lookup(body.kind);
|
|
562
|
+
const spawnAvailability = spawnDriver
|
|
563
|
+
? await spawnDriver.isAvailable().catch(() => ({ available: false }))
|
|
564
|
+
: null;
|
|
565
|
+
const runtimeCapabilities = spawnAvailability?.available
|
|
566
|
+
? [REMOTE_SPAWN_CAPABILITY]
|
|
567
|
+
: undefined;
|
|
455
568
|
const apiResp = await apiPort.register({
|
|
456
569
|
runtime_kind: body.kind,
|
|
457
570
|
endpoint_nonce: randomUUID(),
|
|
@@ -462,6 +575,7 @@ export async function startBrokerDaemon(opts) {
|
|
|
462
575
|
tracking_ref: body.tracking_ref,
|
|
463
576
|
session_name: body.session_name,
|
|
464
577
|
task_hint: body.task_hint,
|
|
578
|
+
runtime_capabilities: runtimeCapabilities,
|
|
465
579
|
});
|
|
466
580
|
registry.register({
|
|
467
581
|
endpoint_id: apiResp.endpoint_id,
|
|
@@ -571,7 +685,19 @@ export async function startBrokerDaemon(opts) {
|
|
|
571
685
|
source_endpoint_id: body.endpoint_id,
|
|
572
686
|
require_live: body.require_live ?? false,
|
|
573
687
|
};
|
|
574
|
-
|
|
688
|
+
let ack;
|
|
689
|
+
try {
|
|
690
|
+
ack = await connector.send(body.to, body.content, body.contentType ?? "text", metadata);
|
|
691
|
+
}
|
|
692
|
+
catch (err) {
|
|
693
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
694
|
+
if (msg === "WebSocket not connected" || msg === "SEND_ACK timeout") {
|
|
695
|
+
throw new BrokerHttpError(503, "connector_unavailable", msg, {
|
|
696
|
+
retryable: true,
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
throw err;
|
|
700
|
+
}
|
|
575
701
|
return { messageId: ack.messageId, status: ack.status };
|
|
576
702
|
},
|
|
577
703
|
async reattachEndpoint(endpoint_id, plugin_pid, _ipcWsHint) {
|
|
@@ -606,6 +732,9 @@ export async function startBrokerDaemon(opts) {
|
|
|
606
732
|
content: msg.envelope.content,
|
|
607
733
|
contentType: msg.envelope.contentType,
|
|
608
734
|
metadata: msg.envelope.metadata,
|
|
735
|
+
...(msg.envelope.sourceMessageId !== undefined && {
|
|
736
|
+
sourceMessageId: msg.envelope.sourceMessageId,
|
|
737
|
+
}),
|
|
609
738
|
});
|
|
610
739
|
}
|
|
611
740
|
emitTransition(endpoint_id, "active", "reattach");
|
|
@@ -672,6 +801,7 @@ export async function startBrokerDaemon(opts) {
|
|
|
672
801
|
bearerToken,
|
|
673
802
|
handlers,
|
|
674
803
|
logger,
|
|
804
|
+
getNetworkPresence: () => networkPresence,
|
|
675
805
|
onChannelOpened: (plugin_pid, ws) => {
|
|
676
806
|
const prior = channels.get(plugin_pid);
|
|
677
807
|
if (prior && prior !== ws) {
|
|
@@ -702,7 +832,10 @@ export async function startBrokerDaemon(opts) {
|
|
|
702
832
|
}
|
|
703
833
|
},
|
|
704
834
|
});
|
|
705
|
-
|
|
835
|
+
const inboundProcessing = new Set();
|
|
836
|
+
const blockedInboundSeqs = new Set();
|
|
837
|
+
let unsequencedInboundBlocked = false;
|
|
838
|
+
const handleInboundMessage = async (payload) => {
|
|
706
839
|
const meta = payload.metadata ?? {};
|
|
707
840
|
const target_endpoint_id = typeof meta.target_endpoint_id === "string"
|
|
708
841
|
? meta.target_endpoint_id
|
|
@@ -710,26 +843,48 @@ export async function startBrokerDaemon(opts) {
|
|
|
710
843
|
const correlation_id = typeof meta.correlation_id === "string" ? meta.correlation_id : undefined;
|
|
711
844
|
const decision = router.route({ target_endpoint_id, correlation_id });
|
|
712
845
|
if (decision.kind === "endpoint") {
|
|
846
|
+
if (payload.messageId !== undefined) {
|
|
847
|
+
const posted = await directPostRuntimeInboundRouted({
|
|
848
|
+
type: "runtime_inbound_routed",
|
|
849
|
+
version: 1,
|
|
850
|
+
source_message_id: payload.messageId,
|
|
851
|
+
routed_to_endpoint_id: decision.entry.endpoint_id,
|
|
852
|
+
route_kind: decision.entry.state === "active"
|
|
853
|
+
? "active_endpoint"
|
|
854
|
+
: "reconnecting_endpoint",
|
|
855
|
+
routed_at: Date.now(),
|
|
856
|
+
}, { queueOnTransientFailure: typeof payload.seq !== "number" });
|
|
857
|
+
if (!posted && typeof payload.seq === "number")
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
713
860
|
recordInboundCorrelation(decision.entry.endpoint_id, meta);
|
|
714
861
|
if (decision.entry.state === "active") {
|
|
715
|
-
pushToPlugin(decision.entry.ipc_ws, {
|
|
862
|
+
const pushed = pushToPlugin(decision.entry.ipc_ws, {
|
|
716
863
|
event: "message_received",
|
|
717
864
|
from: payload.from,
|
|
718
865
|
content: payload.content,
|
|
719
866
|
contentType: payload.contentType,
|
|
720
867
|
metadata: meta,
|
|
868
|
+
...(payload.messageId !== undefined && {
|
|
869
|
+
sourceMessageId: payload.messageId,
|
|
870
|
+
}),
|
|
721
871
|
});
|
|
872
|
+
if (!pushed)
|
|
873
|
+
return false;
|
|
722
874
|
}
|
|
723
875
|
else {
|
|
724
876
|
const buf = reconnectingBuffers.forEndpoint(decision.entry.endpoint_id);
|
|
725
877
|
buf.append({
|
|
726
|
-
id: randomUUID(),
|
|
878
|
+
id: payload.messageId ?? randomUUID(),
|
|
727
879
|
arrived_at: new Date().toISOString(),
|
|
728
880
|
envelope: {
|
|
729
881
|
from: payload.from,
|
|
730
882
|
content: payload.content,
|
|
731
883
|
contentType: payload.contentType,
|
|
732
884
|
metadata: meta,
|
|
885
|
+
...(payload.messageId !== undefined && {
|
|
886
|
+
sourceMessageId: payload.messageId,
|
|
887
|
+
}),
|
|
733
888
|
},
|
|
734
889
|
});
|
|
735
890
|
logger.info("buffered_for_reconnecting_endpoint", {
|
|
@@ -737,7 +892,7 @@ export async function startBrokerDaemon(opts) {
|
|
|
737
892
|
buffer_size: buf.size(),
|
|
738
893
|
});
|
|
739
894
|
}
|
|
740
|
-
return;
|
|
895
|
+
return markDeliverySeqAccepted(payload.seq);
|
|
741
896
|
}
|
|
742
897
|
let reason = "unaddressed";
|
|
743
898
|
let originalTarget;
|
|
@@ -757,7 +912,10 @@ export async function startBrokerDaemon(opts) {
|
|
|
757
912
|
}
|
|
758
913
|
}
|
|
759
914
|
const undispatched = {
|
|
760
|
-
id: randomUUID(),
|
|
915
|
+
id: payload.messageId ?? randomUUID(),
|
|
916
|
+
...(payload.messageId !== undefined && {
|
|
917
|
+
source_message_id: payload.messageId,
|
|
918
|
+
}),
|
|
761
919
|
arrived_at: new Date().toISOString(),
|
|
762
920
|
sender_address: payload.from,
|
|
763
921
|
content: payload.content,
|
|
@@ -774,31 +932,82 @@ export async function startBrokerDaemon(opts) {
|
|
|
774
932
|
size: undispatchedInbox.size(),
|
|
775
933
|
capacity: undispatchedInbox.capacity(),
|
|
776
934
|
});
|
|
935
|
+
return false;
|
|
777
936
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
937
|
+
logger.info("undispatched_added", { id: undispatched.id, reason });
|
|
938
|
+
const wireReason = undispatched.reason === "unaddressed"
|
|
939
|
+
? "no_target_endpoint"
|
|
940
|
+
: undispatched.reason;
|
|
941
|
+
const posted = await directPostUndispatched({
|
|
942
|
+
type: "undispatched_changed",
|
|
943
|
+
version: 1,
|
|
944
|
+
action: "add",
|
|
945
|
+
undispatched_id: undispatched.id,
|
|
946
|
+
...(undispatched.source_message_id !== undefined && {
|
|
947
|
+
source_message_id: undispatched.source_message_id,
|
|
948
|
+
}),
|
|
949
|
+
sender_address: undispatched.sender_address,
|
|
950
|
+
content_preview: truncateContentPreview(undispatched.content, CONTENT_PREVIEW_MAX_CODEPOINTS),
|
|
951
|
+
reason: wireReason,
|
|
952
|
+
...(undispatched.original_target_endpoint_id !== undefined && {
|
|
953
|
+
target_endpoint_id_hint: undispatched.original_target_endpoint_id,
|
|
954
|
+
}),
|
|
955
|
+
...(typeof correlation_id === "string" && {
|
|
956
|
+
in_reply_to: correlation_id,
|
|
957
|
+
}),
|
|
958
|
+
arrived_at: Date.parse(undispatched.arrived_at),
|
|
959
|
+
}, { queueOnTransientFailure: typeof payload.seq !== "number" });
|
|
960
|
+
if (!posted)
|
|
961
|
+
return false;
|
|
962
|
+
return markDeliverySeqAccepted(payload.seq);
|
|
963
|
+
};
|
|
964
|
+
connector.on("message_received", ({ payload }) => {
|
|
965
|
+
let task;
|
|
966
|
+
task = handleInboundMessage(payload)
|
|
967
|
+
.then((accepted) => {
|
|
968
|
+
if (typeof payload.seq === "number") {
|
|
969
|
+
if (accepted)
|
|
970
|
+
blockedInboundSeqs.delete(payload.seq);
|
|
971
|
+
else
|
|
972
|
+
blockedInboundSeqs.add(payload.seq);
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (accepted)
|
|
976
|
+
unsequencedInboundBlocked = false;
|
|
977
|
+
else
|
|
978
|
+
unsequencedInboundBlocked = true;
|
|
979
|
+
})
|
|
980
|
+
.catch((err) => {
|
|
981
|
+
if (typeof payload.seq === "number") {
|
|
982
|
+
blockedInboundSeqs.add(payload.seq);
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
unsequencedInboundBlocked = true;
|
|
986
|
+
}
|
|
987
|
+
logger.error("inbound_message_handling_failed", {
|
|
988
|
+
err: err instanceof Error ? err.message : String(err),
|
|
989
|
+
messageId: payload.messageId,
|
|
990
|
+
from: payload.from,
|
|
798
991
|
});
|
|
799
|
-
}
|
|
992
|
+
})
|
|
993
|
+
.finally(() => {
|
|
994
|
+
inboundProcessing.delete(task);
|
|
995
|
+
});
|
|
996
|
+
inboundProcessing.add(task);
|
|
800
997
|
});
|
|
801
|
-
connector.on("delivery_pending", ({ payload }) => {
|
|
998
|
+
connector.on("delivery_pending", async ({ payload }) => {
|
|
999
|
+
if (inboundProcessing.size > 0) {
|
|
1000
|
+
await Promise.allSettled([...inboundProcessing]);
|
|
1001
|
+
}
|
|
1002
|
+
if (unsequencedInboundBlocked || blockedInboundSeqs.size > 0) {
|
|
1003
|
+
logger.warn("delivery_ack_deferred_until_replay", {
|
|
1004
|
+
upTo: payload.upTo,
|
|
1005
|
+
count: payload.count,
|
|
1006
|
+
blockedSeqs: [...blockedInboundSeqs].sort((a, b) => a - b),
|
|
1007
|
+
unsequencedBlocked: unsequencedInboundBlocked,
|
|
1008
|
+
});
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
802
1011
|
connector.ackDelivery(payload.upTo);
|
|
803
1012
|
});
|
|
804
1013
|
writeDiscoveryFile(paths.discoveryFile, {
|
|
@@ -865,6 +1074,13 @@ export async function startBrokerDaemon(opts) {
|
|
|
865
1074
|
});
|
|
866
1075
|
}
|
|
867
1076
|
await networkPresenceEmitter.shutdown();
|
|
1077
|
+
if (runtimeInboundRoutedEmitter.size() > 0) {
|
|
1078
|
+
logger.warn("runtime_inbound_routed_queue_dropped_on_shutdown", {
|
|
1079
|
+
size: runtimeInboundRoutedEmitter.size(),
|
|
1080
|
+
reason,
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
await runtimeInboundRoutedEmitter.shutdown();
|
|
868
1084
|
const endpoints = registry.list();
|
|
869
1085
|
await Promise.all(endpoints.map(async (entry) => {
|
|
870
1086
|
try {
|
|
@@ -901,6 +1117,7 @@ export async function startBrokerDaemon(opts) {
|
|
|
901
1117
|
retryQueueSize: () => retryQueue.size(),
|
|
902
1118
|
undispatchedChangedQueueSize: () => undispatchedEmitter.size(),
|
|
903
1119
|
networkPresenceChangedQueueSize: () => networkPresenceEmitter.size(),
|
|
1120
|
+
runtimeInboundRoutedQueueSize: () => runtimeInboundRoutedEmitter.size(),
|
|
904
1121
|
replyCorrelationCacheSize: (endpoint_id) => replyCorrelationCache.sizeForEndpoint(endpoint_id),
|
|
905
1122
|
};
|
|
906
1123
|
}
|
|
@@ -38,6 +38,10 @@ export declare class ConnectorWS {
|
|
|
38
38
|
disconnect(): void;
|
|
39
39
|
send(to: string, content: string, contentType?: string, metadata?: Record<string, unknown>): Promise<SendAckEvent>;
|
|
40
40
|
ackDelivery(upTo: string): boolean;
|
|
41
|
+
setDeliveryCursor(seq: number): void;
|
|
42
|
+
getDeliveryCursor(): number | undefined;
|
|
43
|
+
prepareDeliverySeqAccepted(seq: number): number | undefined;
|
|
44
|
+
markDeliverySeqAccepted(seq: number): number | undefined;
|
|
41
45
|
on<E extends ConnectorWSEvent["event"]>(event: E, listener: ListenerFor<E>): this;
|
|
42
46
|
off<E extends ConnectorWSEvent["event"]>(event: E, listener: ListenerFor<E>): this;
|
|
43
47
|
_emitForTesting(e: ConnectorWSEvent): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connector-ws.d.ts","sourceRoot":"","sources":["../../src/broker/connector-ws.ts"],"names":[],"mappings":"AAaA,OAAO,EAAmB,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACrB,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,gBAAgB,GACxB;IAAE,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,qBAAqB,CAAA;CAAE,GAC7D;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAC5C;IAAE,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,GAC5D;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,mBAAmB,CAAA;CAAE,GAC1D;IAAE,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,GAC5D;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,GACtB;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,GACzB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,KAAK,CAAA;CAAE,CAAC;AAEvC,KAAK,WAAW,CAAC,CAAC,SAAS,gBAAgB,CAAC,OAAO,CAAC,IAAI,CACtD,CAAC,EAAE,OAAO,CAAC,gBAAgB,EAAE;IAAE,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC,KACvC,IAAI,CAAC;AAEV,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAsB;gBAE9B,IAAI,EAAE,kBAAkB;IAMpC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,UAAU,IAAI,IAAI;IASlB,IAAI,CACF,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,WAAW,SAAS,EACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,YAAY,CAAC;IAKxB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"connector-ws.d.ts","sourceRoot":"","sources":["../../src/broker/connector-ws.ts"],"names":[],"mappings":"AAaA,OAAO,EAAmB,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACrB,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,gBAAgB,GACxB;IAAE,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,qBAAqB,CAAA;CAAE,GAC7D;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAC5C;IAAE,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,GAC5D;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,mBAAmB,CAAA;CAAE,GAC1D;IAAE,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,GAC5D;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,GACtB;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,GACzB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,KAAK,CAAA;CAAE,CAAC;AAEvC,KAAK,WAAW,CAAC,CAAC,SAAS,gBAAgB,CAAC,OAAO,CAAC,IAAI,CACtD,CAAC,EAAE,OAAO,CAAC,gBAAgB,EAAE;IAAE,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC,KACvC,IAAI,CAAC;AAEV,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAsB;gBAE9B,IAAI,EAAE,kBAAkB;IAMpC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,UAAU,IAAI,IAAI;IASlB,IAAI,CACF,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,WAAW,SAAS,EACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,YAAY,CAAC;IAKxB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAKlC,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAKpC,iBAAiB,IAAI,MAAM,GAAG,SAAS;IAKvC,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAK3D,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIxD,EAAE,CAAC,CAAC,SAAS,gBAAgB,CAAC,OAAO,CAAC,EACpC,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,GACvB,IAAI;IAKP,GAAG,CAAC,CAAC,SAAS,gBAAgB,CAAC,OAAO,CAAC,EACrC,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,GACvB,IAAI;IAMS,eAAe,CAAC,CAAC,EAAE,gBAAgB,GAAG,IAAI;IAI1D,OAAO,CAAC,cAAc;CA0BvB;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -19,6 +19,18 @@ export class ConnectorWS {
|
|
|
19
19
|
ackDelivery(upTo) {
|
|
20
20
|
return this.client.ackDelivery(upTo);
|
|
21
21
|
}
|
|
22
|
+
setDeliveryCursor(seq) {
|
|
23
|
+
this.client.setDeliveryCursor(seq);
|
|
24
|
+
}
|
|
25
|
+
getDeliveryCursor() {
|
|
26
|
+
return this.client.getDeliveryCursor();
|
|
27
|
+
}
|
|
28
|
+
prepareDeliverySeqAccepted(seq) {
|
|
29
|
+
return this.client.prepareDeliverySeqAccepted(seq);
|
|
30
|
+
}
|
|
31
|
+
markDeliverySeqAccepted(seq) {
|
|
32
|
+
return this.client.markDeliverySeqAccepted(seq);
|
|
33
|
+
}
|
|
22
34
|
on(event, listener) {
|
|
23
35
|
this.bus.on(event, listener);
|
|
24
36
|
return this;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface DeliveryCursorRecord {
|
|
2
|
+
version: 1;
|
|
3
|
+
lastKnownSeq: number;
|
|
4
|
+
updatedAt: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function readDeliveryCursorFile(path: string): number | null;
|
|
7
|
+
export declare function writeDeliveryCursorFile(path: string, seq: number): void;
|
|
8
|
+
//# sourceMappingURL=delivery-cursor-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-cursor-file.d.ts","sourceRoot":"","sources":["../../src/broker/delivery-cursor-file.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,CAAC,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBlE;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAiCvE"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
export function readDeliveryCursorFile(path) {
|
|
5
|
+
let raw;
|
|
6
|
+
try {
|
|
7
|
+
raw = readFileSync(path, "utf8");
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
if (isNodeErr(err, "ENOENT"))
|
|
11
|
+
return null;
|
|
12
|
+
throw err;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(raw);
|
|
16
|
+
if (!isDeliveryCursorRecord(parsed))
|
|
17
|
+
return null;
|
|
18
|
+
return parsed.lastKnownSeq;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function writeDeliveryCursorFile(path, seq) {
|
|
25
|
+
if (!Number.isInteger(seq) || seq < 0) {
|
|
26
|
+
throw new Error(`delivery cursor must be a non-negative integer: ${seq}`);
|
|
27
|
+
}
|
|
28
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
29
|
+
const tmp = `${path}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
30
|
+
const record = {
|
|
31
|
+
version: 1,
|
|
32
|
+
lastKnownSeq: seq,
|
|
33
|
+
updatedAt: new Date().toISOString(),
|
|
34
|
+
};
|
|
35
|
+
writeFileSync(tmp, JSON.stringify(record, null, 2), {
|
|
36
|
+
mode: 0o600,
|
|
37
|
+
encoding: "utf8",
|
|
38
|
+
});
|
|
39
|
+
try {
|
|
40
|
+
chmodSync(tmp, 0o600);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
renameSync(tmp, path);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
try {
|
|
49
|
+
unlinkSync(tmp);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
}
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function isDeliveryCursorRecord(value) {
|
|
57
|
+
if (typeof value !== "object" || value === null)
|
|
58
|
+
return false;
|
|
59
|
+
const v = value;
|
|
60
|
+
return (v.version === 1 &&
|
|
61
|
+
typeof v.lastKnownSeq === "number" &&
|
|
62
|
+
Number.isInteger(v.lastKnownSeq) &&
|
|
63
|
+
v.lastKnownSeq >= 0 &&
|
|
64
|
+
typeof v.updatedAt === "string");
|
|
65
|
+
}
|
|
66
|
+
function isNodeErr(err, code) {
|
|
67
|
+
return (typeof err === "object" &&
|
|
68
|
+
err !== null &&
|
|
69
|
+
"code" in err &&
|
|
70
|
+
err.code === code);
|
|
71
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/broker/entry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/broker/entry.ts"],"names":[],"mappings":"AAiFA,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,aAAa,CAAC;AAEpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAMtE,UAAU,mBAAmB;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,gBAAgB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAUD,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CA8F9B;AA2DD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,OAAO,UAAU,CAAC,KAAwB,GACpD,OAAO,CAAC,gBAAgB,CAAC,CA4B3B;AAaD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,YAAY,GACnB,mBAAmB,CAuHrB;AAYD,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAgG1C"}
|