agent-comms 8.3.1 → 8.4.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 (150) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +27 -16
  4. package/dist/bridges/user/web/frontend/mesh-client.js +1 -1
  5. package/dist/bridges/user/web/frontend/mesh-client.js.map +1 -1
  6. package/dist/bridges/user/web/server.d.ts +1 -1
  7. package/dist/bridges/user/web/server.d.ts.map +1 -1
  8. package/dist/bridges/user/web/server.js +6 -12
  9. package/dist/bridges/user/web/server.js.map +1 -1
  10. package/dist/bridges/user/web/test/lan-web-server.integration.test.js +30 -1
  11. package/dist/bridges/user/web/test/lan-web-server.integration.test.js.map +1 -1
  12. package/dist/bridges/user/web/test/orpc-router.integration.test.js +15 -1
  13. package/dist/bridges/user/web/test/orpc-router.integration.test.js.map +1 -1
  14. package/dist/bridges/user/web/test/web-access.unit.test.js +127 -2
  15. package/dist/bridges/user/web/test/web-access.unit.test.js.map +1 -1
  16. package/dist/bridges/user/web/web-access.d.ts +9 -6
  17. package/dist/bridges/user/web/web-access.d.ts.map +1 -1
  18. package/dist/bridges/user/web/web-access.js +70 -22
  19. package/dist/bridges/user/web/web-access.js.map +1 -1
  20. package/dist/core/coordinator-role.d.ts +68 -0
  21. package/dist/core/coordinator-role.d.ts.map +1 -0
  22. package/dist/core/coordinator-role.js +161 -0
  23. package/dist/core/coordinator-role.js.map +1 -0
  24. package/dist/core/delivery-engine.d.ts.map +1 -1
  25. package/dist/core/delivery-engine.js +2 -0
  26. package/dist/core/delivery-engine.js.map +1 -1
  27. package/dist/core/election-sessions.d.ts +31 -0
  28. package/dist/core/election-sessions.d.ts.map +1 -0
  29. package/dist/core/election-sessions.js +59 -0
  30. package/dist/core/election-sessions.js.map +1 -0
  31. package/dist/core/loopback-address.d.ts +6 -0
  32. package/dist/core/loopback-address.d.ts.map +1 -0
  33. package/dist/core/loopback-address.js +21 -0
  34. package/dist/core/loopback-address.js.map +1 -0
  35. package/dist/core/mesh-store-options.d.ts +2 -0
  36. package/dist/core/mesh-store-options.d.ts.map +1 -1
  37. package/dist/core/mesh-store.d.ts +8 -2
  38. package/dist/core/mesh-store.d.ts.map +1 -1
  39. package/dist/core/mesh-store.js +41 -46
  40. package/dist/core/mesh-store.js.map +1 -1
  41. package/dist/core/peer-lifecycle.d.ts +18 -10
  42. package/dist/core/peer-lifecycle.d.ts.map +1 -1
  43. package/dist/core/peer-lifecycle.js +50 -9
  44. package/dist/core/peer-lifecycle.js.map +1 -1
  45. package/dist/core/pending-connection.d.ts +24 -0
  46. package/dist/core/pending-connection.d.ts.map +1 -0
  47. package/dist/core/pending-connection.js +25 -0
  48. package/dist/core/pending-connection.js.map +1 -0
  49. package/dist/core/stale-agent-checker.d.ts +4 -2
  50. package/dist/core/stale-agent-checker.d.ts.map +1 -1
  51. package/dist/core/stale-agent-checker.js +5 -4
  52. package/dist/core/stale-agent-checker.js.map +1 -1
  53. package/dist/core/transport-events.d.ts +2 -0
  54. package/dist/core/transport-events.d.ts.map +1 -1
  55. package/dist/core/transport-events.js +8 -0
  56. package/dist/core/transport-events.js.map +1 -1
  57. package/dist/core/transport.d.ts +22 -2
  58. package/dist/core/transport.d.ts.map +1 -1
  59. package/dist/core/wire-mesh-transport-options.d.ts +14 -0
  60. package/dist/core/wire-mesh-transport-options.d.ts.map +1 -1
  61. package/dist/core/wire-mesh-transport-options.js +23 -1
  62. package/dist/core/wire-mesh-transport-options.js.map +1 -1
  63. package/dist/core/wire-mesh-transport.d.ts +7 -5
  64. package/dist/core/wire-mesh-transport.d.ts.map +1 -1
  65. package/dist/core/wire-mesh-transport.js +53 -52
  66. package/dist/core/wire-mesh-transport.js.map +1 -1
  67. package/dist/core/ws-dial.d.ts +3 -1
  68. package/dist/core/ws-dial.d.ts.map +1 -1
  69. package/dist/core/ws-dial.js +15 -0
  70. package/dist/core/ws-dial.js.map +1 -1
  71. package/dist/test/agent-comms-version-gossip.integration.test.js +2 -0
  72. package/dist/test/agent-comms-version-gossip.integration.test.js.map +1 -1
  73. package/dist/test/agent-self-gossip.integration.test.js +2 -0
  74. package/dist/test/agent-self-gossip.integration.test.js.map +1 -1
  75. package/dist/test/become-coordinator-actual-port.integration.test.js +2 -0
  76. package/dist/test/become-coordinator-actual-port.integration.test.js.map +1 -1
  77. package/dist/test/coordinator-claim-sessions.integration.test.d.ts +5 -0
  78. package/dist/test/coordinator-claim-sessions.integration.test.d.ts.map +1 -0
  79. package/dist/test/coordinator-claim-sessions.integration.test.js +276 -0
  80. package/dist/test/coordinator-claim-sessions.integration.test.js.map +1 -0
  81. package/dist/test/coordinator-election.integration.test.d.ts +5 -0
  82. package/dist/test/coordinator-election.integration.test.d.ts.map +1 -0
  83. package/dist/test/coordinator-election.integration.test.js +182 -0
  84. package/dist/test/coordinator-election.integration.test.js.map +1 -0
  85. package/dist/test/coordinator-failover.unit.test.d.ts +1 -1
  86. package/dist/test/coordinator-failover.unit.test.js +171 -68
  87. package/dist/test/coordinator-failover.unit.test.js.map +1 -1
  88. package/dist/test/coordinator-role.unit.test.d.ts +5 -0
  89. package/dist/test/coordinator-role.unit.test.d.ts.map +1 -0
  90. package/dist/test/coordinator-role.unit.test.js +296 -0
  91. package/dist/test/coordinator-role.unit.test.js.map +1 -0
  92. package/dist/test/data-sync-frame-handling.integration.test.js +2 -0
  93. package/dist/test/data-sync-frame-handling.integration.test.js.map +1 -1
  94. package/dist/test/delivery-engine-delivery.unit.test.js +3 -0
  95. package/dist/test/delivery-engine-delivery.unit.test.js.map +1 -1
  96. package/dist/test/delivery-engine-directed-notify.unit.test.js +3 -0
  97. package/dist/test/delivery-engine-directed-notify.unit.test.js.map +1 -1
  98. package/dist/test/delivery-engine-own-agent.unit.test.js +3 -0
  99. package/dist/test/delivery-engine-own-agent.unit.test.js.map +1 -1
  100. package/dist/test/delivery-engine.unit.test.js +12 -0
  101. package/dist/test/delivery-engine.unit.test.js.map +1 -1
  102. package/dist/test/gossip-directory-aggregation.integration.test.js +2 -0
  103. package/dist/test/gossip-directory-aggregation.integration.test.js.map +1 -1
  104. package/dist/test/gossip-listener-addresses.integration.test.js +2 -0
  105. package/dist/test/gossip-listener-addresses.integration.test.js.map +1 -1
  106. package/dist/test/hosted-rooms-gossip.integration.test.js +2 -0
  107. package/dist/test/hosted-rooms-gossip.integration.test.js.map +1 -1
  108. package/dist/test/hub-mode-session.integration.test.js +2 -0
  109. package/dist/test/hub-mode-session.integration.test.js.map +1 -1
  110. package/dist/test/hub-session-dispatch.unit.test.js +2 -0
  111. package/dist/test/hub-session-dispatch.unit.test.js.map +1 -1
  112. package/dist/test/listener-policy.integration.test.js +2 -0
  113. package/dist/test/listener-policy.integration.test.js.map +1 -1
  114. package/dist/test/loopback-address.unit.test.d.ts +5 -0
  115. package/dist/test/loopback-address.unit.test.d.ts.map +1 -0
  116. package/dist/test/loopback-address.unit.test.js +28 -0
  117. package/dist/test/loopback-address.unit.test.js.map +1 -0
  118. package/dist/test/mesh-graph-trace.integration.test.js +2 -0
  119. package/dist/test/mesh-graph-trace.integration.test.js.map +1 -1
  120. package/dist/test/mesh-store-orchestration.unit.test.js +23 -6
  121. package/dist/test/mesh-store-orchestration.unit.test.js.map +1 -1
  122. package/dist/test/peer-lifecycle.unit.test.js +35 -15
  123. package/dist/test/peer-lifecycle.unit.test.js.map +1 -1
  124. package/dist/test/presence-readvertise.integration.test.js +49 -0
  125. package/dist/test/presence-readvertise.integration.test.js.map +1 -1
  126. package/dist/test/query-version.integration.test.js +2 -0
  127. package/dist/test/query-version.integration.test.js.map +1 -1
  128. package/dist/test/room-join-admission.integration.test.js +3 -0
  129. package/dist/test/room-join-admission.integration.test.js.map +1 -1
  130. package/dist/test/room-router.unit.test.js +2 -0
  131. package/dist/test/room-router.unit.test.js.map +1 -1
  132. package/dist/test/room-send-retry-queue.integration.test.js +3 -0
  133. package/dist/test/room-send-retry-queue.integration.test.js.map +1 -1
  134. package/dist/test/stale-agent-checker.unit.test.js +5 -0
  135. package/dist/test/stale-agent-checker.unit.test.js.map +1 -1
  136. package/dist/test/test-transport.d.ts +3 -0
  137. package/dist/test/test-transport.d.ts.map +1 -1
  138. package/dist/test/test-transport.js +19 -0
  139. package/dist/test/test-transport.js.map +1 -1
  140. package/dist/test/wire-mesh-transport-gateway-trust.integration.test.js +2 -0
  141. package/dist/test/wire-mesh-transport-gateway-trust.integration.test.js.map +1 -1
  142. package/dist/test/wire-mesh-transport-hub.integration.test.js +2 -0
  143. package/dist/test/wire-mesh-transport-hub.integration.test.js.map +1 -1
  144. package/dist/test/wire-mesh-transport-shutdown-unref.integration.test.js +10 -26
  145. package/dist/test/wire-mesh-transport-shutdown-unref.integration.test.js.map +1 -1
  146. package/dist/test/wire-mesh-transport.integration.test.js +51 -51
  147. package/dist/test/wire-mesh-transport.integration.test.js.map +1 -1
  148. package/dist/test/wss-remote-connect.integration.test.js +2 -0
  149. package/dist/test/wss-remote-connect.integration.test.js.map +1 -1
  150. package/package.json +1 -1
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Which sessions a store accepts coordinator claims from, and announces its own over (agent-comms#341), against real sockets. A claim is a bid for this machine's coordinator duties, so it may only come from a trusted session to a peer on this machine: never from a session that may cross machines (an addListener listener, a connectToRemote dial, a data dial at an address other than a loopback one), and never from a session on the well-known port before it has introduced itself. A hand-built peer on the far end of each session sends a claim at a term above the store's own, so an accepted claim would visibly replace the store's; a manage-request sent after the claim over the same session, and answered, proves the store has read the claim before the test looks at its state.
3
+ */
4
+ import * as os from "node:os";
5
+ import { test, expect } from "vitest";
6
+ import { createTlsTransport } from "wire-mesh-core/adapters/tls-transport";
7
+ import { acceptMeshSession, } from "wire-mesh-core/domain/mesh-session";
8
+ import { deviceIdFromHex, deviceIdToHex, } from "wire-mesh-core/domain/device-id";
9
+ import { generateIdentity } from "../core/identity.js";
10
+ import { listenerPort } from "../core/listener-registry.js";
11
+ import { MeshStore } from "../core/mesh-store.js";
12
+ import { DOMAIN, FRAME_SCOPE, buildCommand, } from "../core/wire-mesh-transport.js";
13
+ import { toIdentityPort } from "../core/wire-mesh-identity.js";
14
+ import { TeardownStack, freeLocalPort } from "./hub-helpers.js";
15
+ import { wireTestTransport } from "./test-transport.js";
16
+ /** Above the term 0 a lone store claims at, so accepting this claim would supersede the store's own. */
17
+ const ROGUE_TERM = 7;
18
+ function rawPeer() {
19
+ const identity = generateIdentity();
20
+ return {
21
+ deviceHex: deviceIdToHex(Uint8Array.from(identity.deviceId)),
22
+ identity,
23
+ transport: createTlsTransport({
24
+ certificatePem: identity.certificate,
25
+ privateKeyPem: identity.privateKey,
26
+ }),
27
+ };
28
+ }
29
+ async function openSession(peer, connection, teardown) {
30
+ const session = await acceptMeshSession(connection, await toIdentityPort(peer.identity), [DOMAIN]);
31
+ teardown.push(async () => {
32
+ await session.close();
33
+ });
34
+ return session;
35
+ }
36
+ async function dialAsRawPeer(peer, port, teardown) {
37
+ const connection = await peer.transport.connect(`127.0.0.1:${String(port)}`);
38
+ return openSession(peer, connection, teardown);
39
+ }
40
+ /** A lone store on its own well-known port, holding the role at term 0 as it claims when nothing else could reach it. */
41
+ async function startLoneHolder(teardown) {
42
+ const store = new MeshStore({ coordinatorPort: await freeLocalPort() });
43
+ await wireTestTransport(store);
44
+ teardown.push(async () => {
45
+ await store.shutdown();
46
+ });
47
+ await store.init();
48
+ expect(store.coordinatorClaim).toEqual({ term: 0, holder: store.peerId });
49
+ return store;
50
+ }
51
+ function rogueClaim(holderHex) {
52
+ return {
53
+ type: "coordinator",
54
+ term: ROGUE_TERM,
55
+ coordinator: deviceIdFromHex(holderHex),
56
+ };
57
+ }
58
+ /** Sends a request the store answers once it has read everything sent before it on this session. */
59
+ async function roundTrip(session) {
60
+ const outcome = await session.sendManageRequest(buildCommand({ method: "peer_list", peers: [] }), FRAME_SCOPE);
61
+ expect(outcome.result).toBe("ok");
62
+ }
63
+ async function introduce(session, peer) {
64
+ // Nothing listens on this port, so the store's dial back at the raw peer's data server fails, which none of these tests depend on.
65
+ const dataPort = await freeLocalPort();
66
+ const outcome = await session.sendManageRequest(buildCommand({ method: "introduce", peerId: peer.deviceHex, dataPort }), FRAME_SCOPE);
67
+ expect(outcome.result).toBe("ok");
68
+ }
69
+ async function firstCoordinatorFrame(session) {
70
+ for await (const frame of session.coordinatorFrames)
71
+ return frame;
72
+ throw new Error("the session ended before any coordinator-frame arrived");
73
+ }
74
+ test("a claim from a peer introduced on the well-known port is accepted", async () => {
75
+ const teardown = new TeardownStack();
76
+ try {
77
+ const store = await startLoneHolder(teardown);
78
+ const peer = rawPeer();
79
+ const session = await dialAsRawPeer(peer, store.coordinatorPort, teardown);
80
+ await introduce(session, peer);
81
+ await session.sendCoordinatorClaim(rogueClaim(peer.deviceHex));
82
+ await roundTrip(session);
83
+ expect(store.coordinatorClaim).toEqual({
84
+ term: ROGUE_TERM,
85
+ holder: peer.deviceHex,
86
+ });
87
+ expect(store.holdsCoordinatorRole).toBe(false);
88
+ }
89
+ finally {
90
+ await teardown.run();
91
+ }
92
+ });
93
+ test("a claim sent on the well-known port before introduce is dropped, and the peer is told the incumbent once it introduces itself", async () => {
94
+ const teardown = new TeardownStack();
95
+ try {
96
+ const store = await startLoneHolder(teardown);
97
+ const peer = rawPeer();
98
+ const session = await dialAsRawPeer(peer, store.coordinatorPort, teardown);
99
+ const announced = firstCoordinatorFrame(session);
100
+ await session.sendCoordinatorClaim(rogueClaim(peer.deviceHex));
101
+ await introduce(session, peer);
102
+ await roundTrip(session);
103
+ expect(store.coordinatorClaim).toEqual({ term: 0, holder: store.peerId });
104
+ expect(store.holdsCoordinatorRole).toBe(true);
105
+ expect(await announced).toEqual({
106
+ type: "coordinator",
107
+ term: 0,
108
+ coordinator: deviceIdFromHex(store.peerId),
109
+ });
110
+ }
111
+ finally {
112
+ await teardown.run();
113
+ }
114
+ });
115
+ test("a claim from a peer introduced on an addListener listener is dropped, since that listener may be reached from another machine", async () => {
116
+ const teardown = new TeardownStack();
117
+ try {
118
+ const store = await startLoneHolder(teardown);
119
+ const listenerId = await store.addListener("127.0.0.1", 0, "full");
120
+ const listener = store
121
+ .listListeners()
122
+ .find((entry) => entry.id === listenerId);
123
+ if (listener === undefined)
124
+ throw new Error("expected the added listener");
125
+ const peer = rawPeer();
126
+ const session = await dialAsRawPeer(peer, listener.port, teardown);
127
+ await introduce(session, peer);
128
+ await session.sendCoordinatorClaim(rogueClaim(peer.deviceHex));
129
+ await roundTrip(session);
130
+ expect(store.coordinatorClaim).toEqual({ term: 0, holder: store.peerId });
131
+ expect(store.holdsCoordinatorRole).toBe(true);
132
+ }
133
+ finally {
134
+ await teardown.run();
135
+ }
136
+ });
137
+ test("a claim from the far end of a connectToRemote dial is dropped, since that dial may reach another machine", async () => {
138
+ const teardown = new TeardownStack();
139
+ try {
140
+ const store = await startLoneHolder(teardown);
141
+ const peer = rawPeer();
142
+ const accepted = new Promise((resolve, reject) => {
143
+ void peer.transport
144
+ .listen("127.0.0.1:0", (connection) => {
145
+ openSession(peer, connection, teardown).then(resolve, reject);
146
+ })
147
+ .then(async (listener) => {
148
+ teardown.push(async () => {
149
+ await listener.close();
150
+ });
151
+ await store.connectToRemote("127.0.0.1", listenerPort(listener));
152
+ })
153
+ .catch(reject);
154
+ });
155
+ const session = await accepted;
156
+ for await (const request of session.incomingManageRequests) {
157
+ // The dial's connect_request, answered as an approving operator would.
158
+ await request.respond({ result: "ok" });
159
+ break;
160
+ }
161
+ // Answered only once the store is reading this session, so the claim below cannot arrive before the store has decided whether to enrol it.
162
+ await roundTrip(session);
163
+ await session.sendCoordinatorClaim(rogueClaim(peer.deviceHex));
164
+ await roundTrip(session);
165
+ expect(store.coordinatorClaim).toEqual({ term: 0, holder: store.peerId });
166
+ expect(store.holdsCoordinatorRole).toBe(true);
167
+ }
168
+ finally {
169
+ await teardown.run();
170
+ }
171
+ });
172
+ test("a store that dials a peer's data server tells it the incumbent, not only the side that accepted", async () => {
173
+ const teardown = new TeardownStack();
174
+ try {
175
+ const store = await startLoneHolder(teardown);
176
+ const peer = rawPeer();
177
+ const accepted = new Promise((resolve, reject) => {
178
+ void peer.transport
179
+ .listen("127.0.0.1:0", (connection) => {
180
+ openSession(peer, connection, teardown).then(resolve, reject);
181
+ })
182
+ .then((listener) => {
183
+ teardown.push(async () => {
184
+ await listener.close();
185
+ });
186
+ store.events.onPeerList([
187
+ {
188
+ id: peer.deviceHex,
189
+ port: listenerPort(listener),
190
+ startedAt: new Date().toISOString(),
191
+ },
192
+ ]);
193
+ })
194
+ .catch(reject);
195
+ });
196
+ const session = await accepted;
197
+ expect(await firstCoordinatorFrame(session)).toEqual({
198
+ type: "coordinator",
199
+ term: 0,
200
+ coordinator: deviceIdFromHex(store.peerId),
201
+ });
202
+ }
203
+ finally {
204
+ await teardown.run();
205
+ }
206
+ });
207
+ /** An address of this machine's own that is not a loopback one, so a dial at it reaches a listener here while naming an address another machine on the LAN could equally hold. */
208
+ function nonLoopbackAddress() {
209
+ const address = Object.values(os.networkInterfaces())
210
+ .flat()
211
+ .find((entry) => entry?.family === "IPv4" && !entry.internal);
212
+ if (address === undefined) {
213
+ throw new Error("this test needs a non-loopback IPv4 interface to dial a listener through");
214
+ }
215
+ return address.address;
216
+ }
217
+ /** Has the store dial a raw peer's listener at the given host through the ordinary peer-list path (the one first contact feeds discovered peers into), returning the raw peer's end of the session once the store is reading it. */
218
+ async function dialedByStore(store, peer, host, teardown) {
219
+ const session = await new Promise((resolve, reject) => {
220
+ void peer.transport
221
+ .listen("0.0.0.0:0", (connection) => {
222
+ openSession(peer, connection, teardown).then(resolve, reject);
223
+ })
224
+ .then((listener) => {
225
+ teardown.push(async () => {
226
+ await listener.close();
227
+ });
228
+ store.events.onPeerList([
229
+ {
230
+ id: peer.deviceHex,
231
+ host,
232
+ port: listenerPort(listener),
233
+ startedAt: new Date().toISOString(),
234
+ },
235
+ ]);
236
+ })
237
+ .catch(reject);
238
+ });
239
+ // Answered only once the store is reading this session, so the claim the caller sends next cannot arrive before the store has decided whether to enrol it.
240
+ await roundTrip(session);
241
+ return session;
242
+ }
243
+ test("a claim over a data dial at a non-loopback address is dropped, since that address may belong to another machine on the LAN", async () => {
244
+ const teardown = new TeardownStack();
245
+ try {
246
+ const store = await startLoneHolder(teardown);
247
+ const peer = rawPeer();
248
+ const session = await dialedByStore(store, peer, nonLoopbackAddress(), teardown);
249
+ await session.sendCoordinatorClaim(rogueClaim(peer.deviceHex));
250
+ await roundTrip(session);
251
+ expect(store.coordinatorClaim).toEqual({ term: 0, holder: store.peerId });
252
+ expect(store.holdsCoordinatorRole).toBe(true);
253
+ }
254
+ finally {
255
+ await teardown.run();
256
+ }
257
+ });
258
+ test("a claim over a data dial at a loopback address is accepted", async () => {
259
+ const teardown = new TeardownStack();
260
+ try {
261
+ const store = await startLoneHolder(teardown);
262
+ const peer = rawPeer();
263
+ const session = await dialedByStore(store, peer, "127.0.0.1", teardown);
264
+ await session.sendCoordinatorClaim(rogueClaim(peer.deviceHex));
265
+ await roundTrip(session);
266
+ expect(store.coordinatorClaim).toEqual({
267
+ term: ROGUE_TERM,
268
+ holder: peer.deviceHex,
269
+ });
270
+ expect(store.holdsCoordinatorRole).toBe(false);
271
+ }
272
+ finally {
273
+ await teardown.run();
274
+ }
275
+ });
276
+ //# sourceMappingURL=coordinator-claim-sessions.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-claim-sessions.integration.test.js","sourceRoot":"","sources":["../../src/test/coordinator-claim-sessions.integration.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EACL,iBAAiB,GAElB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,eAAe,EACf,aAAa,GACd,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EACL,MAAM,EACN,WAAW,EACX,YAAY,GACb,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,wGAAwG;AACxG,MAAM,UAAU,GAAG,CAAC,CAAC;AAQrB,SAAS,OAAO;IACd,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,OAAO;QACL,SAAS,EAAE,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC5D,QAAQ;QACR,SAAS,EAAE,kBAAkB,CAAC;YAC5B,cAAc,EAAE,QAAQ,CAAC,WAAW;YACpC,aAAa,EAAE,QAAQ,CAAC,UAAU;SACnC,CAAC;KACH,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,IAAuB,EACvB,UAAgC,EAChC,QAAuB;IAEvB,MAAM,OAAO,GAAG,MAAM,iBAAiB,CACrC,UAAU,EACV,MAAM,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EACnC,CAAC,MAAM,CAAC,CACT,CAAC;IACF,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;QACvB,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,IAAuB,EACvB,IAAY,EACZ,QAAuB;IAEvB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,aAAa,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7E,OAAO,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AACjD,CAAC;AAED,yHAAyH;AACzH,KAAK,UAAU,eAAe,CAAC,QAAuB;IACpD,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,EAAE,eAAe,EAAE,MAAM,aAAa,EAAE,EAAE,CAAC,CAAC;IACxE,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC/B,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;QACvB,MAAM,KAAK,CAAC,QAAQ,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;IACnB,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1E,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,UAAU,CAAC,SAAiB;IACnC,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,eAAe,CAAC,SAAS,CAAC;KACxC,CAAC;AACJ,CAAC;AAED,oGAAoG;AACpG,KAAK,UAAU,SAAS,CAAC,OAA4B;IACnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAC7C,YAAY,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,EAChD,WAAW,CACZ,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,SAAS,CACtB,OAA4B,EAC5B,IAAuB;IAEvB,mIAAmI;IACnI,MAAM,QAAQ,GAAG,MAAM,aAAa,EAAE,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAC7C,YAAY,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAC,EACvE,WAAW,CACZ,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,OAA4B;IAE5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,iBAAiB;QAAE,OAAO,KAAK,CAAC;IAClE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;AAC5E,CAAC;AAED,IAAI,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;IACnF,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;QAE3E,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC/B,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/D,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QAEzB,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC;YACrC,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,IAAI,CAAC,SAAS;SACvB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+HAA+H,EAAE,KAAK,IAAI,EAAE;IAC/I,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;QAC3E,MAAM,SAAS,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAEjD,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/D,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC/B,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QAEzB,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1E,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC,OAAO,CAAC;YAC9B,IAAI,EAAE,aAAa;YACnB,IAAI,EAAE,CAAC;YACP,WAAW,EAAE,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC;SAC3C,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+HAA+H,EAAE,KAAK,IAAI,EAAE;IAC/I,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QACnE,MAAM,QAAQ,GAAG,KAAK;aACnB,aAAa,EAAE;aACf,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;QAC5C,IAAI,QAAQ,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAEnE,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC/B,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/D,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QAEzB,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1E,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0GAA0G,EAAE,KAAK,IAAI,EAAE;IAC1H,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACpE,KAAK,IAAI,CAAC,SAAS;iBAChB,MAAM,CAAC,aAAa,EAAE,CAAC,UAAU,EAAE,EAAE;gBACpC,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAChE,CAAC,CAAC;iBACD,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;gBACvB,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;oBACvB,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACzB,CAAC,CAAC,CAAC;gBACH,MAAM,KAAK,CAAC,eAAe,CAAC,WAAW,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;YACnE,CAAC,CAAC;iBACD,KAAK,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;QAC/B,IAAI,KAAK,EAAE,MAAM,OAAO,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC;YAC3D,uEAAuE;YACvE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACxC,MAAM;QACR,CAAC;QACD,2IAA2I;QAC3I,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QAEzB,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/D,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QAEzB,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1E,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iGAAiG,EAAE,KAAK,IAAI,EAAE;IACjH,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACpE,KAAK,IAAI,CAAC,SAAS;iBAChB,MAAM,CAAC,aAAa,EAAE,CAAC,UAAU,EAAE,EAAE;gBACpC,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAChE,CAAC,CAAC;iBACD,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;gBACjB,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;oBACvB,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACzB,CAAC,CAAC,CAAC;gBACH,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;oBACtB;wBACE,EAAE,EAAE,IAAI,CAAC,SAAS;wBAClB,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC;wBAC5B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;qBACpC;iBACF,CAAC,CAAC;YACL,CAAC,CAAC;iBACD,KAAK,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;QAE/B,MAAM,CAAC,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YACnD,IAAI,EAAE,aAAa;YACnB,IAAI,EAAE,CAAC;YACP,WAAW,EAAE,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC;SAC3C,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,kLAAkL;AAClL,SAAS,kBAAkB;IACzB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC;SAClD,IAAI,EAAE;SACN,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,CAAC;AACzB,CAAC;AAED,oOAAoO;AACpO,KAAK,UAAU,aAAa,CAC1B,KAAgB,EAChB,IAAuB,EACvB,IAAY,EACZ,QAAuB;IAEvB,MAAM,OAAO,GAAG,MAAM,IAAI,OAAO,CAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACzE,KAAK,IAAI,CAAC,SAAS;aAChB,MAAM,CAAC,WAAW,EAAE,CAAC,UAAU,EAAE,EAAE;YAClC,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAChE,CAAC,CAAC;aACD,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;YACjB,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;gBACvB,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;YACzB,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;gBACtB;oBACE,EAAE,EAAE,IAAI,CAAC,SAAS;oBAClB,IAAI;oBACJ,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC;oBAC5B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBACpC;aACF,CAAC,CAAC;QACL,CAAC,CAAC;aACD,KAAK,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;IACH,2JAA2J;IAC3J,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACzB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,IAAI,CAAC,4HAA4H,EAAE,KAAK,IAAI,EAAE;IAC5I,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,MAAM,aAAa,CACjC,KAAK,EACL,IAAI,EACJ,kBAAkB,EAAE,EACpB,QAAQ,CACT,CAAC;QAEF,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/D,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QAEzB,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1E,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;IAC5E,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QAExE,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/D,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QAEzB,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC;YACrC,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,IAAI,CAAC,SAAS;SACvB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The elected coordinator role over real localhost sockets (agent-comms#341): several MeshStore instances, each on its own WireMeshTransport, gossiping coordinator claims over their machine-local sessions. Asserts that exactly one store holds the role and every store agrees which, that a store which never bound the well-known port can hold it and keeps it when lower-id stores arrive, that stores which each claimed alone converge, and that the holder's loss is recovered by a survivor raising the term. Which sessions a claim is accepted from is covered by coordinator-claim-sessions.integration.test.ts; the decision logic is unit-tested against fakes in coordinator-role.unit.test.ts and coordinator-failover.unit.test.ts.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=coordinator-election.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-election.integration.test.d.ts","sourceRoot":"","sources":["../../src/test/coordinator-election.integration.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * The elected coordinator role over real localhost sockets (agent-comms#341): several MeshStore instances, each on its own WireMeshTransport, gossiping coordinator claims over their machine-local sessions. Asserts that exactly one store holds the role and every store agrees which, that a store which never bound the well-known port can hold it and keeps it when lower-id stores arrive, that stores which each claimed alone converge, and that the holder's loss is recovered by a survivor raising the term. Which sessions a claim is accepted from is covered by coordinator-claim-sessions.integration.test.ts; the decision logic is unit-tested against fakes in coordinator-role.unit.test.ts and coordinator-failover.unit.test.ts.
3
+ */
4
+ import * as net from "node:net";
5
+ import { test, expect } from "vitest";
6
+ import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
7
+ import { generateIdentity } from "../core/identity.js";
8
+ import { MeshStore } from "../core/mesh-store.js";
9
+ import { TeardownStack, freeLocalPort } from "./hub-helpers.js";
10
+ import { waitFor, wireTestTransportWithHub } from "./test-transport.js";
11
+ /** A claim wait for a store the test starts with nobody else to meet: there is no incumbent it could hear, so waiting any longer would only slow the test. */
12
+ const NOTHING_TO_WAIT_FOR_MS = 0;
13
+ async function startNode(name, options, teardown, identity) {
14
+ const store = new MeshStore(options);
15
+ const { transport } = await wireTestTransportWithHub(store, identity === undefined ? undefined : { identityLoader: () => identity });
16
+ const roleChanges = [];
17
+ store.onCoordinatorRoleChanged = (isCoordinator) => {
18
+ roleChanges.push(isCoordinator);
19
+ };
20
+ teardown.push(async () => {
21
+ await store.shutdown().catch(() => undefined);
22
+ });
23
+ await store.init();
24
+ return { name, store, transport, roleChanges };
25
+ }
26
+ function holders(nodes) {
27
+ return nodes.filter((node) => node.store.holdsCoordinatorRole);
28
+ }
29
+ /** Whether every node accepts the same claim and exactly one of them is the holder that claim names. */
30
+ function settledOnOneHolder(nodes) {
31
+ const [first, ...rest] = nodes;
32
+ const claim = first?.store.coordinatorClaim;
33
+ if (claim === undefined)
34
+ return false;
35
+ const agreed = rest.every((node) => node.store.coordinatorClaim?.holder === claim.holder &&
36
+ node.store.coordinatorClaim.term === claim.term);
37
+ const held = holders(nodes);
38
+ return agreed && held.length === 1 && held[0]?.store.peerId === claim.holder;
39
+ }
40
+ /** A TCP server on the given port that drops every connection at once, standing in for a stale process holding the well-known port: nothing on it ever answers as a port holder, and nobody else can bind it. */
41
+ async function occupyPort(port, teardown) {
42
+ const squatter = net.createServer((socket) => {
43
+ socket.destroy();
44
+ });
45
+ await new Promise((resolve, reject) => {
46
+ squatter.once("error", reject);
47
+ squatter.listen(port, "127.0.0.1", () => {
48
+ resolve();
49
+ });
50
+ });
51
+ teardown.push(async () => {
52
+ await new Promise((resolve) => {
53
+ squatter.close(() => {
54
+ resolve();
55
+ });
56
+ });
57
+ });
58
+ }
59
+ test("exactly one of three stores holds the elected coordinator role, and every store agrees which", async () => {
60
+ const teardown = new TeardownStack();
61
+ try {
62
+ const coordinatorPort = await freeLocalPort();
63
+ const first = await startNode("first", { coordinatorPort }, teardown);
64
+ const second = await startNode("second", { coordinatorPort }, teardown);
65
+ const third = await startNode("third", { coordinatorPort }, teardown);
66
+ const nodes = [first, second, third];
67
+ await waitFor(() => settledOnOneHolder(nodes), "every store accepts one claim naming a single holder");
68
+ // Only the first store was alone when it started, so it is the only one that claimed; the joiners learned its claim instead of contesting it.
69
+ expect(first.store.holdsCoordinatorRole).toBe(true);
70
+ expect(first.store.coordinatorClaim).toEqual({
71
+ term: 0,
72
+ holder: first.store.peerId,
73
+ });
74
+ expect(first.roleChanges).toEqual([true]);
75
+ expect(second.roleChanges).toEqual([]);
76
+ expect(third.roleChanges).toEqual([]);
77
+ }
78
+ finally {
79
+ await teardown.run();
80
+ }
81
+ });
82
+ /** Three fresh identities, in ascending device-id order. */
83
+ function identitiesByDeviceId() {
84
+ const hex = (identity) => deviceIdToHex(Uint8Array.from(identity.deviceId));
85
+ const [lowest, middle, highest] = [
86
+ generateIdentity(),
87
+ generateIdentity(),
88
+ generateIdentity(),
89
+ ].sort((a, b) => (hex(a) < hex(b) ? -1 : 1));
90
+ if (lowest === undefined || middle === undefined || highest === undefined) {
91
+ throw new Error("expected three identities");
92
+ }
93
+ return [lowest, middle, highest];
94
+ }
95
+ test("an existing mesh behind a squatted port keeps its holder when a lower-id store joins through first contact", async () => {
96
+ const teardown = new TeardownStack();
97
+ try {
98
+ const coordinatorPort = await freeLocalPort();
99
+ const firstContactPort = await freeLocalPort();
100
+ await occupyPort(coordinatorPort, teardown);
101
+ const [lowest, middle, highest] = identitiesByDeviceId();
102
+ const holder = await startNode("holder", {
103
+ coordinatorPort,
104
+ firstContactPort,
105
+ coordinatorClaimWaitMs: NOTHING_TO_WAIT_FOR_MS,
106
+ }, teardown, highest);
107
+ await waitFor(() => holder.store.holdsCoordinatorRole, "the store started alone behind the squatted port claims the role");
108
+ // The joiners keep the default claim wait, which is what gives the holder's announcement time to reach each of them over first contact before it would claim.
109
+ const member = await startNode("member", { coordinatorPort, firstContactPort }, teardown, middle);
110
+ await waitFor(() => settledOnOneHolder([holder, member]), "the second store learned the incumbent through first contact");
111
+ const newcomer = await startNode("newcomer", { coordinatorPort, firstContactPort }, teardown, lowest);
112
+ const nodes = [holder, member, newcomer];
113
+ await waitFor(() => settledOnOneHolder(nodes), "the newcomer learned the incumbent through first contact");
114
+ expect(newcomer.store.peerId < member.store.peerId).toBe(true);
115
+ expect(member.store.peerId < holder.store.peerId).toBe(true);
116
+ for (const node of nodes) {
117
+ expect(node.transport.isCoordinator).toBe(false);
118
+ expect(node.store.coordinatorClaim).toEqual({
119
+ term: 0,
120
+ holder: holder.store.peerId,
121
+ });
122
+ }
123
+ expect(holder.roleChanges).toEqual([true]);
124
+ expect(member.roleChanges).toEqual([]);
125
+ expect(newcomer.roleChanges).toEqual([]);
126
+ }
127
+ finally {
128
+ await teardown.run();
129
+ }
130
+ });
131
+ test("two stores that each claimed alone behind a squatted port converge on the lower device-id once first contact joins them", async () => {
132
+ const teardown = new TeardownStack();
133
+ try {
134
+ const coordinatorPort = await freeLocalPort();
135
+ const firstContactPort = await freeLocalPort();
136
+ await occupyPort(coordinatorPort, teardown);
137
+ const options = {
138
+ coordinatorPort,
139
+ firstContactPort,
140
+ coordinatorClaimWaitMs: NOTHING_TO_WAIT_FOR_MS,
141
+ };
142
+ const left = await startNode("left", options, teardown);
143
+ const right = await startNode("right", options, teardown);
144
+ const nodes = [left, right];
145
+ await waitFor(() => settledOnOneHolder(nodes), "first contact brought both claims together and one superseded the other");
146
+ const [holder] = holders(nodes);
147
+ if (holder === undefined)
148
+ throw new Error("expected a holder");
149
+ // Both claimed term 0 while alone, so the equal-term tiebreak by lowest device-id decided it.
150
+ expect(holder.store.peerId).toBe([left.store.peerId, right.store.peerId].sort()[0]);
151
+ expect(holder.store.coordinatorClaim?.term).toBe(0);
152
+ expect(holder.roleChanges).toEqual([true]);
153
+ }
154
+ finally {
155
+ await teardown.run();
156
+ }
157
+ });
158
+ test("the holder's loss is recovered by a surviving store raising the term, and every survivor agrees on it", async () => {
159
+ const teardown = new TeardownStack();
160
+ try {
161
+ const coordinatorPort = await freeLocalPort();
162
+ const first = await startNode("first", { coordinatorPort }, teardown);
163
+ const second = await startNode("second", { coordinatorPort }, teardown);
164
+ const third = await startNode("third", { coordinatorPort }, teardown);
165
+ await waitFor(() => settledOnOneHolder([first, second, third]), "the mesh settled on the first store as holder before it is lost");
166
+ const lostTerm = first.store.coordinatorClaim?.term;
167
+ expect(first.store.holdsCoordinatorRole).toBe(true);
168
+ // A crash, not a shutdown: the transport goes down without the store giving anything up, as when the process is killed.
169
+ await first.transport.shutdown();
170
+ const survivors = [second, third];
171
+ await waitFor(() => settledOnOneHolder(survivors), "the survivors settled on one new holder");
172
+ const claim = second.store.coordinatorClaim;
173
+ expect(claim?.term).toBe((lostTerm ?? 0) + 1);
174
+ expect(claim?.holder).toBe([second.store.peerId, third.store.peerId].sort()[0]);
175
+ const [holder] = holders(survivors);
176
+ expect(holder?.roleChanges).toEqual([true]);
177
+ }
178
+ finally {
179
+ await teardown.run();
180
+ }
181
+ });
182
+ //# sourceMappingURL=coordinator-election.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-election.integration.test.js","sourceRoot":"","sources":["../../src/test/coordinator-election.integration.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAqB,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGlD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAExE,8JAA8J;AAC9J,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAUjC,KAAK,UAAU,SAAS,CACtB,IAAY,EACZ,OAAmC,EACnC,QAAuB,EACvB,QAAuB;IAEvB,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,wBAAwB,CAClD,KAAK,EACL,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CACxE,CAAC;IACF,MAAM,WAAW,GAAc,EAAE,CAAC;IAClC,KAAK,CAAC,wBAAwB,GAAG,CAAC,aAAa,EAAE,EAAE;QACjD,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAClC,CAAC,CAAC;IACF,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;QACvB,MAAM,KAAK,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;IACnB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;AACjD,CAAC;AAED,SAAS,OAAO,CAAC,KAAsB;IACrC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;AACjE,CAAC;AAED,wGAAwG;AACxG,SAAS,kBAAkB,CAAC,KAAsB;IAChD,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC;IAC/B,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,CAAC,gBAAgB,CAAC;IAC5C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CACvB,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,MAAM,KAAK,KAAK,CAAC,MAAM;QACpD,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAClD,CAAC;IACF,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,MAAM,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC;AAC/E,CAAC;AAED,iNAAiN;AACjN,KAAK,UAAU,UAAU,CACvB,IAAY,EACZ,QAAuB;IAEvB,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QAC3C,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC/B,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE;YACtC,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;QACvB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE;gBAClB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,CAAC,8FAA8F,EAAE,KAAK,IAAI,EAAE;IAC9G,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,MAAM,aAAa,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,QAAQ,EAAE,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,CAAC;QACtE,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAErC,MAAM,OAAO,CACX,GAAG,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAC/B,sDAAsD,CACvD,CAAC;QAEF,8IAA8I;QAC9I,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC;YAC3C,IAAI,EAAE,CAAC;YACP,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;SAC3B,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,4DAA4D;AAC5D,SAAS,oBAAoB;IAC3B,MAAM,GAAG,GAAG,CAAC,QAAsB,EAAU,EAAE,CAC7C,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG;QAChC,gBAAgB,EAAE;QAClB,gBAAgB,EAAE;QAClB,gBAAgB,EAAE;KACnB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACnC,CAAC;AAED,IAAI,CAAC,4GAA4G,EAAE,KAAK,IAAI,EAAE;IAC5H,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,MAAM,aAAa,EAAE,CAAC;QAC9C,MAAM,gBAAgB,GAAG,MAAM,aAAa,EAAE,CAAC;QAC/C,MAAM,UAAU,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,oBAAoB,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,SAAS,CAC5B,QAAQ,EACR;YACE,eAAe;YACf,gBAAgB;YAChB,sBAAsB,EAAE,sBAAsB;SAC/C,EACD,QAAQ,EACR,OAAO,CACR,CAAC;QACF,MAAM,OAAO,CACX,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,EACvC,kEAAkE,CACnE,CAAC;QACF,8JAA8J;QAC9J,MAAM,MAAM,GAAG,MAAM,SAAS,CAC5B,QAAQ,EACR,EAAE,eAAe,EAAE,gBAAgB,EAAE,EACrC,QAAQ,EACR,MAAM,CACP,CAAC;QACF,MAAM,OAAO,CACX,GAAG,EAAE,CAAC,kBAAkB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC1C,8DAA8D,CAC/D,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,SAAS,CAC9B,UAAU,EACV,EAAE,eAAe,EAAE,gBAAgB,EAAE,EACrC,QAAQ,EACR,MAAM,CACP,CAAC;QACF,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACzC,MAAM,OAAO,CACX,GAAG,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAC/B,0DAA0D,CAC3D,CAAC;QAEF,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC;gBAC1C,IAAI,EAAE,CAAC;gBACP,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM;aAC5B,CAAC,CAAC;QACL,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC3C,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yHAAyH,EAAE,KAAK,IAAI,EAAE;IACzI,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,MAAM,aAAa,EAAE,CAAC;QAC9C,MAAM,gBAAgB,GAAG,MAAM,aAAa,EAAE,CAAC;QAC/C,MAAM,UAAU,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG;YACd,eAAe;YACf,gBAAgB;YAChB,sBAAsB,EAAE,sBAAsB;SAC/C,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1D,MAAM,KAAK,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAE5B,MAAM,OAAO,CACX,GAAG,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAC/B,yEAAyE,CAC1E,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAC/D,8FAA8F;QAC9F,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAC9B,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAClD,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uGAAuG,EAAE,KAAK,IAAI,EAAE;IACvH,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,MAAM,aAAa,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,QAAQ,EAAE,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,CAAC;QACtE,MAAM,OAAO,CACX,GAAG,EAAE,CAAC,kBAAkB,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,EAChD,iEAAiE,CAClE,CAAC;QACF,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,gBAAgB,EAAE,IAAI,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEpD,wHAAwH;QACxH,MAAM,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;QAEjC,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAClC,MAAM,OAAO,CACX,GAAG,EAAE,CAAC,kBAAkB,CAAC,SAAS,CAAC,EACnC,yCAAyC,CAC1C,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC;QAC5C,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,IAAI,CACxB,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CACpD,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9C,CAAC;YAAS,CAAC;QACT,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * PeerLifecycle's crash-race coordinator failover decision (agent-comms#285), exercised against fakes: which disconnects contest the vacated coordinator role, what a peer that loses the bind race does instead, and who retires a departed peer's own agent record. The real-socket counterpart lives in coordinator-failover.integration.test.ts.
2
+ * PeerLifecycle's failover decisions, exercised against fakes with a real CoordinatorRole: which disconnects contest the vacated well-known port (agent-comms#285), what a peer that loses the bind race does instead, how the elected coordinator role is recovered when its holder departs (agent-comms#341), and who retires a departed peer's own agent record. The real-socket counterparts live in coordinator-failover.integration.test.ts and coordinator-election.integration.test.ts.
3
3
  */
4
4
  export {};
5
5
  //# sourceMappingURL=coordinator-failover.unit.test.d.ts.map