@sixb/sqlite 0.0.1

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 (152) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/dist/action-run-storage.d.ts +29 -0
  4. package/dist/action-run-storage.d.ts.map +1 -0
  5. package/dist/agents/agent-storage.d.ts +25 -0
  6. package/dist/agents/agent-storage.d.ts.map +1 -0
  7. package/dist/agents/index.d.ts +6 -0
  8. package/dist/agents/index.d.ts.map +1 -0
  9. package/dist/agents/messages.d.ts +14 -0
  10. package/dist/agents/messages.d.ts.map +1 -0
  11. package/dist/agents/rows.d.ts +77 -0
  12. package/dist/agents/rows.d.ts.map +1 -0
  13. package/dist/agents/runs.d.ts +26 -0
  14. package/dist/agents/runs.d.ts.map +1 -0
  15. package/dist/agents/threads.d.ts +14 -0
  16. package/dist/agents/threads.d.ts.map +1 -0
  17. package/dist/auth-storage/access-tokens.d.ts +33 -0
  18. package/dist/auth-storage/access-tokens.d.ts.map +1 -0
  19. package/dist/auth-storage/auth-storage.d.ts +46 -0
  20. package/dist/auth-storage/auth-storage.d.ts.map +1 -0
  21. package/dist/auth-storage/group-memberships.d.ts +21 -0
  22. package/dist/auth-storage/group-memberships.d.ts.map +1 -0
  23. package/dist/auth-storage/identities.d.ts +17 -0
  24. package/dist/auth-storage/identities.d.ts.map +1 -0
  25. package/dist/auth-storage/index.d.ts +3 -0
  26. package/dist/auth-storage/index.d.ts.map +1 -0
  27. package/dist/auth-storage/invitations.d.ts +28 -0
  28. package/dist/auth-storage/invitations.d.ts.map +1 -0
  29. package/dist/auth-storage/magic-links.d.ts +28 -0
  30. package/dist/auth-storage/magic-links.d.ts.map +1 -0
  31. package/dist/auth-storage/oidc-attempts.d.ts +18 -0
  32. package/dist/auth-storage/oidc-attempts.d.ts.map +1 -0
  33. package/dist/auth-storage/rows.d.ts +134 -0
  34. package/dist/auth-storage/rows.d.ts.map +1 -0
  35. package/dist/auth-storage/service-account-group-memberships.d.ts +17 -0
  36. package/dist/auth-storage/service-account-group-memberships.d.ts.map +1 -0
  37. package/dist/auth-storage/service-accounts.d.ts +14 -0
  38. package/dist/auth-storage/service-accounts.d.ts.map +1 -0
  39. package/dist/auth-storage/sessions.d.ts +55 -0
  40. package/dist/auth-storage/sessions.d.ts.map +1 -0
  41. package/dist/auth-storage/shared.d.ts +131 -0
  42. package/dist/auth-storage/shared.d.ts.map +1 -0
  43. package/dist/auth-storage/users.d.ts +19 -0
  44. package/dist/auth-storage/users.d.ts.map +1 -0
  45. package/dist/index.d.ts +71 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +10204 -0
  48. package/dist/index.js.map +54 -0
  49. package/dist/latest-run-query.d.ts +22 -0
  50. package/dist/latest-run-query.d.ts.map +1 -0
  51. package/dist/migrations.d.ts +18 -0
  52. package/dist/migrations.d.ts.map +1 -0
  53. package/dist/object-query-compiler.d.ts +44 -0
  54. package/dist/object-query-compiler.d.ts.map +1 -0
  55. package/dist/object-storage.d.ts +93 -0
  56. package/dist/object-storage.d.ts.map +1 -0
  57. package/dist/ontology-storage/commits.d.ts +14 -0
  58. package/dist/ontology-storage/commits.d.ts.map +1 -0
  59. package/dist/ontology-storage/index.d.ts +22 -0
  60. package/dist/ontology-storage/index.d.ts.map +1 -0
  61. package/dist/ontology-storage/materialization-session.d.ts +49 -0
  62. package/dist/ontology-storage/materialization-session.d.ts.map +1 -0
  63. package/dist/ontology-storage/materialization-state.d.ts +63 -0
  64. package/dist/ontology-storage/materialization-state.d.ts.map +1 -0
  65. package/dist/ontology-storage/materialization-writer.d.ts +15 -0
  66. package/dist/ontology-storage/materialization-writer.d.ts.map +1 -0
  67. package/dist/ontology-storage/materializations.d.ts +32 -0
  68. package/dist/ontology-storage/materializations.d.ts.map +1 -0
  69. package/dist/ontology-storage/outbox.d.ts +14 -0
  70. package/dist/ontology-storage/outbox.d.ts.map +1 -0
  71. package/dist/ontology-storage/shared.d.ts +91 -0
  72. package/dist/ontology-storage/shared.d.ts.map +1 -0
  73. package/dist/ontology-storage/sources.d.ts +26 -0
  74. package/dist/ontology-storage/sources.d.ts.map +1 -0
  75. package/dist/pipeline-run-storage.d.ts +26 -0
  76. package/dist/pipeline-run-storage.d.ts.map +1 -0
  77. package/dist/projection-run-storage.d.ts +32 -0
  78. package/dist/projection-run-storage.d.ts.map +1 -0
  79. package/dist/rules-storage.d.ts +33 -0
  80. package/dist/rules-storage.d.ts.map +1 -0
  81. package/dist/run-list-query.d.ts +27 -0
  82. package/dist/run-list-query.d.ts.map +1 -0
  83. package/dist/storage-errors.d.ts +2 -0
  84. package/dist/storage-errors.d.ts.map +1 -0
  85. package/dist/sync-run-storage.d.ts +23 -0
  86. package/dist/sync-run-storage.d.ts.map +1 -0
  87. package/dist/timeseries-storage.d.ts +41 -0
  88. package/dist/timeseries-storage.d.ts.map +1 -0
  89. package/dist/transactions.d.ts +15 -0
  90. package/dist/transactions.d.ts.map +1 -0
  91. package/dist/tsconfig.json +5 -0
  92. package/dist/validation.d.ts +5 -0
  93. package/dist/validation.d.ts.map +1 -0
  94. package/dist/webhook-delivery-storage.d.ts +27 -0
  95. package/dist/webhook-delivery-storage.d.ts.map +1 -0
  96. package/dist/webhook-run-storage.d.ts +22 -0
  97. package/dist/webhook-run-storage.d.ts.map +1 -0
  98. package/dist/workflow-intervention-storage.d.ts +26 -0
  99. package/dist/workflow-intervention-storage.d.ts.map +1 -0
  100. package/dist/workflow-run-storage.d.ts +63 -0
  101. package/dist/workflow-run-storage.d.ts.map +1 -0
  102. package/package.json +58 -0
  103. package/src/action-run-storage.ts +761 -0
  104. package/src/agents/agent-storage.ts +46 -0
  105. package/src/agents/index.ts +5 -0
  106. package/src/agents/messages.ts +162 -0
  107. package/src/agents/rows.ts +205 -0
  108. package/src/agents/runs.ts +389 -0
  109. package/src/agents/threads.ts +133 -0
  110. package/src/auth-storage/access-tokens.ts +289 -0
  111. package/src/auth-storage/auth-storage.ts +784 -0
  112. package/src/auth-storage/group-memberships.ts +52 -0
  113. package/src/auth-storage/identities.ts +98 -0
  114. package/src/auth-storage/index.ts +2 -0
  115. package/src/auth-storage/invitations.ts +258 -0
  116. package/src/auth-storage/magic-links.ts +149 -0
  117. package/src/auth-storage/oidc-attempts.ts +109 -0
  118. package/src/auth-storage/rows.ts +338 -0
  119. package/src/auth-storage/service-account-group-memberships.ts +161 -0
  120. package/src/auth-storage/service-accounts.ts +168 -0
  121. package/src/auth-storage/sessions.ts +230 -0
  122. package/src/auth-storage/shared.ts +707 -0
  123. package/src/auth-storage/users.ts +226 -0
  124. package/src/index.ts +292 -0
  125. package/src/latest-run-query.ts +49 -0
  126. package/src/migrations/001-initial-schema.sql +1068 -0
  127. package/src/migrations.ts +245 -0
  128. package/src/object-query-compiler.ts +1198 -0
  129. package/src/object-storage.ts +605 -0
  130. package/src/ontology-storage/commits.ts +127 -0
  131. package/src/ontology-storage/index.ts +35 -0
  132. package/src/ontology-storage/materialization-session.ts +393 -0
  133. package/src/ontology-storage/materialization-state.ts +901 -0
  134. package/src/ontology-storage/materialization-writer.ts +394 -0
  135. package/src/ontology-storage/materializations.ts +838 -0
  136. package/src/ontology-storage/outbox.ts +237 -0
  137. package/src/ontology-storage/shared.ts +269 -0
  138. package/src/ontology-storage/sources.ts +653 -0
  139. package/src/pipeline-run-storage.ts +506 -0
  140. package/src/projection-run-storage.ts +592 -0
  141. package/src/rules-storage.ts +375 -0
  142. package/src/run-list-query.ts +98 -0
  143. package/src/sql.d.ts +4 -0
  144. package/src/storage-errors.ts +3 -0
  145. package/src/sync-run-storage.ts +299 -0
  146. package/src/timeseries-storage.ts +212 -0
  147. package/src/transactions.ts +96 -0
  148. package/src/validation.ts +30 -0
  149. package/src/webhook-delivery-storage.ts +272 -0
  150. package/src/webhook-run-storage.ts +240 -0
  151. package/src/workflow-intervention-storage.ts +398 -0
  152. package/src/workflow-run-storage.ts +1148 -0
