station-adapter-sqlite 1.0.2 → 1.0.4
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/dist/broadcast.d.ts +7 -1
- package/dist/broadcast.d.ts.map +1 -1
- package/dist/broadcast.js +147 -15
- package/dist/broadcast.js.map +1 -1
- package/dist/schedules.d.ts +24 -0
- package/dist/schedules.d.ts.map +1 -0
- package/dist/schedules.js +185 -0
- package/dist/schedules.js.map +1 -0
- package/package.json +16 -4
- package/src/broadcast.ts +151 -15
- package/src/schedules.ts +207 -0
package/dist/broadcast.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BroadcastQueueAdapter, BroadcastRun, BroadcastRunPatch, BroadcastRunStatus, BroadcastNodeRun, BroadcastNodeRunPatch } from "station-broadcast";
|
|
1
|
+
import type { BroadcastQueueAdapter, BroadcastRun, BroadcastRunPatch, BroadcastRunStatus, BroadcastNodeRun, BroadcastNodeRunPatch, DynamicBroadcastSpec } from "station-broadcast";
|
|
2
2
|
export interface BroadcastSqliteAdapterOptions {
|
|
3
3
|
dbPath?: string;
|
|
4
4
|
tableName?: string;
|
|
@@ -7,6 +7,7 @@ export declare class BroadcastSqliteAdapter implements BroadcastQueueAdapter {
|
|
|
7
7
|
private db;
|
|
8
8
|
private runsTable;
|
|
9
9
|
private nodesTable;
|
|
10
|
+
private definitionsTable;
|
|
10
11
|
constructor(options?: BroadcastSqliteAdapterOptions);
|
|
11
12
|
addBroadcastRun(run: BroadcastRun): Promise<void>;
|
|
12
13
|
getBroadcastRun(id: string): Promise<BroadcastRun | null>;
|
|
@@ -22,6 +23,11 @@ export declare class BroadcastSqliteAdapter implements BroadcastQueueAdapter {
|
|
|
22
23
|
private static readonly NODE_RUN_PATCH_KEYS;
|
|
23
24
|
updateNodeRun(id: string, patch: BroadcastNodeRunPatch): Promise<void>;
|
|
24
25
|
getNodeRuns(broadcastRunId: string): Promise<BroadcastNodeRun[]>;
|
|
26
|
+
saveDefinition(spec: DynamicBroadcastSpec): Promise<DynamicBroadcastSpec>;
|
|
27
|
+
getDefinition(name: string, version?: number): Promise<DynamicBroadcastSpec | null>;
|
|
28
|
+
listDefinitions(): Promise<DynamicBroadcastSpec[]>;
|
|
29
|
+
listDefinitionVersions(name: string): Promise<DynamicBroadcastSpec[]>;
|
|
30
|
+
deleteDefinition(name: string): Promise<boolean>;
|
|
25
31
|
generateId(): string;
|
|
26
32
|
ping(): Promise<boolean>;
|
|
27
33
|
close(): Promise<void>;
|
package/dist/broadcast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAiC3B,MAAM,WAAW,6BAA6B;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,sBAAuB,YAAW,qBAAqB;IAClE,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAS;gBAErB,OAAO,GAAE,6BAAkC;IAsFjD,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBjD,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAK/D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAG7C;IAEG,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBvE,mBAAmB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAW9C,uBAAuB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAKlD,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAKjE,yBAAyB,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IASlG,kBAAkB,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAUpF,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBpD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAK9D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAExC;IAEG,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBtE,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAOhE,cAAc,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAwCzE,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAcnF,eAAe,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAclD,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAOrE,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAmBtD,UAAU,IAAI,MAAM;IAId,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IASxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
package/dist/broadcast.js
CHANGED
|
@@ -8,6 +8,7 @@ const { toColumn: toBroadcastRunCol, toField: toBroadcastRunField } = createColu
|
|
|
8
8
|
startedAt: "started_at",
|
|
9
9
|
completedAt: "completed_at",
|
|
10
10
|
createdAt: "created_at",
|
|
11
|
+
definitionSnapshot: "definition_snapshot",
|
|
11
12
|
});
|
|
12
13
|
const BROADCAST_RUN_DATE_FIELDS = new Set(["nextRunAt", "startedAt", "completedAt", "createdAt"]);
|
|
13
14
|
const { toColumn: toNodeRunCol, toField: toNodeRunField } = createColumnMapper({
|
|
@@ -30,29 +31,39 @@ export class BroadcastSqliteAdapter {
|
|
|
30
31
|
db;
|
|
31
32
|
runsTable;
|
|
32
33
|
nodesTable;
|
|
34
|
+
definitionsTable;
|
|
33
35
|
constructor(options = {}) {
|
|
34
36
|
const dbPath = options.dbPath ?? "station.db";
|
|
35
37
|
this.runsTable = validateTableName(options.tableName ?? "broadcast_runs");
|
|
36
38
|
this.nodesTable = validateTableName(`${this.runsTable}_nodes`);
|
|
39
|
+
this.definitionsTable = validateTableName(`${this.runsTable}_definitions`);
|
|
37
40
|
this.db = new Database(dbPath);
|
|
38
41
|
this.db.pragma("journal_mode = WAL");
|
|
39
42
|
this.db.pragma("foreign_keys = ON");
|
|
40
43
|
this.db.exec(`
|
|
41
44
|
CREATE TABLE IF NOT EXISTS ${this.runsTable} (
|
|
42
|
-
id
|
|
43
|
-
broadcast_name
|
|
44
|
-
input
|
|
45
|
-
status
|
|
46
|
-
failure_policy
|
|
47
|
-
timeout
|
|
48
|
-
interval
|
|
49
|
-
next_run_at
|
|
50
|
-
started_at
|
|
51
|
-
completed_at
|
|
52
|
-
created_at
|
|
53
|
-
error
|
|
45
|
+
id TEXT PRIMARY KEY,
|
|
46
|
+
broadcast_name TEXT NOT NULL,
|
|
47
|
+
input TEXT NOT NULL,
|
|
48
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
49
|
+
failure_policy TEXT NOT NULL DEFAULT 'fail-fast',
|
|
50
|
+
timeout INTEGER,
|
|
51
|
+
interval TEXT,
|
|
52
|
+
next_run_at TEXT,
|
|
53
|
+
started_at TEXT,
|
|
54
|
+
completed_at TEXT,
|
|
55
|
+
created_at TEXT NOT NULL,
|
|
56
|
+
error TEXT,
|
|
57
|
+
definition_snapshot TEXT
|
|
54
58
|
)
|
|
55
59
|
`);
|
|
60
|
+
// Idempotent migration: add column if it's missing (DB existed before this version).
|
|
61
|
+
const existingCols = this.db
|
|
62
|
+
.prepare(`PRAGMA table_info(${this.runsTable})`)
|
|
63
|
+
.all();
|
|
64
|
+
if (!existingCols.some((c) => c.name === "definition_snapshot")) {
|
|
65
|
+
this.db.exec(`ALTER TABLE ${this.runsTable} ADD COLUMN definition_snapshot TEXT`);
|
|
66
|
+
}
|
|
56
67
|
this.db.exec(`
|
|
57
68
|
CREATE INDEX IF NOT EXISTS idx_${this.runsTable}_status
|
|
58
69
|
ON ${this.runsTable} (status, next_run_at)
|
|
@@ -80,16 +91,32 @@ export class BroadcastSqliteAdapter {
|
|
|
80
91
|
this.db.exec(`
|
|
81
92
|
CREATE INDEX IF NOT EXISTS idx_${this.nodesTable}_run_id
|
|
82
93
|
ON ${this.nodesTable} (broadcast_run_id)
|
|
94
|
+
`);
|
|
95
|
+
// Dynamic broadcast definitions — name + version is the identity, full
|
|
96
|
+
// history is retained so deleted/older versions remain inspectable.
|
|
97
|
+
this.db.exec(`
|
|
98
|
+
CREATE TABLE IF NOT EXISTS ${this.definitionsTable} (
|
|
99
|
+
name TEXT NOT NULL,
|
|
100
|
+
version INTEGER NOT NULL,
|
|
101
|
+
spec TEXT NOT NULL,
|
|
102
|
+
failure_policy TEXT NOT NULL,
|
|
103
|
+
timeout INTEGER,
|
|
104
|
+
created_at TEXT NOT NULL,
|
|
105
|
+
updated_at TEXT NOT NULL,
|
|
106
|
+
created_by TEXT,
|
|
107
|
+
deleted_at TEXT,
|
|
108
|
+
PRIMARY KEY (name, version)
|
|
109
|
+
)
|
|
83
110
|
`);
|
|
84
111
|
}
|
|
85
112
|
async addBroadcastRun(run) {
|
|
86
113
|
this.db.prepare(`
|
|
87
114
|
INSERT INTO ${this.runsTable}
|
|
88
115
|
(id, broadcast_name, input, status, failure_policy, timeout, interval,
|
|
89
|
-
next_run_at, started_at, completed_at, created_at, error)
|
|
116
|
+
next_run_at, started_at, completed_at, created_at, error, definition_snapshot)
|
|
90
117
|
VALUES
|
|
91
118
|
(@id, @broadcast_name, @input, @status, @failure_policy, @timeout, @interval,
|
|
92
|
-
@next_run_at, @started_at, @completed_at, @created_at, @error)
|
|
119
|
+
@next_run_at, @started_at, @completed_at, @created_at, @error, @definition_snapshot)
|
|
93
120
|
`).run({
|
|
94
121
|
id: run.id,
|
|
95
122
|
broadcast_name: run.broadcastName,
|
|
@@ -103,6 +130,7 @@ export class BroadcastSqliteAdapter {
|
|
|
103
130
|
completed_at: dateToStr(run.completedAt),
|
|
104
131
|
created_at: dateToStr(run.createdAt),
|
|
105
132
|
error: run.error ?? null,
|
|
133
|
+
definition_snapshot: run.definitionSnapshot ?? null,
|
|
106
134
|
});
|
|
107
135
|
}
|
|
108
136
|
async getBroadcastRun(id) {
|
|
@@ -111,7 +139,7 @@ export class BroadcastSqliteAdapter {
|
|
|
111
139
|
}
|
|
112
140
|
static BROADCAST_RUN_PATCH_KEYS = new Set([
|
|
113
141
|
"input", "status", "failurePolicy", "timeout", "interval", "nextRunAt",
|
|
114
|
-
"startedAt", "completedAt", "error",
|
|
142
|
+
"startedAt", "completedAt", "error", "definitionSnapshot",
|
|
115
143
|
]);
|
|
116
144
|
async updateBroadcastRun(id, patch) {
|
|
117
145
|
const setClauses = [];
|
|
@@ -220,6 +248,99 @@ export class BroadcastSqliteAdapter {
|
|
|
220
248
|
const rows = this.db.prepare(`SELECT * FROM ${this.nodesTable} WHERE broadcast_run_id = ?`).all(broadcastRunId);
|
|
221
249
|
return rows.map(rowToNodeRun);
|
|
222
250
|
}
|
|
251
|
+
// ─── Dynamic broadcast definitions ───────────────────────────────
|
|
252
|
+
async saveDefinition(spec) {
|
|
253
|
+
// better-sqlite3 transactions serialize writers, so the MAX(version) read
|
|
254
|
+
// + INSERT can't be interleaved with another save — no PK collision is
|
|
255
|
+
// possible on the same DB. The transaction wrapper handles SAVEPOINTs
|
|
256
|
+
// for nested calls.
|
|
257
|
+
const txn = this.db.transaction((spec) => {
|
|
258
|
+
const row = this.db
|
|
259
|
+
.prepare(`SELECT MAX(version) AS v FROM ${this.definitionsTable} WHERE name = ?`)
|
|
260
|
+
.get(spec.name);
|
|
261
|
+
const nextVersion = (row?.v ?? 0) + 1;
|
|
262
|
+
const now = new Date();
|
|
263
|
+
const next = {
|
|
264
|
+
...spec,
|
|
265
|
+
version: nextVersion,
|
|
266
|
+
createdAt: spec.createdAt ?? now,
|
|
267
|
+
updatedAt: now,
|
|
268
|
+
deletedAt: undefined,
|
|
269
|
+
};
|
|
270
|
+
this.db
|
|
271
|
+
.prepare(`
|
|
272
|
+
INSERT INTO ${this.definitionsTable}
|
|
273
|
+
(name, version, spec, failure_policy, timeout, created_at, updated_at, created_by, deleted_at)
|
|
274
|
+
VALUES
|
|
275
|
+
(@name, @version, @spec, @failure_policy, @timeout, @created_at, @updated_at, @created_by, NULL)
|
|
276
|
+
`)
|
|
277
|
+
.run({
|
|
278
|
+
name: next.name,
|
|
279
|
+
version: next.version,
|
|
280
|
+
spec: JSON.stringify(next),
|
|
281
|
+
failure_policy: next.failurePolicy,
|
|
282
|
+
timeout: next.timeout ?? null,
|
|
283
|
+
created_at: dateToStr(next.createdAt),
|
|
284
|
+
updated_at: dateToStr(next.updatedAt),
|
|
285
|
+
created_by: next.createdBy ?? null,
|
|
286
|
+
});
|
|
287
|
+
return next;
|
|
288
|
+
});
|
|
289
|
+
return txn(spec);
|
|
290
|
+
}
|
|
291
|
+
async getDefinition(name, version) {
|
|
292
|
+
let row;
|
|
293
|
+
if (version !== undefined) {
|
|
294
|
+
row = this.db
|
|
295
|
+
.prepare(`SELECT spec FROM ${this.definitionsTable} WHERE name = ? AND version = ?`)
|
|
296
|
+
.get(name, version);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
row = this.db
|
|
300
|
+
.prepare(`SELECT spec FROM ${this.definitionsTable} WHERE name = ? ORDER BY version DESC LIMIT 1`)
|
|
301
|
+
.get(name);
|
|
302
|
+
}
|
|
303
|
+
return row ? deserializeSpec(row.spec) : null;
|
|
304
|
+
}
|
|
305
|
+
async listDefinitions() {
|
|
306
|
+
const rows = this.db
|
|
307
|
+
.prepare(`
|
|
308
|
+
SELECT spec FROM ${this.definitionsTable} d1
|
|
309
|
+
WHERE version = (
|
|
310
|
+
SELECT MAX(version) FROM ${this.definitionsTable} d2 WHERE d2.name = d1.name
|
|
311
|
+
)
|
|
312
|
+
AND deleted_at IS NULL
|
|
313
|
+
ORDER BY name ASC
|
|
314
|
+
`)
|
|
315
|
+
.all();
|
|
316
|
+
return rows.map((r) => deserializeSpec(r.spec));
|
|
317
|
+
}
|
|
318
|
+
async listDefinitionVersions(name) {
|
|
319
|
+
const rows = this.db
|
|
320
|
+
.prepare(`SELECT spec FROM ${this.definitionsTable} WHERE name = ? ORDER BY version DESC`)
|
|
321
|
+
.all(name);
|
|
322
|
+
return rows.map((r) => deserializeSpec(r.spec));
|
|
323
|
+
}
|
|
324
|
+
async deleteDefinition(name) {
|
|
325
|
+
const row = this.db
|
|
326
|
+
.prepare(`SELECT MAX(version) AS v FROM ${this.definitionsTable} WHERE name = ? AND deleted_at IS NULL`)
|
|
327
|
+
.get(name);
|
|
328
|
+
if (!row?.v)
|
|
329
|
+
return false;
|
|
330
|
+
const now = new Date().toISOString();
|
|
331
|
+
// Update the spec JSON with the deletion timestamp so consumers see it.
|
|
332
|
+
const existing = this.db
|
|
333
|
+
.prepare(`SELECT spec FROM ${this.definitionsTable} WHERE name = ? AND version = ?`)
|
|
334
|
+
.get(name, row.v);
|
|
335
|
+
if (!existing)
|
|
336
|
+
return false;
|
|
337
|
+
const spec = deserializeSpec(existing.spec);
|
|
338
|
+
spec.deletedAt = new Date(now);
|
|
339
|
+
this.db
|
|
340
|
+
.prepare(`UPDATE ${this.definitionsTable} SET deleted_at = ?, spec = ? WHERE name = ? AND version = ?`)
|
|
341
|
+
.run(now, JSON.stringify(spec), name, row.v);
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
223
344
|
generateId() {
|
|
224
345
|
return randomUUID();
|
|
225
346
|
}
|
|
@@ -236,4 +357,15 @@ export class BroadcastSqliteAdapter {
|
|
|
236
357
|
this.db.close();
|
|
237
358
|
}
|
|
238
359
|
}
|
|
360
|
+
function deserializeSpec(json) {
|
|
361
|
+
const obj = JSON.parse(json);
|
|
362
|
+
// Revive Date fields
|
|
363
|
+
if (obj.createdAt)
|
|
364
|
+
obj.createdAt = new Date(obj.createdAt);
|
|
365
|
+
if (obj.updatedAt)
|
|
366
|
+
obj.updatedAt = new Date(obj.updatedAt);
|
|
367
|
+
if (obj.deletedAt)
|
|
368
|
+
obj.deletedAt = new Date(obj.deletedAt);
|
|
369
|
+
return obj;
|
|
370
|
+
}
|
|
239
371
|
//# sourceMappingURL=broadcast.js.map
|
package/dist/broadcast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broadcast.js","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"broadcast.js","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Schedule, SchedulePatch, ScheduleAdapter, ScheduleListFilter } from "station-schedules";
|
|
2
|
+
export interface ScheduleSqliteAdapterOptions {
|
|
3
|
+
dbPath?: string;
|
|
4
|
+
tableName?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class ScheduleSqliteAdapter implements ScheduleAdapter {
|
|
7
|
+
private db;
|
|
8
|
+
private table;
|
|
9
|
+
constructor(options?: ScheduleSqliteAdapterOptions);
|
|
10
|
+
add(schedule: Schedule): Promise<void>;
|
|
11
|
+
get(id: string): Promise<Schedule | null>;
|
|
12
|
+
list(filter?: ScheduleListFilter): Promise<Schedule[]>;
|
|
13
|
+
update(id: string, patch: SchedulePatch): Promise<void>;
|
|
14
|
+
delete(id: string): Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Atomic claim — only update if the schedule's nextRunAt is still what we
|
|
17
|
+
* expected. Prevents two runners from double-firing the same schedule.
|
|
18
|
+
*/
|
|
19
|
+
claimDue(id: string, expectedNextRunAt: Date, newNextRunAt: Date): Promise<boolean>;
|
|
20
|
+
generateId(): string;
|
|
21
|
+
ping(): Promise<boolean>;
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=schedules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedules.d.ts","sourceRoot":"","sources":["../src/schedules.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,eAAe,EACf,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAG3B,MAAM,WAAW,4BAA4B;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,GAAE,4BAAiC;IA8BhD,GAAG,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BtC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAOzC,IAAI,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAqBtD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA6CvD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1C;;;OAGG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAczF,UAAU,IAAI,MAAM;IAId,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IASxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import Database from "better-sqlite3";
|
|
3
|
+
import { validateTableName, dateToStr, strToDate } from "./shared.js";
|
|
4
|
+
export class ScheduleSqliteAdapter {
|
|
5
|
+
db;
|
|
6
|
+
table;
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
const dbPath = options.dbPath ?? "station.db";
|
|
9
|
+
this.table = validateTableName(options.tableName ?? "schedules");
|
|
10
|
+
this.db = new Database(dbPath);
|
|
11
|
+
this.db.pragma("journal_mode = WAL");
|
|
12
|
+
this.db.exec(`
|
|
13
|
+
CREATE TABLE IF NOT EXISTS ${this.table} (
|
|
14
|
+
id TEXT PRIMARY KEY,
|
|
15
|
+
kind TEXT NOT NULL,
|
|
16
|
+
target TEXT NOT NULL,
|
|
17
|
+
interval TEXT NOT NULL,
|
|
18
|
+
input TEXT,
|
|
19
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
20
|
+
next_run_at TEXT NOT NULL,
|
|
21
|
+
last_run_at TEXT,
|
|
22
|
+
last_run_status TEXT,
|
|
23
|
+
last_run_id TEXT,
|
|
24
|
+
created_at TEXT NOT NULL,
|
|
25
|
+
updated_at TEXT NOT NULL,
|
|
26
|
+
created_by TEXT
|
|
27
|
+
)
|
|
28
|
+
`);
|
|
29
|
+
this.db.exec(`
|
|
30
|
+
CREATE INDEX IF NOT EXISTS idx_${this.table}_due
|
|
31
|
+
ON ${this.table} (enabled, next_run_at)
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
async add(schedule) {
|
|
35
|
+
this.db.prepare(`
|
|
36
|
+
INSERT INTO ${this.table}
|
|
37
|
+
(id, kind, target, interval, input, enabled, next_run_at,
|
|
38
|
+
last_run_at, last_run_status, last_run_id,
|
|
39
|
+
created_at, updated_at, created_by)
|
|
40
|
+
VALUES
|
|
41
|
+
(@id, @kind, @target, @interval, @input, @enabled, @next_run_at,
|
|
42
|
+
@last_run_at, @last_run_status, @last_run_id,
|
|
43
|
+
@created_at, @updated_at, @created_by)
|
|
44
|
+
`).run({
|
|
45
|
+
id: schedule.id,
|
|
46
|
+
kind: schedule.kind,
|
|
47
|
+
target: schedule.target,
|
|
48
|
+
interval: schedule.interval,
|
|
49
|
+
input: schedule.input !== undefined ? JSON.stringify(schedule.input) : null,
|
|
50
|
+
enabled: schedule.enabled ? 1 : 0,
|
|
51
|
+
next_run_at: dateToStr(schedule.nextRunAt),
|
|
52
|
+
last_run_at: dateToStr(schedule.lastRunAt),
|
|
53
|
+
last_run_status: schedule.lastRunStatus ?? null,
|
|
54
|
+
last_run_id: schedule.lastRunId ?? null,
|
|
55
|
+
created_at: dateToStr(schedule.createdAt),
|
|
56
|
+
updated_at: dateToStr(schedule.updatedAt),
|
|
57
|
+
created_by: schedule.createdBy ?? null,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async get(id) {
|
|
61
|
+
const row = this.db.prepare(`SELECT * FROM ${this.table} WHERE id = ?`).get(id);
|
|
62
|
+
return row ? rowToSchedule(row) : null;
|
|
63
|
+
}
|
|
64
|
+
async list(filter) {
|
|
65
|
+
const conditions = [];
|
|
66
|
+
const params = [];
|
|
67
|
+
if (filter?.kind) {
|
|
68
|
+
conditions.push("kind = ?");
|
|
69
|
+
params.push(filter.kind);
|
|
70
|
+
}
|
|
71
|
+
if (filter?.enabled !== undefined) {
|
|
72
|
+
conditions.push("enabled = ?");
|
|
73
|
+
params.push(filter.enabled ? 1 : 0);
|
|
74
|
+
}
|
|
75
|
+
if (filter?.due) {
|
|
76
|
+
conditions.push("enabled = 1");
|
|
77
|
+
conditions.push("next_run_at <= ?");
|
|
78
|
+
params.push(new Date().toISOString());
|
|
79
|
+
}
|
|
80
|
+
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
81
|
+
const rows = this.db.prepare(`SELECT * FROM ${this.table} ${where} ORDER BY next_run_at ASC`).all(...params);
|
|
82
|
+
return rows.map(rowToSchedule);
|
|
83
|
+
}
|
|
84
|
+
async update(id, patch) {
|
|
85
|
+
const setClauses = [];
|
|
86
|
+
const values = { id };
|
|
87
|
+
const map = {
|
|
88
|
+
interval: "interval",
|
|
89
|
+
input: "input",
|
|
90
|
+
enabled: "enabled",
|
|
91
|
+
nextRunAt: "next_run_at",
|
|
92
|
+
lastRunAt: "last_run_at",
|
|
93
|
+
lastRunStatus: "last_run_status",
|
|
94
|
+
lastRunId: "last_run_id",
|
|
95
|
+
updatedAt: "updated_at",
|
|
96
|
+
createdBy: "created_by",
|
|
97
|
+
};
|
|
98
|
+
let touched = false;
|
|
99
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
100
|
+
const col = map[key];
|
|
101
|
+
if (!col)
|
|
102
|
+
continue;
|
|
103
|
+
touched = true;
|
|
104
|
+
const param = `p_${col}`;
|
|
105
|
+
setClauses.push(`${col} = @${param}`);
|
|
106
|
+
if (value === undefined) {
|
|
107
|
+
values[param] = null;
|
|
108
|
+
}
|
|
109
|
+
else if (key === "input") {
|
|
110
|
+
values[param] = JSON.stringify(value);
|
|
111
|
+
}
|
|
112
|
+
else if (key === "enabled") {
|
|
113
|
+
values[param] = value ? 1 : 0;
|
|
114
|
+
}
|
|
115
|
+
else if (key === "nextRunAt" || key === "lastRunAt" || key === "updatedAt") {
|
|
116
|
+
values[param] = dateToStr(value);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
values[param] = value;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Always bump updated_at on update
|
|
123
|
+
if (touched && !("updatedAt" in patch)) {
|
|
124
|
+
setClauses.push("updated_at = @p_updated_at");
|
|
125
|
+
values.p_updated_at = new Date().toISOString();
|
|
126
|
+
}
|
|
127
|
+
if (setClauses.length === 0)
|
|
128
|
+
return;
|
|
129
|
+
this.db.prepare(`UPDATE ${this.table} SET ${setClauses.join(", ")} WHERE id = @id`).run(values);
|
|
130
|
+
}
|
|
131
|
+
async delete(id) {
|
|
132
|
+
const result = this.db.prepare(`DELETE FROM ${this.table} WHERE id = ?`).run(id);
|
|
133
|
+
return result.changes > 0;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Atomic claim — only update if the schedule's nextRunAt is still what we
|
|
137
|
+
* expected. Prevents two runners from double-firing the same schedule.
|
|
138
|
+
*/
|
|
139
|
+
async claimDue(id, expectedNextRunAt, newNextRunAt) {
|
|
140
|
+
const result = this.db
|
|
141
|
+
.prepare(`UPDATE ${this.table}
|
|
142
|
+
SET next_run_at = @new_next, updated_at = @now
|
|
143
|
+
WHERE id = @id AND next_run_at = @expected AND enabled = 1`)
|
|
144
|
+
.run({
|
|
145
|
+
id,
|
|
146
|
+
new_next: dateToStr(newNextRunAt),
|
|
147
|
+
expected: dateToStr(expectedNextRunAt),
|
|
148
|
+
now: new Date().toISOString(),
|
|
149
|
+
});
|
|
150
|
+
return result.changes > 0;
|
|
151
|
+
}
|
|
152
|
+
generateId() {
|
|
153
|
+
return randomUUID();
|
|
154
|
+
}
|
|
155
|
+
async ping() {
|
|
156
|
+
try {
|
|
157
|
+
this.db.prepare("SELECT 1").get();
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async close() {
|
|
165
|
+
this.db.close();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function rowToSchedule(row) {
|
|
169
|
+
return {
|
|
170
|
+
id: row.id,
|
|
171
|
+
kind: row.kind,
|
|
172
|
+
target: row.target,
|
|
173
|
+
interval: row.interval,
|
|
174
|
+
input: row.input ? JSON.parse(row.input) : undefined,
|
|
175
|
+
enabled: Boolean(row.enabled),
|
|
176
|
+
nextRunAt: strToDate(row.next_run_at),
|
|
177
|
+
lastRunAt: strToDate(row.last_run_at),
|
|
178
|
+
lastRunStatus: row.last_run_status ?? undefined,
|
|
179
|
+
lastRunId: row.last_run_id ?? undefined,
|
|
180
|
+
createdAt: strToDate(row.created_at),
|
|
181
|
+
updatedAt: strToDate(row.updated_at),
|
|
182
|
+
createdBy: row.created_by ?? undefined,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=schedules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedules.js","sourceRoot":"","sources":["../src/schedules.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "station-adapter-sqlite",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "SQLite adapter for station-signal using better-sqlite3",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"types": "./dist/broadcast.d.ts",
|
|
15
15
|
"import": "./dist/broadcast.js",
|
|
16
16
|
"default": "./dist/broadcast.js"
|
|
17
|
+
},
|
|
18
|
+
"./schedules": {
|
|
19
|
+
"types": "./dist/schedules.d.ts",
|
|
20
|
+
"import": "./dist/schedules.js",
|
|
21
|
+
"default": "./dist/schedules.js"
|
|
17
22
|
}
|
|
18
23
|
},
|
|
19
24
|
"files": [
|
|
@@ -32,11 +37,18 @@
|
|
|
32
37
|
"@types/better-sqlite3": "^7.6.13"
|
|
33
38
|
},
|
|
34
39
|
"peerDependencies": {
|
|
35
|
-
"station-signal": "1.0.
|
|
36
|
-
"station-broadcast": "1.0.
|
|
40
|
+
"station-signal": "1.0.4",
|
|
41
|
+
"station-broadcast": "1.0.4",
|
|
42
|
+
"station-schedules": "1.0.4"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"station-schedules": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
37
48
|
},
|
|
38
49
|
"scripts": {
|
|
39
50
|
"build": "tsc",
|
|
40
|
-
"typecheck": "tsc --noEmit"
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"test": "node --import tsx --test test/*.test.ts"
|
|
41
53
|
}
|
|
42
54
|
}
|
package/src/broadcast.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
BroadcastRunStatus,
|
|
8
8
|
BroadcastNodeRun,
|
|
9
9
|
BroadcastNodeRunPatch,
|
|
10
|
+
DynamicBroadcastSpec,
|
|
10
11
|
} from "station-broadcast";
|
|
11
12
|
|
|
12
13
|
import { validateTableName, dateToStr, createColumnMapper, rowToObject } from "./shared.js";
|
|
@@ -18,6 +19,7 @@ const { toColumn: toBroadcastRunCol, toField: toBroadcastRunField } = createColu
|
|
|
18
19
|
startedAt: "started_at",
|
|
19
20
|
completedAt: "completed_at",
|
|
20
21
|
createdAt: "created_at",
|
|
22
|
+
definitionSnapshot: "definition_snapshot",
|
|
21
23
|
});
|
|
22
24
|
const BROADCAST_RUN_DATE_FIELDS = new Set(["nextRunAt", "startedAt", "completedAt", "createdAt"]);
|
|
23
25
|
|
|
@@ -48,11 +50,13 @@ export class BroadcastSqliteAdapter implements BroadcastQueueAdapter {
|
|
|
48
50
|
private db: Database.Database;
|
|
49
51
|
private runsTable: string;
|
|
50
52
|
private nodesTable: string;
|
|
53
|
+
private definitionsTable: string;
|
|
51
54
|
|
|
52
55
|
constructor(options: BroadcastSqliteAdapterOptions = {}) {
|
|
53
56
|
const dbPath = options.dbPath ?? "station.db";
|
|
54
57
|
this.runsTable = validateTableName(options.tableName ?? "broadcast_runs");
|
|
55
58
|
this.nodesTable = validateTableName(`${this.runsTable}_nodes`);
|
|
59
|
+
this.definitionsTable = validateTableName(`${this.runsTable}_definitions`);
|
|
56
60
|
this.db = new Database(dbPath);
|
|
57
61
|
|
|
58
62
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -60,21 +64,30 @@ export class BroadcastSqliteAdapter implements BroadcastQueueAdapter {
|
|
|
60
64
|
|
|
61
65
|
this.db.exec(`
|
|
62
66
|
CREATE TABLE IF NOT EXISTS ${this.runsTable} (
|
|
63
|
-
id
|
|
64
|
-
broadcast_name
|
|
65
|
-
input
|
|
66
|
-
status
|
|
67
|
-
failure_policy
|
|
68
|
-
timeout
|
|
69
|
-
interval
|
|
70
|
-
next_run_at
|
|
71
|
-
started_at
|
|
72
|
-
completed_at
|
|
73
|
-
created_at
|
|
74
|
-
error
|
|
67
|
+
id TEXT PRIMARY KEY,
|
|
68
|
+
broadcast_name TEXT NOT NULL,
|
|
69
|
+
input TEXT NOT NULL,
|
|
70
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
71
|
+
failure_policy TEXT NOT NULL DEFAULT 'fail-fast',
|
|
72
|
+
timeout INTEGER,
|
|
73
|
+
interval TEXT,
|
|
74
|
+
next_run_at TEXT,
|
|
75
|
+
started_at TEXT,
|
|
76
|
+
completed_at TEXT,
|
|
77
|
+
created_at TEXT NOT NULL,
|
|
78
|
+
error TEXT,
|
|
79
|
+
definition_snapshot TEXT
|
|
75
80
|
)
|
|
76
81
|
`);
|
|
77
82
|
|
|
83
|
+
// Idempotent migration: add column if it's missing (DB existed before this version).
|
|
84
|
+
const existingCols = this.db
|
|
85
|
+
.prepare(`PRAGMA table_info(${this.runsTable})`)
|
|
86
|
+
.all() as Array<{ name: string }>;
|
|
87
|
+
if (!existingCols.some((c) => c.name === "definition_snapshot")) {
|
|
88
|
+
this.db.exec(`ALTER TABLE ${this.runsTable} ADD COLUMN definition_snapshot TEXT`);
|
|
89
|
+
}
|
|
90
|
+
|
|
78
91
|
this.db.exec(`
|
|
79
92
|
CREATE INDEX IF NOT EXISTS idx_${this.runsTable}_status
|
|
80
93
|
ON ${this.runsTable} (status, next_run_at)
|
|
@@ -106,16 +119,33 @@ export class BroadcastSqliteAdapter implements BroadcastQueueAdapter {
|
|
|
106
119
|
CREATE INDEX IF NOT EXISTS idx_${this.nodesTable}_run_id
|
|
107
120
|
ON ${this.nodesTable} (broadcast_run_id)
|
|
108
121
|
`);
|
|
122
|
+
|
|
123
|
+
// Dynamic broadcast definitions — name + version is the identity, full
|
|
124
|
+
// history is retained so deleted/older versions remain inspectable.
|
|
125
|
+
this.db.exec(`
|
|
126
|
+
CREATE TABLE IF NOT EXISTS ${this.definitionsTable} (
|
|
127
|
+
name TEXT NOT NULL,
|
|
128
|
+
version INTEGER NOT NULL,
|
|
129
|
+
spec TEXT NOT NULL,
|
|
130
|
+
failure_policy TEXT NOT NULL,
|
|
131
|
+
timeout INTEGER,
|
|
132
|
+
created_at TEXT NOT NULL,
|
|
133
|
+
updated_at TEXT NOT NULL,
|
|
134
|
+
created_by TEXT,
|
|
135
|
+
deleted_at TEXT,
|
|
136
|
+
PRIMARY KEY (name, version)
|
|
137
|
+
)
|
|
138
|
+
`);
|
|
109
139
|
}
|
|
110
140
|
|
|
111
141
|
async addBroadcastRun(run: BroadcastRun): Promise<void> {
|
|
112
142
|
this.db.prepare(`
|
|
113
143
|
INSERT INTO ${this.runsTable}
|
|
114
144
|
(id, broadcast_name, input, status, failure_policy, timeout, interval,
|
|
115
|
-
next_run_at, started_at, completed_at, created_at, error)
|
|
145
|
+
next_run_at, started_at, completed_at, created_at, error, definition_snapshot)
|
|
116
146
|
VALUES
|
|
117
147
|
(@id, @broadcast_name, @input, @status, @failure_policy, @timeout, @interval,
|
|
118
|
-
@next_run_at, @started_at, @completed_at, @created_at, @error)
|
|
148
|
+
@next_run_at, @started_at, @completed_at, @created_at, @error, @definition_snapshot)
|
|
119
149
|
`).run({
|
|
120
150
|
id: run.id,
|
|
121
151
|
broadcast_name: run.broadcastName,
|
|
@@ -129,6 +159,7 @@ export class BroadcastSqliteAdapter implements BroadcastQueueAdapter {
|
|
|
129
159
|
completed_at: dateToStr(run.completedAt),
|
|
130
160
|
created_at: dateToStr(run.createdAt),
|
|
131
161
|
error: run.error ?? null,
|
|
162
|
+
definition_snapshot: run.definitionSnapshot ?? null,
|
|
132
163
|
});
|
|
133
164
|
}
|
|
134
165
|
|
|
@@ -139,7 +170,7 @@ export class BroadcastSqliteAdapter implements BroadcastQueueAdapter {
|
|
|
139
170
|
|
|
140
171
|
private static readonly BROADCAST_RUN_PATCH_KEYS = new Set([
|
|
141
172
|
"input", "status", "failurePolicy", "timeout", "interval", "nextRunAt",
|
|
142
|
-
"startedAt", "completedAt", "error",
|
|
173
|
+
"startedAt", "completedAt", "error", "definitionSnapshot",
|
|
143
174
|
]);
|
|
144
175
|
|
|
145
176
|
async updateBroadcastRun(id: string, patch: BroadcastRunPatch): Promise<void> {
|
|
@@ -260,6 +291,102 @@ export class BroadcastSqliteAdapter implements BroadcastQueueAdapter {
|
|
|
260
291
|
return rows.map(rowToNodeRun);
|
|
261
292
|
}
|
|
262
293
|
|
|
294
|
+
// ─── Dynamic broadcast definitions ───────────────────────────────
|
|
295
|
+
|
|
296
|
+
async saveDefinition(spec: DynamicBroadcastSpec): Promise<DynamicBroadcastSpec> {
|
|
297
|
+
// better-sqlite3 transactions serialize writers, so the MAX(version) read
|
|
298
|
+
// + INSERT can't be interleaved with another save — no PK collision is
|
|
299
|
+
// possible on the same DB. The transaction wrapper handles SAVEPOINTs
|
|
300
|
+
// for nested calls.
|
|
301
|
+
const txn = this.db.transaction((spec: DynamicBroadcastSpec): DynamicBroadcastSpec => {
|
|
302
|
+
const row = this.db
|
|
303
|
+
.prepare(`SELECT MAX(version) AS v FROM ${this.definitionsTable} WHERE name = ?`)
|
|
304
|
+
.get(spec.name) as { v: number | null } | undefined;
|
|
305
|
+
const nextVersion = (row?.v ?? 0) + 1;
|
|
306
|
+
const now = new Date();
|
|
307
|
+
const next: DynamicBroadcastSpec = {
|
|
308
|
+
...spec,
|
|
309
|
+
version: nextVersion,
|
|
310
|
+
createdAt: spec.createdAt ?? now,
|
|
311
|
+
updatedAt: now,
|
|
312
|
+
deletedAt: undefined,
|
|
313
|
+
};
|
|
314
|
+
this.db
|
|
315
|
+
.prepare(`
|
|
316
|
+
INSERT INTO ${this.definitionsTable}
|
|
317
|
+
(name, version, spec, failure_policy, timeout, created_at, updated_at, created_by, deleted_at)
|
|
318
|
+
VALUES
|
|
319
|
+
(@name, @version, @spec, @failure_policy, @timeout, @created_at, @updated_at, @created_by, NULL)
|
|
320
|
+
`)
|
|
321
|
+
.run({
|
|
322
|
+
name: next.name,
|
|
323
|
+
version: next.version,
|
|
324
|
+
spec: JSON.stringify(next),
|
|
325
|
+
failure_policy: next.failurePolicy,
|
|
326
|
+
timeout: next.timeout ?? null,
|
|
327
|
+
created_at: dateToStr(next.createdAt),
|
|
328
|
+
updated_at: dateToStr(next.updatedAt),
|
|
329
|
+
created_by: next.createdBy ?? null,
|
|
330
|
+
});
|
|
331
|
+
return next;
|
|
332
|
+
});
|
|
333
|
+
return txn(spec);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async getDefinition(name: string, version?: number): Promise<DynamicBroadcastSpec | null> {
|
|
337
|
+
let row: { spec: string } | undefined;
|
|
338
|
+
if (version !== undefined) {
|
|
339
|
+
row = this.db
|
|
340
|
+
.prepare(`SELECT spec FROM ${this.definitionsTable} WHERE name = ? AND version = ?`)
|
|
341
|
+
.get(name, version) as { spec: string } | undefined;
|
|
342
|
+
} else {
|
|
343
|
+
row = this.db
|
|
344
|
+
.prepare(`SELECT spec FROM ${this.definitionsTable} WHERE name = ? ORDER BY version DESC LIMIT 1`)
|
|
345
|
+
.get(name) as { spec: string } | undefined;
|
|
346
|
+
}
|
|
347
|
+
return row ? deserializeSpec(row.spec) : null;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async listDefinitions(): Promise<DynamicBroadcastSpec[]> {
|
|
351
|
+
const rows = this.db
|
|
352
|
+
.prepare(`
|
|
353
|
+
SELECT spec FROM ${this.definitionsTable} d1
|
|
354
|
+
WHERE version = (
|
|
355
|
+
SELECT MAX(version) FROM ${this.definitionsTable} d2 WHERE d2.name = d1.name
|
|
356
|
+
)
|
|
357
|
+
AND deleted_at IS NULL
|
|
358
|
+
ORDER BY name ASC
|
|
359
|
+
`)
|
|
360
|
+
.all() as Array<{ spec: string }>;
|
|
361
|
+
return rows.map((r) => deserializeSpec(r.spec));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async listDefinitionVersions(name: string): Promise<DynamicBroadcastSpec[]> {
|
|
365
|
+
const rows = this.db
|
|
366
|
+
.prepare(`SELECT spec FROM ${this.definitionsTable} WHERE name = ? ORDER BY version DESC`)
|
|
367
|
+
.all(name) as Array<{ spec: string }>;
|
|
368
|
+
return rows.map((r) => deserializeSpec(r.spec));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async deleteDefinition(name: string): Promise<boolean> {
|
|
372
|
+
const row = this.db
|
|
373
|
+
.prepare(`SELECT MAX(version) AS v FROM ${this.definitionsTable} WHERE name = ? AND deleted_at IS NULL`)
|
|
374
|
+
.get(name) as { v: number | null } | undefined;
|
|
375
|
+
if (!row?.v) return false;
|
|
376
|
+
const now = new Date().toISOString();
|
|
377
|
+
// Update the spec JSON with the deletion timestamp so consumers see it.
|
|
378
|
+
const existing = this.db
|
|
379
|
+
.prepare(`SELECT spec FROM ${this.definitionsTable} WHERE name = ? AND version = ?`)
|
|
380
|
+
.get(name, row.v) as { spec: string } | undefined;
|
|
381
|
+
if (!existing) return false;
|
|
382
|
+
const spec = deserializeSpec(existing.spec);
|
|
383
|
+
spec.deletedAt = new Date(now);
|
|
384
|
+
this.db
|
|
385
|
+
.prepare(`UPDATE ${this.definitionsTable} SET deleted_at = ?, spec = ? WHERE name = ? AND version = ?`)
|
|
386
|
+
.run(now, JSON.stringify(spec), name, row.v);
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
|
|
263
390
|
generateId(): string {
|
|
264
391
|
return randomUUID();
|
|
265
392
|
}
|
|
@@ -277,3 +404,12 @@ export class BroadcastSqliteAdapter implements BroadcastQueueAdapter {
|
|
|
277
404
|
this.db.close();
|
|
278
405
|
}
|
|
279
406
|
}
|
|
407
|
+
|
|
408
|
+
function deserializeSpec(json: string): DynamicBroadcastSpec {
|
|
409
|
+
const obj = JSON.parse(json) as DynamicBroadcastSpec;
|
|
410
|
+
// Revive Date fields
|
|
411
|
+
if (obj.createdAt) obj.createdAt = new Date(obj.createdAt);
|
|
412
|
+
if (obj.updatedAt) obj.updatedAt = new Date(obj.updatedAt);
|
|
413
|
+
if (obj.deletedAt) obj.deletedAt = new Date(obj.deletedAt);
|
|
414
|
+
return obj;
|
|
415
|
+
}
|
package/src/schedules.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import Database from "better-sqlite3";
|
|
3
|
+
import type {
|
|
4
|
+
Schedule,
|
|
5
|
+
SchedulePatch,
|
|
6
|
+
ScheduleAdapter,
|
|
7
|
+
ScheduleListFilter,
|
|
8
|
+
} from "station-schedules";
|
|
9
|
+
import { validateTableName, dateToStr, strToDate } from "./shared.js";
|
|
10
|
+
|
|
11
|
+
export interface ScheduleSqliteAdapterOptions {
|
|
12
|
+
dbPath?: string;
|
|
13
|
+
tableName?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class ScheduleSqliteAdapter implements ScheduleAdapter {
|
|
17
|
+
private db: Database.Database;
|
|
18
|
+
private table: string;
|
|
19
|
+
|
|
20
|
+
constructor(options: ScheduleSqliteAdapterOptions = {}) {
|
|
21
|
+
const dbPath = options.dbPath ?? "station.db";
|
|
22
|
+
this.table = validateTableName(options.tableName ?? "schedules");
|
|
23
|
+
this.db = new Database(dbPath);
|
|
24
|
+
this.db.pragma("journal_mode = WAL");
|
|
25
|
+
|
|
26
|
+
this.db.exec(`
|
|
27
|
+
CREATE TABLE IF NOT EXISTS ${this.table} (
|
|
28
|
+
id TEXT PRIMARY KEY,
|
|
29
|
+
kind TEXT NOT NULL,
|
|
30
|
+
target TEXT NOT NULL,
|
|
31
|
+
interval TEXT NOT NULL,
|
|
32
|
+
input TEXT,
|
|
33
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
34
|
+
next_run_at TEXT NOT NULL,
|
|
35
|
+
last_run_at TEXT,
|
|
36
|
+
last_run_status TEXT,
|
|
37
|
+
last_run_id TEXT,
|
|
38
|
+
created_at TEXT NOT NULL,
|
|
39
|
+
updated_at TEXT NOT NULL,
|
|
40
|
+
created_by TEXT
|
|
41
|
+
)
|
|
42
|
+
`);
|
|
43
|
+
|
|
44
|
+
this.db.exec(`
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_${this.table}_due
|
|
46
|
+
ON ${this.table} (enabled, next_run_at)
|
|
47
|
+
`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async add(schedule: Schedule): Promise<void> {
|
|
51
|
+
this.db.prepare(`
|
|
52
|
+
INSERT INTO ${this.table}
|
|
53
|
+
(id, kind, target, interval, input, enabled, next_run_at,
|
|
54
|
+
last_run_at, last_run_status, last_run_id,
|
|
55
|
+
created_at, updated_at, created_by)
|
|
56
|
+
VALUES
|
|
57
|
+
(@id, @kind, @target, @interval, @input, @enabled, @next_run_at,
|
|
58
|
+
@last_run_at, @last_run_status, @last_run_id,
|
|
59
|
+
@created_at, @updated_at, @created_by)
|
|
60
|
+
`).run({
|
|
61
|
+
id: schedule.id,
|
|
62
|
+
kind: schedule.kind,
|
|
63
|
+
target: schedule.target,
|
|
64
|
+
interval: schedule.interval,
|
|
65
|
+
input: schedule.input !== undefined ? JSON.stringify(schedule.input) : null,
|
|
66
|
+
enabled: schedule.enabled ? 1 : 0,
|
|
67
|
+
next_run_at: dateToStr(schedule.nextRunAt),
|
|
68
|
+
last_run_at: dateToStr(schedule.lastRunAt),
|
|
69
|
+
last_run_status: schedule.lastRunStatus ?? null,
|
|
70
|
+
last_run_id: schedule.lastRunId ?? null,
|
|
71
|
+
created_at: dateToStr(schedule.createdAt),
|
|
72
|
+
updated_at: dateToStr(schedule.updatedAt),
|
|
73
|
+
created_by: schedule.createdBy ?? null,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async get(id: string): Promise<Schedule | null> {
|
|
78
|
+
const row = this.db.prepare(`SELECT * FROM ${this.table} WHERE id = ?`).get(id) as
|
|
79
|
+
| Record<string, unknown>
|
|
80
|
+
| undefined;
|
|
81
|
+
return row ? rowToSchedule(row) : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async list(filter?: ScheduleListFilter): Promise<Schedule[]> {
|
|
85
|
+
const conditions: string[] = [];
|
|
86
|
+
const params: unknown[] = [];
|
|
87
|
+
if (filter?.kind) {
|
|
88
|
+
conditions.push("kind = ?");
|
|
89
|
+
params.push(filter.kind);
|
|
90
|
+
}
|
|
91
|
+
if (filter?.enabled !== undefined) {
|
|
92
|
+
conditions.push("enabled = ?");
|
|
93
|
+
params.push(filter.enabled ? 1 : 0);
|
|
94
|
+
}
|
|
95
|
+
if (filter?.due) {
|
|
96
|
+
conditions.push("enabled = 1");
|
|
97
|
+
conditions.push("next_run_at <= ?");
|
|
98
|
+
params.push(new Date().toISOString());
|
|
99
|
+
}
|
|
100
|
+
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
101
|
+
const rows = this.db.prepare(`SELECT * FROM ${this.table} ${where} ORDER BY next_run_at ASC`).all(...params) as Record<string, unknown>[];
|
|
102
|
+
return rows.map(rowToSchedule);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async update(id: string, patch: SchedulePatch): Promise<void> {
|
|
106
|
+
const setClauses: string[] = [];
|
|
107
|
+
const values: Record<string, unknown> = { id };
|
|
108
|
+
const map: Record<string, string> = {
|
|
109
|
+
interval: "interval",
|
|
110
|
+
input: "input",
|
|
111
|
+
enabled: "enabled",
|
|
112
|
+
nextRunAt: "next_run_at",
|
|
113
|
+
lastRunAt: "last_run_at",
|
|
114
|
+
lastRunStatus: "last_run_status",
|
|
115
|
+
lastRunId: "last_run_id",
|
|
116
|
+
updatedAt: "updated_at",
|
|
117
|
+
createdBy: "created_by",
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
let touched = false;
|
|
121
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
122
|
+
const col = map[key];
|
|
123
|
+
if (!col) continue;
|
|
124
|
+
touched = true;
|
|
125
|
+
const param = `p_${col}`;
|
|
126
|
+
setClauses.push(`${col} = @${param}`);
|
|
127
|
+
if (value === undefined) {
|
|
128
|
+
values[param] = null;
|
|
129
|
+
} else if (key === "input") {
|
|
130
|
+
values[param] = JSON.stringify(value);
|
|
131
|
+
} else if (key === "enabled") {
|
|
132
|
+
values[param] = value ? 1 : 0;
|
|
133
|
+
} else if (key === "nextRunAt" || key === "lastRunAt" || key === "updatedAt") {
|
|
134
|
+
values[param] = dateToStr(value);
|
|
135
|
+
} else {
|
|
136
|
+
values[param] = value;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Always bump updated_at on update
|
|
141
|
+
if (touched && !("updatedAt" in patch)) {
|
|
142
|
+
setClauses.push("updated_at = @p_updated_at");
|
|
143
|
+
values.p_updated_at = new Date().toISOString();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (setClauses.length === 0) return;
|
|
147
|
+
this.db.prepare(`UPDATE ${this.table} SET ${setClauses.join(", ")} WHERE id = @id`).run(values);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async delete(id: string): Promise<boolean> {
|
|
151
|
+
const result = this.db.prepare(`DELETE FROM ${this.table} WHERE id = ?`).run(id);
|
|
152
|
+
return result.changes > 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Atomic claim — only update if the schedule's nextRunAt is still what we
|
|
157
|
+
* expected. Prevents two runners from double-firing the same schedule.
|
|
158
|
+
*/
|
|
159
|
+
async claimDue(id: string, expectedNextRunAt: Date, newNextRunAt: Date): Promise<boolean> {
|
|
160
|
+
const result = this.db
|
|
161
|
+
.prepare(`UPDATE ${this.table}
|
|
162
|
+
SET next_run_at = @new_next, updated_at = @now
|
|
163
|
+
WHERE id = @id AND next_run_at = @expected AND enabled = 1`)
|
|
164
|
+
.run({
|
|
165
|
+
id,
|
|
166
|
+
new_next: dateToStr(newNextRunAt),
|
|
167
|
+
expected: dateToStr(expectedNextRunAt),
|
|
168
|
+
now: new Date().toISOString(),
|
|
169
|
+
});
|
|
170
|
+
return result.changes > 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
generateId(): string {
|
|
174
|
+
return randomUUID();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async ping(): Promise<boolean> {
|
|
178
|
+
try {
|
|
179
|
+
this.db.prepare("SELECT 1").get();
|
|
180
|
+
return true;
|
|
181
|
+
} catch {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async close(): Promise<void> {
|
|
187
|
+
this.db.close();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function rowToSchedule(row: Record<string, unknown>): Schedule {
|
|
192
|
+
return {
|
|
193
|
+
id: row.id as string,
|
|
194
|
+
kind: row.kind as Schedule["kind"],
|
|
195
|
+
target: row.target as string,
|
|
196
|
+
interval: row.interval as string,
|
|
197
|
+
input: row.input ? JSON.parse(row.input as string) : undefined,
|
|
198
|
+
enabled: Boolean(row.enabled),
|
|
199
|
+
nextRunAt: strToDate(row.next_run_at)!,
|
|
200
|
+
lastRunAt: strToDate(row.last_run_at),
|
|
201
|
+
lastRunStatus: (row.last_run_status as string | null) ?? undefined,
|
|
202
|
+
lastRunId: (row.last_run_id as string | null) ?? undefined,
|
|
203
|
+
createdAt: strToDate(row.created_at)!,
|
|
204
|
+
updatedAt: strToDate(row.updated_at)!,
|
|
205
|
+
createdBy: (row.created_by as string | null) ?? undefined,
|
|
206
|
+
};
|
|
207
|
+
}
|