signalk-wakelogger 0.2.0-beta.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 (83) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +201 -0
  3. package/README.md +49 -0
  4. package/SECURITY.md +7 -0
  5. package/assets/screenshots/plugin-config.png +0 -0
  6. package/assets/screenshots/plugin-status.png +0 -0
  7. package/assets/wakelogger.svg +6 -0
  8. package/dist/config/defaults.d.ts +18 -0
  9. package/dist/config/defaults.js +30 -0
  10. package/dist/config/defaults.js.map +1 -0
  11. package/dist/config/schema.d.ts +43 -0
  12. package/dist/config/schema.js +47 -0
  13. package/dist/config/schema.js.map +1 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.js +338 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/outbox/database-outbox.d.ts +25 -0
  18. package/dist/outbox/database-outbox.js +186 -0
  19. package/dist/outbox/database-outbox.js.map +1 -0
  20. package/dist/outbox/database-types.d.ts +19 -0
  21. package/dist/outbox/database-types.js +3 -0
  22. package/dist/outbox/database-types.js.map +1 -0
  23. package/dist/outbox/factory.d.ts +12 -0
  24. package/dist/outbox/factory.js +98 -0
  25. package/dist/outbox/factory.js.map +1 -0
  26. package/dist/outbox/file-outbox.d.ts +27 -0
  27. package/dist/outbox/file-outbox.js +280 -0
  28. package/dist/outbox/file-outbox.js.map +1 -0
  29. package/dist/outbox/interface.d.ts +33 -0
  30. package/dist/outbox/interface.js +3 -0
  31. package/dist/outbox/interface.js.map +1 -0
  32. package/dist/pairing/association-client.d.ts +3 -0
  33. package/dist/pairing/association-client.js +31 -0
  34. package/dist/pairing/association-client.js.map +1 -0
  35. package/dist/pairing/credentials.d.ts +46 -0
  36. package/dist/pairing/credentials.js +168 -0
  37. package/dist/pairing/credentials.js.map +1 -0
  38. package/dist/pairing/pairing-client.d.ts +19 -0
  39. package/dist/pairing/pairing-client.js +102 -0
  40. package/dist/pairing/pairing-client.js.map +1 -0
  41. package/dist/signalk/normaliser.d.ts +11 -0
  42. package/dist/signalk/normaliser.js +119 -0
  43. package/dist/signalk/normaliser.js.map +1 -0
  44. package/dist/signalk/paths.d.ts +2 -0
  45. package/dist/signalk/paths.js +14 -0
  46. package/dist/signalk/paths.js.map +1 -0
  47. package/dist/signalk/subscriber.d.ts +2 -0
  48. package/dist/signalk/subscriber.js +20 -0
  49. package/dist/signalk/subscriber.js.map +1 -0
  50. package/dist/telemetry/profile-store.d.ts +7 -0
  51. package/dist/telemetry/profile-store.js +39 -0
  52. package/dist/telemetry/profile-store.js.map +1 -0
  53. package/dist/telemetry/profile.d.ts +32 -0
  54. package/dist/telemetry/profile.js +94 -0
  55. package/dist/telemetry/profile.js.map +1 -0
  56. package/dist/telemetry/sampler.d.ts +14 -0
  57. package/dist/telemetry/sampler.js +44 -0
  58. package/dist/telemetry/sampler.js.map +1 -0
  59. package/dist/telemetry/types.d.ts +73 -0
  60. package/dist/telemetry/types.js +3 -0
  61. package/dist/telemetry/types.js.map +1 -0
  62. package/dist/transport/adaptive-mode.d.ts +23 -0
  63. package/dist/transport/adaptive-mode.js +63 -0
  64. package/dist/transport/adaptive-mode.js.map +1 -0
  65. package/dist/transport/mqtt-client.d.ts +69 -0
  66. package/dist/transport/mqtt-client.js +365 -0
  67. package/dist/transport/mqtt-client.js.map +1 -0
  68. package/dist/transport/topics.d.ts +10 -0
  69. package/dist/transport/topics.js +13 -0
  70. package/dist/transport/topics.js.map +1 -0
  71. package/dist/trips/state-machine.d.ts +38 -0
  72. package/dist/trips/state-machine.js +118 -0
  73. package/dist/trips/state-machine.js.map +1 -0
  74. package/docs/architecture.md +17 -0
  75. package/docs/development.md +49 -0
  76. package/docs/implementation-plan.md +1943 -0
  77. package/docs/original-plan-gap-audit.md +47 -0
  78. package/docs/pairing.md +17 -0
  79. package/docs/release.md +52 -0
  80. package/docs/telemetry-protocol.md +50 -0
  81. package/docs/trip-detection.md +7 -0
  82. package/docs/validation.md +35 -0
  83. package/package.json +80 -0