@@ -0,0 +1,506 @@
1
+ import type { Database } from "bun:sqlite"
2
+ import type { DatasetVersionRef } from "@sixb/core/lake-storage"
3
+ import type {
4
+ FinishPipelineRunInput,
5
+ FinishPipelineStepRunInput,
6
+ ListLatestPipelineRunsInput,
7
+ ListLatestPipelineRunsResult,
8
+ ListPipelineRunsInput,
9
+ ListPipelineRunsResult,
10
+ ListPipelineStepRunsInput,
11
+ ListPipelineStepRunsResult,
12
+ PipelineRunFailure,
13
+ PipelineRunRecord,
14
+ PipelineRunStorage,
15
+ PipelineStepRunRecord,
16
+ StartPipelineRunInput,
17
+ StartPipelineStepRunInput,
18
+ } from "@sixb/core/storage"
19
+ import { PipelineRunError } from "@sixb/core/storage"
20
+ import { queryLatestRunsByOwnerId } from "./latest-run-query"
21
+ import { installFreshSqliteSchema } from "./migrations"
22
+ import {
23
+ appendRunListFilters,
24
+ hasEmptyStatuses,
25
+ queryRunList,
26
+ type SqliteValue,
27
+ } from "./run-list-query"
28
+ import { isUniqueConstraintError } from "./storage-errors"
29
+ import {
30
+ closeSqliteStoreConnection,
31
+ openSqliteStoreConnection,
32
+ type SqliteStoreConnection,
33
+ } from "./transactions"
34
+
35
+ export interface SqlitePipelineRunStorageOptions {
36
+ /** Path to SQLite database file. Defaults to ':memory:' for in-memory database. */
37
+ path?: string
38
+ /** Internal shared connection used by bundled SqliteStorage. */
39
+ connection?: SqliteStoreConnection
40
+ }
41
+
42
+ export class SqlitePipelineRunStorage implements PipelineRunStorage {
43
+ private readonly connection: SqliteStoreConnection
44
+ private readonly db: Database
45
+
46
+ constructor(options: SqlitePipelineRunStorageOptions = {}) {
47
+ this.connection = openSqliteStoreConnection(options)
48
+ this.db = this.connection.db
49
+
50
+ if (this.connection.installFreshSchema) {
51
+ installFreshSqliteSchema(this.db)
52
+ }
53
+ }
54
+
55
+ async start(input: StartPipelineRunInput): Promise<PipelineRunRecord> {
56
+ const startedAt = input.startedAt ?? new Date()
57
+
58
+ try {
59
+ this.db
60
+ .query(
61
+ `
62
+ INSERT INTO pipeline_runs (
63
+ project_id,
64
+ id,
65
+ pipeline_id,
66
+ status,
67
+ started_at
68
+ ) VALUES (?, ?, ?, ?, ?)
69
+ `
70
+ )
71
+ .run(input.projectId, input.id, input.pipelineId, "running", startedAt.toISOString())
72
+ } catch (error) {
73
+ if (isUniqueConstraintError(error)) {
74
+ throw new PipelineRunError(
75
+ `[SixbSqlite] Pipeline run '${input.id}' already exists for project '${input.projectId}'.`
76
+ )
77
+ }
78
+
79
+ throw error
80
+ }
81
+
82
+ const record = await this.getById({ projectId: input.projectId, id: input.id })
83
+ if (!record) {
84
+ throw new PipelineRunError(
85
+ `[SixbSqlite] Failed to load pipeline run '${input.id}' for project '${input.projectId}'.`
86
+ )
87
+ }
88
+
89
+ return record
90
+ }
91
+
92
+ async finish(input: FinishPipelineRunInput): Promise<PipelineRunRecord> {
93
+ return this.db.transaction(() => {
94
+ const existing = this.db
95
+ .query("SELECT * FROM pipeline_runs WHERE project_id = ? AND id = ?")
96
+ .get(input.projectId, input.id) as PipelineRunDatabaseRow | null
97
+
98
+ if (!existing) {
99
+ throw new PipelineRunError(
100
+ `[SixbSqlite] Pipeline run '${input.id}' not found for project '${input.projectId}'.`
101
+ )
102
+ }
103
+
104
+ if (existing.status !== "running") {
105
+ throw new PipelineRunError(
106
+ `[SixbSqlite] Pipeline run '${input.id}' for project '${input.projectId}' is already terminal.`
107
+ )
108
+ }
109
+
110
+ this.db
111
+ .query(
112
+ `
113
+ UPDATE pipeline_runs
114
+ SET
115
+ status = ?,
116
+ finished_at = ?,
117
+ output_dataset_id = ?,
118
+ output_version_id = ?,
119
+ error_name = ?,
120
+ error_message = ?
121
+ WHERE project_id = ? AND id = ?
122
+ `
123
+ )
124
+ .run(
125
+ input.status,
126
+ (input.finishedAt ?? new Date()).toISOString(),
127
+ input.status === "succeeded" ? (input.output?.datasetId ?? null) : null,
128
+ input.status === "succeeded" ? (input.output?.versionId ?? null) : null,
129
+ input.status === "succeeded" ? null : (input.error?.name ?? null),
130
+ input.status === "succeeded" ? null : (input.error?.message ?? null),
131
+ input.projectId,
132
+ input.id
133
+ )
134
+
135
+ const updated = this.db
136
+ .query("SELECT * FROM pipeline_runs WHERE project_id = ? AND id = ?")
137
+ .get(input.projectId, input.id) as PipelineRunDatabaseRow
138
+
139
+ return rowToPipelineRunRecord(updated)
140
+ })()
141
+ }
142
+
143
+ async startStep(input: StartPipelineStepRunInput): Promise<PipelineStepRunRecord> {
144
+ return this.db.transaction(() => {
145
+ const pipelineRun = this.db
146
+ .query("SELECT * FROM pipeline_runs WHERE project_id = ? AND id = ?")
147
+ .get(input.projectId, input.pipelineRunId) as PipelineRunDatabaseRow | null
148
+
149
+ if (!pipelineRun) {
150
+ throw new PipelineRunError(
151
+ `[SixbSqlite] Pipeline run '${input.pipelineRunId}' not found for project '${input.projectId}'.`
152
+ )
153
+ }
154
+
155
+ if (pipelineRun.status !== "running") {
156
+ throw new PipelineRunError(
157
+ `[SixbSqlite] Pipeline run '${input.pipelineRunId}' for project '${input.projectId}' is already terminal.`
158
+ )
159
+ }
160
+
161
+ if (pipelineRun.pipeline_id !== input.pipelineId) {
162
+ throw new PipelineRunError(
163
+ `[SixbSqlite] Pipeline step run '${input.id}' pipeline '${input.pipelineId}' does not match pipeline run '${input.pipelineRunId}' pipeline '${pipelineRun.pipeline_id}'.`
164
+ )
165
+ }
166
+
167
+ const startedAt = input.startedAt ?? new Date()
168
+
169
+ try {
170
+ this.db
171
+ .query(
172
+ `
173
+ INSERT INTO pipeline_step_runs (
174
+ project_id,
175
+ id,
176
+ pipeline_run_id,
177
+ pipeline_id,
178
+ step_id,
179
+ dataset_id,
180
+ mode,
181
+ status,
182
+ started_at,
183
+ inputs
184
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
185
+ `
186
+ )
187
+ .run(
188
+ input.projectId,
189
+ input.id,
190
+ input.pipelineRunId,
191
+ input.pipelineId,
192
+ input.stepId,
193
+ input.datasetId,
194
+ input.mode,
195
+ "running",
196
+ startedAt.toISOString(),
197
+ serializeDatasetVersionRefs(input.inputs)
198
+ )
199
+ } catch (error) {
200
+ if (isUniqueConstraintError(error)) {
201
+ throw new PipelineRunError(
202
+ `[SixbSqlite] Pipeline step run '${input.id}' already exists for project '${input.projectId}'.`
203
+ )
204
+ }
205
+
206
+ throw error
207
+ }
208
+
209
+ const row = this.db
210
+ .query("SELECT * FROM pipeline_step_runs WHERE project_id = ? AND id = ?")
211
+ .get(input.projectId, input.id) as PipelineStepRunDatabaseRow | null
212
+
213
+ if (!row) {
214
+ throw new PipelineRunError(
215
+ `[SixbSqlite] Failed to load pipeline step run '${input.id}' for project '${input.projectId}'.`
216
+ )
217
+ }
218
+
219
+ return rowToPipelineStepRunRecord(row)
220
+ })()
221
+ }
222
+
223
+ async finishStep(input: FinishPipelineStepRunInput): Promise<PipelineStepRunRecord> {
224
+ assertOptionalNonNegativeInteger(input.rowsWritten, "rowsWritten")
225
+
226
+ return this.db.transaction(() => {
227
+ const existing = this.db
228
+ .query("SELECT * FROM pipeline_step_runs WHERE project_id = ? AND id = ?")
229
+ .get(input.projectId, input.id) as PipelineStepRunDatabaseRow | null
230
+
231
+ if (!existing) {
232
+ throw new PipelineRunError(
233
+ `[SixbSqlite] Pipeline step run '${input.id}' not found for project '${input.projectId}'.`
234
+ )
235
+ }
236
+
237
+ if (existing.status !== "running") {
238
+ throw new PipelineRunError(
239
+ `[SixbSqlite] Pipeline step run '${input.id}' for project '${input.projectId}' is already terminal.`
240
+ )
241
+ }
242
+
243
+ if (input.status === "succeeded" && input.output.datasetId !== existing.dataset_id) {
244
+ throw new PipelineRunError(
245
+ `[SixbSqlite] Pipeline step run '${input.id}' output dataset '${input.output.datasetId}' does not match '${existing.dataset_id}'.`
246
+ )
247
+ }
248
+
249
+ this.db
250
+ .query(
251
+ `
252
+ UPDATE pipeline_step_runs
253
+ SET
254
+ status = ?,
255
+ finished_at = ?,
256
+ output_version_id = ?,
257
+ rows_written = ?,
258
+ error_name = ?,
259
+ error_message = ?
260
+ WHERE project_id = ? AND id = ?
261
+ `
262
+ )
263
+ .run(
264
+ input.status,
265
+ (input.finishedAt ?? new Date()).toISOString(),
266
+ input.status === "succeeded" ? input.output.versionId : null,
267
+ input.rowsWritten ?? existing.rows_written ?? null,
268
+ input.status === "succeeded" ? null : (input.error?.name ?? null),
269
+ input.status === "succeeded" ? null : (input.error?.message ?? null),
270
+ input.projectId,
271
+ input.id
272
+ )
273
+
274
+ const updated = this.db
275
+ .query("SELECT * FROM pipeline_step_runs WHERE project_id = ? AND id = ?")
276
+ .get(input.projectId, input.id) as PipelineStepRunDatabaseRow
277
+
278
+ return rowToPipelineStepRunRecord(updated)
279
+ })()
280
+ }
281
+
282
+ async getById(params: { projectId: string; id: string }): Promise<PipelineRunRecord | null> {
283
+ const row = this.db
284
+ .query("SELECT * FROM pipeline_runs WHERE project_id = ? AND id = ?")
285
+ .get(params.projectId, params.id) as PipelineRunDatabaseRow | null
286
+
287
+ return row ? rowToPipelineRunRecord(row) : null
288
+ }
289
+
290
+ async list(input: ListPipelineRunsInput): Promise<ListPipelineRunsResult> {
291
+ if (hasEmptyStatuses(input) || input.pipelineIds?.length === 0) {
292
+ return {
293
+ runs: [],
294
+ hasMore: false,
295
+ total: 0,
296
+ }
297
+ }
298
+
299
+ const whereClauses = ["project_id = ?"]
300
+ const args: SqliteValue[] = [input.projectId]
301
+
302
+ if (input.pipelineId) {
303
+ whereClauses.push("pipeline_id = ?")
304
+ args.push(input.pipelineId)
305
+ }
306
+
307
+ if (input.pipelineIds) {
308
+ whereClauses.push(`pipeline_id IN (${input.pipelineIds.map(() => "?").join(", ")})`)
309
+ args.push(...input.pipelineIds)
310
+ }
311
+
312
+ appendRunListFilters(whereClauses, args, input)
313
+
314
+ const { rows, total, hasMore } = queryRunList<PipelineRunDatabaseRow>({
315
+ db: this.db,
316
+ tableName: "pipeline_runs",
317
+ whereClauses,
318
+ args,
319
+ order: input.order,
320
+ limit: input.limit,
321
+ offset: input.offset,
322
+ })
323
+
324
+ return {
325
+ runs: rows.map(rowToPipelineRunRecord),
326
+ hasMore,
327
+ total,
328
+ }
329
+ }
330
+
331
+ async listLatestByPipelineIds(
332
+ input: ListLatestPipelineRunsInput
333
+ ): Promise<ListLatestPipelineRunsResult> {
334
+ const rows = queryLatestRunsByOwnerId<PipelineRunDatabaseRow>({
335
+ db: this.db,
336
+ tableName: "pipeline_runs",
337
+ ownerColumn: "pipeline_id",
338
+ ownerIds: input.pipelineIds,
339
+ projectId: input.projectId,
340
+ ownerIdFor: (row) => row.pipeline_id,
341
+ })
342
+
343
+ return {
344
+ runs: rows.map(rowToPipelineRunRecord),
345
+ }
346
+ }
347
+
348
+ async listSteps(input: ListPipelineStepRunsInput): Promise<ListPipelineStepRunsResult> {
349
+ if (hasEmptyStatuses(input)) {
350
+ return {
351
+ steps: [],
352
+ hasMore: false,
353
+ total: 0,
354
+ }
355
+ }
356
+
357
+ const whereClauses = ["project_id = ?"]
358
+ const args: SqliteValue[] = [input.projectId]
359
+
360
+ if (input.pipelineRunId) {
361
+ whereClauses.push("pipeline_run_id = ?")
362
+ args.push(input.pipelineRunId)
363
+ }
364
+
365
+ if (input.pipelineId) {
366
+ whereClauses.push("pipeline_id = ?")
367
+ args.push(input.pipelineId)
368
+ }
369
+
370
+ if (input.stepId) {
371
+ whereClauses.push("step_id = ?")
372
+ args.push(input.stepId)
373
+ }
374
+
375
+ if (input.datasetId) {
376
+ whereClauses.push("dataset_id = ?")
377
+ args.push(input.datasetId)
378
+ }
379
+
380
+ appendRunListFilters(whereClauses, args, input)
381
+
382
+ const { rows, total, hasMore } = queryRunList<PipelineStepRunDatabaseRow>({
383
+ db: this.db,
384
+ tableName: "pipeline_step_runs",
385
+ whereClauses,
386
+ args,
387
+ order: input.order,
388
+ limit: input.limit,
389
+ offset: input.offset,
390
+ })
391
+
392
+ return {
393
+ steps: rows.map(rowToPipelineStepRunRecord),
394
+ hasMore,
395
+ total,
396
+ }
397
+ }
398
+
399
+ close(): void {
400
+ closeSqliteStoreConnection(this.connection)
401
+ }
402
+ }
403
+
404
+ function serializeDatasetVersionRefs(refs: readonly DatasetVersionRef[]): string {
405
+ return JSON.stringify(refs)
406
+ }
407
+
408
+ function parseDatasetVersionRefs(value: string): readonly DatasetVersionRef[] {
409
+ return JSON.parse(value) as readonly DatasetVersionRef[]
410
+ }
411
+
412
+ function toFailure(row: {
413
+ readonly error_name: string | null
414
+ readonly error_message: string | null
415
+ }): PipelineRunFailure | undefined {
416
+ if (!row.error_message) {
417
+ return undefined
418
+ }
419
+
420
+ return {
421
+ name: row.error_name ?? undefined,
422
+ message: row.error_message,
423
+ }
424
+ }
425
+
426
+ function rowToPipelineRunRecord(row: PipelineRunDatabaseRow): PipelineRunRecord {
427
+ return {
428
+ id: row.id,
429
+ projectId: row.project_id,
430
+ pipelineId: row.pipeline_id,
431
+ status: row.status,
432
+ startedAt: new Date(row.started_at),
433
+ finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
434
+ output:
435
+ row.output_dataset_id && row.output_version_id
436
+ ? {
437
+ datasetId: row.output_dataset_id,
438
+ versionId: row.output_version_id,
439
+ }
440
+ : undefined,
441
+ error: toFailure(row),
442
+ }
443
+ }
444
+
445
+ function rowToPipelineStepRunRecord(row: PipelineStepRunDatabaseRow): PipelineStepRunRecord {
446
+ return {
447
+ id: row.id,
448
+ projectId: row.project_id,
449
+ pipelineRunId: row.pipeline_run_id,
450
+ pipelineId: row.pipeline_id,
451
+ stepId: row.step_id,
452
+ datasetId: row.dataset_id,
453
+ mode: row.mode,
454
+ status: row.status,
455
+ startedAt: new Date(row.started_at),
456
+ finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
457
+ inputs: parseDatasetVersionRefs(row.inputs),
458
+ output: row.output_version_id
459
+ ? {
460
+ datasetId: row.dataset_id,
461
+ versionId: row.output_version_id,
462
+ }
463
+ : undefined,
464
+ rowsWritten: row.rows_written ?? undefined,
465
+ error: toFailure(row),
466
+ }
467
+ }
468
+
469
+ function assertOptionalNonNegativeInteger(value: number | undefined, fieldName: string): void {
470
+ if (value !== undefined && (!Number.isInteger(value) || value < 0)) {
471
+ throw new PipelineRunError(
472
+ `[SixbSqlite] Pipeline run ${fieldName} must be a non-negative integer.`
473
+ )
474
+ }
475
+ }
476
+
477
+ interface PipelineRunDatabaseRow {
478
+ project_id: string
479
+ id: string
480
+ pipeline_id: string
481
+ status: PipelineRunRecord["status"]
482
+ started_at: string
483
+ finished_at: string | null
484
+ output_dataset_id: string | null
485
+ output_version_id: string | null
486
+ error_name: string | null
487
+ error_message: string | null
488
+ }
489
+
490
+ interface PipelineStepRunDatabaseRow {
491
+ project_id: string
492
+ id: string
493
+ pipeline_run_id: string
494
+ pipeline_id: string
495
+ step_id: string
496
+ dataset_id: string
497
+ mode: PipelineStepRunRecord["mode"]
498
+ status: PipelineStepRunRecord["status"]
499
+ started_at: string
500
+ finished_at: string | null
501
+ inputs: string
502
+ output_version_id: string | null
503
+ rows_written: number | null
504
+ error_name: string | null
505
+ error_message: string | null
506
+ }