agent-comms 3.5.0 → 3.7.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 (72) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +1 -1
  4. package/dist/core/bridge-mesh.d.ts +2 -2
  5. package/dist/core/bridge-mesh.d.ts.map +1 -1
  6. package/dist/core/bridge-mesh.js +4 -4
  7. package/dist/core/bridge-mesh.js.map +1 -1
  8. package/dist/core/capability-bubble-up.d.ts +69 -0
  9. package/dist/core/capability-bubble-up.d.ts.map +1 -0
  10. package/dist/core/capability-bubble-up.js +138 -0
  11. package/dist/core/capability-bubble-up.js.map +1 -0
  12. package/dist/core/coordinator-gateway.d.ts +25 -0
  13. package/dist/core/coordinator-gateway.d.ts.map +1 -0
  14. package/dist/core/coordinator-gateway.js +34 -0
  15. package/dist/core/coordinator-gateway.js.map +1 -0
  16. package/dist/core/gossip-directory.d.ts +8 -0
  17. package/dist/core/gossip-directory.d.ts.map +1 -0
  18. package/dist/core/gossip-directory.js +16 -0
  19. package/dist/core/gossip-directory.js.map +1 -0
  20. package/dist/core/hub-session.d.ts +5 -1
  21. package/dist/core/hub-session.d.ts.map +1 -1
  22. package/dist/core/hub-session.js +18 -2
  23. package/dist/core/hub-session.js.map +1 -1
  24. package/dist/core/mesh-store-shared.d.ts +2 -0
  25. package/dist/core/mesh-store-shared.d.ts.map +1 -1
  26. package/dist/core/mesh-store-shared.js +2 -0
  27. package/dist/core/mesh-store-shared.js.map +1 -1
  28. package/dist/core/mesh-store.d.ts +3 -1
  29. package/dist/core/mesh-store.d.ts.map +1 -1
  30. package/dist/core/mesh-store.js +21 -2
  31. package/dist/core/mesh-store.js.map +1 -1
  32. package/dist/core/peer-lifecycle.d.ts +4 -1
  33. package/dist/core/peer-lifecycle.d.ts.map +1 -1
  34. package/dist/core/peer-lifecycle.js +1 -0
  35. package/dist/core/peer-lifecycle.js.map +1 -1
  36. package/dist/core/transport.d.ts +8 -0
  37. package/dist/core/transport.d.ts.map +1 -1
  38. package/dist/core/wire-mesh-transport.d.ts +2 -2
  39. package/dist/core/wire-mesh-transport.d.ts.map +1 -1
  40. package/dist/core/wire-mesh-transport.js +11 -12
  41. package/dist/core/wire-mesh-transport.js.map +1 -1
  42. package/dist/test/bridge-mesh.test.js +18 -0
  43. package/dist/test/bridge-mesh.test.js.map +1 -1
  44. package/dist/test/capability-bubble-up.test.d.ts +2 -0
  45. package/dist/test/capability-bubble-up.test.d.ts.map +1 -0
  46. package/dist/test/capability-bubble-up.test.js +350 -0
  47. package/dist/test/capability-bubble-up.test.js.map +1 -0
  48. package/dist/test/coordinator-gateway.test.d.ts +2 -0
  49. package/dist/test/coordinator-gateway.test.d.ts.map +1 -0
  50. package/dist/test/coordinator-gateway.test.js +88 -0
  51. package/dist/test/coordinator-gateway.test.js.map +1 -0
  52. package/dist/test/gossip-directory.test.d.ts +2 -0
  53. package/dist/test/gossip-directory.test.d.ts.map +1 -0
  54. package/dist/test/gossip-directory.test.js +67 -0
  55. package/dist/test/gossip-directory.test.js.map +1 -0
  56. package/dist/test/hub-helpers.d.ts +8 -0
  57. package/dist/test/hub-helpers.d.ts.map +1 -1
  58. package/dist/test/hub-helpers.js +132 -2
  59. package/dist/test/hub-helpers.js.map +1 -1
  60. package/dist/test/hub-mode-session.integration.test.js +47 -124
  61. package/dist/test/hub-mode-session.integration.test.js.map +1 -1
  62. package/dist/test/mesh-smoke.runner.js +3 -1
  63. package/dist/test/mesh-smoke.runner.js.map +1 -1
  64. package/dist/test/mesh-store-orchestration.test.js +57 -0
  65. package/dist/test/mesh-store-orchestration.test.js.map +1 -1
  66. package/dist/test/peer-lifecycle.test.js +8 -0
  67. package/dist/test/peer-lifecycle.test.js.map +1 -1
  68. package/dist/test/wire-mesh-transport-hub.test.d.ts +5 -0
  69. package/dist/test/wire-mesh-transport-hub.test.d.ts.map +1 -0
  70. package/dist/test/wire-mesh-transport-hub.test.js +50 -0
  71. package/dist/test/wire-mesh-transport-hub.test.js.map +1 -0
  72. package/package.json +1 -1
