@hotmeshio/long-tail 0.25.4 → 0.27.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.
- package/README.md +1 -0
- package/build/index.d.ts +5 -0
- package/build/index.js +36 -7
- package/build/lib/events/nats.d.ts +11 -0
- package/build/lib/events/nats.js +33 -2
- package/build/lib/machines/index.d.ts +6 -0
- package/build/lib/machines/index.js +22 -0
- package/build/lib/machines/line.d.ts +44 -0
- package/build/lib/machines/line.js +114 -0
- package/build/lib/machines/protocol/codec.d.ts +108 -0
- package/build/lib/machines/protocol/codec.js +80 -0
- package/build/lib/machines/protocol/constants.d.ts +120 -0
- package/build/lib/machines/protocol/constants.js +97 -0
- package/build/lib/machines/protocol/contract.d.ts +19 -0
- package/build/lib/machines/protocol/contract.js +141 -0
- package/build/lib/machines/protocol/envelope.d.ts +14 -0
- package/build/lib/machines/protocol/envelope.js +43 -0
- package/build/lib/machines/protocol/index.d.ts +5 -0
- package/build/lib/machines/protocol/index.js +21 -0
- package/build/lib/machines/protocol/topics.d.ts +33 -0
- package/build/lib/machines/protocol/topics.js +79 -0
- package/build/lib/machines/site/edge-account.d.ts +11 -0
- package/build/lib/machines/site/edge-account.js +24 -0
- package/build/lib/machines/site/mosquitto-conf.d.ts +27 -0
- package/build/lib/machines/site/mosquitto-conf.js +81 -0
- package/build/lib/machines/site/site-conf.d.ts +23 -0
- package/build/lib/machines/site/site-conf.js +62 -0
- package/build/lib/machines/transport/callback-feed.d.ts +18 -0
- package/build/lib/machines/transport/callback-feed.js +31 -0
- package/build/lib/machines/transport/delivery.d.ts +20 -0
- package/build/lib/machines/transport/delivery.js +37 -0
- package/build/lib/machines/transport/index.d.ts +13 -0
- package/build/lib/machines/transport/index.js +29 -0
- package/build/lib/machines/transport/memory-broker.d.ts +35 -0
- package/build/lib/machines/transport/memory-broker.js +145 -0
- package/build/lib/machines/transport/memory.d.ts +40 -0
- package/build/lib/machines/transport/memory.js +79 -0
- package/build/lib/machines/transport/mqtt-bus.d.ts +24 -0
- package/build/lib/machines/transport/mqtt-bus.js +43 -0
- package/build/lib/machines/transport/mqtt-link.d.ts +16 -0
- package/build/lib/machines/transport/mqtt-link.js +37 -0
- package/build/lib/machines/transport/mqtt-session.d.ts +34 -0
- package/build/lib/machines/transport/mqtt-session.js +127 -0
- package/build/lib/machines/transport/nats-bus.d.ts +36 -0
- package/build/lib/machines/transport/nats-bus.js +47 -0
- package/build/lib/machines/transport/nats-feed.d.ts +26 -0
- package/build/lib/machines/transport/nats-feed.js +49 -0
- package/build/lib/machines/transport/pubsub-feed.d.ts +17 -0
- package/build/lib/machines/transport/pubsub-feed.js +39 -0
- package/build/lib/machines/transport/serial.d.ts +21 -0
- package/build/lib/machines/transport/serial.js +54 -0
- package/build/lib/machines/transport/stream-bus.d.ts +40 -0
- package/build/lib/machines/transport/stream-bus.js +57 -0
- package/build/lib/machines/transport/streams.d.ts +24 -0
- package/build/lib/machines/transport/streams.js +49 -0
- package/build/lib/messaging/config.d.ts +25 -0
- package/build/lib/messaging/config.js +55 -0
- package/build/lib/messaging/constants.d.ts +26 -0
- package/build/lib/messaging/constants.js +26 -0
- package/build/lib/messaging/index.d.ts +24 -0
- package/build/lib/messaging/index.js +65 -0
- package/build/lib/messaging/memory.d.ts +16 -0
- package/build/lib/messaging/memory.js +147 -0
- package/build/lib/messaging/nats/permissions.d.ts +13 -0
- package/build/lib/messaging/nats/permissions.js +22 -0
- package/build/lib/messaging/nats/provider.d.ts +30 -0
- package/build/lib/messaging/nats/provider.js +77 -0
- package/build/lib/messaging/nats/render-conf.d.ts +68 -0
- package/build/lib/messaging/nats/render-conf.js +58 -0
- package/build/lib/messaging/nats/streams.d.ts +26 -0
- package/build/lib/messaging/nats/streams.js +97 -0
- package/build/lib/messaging/subjects.d.ts +8 -0
- package/build/lib/messaging/subjects.js +25 -0
- package/build/scripts/bambu-boat.d.ts +17 -0
- package/build/scripts/bambu-boat.js +134 -0
- package/build/scripts/bambu-print.d.ts +18 -0
- package/build/scripts/bambu-print.js +114 -0
- package/build/scripts/machine-line.d.ts +33 -0
- package/build/scripts/machine-line.js +301 -0
- package/build/services/machines/commands.d.ts +29 -0
- package/build/services/machines/commands.js +37 -0
- package/build/services/machines/config.d.ts +49 -0
- package/build/services/machines/config.js +94 -0
- package/build/services/machines/contracts.d.ts +210 -0
- package/build/services/machines/contracts.js +124 -0
- package/build/services/machines/forms.d.ts +55 -0
- package/build/services/machines/forms.js +37 -0
- package/build/services/machines/gateway/actions.d.ts +111 -0
- package/build/services/machines/gateway/actions.js +8 -0
- package/build/services/machines/gateway/address-book.d.ts +16 -0
- package/build/services/machines/gateway/address-book.js +24 -0
- package/build/services/machines/gateway/admission.d.ts +35 -0
- package/build/services/machines/gateway/admission.js +66 -0
- package/build/services/machines/gateway/api-escalations.d.ts +23 -0
- package/build/services/machines/gateway/api-escalations.js +101 -0
- package/build/services/machines/gateway/dispatcher.d.ts +76 -0
- package/build/services/machines/gateway/dispatcher.js +197 -0
- package/build/services/machines/gateway/escalations.d.ts +51 -0
- package/build/services/machines/gateway/escalations.js +40 -0
- package/build/services/machines/gateway/executor.d.ts +60 -0
- package/build/services/machines/gateway/executor.js +221 -0
- package/build/services/machines/gateway/gateway.d.ts +88 -0
- package/build/services/machines/gateway/gateway.js +270 -0
- package/build/services/machines/gateway/index.d.ts +10 -0
- package/build/services/machines/gateway/index.js +26 -0
- package/build/services/machines/gateway/jobs.d.ts +16 -0
- package/build/services/machines/gateway/jobs.js +24 -0
- package/build/services/machines/gateway/lifecycle.d.ts +21 -0
- package/build/services/machines/gateway/lifecycle.js +89 -0
- package/build/services/machines/gateway/log.d.ts +41 -0
- package/build/services/machines/gateway/log.js +42 -0
- package/build/services/machines/gateway/lt-http.d.ts +51 -0
- package/build/services/machines/gateway/lt-http.js +120 -0
- package/build/services/machines/gateway/pacing.d.ts +43 -0
- package/build/services/machines/gateway/pacing.js +158 -0
- package/build/services/machines/gateway/plan-escalation.d.ts +19 -0
- package/build/services/machines/gateway/plan-escalation.js +210 -0
- package/build/services/machines/gateway/plan-machine.d.ts +17 -0
- package/build/services/machines/gateway/plan-machine.js +283 -0
- package/build/services/machines/gateway/plan-shared.d.ts +18 -0
- package/build/services/machines/gateway/plan-shared.js +36 -0
- package/build/services/machines/gateway/provision.d.ts +22 -0
- package/build/services/machines/gateway/provision.js +79 -0
- package/build/services/machines/gateway/receipts.d.ts +33 -0
- package/build/services/machines/gateway/receipts.js +65 -0
- package/build/services/machines/gateway/reconcile.d.ts +35 -0
- package/build/services/machines/gateway/reconcile.js +99 -0
- package/build/services/machines/gateway/registration.d.ts +38 -0
- package/build/services/machines/gateway/registration.js +121 -0
- package/build/services/machines/gateway/reply-waiter.d.ts +24 -0
- package/build/services/machines/gateway/reply-waiter.js +65 -0
- package/build/services/machines/gateway/state.d.ts +145 -0
- package/build/services/machines/gateway/state.js +241 -0
- package/build/services/machines/gateway/trace.d.ts +16 -0
- package/build/services/machines/gateway/trace.js +43 -0
- package/build/services/machines/identities.d.ts +25 -0
- package/build/services/machines/identities.js +77 -0
- package/build/services/machines/index.d.ts +11 -0
- package/build/services/machines/index.js +27 -0
- package/build/services/machines/leadership.d.ts +35 -0
- package/build/services/machines/leadership.js +100 -0
- package/build/services/machines/roles.d.ts +13 -0
- package/build/services/machines/roles.js +62 -0
- package/build/services/machines/rows.d.ts +30 -0
- package/build/services/machines/rows.js +71 -0
- package/build/services/machines/select.d.ts +25 -0
- package/build/services/machines/select.js +76 -0
- package/build/services/machines/surface.d.ts +33 -0
- package/build/services/machines/surface.js +104 -0
- package/build/services/machines/testing/fake-long-tail.d.ts +79 -0
- package/build/services/machines/testing/fake-long-tail.js +158 -0
- package/build/services/machines/testing/index.d.ts +3 -0
- package/build/services/machines/testing/index.js +19 -0
- package/build/services/machines/testing/recording-log.d.ts +14 -0
- package/build/services/machines/testing/recording-log.js +22 -0
- package/build/services/machines/testing/wait.d.ts +3 -0
- package/build/services/machines/testing/wait.js +17 -0
- package/build/services/mcp/db-server/schemas.d.ts +2 -2
- package/build/start/adapters.js +11 -2
- package/build/start/index.js +20 -1
- package/build/system/mcp-servers/admin/schemas.d.ts +221 -221
- package/build/system/mcp-servers/db-query/schemas.d.ts +2 -2
- package/build/system/mcp-servers/human-queue-schemas.d.ts +6 -6
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/index.d.ts +3 -1
- package/build/types/machines.d.ts +167 -0
- package/build/types/machines.js +7 -0
- package/build/types/messaging.d.ts +72 -0
- package/build/types/messaging.js +11 -0
- package/build/types/startup.d.ts +49 -1
- package/docs/events.md +50 -4
- package/docs/machines.md +322 -0
- package/package.json +13 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the gateway knows: the open handoff rows (from escalation events, so a
|
|
3
|
+
* replay rebuilds it), recently closed ones and how they ended (so replayed
|
|
4
|
+
* machine events are recognized as done), open alarm rows, and each
|
|
5
|
+
* machine's link: presence, last message, software and boot id.
|
|
6
|
+
*/
|
|
7
|
+
import type { HandoffFacets, TraceEntry } from '../contracts';
|
|
8
|
+
/**
|
|
9
|
+
* WAITING: the machine has no room yet (pacing.ts); the row waits in its role queue.
|
|
10
|
+
* ARRIVING: the job goes out once its units arrive at the receiver (codec `dispatchOn: arrival`).
|
|
11
|
+
*/
|
|
12
|
+
export declare const DISPATCH_STATE: {
|
|
13
|
+
readonly NONE: "none";
|
|
14
|
+
readonly WAITING: "waiting";
|
|
15
|
+
readonly ARRIVING: "arriving";
|
|
16
|
+
readonly PENDING: "pending";
|
|
17
|
+
readonly ACCEPTED: "accepted";
|
|
18
|
+
readonly REFUSED: "refused";
|
|
19
|
+
};
|
|
20
|
+
export type DispatchState = typeof DISPATCH_STATE[keyof typeof DISPATCH_STATE];
|
|
21
|
+
export declare const ROW_END: {
|
|
22
|
+
readonly RESOLVED: "resolved";
|
|
23
|
+
readonly CANCELLED: "cancelled";
|
|
24
|
+
readonly EXPIRED: "expired";
|
|
25
|
+
};
|
|
26
|
+
export type RowEnd = typeof ROW_END[keyof typeof ROW_END];
|
|
27
|
+
export interface HandoffRow extends HandoffFacets {
|
|
28
|
+
id: string;
|
|
29
|
+
role: string;
|
|
30
|
+
dispatch: DispatchState;
|
|
31
|
+
ended?: RowEnd;
|
|
32
|
+
/** Finished-unit records already on the row's metadata (trace.ts). */
|
|
33
|
+
trace?: TraceEntry[];
|
|
34
|
+
/** Each unit's arrival event, as its receiver reported it (dispatch on arrival). */
|
|
35
|
+
arrivals?: Record<string, Record<string, any>>;
|
|
36
|
+
/** Lifecycle item keys already on the row (lifecycle.ts). */
|
|
37
|
+
noted?: string[];
|
|
38
|
+
/** Parked with a limit on its items, which lifecycle notes must never use up. */
|
|
39
|
+
capped?: boolean;
|
|
40
|
+
/** When the row was created (epoch ms). */
|
|
41
|
+
createdAt?: number;
|
|
42
|
+
/** When its last unit arrived (epoch ms, dispatch on arrival); its dispatch windows count from here, else from createdAt. */
|
|
43
|
+
dueAt?: number;
|
|
44
|
+
}
|
|
45
|
+
export interface AlarmTrack {
|
|
46
|
+
machineId: string;
|
|
47
|
+
alarmId: string;
|
|
48
|
+
/** When the machine raised it; a reused alarm id with a new time is a new alarm. */
|
|
49
|
+
raisedAt?: string;
|
|
50
|
+
rowId?: string;
|
|
51
|
+
/** Created and not yet resolved by anyone. */
|
|
52
|
+
open: boolean;
|
|
53
|
+
/** Rows the alarm paused, noted again when it clears. */
|
|
54
|
+
noted?: string[];
|
|
55
|
+
}
|
|
56
|
+
export interface LinkTrack {
|
|
57
|
+
online: boolean;
|
|
58
|
+
since: number;
|
|
59
|
+
lastSeen: number;
|
|
60
|
+
rowId?: string;
|
|
61
|
+
quietRowId?: string;
|
|
62
|
+
watching?: boolean;
|
|
63
|
+
software?: string;
|
|
64
|
+
bootId?: string;
|
|
65
|
+
}
|
|
66
|
+
export declare class GatewayState {
|
|
67
|
+
/** Units one dispatch may carry, from the codec; null when any number may. */
|
|
68
|
+
unitsPerDispatch: number | null;
|
|
69
|
+
/** Jobs go to a receiver once its units arrive there, from the codec. */
|
|
70
|
+
dispatchOnArrival: boolean;
|
|
71
|
+
/** Note each job's start, progress steps and pauses on the row that holds it (lifecycle.ts). */
|
|
72
|
+
lifecycle: boolean;
|
|
73
|
+
/** Active alarms are retained on the broker; otherwise snapshots list them (codec.ts retainedAlarms). */
|
|
74
|
+
retainedAlarms: boolean;
|
|
75
|
+
/** Reading the broker's retained messages on taking the line; a snapshot read back may be older than its alarms. */
|
|
76
|
+
readingRetained: boolean;
|
|
77
|
+
/** When this gateway took the line (epoch ms); a command for an older row may have been sent by an earlier one. */
|
|
78
|
+
takenAt: number;
|
|
79
|
+
/** With lifecycle notes, progress is noted each time it crosses this many percent; 0 notes none. */
|
|
80
|
+
progressStep: number;
|
|
81
|
+
/** While reconciling, new handoffs wait so pacing sees the whole line before anything is sent. */
|
|
82
|
+
holdDispatch: boolean;
|
|
83
|
+
/** Units at each machine that arrived and have not left: machine → unit → job. */
|
|
84
|
+
private held;
|
|
85
|
+
private openRows;
|
|
86
|
+
private closedRows;
|
|
87
|
+
readonly alarms: Map<string, AlarmTrack>;
|
|
88
|
+
/** Finished records that came before their row: machine/unit → job body. */
|
|
89
|
+
private earlyFinishes;
|
|
90
|
+
private openCommands;
|
|
91
|
+
/** Accepted dispatch command ids → their row, so a later failure finds the row. */
|
|
92
|
+
private dispatches;
|
|
93
|
+
private endedCommands;
|
|
94
|
+
readonly links: Map<string, LinkTrack>;
|
|
95
|
+
/** Messages already shown to a person as contract violations. */
|
|
96
|
+
private violations;
|
|
97
|
+
/** Learned addresses, both ways: station → address and address → station. */
|
|
98
|
+
private addressOfStation;
|
|
99
|
+
private stationOfAddress;
|
|
100
|
+
/** Contested claims already raised as registration rows, `${address}>${station}`. */
|
|
101
|
+
private claims;
|
|
102
|
+
/** Contested claims heard from the machines themselves, `${address}>${station}`. */
|
|
103
|
+
private heard;
|
|
104
|
+
/** Addresses heard that claim no station, logged once each. */
|
|
105
|
+
private strangers;
|
|
106
|
+
index(row: HandoffRow): void;
|
|
107
|
+
get(id: string): HandoffRow | undefined;
|
|
108
|
+
close(id: string, ended?: RowEnd): HandoffRow | undefined;
|
|
109
|
+
isClosed(id: string): boolean;
|
|
110
|
+
open(): HandoffRow[];
|
|
111
|
+
/** The open handoff carrying this unit that `pred` picks (latest attempt first). */
|
|
112
|
+
openFor(unitId: string, pred: (r: HandoffRow) => boolean): HandoffRow | undefined;
|
|
113
|
+
closedFor(unitId: string, pred: (r: HandoffRow) => boolean): HandoffRow | undefined;
|
|
114
|
+
hold(machineId: string, unitId: string, jobId: string): void;
|
|
115
|
+
letGo(machineId: string, unitId: string): void;
|
|
116
|
+
/** The job a unit arrived under at `machineId`, while it is still there. */
|
|
117
|
+
heldJob(machineId: string, unitId: string): string | undefined;
|
|
118
|
+
heldAt(machineId: string): string[];
|
|
119
|
+
/** Keep only units whose job the machine still lists (its snapshot). */
|
|
120
|
+
keepHeld(machineId: string, jobIds: Set<string>): void;
|
|
121
|
+
/** Keep a sender's finished record until its row is known, so an arrival cannot close the row without it. */
|
|
122
|
+
rememberFinish(machineId: string, unitId: string, body: Record<string, any>): void;
|
|
123
|
+
takeFinish(machineId: string, unitId: string): Record<string, any> | undefined;
|
|
124
|
+
rememberDispatch(cmdId: string, rowId: string): void;
|
|
125
|
+
/** The row a dispatch command belongs to, open or recently closed. */
|
|
126
|
+
rowForDispatch(cmdId: string): HandoffRow | undefined;
|
|
127
|
+
/** Track a machine-command row; false when it is already tracked or has ended. */
|
|
128
|
+
trackCommand(id: string): boolean;
|
|
129
|
+
endCommand(id: string): void;
|
|
130
|
+
/** Bind a station to an address, replacing whatever either was bound to before. */
|
|
131
|
+
bindAddress(machineId: string, address: string): void;
|
|
132
|
+
learnedAddress(machineId: string): string | undefined;
|
|
133
|
+
stationAt(address: string): string | undefined;
|
|
134
|
+
claimRaised(address: string, machineId: string): boolean;
|
|
135
|
+
raiseClaim(address: string, machineId: string): void;
|
|
136
|
+
settleClaim(address: string, machineId: string): void;
|
|
137
|
+
hearClaim(address: string, machineId: string): void;
|
|
138
|
+
claimHeard(address: string, machineId: string): boolean;
|
|
139
|
+
/** True the first time an address that claims no station is heard. */
|
|
140
|
+
firstHeardStranger(address: string): boolean;
|
|
141
|
+
violationRaised(key: string): boolean;
|
|
142
|
+
raiseViolation(key: string): void;
|
|
143
|
+
link(machineId: string, now?: number): LinkTrack;
|
|
144
|
+
alarmKey(machineId: string, alarmId: string): string;
|
|
145
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* What the gateway knows: the open handoff rows (from escalation events, so a
|
|
4
|
+
* replay rebuilds it), recently closed ones and how they ended (so replayed
|
|
5
|
+
* machine events are recognized as done), open alarm rows, and each
|
|
6
|
+
* machine's link: presence, last message, software and boot id.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.GatewayState = exports.ROW_END = exports.DISPATCH_STATE = void 0;
|
|
10
|
+
const trace_1 = require("./trace");
|
|
11
|
+
/**
|
|
12
|
+
* WAITING: the machine has no room yet (pacing.ts); the row waits in its role queue.
|
|
13
|
+
* ARRIVING: the job goes out once its units arrive at the receiver (codec `dispatchOn: arrival`).
|
|
14
|
+
*/
|
|
15
|
+
exports.DISPATCH_STATE = {
|
|
16
|
+
NONE: 'none', WAITING: 'waiting', ARRIVING: 'arriving', PENDING: 'pending', ACCEPTED: 'accepted', REFUSED: 'refused',
|
|
17
|
+
};
|
|
18
|
+
exports.ROW_END = { RESOLVED: 'resolved', CANCELLED: 'cancelled', EXPIRED: 'expired' };
|
|
19
|
+
const CLOSED_LIMIT = 2_000;
|
|
20
|
+
class GatewayState {
|
|
21
|
+
constructor() {
|
|
22
|
+
/** Units one dispatch may carry, from the codec; null when any number may. */
|
|
23
|
+
this.unitsPerDispatch = null;
|
|
24
|
+
/** Jobs go to a receiver once its units arrive there, from the codec. */
|
|
25
|
+
this.dispatchOnArrival = false;
|
|
26
|
+
/** Note each job's start, progress steps and pauses on the row that holds it (lifecycle.ts). */
|
|
27
|
+
this.lifecycle = false;
|
|
28
|
+
/** Active alarms are retained on the broker; otherwise snapshots list them (codec.ts retainedAlarms). */
|
|
29
|
+
this.retainedAlarms = false;
|
|
30
|
+
/** Reading the broker's retained messages on taking the line; a snapshot read back may be older than its alarms. */
|
|
31
|
+
this.readingRetained = false;
|
|
32
|
+
/** When this gateway took the line (epoch ms); a command for an older row may have been sent by an earlier one. */
|
|
33
|
+
this.takenAt = 0;
|
|
34
|
+
/** With lifecycle notes, progress is noted each time it crosses this many percent; 0 notes none. */
|
|
35
|
+
this.progressStep = 25;
|
|
36
|
+
/** While reconciling, new handoffs wait so pacing sees the whole line before anything is sent. */
|
|
37
|
+
this.holdDispatch = false;
|
|
38
|
+
/** Units at each machine that arrived and have not left: machine → unit → job. */
|
|
39
|
+
this.held = new Map();
|
|
40
|
+
this.openRows = new Map();
|
|
41
|
+
this.closedRows = new Map();
|
|
42
|
+
this.alarms = new Map();
|
|
43
|
+
/** Finished records that came before their row: machine/unit → job body. */
|
|
44
|
+
this.earlyFinishes = new Map();
|
|
45
|
+
this.openCommands = new Set();
|
|
46
|
+
/** Accepted dispatch command ids → their row, so a later failure finds the row. */
|
|
47
|
+
this.dispatches = new Map();
|
|
48
|
+
this.endedCommands = new Set();
|
|
49
|
+
this.links = new Map();
|
|
50
|
+
/** Messages already shown to a person as contract violations. */
|
|
51
|
+
this.violations = new Set();
|
|
52
|
+
/** Learned addresses, both ways: station → address and address → station. */
|
|
53
|
+
this.addressOfStation = new Map();
|
|
54
|
+
this.stationOfAddress = new Map();
|
|
55
|
+
/** Contested claims already raised as registration rows, `${address}>${station}`. */
|
|
56
|
+
this.claims = new Set();
|
|
57
|
+
/** Contested claims heard from the machines themselves, `${address}>${station}`. */
|
|
58
|
+
this.heard = new Set();
|
|
59
|
+
/** Addresses heard that claim no station, logged once each. */
|
|
60
|
+
this.strangers = new Set();
|
|
61
|
+
}
|
|
62
|
+
index(row) {
|
|
63
|
+
if (this.closedRows.has(row.id))
|
|
64
|
+
return;
|
|
65
|
+
const existing = this.openRows.get(row.id);
|
|
66
|
+
// An event may carry metadata older than what the gateway already wrote: keep both traces.
|
|
67
|
+
this.openRows.set(row.id, existing
|
|
68
|
+
? {
|
|
69
|
+
...row, dispatch: existing.dispatch, trace: (0, trace_1.mergeTrace)(row.trace, existing.trace), arrivals: existing.arrivals,
|
|
70
|
+
dueAt: existing.dueAt, noted: existing.noted ?? row.noted,
|
|
71
|
+
}
|
|
72
|
+
: row);
|
|
73
|
+
}
|
|
74
|
+
get(id) {
|
|
75
|
+
return this.openRows.get(id);
|
|
76
|
+
}
|
|
77
|
+
close(id, ended = exports.ROW_END.RESOLVED) {
|
|
78
|
+
const row = this.openRows.get(id);
|
|
79
|
+
if (!row)
|
|
80
|
+
return undefined;
|
|
81
|
+
this.openRows.delete(id);
|
|
82
|
+
row.ended = ended;
|
|
83
|
+
this.closedRows.set(id, row);
|
|
84
|
+
if (this.closedRows.size > CLOSED_LIMIT) {
|
|
85
|
+
const oldest = this.closedRows.keys().next().value;
|
|
86
|
+
if (oldest)
|
|
87
|
+
this.closedRows.delete(oldest);
|
|
88
|
+
}
|
|
89
|
+
return row;
|
|
90
|
+
}
|
|
91
|
+
isClosed(id) {
|
|
92
|
+
return this.closedRows.has(id);
|
|
93
|
+
}
|
|
94
|
+
open() {
|
|
95
|
+
return [...this.openRows.values()];
|
|
96
|
+
}
|
|
97
|
+
/** The open handoff carrying this unit that `pred` picks (latest attempt first). */
|
|
98
|
+
openFor(unitId, pred) {
|
|
99
|
+
return this.open().filter((r) => carries(r, unitId) && pred(r)).sort((a, b) => b.attempt - a.attempt)[0];
|
|
100
|
+
}
|
|
101
|
+
closedFor(unitId, pred) {
|
|
102
|
+
return [...this.closedRows.values()].find((r) => carries(r, unitId) && pred(r));
|
|
103
|
+
}
|
|
104
|
+
hold(machineId, unitId, jobId) {
|
|
105
|
+
if (!this.held.has(machineId))
|
|
106
|
+
this.held.set(machineId, new Map());
|
|
107
|
+
this.held.get(machineId).set(unitId, jobId);
|
|
108
|
+
}
|
|
109
|
+
letGo(machineId, unitId) {
|
|
110
|
+
this.held.get(machineId)?.delete(unitId);
|
|
111
|
+
}
|
|
112
|
+
/** The job a unit arrived under at `machineId`, while it is still there. */
|
|
113
|
+
heldJob(machineId, unitId) {
|
|
114
|
+
return this.held.get(machineId)?.get(unitId);
|
|
115
|
+
}
|
|
116
|
+
heldAt(machineId) {
|
|
117
|
+
return [...(this.held.get(machineId)?.values() ?? [])];
|
|
118
|
+
}
|
|
119
|
+
/** Keep only units whose job the machine still lists (its snapshot). */
|
|
120
|
+
keepHeld(machineId, jobIds) {
|
|
121
|
+
const units = this.held.get(machineId);
|
|
122
|
+
if (!units)
|
|
123
|
+
return;
|
|
124
|
+
for (const [unitId, jobId] of units)
|
|
125
|
+
if (!jobIds.has(jobId))
|
|
126
|
+
units.delete(unitId);
|
|
127
|
+
}
|
|
128
|
+
/** Keep a sender's finished record until its row is known, so an arrival cannot close the row without it. */
|
|
129
|
+
rememberFinish(machineId, unitId, body) {
|
|
130
|
+
this.earlyFinishes.delete(`${machineId}/${unitId}`);
|
|
131
|
+
this.earlyFinishes.set(`${machineId}/${unitId}`, body);
|
|
132
|
+
if (this.earlyFinishes.size > CLOSED_LIMIT) {
|
|
133
|
+
const oldest = this.earlyFinishes.keys().next().value;
|
|
134
|
+
if (oldest)
|
|
135
|
+
this.earlyFinishes.delete(oldest);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
takeFinish(machineId, unitId) {
|
|
139
|
+
const body = this.earlyFinishes.get(`${machineId}/${unitId}`);
|
|
140
|
+
this.earlyFinishes.delete(`${machineId}/${unitId}`);
|
|
141
|
+
return body;
|
|
142
|
+
}
|
|
143
|
+
rememberDispatch(cmdId, rowId) {
|
|
144
|
+
this.dispatches.set(cmdId, rowId);
|
|
145
|
+
if (this.dispatches.size > CLOSED_LIMIT) {
|
|
146
|
+
const oldest = this.dispatches.keys().next().value;
|
|
147
|
+
if (oldest)
|
|
148
|
+
this.dispatches.delete(oldest);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** The row a dispatch command belongs to, open or recently closed. */
|
|
152
|
+
rowForDispatch(cmdId) {
|
|
153
|
+
const rowId = this.dispatches.get(cmdId);
|
|
154
|
+
return rowId ? this.openRows.get(rowId) ?? this.closedRows.get(rowId) : undefined;
|
|
155
|
+
}
|
|
156
|
+
/** Track a machine-command row; false when it is already tracked or has ended. */
|
|
157
|
+
trackCommand(id) {
|
|
158
|
+
if (this.openCommands.has(id) || this.endedCommands.has(id))
|
|
159
|
+
return false;
|
|
160
|
+
this.openCommands.add(id);
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
endCommand(id) {
|
|
164
|
+
this.openCommands.delete(id);
|
|
165
|
+
this.endedCommands.add(id);
|
|
166
|
+
if (this.endedCommands.size > CLOSED_LIMIT) {
|
|
167
|
+
const oldest = this.endedCommands.values().next().value;
|
|
168
|
+
if (oldest)
|
|
169
|
+
this.endedCommands.delete(oldest);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/** Bind a station to an address, replacing whatever either was bound to before. */
|
|
173
|
+
bindAddress(machineId, address) {
|
|
174
|
+
const before = this.addressOfStation.get(machineId);
|
|
175
|
+
if (before)
|
|
176
|
+
this.stationOfAddress.delete(before);
|
|
177
|
+
const other = this.stationOfAddress.get(address);
|
|
178
|
+
if (other)
|
|
179
|
+
this.addressOfStation.delete(other);
|
|
180
|
+
this.addressOfStation.set(machineId, address);
|
|
181
|
+
this.stationOfAddress.set(address, machineId);
|
|
182
|
+
}
|
|
183
|
+
learnedAddress(machineId) {
|
|
184
|
+
return this.addressOfStation.get(machineId);
|
|
185
|
+
}
|
|
186
|
+
stationAt(address) {
|
|
187
|
+
return this.stationOfAddress.get(address);
|
|
188
|
+
}
|
|
189
|
+
claimRaised(address, machineId) {
|
|
190
|
+
return this.claims.has(`${address}>${machineId}`);
|
|
191
|
+
}
|
|
192
|
+
raiseClaim(address, machineId) {
|
|
193
|
+
this.claims.add(`${address}>${machineId}`);
|
|
194
|
+
}
|
|
195
|
+
settleClaim(address, machineId) {
|
|
196
|
+
this.claims.delete(`${address}>${machineId}`);
|
|
197
|
+
}
|
|
198
|
+
hearClaim(address, machineId) {
|
|
199
|
+
this.heard.add(`${address}>${machineId}`);
|
|
200
|
+
if (this.heard.size > CLOSED_LIMIT)
|
|
201
|
+
this.heard.delete(this.heard.values().next().value);
|
|
202
|
+
}
|
|
203
|
+
claimHeard(address, machineId) {
|
|
204
|
+
return this.heard.has(`${address}>${machineId}`);
|
|
205
|
+
}
|
|
206
|
+
/** True the first time an address that claims no station is heard. */
|
|
207
|
+
firstHeardStranger(address) {
|
|
208
|
+
if (this.strangers.has(address))
|
|
209
|
+
return false;
|
|
210
|
+
this.strangers.add(address);
|
|
211
|
+
if (this.strangers.size > CLOSED_LIMIT)
|
|
212
|
+
this.strangers.delete(this.strangers.values().next().value);
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
violationRaised(key) {
|
|
216
|
+
return this.violations.has(key);
|
|
217
|
+
}
|
|
218
|
+
raiseViolation(key) {
|
|
219
|
+
this.violations.add(key);
|
|
220
|
+
if (this.violations.size > CLOSED_LIMIT) {
|
|
221
|
+
const oldest = this.violations.values().next().value;
|
|
222
|
+
if (oldest)
|
|
223
|
+
this.violations.delete(oldest);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
link(machineId, now = Date.now()) {
|
|
227
|
+
let link = this.links.get(machineId);
|
|
228
|
+
if (!link) {
|
|
229
|
+
link = { online: true, since: now, lastSeen: now };
|
|
230
|
+
this.links.set(machineId, link);
|
|
231
|
+
}
|
|
232
|
+
return link;
|
|
233
|
+
}
|
|
234
|
+
alarmKey(machineId, alarmId) {
|
|
235
|
+
return `${machineId}/${alarmId}`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
exports.GatewayState = GatewayState;
|
|
239
|
+
function carries(row, unitId) {
|
|
240
|
+
return row.units.some((u) => u.unitId === unitId);
|
|
241
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Traceability on the row. When a sender finishes a unit, its record (result
|
|
3
|
+
* and the material lots it consumed) is added to the handoff row's metadata
|
|
4
|
+
* in the same statement that records the release, so a search by lot finds
|
|
5
|
+
* every handoff, and so every unit, that used it. The full record stays on
|
|
6
|
+
* the row as the release item.
|
|
7
|
+
*/
|
|
8
|
+
import { type TraceEntry } from '../contracts';
|
|
9
|
+
/** The trace entry for a finished job event from `machineId`. */
|
|
10
|
+
export declare function traceEntry(machineId: string, body: Record<string, any>): TraceEntry;
|
|
11
|
+
/** A row's trace as read back from its metadata; entries without a unit or machine are dropped. */
|
|
12
|
+
export declare function readTrace(md?: Record<string, any>): TraceEntry[];
|
|
13
|
+
/** Entries from both lists, one per unit and machine; a later entry replaces an earlier one. */
|
|
14
|
+
export declare function mergeTrace(a?: TraceEntry[], b?: TraceEntry[]): TraceEntry[];
|
|
15
|
+
/** The metadata patch for a trace: the entries, and every lot once. */
|
|
16
|
+
export declare function traceFacets(trace: TraceEntry[]): Record<string, unknown>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Traceability on the row. When a sender finishes a unit, its record (result
|
|
4
|
+
* and the material lots it consumed) is added to the handoff row's metadata
|
|
5
|
+
* in the same statement that records the release, so a search by lot finds
|
|
6
|
+
* every handoff, and so every unit, that used it. The full record stays on
|
|
7
|
+
* the row as the release item.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.traceEntry = traceEntry;
|
|
11
|
+
exports.readTrace = readTrace;
|
|
12
|
+
exports.mergeTrace = mergeTrace;
|
|
13
|
+
exports.traceFacets = traceFacets;
|
|
14
|
+
const contracts_1 = require("../contracts");
|
|
15
|
+
const str = (v) => (typeof v === 'string' && v ? v : null);
|
|
16
|
+
function materialsOf(v) {
|
|
17
|
+
if (!Array.isArray(v))
|
|
18
|
+
return [];
|
|
19
|
+
return v.filter((m) => str(m?.material) && str(m?.lot)).map((m) => ({ material: m.material, lot: m.lot }));
|
|
20
|
+
}
|
|
21
|
+
/** The trace entry for a finished job event from `machineId`. */
|
|
22
|
+
function traceEntry(machineId, body) {
|
|
23
|
+
return { unitId: String(body.unitId), machineId, jobId: str(body.jobId), result: str(body.result), materials: materialsOf(body.materials) };
|
|
24
|
+
}
|
|
25
|
+
/** A row's trace as read back from its metadata; entries without a unit or machine are dropped. */
|
|
26
|
+
function readTrace(md = {}) {
|
|
27
|
+
const raw = md[contracts_1.TRACE_FACET.TRACE];
|
|
28
|
+
if (!Array.isArray(raw))
|
|
29
|
+
return [];
|
|
30
|
+
return raw.filter((e) => str(e?.unitId) && str(e?.machineId)).map((e) => ({ ...e, materials: materialsOf(e.materials) }));
|
|
31
|
+
}
|
|
32
|
+
/** Entries from both lists, one per unit and machine; a later entry replaces an earlier one. */
|
|
33
|
+
function mergeTrace(a = [], b = []) {
|
|
34
|
+
const byKey = new Map();
|
|
35
|
+
for (const e of [...a, ...b])
|
|
36
|
+
byKey.set(`${e.unitId}@${e.machineId}`, e);
|
|
37
|
+
return [...byKey.values()];
|
|
38
|
+
}
|
|
39
|
+
/** The metadata patch for a trace: the entries, and every lot once. */
|
|
40
|
+
function traceFacets(trace) {
|
|
41
|
+
const lots = [...new Set(trace.flatMap((e) => e.materials.map((m) => m.lot)))];
|
|
42
|
+
return { [contracts_1.TRACE_FACET.TRACE]: trace, [contracts_1.TRACE_FACET.LOTS]: lots };
|
|
43
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identities inside Long Tail: each machine is a bot account holding its
|
|
3
|
+
* station roles, so rows it claims and resolves name the machine. Bots are
|
|
4
|
+
* found by name and given any role they are missing; nothing is removed. A
|
|
5
|
+
* person's account under a bot's name stops the line from starting.
|
|
6
|
+
*/
|
|
7
|
+
import type { LineDef } from '../../lib/machines/line';
|
|
8
|
+
import { type Actor } from './gateway/actions';
|
|
9
|
+
import type { AddressBook } from './gateway/address-book';
|
|
10
|
+
/** The user property on a station's bot account that holds the address its machine registered. */
|
|
11
|
+
export declare const ADDRESS_PROPERTY = "machineAddress";
|
|
12
|
+
/** Make sure every bot the line needs exists with its roles; returns actor → user id. */
|
|
13
|
+
export declare function ensureLineBots(line: LineDef): Promise<Map<Actor, string>>;
|
|
14
|
+
/**
|
|
15
|
+
* Learned addresses kept on each station's bot account, so a replica that
|
|
16
|
+
* takes the line over sends to the machines the line already knows. An address
|
|
17
|
+
* belongs to one station: binding it to a station clears it from any other.
|
|
18
|
+
*/
|
|
19
|
+
export declare class BotAddressBook implements AddressBook {
|
|
20
|
+
private readonly bots;
|
|
21
|
+
constructor(bots: Map<Actor, string>);
|
|
22
|
+
load(): Promise<Map<string, string>>;
|
|
23
|
+
bind(machineId: string, address: string): Promise<void>;
|
|
24
|
+
private read;
|
|
25
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Identities inside Long Tail: each machine is a bot account holding its
|
|
4
|
+
* station roles, so rows it claims and resolves name the machine. Bots are
|
|
5
|
+
* found by name and given any role they are missing; nothing is removed. A
|
|
6
|
+
* person's account under a bot's name stops the line from starting.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.BotAddressBook = exports.ADDRESS_PROPERTY = void 0;
|
|
10
|
+
exports.ensureLineBots = ensureLineBots;
|
|
11
|
+
const iam_1 = require("../iam");
|
|
12
|
+
const user_1 = require("../user");
|
|
13
|
+
const actions_1 = require("./gateway/actions");
|
|
14
|
+
const provision_1 = require("./gateway/provision");
|
|
15
|
+
/** The user property on a station's bot account that holds the address its machine registered. */
|
|
16
|
+
exports.ADDRESS_PROPERTY = 'machineAddress';
|
|
17
|
+
const MEMBER = 'member';
|
|
18
|
+
const BOT = 'bot';
|
|
19
|
+
/** Make sure every bot the line needs exists with its roles; returns actor → user id. */
|
|
20
|
+
async function ensureLineBots(line) {
|
|
21
|
+
const ids = new Map();
|
|
22
|
+
for (const identity of (0, provision_1.lineIdentities)(line)) {
|
|
23
|
+
const existing = await (0, user_1.getUserByExternalId)(identity.botName);
|
|
24
|
+
if (existing && existing.account_type !== BOT) {
|
|
25
|
+
throw new Error(`"${identity.botName}" belongs to a person's account; the gateway acts only as bot accounts`);
|
|
26
|
+
}
|
|
27
|
+
if (!existing) {
|
|
28
|
+
const bot = await (0, iam_1.createBot)({
|
|
29
|
+
name: identity.botName,
|
|
30
|
+
description: `Machine line ${line.siteId}/${line.lineId}: ${identity.actor}`,
|
|
31
|
+
roles: identity.roles.map((role) => ({ role, type: MEMBER })),
|
|
32
|
+
});
|
|
33
|
+
ids.set(identity.actor, bot.id);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const held = new Set((await (0, iam_1.getBotRoles)(existing.id)).map((r) => r.role));
|
|
37
|
+
for (const role of identity.roles.filter((r) => !held.has(r)))
|
|
38
|
+
await (0, iam_1.addBotRole)(existing.id, role, MEMBER);
|
|
39
|
+
ids.set(identity.actor, existing.id);
|
|
40
|
+
}
|
|
41
|
+
return ids;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Learned addresses kept on each station's bot account, so a replica that
|
|
45
|
+
* takes the line over sends to the machines the line already knows. An address
|
|
46
|
+
* belongs to one station: binding it to a station clears it from any other.
|
|
47
|
+
*/
|
|
48
|
+
class BotAddressBook {
|
|
49
|
+
constructor(bots) {
|
|
50
|
+
this.bots = bots;
|
|
51
|
+
}
|
|
52
|
+
async load() {
|
|
53
|
+
const bound = new Map();
|
|
54
|
+
for (const [machineId, address] of await this.read())
|
|
55
|
+
if (address)
|
|
56
|
+
bound.set(machineId, address);
|
|
57
|
+
return bound;
|
|
58
|
+
}
|
|
59
|
+
async bind(machineId, address) {
|
|
60
|
+
const id = this.bots.get(machineId);
|
|
61
|
+
if (!id)
|
|
62
|
+
throw new Error(`no bot for station "${machineId}"`);
|
|
63
|
+
for (const [other, held] of await this.read()) {
|
|
64
|
+
if (other !== machineId && held === address)
|
|
65
|
+
await (0, user_1.patchUserProperties)(this.bots.get(other), { remove: [exports.ADDRESS_PROPERTY] });
|
|
66
|
+
}
|
|
67
|
+
await (0, user_1.patchUserProperties)(id, { set: { [exports.ADDRESS_PROPERTY]: address } });
|
|
68
|
+
}
|
|
69
|
+
async read() {
|
|
70
|
+
const stations = [...this.bots.entries()].filter(([actor]) => actor !== actions_1.CONTROLLER);
|
|
71
|
+
return Promise.all(stations.map(async ([machineId, id]) => {
|
|
72
|
+
const address = (await (0, user_1.getUser)(id))?.metadata?.[exports.ADDRESS_PROPERTY];
|
|
73
|
+
return [machineId, typeof address === 'string' ? address : undefined];
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.BotAddressBook = BotAddressBook;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './config';
|
|
2
|
+
export * from './contracts';
|
|
3
|
+
export * from './commands';
|
|
4
|
+
export * from './forms';
|
|
5
|
+
export * from './select';
|
|
6
|
+
export * from './gateway';
|
|
7
|
+
export * from './roles';
|
|
8
|
+
export * from './identities';
|
|
9
|
+
export * from './surface';
|
|
10
|
+
export * from './rows';
|
|
11
|
+
export * from '../../lib/machines';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./config"), exports);
|
|
18
|
+
__exportStar(require("./contracts"), exports);
|
|
19
|
+
__exportStar(require("./commands"), exports);
|
|
20
|
+
__exportStar(require("./forms"), exports);
|
|
21
|
+
__exportStar(require("./select"), exports);
|
|
22
|
+
__exportStar(require("./gateway"), exports);
|
|
23
|
+
__exportStar(require("./roles"), exports);
|
|
24
|
+
__exportStar(require("./identities"), exports);
|
|
25
|
+
__exportStar(require("./surface"), exports);
|
|
26
|
+
__exportStar(require("./rows"), exports);
|
|
27
|
+
__exportStar(require("../../lib/machines"), exports);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One gateway per line across every replica. Each line contends for a
|
|
3
|
+
* Postgres advisory lock on a dedicated connection; `pg_advisory_lock`
|
|
4
|
+
* waits on the server, so standby replicas hold no timers and the next one
|
|
5
|
+
* takes over the moment the leader's session ends.
|
|
6
|
+
*/
|
|
7
|
+
import type { PoolClient } from 'pg';
|
|
8
|
+
export interface LeadershipOptions {
|
|
9
|
+
/** A dedicated connection for the lock; the shared pool's by default. */
|
|
10
|
+
connect?: () => Promise<PoolClient>;
|
|
11
|
+
/** The pause before contending again. */
|
|
12
|
+
retryMs?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class LineLeadership {
|
|
15
|
+
readonly key: string;
|
|
16
|
+
private client?;
|
|
17
|
+
private stopped;
|
|
18
|
+
private leading;
|
|
19
|
+
private endTurn?;
|
|
20
|
+
private readonly connect;
|
|
21
|
+
private readonly retryMs;
|
|
22
|
+
constructor(key: string, opts?: LeadershipOptions);
|
|
23
|
+
/**
|
|
24
|
+
* Wait for the lock, then call `lead`. When the connection is refused,
|
|
25
|
+
* leading fails or the connection drops, `lost` runs before this replica lets
|
|
26
|
+
* the lock go, so no other replica leads alongside a gateway that is
|
|
27
|
+
* stopping; then the line contends again after a pause.
|
|
28
|
+
*/
|
|
29
|
+
run(lead: () => Promise<void>, lost: (err: Error) => Promise<void>): Promise<void>;
|
|
30
|
+
private leadOnce;
|
|
31
|
+
/** Lead while the lock is held; the error that ended it. */
|
|
32
|
+
private holdLock;
|
|
33
|
+
/** A leader unlocks; a replica still waiting drops its connection, which ends the wait. */
|
|
34
|
+
stop(): Promise<void>;
|
|
35
|
+
}
|