package/dist/index.js ADDED
@@ -0,0 +1,338 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = __importDefault(require("node:path"));
7
+ const schema_1 = require("./config/schema");
8
+ const defaults_1 = require("./config/defaults");
9
+ const factory_1 = require("./outbox/factory");
10
+ const association_client_1 = require("./pairing/association-client");
11
+ const credentials_1 = require("./pairing/credentials");
12
+ const pairing_client_1 = require("./pairing/pairing-client");
13
+ const normaliser_1 = require("./signalk/normaliser");
14
+ const subscriber_1 = require("./signalk/subscriber");
15
+ const profile_1 = require("./telemetry/profile");
16
+ const profile_store_1 = require("./telemetry/profile-store");
17
+ const sampler_1 = require("./telemetry/sampler");
18
+ const mqtt_client_1 = require("./transport/mqtt-client");
19
+ const state_machine_1 = require("./trips/state-machine");
20
+ const constructor = (app) => {
21
+ const pluginVersion = '0.2.0-beta.0';
22
+ let generation = 0;
23
+ let stopSubscription;
24
+ let sampleTimer;
25
+ let statusTimer;
26
+ let outbox;
27
+ let transport;
28
+ let tripState;
29
+ let connectionState = 'unpaired';
30
+ let activeProfile;
31
+ let storageBackend = 'file';
32
+ let initialization;
33
+ let pairingAbortController;
34
+ let associationAbortController;
35
+ let associationCheck;
36
+ const plugin = {
37
+ id: 'signalk-wakelogger',
38
+ name: 'Wake Logger',
39
+ description: 'Secure, resilient live vessel telemetry for Wake Logger',
40
+ schema: schema_1.configSchema,
41
+ start(configuration) {
42
+ const previous = initialization;
43
+ const thisGeneration = ++generation;
44
+ initialization = (async () => {
45
+ await previous?.catch(() => undefined);
46
+ await cleanupResources();
47
+ if (thisGeneration === generation)
48
+ await initialise((0, defaults_1.parseConfig)(configuration), thisGeneration);
49
+ })().catch((error) => {
50
+ if (thisGeneration !== generation)
51
+ return;
52
+ const message = safeError(error);
53
+ app.setPluginError(`Wake Logger initialization failed: ${message}`);
54
+ app.error(`Wake Logger initialization failed: ${message}`);
55
+ });
56
+ },
57
+ async stop() {
58
+ const running = initialization;
59
+ generation += 1;
60
+ pairingAbortController?.abort();
61
+ associationAbortController?.abort();
62
+ await running?.catch(() => undefined);
63
+ await cleanupResources();
64
+ initialization = undefined;
65
+ app.setPluginStatus('Wake Logger: Stopped');
66
+ },
67
+ registerWithRouter(router) {
68
+ // Signal K protects routes registered directly on the plugin router with
69
+ // administrator authentication. Do not downgrade this action via access().
70
+ const adminRouter = router;
71
+ adminRouter.post('/forget-credentials', async (_request, response, next) => {
72
+ try {
73
+ generation += 1;
74
+ pairingAbortController?.abort();
75
+ associationAbortController?.abort();
76
+ await initialization?.catch(() => undefined);
77
+ await cleanupResources();
78
+ const store = new credentials_1.CredentialStore(node_path_1.default.join(app.getDataDirPath(), 'identity'));
79
+ const result = await store.forget();
80
+ initialization = undefined;
81
+ connectionState = 'unpaired';
82
+ app.setPluginStatus('Wake Logger: Not paired — credentials forgotten; retired outboxes preserved');
83
+ response.status(200).json({
84
+ forgotten: result.forgotten,
85
+ retiredDeviceId: result.deviceId,
86
+ outboxesPreserved: true,
87
+ next: 'Enter and save a fresh Wake Logger pairing code.'
88
+ });
89
+ }
90
+ catch (error) {
91
+ next(error);
92
+ }
93
+ });
94
+ },
95
+ getOpenApi: () => ({
96
+ openapi: '3.0.3',
97
+ info: { title: 'Wake Logger Signal K plugin', version: pluginVersion },
98
+ paths: {
99
+ '/forget-credentials': {
100
+ post: {
101
+ summary: 'Forget Wake Logger credentials while preserving all device outboxes',
102
+ responses: { '200': { description: 'Credentials forgotten or already absent' } }
103
+ }
104
+ }
105
+ }
106
+ })
107
+ };
108
+ async function cleanupResources() {
109
+ stopSubscription?.();
110
+ stopSubscription = undefined;
111
+ if (sampleTimer)
112
+ clearTimeout(sampleTimer);
113
+ if (statusTimer)
114
+ clearInterval(statusTimer);
115
+ sampleTimer = undefined;
116
+ statusTimer = undefined;
117
+ await transport?.stop();
118
+ transport = undefined;
119
+ await outbox?.close();
120
+ outbox = undefined;
121
+ tripState = undefined;
122
+ pairingAbortController = undefined;
123
+ associationAbortController = undefined;
124
+ associationCheck = undefined;
125
+ }
126
+ async function initialise(config, thisGeneration) {
127
+ const dataDirectory = app.getDataDirPath();
128
+ const credentialStore = new credentials_1.CredentialStore(node_path_1.default.join(dataDirectory, 'identity'));
129
+ let credentials = await credentialStore.load();
130
+ const lastPairingCodeFingerprint = await credentialStore.lastPairingCodeFingerprint();
131
+ if ((0, credentials_1.shouldExchangePairingCode)(credentials, config.pairingCode, lastPairingCodeFingerprint)) {
132
+ const controller = new AbortController();
133
+ pairingAbortController = controller;
134
+ try {
135
+ const replacement = await (0, pairing_client_1.pairDeviceWithRetry)(config.pairingApiUrl, config.pairingCode, await credentialStore.installationId(), {
136
+ signal: controller.signal,
137
+ onAttempt: (attempt, maximum) => {
138
+ if (generation === thisGeneration)
139
+ app.setPluginStatus(`Wake Logger: Pairing attempt ${attempt}/${maximum}`);
140
+ },
141
+ onRetry: (attempt, maximum, delayMs) => {
142
+ if (generation === thisGeneration)
143
+ app.setPluginStatus(`Wake Logger: Retry pairing ${attempt}/${maximum} in ${Math.ceil(delayMs / 1000)} seconds`);
144
+ }
145
+ });
146
+ if (generation !== thisGeneration)
147
+ return;
148
+ replacement.pairingCodeFingerprint = (0, credentials_1.fingerprintPairingCode)(config.pairingCode);
149
+ await credentialStore.save(replacement);
150
+ credentials = replacement;
151
+ }
152
+ catch (error) {
153
+ if (generation !== thisGeneration || controller.signal.aborted)
154
+ return;
155
+ if (!credentials) {
156
+ connectionState = 'unpaired';
157
+ const expired = error instanceof pairing_client_1.PairingError && error.status === 400;
158
+ const retryExhausted = error instanceof pairing_client_1.PairingError && error.retryable;
159
+ app.setPluginStatus(expired
160
+ ? 'Wake Logger: Pairing code invalid or expired — enter a new pairing code'
161
+ : retryExhausted
162
+ ? 'Wake Logger: Retry pairing stopped after 6 attempts — save configuration to retry or enter a new code'
163
+ : `Wake Logger: Pairing rejected — ${safeError(error)}; enter a new pairing code`);
164
+ return;
165
+ }
166
+ app.error(`Wake Logger replacement pairing failed; continuing with the existing device: ${safeError(error)}`);
167
+ }
168
+ finally {
169
+ if (pairingAbortController === controller)
170
+ pairingAbortController = undefined;
171
+ }
172
+ }
173
+ if (!credentials) {
174
+ connectionState = 'unpaired';
175
+ app.setPluginStatus('Wake Logger: Not paired');
176
+ return;
177
+ }
178
+ if (generation !== thisGeneration)
179
+ return;
180
+ await startTelemetry(config, credentials, credentialStore, dataDirectory, thisGeneration);
181
+ }
182
+ async function startTelemetry(config, credentials, credentialStore, dataDirectory, thisGeneration) {
183
+ const normaliser = new normaliser_1.TelemetryNormaliser();
184
+ const tripFile = node_path_1.default.join(dataDirectory, 'trip-state.json');
185
+ const trip = new state_machine_1.TripStateMachine(await readTripSnapshot(tripFile));
186
+ tripState = trip;
187
+ let tripSnapshotJson = JSON.stringify(trip.currentState());
188
+ // Each provisioned device owns an independent sequence space. A replacement
189
+ // device must never replay the retired device's records under new credentials.
190
+ const profileStore = new profile_store_1.TelemetryProfileStore(node_path_1.default.join(dataDirectory, 'profiles', credentials.deviceId));
191
+ const credentialProfile = (0, profile_1.parseTelemetryProfile)(credentials.telemetryProfile);
192
+ const legacy = credentials.telemetryProfile;
193
+ const profile = await profileStore.load() ?? credentialProfile ?? (0, profile_1.legacyProfile)(legacy?.sample_period_ms ?? config.samplePeriodMs, legacy?.batch_size);
194
+ activeProfile = profile;
195
+ const sampler = new sampler_1.PathSampler(profile);
196
+ const selected = await (0, factory_1.createOutbox)(app, dataDirectory, credentials.deviceId, {
197
+ maxBytes: config.maxOutboxMb * 1024 * 1024,
198
+ maxAgeMs: config.maxOutboxDays * 24 * 60 * 60 * 1000,
199
+ segmentBytes: defaults_1.DEFAULTS.segmentBytes
200
+ }, credentials.outboxBinding?.backend);
201
+ outbox = selected.store;
202
+ storageBackend = selected.backend;
203
+ if (!credentials.outboxBinding) {
204
+ credentials.outboxBinding = { version: 1, backend: selected.backend, initializedAt: Date.now() };
205
+ await credentialStore.save(credentials);
206
+ }
207
+ if (generation !== thisGeneration)
208
+ return;
209
+ transport = new mqtt_client_1.WakeLoggerTransport(credentials, outbox, {
210
+ profile,
211
+ onState: (state, detail) => {
212
+ if (connectionState === 'device_revoked')
213
+ return;
214
+ connectionState = state;
215
+ void updateStatus(detail);
216
+ if (state === 'authentication_failed')
217
+ void confirmAssociation(credentials, thisGeneration);
218
+ },
219
+ onMode: (mode) => sampler.updateMode(mode),
220
+ onProfile: async (replacement) => {
221
+ await profileStore.save(replacement);
222
+ activeProfile = replacement;
223
+ sampler.updateProfile(replacement);
224
+ },
225
+ debug: config.debugTelemetry ? (message) => app.debug(message) : undefined
226
+ });
227
+ stopSubscription = (0, subscriber_1.subscribeToTelemetry)(app, (delta) => normaliser.ingest(delta));
228
+ const sample = () => {
229
+ const now = Date.now();
230
+ const draft = normaliser.takeSample(now, sampler.dueFields(now));
231
+ if (!draft || !outbox || !transport)
232
+ return;
233
+ const evidence = trip.process(draft);
234
+ draft.trackingSessionId = evidence?.trackingSessionId ?? trip.trackingSessionId();
235
+ if (evidence)
236
+ draft.evidence = evidence;
237
+ const nextSnapshotJson = JSON.stringify(trip.currentState());
238
+ if (nextSnapshotJson !== tripSnapshotJson) {
239
+ tripSnapshotJson = nextSnapshotJson;
240
+ void atomicWrite(tripFile, `${tripSnapshotJson}\n`).catch((error) => app.error(`Unable to persist Wake Logger trip state: ${safeError(error)}`));
241
+ }
242
+ void outbox.append(credentials.deviceId, draft).then((sample) => {
243
+ transport?.updateCurrent(sample);
244
+ if (config.debugTelemetry)
245
+ app.debug(`Queued Wake Logger sequence ${sample.sequence}`);
246
+ }).catch((error) => app.error(`Unable to queue Wake Logger telemetry: ${safeError(error)}`));
247
+ };
248
+ const scheduleSample = () => {
249
+ if (generation !== thisGeneration)
250
+ return;
251
+ sampleTimer = setTimeout(() => {
252
+ sample();
253
+ scheduleSample();
254
+ }, sampler.samplePeriodMs());
255
+ };
256
+ scheduleSample();
257
+ statusTimer = setInterval(() => void updateStatus(), 10_000);
258
+ transport.start();
259
+ await updateStatus();
260
+ }
261
+ async function updateStatus(detail) {
262
+ if (connectionState === 'unpaired' || !outbox) {
263
+ app.setPluginStatus('Wake Logger: Not paired');
264
+ return;
265
+ }
266
+ const stats = await outbox.stats();
267
+ const queue = `${stats.messageCount} queued, ${(stats.diskBytes / 1024 / 1024).toFixed(1)} MB`;
268
+ const dropped = stats.droppedCount ? `, ${stats.droppedCount} dropped` : '';
269
+ const sequence = `, seq ${stats.acknowledgedSequence}/${stats.currentSequence}`;
270
+ const trip = tripState ? `, trip ${tripState.currentState().state}` : '';
271
+ const extra = detail ? ` — ${detail}` : '';
272
+ transport?.updateStatus({
273
+ pluginVersion,
274
+ connectionState,
275
+ queueMessageCount: stats.messageCount,
276
+ queueDiskBytes: stats.diskBytes,
277
+ queueOldestCapturedAt: stats.oldestCapturedAt,
278
+ queueDroppedCount: stats.droppedCount,
279
+ acknowledgedSequence: stats.acknowledgedSequence,
280
+ currentSequence: stats.currentSequence,
281
+ trackingState: tripState?.currentState().state,
282
+ storageBackend,
283
+ profileId: activeProfile?.profileId,
284
+ profileRevision: activeProfile?.revision,
285
+ ...transport?.transportMetrics()
286
+ });
287
+ if (connectionState === 'device_revoked') {
288
+ app.setPluginStatus(`Wake Logger: Device revoked — enter a new pairing code; ${queue}${dropped}${sequence}`);
289
+ return;
290
+ }
291
+ app.setPluginStatus(`Wake Logger: ${connectionState} — ${queue}${dropped}${sequence}${trip}${extra}`);
292
+ }
293
+ function confirmAssociation(credentials, thisGeneration) {
294
+ if (associationCheck)
295
+ return associationCheck;
296
+ const controller = new AbortController();
297
+ associationAbortController = controller;
298
+ associationCheck = (async () => {
299
+ const status = await (0, association_client_1.checkAssociationStatus)(credentials, controller.signal);
300
+ if (status !== 'revoked' || generation !== thisGeneration || controller.signal.aborted)
301
+ return;
302
+ connectionState = 'device_revoked';
303
+ stopSubscription?.();
304
+ stopSubscription = undefined;
305
+ if (sampleTimer)
306
+ clearTimeout(sampleTimer);
307
+ sampleTimer = undefined;
308
+ await transport?.stop();
309
+ await updateStatus();
310
+ })().finally(() => {
311
+ if (associationAbortController === controller)
312
+ associationAbortController = undefined;
313
+ associationCheck = undefined;
314
+ });
315
+ return associationCheck;
316
+ }
317
+ return plugin;
318
+ };
319
+ async function readTripSnapshot(target) {
320
+ try {
321
+ return JSON.parse(await node_fs_1.promises.readFile(target, 'utf8'));
322
+ }
323
+ catch (error) {
324
+ if (error?.code === 'ENOENT' || error instanceof SyntaxError)
325
+ return undefined;
326
+ throw error;
327
+ }
328
+ }
329
+ async function atomicWrite(target, contents) {
330
+ const temporary = `${target}.tmp`;
331
+ await node_fs_1.promises.writeFile(temporary, contents, { mode: 0o600 });
332
+ await node_fs_1.promises.rename(temporary, target);
333
+ }
334
+ function safeError(error) {
335
+ return (error instanceof Error ? error.message : String(error)).replace(/(password|token|secret)=\S+/gi, '$1=[redacted]').slice(0, 500);
336
+ }
337
+ module.exports = constructor;
338
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { TelemetryDraft, TelemetrySample } from '../telemetry/types';
2
+ import type { PluginDatabase } from './database-types';
3
+ import type { OutboxOptions, OutboxSeed, OutboxStats, OutboxStore } from './interface';
4
+ export declare class DatabaseOutbox implements OutboxStore {
5
+ private readonly database;
6
+ private readonly deviceId;
7
+ private readonly options;
8
+ private readonly seed?;
9
+ private opened;
10
+ private operation;
11
+ constructor(database: PluginDatabase, deviceId: string, options: OutboxOptions, seed?: OutboxSeed | undefined);
12
+ open(): Promise<void>;
13
+ append(deviceId: string, draft: TelemetryDraft): Promise<TelemetrySample>;
14
+ pending(limit: number, maxBytes: number): Promise<TelemetrySample[]>;
15
+ pendingAfter(sequence: number, limit: number, maxBytes: number): Promise<TelemetrySample[]>;
16
+ acknowledge(sequence: number): Promise<void>;
17
+ latest(): Promise<TelemetrySample | undefined>;
18
+ stats(): Promise<OutboxStats>;
19
+ close(): Promise<void>;
20
+ private enforceLimits;
21
+ private enforceLimitsUnlocked;
22
+ private readState;
23
+ private assertOpen;
24
+ private exclusive;
25
+ }
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DatabaseOutbox = void 0;
4
+ const MIGRATIONS = [{
5
+ version: 1,
6
+ sql: `
7
+ CREATE TABLE IF NOT EXISTS outbox_state (
8
+ device_id TEXT PRIMARY KEY,
9
+ next_sequence INTEGER NOT NULL,
10
+ acknowledged_sequence INTEGER NOT NULL,
11
+ dropped_count INTEGER NOT NULL,
12
+ dropped_through INTEGER NOT NULL
13
+ );
14
+ CREATE TABLE IF NOT EXISTS outbox_records (
15
+ device_id TEXT NOT NULL,
16
+ sequence INTEGER NOT NULL,
17
+ captured_at INTEGER NOT NULL,
18
+ received_at INTEGER NOT NULL,
19
+ payload TEXT NOT NULL,
20
+ payload_bytes INTEGER NOT NULL,
21
+ PRIMARY KEY (device_id, sequence)
22
+ );
23
+ CREATE INDEX IF NOT EXISTS ix_outbox_records_device_sequence ON outbox_records (device_id, sequence);
24
+ CREATE INDEX IF NOT EXISTS ix_outbox_records_device_captured ON outbox_records (device_id, captured_at);
25
+ `
26
+ }];
27
+ class DatabaseOutbox {
28
+ database;
29
+ deviceId;
30
+ options;
31
+ seed;
32
+ opened = false;
33
+ operation = Promise.resolve();
34
+ constructor(database, deviceId, options, seed) {
35
+ this.database = database;
36
+ this.deviceId = deviceId;
37
+ this.options = options;
38
+ this.seed = seed;
39
+ }
40
+ async open() {
41
+ await this.database.migrate(MIGRATIONS);
42
+ const seed = this.seed ?? { currentSequence: 0, acknowledgedSequence: 0, droppedCount: 0, droppedThrough: 0 };
43
+ await this.database.run(`INSERT INTO outbox_state (device_id, next_sequence, acknowledged_sequence, dropped_count, dropped_through)
44
+ SELECT ?, ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM outbox_state WHERE device_id = ?)`, [this.deviceId, seed.currentSequence + 1, seed.acknowledgedSequence, seed.droppedCount, seed.droppedThrough, this.deviceId]);
45
+ if (this.seed) {
46
+ const state = await this.readState(this.database);
47
+ await this.database.run(`UPDATE outbox_state SET next_sequence = ?, acknowledged_sequence = ?, dropped_count = ?, dropped_through = ?
48
+ WHERE device_id = ?`, [
49
+ Math.max(Number(state.next_sequence), seed.currentSequence + 1),
50
+ Math.max(Number(state.acknowledged_sequence), seed.acknowledgedSequence),
51
+ Math.max(Number(state.dropped_count), seed.droppedCount),
52
+ Math.max(Number(state.dropped_through), seed.droppedThrough),
53
+ this.deviceId
54
+ ]);
55
+ }
56
+ this.opened = true;
57
+ await this.enforceLimits();
58
+ }
59
+ append(deviceId, draft) {
60
+ return this.exclusive(async () => {
61
+ this.assertOpen();
62
+ if (deviceId !== this.deviceId)
63
+ throw new Error('Outbox device identity does not match');
64
+ const sample = await this.database.transaction(async (database) => {
65
+ const state = await this.readState(database);
66
+ const value = { v: 1, deviceId, sequence: state.next_sequence, ...draft };
67
+ const payload = JSON.stringify(value);
68
+ await database.run('INSERT INTO outbox_records (device_id, sequence, captured_at, received_at, payload, payload_bytes) VALUES (?, ?, ?, ?, ?, ?)', [deviceId, value.sequence, value.capturedAt, value.receivedAt, payload, Buffer.byteLength(payload)]);
69
+ await database.run('UPDATE outbox_state SET next_sequence = ? WHERE device_id = ?', [value.sequence + 1, deviceId]);
70
+ return value;
71
+ });
72
+ await this.enforceLimitsUnlocked();
73
+ return sample;
74
+ });
75
+ }
76
+ pending(limit, maxBytes) {
77
+ return this.pendingAfter(0, limit, maxBytes);
78
+ }
79
+ pendingAfter(sequence, limit, maxBytes) {
80
+ return this.exclusive(async () => {
81
+ this.assertOpen();
82
+ const rows = await this.database.query('SELECT payload FROM outbox_records WHERE device_id = ? AND sequence > ? ORDER BY sequence ASC LIMIT ?', [this.deviceId, sequence, Math.max(1, limit)]);
83
+ const result = [];
84
+ let bytes = 0;
85
+ for (const row of rows) {
86
+ const size = Buffer.byteLength(row.payload);
87
+ if (result.length && bytes + size > maxBytes)
88
+ break;
89
+ result.push(JSON.parse(row.payload));
90
+ bytes += size;
91
+ }
92
+ return result;
93
+ });
94
+ }
95
+ acknowledge(sequence) {
96
+ return this.exclusive(async () => {
97
+ this.assertOpen();
98
+ await this.database.transaction(async (database) => {
99
+ const state = await this.readState(database);
100
+ const maximum = state.next_sequence - 1;
101
+ if (!Number.isSafeInteger(sequence) || sequence <= state.acknowledged_sequence || sequence > maximum)
102
+ return;
103
+ await database.run('UPDATE outbox_state SET acknowledged_sequence = ? WHERE device_id = ?', [sequence, this.deviceId]);
104
+ await database.run('DELETE FROM outbox_records WHERE device_id = ? AND sequence <= ?', [this.deviceId, sequence]);
105
+ });
106
+ });
107
+ }
108
+ latest() {
109
+ return this.exclusive(async () => {
110
+ this.assertOpen();
111
+ const rows = await this.database.query('SELECT payload FROM outbox_records WHERE device_id = ? ORDER BY sequence DESC LIMIT 1', [this.deviceId]);
112
+ return rows[0] ? JSON.parse(rows[0].payload) : undefined;
113
+ });
114
+ }
115
+ stats() {
116
+ return this.exclusive(async () => {
117
+ this.assertOpen();
118
+ const state = await this.readState(this.database);
119
+ const rows = await this.database.query(`SELECT COUNT(*) AS message_count, COALESCE(SUM(payload_bytes), 0) AS storage_bytes,
120
+ MIN(captured_at) AS oldest_captured_at FROM outbox_records WHERE device_id = ?`, [this.deviceId]);
121
+ const aggregate = rows[0] ?? { message_count: 0, storage_bytes: 0, oldest_captured_at: null };
122
+ return {
123
+ storageBackend: 'database',
124
+ messageCount: Number(aggregate.message_count),
125
+ diskBytes: Number(aggregate.storage_bytes),
126
+ oldestCapturedAt: aggregate.oldest_captured_at === null ? undefined : Number(aggregate.oldest_captured_at),
127
+ acknowledgedSequence: Number(state.acknowledged_sequence),
128
+ currentSequence: Number(state.next_sequence) - 1,
129
+ droppedCount: Number(state.dropped_count),
130
+ droppedThrough: Number(state.dropped_through)
131
+ };
132
+ });
133
+ }
134
+ async close() {
135
+ await this.operation;
136
+ this.opened = false;
137
+ }
138
+ async enforceLimits() {
139
+ await this.exclusive(() => this.enforceLimitsUnlocked());
140
+ }
141
+ async enforceLimitsUnlocked() {
142
+ const now = this.options.now?.() ?? Date.now();
143
+ const rows = await this.database.query('SELECT sequence, payload_bytes FROM outbox_records WHERE device_id = ? ORDER BY sequence ASC', [this.deviceId]);
144
+ let total = rows.reduce((sum, row) => sum + Number(row.payload_bytes), 0);
145
+ const cutoff = now - this.options.maxAgeMs;
146
+ const capturedRows = await this.database.query('SELECT sequence, captured_at FROM outbox_records WHERE device_id = ? ORDER BY sequence ASC', [this.deviceId]);
147
+ let dropThrough = 0;
148
+ let dropCount = 0;
149
+ for (let index = 0; index < rows.length - 1; index += 1) {
150
+ const row = rows[index];
151
+ const captured = capturedRows[index];
152
+ if (!row || !captured)
153
+ continue;
154
+ const tooOld = Number(captured.captured_at) < cutoff;
155
+ if (!tooOld && total <= this.options.maxBytes)
156
+ break;
157
+ total -= Number(row.payload_bytes);
158
+ dropThrough = Number(row.sequence);
159
+ dropCount += 1;
160
+ }
161
+ if (!dropCount)
162
+ return;
163
+ await this.database.transaction(async (database) => {
164
+ await database.run('DELETE FROM outbox_records WHERE device_id = ? AND sequence <= ?', [this.deviceId, dropThrough]);
165
+ await database.run(`UPDATE outbox_state SET dropped_count = dropped_count + ?,
166
+ dropped_through = CASE WHEN dropped_through > ? THEN dropped_through ELSE ? END WHERE device_id = ?`, [dropCount, dropThrough, dropThrough, this.deviceId]);
167
+ });
168
+ }
169
+ async readState(database) {
170
+ const rows = await database.query('SELECT next_sequence, acknowledged_sequence, dropped_count, dropped_through FROM outbox_state WHERE device_id = ?', [this.deviceId]);
171
+ if (!rows[0])
172
+ throw new Error('Outbox state is unavailable');
173
+ return rows[0];
174
+ }
175
+ assertOpen() {
176
+ if (!this.opened)
177
+ throw new Error('Outbox is not open');
178
+ }
179
+ exclusive(action) {
180
+ const result = this.operation.then(action, action);
181
+ this.operation = result.then(() => undefined, () => undefined);
182
+ return result;
183
+ }
184
+ }
185
+ exports.DatabaseOutbox = DatabaseOutbox;
186
+ //# sourceMappingURL=database-outbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-outbox.js","sourceRoot":"","sources":["../../src/outbox/database-outbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export interface DatabaseRunResult {
2
+ changes: number;
3
+ lastInsertRowid: number | bigint;
4
+ }
5
+ export interface PluginDatabase {
6
+ migrate(migrations: Array<{
7
+ version: number;
8
+ sql: string;
9
+ }>): Promise<void>;
10
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
11
+ run(sql: string, params?: unknown[]): Promise<DatabaseRunResult>;
12
+ transaction<T>(action: (database: PluginDatabase) => Promise<T>): Promise<T>;
13
+ }
14
+ export interface SignalKDatabaseApi {
15
+ getPluginDb(pluginId: string): Promise<PluginDatabase>;
16
+ }
17
+ export interface WithSignalKDatabaseApi {
18
+ getDatabaseApi?: () => SignalKDatabaseApi;
19
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=database-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-types.js","sourceRoot":"","sources":["../../src/outbox/database-types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import type { ServerAPI } from '@signalk/server-api';
2
+ import type { OutboxOptions, OutboxStore } from './interface';
3
+ interface BackendMarker {
4
+ version: 1;
5
+ backend: 'file' | 'database';
6
+ }
7
+ export interface SelectedOutbox {
8
+ store: OutboxStore;
9
+ backend: BackendMarker['backend'];
10
+ }
11
+ export declare function createOutbox(app: ServerAPI, dataDirectory: string, deviceId: string, options: OutboxOptions, expectedBackend?: BackendMarker['backend']): Promise<SelectedOutbox>;
12
+ export {};