@smithers-orchestrator/engine 0.20.1 → 0.20.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/package.json +14 -15
- package/src/approvals.js +6 -0
- package/src/child-workflow.js +7 -0
- package/src/effect/builder.js +48 -13
- package/src/effect/compute-task-bridge.js +42 -47
- package/src/effect/deferred-state-bridge.js +37 -0
- package/src/effect/diff-bundle.js +24 -12
- package/src/effect/durable-deferred-bridge.js +16 -4
- package/src/effect/http-runner.js +2 -86
- package/src/effect/single-runner.js +26 -1
- package/src/effect/sql-message-storage.js +2 -817
- package/src/effect/static-task-bridge.js +6 -2
- package/src/effect/workflow-bridge.js +15 -4
- package/src/effect/workflow-make-bridge.js +10 -3
- package/src/engine.js +85 -8
- package/src/hot/HotWorkflowController.js +37 -23
- package/src/hot/overlay.js +42 -24
- package/src/human-requests.js +3 -0
|
@@ -1,817 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { SqlError } from "@effect/sql/SqlError";
|
|
4
|
-
import * as Statement from "@effect/sql/Statement";
|
|
5
|
-
import { Database } from "bun:sqlite";
|
|
6
|
-
import { Context, Effect, Layer, ManagedRuntime, Scope } from "effect";
|
|
7
|
-
import { camelToSnake } from "@smithers-orchestrator/db/utils/camelToSnake";
|
|
8
|
-
/** @typedef {import("drizzle-orm/bun-sqlite").BunSQLiteDatabase} BunSQLiteDatabase */
|
|
9
|
-
/** @typedef {import("./SqlMessageStorageEventHistoryQuery.ts").SqlMessageStorageEventHistoryQuery} SqlMessageStorageEventHistoryQuery */
|
|
10
|
-
/**
|
|
11
|
-
* @typedef {string | number | bigint | boolean | Uint8Array | null | undefined} SqliteParam
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const ATTR_DB_SYSTEM_NAME = "db.system.name";
|
|
15
|
-
const CREATE_TABLE_STATEMENTS = [
|
|
16
|
-
`CREATE TABLE IF NOT EXISTS _smithers_runs (
|
|
17
|
-
run_id TEXT PRIMARY KEY,
|
|
18
|
-
parent_run_id TEXT,
|
|
19
|
-
workflow_name TEXT NOT NULL,
|
|
20
|
-
workflow_path TEXT,
|
|
21
|
-
workflow_hash TEXT,
|
|
22
|
-
status TEXT NOT NULL,
|
|
23
|
-
created_at_ms INTEGER NOT NULL,
|
|
24
|
-
started_at_ms INTEGER,
|
|
25
|
-
finished_at_ms INTEGER,
|
|
26
|
-
heartbeat_at_ms INTEGER,
|
|
27
|
-
runtime_owner_id TEXT,
|
|
28
|
-
cancel_requested_at_ms INTEGER,
|
|
29
|
-
hijack_requested_at_ms INTEGER,
|
|
30
|
-
hijack_target TEXT,
|
|
31
|
-
vcs_type TEXT,
|
|
32
|
-
vcs_root TEXT,
|
|
33
|
-
vcs_revision TEXT,
|
|
34
|
-
error_json TEXT,
|
|
35
|
-
config_json TEXT
|
|
36
|
-
)`,
|
|
37
|
-
`CREATE INDEX IF NOT EXISTS _smithers_runs_status_heartbeat_idx
|
|
38
|
-
ON _smithers_runs (status, heartbeat_at_ms)`,
|
|
39
|
-
`CREATE TABLE IF NOT EXISTS _smithers_nodes (
|
|
40
|
-
run_id TEXT NOT NULL,
|
|
41
|
-
node_id TEXT NOT NULL,
|
|
42
|
-
iteration INTEGER NOT NULL DEFAULT 0,
|
|
43
|
-
state TEXT NOT NULL,
|
|
44
|
-
last_attempt INTEGER,
|
|
45
|
-
updated_at_ms INTEGER NOT NULL,
|
|
46
|
-
output_table TEXT NOT NULL,
|
|
47
|
-
label TEXT,
|
|
48
|
-
PRIMARY KEY (run_id, node_id, iteration)
|
|
49
|
-
)`,
|
|
50
|
-
`CREATE TABLE IF NOT EXISTS _smithers_attempts (
|
|
51
|
-
run_id TEXT NOT NULL,
|
|
52
|
-
node_id TEXT NOT NULL,
|
|
53
|
-
iteration INTEGER NOT NULL DEFAULT 0,
|
|
54
|
-
attempt INTEGER NOT NULL,
|
|
55
|
-
state TEXT NOT NULL,
|
|
56
|
-
started_at_ms INTEGER NOT NULL,
|
|
57
|
-
finished_at_ms INTEGER,
|
|
58
|
-
heartbeat_at_ms INTEGER,
|
|
59
|
-
heartbeat_data_json TEXT,
|
|
60
|
-
error_json TEXT,
|
|
61
|
-
jj_pointer TEXT,
|
|
62
|
-
response_text TEXT,
|
|
63
|
-
jj_cwd TEXT,
|
|
64
|
-
cached INTEGER DEFAULT 0,
|
|
65
|
-
meta_json TEXT,
|
|
66
|
-
PRIMARY KEY (run_id, node_id, iteration, attempt)
|
|
67
|
-
)`,
|
|
68
|
-
`CREATE TABLE IF NOT EXISTS _smithers_frames (
|
|
69
|
-
run_id TEXT NOT NULL,
|
|
70
|
-
frame_no INTEGER NOT NULL,
|
|
71
|
-
created_at_ms INTEGER NOT NULL,
|
|
72
|
-
xml_json TEXT NOT NULL,
|
|
73
|
-
xml_hash TEXT NOT NULL,
|
|
74
|
-
encoding TEXT NOT NULL DEFAULT 'full',
|
|
75
|
-
mounted_task_ids_json TEXT,
|
|
76
|
-
task_index_json TEXT,
|
|
77
|
-
note TEXT,
|
|
78
|
-
PRIMARY KEY (run_id, frame_no)
|
|
79
|
-
)`,
|
|
80
|
-
`CREATE TABLE IF NOT EXISTS _smithers_approvals (
|
|
81
|
-
run_id TEXT NOT NULL,
|
|
82
|
-
node_id TEXT NOT NULL,
|
|
83
|
-
iteration INTEGER NOT NULL DEFAULT 0,
|
|
84
|
-
status TEXT NOT NULL,
|
|
85
|
-
requested_at_ms INTEGER,
|
|
86
|
-
decided_at_ms INTEGER,
|
|
87
|
-
note TEXT,
|
|
88
|
-
decided_by TEXT,
|
|
89
|
-
request_json TEXT,
|
|
90
|
-
decision_json TEXT,
|
|
91
|
-
auto_approved INTEGER NOT NULL DEFAULT 0,
|
|
92
|
-
PRIMARY KEY (run_id, node_id, iteration)
|
|
93
|
-
)`,
|
|
94
|
-
`CREATE TABLE IF NOT EXISTS _smithers_human_requests (
|
|
95
|
-
request_id TEXT PRIMARY KEY,
|
|
96
|
-
run_id TEXT NOT NULL,
|
|
97
|
-
node_id TEXT NOT NULL,
|
|
98
|
-
iteration INTEGER NOT NULL DEFAULT 0,
|
|
99
|
-
kind TEXT NOT NULL,
|
|
100
|
-
status TEXT NOT NULL,
|
|
101
|
-
prompt TEXT NOT NULL,
|
|
102
|
-
schema_json TEXT,
|
|
103
|
-
options_json TEXT,
|
|
104
|
-
response_json TEXT,
|
|
105
|
-
requested_at_ms INTEGER NOT NULL,
|
|
106
|
-
answered_at_ms INTEGER,
|
|
107
|
-
answered_by TEXT,
|
|
108
|
-
timeout_at_ms INTEGER
|
|
109
|
-
)`,
|
|
110
|
-
`CREATE TABLE IF NOT EXISTS _smithers_alerts (
|
|
111
|
-
alert_id TEXT PRIMARY KEY,
|
|
112
|
-
run_id TEXT,
|
|
113
|
-
policy_name TEXT NOT NULL,
|
|
114
|
-
severity TEXT NOT NULL,
|
|
115
|
-
status TEXT NOT NULL,
|
|
116
|
-
fired_at_ms INTEGER NOT NULL,
|
|
117
|
-
resolved_at_ms INTEGER,
|
|
118
|
-
acknowledged_at_ms INTEGER,
|
|
119
|
-
message TEXT NOT NULL,
|
|
120
|
-
details_json TEXT,
|
|
121
|
-
fingerprint TEXT,
|
|
122
|
-
node_id TEXT,
|
|
123
|
-
iteration INTEGER,
|
|
124
|
-
owner TEXT,
|
|
125
|
-
runbook TEXT,
|
|
126
|
-
labels_json TEXT,
|
|
127
|
-
reaction_json TEXT,
|
|
128
|
-
source_event_type TEXT,
|
|
129
|
-
first_fired_at_ms INTEGER,
|
|
130
|
-
last_fired_at_ms INTEGER,
|
|
131
|
-
occurrence_count INTEGER DEFAULT 1,
|
|
132
|
-
silenced_until_ms INTEGER,
|
|
133
|
-
acknowledged_by TEXT,
|
|
134
|
-
resolved_by TEXT
|
|
135
|
-
)`,
|
|
136
|
-
`CREATE TABLE IF NOT EXISTS _smithers_signals (
|
|
137
|
-
run_id TEXT NOT NULL,
|
|
138
|
-
seq INTEGER NOT NULL,
|
|
139
|
-
signal_name TEXT NOT NULL,
|
|
140
|
-
correlation_id TEXT,
|
|
141
|
-
payload_json TEXT NOT NULL,
|
|
142
|
-
received_at_ms INTEGER NOT NULL,
|
|
143
|
-
received_by TEXT,
|
|
144
|
-
PRIMARY KEY (run_id, seq)
|
|
145
|
-
)`,
|
|
146
|
-
`CREATE INDEX IF NOT EXISTS _smithers_signals_lookup_idx
|
|
147
|
-
ON _smithers_signals (run_id, signal_name, correlation_id, received_at_ms)`,
|
|
148
|
-
`CREATE TABLE IF NOT EXISTS _smithers_cache (
|
|
149
|
-
cache_key TEXT PRIMARY KEY,
|
|
150
|
-
created_at_ms INTEGER NOT NULL,
|
|
151
|
-
workflow_name TEXT NOT NULL,
|
|
152
|
-
node_id TEXT NOT NULL,
|
|
153
|
-
output_table TEXT NOT NULL,
|
|
154
|
-
schema_sig TEXT NOT NULL,
|
|
155
|
-
agent_sig TEXT,
|
|
156
|
-
tools_sig TEXT,
|
|
157
|
-
jj_pointer TEXT,
|
|
158
|
-
payload_json TEXT NOT NULL
|
|
159
|
-
)`,
|
|
160
|
-
`CREATE TABLE IF NOT EXISTS _smithers_sandboxes (
|
|
161
|
-
run_id TEXT NOT NULL,
|
|
162
|
-
sandbox_id TEXT NOT NULL,
|
|
163
|
-
runtime TEXT NOT NULL DEFAULT 'bubblewrap',
|
|
164
|
-
remote_run_id TEXT,
|
|
165
|
-
workspace_id TEXT,
|
|
166
|
-
container_id TEXT,
|
|
167
|
-
config_json TEXT NOT NULL,
|
|
168
|
-
status TEXT NOT NULL DEFAULT 'pending',
|
|
169
|
-
shipped_at_ms INTEGER,
|
|
170
|
-
completed_at_ms INTEGER,
|
|
171
|
-
bundle_path TEXT,
|
|
172
|
-
PRIMARY KEY (run_id, sandbox_id)
|
|
173
|
-
)`,
|
|
174
|
-
`CREATE TABLE IF NOT EXISTS _smithers_tool_calls (
|
|
175
|
-
run_id TEXT NOT NULL,
|
|
176
|
-
node_id TEXT NOT NULL,
|
|
177
|
-
iteration INTEGER NOT NULL DEFAULT 0,
|
|
178
|
-
attempt INTEGER NOT NULL,
|
|
179
|
-
seq INTEGER NOT NULL,
|
|
180
|
-
tool_name TEXT NOT NULL,
|
|
181
|
-
input_json TEXT,
|
|
182
|
-
output_json TEXT,
|
|
183
|
-
started_at_ms INTEGER NOT NULL,
|
|
184
|
-
finished_at_ms INTEGER,
|
|
185
|
-
status TEXT NOT NULL,
|
|
186
|
-
error_json TEXT,
|
|
187
|
-
PRIMARY KEY (run_id, node_id, iteration, attempt, seq)
|
|
188
|
-
)`,
|
|
189
|
-
`CREATE TABLE IF NOT EXISTS _smithers_events (
|
|
190
|
-
run_id TEXT NOT NULL,
|
|
191
|
-
seq INTEGER NOT NULL,
|
|
192
|
-
timestamp_ms INTEGER NOT NULL,
|
|
193
|
-
type TEXT NOT NULL,
|
|
194
|
-
payload_json TEXT NOT NULL,
|
|
195
|
-
PRIMARY KEY (run_id, seq)
|
|
196
|
-
)`,
|
|
197
|
-
`CREATE TABLE IF NOT EXISTS _smithers_ralph (
|
|
198
|
-
run_id TEXT NOT NULL,
|
|
199
|
-
ralph_id TEXT NOT NULL,
|
|
200
|
-
iteration INTEGER NOT NULL DEFAULT 0,
|
|
201
|
-
done INTEGER NOT NULL DEFAULT 0,
|
|
202
|
-
updated_at_ms INTEGER NOT NULL,
|
|
203
|
-
PRIMARY KEY (run_id, ralph_id)
|
|
204
|
-
)`,
|
|
205
|
-
`CREATE TABLE IF NOT EXISTS _smithers_cron (
|
|
206
|
-
cron_id TEXT PRIMARY KEY,
|
|
207
|
-
pattern TEXT NOT NULL,
|
|
208
|
-
workflow_path TEXT NOT NULL,
|
|
209
|
-
enabled INTEGER DEFAULT 1,
|
|
210
|
-
created_at_ms INTEGER NOT NULL,
|
|
211
|
-
last_run_at_ms INTEGER,
|
|
212
|
-
next_run_at_ms INTEGER,
|
|
213
|
-
error_json TEXT
|
|
214
|
-
)`,
|
|
215
|
-
`CREATE TABLE IF NOT EXISTS _smithers_snapshots (
|
|
216
|
-
run_id TEXT NOT NULL,
|
|
217
|
-
frame_no INTEGER NOT NULL,
|
|
218
|
-
nodes_json TEXT NOT NULL,
|
|
219
|
-
outputs_json TEXT NOT NULL,
|
|
220
|
-
ralph_json TEXT NOT NULL,
|
|
221
|
-
input_json TEXT NOT NULL,
|
|
222
|
-
vcs_pointer TEXT,
|
|
223
|
-
workflow_hash TEXT,
|
|
224
|
-
content_hash TEXT NOT NULL,
|
|
225
|
-
created_at_ms INTEGER NOT NULL,
|
|
226
|
-
PRIMARY KEY (run_id, frame_no)
|
|
227
|
-
)`,
|
|
228
|
-
`CREATE TABLE IF NOT EXISTS _smithers_branches (
|
|
229
|
-
run_id TEXT PRIMARY KEY,
|
|
230
|
-
parent_run_id TEXT NOT NULL,
|
|
231
|
-
parent_frame_no INTEGER NOT NULL,
|
|
232
|
-
branch_label TEXT,
|
|
233
|
-
fork_description TEXT,
|
|
234
|
-
created_at_ms INTEGER NOT NULL
|
|
235
|
-
)`,
|
|
236
|
-
`CREATE TABLE IF NOT EXISTS _smithers_vcs_tags (
|
|
237
|
-
run_id TEXT NOT NULL,
|
|
238
|
-
frame_no INTEGER NOT NULL,
|
|
239
|
-
vcs_type TEXT NOT NULL,
|
|
240
|
-
vcs_pointer TEXT NOT NULL,
|
|
241
|
-
vcs_root TEXT,
|
|
242
|
-
jj_operation_id TEXT,
|
|
243
|
-
created_at_ms INTEGER NOT NULL,
|
|
244
|
-
PRIMARY KEY (run_id, frame_no)
|
|
245
|
-
)`,
|
|
246
|
-
`CREATE TABLE IF NOT EXISTS _smithers_vectors (
|
|
247
|
-
id TEXT PRIMARY KEY,
|
|
248
|
-
namespace TEXT NOT NULL,
|
|
249
|
-
content TEXT NOT NULL,
|
|
250
|
-
embedding BLOB NOT NULL,
|
|
251
|
-
dimensions INTEGER NOT NULL,
|
|
252
|
-
metadata_json TEXT,
|
|
253
|
-
document_id TEXT,
|
|
254
|
-
chunk_index INTEGER,
|
|
255
|
-
created_at_ms INTEGER NOT NULL
|
|
256
|
-
)`,
|
|
257
|
-
`CREATE TABLE IF NOT EXISTS _smithers_scorers (
|
|
258
|
-
id TEXT PRIMARY KEY,
|
|
259
|
-
run_id TEXT NOT NULL,
|
|
260
|
-
node_id TEXT NOT NULL,
|
|
261
|
-
iteration INTEGER NOT NULL DEFAULT 0,
|
|
262
|
-
attempt INTEGER NOT NULL DEFAULT 0,
|
|
263
|
-
scorer_id TEXT NOT NULL,
|
|
264
|
-
scorer_name TEXT NOT NULL,
|
|
265
|
-
source TEXT NOT NULL,
|
|
266
|
-
score REAL NOT NULL,
|
|
267
|
-
reason TEXT,
|
|
268
|
-
meta_json TEXT,
|
|
269
|
-
input_json TEXT,
|
|
270
|
-
output_json TEXT,
|
|
271
|
-
latency_ms REAL,
|
|
272
|
-
scored_at_ms INTEGER NOT NULL,
|
|
273
|
-
duration_ms REAL
|
|
274
|
-
)`,
|
|
275
|
-
`CREATE TABLE IF NOT EXISTS _smithers_memory_facts (
|
|
276
|
-
namespace TEXT NOT NULL,
|
|
277
|
-
key TEXT NOT NULL,
|
|
278
|
-
value_json TEXT NOT NULL,
|
|
279
|
-
schema_sig TEXT,
|
|
280
|
-
created_at_ms INTEGER NOT NULL,
|
|
281
|
-
updated_at_ms INTEGER NOT NULL,
|
|
282
|
-
ttl_ms INTEGER,
|
|
283
|
-
PRIMARY KEY (namespace, key)
|
|
284
|
-
)`,
|
|
285
|
-
`CREATE TABLE IF NOT EXISTS _smithers_memory_threads (
|
|
286
|
-
thread_id TEXT PRIMARY KEY,
|
|
287
|
-
namespace TEXT NOT NULL,
|
|
288
|
-
title TEXT,
|
|
289
|
-
metadata_json TEXT,
|
|
290
|
-
created_at_ms INTEGER NOT NULL,
|
|
291
|
-
updated_at_ms INTEGER NOT NULL
|
|
292
|
-
)`,
|
|
293
|
-
`CREATE TABLE IF NOT EXISTS _smithers_memory_messages (
|
|
294
|
-
id TEXT PRIMARY KEY,
|
|
295
|
-
thread_id TEXT NOT NULL,
|
|
296
|
-
role TEXT NOT NULL,
|
|
297
|
-
content_json TEXT NOT NULL,
|
|
298
|
-
run_id TEXT,
|
|
299
|
-
node_id TEXT,
|
|
300
|
-
created_at_ms INTEGER NOT NULL
|
|
301
|
-
)`,
|
|
302
|
-
];
|
|
303
|
-
const MIGRATION_STATEMENTS = [
|
|
304
|
-
`ALTER TABLE _smithers_attempts ADD COLUMN response_text TEXT`,
|
|
305
|
-
`ALTER TABLE _smithers_attempts ADD COLUMN jj_cwd TEXT`,
|
|
306
|
-
`ALTER TABLE _smithers_attempts ADD COLUMN heartbeat_at_ms INTEGER`,
|
|
307
|
-
`ALTER TABLE _smithers_attempts ADD COLUMN heartbeat_data_json TEXT`,
|
|
308
|
-
`ALTER TABLE _smithers_attempts ADD COLUMN cached INTEGER DEFAULT 0`,
|
|
309
|
-
`ALTER TABLE _smithers_attempts ADD COLUMN meta_json TEXT`,
|
|
310
|
-
`ALTER TABLE _smithers_runs ADD COLUMN workflow_hash TEXT`,
|
|
311
|
-
`ALTER TABLE _smithers_runs ADD COLUMN heartbeat_at_ms INTEGER`,
|
|
312
|
-
`ALTER TABLE _smithers_runs ADD COLUMN runtime_owner_id TEXT`,
|
|
313
|
-
`ALTER TABLE _smithers_runs ADD COLUMN cancel_requested_at_ms INTEGER`,
|
|
314
|
-
`ALTER TABLE _smithers_runs ADD COLUMN hijack_requested_at_ms INTEGER`,
|
|
315
|
-
`ALTER TABLE _smithers_runs ADD COLUMN hijack_target TEXT`,
|
|
316
|
-
`ALTER TABLE _smithers_runs ADD COLUMN vcs_type TEXT`,
|
|
317
|
-
`ALTER TABLE _smithers_runs ADD COLUMN vcs_root TEXT`,
|
|
318
|
-
`ALTER TABLE _smithers_runs ADD COLUMN vcs_revision TEXT`,
|
|
319
|
-
`ALTER TABLE _smithers_runs ADD COLUMN parent_run_id TEXT`,
|
|
320
|
-
`ALTER TABLE _smithers_runs ADD COLUMN error_json TEXT`,
|
|
321
|
-
`ALTER TABLE _smithers_runs ADD COLUMN config_json TEXT`,
|
|
322
|
-
`ALTER TABLE _smithers_approvals ADD COLUMN request_json TEXT`,
|
|
323
|
-
`ALTER TABLE _smithers_approvals ADD COLUMN decision_json TEXT`,
|
|
324
|
-
`ALTER TABLE _smithers_approvals ADD COLUMN auto_approved INTEGER NOT NULL DEFAULT 0`,
|
|
325
|
-
`CREATE INDEX IF NOT EXISTS _smithers_runs_parent_idx ON _smithers_runs (parent_run_id)`,
|
|
326
|
-
// Ticket 0001: Alert model extensions
|
|
327
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN fingerprint TEXT`,
|
|
328
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN node_id TEXT`,
|
|
329
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN iteration INTEGER`,
|
|
330
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN owner TEXT`,
|
|
331
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN runbook TEXT`,
|
|
332
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN labels_json TEXT`,
|
|
333
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN reaction_json TEXT`,
|
|
334
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN source_event_type TEXT`,
|
|
335
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN first_fired_at_ms INTEGER`,
|
|
336
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN last_fired_at_ms INTEGER`,
|
|
337
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN occurrence_count INTEGER DEFAULT 1`,
|
|
338
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN silenced_until_ms INTEGER`,
|
|
339
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN acknowledged_by TEXT`,
|
|
340
|
-
`ALTER TABLE _smithers_alerts ADD COLUMN resolved_by TEXT`,
|
|
341
|
-
`CREATE INDEX IF NOT EXISTS _smithers_alerts_fingerprint_idx ON _smithers_alerts (fingerprint)`,
|
|
342
|
-
`CREATE INDEX IF NOT EXISTS _smithers_alerts_run_status_idx ON _smithers_alerts (run_id, status)`,
|
|
343
|
-
];
|
|
344
|
-
/**
|
|
345
|
-
* @param {string} identifier
|
|
346
|
-
* @returns {string}
|
|
347
|
-
*/
|
|
348
|
-
function quoteIdentifier(identifier) {
|
|
349
|
-
return `"${identifier.replace(/"/g, "\"\"")}"`;
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* @param {string} value
|
|
353
|
-
* @returns {string}
|
|
354
|
-
*/
|
|
355
|
-
function snakeToCamel(value) {
|
|
356
|
-
return value.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* @param {SqliteParam} value
|
|
360
|
-
* @returns {Exclude<SqliteParam, undefined>}
|
|
361
|
-
*/
|
|
362
|
-
function encodeParam(value) {
|
|
363
|
-
if (typeof value === "boolean") {
|
|
364
|
-
return value ? 1 : 0;
|
|
365
|
-
}
|
|
366
|
-
return value ?? null;
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* @template T
|
|
370
|
-
* @param {ReadonlyArray<T>} rows
|
|
371
|
-
* @returns {ReadonlyArray<T>}
|
|
372
|
-
*/
|
|
373
|
-
function transformRowKeys(rows) {
|
|
374
|
-
return rows.map((row) => {
|
|
375
|
-
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
376
|
-
return row;
|
|
377
|
-
}
|
|
378
|
-
const next = {};
|
|
379
|
-
for (const [key, value] of Object.entries(row)) {
|
|
380
|
-
next[snakeToCamel(key)] = value;
|
|
381
|
-
}
|
|
382
|
-
return next;
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* @template T
|
|
387
|
-
* @param {T} row
|
|
388
|
-
* @param {readonly string[]} [booleanColumns]
|
|
389
|
-
* @returns {T}
|
|
390
|
-
*/
|
|
391
|
-
function applyBooleanColumns(row, booleanColumns) {
|
|
392
|
-
if (!booleanColumns || booleanColumns.length === 0) {
|
|
393
|
-
return row;
|
|
394
|
-
}
|
|
395
|
-
const next = { ...row };
|
|
396
|
-
for (const column of booleanColumns) {
|
|
397
|
-
const current = next[column];
|
|
398
|
-
if (current !== null && current !== undefined) {
|
|
399
|
-
next[column] = Boolean(current);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return next;
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* @param {string} table
|
|
406
|
-
* @param {Record<string, unknown>} row
|
|
407
|
-
* @param {{ orIgnore?: boolean; conflictColumns?: readonly string[]; updateColumns?: readonly string[]; }} [options]
|
|
408
|
-
*/
|
|
409
|
-
function buildInsertSql(table, row, options) {
|
|
410
|
-
const entries = Object.entries(row).filter(([, value]) => value !== undefined);
|
|
411
|
-
const columns = entries.map(([key]) => camelToSnake(key));
|
|
412
|
-
const params = entries.map(([, value]) => encodeParam(value));
|
|
413
|
-
const tableSql = quoteIdentifier(table);
|
|
414
|
-
const columnSql = columns.map(quoteIdentifier).join(", ");
|
|
415
|
-
const placeholderSql = columns.map(() => "?").join(", ");
|
|
416
|
-
let statement = `INSERT${options?.orIgnore ? " OR IGNORE" : ""} INTO ${tableSql} (${columnSql}) ` +
|
|
417
|
-
`VALUES (${placeholderSql})`;
|
|
418
|
-
if (options?.conflictColumns && options.conflictColumns.length > 0) {
|
|
419
|
-
const conflictSql = options.conflictColumns.map(camelToSnake).map(quoteIdentifier).join(", ");
|
|
420
|
-
const updateColumns = (options.updateColumns ?? Object.keys(row))
|
|
421
|
-
.map(camelToSnake)
|
|
422
|
-
.filter((column) => !options.conflictColumns.includes(snakeToCamel(column)));
|
|
423
|
-
if (updateColumns.length === 0) {
|
|
424
|
-
statement += ` ON CONFLICT (${conflictSql}) DO NOTHING`;
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
const updateSql = updateColumns
|
|
428
|
-
.map((column) => `${quoteIdentifier(column)} = excluded.${quoteIdentifier(column)}`)
|
|
429
|
-
.join(", ");
|
|
430
|
-
statement += ` ON CONFLICT (${conflictSql}) DO UPDATE SET ${updateSql}`;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
return { statement, params };
|
|
434
|
-
}
|
|
435
|
-
/**
|
|
436
|
-
* @param {string} table
|
|
437
|
-
* @param {Record<string, unknown>} patch
|
|
438
|
-
* @param {string} whereSql
|
|
439
|
-
* @param {ReadonlyArray<SqliteParam>} [params]
|
|
440
|
-
*/
|
|
441
|
-
function buildUpdateSql(table, patch, whereSql, params = []) {
|
|
442
|
-
const entries = Object.entries(patch).filter(([, value]) => value !== undefined);
|
|
443
|
-
if (entries.length === 0) {
|
|
444
|
-
return null;
|
|
445
|
-
}
|
|
446
|
-
const setSql = entries
|
|
447
|
-
.map(([key]) => `${quoteIdentifier(camelToSnake(key))} = ?`)
|
|
448
|
-
.join(", ");
|
|
449
|
-
return {
|
|
450
|
-
statement: `UPDATE ${quoteIdentifier(table)} SET ${setSql} WHERE ${whereSql}`,
|
|
451
|
-
params: [
|
|
452
|
-
...entries.map(([, value]) => encodeParam(value)),
|
|
453
|
-
...params.map(encodeParam),
|
|
454
|
-
],
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
/**
|
|
458
|
-
* @param {BunSQLiteDatabase<any> | Database} db
|
|
459
|
-
* @returns {Database}
|
|
460
|
-
*/
|
|
461
|
-
function resolveSqliteDatabase(db) {
|
|
462
|
-
if (db instanceof Database) {
|
|
463
|
-
return db;
|
|
464
|
-
}
|
|
465
|
-
const candidate = db.session?.client ?? db.$client;
|
|
466
|
-
if (!candidate || typeof candidate.query !== "function" || typeof candidate.run !== "function") {
|
|
467
|
-
throw new TypeError("SqlMessageStorage requires a Bun SQLite client.");
|
|
468
|
-
}
|
|
469
|
-
return candidate;
|
|
470
|
-
}
|
|
471
|
-
/**
|
|
472
|
-
* @param {Database} sqlite
|
|
473
|
-
* @returns {Connection}
|
|
474
|
-
*/
|
|
475
|
-
function createConnection(sqlite) {
|
|
476
|
-
/**
|
|
477
|
-
* @param {string} statement
|
|
478
|
-
* @param {ReadonlyArray<unknown>} params
|
|
479
|
-
* @param {(<A extends object>(rows: ReadonlyArray<A>) => ReadonlyArray<A>) | undefined} [transformRows]
|
|
480
|
-
*/
|
|
481
|
-
const execute = (statement, params, transformRows) => Effect.withFiberRuntime((fiber) => {
|
|
482
|
-
const useSafeIntegers = Context.get(fiber.currentContext, SqlClient.SafeIntegers);
|
|
483
|
-
try {
|
|
484
|
-
const query = sqlite.query(statement);
|
|
485
|
-
// @ts-ignore bun-types missing safeIntegers()
|
|
486
|
-
query.safeIntegers(useSafeIntegers);
|
|
487
|
-
const rows = (query.all(...params) ?? []);
|
|
488
|
-
return Effect.succeed(transformRows ? transformRows(rows) : rows);
|
|
489
|
-
}
|
|
490
|
-
catch (cause) {
|
|
491
|
-
return Effect.fail(new SqlError({ cause, message: "Failed to execute SQLite statement" }));
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
return {
|
|
495
|
-
execute: (statement, params, transformRows) => execute(statement, params, transformRows),
|
|
496
|
-
executeRaw: (statement, params) => execute(statement, params, undefined),
|
|
497
|
-
executeValues: (statement, params) => Effect.withFiberRuntime((fiber) => {
|
|
498
|
-
const useSafeIntegers = Context.get(fiber.currentContext, SqlClient.SafeIntegers);
|
|
499
|
-
try {
|
|
500
|
-
const query = sqlite.query(statement);
|
|
501
|
-
// @ts-ignore bun-types missing safeIntegers()
|
|
502
|
-
query.safeIntegers(useSafeIntegers);
|
|
503
|
-
return Effect.succeed((query.values(...params) ?? []));
|
|
504
|
-
}
|
|
505
|
-
catch (cause) {
|
|
506
|
-
return Effect.fail(new SqlError({ cause, message: "Failed to execute SQLite values statement" }));
|
|
507
|
-
}
|
|
508
|
-
}),
|
|
509
|
-
executeUnprepared: (statement, params, transformRows) => execute(statement, params, transformRows),
|
|
510
|
-
executeStream: () => Effect.dieMessage("executeStream not implemented"),
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
/**
|
|
514
|
-
* @param {Database} sqlite
|
|
515
|
-
* @returns {Effect.Effect<SqlClient.SqlClient, never>}
|
|
516
|
-
*/
|
|
517
|
-
function makeSqlClientEffect(sqlite) {
|
|
518
|
-
const compiler = Statement.makeCompilerSqlite(camelToSnake);
|
|
519
|
-
const connection = createConnection(sqlite);
|
|
520
|
-
return Effect.gen(function* () {
|
|
521
|
-
const semaphore = yield* Effect.makeSemaphore(1);
|
|
522
|
-
const acquirer = semaphore.withPermits(1)(Effect.succeed(connection));
|
|
523
|
-
const transactionAcquirer = Effect.uninterruptibleMask((restore) => Effect.as(Effect.zipRight(restore(semaphore.take(1)), Effect.tap(Effect.scope, (scope) => Scope.addFinalizer(scope, semaphore.release(1)))), connection));
|
|
524
|
-
const reactivity = yield* Reactivity.make;
|
|
525
|
-
return yield* SqlClient.make({
|
|
526
|
-
acquirer,
|
|
527
|
-
compiler,
|
|
528
|
-
transactionAcquirer,
|
|
529
|
-
spanAttributes: [[ATTR_DB_SYSTEM_NAME, "sqlite"]],
|
|
530
|
-
transformRows: transformRowKeys,
|
|
531
|
-
}).pipe(Effect.provideService(Reactivity.Reactivity, reactivity));
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* @param {Database} sqlite
|
|
536
|
-
*/
|
|
537
|
-
function makeSqlClientLayer(sqlite) {
|
|
538
|
-
return Layer.scoped(SqlClient.SqlClient, makeSqlClientEffect(sqlite));
|
|
539
|
-
}
|
|
540
|
-
export class SqlMessageStorage {
|
|
541
|
-
sqlite;
|
|
542
|
-
// TODO(Phase 8): Keep this per-DB runtime until the unified runtime can
|
|
543
|
-
// inject a scoped SqlClient without rebuilding the per-connection semaphore.
|
|
544
|
-
runtime;
|
|
545
|
-
tableColumnsCache = new Map();
|
|
546
|
-
/**
|
|
547
|
-
* @param {BunSQLiteDatabase<any> | Database} db
|
|
548
|
-
*/
|
|
549
|
-
constructor(db) {
|
|
550
|
-
this.sqlite = resolveSqliteDatabase(db);
|
|
551
|
-
this.runtime = ManagedRuntime.make(makeSqlClientLayer(this.sqlite));
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* @param {string} table
|
|
555
|
-
* @returns {Set<string>}
|
|
556
|
-
*/
|
|
557
|
-
getTableColumns(table) {
|
|
558
|
-
const cached = this.tableColumnsCache.get(table);
|
|
559
|
-
if (cached) {
|
|
560
|
-
return cached;
|
|
561
|
-
}
|
|
562
|
-
const rows = this.sqlite
|
|
563
|
-
.query(`PRAGMA table_info(${quoteIdentifier(table)})`)
|
|
564
|
-
.all();
|
|
565
|
-
const columns = new Set(rows
|
|
566
|
-
.map((row) => (typeof row.name === "string" ? snakeToCamel(row.name) : ""))
|
|
567
|
-
.filter((value) => value.length > 0));
|
|
568
|
-
this.tableColumnsCache.set(table, columns);
|
|
569
|
-
return columns;
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* @param {string} table
|
|
573
|
-
* @param {Record<string, unknown>} row
|
|
574
|
-
* @returns {Record<string, unknown>}
|
|
575
|
-
*/
|
|
576
|
-
filterKnownColumns(table, row) {
|
|
577
|
-
const knownColumns = this.getTableColumns(table);
|
|
578
|
-
return Object.fromEntries(Object.entries(row).filter(([key, value]) => value !== undefined && knownColumns.has(key)));
|
|
579
|
-
}
|
|
580
|
-
/**
|
|
581
|
-
* @template A, E
|
|
582
|
-
* @param {Effect.Effect<A, E, SqlClient.SqlClient>} effect
|
|
583
|
-
* @returns {Promise<A>}
|
|
584
|
-
*/
|
|
585
|
-
runEffect(effect) {
|
|
586
|
-
return this.runtime.runPromise(effect);
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* @template A
|
|
590
|
-
* @param {(connection: Connection) => Effect.Effect<A, SqlError>} f
|
|
591
|
-
* @returns {Promise<A>}
|
|
592
|
-
*/
|
|
593
|
-
withConnection(f) {
|
|
594
|
-
return this.runEffect(Effect.flatMap(SqlClient.SqlClient, (client) => Effect.scoped(Effect.flatMap(client.reserve, f))));
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* @returns {Effect.Effect<void, never>}
|
|
598
|
-
*/
|
|
599
|
-
ensureSchemaEffect() {
|
|
600
|
-
const sqlite = this.sqlite;
|
|
601
|
-
return Effect.sync(() => {
|
|
602
|
-
for (const statement of CREATE_TABLE_STATEMENTS) {
|
|
603
|
-
sqlite.run(statement);
|
|
604
|
-
}
|
|
605
|
-
for (const statement of MIGRATION_STATEMENTS) {
|
|
606
|
-
try {
|
|
607
|
-
sqlite.run(statement);
|
|
608
|
-
}
|
|
609
|
-
catch {
|
|
610
|
-
// Ignore legacy migration failures for already-applied changes.
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
const frameColumns = sqlite
|
|
614
|
-
.query(`PRAGMA table_info("_smithers_frames")`)
|
|
615
|
-
.all();
|
|
616
|
-
if (!frameColumns.some((column) => column.name === "encoding")) {
|
|
617
|
-
try {
|
|
618
|
-
sqlite.run(`ALTER TABLE _smithers_frames ADD COLUMN encoding TEXT NOT NULL DEFAULT 'full'`);
|
|
619
|
-
}
|
|
620
|
-
catch {
|
|
621
|
-
// Ignore if another caller added it first.
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* @returns {Promise<void>}
|
|
628
|
-
*/
|
|
629
|
-
ensureSchema() {
|
|
630
|
-
return this.runtime.runPromise(this.ensureSchemaEffect());
|
|
631
|
-
}
|
|
632
|
-
/**
|
|
633
|
-
* @template T
|
|
634
|
-
* @param {string} statement
|
|
635
|
-
* @param {ReadonlyArray<SqliteParam>} [params]
|
|
636
|
-
* @param {{ booleanColumns?: readonly string[] }} [options]
|
|
637
|
-
* @returns {Promise<Array<T>>}
|
|
638
|
-
*/
|
|
639
|
-
queryAll(statement, params = [], options) {
|
|
640
|
-
return this.withConnection((connection) => connection
|
|
641
|
-
.execute(statement, params.map(encodeParam), transformRowKeys)
|
|
642
|
-
.pipe(Effect.map((rows) => rows.map((row) => applyBooleanColumns(row, options?.booleanColumns)))));
|
|
643
|
-
}
|
|
644
|
-
/**
|
|
645
|
-
* @template T
|
|
646
|
-
* @param {string} statement
|
|
647
|
-
* @param {ReadonlyArray<SqliteParam>} [params]
|
|
648
|
-
* @param {{ booleanColumns?: readonly string[] }} [options]
|
|
649
|
-
* @returns {Promise<T | undefined>}
|
|
650
|
-
*/
|
|
651
|
-
async queryOne(statement, params = [], options) {
|
|
652
|
-
const rows = await this.queryAll(statement, params, options);
|
|
653
|
-
return rows[0];
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* @param {string} statement
|
|
657
|
-
* @param {ReadonlyArray<SqliteParam>} [params]
|
|
658
|
-
* @returns {Promise<void>}
|
|
659
|
-
*/
|
|
660
|
-
execute(statement, params = []) {
|
|
661
|
-
return this.withConnection((connection) => connection.executeRaw(statement, params.map(encodeParam)).pipe(Effect.asVoid));
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* @param {string} table
|
|
665
|
-
* @param {Record<string, unknown>} row
|
|
666
|
-
* @returns {Promise<void>}
|
|
667
|
-
*/
|
|
668
|
-
insertIgnore(table, row) {
|
|
669
|
-
const filteredRow = this.filterKnownColumns(table, row);
|
|
670
|
-
const { statement, params } = buildInsertSql(table, filteredRow, { orIgnore: true });
|
|
671
|
-
return this.execute(statement, params);
|
|
672
|
-
}
|
|
673
|
-
/**
|
|
674
|
-
* @param {string} table
|
|
675
|
-
* @param {Record<string, unknown>} row
|
|
676
|
-
* @param {readonly string[]} conflictColumns
|
|
677
|
-
* @param {readonly string[]} [updateColumns]
|
|
678
|
-
* @returns {Promise<void>}
|
|
679
|
-
*/
|
|
680
|
-
upsert(table, row, conflictColumns, updateColumns) {
|
|
681
|
-
const filteredRow = this.filterKnownColumns(table, row);
|
|
682
|
-
const { statement, params } = buildInsertSql(table, filteredRow, {
|
|
683
|
-
conflictColumns,
|
|
684
|
-
updateColumns,
|
|
685
|
-
});
|
|
686
|
-
return this.execute(statement, params);
|
|
687
|
-
}
|
|
688
|
-
/**
|
|
689
|
-
* @param {string} table
|
|
690
|
-
* @param {Record<string, unknown>} patch
|
|
691
|
-
* @param {string} whereSql
|
|
692
|
-
* @param {ReadonlyArray<SqliteParam>} [params]
|
|
693
|
-
* @returns {Promise<void>}
|
|
694
|
-
*/
|
|
695
|
-
updateWhere(table, patch, whereSql, params = []) {
|
|
696
|
-
const built = buildUpdateSql(table, this.filterKnownColumns(table, patch), whereSql, params);
|
|
697
|
-
if (!built) {
|
|
698
|
-
return Promise.resolve();
|
|
699
|
-
}
|
|
700
|
-
return this.execute(built.statement, built.params);
|
|
701
|
-
}
|
|
702
|
-
/**
|
|
703
|
-
* @param {string} table
|
|
704
|
-
* @param {string} whereSql
|
|
705
|
-
* @param {ReadonlyArray<SqliteParam>} [params]
|
|
706
|
-
* @returns {Promise<void>}
|
|
707
|
-
*/
|
|
708
|
-
deleteWhere(table, whereSql, params = []) {
|
|
709
|
-
return this.execute(`DELETE FROM ${quoteIdentifier(table)} WHERE ${whereSql}`, params);
|
|
710
|
-
}
|
|
711
|
-
/**
|
|
712
|
-
* @param {string} runId
|
|
713
|
-
* @param {SqlMessageStorageEventHistoryQuery} [query]
|
|
714
|
-
* @returns {{ whereSql: string; params: Array<SqliteParam> }}
|
|
715
|
-
*/
|
|
716
|
-
buildEventHistoryWhere(runId, query = {}) {
|
|
717
|
-
const clauses = ["run_id = ?", "seq > ?"];
|
|
718
|
-
const params = [runId, query.afterSeq ?? -1];
|
|
719
|
-
if (typeof query.sinceTimestampMs === "number") {
|
|
720
|
-
clauses.push("timestamp_ms >= ?");
|
|
721
|
-
params.push(query.sinceTimestampMs);
|
|
722
|
-
}
|
|
723
|
-
if (query.types && query.types.length > 0) {
|
|
724
|
-
clauses.push(`type IN (${query.types.map(() => "?").join(", ")})`);
|
|
725
|
-
params.push(...query.types);
|
|
726
|
-
}
|
|
727
|
-
if (query.nodeId) {
|
|
728
|
-
clauses.push("json_extract(payload_json, '$.nodeId') = ?");
|
|
729
|
-
params.push(query.nodeId);
|
|
730
|
-
}
|
|
731
|
-
return {
|
|
732
|
-
whereSql: clauses.join(" AND "),
|
|
733
|
-
params,
|
|
734
|
-
};
|
|
735
|
-
}
|
|
736
|
-
/**
|
|
737
|
-
* @param {string} runId
|
|
738
|
-
* @param {SqlMessageStorageEventHistoryQuery} [query]
|
|
739
|
-
* @returns {Promise<Array<Record<string, unknown>>>}
|
|
740
|
-
*/
|
|
741
|
-
listEventHistory(runId, query = {}) {
|
|
742
|
-
const limit = Math.max(1, Math.floor(query.limit ?? 200));
|
|
743
|
-
const { whereSql, params } = this.buildEventHistoryWhere(runId, query);
|
|
744
|
-
return this.queryAll(`SELECT * FROM _smithers_events
|
|
745
|
-
WHERE ${whereSql}
|
|
746
|
-
ORDER BY seq ASC
|
|
747
|
-
LIMIT ?`, [...params, limit]);
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* @param {string} runId
|
|
751
|
-
* @param {SqlMessageStorageEventHistoryQuery} [query]
|
|
752
|
-
* @returns {Promise<number>}
|
|
753
|
-
*/
|
|
754
|
-
async countEventHistory(runId, query = {}) {
|
|
755
|
-
const { whereSql, params } = this.buildEventHistoryWhere(runId, query);
|
|
756
|
-
const row = await this.queryOne(`SELECT COUNT(*) AS count
|
|
757
|
-
FROM _smithers_events
|
|
758
|
-
WHERE ${whereSql}`, params);
|
|
759
|
-
return Number(row?.count ?? 0);
|
|
760
|
-
}
|
|
761
|
-
/**
|
|
762
|
-
* @param {string} runId
|
|
763
|
-
* @returns {Promise<number | undefined>}
|
|
764
|
-
*/
|
|
765
|
-
async getLastEventSeq(runId) {
|
|
766
|
-
const row = await this.queryOne(`SELECT seq
|
|
767
|
-
FROM _smithers_events
|
|
768
|
-
WHERE run_id = ?
|
|
769
|
-
ORDER BY seq DESC
|
|
770
|
-
LIMIT 1`, [runId]);
|
|
771
|
-
return row?.seq;
|
|
772
|
-
}
|
|
773
|
-
/**
|
|
774
|
-
* @param {string} runId
|
|
775
|
-
* @param {string} type
|
|
776
|
-
* @returns {Promise<Array<Record<string, unknown>>>}
|
|
777
|
-
*/
|
|
778
|
-
listEventsByType(runId, type) {
|
|
779
|
-
return this.queryAll(`SELECT *
|
|
780
|
-
FROM _smithers_events
|
|
781
|
-
WHERE run_id = ? AND type = ?
|
|
782
|
-
ORDER BY seq ASC`, [runId, type]);
|
|
783
|
-
}
|
|
784
|
-
/**
|
|
785
|
-
* @param {string} runId
|
|
786
|
-
* @returns {Promise<number | undefined>}
|
|
787
|
-
*/
|
|
788
|
-
async getLastSignalSeq(runId) {
|
|
789
|
-
const row = await this.queryOne(`SELECT seq
|
|
790
|
-
FROM _smithers_signals
|
|
791
|
-
WHERE run_id = ?
|
|
792
|
-
ORDER BY seq DESC
|
|
793
|
-
LIMIT 1`, [runId]);
|
|
794
|
-
return row?.seq;
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
/**
|
|
798
|
-
* @param {BunSQLiteDatabase<any> | Database} db
|
|
799
|
-
* @returns {SqlMessageStorage}
|
|
800
|
-
*/
|
|
801
|
-
export function getSqlMessageStorage(db) {
|
|
802
|
-
return new SqlMessageStorage(db);
|
|
803
|
-
}
|
|
804
|
-
/**
|
|
805
|
-
* @param {BunSQLiteDatabase<any> | Database} db
|
|
806
|
-
* @returns {Effect.Effect<void, never>}
|
|
807
|
-
*/
|
|
808
|
-
export function ensureSqlMessageStorageEffect(db) {
|
|
809
|
-
return getSqlMessageStorage(db).ensureSchemaEffect();
|
|
810
|
-
}
|
|
811
|
-
/**
|
|
812
|
-
* @param {BunSQLiteDatabase<any> | Database} db
|
|
813
|
-
* @returns {Promise<void>}
|
|
814
|
-
*/
|
|
815
|
-
export function ensureSqlMessageStorage(db) {
|
|
816
|
-
return getSqlMessageStorage(db).ensureSchema();
|
|
817
|
-
}
|
|
1
|
+
// Preserve the engine subpath while using the canonical DB implementation.
|
|
2
|
+
export * from "@smithers-orchestrator/db/sql-message-storage";
|