@@ -0,0 +1,350 @@
1
+ import { webcrypto } from "node:crypto";
2
+ import { describe, it, expect, vi } from "vitest";
3
+ import { createNodeIdentity } from "wire-mesh-core/adapters/node-identity";
4
+ import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
5
+ import { mintCapabilityToken } from "wire-mesh-core/domain/tokens";
6
+ import { createRevocationView } from "wire-mesh-core/domain/revocation-view";
7
+ import { verifyCapabilityToken } from "wire-mesh-core/domain/tokens";
8
+ import { resolveBubbleUpRoute, createBubbleUpCapabilityRequestHandler, } from "../core/capability-bubble-up.js";
9
+ const ES256 = -7;
10
+ const HOUR_MS = 3_600_000;
11
+ const NOW_MS = 1_893_456_000_000;
12
+ const EXPIRES_MS = NOW_MS + HOUR_MS;
13
+ const CAPABILITY = "exec:pty";
14
+ const SCOPE = { kind: "folder" };
15
+ function buf(bytes) {
16
+ return Uint8Array.from(bytes);
17
+ }
18
+ async function generateEs256Identity() {
19
+ const keyPair = await webcrypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
20
+ const publicKeyBytes = new Uint8Array(await webcrypto.subtle.exportKey("raw", keyPair.publicKey));
21
+ return createNodeIdentity(keyPair.privateKey, publicKeyBytes, ES256);
22
+ }
23
+ function fixedClock(atMs) {
24
+ return { now: () => atMs };
25
+ }
26
+ let issuedTokenIds = 0;
27
+ function nextTokenId() {
28
+ issuedTokenIds += 1;
29
+ return buf([issuedTokenIds]);
30
+ }
31
+ async function mint(issuer, bearer, options = {}) {
32
+ const verdict = await mintCapabilityToken({
33
+ identity: issuer,
34
+ clock: fixedClock(options.mintedAt ?? NOW_MS),
35
+ tokenId: nextTokenId(),
36
+ bearer: bearer.deviceId,
37
+ capability: options.capability ?? CAPABILITY,
38
+ scope: options.scope ?? SCOPE,
39
+ expires: options.expires ?? EXPIRES_MS,
40
+ ...(options.delegationsRemaining !== undefined
41
+ ? { delegationsRemaining: options.delegationsRemaining }
42
+ : {}),
43
+ ...(options.parent !== undefined ? { parent: options.parent } : {}),
44
+ });
45
+ if (!verdict.ok)
46
+ throw new Error(`mint failed: ${verdict.reason}`);
47
+ return verdict.token;
48
+ }
49
+ describe("resolveBubbleUpRoute", () => {
50
+ it("grants directly when the held token has depth remaining that narrows the request", async () => {
51
+ const root = await generateEs256Identity();
52
+ const holder = await generateEs256Identity();
53
+ const heldToken = await mint(root, holder, { delegationsRemaining: 2 });
54
+ const route = await resolveBubbleUpRoute({
55
+ heldToken,
56
+ identity: holder,
57
+ clock: fixedClock(NOW_MS),
58
+ revocation: createRevocationView(),
59
+ capability: CAPABILITY,
60
+ scope: SCOPE,
61
+ expires: EXPIRES_MS,
62
+ delegationsRemaining: 1,
63
+ });
64
+ expect(route.kind).toBe("grant");
65
+ if (route.kind === "grant") {
66
+ expect(route.token).toBe(heldToken);
67
+ }
68
+ });
69
+ it("forwards to the held token's own issuer when its delegation depth is exhausted", async () => {
70
+ const root = await generateEs256Identity();
71
+ const holder = await generateEs256Identity();
72
+ const heldToken = await mint(root, holder, { delegationsRemaining: 0 });
73
+ const route = await resolveBubbleUpRoute({
74
+ heldToken,
75
+ identity: holder,
76
+ clock: fixedClock(NOW_MS),
77
+ revocation: createRevocationView(),
78
+ capability: CAPABILITY,
79
+ scope: SCOPE,
80
+ expires: EXPIRES_MS,
81
+ delegationsRemaining: 0,
82
+ });
83
+ expect(route.kind).toBe("forward");
84
+ if (route.kind === "forward") {
85
+ expect(deviceIdToHex(route.target)).toBe(deviceIdToHex(root.deviceId));
86
+ }
87
+ });
88
+ it("forwards to the held token's own issuer when the requested scope does not narrow", async () => {
89
+ const root = await generateEs256Identity();
90
+ const holder = await generateEs256Identity();
91
+ const heldToken = await mint(root, holder, {
92
+ scope: { kind: "room", path: "abc/general" },
93
+ delegationsRemaining: 3,
94
+ });
95
+ const route = await resolveBubbleUpRoute({
96
+ heldToken,
97
+ identity: holder,
98
+ clock: fixedClock(NOW_MS),
99
+ revocation: createRevocationView(),
100
+ capability: CAPABILITY,
101
+ scope: { kind: "room", path: "abc/other" },
102
+ expires: EXPIRES_MS,
103
+ delegationsRemaining: 1,
104
+ });
105
+ expect(route.kind).toBe("forward");
106
+ if (route.kind === "forward") {
107
+ expect(deviceIdToHex(route.target)).toBe(deviceIdToHex(root.deviceId));
108
+ }
109
+ });
110
+ it("reports no-route when this device holds no token for the capability at all", async () => {
111
+ const holder = await generateEs256Identity();
112
+ const route = await resolveBubbleUpRoute({
113
+ heldToken: undefined,
114
+ identity: holder,
115
+ clock: fixedClock(NOW_MS),
116
+ revocation: createRevocationView(),
117
+ capability: CAPABILITY,
118
+ scope: SCOPE,
119
+ expires: EXPIRES_MS,
120
+ });
121
+ expect(route).toEqual({ kind: "no-route", reason: "no-held-token" });
122
+ });
123
+ it("reports no-route when the held token has already expired", async () => {
124
+ const root = await generateEs256Identity();
125
+ const holder = await generateEs256Identity();
126
+ const heldToken = await mint(root, holder, {
127
+ mintedAt: NOW_MS - HOUR_MS,
128
+ expires: NOW_MS - 1,
129
+ delegationsRemaining: 2,
130
+ });
131
+ const route = await resolveBubbleUpRoute({
132
+ heldToken,
133
+ identity: holder,
134
+ clock: fixedClock(NOW_MS),
135
+ revocation: createRevocationView(),
136
+ capability: CAPABILITY,
137
+ scope: SCOPE,
138
+ expires: NOW_MS,
139
+ delegationsRemaining: 1,
140
+ });
141
+ expect(route).toEqual({ kind: "no-route", reason: "held-token-invalid" });
142
+ });
143
+ it("reports no-route rather than forwarding to itself when its held token is self-issued", async () => {
144
+ const holder = await generateEs256Identity();
145
+ const heldToken = await mint(holder, holder, { delegationsRemaining: 0 });
146
+ const route = await resolveBubbleUpRoute({
147
+ heldToken,
148
+ identity: holder,
149
+ clock: fixedClock(NOW_MS),
150
+ revocation: createRevocationView(),
151
+ capability: CAPABILITY,
152
+ scope: SCOPE,
153
+ expires: EXPIRES_MS,
154
+ delegationsRemaining: 0,
155
+ });
156
+ expect(route).toEqual({ kind: "no-route", reason: "self-issued" });
157
+ });
158
+ });
159
+ let nextRequestId = 0;
160
+ function fakeIncoming(params) {
161
+ nextRequestId += 1;
162
+ return {
163
+ requestId: nextRequestId,
164
+ command: { verb: CAPABILITY, params },
165
+ scope: SCOPE,
166
+ respond: vi.fn(async () => undefined),
167
+ };
168
+ }
169
+ describe("createBubbleUpCapabilityRequestHandler", () => {
170
+ it("mints a delegated child token when this device can grant from its own held depth", async () => {
171
+ const root = await generateEs256Identity();
172
+ const holder = await generateEs256Identity();
173
+ const requester = await generateEs256Identity();
174
+ const heldToken = await mint(root, holder, { delegationsRemaining: 2 });
175
+ const sendManageRequest = vi.fn();
176
+ const handler = createBubbleUpCapabilityRequestHandler({
177
+ capability: CAPABILITY,
178
+ identity: holder,
179
+ clock: fixedClock(NOW_MS),
180
+ revocation: createRevocationView(),
181
+ bearerDevice: requester.deviceId,
182
+ expires: EXPIRES_MS,
183
+ delegationsRemaining: 1,
184
+ ownToken: () => heldToken,
185
+ session: { sendManageRequest },
186
+ });
187
+ const incoming = fakeIncoming({
188
+ verb: "capability.request",
189
+ capability: CAPABILITY,
190
+ });
191
+ await handler(incoming);
192
+ expect(sendManageRequest).not.toHaveBeenCalled();
193
+ expect(incoming.respond).toHaveBeenCalledTimes(1);
194
+ const outcome = incoming.respond.mock.calls[0]?.[0];
195
+ expect(outcome.result).toBe("ok");
196
+ const verdict = await verifyCapabilityToken(outcome["granted-token"], {
197
+ identity: holder,
198
+ clock: fixedClock(NOW_MS),
199
+ revocation: createRevocationView(),
200
+ expectedBearer: requester.deviceId,
201
+ });
202
+ expect(verdict.ok).toBe(true);
203
+ if (verdict.ok) {
204
+ expect(deviceIdToHex(verdict.rootIssuer)).toBe(deviceIdToHex(root.deviceId));
205
+ expect(verdict.depth).toBe(1);
206
+ }
207
+ });
208
+ it("responds malformed for a request naming a different capability", async () => {
209
+ const holder = await generateEs256Identity();
210
+ const requester = await generateEs256Identity();
211
+ const handler = createBubbleUpCapabilityRequestHandler({
212
+ capability: CAPABILITY,
213
+ identity: holder,
214
+ clock: fixedClock(NOW_MS),
215
+ revocation: createRevocationView(),
216
+ bearerDevice: requester.deviceId,
217
+ expires: EXPIRES_MS,
218
+ ownToken: () => undefined,
219
+ session: { sendManageRequest: vi.fn() },
220
+ });
221
+ const incoming = fakeIncoming({
222
+ verb: "capability.request",
223
+ capability: "room:member",
224
+ });
225
+ await handler(incoming);
226
+ expect(incoming.respond).toHaveBeenCalledWith({
227
+ result: "error",
228
+ code: "malformed",
229
+ });
230
+ });
231
+ it("responds expired for an already-stale ask", async () => {
232
+ const holder = await generateEs256Identity();
233
+ const requester = await generateEs256Identity();
234
+ const handler = createBubbleUpCapabilityRequestHandler({
235
+ capability: CAPABILITY,
236
+ identity: holder,
237
+ clock: fixedClock(NOW_MS),
238
+ revocation: createRevocationView(),
239
+ bearerDevice: requester.deviceId,
240
+ expires: EXPIRES_MS,
241
+ ownToken: () => undefined,
242
+ session: { sendManageRequest: vi.fn() },
243
+ });
244
+ const incoming = fakeIncoming({
245
+ verb: "capability.request",
246
+ capability: CAPABILITY,
247
+ "valid-until": NOW_MS - 1,
248
+ });
249
+ await handler(incoming);
250
+ expect(incoming.respond).toHaveBeenCalledWith({
251
+ result: "error",
252
+ code: "expired",
253
+ });
254
+ });
255
+ it("forwards to the held token's own issuer and re-delegates the returned grant when its own depth is exhausted", async () => {
256
+ const root = await generateEs256Identity();
257
+ const holder = await generateEs256Identity();
258
+ const requester = await generateEs256Identity();
259
+ const heldToken = await mint(root, holder, { delegationsRemaining: 0 });
260
+ const upstreamGrant = await mint(root, holder, { delegationsRemaining: 1 });
261
+ const sendManageRequest = vi.fn(async () => ({
262
+ result: "ok",
263
+ "granted-token": upstreamGrant,
264
+ }));
265
+ const handler = createBubbleUpCapabilityRequestHandler({
266
+ capability: CAPABILITY,
267
+ identity: holder,
268
+ clock: fixedClock(NOW_MS),
269
+ revocation: createRevocationView(),
270
+ bearerDevice: requester.deviceId,
271
+ expires: EXPIRES_MS,
272
+ delegationsRemaining: 0,
273
+ ownToken: () => heldToken,
274
+ session: { sendManageRequest },
275
+ });
276
+ const incoming = fakeIncoming({
277
+ verb: "capability.request",
278
+ capability: CAPABILITY,
279
+ });
280
+ await handler(incoming);
281
+ expect(sendManageRequest).toHaveBeenCalledWith(expect.objectContaining({ verb: CAPABILITY }), SCOPE, root.deviceId, undefined, undefined);
282
+ const outcome = incoming.respond.mock.calls[0]?.[0];
283
+ expect(outcome.result).toBe("ok");
284
+ const verdict = await verifyCapabilityToken(outcome["granted-token"], {
285
+ identity: holder,
286
+ clock: fixedClock(NOW_MS),
287
+ revocation: createRevocationView(),
288
+ expectedBearer: requester.deviceId,
289
+ });
290
+ expect(verdict.ok).toBe(true);
291
+ if (verdict.ok) {
292
+ expect(deviceIdToHex(verdict.rootIssuer)).toBe(deviceIdToHex(root.deviceId));
293
+ }
294
+ });
295
+ it("relays the upstream denial unchanged when forwarding is refused", async () => {
296
+ const root = await generateEs256Identity();
297
+ const holder = await generateEs256Identity();
298
+ const requester = await generateEs256Identity();
299
+ const heldToken = await mint(root, holder, { delegationsRemaining: 0 });
300
+ const sendManageRequest = vi.fn(async () => ({
301
+ result: "error",
302
+ code: "denied",
303
+ message: "no more depth to give",
304
+ }));
305
+ const handler = createBubbleUpCapabilityRequestHandler({
306
+ capability: CAPABILITY,
307
+ identity: holder,
308
+ clock: fixedClock(NOW_MS),
309
+ revocation: createRevocationView(),
310
+ bearerDevice: requester.deviceId,
311
+ expires: EXPIRES_MS,
312
+ delegationsRemaining: 0,
313
+ ownToken: () => heldToken,
314
+ session: { sendManageRequest },
315
+ });
316
+ const incoming = fakeIncoming({
317
+ verb: "capability.request",
318
+ capability: CAPABILITY,
319
+ });
320
+ await handler(incoming);
321
+ expect(incoming.respond).toHaveBeenCalledWith({
322
+ result: "error",
323
+ code: "denied",
324
+ message: "no more depth to give",
325
+ });
326
+ });
327
+ it("responds denied when this device holds no token and has nothing to forward from", async () => {
328
+ const holder = await generateEs256Identity();
329
+ const requester = await generateEs256Identity();
330
+ const handler = createBubbleUpCapabilityRequestHandler({
331
+ capability: CAPABILITY,
332
+ identity: holder,
333
+ clock: fixedClock(NOW_MS),
334
+ revocation: createRevocationView(),
335
+ bearerDevice: requester.deviceId,
336
+ expires: EXPIRES_MS,
337
+ ownToken: () => undefined,
338
+ session: { sendManageRequest: vi.fn() },
339
+ });
340
+ const incoming = fakeIncoming({
341
+ verb: "capability.request",
342
+ capability: CAPABILITY,
343
+ });
344
+ await handler(incoming);
345
+ const outcome = incoming.respond.mock.calls[0]?.[0];
346
+ expect(outcome.result).toBe("error");
347
+ expect(outcome.code).toBe("denied");
348
+ });
349
+ });
350
+ //# sourceMappingURL=capability-bubble-up.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-bubble-up.test.js","sourceRoot":"","sources":["../../src/test/capability-bubble-up.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAQrE,OAAO,EACL,oBAAoB,EACpB,sCAAsC,GACvC,MAAM,iCAAiC,CAAC;AAEzC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC;AACjB,MAAM,OAAO,GAAG,SAAS,CAAC;AAC1B,MAAM,MAAM,GAAG,iBAAiB,CAAC;AACjC,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AACpC,MAAM,UAAU,GAAG,UAAU,CAAC;AAC9B,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,QAAiB,EAAE,CAAC;AAE1C,SAAS,GAAG,CAAC,KAAqC;IAChD,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,KAAK,UAAU,qBAAqB;IAClC,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,WAAW,CAChD,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,EACtC,IAAI,EACJ,CAAC,MAAM,EAAE,QAAQ,CAAC,CACnB,CAAC;IACF,MAAM,cAAc,GAAG,IAAI,UAAU,CACnC,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,CAC3D,CAAC;IACF,OAAO,kBAAkB,CAAC,OAAO,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;AAC7B,CAAC;AAED,IAAI,cAAc,GAAG,CAAC,CAAC;AACvB,SAAS,WAAW;IAClB,cAAc,IAAI,CAAC,CAAC;IACpB,OAAO,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,KAAK,UAAU,IAAI,CACjB,MAAoB,EACpB,MAAoB,EACpB,UAQI,EAAE;IAEN,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC;QACxC,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC;QAC7C,OAAO,EAAE,WAAW,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC,QAAQ;QACvB,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,UAAU;QAC5C,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,KAAK;QAC7B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,UAAU;QACtC,GAAG,CAAC,OAAO,CAAC,oBAAoB,KAAK,SAAS;YAC5C,CAAC,CAAC,EAAE,oBAAoB,EAAE,OAAO,CAAC,oBAAoB,EAAE;YACxD,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpE,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACnE,OAAO,OAAO,CAAC,KAAK,CAAC;AACvB,CAAC;AAED,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,kFAAkF,EAAE,KAAK,IAAI,EAAE;QAChG,MAAM,IAAI,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAC;QAExE,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC;YACvC,SAAS;YACT,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,UAAU;YACnB,oBAAoB,EAAE,CAAC;SACxB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE;QAC9F,MAAM,IAAI,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAC;QAExE,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC;YACvC,SAAS;YACT,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,UAAU;YACnB,oBAAoB,EAAE,CAAC;SACxB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACzE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kFAAkF,EAAE,KAAK,IAAI,EAAE;QAChG,MAAM,IAAI,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE;YACzC,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE;YAC5C,oBAAoB,EAAE,CAAC;SACxB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC;YACvC,SAAS;YACT,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE;YAC1C,OAAO,EAAE,UAAU;YACnB,oBAAoB,EAAE,CAAC;SACxB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACzE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;QAC1F,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAE7C,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC;YACvC,SAAS,EAAE,SAAS;YACpB,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,UAAU;SACpB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,IAAI,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE;YACzC,QAAQ,EAAE,MAAM,GAAG,OAAO;YAC1B,OAAO,EAAE,MAAM,GAAG,CAAC;YACnB,oBAAoB,EAAE,CAAC;SACxB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC;YACvC,SAAS;YACT,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,MAAM;YACf,oBAAoB,EAAE,CAAC;SACxB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,KAAK,IAAI,EAAE;QACpG,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAC;QAE1E,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC;YACvC,SAAS;YACT,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,UAAU;YACnB,oBAAoB,EAAE,CAAC;SACxB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAMH,IAAI,aAAa,GAAG,CAAC,CAAC;AACtB,SAAS,YAAY,CAAC,MAA+B;IACnD,aAAa,IAAI,CAAC,CAAC;IACnB,OAAO;QACL,SAAS,EAAE,aAAa;QACxB,OAAO,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE;QACrC,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACtD,EAAE,CAAC,kFAAkF,EAAE,KAAK,IAAI,EAAE;QAChG,MAAM,IAAI,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAChD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAC;QAExE,MAAM,iBAAiB,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,sCAAsC,CAAC;YACrD,UAAU,EAAE,UAAU;YACtB,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,YAAY,EAAE,SAAS,CAAC,QAAQ;YAChC,OAAO,EAAE,UAAU;YACnB,oBAAoB,EAAE,CAAC;YACvB,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;YACzB,OAAO,EAAE,EAAE,iBAAiB,EAAE;SAC/B,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC5B,IAAI,EAAE,oBAAoB;YAC1B,UAAU,EAAE,UAAU;SACvB,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;QAExB,MAAM,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACjD,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAGjD,CAAC;QACF,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAElC,MAAM,OAAO,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE;YACpE,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,cAAc,EAAE,SAAS,CAAC,QAAQ;SACnC,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAC5C,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAC7B,CAAC;YACF,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAEhD,MAAM,OAAO,GAAG,sCAAsC,CAAC;YACrD,UAAU,EAAE,UAAU;YACtB,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,YAAY,EAAE,SAAS,CAAC,QAAQ;YAChC,OAAO,EAAE,UAAU;YACnB,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;YACzB,OAAO,EAAE,EAAE,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;SACxC,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC5B,IAAI,EAAE,oBAAoB;YAC1B,UAAU,EAAE,aAAa;SAC1B,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;QAExB,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,oBAAoB,CAAC;YAC5C,MAAM,EAAE,OAAO;YACf,IAAI,EAAE,WAAW;SAClB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAEhD,MAAM,OAAO,GAAG,sCAAsC,CAAC;YACrD,UAAU,EAAE,UAAU;YACtB,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,YAAY,EAAE,SAAS,CAAC,QAAQ;YAChC,OAAO,EAAE,UAAU;YACnB,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;YACzB,OAAO,EAAE,EAAE,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;SACxC,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC5B,IAAI,EAAE,oBAAoB;YAC1B,UAAU,EAAE,UAAU;YACtB,aAAa,EAAE,MAAM,GAAG,CAAC;SAC1B,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;QAExB,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,oBAAoB,CAAC;YAC5C,MAAM,EAAE,OAAO;YACf,IAAI,EAAE,SAAS;SAChB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6GAA6G,EAAE,KAAK,IAAI,EAAE;QAC3H,MAAM,IAAI,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAChD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAC;QACxE,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAC;QAE5E,MAAM,iBAAiB,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,IAA4B,EAAE,CAAC,CAAC;YACnE,MAAM,EAAE,IAAI;YACZ,eAAe,EAAE,aAAa;SAC/B,CAAC,CAAC,CAAC;QACJ,MAAM,OAAO,GAAG,sCAAsC,CAAC;YACrD,UAAU,EAAE,UAAU;YACtB,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,YAAY,EAAE,SAAS,CAAC,QAAQ;YAChC,OAAO,EAAE,UAAU;YACnB,oBAAoB,EAAE,CAAC;YACvB,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;YACzB,OAAO,EAAE,EAAE,iBAAiB,EAAE;SAC/B,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC5B,IAAI,EAAE,oBAAoB;YAC1B,UAAU,EAAE,UAAU;SACvB,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;QAExB,MAAM,CAAC,iBAAiB,CAAC,CAAC,oBAAoB,CAC5C,MAAM,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAC7C,KAAK,EACL,IAAI,CAAC,QAAQ,EACb,SAAS,EACT,SAAS,CACV,CAAC;QAEF,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAGjD,CAAC;QACF,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE;YACpE,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,cAAc,EAAE,SAAS,CAAC,QAAQ;SACnC,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAC5C,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAC7B,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,IAAI,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAChD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAC;QAExE,MAAM,iBAAiB,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,IAA4B,EAAE,CAAC,CAAC;YACnE,MAAM,EAAE,OAAO;YACf,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,uBAAuB;SACjC,CAAC,CAAC,CAAC;QACJ,MAAM,OAAO,GAAG,sCAAsC,CAAC;YACrD,UAAU,EAAE,UAAU;YACtB,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,YAAY,EAAE,SAAS,CAAC,QAAQ;YAChC,OAAO,EAAE,UAAU;YACnB,oBAAoB,EAAE,CAAC;YACvB,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;YACzB,OAAO,EAAE,EAAE,iBAAiB,EAAE;SAC/B,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC5B,IAAI,EAAE,oBAAoB;YAC1B,UAAU,EAAE,UAAU;SACvB,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;QAExB,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,oBAAoB,CAAC;YAC5C,MAAM,EAAE,OAAO;YACf,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,uBAAuB;SACjC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,KAAK,IAAI,EAAE;QAC/F,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,qBAAqB,EAAE,CAAC;QAEhD,MAAM,OAAO,GAAG,sCAAsC,CAAC;YACrD,UAAU,EAAE,UAAU;YACtB,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU,EAAE,oBAAoB,EAAE;YAClC,YAAY,EAAE,SAAS,CAAC,QAAQ;YAChC,OAAO,EAAE,UAAU;YACnB,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;YACzB,OAAO,EAAE,EAAE,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;SACxC,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC5B,IAAI,EAAE,oBAAoB;YAC1B,UAAU,EAAE,UAAU;SACvB,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;QAExB,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAGjD,CAAC;QACF,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=coordinator-gateway.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-gateway.test.d.ts","sourceRoot":"","sources":["../../src/test/coordinator-gateway.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Direct unit tests for CoordinatorGateway -- the connect-hub-on-become-coordinator / disconnect-hub-on-lose-coordinator wiring (agent-comms#154), tested against injected connectHub/disconnectHub closures rather than a real transport or hub socket, mirroring peer-lifecycle.test.ts's own DI-based approach.
3
+ */
4
+ import { describe, expect, it, vi } from "vitest";
5
+ import { CoordinatorGateway, } from "../core/coordinator-gateway.js";
6
+ const HUB_URL = "wss://mesh.example.test/";
7
+ function makeHarness() {
8
+ const connectHub = vi.fn().mockResolvedValue(undefined);
9
+ const disconnectHub = vi.fn().mockResolvedValue(undefined);
10
+ const onError = vi.fn();
11
+ const deps = {
12
+ hubUrl: HUB_URL,
13
+ connectHub,
14
+ disconnectHub,
15
+ onError,
16
+ };
17
+ return {
18
+ gateway: new CoordinatorGateway(deps),
19
+ connectHub,
20
+ disconnectHub,
21
+ onError,
22
+ };
23
+ }
24
+ describe("CoordinatorGateway — onBecameCoordinator", () => {
25
+ it("dials the configured hub URL", async () => {
26
+ const { gateway, connectHub } = makeHarness();
27
+ await gateway.onBecameCoordinator();
28
+ expect(connectHub).toHaveBeenCalledWith(HUB_URL);
29
+ expect(connectHub).toHaveBeenCalledTimes(1);
30
+ });
31
+ it("marks isConnected true once dialled", async () => {
32
+ const { gateway } = makeHarness();
33
+ expect(gateway.isConnected).toBe(false);
34
+ await gateway.onBecameCoordinator();
35
+ expect(gateway.isConnected).toBe(true);
36
+ });
37
+ it("is idempotent -- a second call while already connected does not redial", async () => {
38
+ const { gateway, connectHub } = makeHarness();
39
+ await gateway.onBecameCoordinator();
40
+ await gateway.onBecameCoordinator();
41
+ expect(connectHub).toHaveBeenCalledTimes(1);
42
+ });
43
+ it("reports a dial failure via onError rather than throwing -- local coordinator election must not depend on hub reachability", async () => {
44
+ const { gateway, connectHub, onError } = makeHarness();
45
+ const dialError = new Error("ECONNREFUSED");
46
+ connectHub.mockRejectedValueOnce(dialError);
47
+ await expect(gateway.onBecameCoordinator()).resolves.toBeUndefined();
48
+ expect(onError).toHaveBeenCalledWith(dialError);
49
+ });
50
+ it("does not mark itself connected after a failed dial, so a later call can retry", async () => {
51
+ const { gateway, connectHub } = makeHarness();
52
+ connectHub.mockRejectedValueOnce(new Error("ECONNREFUSED"));
53
+ await gateway.onBecameCoordinator();
54
+ expect(gateway.isConnected).toBe(false);
55
+ await gateway.onBecameCoordinator();
56
+ expect(gateway.isConnected).toBe(true);
57
+ expect(connectHub).toHaveBeenCalledTimes(2);
58
+ });
59
+ });
60
+ describe("CoordinatorGateway — onLostCoordinator", () => {
61
+ it("drops the hub connection when one is held", async () => {
62
+ const { gateway, disconnectHub } = makeHarness();
63
+ await gateway.onBecameCoordinator();
64
+ await gateway.onLostCoordinator();
65
+ expect(disconnectHub).toHaveBeenCalledTimes(1);
66
+ expect(gateway.isConnected).toBe(false);
67
+ });
68
+ it("is a no-op when this side never became the gateway", async () => {
69
+ const { gateway, disconnectHub } = makeHarness();
70
+ await gateway.onLostCoordinator();
71
+ expect(disconnectHub).not.toHaveBeenCalled();
72
+ });
73
+ it("is a no-op on a second call after already losing the role", async () => {
74
+ const { gateway, disconnectHub } = makeHarness();
75
+ await gateway.onBecameCoordinator();
76
+ await gateway.onLostCoordinator();
77
+ await gateway.onLostCoordinator();
78
+ expect(disconnectHub).toHaveBeenCalledTimes(1);
79
+ });
80
+ it("allows redialling after losing and regaining the role", async () => {
81
+ const { gateway, connectHub } = makeHarness();
82
+ await gateway.onBecameCoordinator();
83
+ await gateway.onLostCoordinator();
84
+ await gateway.onBecameCoordinator();
85
+ expect(connectHub).toHaveBeenCalledTimes(2);
86
+ });
87
+ });
88
+ //# sourceMappingURL=coordinator-gateway.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-gateway.test.js","sourceRoot":"","sources":["../../src/test/coordinator-gateway.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gossip-directory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gossip-directory.test.d.ts","sourceRoot":"","sources":["../../src/test/gossip-directory.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Direct unit tests for mergeKnownDevices, extracted from WireMeshTransport (see gossip-directory.ts's own header) -- previously only exercised indirectly through gossip-directory-aggregation.integration.test.ts's real-session harness.
3
+ */
4
+ import { describe, expect, it } from "vitest";
5
+ import { mergeKnownDevices } from "../core/gossip-directory.js";
6
+ const DEVICE_ID_HEX_LENGTH = 64;
7
+ const DEVICE_A_HEX = "a".repeat(DEVICE_ID_HEX_LENGTH);
8
+ const DEVICE_B_HEX = "b".repeat(DEVICE_ID_HEX_LENGTH);
9
+ /** An arbitrary "later" snapshot-seconds value, distinct from every other one used in this file's fixtures. */
10
+ const NEWER_SNAPSHOT_SECONDS = 5;
11
+ function deviceIdBytes(hex) {
12
+ const bytes = new Uint8Array(new ArrayBuffer(hex.length / 2));
13
+ for (let i = 0; i < bytes.length; i += 1) {
14
+ bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
15
+ }
16
+ return bytes;
17
+ }
18
+ function advert(snapshotSeconds) {
19
+ return {
20
+ addresses: [],
21
+ "snapshot-seconds": snapshotSeconds,
22
+ };
23
+ }
24
+ function entry(hex, snapshotSeconds) {
25
+ return { device: deviceIdBytes(hex), advert: advert(snapshotSeconds) };
26
+ }
27
+ describe("mergeKnownDevices", () => {
28
+ it("records a device this map has never seen before", () => {
29
+ const knownDevices = new Map();
30
+ mergeKnownDevices(knownDevices, [entry(DEVICE_A_HEX, 1)]);
31
+ expect(knownDevices.get(DEVICE_A_HEX)?.["snapshot-seconds"]).toBe(1);
32
+ });
33
+ it("replaces an existing entry with a strictly newer advert", () => {
34
+ const knownDevices = new Map([
35
+ [DEVICE_A_HEX, advert(1)],
36
+ ]);
37
+ mergeKnownDevices(knownDevices, [entry(DEVICE_A_HEX, 2)]);
38
+ expect(knownDevices.get(DEVICE_A_HEX)?.["snapshot-seconds"]).toBe(2);
39
+ });
40
+ it("keeps an equal-snapshot advert as the incoming one (>=, not >)", () => {
41
+ const knownDevices = new Map([
42
+ [DEVICE_A_HEX, advert(1)],
43
+ ]);
44
+ const incoming = advert(1);
45
+ mergeKnownDevices(knownDevices, [
46
+ { device: deviceIdBytes(DEVICE_A_HEX), advert: incoming },
47
+ ]);
48
+ expect(knownDevices.get(DEVICE_A_HEX)).toBe(incoming);
49
+ });
50
+ it("never regresses an existing entry to an older advert", () => {
51
+ const knownDevices = new Map([
52
+ [DEVICE_A_HEX, advert(NEWER_SNAPSHOT_SECONDS)],
53
+ ]);
54
+ mergeKnownDevices(knownDevices, [entry(DEVICE_A_HEX, 1)]);
55
+ expect(knownDevices.get(DEVICE_A_HEX)?.["snapshot-seconds"]).toBe(NEWER_SNAPSHOT_SECONDS);
56
+ });
57
+ it("merges multiple distinct devices from the same directory independently", () => {
58
+ const knownDevices = new Map();
59
+ mergeKnownDevices(knownDevices, [
60
+ entry(DEVICE_A_HEX, 1),
61
+ entry(DEVICE_B_HEX, 2),
62
+ ]);
63
+ expect(knownDevices.get(DEVICE_A_HEX)?.["snapshot-seconds"]).toBe(1);
64
+ expect(knownDevices.get(DEVICE_B_HEX)?.["snapshot-seconds"]).toBe(2);
65
+ });
66
+ });
67
+ //# sourceMappingURL=gossip-directory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gossip-directory.test.js","sourceRoot":"","sources":["../../src/test/gossip-directory.test.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,11 @@
1
1
  import type { Frame } from "wire-mesh-core/generated/protocol";
