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