@zukhruf/single-flight 0.3.17 → 0.3.19

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 (76) hide show
  1. package/README.md +13 -11
  2. package/dist/client/flight-client.d.ts +2 -2
  3. package/dist/client/flight-client.d.ts.map +1 -1
  4. package/dist/client/flight-client.js +6 -1
  5. package/dist/client/flight-client.js.map +1 -1
  6. package/dist/connection/connection-supervisor.d.ts +4 -4
  7. package/dist/connection/connection-supervisor.d.ts.map +1 -1
  8. package/dist/connection/connection-supervisor.js.map +1 -1
  9. package/dist/connection/connection.d.ts +4 -3
  10. package/dist/connection/connection.d.ts.map +1 -1
  11. package/dist/connection/connector.d.ts +5 -5
  12. package/dist/connection/connector.d.ts.map +1 -1
  13. package/dist/connection/electing-connector.d.ts +4 -5
  14. package/dist/connection/electing-connector.d.ts.map +1 -1
  15. package/dist/connection/electing-connector.js +38 -42
  16. package/dist/connection/electing-connector.js.map +1 -1
  17. package/dist/connection/flight-election.d.ts +11 -0
  18. package/dist/connection/flight-election.d.ts.map +1 -0
  19. package/dist/connection/flight-election.js +18 -0
  20. package/dist/connection/flight-election.js.map +1 -0
  21. package/dist/connection/handshake.d.ts.map +1 -1
  22. package/dist/connection/handshake.js +6 -5
  23. package/dist/connection/handshake.js.map +1 -1
  24. package/dist/connection/json-line.d.ts +6 -0
  25. package/dist/connection/json-line.d.ts.map +1 -0
  26. package/dist/connection/json-line.js +8 -0
  27. package/dist/connection/json-line.js.map +1 -0
  28. package/dist/connection/leave-errors-to-close.d.ts +9 -0
  29. package/dist/connection/leave-errors-to-close.d.ts.map +1 -0
  30. package/dist/connection/leave-errors-to-close.js +10 -0
  31. package/dist/connection/leave-errors-to-close.js.map +1 -0
  32. package/dist/connection/socket-connection.d.ts +5 -5
  33. package/dist/connection/socket-connection.d.ts.map +1 -1
  34. package/dist/connection/socket-connection.js +21 -23
  35. package/dist/connection/socket-connection.js.map +1 -1
  36. package/dist/coordinator/flight-coordinator.d.ts +9 -4
  37. package/dist/coordinator/flight-coordinator.d.ts.map +1 -1
  38. package/dist/coordinator/flight-coordinator.js +26 -18
  39. package/dist/coordinator/flight-coordinator.js.map +1 -1
  40. package/dist/coordinator/flight-server.d.ts +9 -3
  41. package/dist/coordinator/flight-server.d.ts.map +1 -1
  42. package/dist/coordinator/flight-server.js +38 -24
  43. package/dist/coordinator/flight-server.js.map +1 -1
  44. package/dist/coordinator/flight.d.ts +3 -1
  45. package/dist/coordinator/flight.d.ts.map +1 -1
  46. package/dist/coordinator/flight.js +9 -5
  47. package/dist/coordinator/flight.js.map +1 -1
  48. package/dist/coordinator/party.d.ts +11 -2
  49. package/dist/coordinator/party.d.ts.map +1 -1
  50. package/dist/coordinator/party.js +15 -15
  51. package/dist/coordinator/party.js.map +1 -1
  52. package/dist/coordinator/session.d.ts +2 -2
  53. package/dist/coordinator/session.d.ts.map +1 -1
  54. package/dist/coordinator/session.js +7 -2
  55. package/dist/coordinator/session.js.map +1 -1
  56. package/dist/protocol/flight-protocol.d.ts +6 -2
  57. package/dist/protocol/flight-protocol.d.ts.map +1 -1
  58. package/dist/protocol/flight-protocol.js +15 -9
  59. package/dist/protocol/flight-protocol.js.map +1 -1
  60. package/dist/single-flight.d.ts +4 -2
  61. package/dist/single-flight.d.ts.map +1 -1
  62. package/dist/single-flight.js +10 -8
  63. package/dist/single-flight.js.map +1 -1
  64. package/package.json +2 -1
  65. package/dist/election/leader-election.d.ts +0 -24
  66. package/dist/election/leader-election.d.ts.map +0 -1
  67. package/dist/election/leader-election.js +0 -78
  68. package/dist/election/leader-election.js.map +0 -1
  69. package/dist/election/leadership.d.ts +0 -14
  70. package/dist/election/leadership.d.ts.map +0 -1
  71. package/dist/election/leadership.js +0 -23
  72. package/dist/election/leadership.js.map +0 -1
  73. package/dist/shared/sqlite/is-busy.d.ts +0 -3
  74. package/dist/shared/sqlite/is-busy.d.ts.map +0 -1
  75. package/dist/shared/sqlite/is-busy.js +0 -10
  76. package/dist/shared/sqlite/is-busy.js.map +0 -1
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Keeps the errors of a socket, or of a stream reading it, from stopping the
3
+ * process: an `error` with no listener does that. Every such error is followed
4
+ * by the socket's `close`, which is where the lost connection is reported, so
5
+ * this listener does nothing.
6
+ */
7
+ export function leaveErrorsToClose(emitter) {
8
+ emitter.on('error', () => { });
9
+ }
10
+ //# sourceMappingURL=leave-errors-to-close.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leave-errors-to-close.js","sourceRoot":"","sources":["../../src/connection/leave-errors-to-close.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAqB;IACtD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAChC,CAAC"}
@@ -3,13 +3,13 @@ import type { Socket } from 'node:net';
3
3
  import type { Connection, ConnectionEvents } from './connection.ts';