2
2
  export declare function cbor2ToBytes(frame: Frame): Uint8Array;
3
+ /** A real relay hub served over ws: each accepted socket is wrapped as a Connection for createRelayHub, exactly what the production Durable Object does (pre-hibernation shape). Also exposes the hub's own live-connection count, since a test asserting a client-side disconnect actually reached the far end needs an observable on the hub itself, not just the client. */
4
+ export declare function realHubOverWs(): Promise<{
5
+ url: string;
6
+ connectionCount: () => number;
7
+ close: () => Promise<void>;
8
+ }>;
9
+ /** Polls `condition` until it's true or `timeoutMs` elapses, rejecting on timeout -- the shared shape every hub integration test uses to wait out real async gossip/discovery/close propagation rather than asserting immediately after firing an action. */
10
+ export declare function waitForCondition(condition: () => boolean, timeoutMs?: number): Promise<void>;
3
11
  //# sourceMappingURL=hub-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hub-helpers.d.ts","sourceRoot":"","sources":["../../src/test/hub-helpers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAC;AAE/D,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,CAErD"}
1
+ {"version":3,"file":"hub-helpers.d.ts","sourceRoot":"","sources":["../../src/test/hub-helpers.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAC;AAO/D,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,CAErD;AA2ED,+WAA+W;AAC/W,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC,CAgCD;AAED,6PAA6P;AAC7P,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,OAAO,EACxB,SAAS,SAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAgBf"}