@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,339 @@
1
+ import type { WorkflowIOSnapshot } from "@sixb/core/internal/workflows"
2
+ import type {
3
+ CancelWorkflowInterventionInput,
4
+ CreateWorkflowInterventionInput,
5
+ ExpireWorkflowInterventionInput,
6
+ ListWorkflowInterventionsInput,
7
+ ListWorkflowInterventionsResult,
8
+ SubmitWorkflowInterventionInput,
9
+ WorkflowInterventionActor,
10
+ WorkflowInterventionRecord,
11
+ WorkflowInterventionStorage,
12
+ } from "@sixb/core/storage"
13
+ import { WorkflowInterventionError } from "@sixb/core/storage"
14
+ import type { SQLClient, SqlParameter } from "./pg-client"
15
+ import { isUniqueViolation } from "./storage-errors"
16
+ import { type PgStoreClient, runPgTransaction } from "./transactions"
17
+
18
+ export class PgWorkflowInterventionStorage implements WorkflowInterventionStorage {
19
+ constructor(private readonly sql: PgStoreClient) {}
20
+
21
+ async create(input: CreateWorkflowInterventionInput): Promise<WorkflowInterventionRecord> {
22
+ assertNonNegativeInteger(input.nodeIndex, "nodeIndex")
23
+
24
+ try {
25
+ const [row] = await this.sql<WorkflowInterventionDatabaseRow[]>`
26
+ INSERT INTO workflow_interventions (
27
+ project_id,
28
+ id,
29
+ workflow_id,
30
+ workflow_run_id,
31
+ node_run_id,
32
+ node_index,
33
+ node_id,
34
+ node_key,
35
+ intervention_id,
36
+ input,
37
+ default_response,
38
+ status,
39
+ requested_at,
40
+ expires_at
41
+ ) VALUES (
42
+ ${input.projectId},
43
+ ${input.id},
44
+ ${input.workflowId},
45
+ ${input.workflowRunId},
46
+ ${input.nodeRunId},
47
+ ${input.nodeIndex},
48
+ ${input.nodeId},
49
+ ${input.nodeKey},
50
+ ${input.interventionId},
51
+ ${serializeRecord(input.input)}::text::jsonb,
52
+ ${serializeRecord(input.defaultResponse)}::text::jsonb,
53
+ ${"pending"},
54
+ ${input.requestedAt ?? new Date()},
55
+ ${input.expiresAt ?? null}
56
+ )
57
+ RETURNING *
58
+ `
59
+
60
+ return rowToWorkflowInterventionRecord(row)
61
+ } catch (error) {
62
+ if (isUniqueViolation(error)) {
63
+ throw new WorkflowInterventionError(
64
+ `[SixbPg] Workflow intervention '${input.id}' already exists for project '${input.projectId}'.`
65
+ )
66
+ }
67
+
68
+ throw error
69
+ }
70
+ }
71
+
72
+ async submit(input: SubmitWorkflowInterventionInput): Promise<WorkflowInterventionRecord> {
73
+ return runPgTransaction(this.sql, async (tx) => {
74
+ await requirePendingIntervention(tx, input.projectId, input.id)
75
+
76
+ const [updated] = await tx<WorkflowInterventionDatabaseRow[]>`
77
+ UPDATE workflow_interventions
78
+ SET
79
+ status = ${"submitted"},
80
+ submitted_at = ${input.submittedAt ?? new Date()},
81
+ submitted_by = ${serializeActor(input.submittedBy)}::text::jsonb,
82
+ response = ${serializeRecord(input.response)}::text::jsonb
83
+ WHERE project_id = ${input.projectId} AND id = ${input.id}
84
+ RETURNING *
85
+ `
86
+
87
+ return rowToWorkflowInterventionRecord(updated)
88
+ })
89
+ }
90
+
91
+ async cancel(input: CancelWorkflowInterventionInput): Promise<WorkflowInterventionRecord> {
92
+ return runPgTransaction(this.sql, async (tx) => {
93
+ await requirePendingIntervention(tx, input.projectId, input.id)
94
+
95
+ const [updated] = await tx<WorkflowInterventionDatabaseRow[]>`
96
+ UPDATE workflow_interventions
97
+ SET
98
+ status = ${"cancelled"},
99
+ cancelled_at = ${input.cancelledAt ?? new Date()},
100
+ cancelled_by = ${serializeActor(input.cancelledBy)}::text::jsonb
101
+ WHERE project_id = ${input.projectId} AND id = ${input.id}
102
+ RETURNING *
103
+ `
104
+
105
+ return rowToWorkflowInterventionRecord(updated)
106
+ })
107
+ }
108
+
109
+ async expire(input: ExpireWorkflowInterventionInput): Promise<WorkflowInterventionRecord> {
110
+ return runPgTransaction(this.sql, async (tx) => {
111
+ await requirePendingIntervention(tx, input.projectId, input.id)
112
+
113
+ const [updated] = await tx<WorkflowInterventionDatabaseRow[]>`
114
+ UPDATE workflow_interventions
115
+ SET
116
+ status = ${"expired"},
117
+ expired_at = ${input.expiredAt ?? new Date()}
118
+ WHERE project_id = ${input.projectId} AND id = ${input.id}
119
+ RETURNING *
120
+ `
121
+
122
+ return rowToWorkflowInterventionRecord(updated)
123
+ })
124
+ }
125
+
126
+ async getById(params: {
127
+ projectId: string
128
+ id: string
129
+ }): Promise<WorkflowInterventionRecord | null> {
130
+ const [row] = await this.sql<WorkflowInterventionDatabaseRow[]>`
131
+ SELECT * FROM workflow_interventions
132
+ WHERE project_id = ${params.projectId} AND id = ${params.id}
133
+ `
134
+
135
+ return row ? rowToWorkflowInterventionRecord(row) : null
136
+ }
137
+
138
+ async list(input: ListWorkflowInterventionsInput): Promise<ListWorkflowInterventionsResult> {
139
+ if ((input.statuses && input.statuses.length === 0) || input.workflowIds?.length === 0) {
140
+ return {
141
+ interventions: [],
142
+ hasMore: false,
143
+ total: 0,
144
+ }
145
+ }
146
+
147
+ const whereClauses = ["project_id = $1"]
148
+ const params: SqlParameter[] = [input.projectId]
149
+ let index = 2
150
+
151
+ if (input.statuses) {
152
+ const placeholders = input.statuses.map(() => `$${index++}`)
153
+ whereClauses.push(`status IN (${placeholders.join(", ")})`)
154
+ params.push(...input.statuses)
155
+ }
156
+
157
+ if (input.workflowId) {
158
+ whereClauses.push(`workflow_id = $${index++}`)
159
+ params.push(input.workflowId)
160
+ }
161
+
162
+ if (input.workflowIds) {
163
+ const placeholders = input.workflowIds.map(() => `$${index++}`)
164
+ whereClauses.push(`workflow_id IN (${placeholders.join(", ")})`)
165
+ params.push(...input.workflowIds)
166
+ }
167
+
168
+ if (input.workflowRunId) {
169
+ whereClauses.push(`workflow_run_id = $${index++}`)
170
+ params.push(input.workflowRunId)
171
+ }
172
+
173
+ if (input.nodeRunId) {
174
+ whereClauses.push(`node_run_id = $${index++}`)
175
+ params.push(input.nodeRunId)
176
+ }
177
+
178
+ if (input.nodeId) {
179
+ whereClauses.push(`node_id = $${index++}`)
180
+ params.push(input.nodeId)
181
+ }
182
+
183
+ if (input.nodeKey) {
184
+ whereClauses.push(`node_key = $${index++}`)
185
+ params.push(input.nodeKey)
186
+ }
187
+
188
+ if (input.interventionId) {
189
+ whereClauses.push(`intervention_id = $${index++}`)
190
+ params.push(input.interventionId)
191
+ }
192
+
193
+ if (input.requestedAfter) {
194
+ whereClauses.push(`requested_at >= $${index++}`)
195
+ params.push(input.requestedAfter)
196
+ }
197
+
198
+ if (input.requestedBefore) {
199
+ whereClauses.push(`requested_at <= $${index++}`)
200
+ params.push(input.requestedBefore)
201
+ }
202
+
203
+ const where = `WHERE ${whereClauses.join(" AND ")}`
204
+ const order = input.order === "asc" ? "ASC" : "DESC"
205
+ const offset = input.offset ?? 0
206
+ const [totalRow] = await this.sql.unsafe<{ count: string | number }[]>(
207
+ `SELECT COUNT(*)::bigint AS count FROM workflow_interventions ${where}`,
208
+ params
209
+ )
210
+
211
+ const queryParams = [...params]
212
+ let query = `
213
+ SELECT * FROM workflow_interventions
214
+ ${where}
215
+ ORDER BY requested_at ${order}, id ${order}
216
+ `
217
+
218
+ if (input.limit !== undefined) {
219
+ query += ` LIMIT $${index++} OFFSET $${index++}`
220
+ queryParams.push(input.limit, offset)
221
+ } else if (offset > 0) {
222
+ query += ` OFFSET $${index++}`
223
+ queryParams.push(offset)
224
+ }
225
+
226
+ const rows = await this.sql.unsafe<WorkflowInterventionDatabaseRow[]>(query, queryParams)
227
+ const total = Number(totalRow?.count ?? 0)
228
+
229
+ return {
230
+ interventions: rows.map(rowToWorkflowInterventionRecord),
231
+ hasMore: offset + rows.length < total,
232
+ total,
233
+ }
234
+ }
235
+ }
236
+
237
+ async function requirePendingIntervention(
238
+ sql: SQLClient,
239
+ projectId: string,
240
+ id: string
241
+ ): Promise<WorkflowInterventionDatabaseRow> {
242
+ const [row] = await sql<WorkflowInterventionDatabaseRow[]>`
243
+ SELECT * FROM workflow_interventions
244
+ WHERE project_id = ${projectId} AND id = ${id}
245
+ FOR UPDATE
246
+ `
247
+
248
+ if (!row) {
249
+ throw new WorkflowInterventionError(
250
+ `[SixbPg] Workflow intervention '${id}' not found for project '${projectId}'.`
251
+ )
252
+ }
253
+
254
+ if (row.status !== "pending") {
255
+ throw new WorkflowInterventionError(
256
+ `[SixbPg] Workflow intervention '${id}' for project '${projectId}' is not pending.`
257
+ )
258
+ }
259
+
260
+ return row
261
+ }
262
+
263
+ function serializeRecord(value: WorkflowIOSnapshot): string {
264
+ return JSON.stringify(value)
265
+ }
266
+
267
+ function parseRecord(value: WorkflowIOSnapshot | string): WorkflowIOSnapshot {
268
+ return typeof value === "string" ? (JSON.parse(value) as WorkflowIOSnapshot) : value
269
+ }
270
+
271
+ function serializeActor(actor: WorkflowInterventionActor | undefined): string | null {
272
+ return actor ? JSON.stringify(actor) : null
273
+ }
274
+
275
+ function parseActor(
276
+ value: WorkflowInterventionActor | string | null
277
+ ): WorkflowInterventionActor | undefined {
278
+ return typeof value === "string"
279
+ ? (JSON.parse(value) as WorkflowInterventionActor)
280
+ : (value ?? undefined)
281
+ }
282
+
283
+ function rowToWorkflowInterventionRecord(
284
+ row: WorkflowInterventionDatabaseRow
285
+ ): WorkflowInterventionRecord {
286
+ return {
287
+ id: row.id,
288
+ projectId: row.project_id,
289
+ workflowId: row.workflow_id,
290
+ workflowRunId: row.workflow_run_id,
291
+ nodeRunId: row.node_run_id,
292
+ nodeIndex: Number(row.node_index),
293
+ nodeId: row.node_id,
294
+ nodeKey: row.node_key,
295
+ interventionId: row.intervention_id,
296
+ input: parseRecord(row.input),
297
+ defaultResponse: parseRecord(row.default_response),
298
+ status: row.status,
299
+ requestedAt: new Date(row.requested_at),
300
+ expiresAt: row.expires_at ? new Date(row.expires_at) : undefined,
301
+ submittedAt: row.submitted_at ? new Date(row.submitted_at) : undefined,
302
+ submittedBy: parseActor(row.submitted_by),
303
+ response: row.response ? parseRecord(row.response) : undefined,
304
+ cancelledAt: row.cancelled_at ? new Date(row.cancelled_at) : undefined,
305
+ cancelledBy: parseActor(row.cancelled_by),
306
+ expiredAt: row.expired_at ? new Date(row.expired_at) : undefined,
307
+ }
308
+ }
309
+
310
+ function assertNonNegativeInteger(value: number, fieldName: string): void {
311
+ if (!Number.isInteger(value) || value < 0) {
312
+ throw new WorkflowInterventionError(
313
+ `[SixbPg] Workflow intervention ${fieldName} must be a non-negative integer.`
314
+ )
315
+ }
316
+ }
317
+
318
+ interface WorkflowInterventionDatabaseRow {
319
+ project_id: string
320
+ id: string
321
+ workflow_id: string
322
+ workflow_run_id: string
323
+ node_run_id: string
324
+ node_index: number | string
325
+ node_id: string
326
+ node_key: string
327
+ intervention_id: string
328
+ input: WorkflowIOSnapshot | string
329
+ default_response: WorkflowIOSnapshot | string
330
+ status: WorkflowInterventionRecord["status"]
331
+ requested_at: Date | string
332
+ expires_at: Date | string | null
333
+ submitted_at: Date | string | null
334
+ submitted_by: WorkflowInterventionActor | string | null
335
+ response: WorkflowIOSnapshot | string | null
336
+ cancelled_at: Date | string | null
337
+ cancelled_by: WorkflowInterventionActor | string | null
338
+ expired_at: Date | string | null
339
+ }