4
4
  /**
5
5
  * Newline-delimited JSON over a stream socket. A stream has no message
6
- * boundaries (writes arrive merged and split), and JSON escapes newlines inside
7
- * strings, so one line is always one message. `isIncoming` checks each message
8
- * from the peer; a line that is not one closes the connection.
6
+ * boundaries (writes arrive merged and split), so each message is one line
7
+ * (see `jsonLine`). A line that is not JSON breaks the framing, so it closes
8
+ * the connection; any JSON value goes to the listener, which checks it.
9
9
  */
10
- export declare class SocketConnection<Outgoing, Incoming> extends EventEmitter<ConnectionEvents<Incoming>> implements Connection<Outgoing, Incoming> {
10
+ export declare class SocketConnection<Outgoing> extends EventEmitter<ConnectionEvents> implements Connection<Outgoing> {
11
11
  #private;
12
- constructor(socket: Socket, isIncoming: (message: unknown) => message is Incoming);
12
+ constructor(socket: Socket);
13
13
  send(message: Outgoing): Promise<void>;
14
14
  ref(): void;
15
15
  unref(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"socket-connection.d.ts","sourceRoot":"","sources":["../../src/connection/socket-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEpE;;;;;GAKG;AACH,qBAAa,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAC9C,SAAQ,YAAY,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAC/C,YAAW,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC;;gBAKvC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,IAAI,QAAQ;IAqBvD,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtC,GAAG;IAIH,KAAK;IAIL,KAAK;CAGN"}
1
+ {"version":3,"file":"socket-connection.d.ts","sourceRoot":"","sources":["../../src/connection/socket-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAIvC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAIpE;;;;;GAKG;AACH,qBAAa,gBAAgB,CAAC,QAAQ,CACpC,SAAQ,YAAY,CAAC,gBAAgB,CACrC,YAAW,UAAU,CAAC,QAAQ,CAAC;;gBAMnB,MAAM,EAAE,MAAM;IAqB1B,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,GAAG;IAIH,KAAK;IAIL,KAAK;CAGN"}
@@ -1,43 +1,41 @@
1
1
  import { EventEmitter } from 'node:events';
2
2
  import { createInterface } from 'node:readline';
3
+ import { promisify } from 'node:util';
4
+ import { jsonLine } from "./json-line.js";
5
+ import { leaveErrorsToClose } from "./leave-errors-to-close.js";
3
6
  /**
4
7
  * Newline-delimited JSON over a stream socket. A stream has no message
5
- * boundaries (writes arrive merged and split), and JSON escapes newlines inside
6
- * strings, so one line is always one message. `isIncoming` checks each message
7
- * from the peer; a line that is not one closes the connection.
8
+ * boundaries (writes arrive merged and split), so each message is one line
9
+ * (see `jsonLine`). A line that is not JSON breaks the framing, so it closes
10
+ * the connection; any JSON value goes to the listener, which checks it.
8
11
  */
9
12
  export class SocketConnection extends EventEmitter {
10
13
  #socket;
11
- constructor(socket, isIncoming) {
14
+ /** A write after the socket closed reports the error through its callback. */
15
+ #write;
16
+ constructor(socket) {
12
17
  super();
13
18
  this.#socket = socket;
14
- // Every error is followed by `close`, which is where the peer's loss is handled.
15
- socket.on('error', () => { });
16
- createInterface({ input: socket, crlfDelay: Infinity })
17
- .on('line', (line) => {
19
+ this.#write = promisify(socket.write).bind(socket);
20
+ // readline removes its own 'error' listener from the socket when the
21
+ // interface closes on the peer's FIN, so an error after that (e.g. a reset
22
+ // after the half-close) needs this listener of the socket's own.
23
+ leaveErrorsToClose(socket);
24
+ const lines = createInterface({ input: socket, crlfDelay: Infinity });
25
+ lines.on('line', (line) => {
18
26
  try {
19
- const parsed = JSON.parse(line);
20
- if (isIncoming(parsed))
21
- this.emit('message', parsed);
22
- else
23
- this.close();
27
+ this.emit('message', JSON.parse(line));
24
28
  }
25
29
  catch {
26
30
  this.close();
27
31
  }
28
- })
29
- // readline re-emits socket errors (e.g. EPIPE when the peer died); `close` reports the loss.
30
- .on('error', () => { });
32
+ });
33
+ // readline re-emits the socket's errors (e.g. EPIPE when the peer died).
34
+ leaveErrorsToClose(lines);
31
35
  socket.once('close', () => this.emit('close'));
32
36
  }
33
37
  send(message) {
34
- return new Promise((resolve, reject) => {
35
- if (!this.#socket.writable) {
36
- reject(new Error('The socket is closed.'));
37
- return;
38
- }
39
- this.#socket.write(`${JSON.stringify(message)}\n`, (error) => error ? reject(error) : resolve());
40
- });
38
+ return this.#write(jsonLine(message));
41
39
  }
42
40
  ref() {
43
41
  this.#socket.ref();
@@ -1 +1 @@
1
- {"version":3,"file":"socket-connection.js","sourceRoot":"","sources":["../../src/connection/socket-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAIhD;;;;;GAKG;AACH,MAAM,OAAO,gBACX,SAAQ,YAAwC;IAGvC,OAAO,CAAS;IAEzB,YACE,MAAc,EACd,UAAqD;QAErD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,iFAAiF;QACjF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC7B,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;aACpD,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACnB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACzC,IAAI,UAAU,CAAC,MAAM,CAAC;oBAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;;oBAChD,IAAI,CAAC,KAAK,EAAE,CAAC;YACpB,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;QACH,CAAC,CAAC;YACF,6FAA6F;aAC5F,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,CAAC,OAAiB;QACpB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC;gBAC3C,OAAO;YACT,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAC3D,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAClC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,GAAG;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IACrB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC;CACF"}
1
+ {"version":3,"file":"socket-connection.js","sourceRoot":"","sources":["../../src/connection/socket-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE;;;;;GAKG;AACH,MAAM,OAAO,gBACX,SAAQ,YAA8B;IAG7B,OAAO,CAAS;IACzB,8EAA8E;IACrE,MAAM,CAAkC;IAEjD,YAAY,MAAc;QACxB,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAe,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjE,qEAAqE;QACrE,2EAA2E;QAC3E,iEAAiE;QACjE,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,KAAK,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;QACtE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;QACH,CAAC,CAAC,CAAC;QACH,yEAAyE;QACzE,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,CAAC,OAAiB;QACpB,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,GAAG;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IACrB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC;CACF"}
@@ -1,6 +1,6 @@
1
1
  import type { FencingToken, TokenSource } from '@zukhruf/fencing';
2
2
  import type { Connection } from '../connection/connection.ts';
3
- import type { FlightResponse, Outcome, RequestEnvelope } from '../protocol/flight-protocol.ts';
3
+ import type { FlightResponse, Outcome } from '../protocol/flight-protocol.ts';
4
4
  import { Flight } from './flight.ts';
5
5
  import type { Party } from './party.ts';
6
6
  export interface FlightCoordinatorOptions {
@@ -8,8 +8,8 @@ export interface FlightCoordinatorOptions {
8
8
  tokens: TokenSource;
9
9
  /**
10
10
  * Milliseconds after start during which no flight starts, so leaders from a
11
- * previous coordinator can reassert their flights first. Zero for a
12
- * coordinator that never replaces another.
11
+ * previous coordinator can reassert their flights first. Zero when no
12
+ * earlier coordinator can have left a flight in progress.
13
13
  */
14
14
  graceWindow: number;
15
15
  }
@@ -21,7 +21,12 @@ export interface FlightCoordinatorOptions {
21
21
  export declare class FlightCoordinator {
22
22
  #private;
23
23
  constructor({ tokens, graceWindow }: FlightCoordinatorOptions);
24
- serve(connection: Connection<FlightResponse, RequestEnvelope>): void;
24
+ /**
25
+ * Whether the next coordinator would find nothing to recover: no flight is
26
+ * in progress, and the grace window is over or there was none.
27
+ */
28
+ get leavesNothingToRecover(): boolean;
29
+ serve(connection: Connection<FlightResponse>): void;
25
30
  run(party: Party, key: string): Promise<void>;
26
31
  /** A joiner that lost its connection, back for the flight it joined: it never leads, so its work never runs twice. */
27
32
  rejoin(party: Party, key: string, flight: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"flight-coordinator.d.ts","sourceRoot":"","sources":["../../src/coordinator/flight-coordinator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EACV,cAAc,EACd,OAAO,EACP,eAAe,EAChB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,MAAM,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAUD;;;;GAIG;AACH,qBAAa,iBAAiB;;gBAGhB,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,wBAAwB;IAgB7D,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,cAAc,EAAE,eAAe,CAAC,GAAG,IAAI;IAIpE,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,sHAAsH;IACtH,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;IAI9D,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAI5C,qDAAqD;IACrD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAGhC"}
1
+ {"version":3,"file":"flight-coordinator.d.ts","sourceRoot":"","sources":["../../src/coordinator/flight-coordinator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,MAAM,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAWD;;;;GAIG;AACH,qBAAa,iBAAiB;;gBAGhB,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,wBAAwB;IAgB7D;;;OAGG;IACH,IAAI,sBAAsB,IAAI,OAAO,CAEpC;IAED,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,cAAc,CAAC,GAAG,IAAI;IAInD,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C,sHAAsH;IACtH,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;IAI9D,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAI5C,qDAAqD;IACrD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAGhC"}
@@ -24,6 +24,13 @@ export class FlightCoordinator {
24
24
  this.#phase = coordinating;
25
25
  }
26
26
  }
27
+ /**
28
+ * Whether the next coordinator would find nothing to recover: no flight is
29
+ * in progress, and the grace window is over or there was none.
30
+ */
31
+ get leavesNothingToRecover() {
32
+ return this.#phase.leavesNothingToRecover;
33
+ }
27
34
  serve(connection) {
28
35
  new Session(this, connection);
29
36
  }
@@ -53,11 +60,14 @@ class Coordinating {
53
60
  this.#flights = flights;
54
61
  this.#tokens = tokens;
55
62
  }
63
+ get leavesNothingToRecover() {
64
+ return this.#flights.size === 0;
65
+ }
56
66
  async run(party, key) {
57
67
  // A token that goes unused is harmless: tokens only need to grow.
58
68
  const token = await this.#tokens.next(key);
59
69
  // The client may have withdrawn the run while the token was minted.
60
- if (party.role.kind !== 'waiting')
70
+ if (!party.waiting)
61
71
  return;
62
72
  const inProgress = this.#flights.get(key);
63
73
  if (inProgress)
@@ -68,15 +78,15 @@ class Coordinating {
68
78
  }
69
79
  /** A rejoin joins only its own flight; any other answer could be a second run of the work. */
70
80
  async rejoin(party, key, flight) {
71
- if (party.role.kind !== 'waiting')
81
+ if (!party.waiting)
72
82
  return;
73
83
  const inProgress = this.#flights.get(key);
74
84
  if (inProgress?.is(flight))
75
85
  return inProgress.add(party);
76
- party.end({ op: 'interrupted', id: party.id });
86
+ party.end({ op: 'interrupted' });
77
87
  }
78
88
  reassert(party) {
79
- party.end({ op: 'rejected', id: party.id });
89
+ party.end({ op: 'rejected' });
80
90
  }
81
91
  land(flight, outcome) {
82
92
  this.#forget(flight);
@@ -105,6 +115,10 @@ class GraceWindow {
105
115
  this.#over = over;
106
116
  this.#next = next;
107
117
  }
118
+ /** A leader of the previous term may not have reasserted its flight yet, so this coordinator cannot know. */
119
+ get leavesNothingToRecover() {
120
+ return false;
121
+ }
108
122
  /** A fresh run leads or joins only once every leader had its chance to reassert. */
109
123
  async run(party, key) {
110
124
  await this.#over.wait();
@@ -118,16 +132,16 @@ class GraceWindow {
118
132
  async rejoin(party, key, flight) {
119
133
  const over = this.#over.wait().then(() => 'over');
120
134
  for (;;) {
121
- if (party.role.kind !== 'waiting')
135
+ if (!party.waiting)
122
136
  return;
123
137
  const inProgress = this.#flights.get(key);
124
138
  if (inProgress?.is(flight))
125
139
  return inProgress.add(party);
126
140
  const ending = this.#endings.get(key);
127
- if (ending?.token === flight) {
141
+ if (ending?.flight.is(flight)) {
128
142
  return party.end('landed' in ending
129
- ? { op: 'landed', id: party.id, outcome: ending.landed }
130
- : { op: 'interrupted', id: party.id });
143
+ ? { op: 'landed', outcome: ending.landed }
144
+ : { op: 'interrupted' });
131
145
  }
132
146
  const woken = await Promise.race([over, this.#reassertionOf(key)]);
133
147
  if (woken === 'over')
@@ -138,10 +152,10 @@ class GraceWindow {
138
152
  reassert(party, key, token) {
139
153
  const claimed = this.#flights.get(key);
140
154
  if (claimed && !token.isNewerThan(claimed.token)) {
141
- return party.end({ op: 'rejected', id: party.id });
155
+ return party.end({ op: 'rejected' });
142
156
  }
143
157
  if (claimed) {
144
- claimed.leader.end({ op: 'rejected', id: claimed.leader.id });
158
+ claimed.leader.end({ op: 'rejected' });
145
159
  this.interrupt(claimed);
146
160
  }
147
161
  const flight = new Flight(key, token, party);
@@ -153,17 +167,11 @@ class GraceWindow {
153
167
  wake();
154
168
  }
155
169
  land(flight, outcome) {
156
- this.#endings.set(flight.key, {
157
- token: flight.token.toString(),
158
- landed: outcome,
159
- });
170
+ this.#endings.set(flight.key, { flight, landed: outcome });
160
171
  this.#next.land(flight, outcome);
161
172
  }
162
173
  interrupt(flight) {
163
- this.#endings.set(flight.key, {
164
- token: flight.token.toString(),
165
- interrupted: true,
166
- });
174
+ this.#endings.set(flight.key, { flight, interrupted: true });
167
175
  this.#next.interrupt(flight);
168
176
  }
169
177
  #reassertionOf(key) {
@@ -1 +1 @@
1
- {"version":3,"file":"flight-coordinator.js","sourceRoot":"","sources":["../../src/coordinator/flight-coordinator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flight-coordinator.js","sourceRoot":"","sources":["../../src/coordinator/flight-coordinator.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,18 @@
1
- import type { Leadership } from '../election/leadership.ts';
1
+ import type { Term } from '@zukhruf/election';
2
2
  export interface FlightServerOptions {
3
3
  /** Milliseconds the new term starts no flight, so leaders from the last term can reassert theirs. */
4
4
  graceWindow: number;
5
5
  }
6
6
  /**
7
7
  * Serves a `FlightCoordinator` on a Unix socket for one term. Lease tokens
8
- * carry the term's epoch, so they outrank every token of earlier terms.
8
+ * carry the term's epoch, so they outrank every token of earlier terms. A
9
+ * term that is lost may already be another process's, so the server closes
10
+ * itself when the term's signal aborts.
9
11
  */
10
12
  export declare class FlightServer {
11
13
  #private;
12
14
  private constructor();
13
- static start(socketPath: string, leadership: Leadership, { graceWindow }: FlightServerOptions): Promise<FlightServer>;
15
+ static start(socketPath: string, term: Term, { graceWindow }: FlightServerOptions): Promise<FlightServer>;
14
16
  /**
15
17
  * Hands the term over. Ending every connection tells the other processes to
16
18
  * elect a successor and reassert their flights during its grace window;
@@ -18,6 +20,10 @@ export declare class FlightServer {
18
20
  * Each connection ends only after what was written to it, such as an
19
21
  * outcome for a joiner, is sent. The term ends only after the socket is
20
22
  * gone: in the other order, this close could remove a successor's socket file.
23
+ * The term resigns clean when the coordinator leaves nothing to recover, so
24
+ * the successor skips its grace window; a lost term records nothing.
25
+ * The first call removes the file at once, so a second caller finds it gone,
26
+ * and it too resolves only once the term ended as the first call ended it.
21
27
  */
22
28
  close(): Promise<void>;
23
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"flight-server.d.ts","sourceRoot":"","sources":["../../src/coordinator/flight-server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAQ5D,MAAM,WAAW,mBAAmB;IAClC,qGAAqG;IACrG,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,YAAY;;IAKvB,OAAO;WAUM,KAAK,CAChB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,UAAU,EACtB,EAAE,WAAW,EAAE,EAAE,mBAAmB,GACnC,OAAO,CAAC,YAAY,CAAC;IAwCxB;;;;;;;OAOG;IACG,KAAK;CAQZ"}
1
+ {"version":3,"file":"flight-server.d.ts","sourceRoot":"","sources":["../../src/coordinator/flight-server.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAQ9C,MAAM,WAAW,mBAAmB;IAClC,qGAAqG;IACrG,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,YAAY;;IAOvB,OAAO;WAaM,KAAK,CAChB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,IAAI,EACV,EAAE,WAAW,EAAE,EAAE,mBAAmB,GACnC,OAAO,CAAC,YAAY,CAAC;IAkCxB;;;;;;;;;;;OAWG;IACG,KAAK;CAaZ"}
@@ -1,35 +1,37 @@
1
- import { unlink } from 'node:fs/promises';
1
+ import { addAbortListener, once } from 'node:events';
2
+ import { rm } from 'node:fs/promises';
2
3
  import { createServer } from 'node:net';
3
4
  import { EpochTokenSource } from '@zukhruf/fencing';
4
- import { isErrno } from '@zukhruf/fs';
5
5
  import { welcome } from "../connection/handshake.js";
6
6
  import { SocketConnection } from "../connection/socket-connection.js";
7
- import { isRequestEnvelope, } from "../protocol/flight-protocol.js";
8
7
  import { FlightCoordinator } from "./flight-coordinator.js";
9
8
  /**
10
9
  * Serves a `FlightCoordinator` on a Unix socket for one term. Lease tokens
11
- * carry the term's epoch, so they outrank every token of earlier terms.
10
+ * carry the term's epoch, so they outrank every token of earlier terms. A
11
+ * term that is lost may already be another process's, so the server closes
12
+ * itself when the term's signal aborts.
12
13
  */
13
14
  export class FlightServer {
14
15
  #server;
15
16
  #connections;
16
- #leadership;
17
- constructor(server, connections, leadership) {
17
+ #coordinator;
18
+ #term;
19
+ #closeOnLoss;
20
+ constructor(server, connections, coordinator, term) {
18
21
  this.#server = server;
19
22
  this.#connections = connections;
20
- this.#leadership = leadership;
23
+ this.#coordinator = coordinator;
24
+ this.#term = term;
25
+ this.#closeOnLoss = addAbortListener(term.signal, () => void this.close());
21
26
  }
22
- static async start(socketPath, leadership, { graceWindow }) {
27
+ static async start(socketPath, term, { graceWindow }) {
23
28
  // Only the coordinator gets here, so removing a dead coordinator's socket file cannot race another server.
24
29
  // Windows removes a named pipe when its process stops, so there is no file to remove.
25
30
  if (process.platform !== 'win32') {
26
- await unlink(socketPath).catch((error) => {
27
- if (!isErrno(error, 'ENOENT'))
28
- throw error;
29
- });
31
+ await rm(socketPath, { force: true });
30
32
  }
31
33
  const coordinator = new FlightCoordinator({
32
- tokens: new EpochTokenSource(leadership.epoch),
34
+ tokens: new EpochTokenSource(term.epoch),
33
35
  graceWindow,
34
36
  });
35
37
  const connections = new Set();
@@ -42,18 +44,19 @@ export class FlightServer {
42
44
  void welcome(socket).then((speaksOurs) => {
43
45
  if (!speaksOurs)
44
46
  return;
45
- coordinator.serve(new SocketConnection(socket, isRequestEnvelope));
46
- });
47
- });
48
- await new Promise((resolve, reject) => {
49
- server.once('error', reject);
50
- server.listen(socketPath, () => {
51
- server.off('error', reject);
52
- resolve();
47
+ coordinator.serve(new SocketConnection(socket));
53
48
  });
54
49
  });
50
+ server.listen(socketPath);
51
+ await once(server, 'listening');
55
52
  server.unref();
56
- return new FlightServer(server, connections, leadership);
53
+ const started = new FlightServer(server, connections, coordinator, term);
54
+ // The term may have been lost while the server started.
55
+ if (term.signal.aborted) {
56
+ await started.close();
57
+ term.signal.throwIfAborted();
58
+ }
59
+ return started;
57
60
  }
58
61
  /**
59
62
  * Hands the term over. Ending every connection tells the other processes to
@@ -62,13 +65,24 @@ export class FlightServer {
62
65
  * Each connection ends only after what was written to it, such as an
63
66
  * outcome for a joiner, is sent. The term ends only after the socket is
64
67
  * gone: in the other order, this close could remove a successor's socket file.
68
+ * The term resigns clean when the coordinator leaves nothing to recover, so
69
+ * the successor skips its grace window; a lost term records nothing.
70
+ * The first call removes the file at once, so a second caller finds it gone,
71
+ * and it too resolves only once the term ended as the first call ended it.
65
72
  */
66
73
  async close() {
67
- const closed = new Promise((resolve) => this.#server.close(() => resolve()));
74
+ this.#closeOnLoss[Symbol.dispose]();
75
+ const closed = this.#server[Symbol.asyncDispose]();
76
+ // Read in the step that ends the connections: each session that ends
77
+ // withdraws its flights, so later there is never a flight in progress.
78
+ // An answer written after this step, such as the `lead` of a run whose
79
+ // token is being minted, never reaches its client, which sends the run
80
+ // again to the successor. So no client knows of a flight this read missed.
81
+ const clean = this.#coordinator.leavesNothingToRecover;
68
82
  for (const socket of this.#connections)
69
83
  socket.destroySoon();
70
84
  await closed;
71
- await this.#leadership.resign();
85
+ await this.#term.resign({ clean });
72
86
  }
73
87
  }
74
88
  //# sourceMappingURL=flight-server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"flight-server.js","sourceRoot":"","sources":["../../src/coordinator/flight-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flight-server.js","sourceRoot":"","sources":["../../src/coordinator/flight-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAA4B,YAAY,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAO5D;;;;;GAKG;AACH,MAAM,OAAO,YAAY;IACd,OAAO,CAAS;IAChB,YAAY,CAAc;IAC1B,YAAY,CAAoB;IAChC,KAAK,CAAO;IACZ,YAAY,CAAa;IAElC,YACE,MAAc,EACd,WAAwB,EACxB,WAA8B,EAC9B,IAAU;QAEV,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;QAChC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,YAAY,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,KAAK,CAChB,UAAkB,EAClB,IAAU,EACV,EAAE,WAAW,EAAuB;QAEpC,2GAA2G;QAC3G,sFAAsF;QACtF,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,CAAC;QACD,MAAM,WAAW,GAAG,IAAI,iBAAiB,CAAC;YACxC,MAAM,EAAE,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;YACxC,WAAW;SACZ,CAAC,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;QACtC,qHAAqH;QACrH,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;YACrC,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YACvD,kHAAkH;YAClH,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE;gBACvC,IAAI,CAAC,UAAU;oBAAE,OAAO;gBACxB,WAAW,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAiB,MAAM,CAAC,CAAC,CAAC;YAClE,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC1B,MAAM,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QACzE,wDAAwD;QACxD,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACxB,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAC/B,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;QACnD,qEAAqE;QACrE,uEAAuE;QACvE,uEAAuE;QACvE,uEAAuE;QACvE,2EAA2E;QAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,sBAAsB,CAAC;QACvD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,YAAY;YAAE,MAAM,CAAC,WAAW,EAAE,CAAC;QAC7D,MAAM,MAAM,CAAC;QACb,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACrC,CAAC;CACF"}
@@ -8,7 +8,9 @@ export declare class Flight {
8
8
  readonly token: FencingToken;
9
9
  readonly leader: Party;
10
10
  constructor(key: string, token: FencingToken, leader: Party);
11
- /** Whether this is the flight with the token `flight`, which a rejoin names. */
11
+ /** The flight's name on the wire: its token as decimal text, which a joined answer and a rejoin carry. */
12
+ get id(): string;
13
+ /** Whether this is the flight with the id `flight`, which a rejoin names. */
12
14
  is(flight: string): boolean;
13
15
  add(joiner: Party): void;
14
16
  remove(joiner: Party): void;
@@ -1 +1 @@
1
- {"version":3,"file":"flight.d.ts","sourceRoot":"","sources":["../../src/coordinator/flight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,kHAAkH;AAClH,qBAAa,MAAM;;IACjB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;gBAGX,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK;IAM3D,gFAAgF;IAChF,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAI3B,GAAG,CAAC,MAAM,EAAE,KAAK;IAKjB,MAAM,CAAC,MAAM,EAAE,KAAK;IAIpB,yFAAyF;IACzF,IAAI,CAAC,OAAO,EAAE,OAAO;IAQrB,0GAA0G;IAC1G,SAAS;CAMV"}
1
+ {"version":3,"file":"flight.d.ts","sourceRoot":"","sources":["../../src/coordinator/flight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,kHAAkH;AAClH,qBAAa,MAAM;;IACjB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;gBAGX,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK;IAM3D,0GAA0G;IAC1G,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,6EAA6E;IAC7E,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAI3B,GAAG,CAAC,MAAM,EAAE,KAAK;IAKjB,MAAM,CAAC,MAAM,EAAE,KAAK;IAIpB,yFAAyF;IACzF,IAAI,CAAC,OAAO,EAAE,OAAO;IAQrB,0GAA0G;IAC1G,SAAS;CAMV"}
@@ -9,9 +9,13 @@ export class Flight {
9
9
  this.token = token;
10
10
  this.leader = leader;
11
11
  }
12
- /** Whether this is the flight with the token `flight`, which a rejoin names. */
12
+ /** The flight's name on the wire: its token as decimal text, which a joined answer and a rejoin carry. */
13
+ get id() {
14
+ return this.token.toString();
15
+ }
16
+ /** Whether this is the flight with the id `flight`, which a rejoin names. */
13
17
  is(flight) {
14
- return flight === this.token.toString();
18
+ return flight === this.id;
15
19
  }
16
20
  add(joiner) {
17
21
  this.#joiners.add(joiner);
@@ -23,15 +27,15 @@ export class Flight {
23
27
  /** Pushes the outcome to every joiner, then tells the leader that it was handed over. */
24
28
  land(outcome) {
25
29
  for (const joiner of this.#joiners) {
26
- joiner.end({ op: 'landed', id: joiner.id, outcome });
30
+ joiner.end({ op: 'landed', outcome });
27
31
  }
28
32
  this.#joiners.clear();
29
- this.leader.end({ op: 'ack', id: this.leader.id });
33
+ this.leader.end({ op: 'ack' });
30
34
  }
31
35
  /** Ends without an outcome: every joiner learns that its flight is lost, and none runs the work again. */
32
36
  interrupt() {
33
37
  for (const joiner of this.#joiners) {
34
- joiner.end({ op: 'interrupted', id: joiner.id });
38
+ joiner.end({ op: 'interrupted' });
35
39
  }
36
40
  this.#joiners.clear();
37
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"flight.js","sourceRoot":"","sources":["../../src/coordinator/flight.ts"],"names":[],"mappings":"AAKA,kHAAkH;AAClH,MAAM,OAAO,MAAM;IACR,GAAG,CAAS;IACZ,KAAK,CAAe;IACpB,MAAM,CAAQ;IACd,QAAQ,GAAG,IAAI,GAAG,EAAS,CAAC;IAErC,YAAY,GAAW,EAAE,KAAmB,EAAE,MAAa;QACzD,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,gFAAgF;IAChF,EAAE,CAAC,MAAc;QACf,OAAO,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC1C,CAAC;IAED,GAAG,CAAC,MAAa;QACf,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAED,MAAM,CAAC,MAAa;QAClB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED,yFAAyF;IACzF,IAAI,CAAC,OAAgB;QACnB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,0GAA0G;IAC1G,SAAS;QACP,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF"}
1
+ {"version":3,"file":"flight.js","sourceRoot":"","sources":["../../src/coordinator/flight.ts"],"names":[],"mappings":"AAKA,kHAAkH;AAClH,MAAM,OAAO,MAAM;IACR,GAAG,CAAS;IACZ,KAAK,CAAe;IACpB,MAAM,CAAQ;IACd,QAAQ,GAAG,IAAI,GAAG,EAAS,CAAC;IAErC,YAAY,GAAW,EAAE,KAAmB,EAAE,MAAa;QACzD,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,0GAA0G;IAC1G,IAAI,EAAE;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC/B,CAAC;IAED,6EAA6E;IAC7E,EAAE,CAAC,MAAc;QACf,OAAO,MAAM,KAAK,IAAI,CAAC,EAAE,CAAC;IAC5B,CAAC;IAED,GAAG,CAAC,MAAa;QACf,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAED,MAAM,CAAC,MAAa;QAClB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED,yFAAyF;IACzF,IAAI,CAAC,OAAgB;QACnB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,0GAA0G;IAC1G,SAAS;QACP,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF"}
@@ -1,5 +1,11 @@
1
1
  import type { FlightResponse } from '../protocol/flight-protocol.ts';
2
2
  import type { Flight } from './flight.ts';
3
+ /** A response to one party, without the id: the party fills in its own. */
4
+ type Answer = {
5
+ [Op in FlightResponse['op']]: Omit<Extract<FlightResponse, {
6
+ op: Op;
7
+ }>, 'id'>;
8
+ }[FlightResponse['op']];
3
9
  /** Where a party's answers go: the session of the party's connection. */
4
10
  export interface Inbox {
5
11
  reply(response: FlightResponse): void;
@@ -28,11 +34,14 @@ export declare class Party {
28
34
  readonly id: string;
29
35
  constructor(id: string, inbox: Inbox);
30
36
  get role(): Role;
37
+ /** Whether the party still waits: it has no answer yet, it was not withdrawn, and its connection is not gone. */
38
+ get waiting(): boolean;
31
39
  lead(flight: Flight): void;
32
40
  /** A leader from before a failover claimed its flight again; it already knows that it leads. */
33
41
  resume(flight: Flight): void;
34
42
  join(flight: Flight): void;
35
- /** Answers for good with `response`, or ends without an answer when it was withdrawn or its connection is gone. */
36
- end(response?: FlightResponse): void;
43
+ /** Answers for good with `answer`, or ends without an answer when it was withdrawn or its connection is gone. */
44
+ end(answer?: Answer): void;
37
45
  }
46
+ export {};
38
47
  //# sourceMappingURL=party.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"party.d.ts","sourceRoot":"","sources":["../../src/coordinator/party.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,gFAAgF;IAChF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,8CAA8C;AAC9C,MAAM,MAAM,IAAI;AACd,wFAAwF;AACtF;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACrC,+DAA+D;GAC7D;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,sHAAsH;AACtH,qBAAa,KAAK;;IAChB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAIR,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;IAKpC,IAAI,IAAI,IAAI,IAAI,CAEf;IAED,IAAI,CAAC,MAAM,EAAE,MAAM;IASnB,gGAAgG;IAChG,MAAM,CAAC,MAAM,EAAE,MAAM;IAIrB,IAAI,CAAC,MAAM,EAAE,MAAM;IASnB,mHAAmH;IACnH,GAAG,CAAC,QAAQ,CAAC,EAAE,cAAc;CAM9B"}
1
+ {"version":3,"file":"party.d.ts","sourceRoot":"","sources":["../../src/coordinator/party.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,2EAA2E;AAC3E,KAAK,MAAM,GAAG;KACX,EAAE,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE;QAAE,EAAE,EAAE,EAAE,CAAA;KAAE,CAAC,EAAE,IAAI,CAAC;CAC9E,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;AAExB,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,gFAAgF;IAChF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,8CAA8C;AAC9C,MAAM,MAAM,IAAI;AACd,wFAAwF;AACtF;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACrC,+DAA+D;GAC7D;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,sHAAsH;AACtH,qBAAa,KAAK;;IAChB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAIR,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;IAKpC,IAAI,IAAI,IAAI,IAAI,CAEf;IAED,iHAAiH;IACjH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,CAAC,MAAM,EAAE,MAAM;IAKnB,gGAAgG;IAChG,MAAM,CAAC,MAAM,EAAE,MAAM;IAIrB,IAAI,CAAC,MAAM,EAAE,MAAM;IAKnB,iHAAiH;IACjH,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM;CAWpB"}
@@ -10,13 +10,13 @@ export class Party {
10
10
  get role() {
11
11
  return this.#role;
12
12
  }
13
+ /** Whether the party still waits: it has no answer yet, it was not withdrawn, and its connection is not gone. */
14
+ get waiting() {
15
+ return this.#role.kind === 'waiting';
16
+ }
13
17
  lead(flight) {
14
18
  this.#role = { kind: 'leading', flight };
15
- this.#inbox.reply({
16
- op: 'lead',
17
- id: this.id,
18
- token: flight.token.toString(),
19
- });
19
+ this.#reply({ op: 'lead', token: flight.token.toString() });
20
20
  }
21
21
  /** A leader from before a failover claimed its flight again; it already knows that it leads. */
22
22
  resume(flight) {
@@ -24,20 +24,20 @@ export class Party {
24
24
  }
25
25
  join(flight) {
26
26
  this.#role = { kind: 'joining', flight };
27
- this.#inbox.reply({
28
- op: 'joined',
29
- id: this.id,
30
- flight: flight.token.toString(),
31
- });
32
- }
33
- /** Answers for good with `response`, or ends without an answer when it was withdrawn or its connection is gone. */
34
- end(response) {
27
+ this.#reply({ op: 'joined', flight: flight.id });
28
+ }
29
+ /** Answers for good with `answer`, or ends without an answer when it was withdrawn or its connection is gone. */
30
+ end(answer) {
35
31
  if (this.#role.kind === 'gone')
36
32
  return;
37
33
  this.#role = { kind: 'gone' };
38
34
  this.#inbox.forget(this.id);
39
- if (response)
40
- this.#inbox.reply(response);
35
+ if (answer)
36
+ this.#reply(answer);
37
+ }
38
+ #reply(answer) {
39
+ // The id goes right after the op, so each line keeps its bytes: op, id, then the fields of the op.
40
+ this.#inbox.reply(Object.assign({ op: answer.op, id: this.id }, answer));
41
41
  }
42
42
  }
43
43
  //# sourceMappingURL=party.js.map