@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.
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/action-run-storage.d.ts +29 -0
- package/dist/action-run-storage.d.ts.map +1 -0
- package/dist/agents/agent-storage.d.ts +25 -0
- package/dist/agents/agent-storage.d.ts.map +1 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/messages.d.ts +14 -0
- package/dist/agents/messages.d.ts.map +1 -0
- package/dist/agents/rows.d.ts +77 -0
- package/dist/agents/rows.d.ts.map +1 -0
- package/dist/agents/runs.d.ts +26 -0
- package/dist/agents/runs.d.ts.map +1 -0
- package/dist/agents/threads.d.ts +14 -0
- package/dist/agents/threads.d.ts.map +1 -0
- package/dist/auth-storage/access-tokens.d.ts +33 -0
- package/dist/auth-storage/access-tokens.d.ts.map +1 -0
- package/dist/auth-storage/auth-storage.d.ts +46 -0
- package/dist/auth-storage/auth-storage.d.ts.map +1 -0
- package/dist/auth-storage/group-memberships.d.ts +21 -0
- package/dist/auth-storage/group-memberships.d.ts.map +1 -0
- package/dist/auth-storage/identities.d.ts +17 -0
- package/dist/auth-storage/identities.d.ts.map +1 -0
- package/dist/auth-storage/index.d.ts +3 -0
- package/dist/auth-storage/index.d.ts.map +1 -0
- package/dist/auth-storage/invitations.d.ts +28 -0
- package/dist/auth-storage/invitations.d.ts.map +1 -0
- package/dist/auth-storage/magic-links.d.ts +28 -0
- package/dist/auth-storage/magic-links.d.ts.map +1 -0
- package/dist/auth-storage/oidc-attempts.d.ts +18 -0
- package/dist/auth-storage/oidc-attempts.d.ts.map +1 -0
- package/dist/auth-storage/rows.d.ts +134 -0
- package/dist/auth-storage/rows.d.ts.map +1 -0
- package/dist/auth-storage/service-account-group-memberships.d.ts +17 -0
- package/dist/auth-storage/service-account-group-memberships.d.ts.map +1 -0
- package/dist/auth-storage/service-accounts.d.ts +14 -0
- package/dist/auth-storage/service-accounts.d.ts.map +1 -0
- package/dist/auth-storage/sessions.d.ts +55 -0
- package/dist/auth-storage/sessions.d.ts.map +1 -0
- package/dist/auth-storage/shared.d.ts +131 -0
- package/dist/auth-storage/shared.d.ts.map +1 -0
- package/dist/auth-storage/users.d.ts +19 -0
- package/dist/auth-storage/users.d.ts.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10204 -0
- package/dist/index.js.map +54 -0
- package/dist/latest-run-query.d.ts +22 -0
- package/dist/latest-run-query.d.ts.map +1 -0
- package/dist/migrations.d.ts +18 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/object-query-compiler.d.ts +44 -0
- package/dist/object-query-compiler.d.ts.map +1 -0
- package/dist/object-storage.d.ts +93 -0
- package/dist/object-storage.d.ts.map +1 -0
- package/dist/ontology-storage/commits.d.ts +14 -0
- package/dist/ontology-storage/commits.d.ts.map +1 -0
- package/dist/ontology-storage/index.d.ts +22 -0
- package/dist/ontology-storage/index.d.ts.map +1 -0
- package/dist/ontology-storage/materialization-session.d.ts +49 -0
- package/dist/ontology-storage/materialization-session.d.ts.map +1 -0
- package/dist/ontology-storage/materialization-state.d.ts +63 -0
- package/dist/ontology-storage/materialization-state.d.ts.map +1 -0
- package/dist/ontology-storage/materialization-writer.d.ts +15 -0
- package/dist/ontology-storage/materialization-writer.d.ts.map +1 -0
- package/dist/ontology-storage/materializations.d.ts +32 -0
- package/dist/ontology-storage/materializations.d.ts.map +1 -0
- package/dist/ontology-storage/outbox.d.ts +14 -0
- package/dist/ontology-storage/outbox.d.ts.map +1 -0
- package/dist/ontology-storage/shared.d.ts +91 -0
- package/dist/ontology-storage/shared.d.ts.map +1 -0
- package/dist/ontology-storage/sources.d.ts +26 -0
- package/dist/ontology-storage/sources.d.ts.map +1 -0
- package/dist/pipeline-run-storage.d.ts +26 -0
- package/dist/pipeline-run-storage.d.ts.map +1 -0
- package/dist/projection-run-storage.d.ts +32 -0
- package/dist/projection-run-storage.d.ts.map +1 -0
- package/dist/rules-storage.d.ts +33 -0
- package/dist/rules-storage.d.ts.map +1 -0
- package/dist/run-list-query.d.ts +27 -0
- package/dist/run-list-query.d.ts.map +1 -0
- package/dist/storage-errors.d.ts +2 -0
- package/dist/storage-errors.d.ts.map +1 -0
- package/dist/sync-run-storage.d.ts +23 -0
- package/dist/sync-run-storage.d.ts.map +1 -0
- package/dist/timeseries-storage.d.ts +41 -0
- package/dist/timeseries-storage.d.ts.map +1 -0
- package/dist/transactions.d.ts +15 -0
- package/dist/transactions.d.ts.map +1 -0
- package/dist/tsconfig.json +5 -0
- package/dist/validation.d.ts +5 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/webhook-delivery-storage.d.ts +27 -0
- package/dist/webhook-delivery-storage.d.ts.map +1 -0
- package/dist/webhook-run-storage.d.ts +22 -0
- package/dist/webhook-run-storage.d.ts.map +1 -0
- package/dist/workflow-intervention-storage.d.ts +26 -0
- package/dist/workflow-intervention-storage.d.ts.map +1 -0
- package/dist/workflow-run-storage.d.ts +63 -0
- package/dist/workflow-run-storage.d.ts.map +1 -0
- package/package.json +58 -0
- package/src/action-run-storage.ts +761 -0
- package/src/agents/agent-storage.ts +46 -0
- package/src/agents/index.ts +5 -0
- package/src/agents/messages.ts +162 -0
- package/src/agents/rows.ts +205 -0
- package/src/agents/runs.ts +389 -0
- package/src/agents/threads.ts +133 -0
- package/src/auth-storage/access-tokens.ts +289 -0
- package/src/auth-storage/auth-storage.ts +784 -0
- package/src/auth-storage/group-memberships.ts +52 -0
- package/src/auth-storage/identities.ts +98 -0
- package/src/auth-storage/index.ts +2 -0
- package/src/auth-storage/invitations.ts +258 -0
- package/src/auth-storage/magic-links.ts +149 -0
- package/src/auth-storage/oidc-attempts.ts +109 -0
- package/src/auth-storage/rows.ts +338 -0
- package/src/auth-storage/service-account-group-memberships.ts +161 -0
- package/src/auth-storage/service-accounts.ts +168 -0
- package/src/auth-storage/sessions.ts +230 -0
- package/src/auth-storage/shared.ts +707 -0
- package/src/auth-storage/users.ts +226 -0
- package/src/index.ts +292 -0
- package/src/latest-run-query.ts +49 -0
- package/src/migrations/001-initial-schema.sql +1068 -0
- package/src/migrations.ts +245 -0
- package/src/object-query-compiler.ts +1198 -0
- package/src/object-storage.ts +605 -0
- package/src/ontology-storage/commits.ts +127 -0
- package/src/ontology-storage/index.ts +35 -0
- package/src/ontology-storage/materialization-session.ts +393 -0
- package/src/ontology-storage/materialization-state.ts +901 -0
- package/src/ontology-storage/materialization-writer.ts +394 -0
- package/src/ontology-storage/materializations.ts +838 -0
- package/src/ontology-storage/outbox.ts +237 -0
- package/src/ontology-storage/shared.ts +269 -0
- package/src/ontology-storage/sources.ts +653 -0
- package/src/pipeline-run-storage.ts +506 -0
- package/src/projection-run-storage.ts +592 -0
- package/src/rules-storage.ts +375 -0
- package/src/run-list-query.ts +98 -0
- package/src/sql.d.ts +4 -0
- package/src/storage-errors.ts +3 -0
- package/src/sync-run-storage.ts +299 -0
- package/src/timeseries-storage.ts +212 -0
- package/src/transactions.ts +96 -0
- package/src/validation.ts +30 -0
- package/src/webhook-delivery-storage.ts +272 -0
- package/src/webhook-run-storage.ts +240 -0
- package/src/workflow-intervention-storage.ts +398 -0
- package/src/workflow-run-storage.ts +1148 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite"
|
|
2
|
+
import type {
|
|
3
|
+
WebhookDeliveryClaimRecord,
|
|
4
|
+
WebhookDeliveryClaimResult,
|
|
5
|
+
WebhookDeliveryKey,
|
|
6
|
+
WebhookDeliveryRecord,
|
|
7
|
+
WebhookDeliveryStorage,
|
|
8
|
+
} from "@sixb/core/storage"
|
|
9
|
+
import { installFreshSqliteSchema } from "./migrations"
|
|
10
|
+
import {
|
|
11
|
+
closeSqliteStoreConnection,
|
|
12
|
+
openSqliteStoreConnection,
|
|
13
|
+
type SqliteStoreConnection,
|
|
14
|
+
} from "./transactions"
|
|
15
|
+
|
|
16
|
+
export interface SqliteWebhookDeliveryStorageOptions {
|
|
17
|
+
/** Path to SQLite database file. Defaults to ':memory:' for in-memory database. */
|
|
18
|
+
path?: string
|
|
19
|
+
/** Internal shared connection used by bundled SqliteStorage. */
|
|
20
|
+
connection?: SqliteStoreConnection
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class SqliteWebhookDeliveryStorage implements WebhookDeliveryStorage {
|
|
24
|
+
private readonly connection: SqliteStoreConnection
|
|
25
|
+
private readonly db: Database
|
|
26
|
+
|
|
27
|
+
constructor(options: SqliteWebhookDeliveryStorageOptions = {}) {
|
|
28
|
+
this.connection = openSqliteStoreConnection(options)
|
|
29
|
+
this.db = this.connection.db
|
|
30
|
+
|
|
31
|
+
if (this.connection.installFreshSchema) {
|
|
32
|
+
installFreshSqliteSchema(this.db)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async claim(
|
|
37
|
+
input: WebhookDeliveryKey & { receivedAt: string }
|
|
38
|
+
): Promise<WebhookDeliveryClaimRecord> {
|
|
39
|
+
return this.db.transaction(() => {
|
|
40
|
+
// Atomic first claim: only one request can insert this scoped delivery key.
|
|
41
|
+
const inserted = this.db
|
|
42
|
+
.query(
|
|
43
|
+
`
|
|
44
|
+
INSERT OR IGNORE INTO webhook_deliveries (
|
|
45
|
+
project_id,
|
|
46
|
+
connector_id,
|
|
47
|
+
webhook_id,
|
|
48
|
+
idempotency_key,
|
|
49
|
+
status,
|
|
50
|
+
received_at
|
|
51
|
+
) VALUES (?, ?, ?, ?, 'in_progress', ?)
|
|
52
|
+
`
|
|
53
|
+
)
|
|
54
|
+
.run(
|
|
55
|
+
input.projectId,
|
|
56
|
+
input.connectorId,
|
|
57
|
+
input.webhookId,
|
|
58
|
+
input.idempotencyKey,
|
|
59
|
+
input.receivedAt
|
|
60
|
+
) as { changes: number }
|
|
61
|
+
|
|
62
|
+
if (inserted.changes > 0) {
|
|
63
|
+
return toWebhookDeliveryClaimRecord(
|
|
64
|
+
{
|
|
65
|
+
projectId: input.projectId,
|
|
66
|
+
connectorId: input.connectorId,
|
|
67
|
+
webhookId: input.webhookId,
|
|
68
|
+
idempotencyKey: input.idempotencyKey,
|
|
69
|
+
status: "in_progress",
|
|
70
|
+
receivedAt: input.receivedAt,
|
|
71
|
+
},
|
|
72
|
+
"claimed"
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const existing = this.getDeliveryRecord(input)
|
|
77
|
+
|
|
78
|
+
if (existing?.status === "completed") {
|
|
79
|
+
return toWebhookDeliveryClaimRecord(existing, "duplicate")
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (existing?.status === "in_progress") {
|
|
83
|
+
return toWebhookDeliveryClaimRecord(existing, "in_progress")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (existing?.status !== "failed") {
|
|
87
|
+
throw new Error("[SixbSqlite] Webhook delivery row disappeared during claim.")
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Failed deliveries are intentionally claimable so provider retries can run again.
|
|
91
|
+
const claimed = this.db
|
|
92
|
+
.query(
|
|
93
|
+
`
|
|
94
|
+
UPDATE webhook_deliveries
|
|
95
|
+
SET status = 'in_progress',
|
|
96
|
+
received_at = ?,
|
|
97
|
+
completed_at = NULL,
|
|
98
|
+
failed_at = NULL,
|
|
99
|
+
error = NULL
|
|
100
|
+
WHERE project_id = ?
|
|
101
|
+
AND connector_id = ?
|
|
102
|
+
AND webhook_id = ?
|
|
103
|
+
AND idempotency_key = ?
|
|
104
|
+
AND status = 'failed'
|
|
105
|
+
`
|
|
106
|
+
)
|
|
107
|
+
.run(
|
|
108
|
+
input.receivedAt,
|
|
109
|
+
input.projectId,
|
|
110
|
+
input.connectorId,
|
|
111
|
+
input.webhookId,
|
|
112
|
+
input.idempotencyKey
|
|
113
|
+
) as { changes: number }
|
|
114
|
+
|
|
115
|
+
if (claimed.changes > 0) {
|
|
116
|
+
return toWebhookDeliveryClaimRecord(this.requireDeliveryRecord(input, "claim"), "claimed")
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const current = this.requireDeliveryRecord(input, "claim")
|
|
120
|
+
return toWebhookDeliveryClaimRecord(
|
|
121
|
+
current,
|
|
122
|
+
current.status === "completed" ? "duplicate" : "in_progress"
|
|
123
|
+
)
|
|
124
|
+
})()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async complete(
|
|
128
|
+
input: WebhookDeliveryKey & { completedAt: string }
|
|
129
|
+
): Promise<WebhookDeliveryRecord> {
|
|
130
|
+
this.db
|
|
131
|
+
.query(
|
|
132
|
+
`
|
|
133
|
+
UPDATE webhook_deliveries
|
|
134
|
+
SET status = 'completed',
|
|
135
|
+
completed_at = ?,
|
|
136
|
+
failed_at = NULL,
|
|
137
|
+
error = NULL
|
|
138
|
+
WHERE project_id = ?
|
|
139
|
+
AND connector_id = ?
|
|
140
|
+
AND webhook_id = ?
|
|
141
|
+
AND idempotency_key = ?
|
|
142
|
+
`
|
|
143
|
+
)
|
|
144
|
+
.run(
|
|
145
|
+
input.completedAt,
|
|
146
|
+
input.projectId,
|
|
147
|
+
input.connectorId,
|
|
148
|
+
input.webhookId,
|
|
149
|
+
input.idempotencyKey
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return this.requireDeliveryRecord(input, "complete")
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async fail(
|
|
156
|
+
input: WebhookDeliveryKey & { failedAt: string; error: string }
|
|
157
|
+
): Promise<WebhookDeliveryRecord> {
|
|
158
|
+
this.db
|
|
159
|
+
.query(
|
|
160
|
+
`
|
|
161
|
+
UPDATE webhook_deliveries
|
|
162
|
+
SET status = 'failed',
|
|
163
|
+
failed_at = ?,
|
|
164
|
+
error = ?
|
|
165
|
+
WHERE project_id = ?
|
|
166
|
+
AND connector_id = ?
|
|
167
|
+
AND webhook_id = ?
|
|
168
|
+
AND idempotency_key = ?
|
|
169
|
+
`
|
|
170
|
+
)
|
|
171
|
+
.run(
|
|
172
|
+
input.failedAt,
|
|
173
|
+
input.error,
|
|
174
|
+
input.projectId,
|
|
175
|
+
input.connectorId,
|
|
176
|
+
input.webhookId,
|
|
177
|
+
input.idempotencyKey
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return this.requireDeliveryRecord(input, "fail")
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
close(): void {
|
|
184
|
+
closeSqliteStoreConnection(this.connection)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private getDeliveryRecord(key: WebhookDeliveryKey): WebhookDeliveryRecord | null {
|
|
188
|
+
const row = this.db
|
|
189
|
+
.query(
|
|
190
|
+
`
|
|
191
|
+
SELECT
|
|
192
|
+
project_id,
|
|
193
|
+
connector_id,
|
|
194
|
+
webhook_id,
|
|
195
|
+
idempotency_key,
|
|
196
|
+
status,
|
|
197
|
+
received_at,
|
|
198
|
+
completed_at,
|
|
199
|
+
failed_at,
|
|
200
|
+
error
|
|
201
|
+
FROM webhook_deliveries
|
|
202
|
+
WHERE project_id = ?
|
|
203
|
+
AND connector_id = ?
|
|
204
|
+
AND webhook_id = ?
|
|
205
|
+
AND idempotency_key = ?
|
|
206
|
+
`
|
|
207
|
+
)
|
|
208
|
+
.get(
|
|
209
|
+
key.projectId,
|
|
210
|
+
key.connectorId,
|
|
211
|
+
key.webhookId,
|
|
212
|
+
key.idempotencyKey
|
|
213
|
+
) as SqliteWebhookDeliveryRow | null
|
|
214
|
+
|
|
215
|
+
return row ? toWebhookDeliveryRecord(row) : null
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private requireDeliveryRecord(
|
|
219
|
+
key: WebhookDeliveryKey,
|
|
220
|
+
action: "claim" | "complete" | "fail"
|
|
221
|
+
): WebhookDeliveryRecord {
|
|
222
|
+
const record = this.getDeliveryRecord(key)
|
|
223
|
+
|
|
224
|
+
if (!record) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`[SixbSqlite] Missing webhook delivery row after ${action} for ${serializeKey(key)}.`
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return record
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
interface SqliteWebhookDeliveryRow {
|
|
235
|
+
readonly project_id: string
|
|
236
|
+
readonly connector_id: string
|
|
237
|
+
readonly webhook_id: string
|
|
238
|
+
readonly idempotency_key: string
|
|
239
|
+
readonly status: WebhookDeliveryRecord["status"]
|
|
240
|
+
readonly received_at: string
|
|
241
|
+
readonly completed_at: string | null
|
|
242
|
+
readonly failed_at: string | null
|
|
243
|
+
readonly error: string | null
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function toWebhookDeliveryRecord(row: SqliteWebhookDeliveryRow): WebhookDeliveryRecord {
|
|
247
|
+
return {
|
|
248
|
+
projectId: row.project_id,
|
|
249
|
+
connectorId: row.connector_id,
|
|
250
|
+
webhookId: row.webhook_id,
|
|
251
|
+
idempotencyKey: row.idempotency_key,
|
|
252
|
+
status: row.status,
|
|
253
|
+
receivedAt: row.received_at,
|
|
254
|
+
completedAt: row.completed_at ?? undefined,
|
|
255
|
+
failedAt: row.failed_at ?? undefined,
|
|
256
|
+
error: row.error ?? undefined,
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function toWebhookDeliveryClaimRecord(
|
|
261
|
+
record: WebhookDeliveryRecord,
|
|
262
|
+
claimResult: WebhookDeliveryClaimResult
|
|
263
|
+
): WebhookDeliveryClaimRecord {
|
|
264
|
+
return {
|
|
265
|
+
...record,
|
|
266
|
+
claimResult,
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function serializeKey(key: WebhookDeliveryKey): string {
|
|
271
|
+
return JSON.stringify([key.projectId, key.connectorId, key.webhookId, key.idempotencyKey])
|
|
272
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite"
|
|
2
|
+
import type {
|
|
3
|
+
FinishWebhookRunInput,
|
|
4
|
+
ListWebhookRunsInput,
|
|
5
|
+
ListWebhookRunsResult,
|
|
6
|
+
StartWebhookRunInput,
|
|
7
|
+
WebhookRunRecord,
|
|
8
|
+
WebhookRunStorage,
|
|
9
|
+
} from "@sixb/core/storage"
|
|
10
|
+
import { WebhookRunError } from "@sixb/core/storage"
|
|
11
|
+
import { installFreshSqliteSchema } from "./migrations"
|
|
12
|
+
import {
|
|
13
|
+
appendRunListFilters,
|
|
14
|
+
hasEmptyStatuses,
|
|
15
|
+
queryRunList,
|
|
16
|
+
type SqliteValue,
|
|
17
|
+
} from "./run-list-query"
|
|
18
|
+
import { isUniqueConstraintError } from "./storage-errors"
|
|
19
|
+
import {
|
|
20
|
+
closeSqliteStoreConnection,
|
|
21
|
+
openSqliteStoreConnection,
|
|
22
|
+
type SqliteStoreConnection,
|
|
23
|
+
} from "./transactions"
|
|
24
|
+
|
|
25
|
+
export interface SqliteWebhookRunStorageOptions {
|
|
26
|
+
/** Path to SQLite database file. Defaults to ':memory:' for in-memory database. */
|
|
27
|
+
path?: string
|
|
28
|
+
/** Internal shared connection used by bundled SqliteStorage. */
|
|
29
|
+
connection?: SqliteStoreConnection
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class SqliteWebhookRunStorage implements WebhookRunStorage {
|
|
33
|
+
private readonly connection: SqliteStoreConnection
|
|
34
|
+
private readonly db: Database
|
|
35
|
+
|
|
36
|
+
constructor(options: SqliteWebhookRunStorageOptions = {}) {
|
|
37
|
+
this.connection = openSqliteStoreConnection(options)
|
|
38
|
+
this.db = this.connection.db
|
|
39
|
+
|
|
40
|
+
if (this.connection.installFreshSchema) {
|
|
41
|
+
installFreshSqliteSchema(this.db)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async start(input: StartWebhookRunInput): Promise<WebhookRunRecord> {
|
|
46
|
+
const startedAt = input.startedAt ?? new Date()
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
this.db
|
|
50
|
+
.query(
|
|
51
|
+
`
|
|
52
|
+
INSERT INTO webhook_runs (
|
|
53
|
+
project_id,
|
|
54
|
+
id,
|
|
55
|
+
connector_id,
|
|
56
|
+
webhook_id,
|
|
57
|
+
status,
|
|
58
|
+
method,
|
|
59
|
+
route,
|
|
60
|
+
started_at
|
|
61
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
62
|
+
`
|
|
63
|
+
)
|
|
64
|
+
.run(
|
|
65
|
+
input.projectId,
|
|
66
|
+
input.id,
|
|
67
|
+
input.connectorId,
|
|
68
|
+
input.webhookId,
|
|
69
|
+
"running",
|
|
70
|
+
input.method,
|
|
71
|
+
input.route,
|
|
72
|
+
startedAt.toISOString()
|
|
73
|
+
)
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (isUniqueConstraintError(error)) {
|
|
76
|
+
throw new WebhookRunError(
|
|
77
|
+
`[SixbSqlite] Webhook run '${input.id}' already exists for project '${input.projectId}'.`
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
throw error
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const record = await this.getById({ projectId: input.projectId, id: input.id })
|
|
85
|
+
if (!record) {
|
|
86
|
+
throw new WebhookRunError(
|
|
87
|
+
`[SixbSqlite] Failed to load webhook run '${input.id}' for project '${input.projectId}'.`
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return record
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async finish(input: FinishWebhookRunInput): Promise<WebhookRunRecord> {
|
|
95
|
+
return this.db.transaction(() => {
|
|
96
|
+
const existing = this.db
|
|
97
|
+
.query("SELECT * FROM webhook_runs WHERE project_id = ? AND id = ?")
|
|
98
|
+
.get(input.projectId, input.id) as WebhookRunDatabaseRow | null
|
|
99
|
+
|
|
100
|
+
if (!existing) {
|
|
101
|
+
throw new WebhookRunError(
|
|
102
|
+
`[SixbSqlite] Webhook run '${input.id}' not found for project '${input.projectId}'.`
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (existing.status !== "running") {
|
|
107
|
+
throw new WebhookRunError(
|
|
108
|
+
`[SixbSqlite] Webhook run '${input.id}' for project '${input.projectId}' is already terminal.`
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.db
|
|
113
|
+
.query(
|
|
114
|
+
`
|
|
115
|
+
UPDATE webhook_runs
|
|
116
|
+
SET
|
|
117
|
+
status = ?,
|
|
118
|
+
finished_at = ?,
|
|
119
|
+
request_body_bytes = ?,
|
|
120
|
+
response_status = ?,
|
|
121
|
+
idempotency_key = ?,
|
|
122
|
+
delivery_claim_result = ?,
|
|
123
|
+
error = ?
|
|
124
|
+
WHERE project_id = ? AND id = ?
|
|
125
|
+
`
|
|
126
|
+
)
|
|
127
|
+
.run(
|
|
128
|
+
input.status,
|
|
129
|
+
(input.finishedAt ?? new Date()).toISOString(),
|
|
130
|
+
input.requestBodyBytes ?? null,
|
|
131
|
+
input.responseStatus ?? null,
|
|
132
|
+
input.idempotencyKey ?? null,
|
|
133
|
+
input.deliveryClaimResult ?? null,
|
|
134
|
+
input.status === "succeeded" ? null : (input.error ?? null),
|
|
135
|
+
input.projectId,
|
|
136
|
+
input.id
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
const updated = this.db
|
|
140
|
+
.query("SELECT * FROM webhook_runs WHERE project_id = ? AND id = ?")
|
|
141
|
+
.get(input.projectId, input.id) as WebhookRunDatabaseRow
|
|
142
|
+
|
|
143
|
+
return rowToWebhookRunRecord(updated)
|
|
144
|
+
})()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async getById(params: { projectId: string; id: string }): Promise<WebhookRunRecord | null> {
|
|
148
|
+
const row = this.db
|
|
149
|
+
.query("SELECT * FROM webhook_runs WHERE project_id = ? AND id = ?")
|
|
150
|
+
.get(params.projectId, params.id) as WebhookRunDatabaseRow | null
|
|
151
|
+
|
|
152
|
+
return row ? rowToWebhookRunRecord(row) : null
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async list(input: ListWebhookRunsInput): Promise<ListWebhookRunsResult> {
|
|
156
|
+
if (hasEmptyStatuses(input)) {
|
|
157
|
+
return {
|
|
158
|
+
runs: [],
|
|
159
|
+
hasMore: false,
|
|
160
|
+
total: 0,
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const whereClauses = ["project_id = ?"]
|
|
165
|
+
const args: SqliteValue[] = [input.projectId]
|
|
166
|
+
|
|
167
|
+
if (input.connectorId) {
|
|
168
|
+
whereClauses.push("connector_id = ?")
|
|
169
|
+
args.push(input.connectorId)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (input.webhookId) {
|
|
173
|
+
whereClauses.push("webhook_id = ?")
|
|
174
|
+
args.push(input.webhookId)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (input.idempotencyKey) {
|
|
178
|
+
whereClauses.push("idempotency_key = ?")
|
|
179
|
+
args.push(input.idempotencyKey)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
appendRunListFilters(whereClauses, args, input)
|
|
183
|
+
|
|
184
|
+
const { rows, total, hasMore } = queryRunList<WebhookRunDatabaseRow>({
|
|
185
|
+
db: this.db,
|
|
186
|
+
tableName: "webhook_runs",
|
|
187
|
+
whereClauses,
|
|
188
|
+
args,
|
|
189
|
+
order: input.order,
|
|
190
|
+
limit: input.limit,
|
|
191
|
+
offset: input.offset,
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
runs: rows.map(rowToWebhookRunRecord),
|
|
196
|
+
hasMore,
|
|
197
|
+
total,
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
close(): void {
|
|
202
|
+
closeSqliteStoreConnection(this.connection)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function rowToWebhookRunRecord(row: WebhookRunDatabaseRow): WebhookRunRecord {
|
|
207
|
+
return {
|
|
208
|
+
id: row.id,
|
|
209
|
+
projectId: row.project_id,
|
|
210
|
+
connectorId: row.connector_id,
|
|
211
|
+
webhookId: row.webhook_id,
|
|
212
|
+
status: row.status,
|
|
213
|
+
method: row.method,
|
|
214
|
+
route: row.route,
|
|
215
|
+
startedAt: new Date(row.started_at),
|
|
216
|
+
finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
|
|
217
|
+
requestBodyBytes: row.request_body_bytes ?? undefined,
|
|
218
|
+
responseStatus: row.response_status ?? undefined,
|
|
219
|
+
idempotencyKey: row.idempotency_key ?? undefined,
|
|
220
|
+
deliveryClaimResult: row.delivery_claim_result ?? undefined,
|
|
221
|
+
error: row.error ?? undefined,
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
interface WebhookRunDatabaseRow {
|
|
226
|
+
readonly project_id: string
|
|
227
|
+
readonly id: string
|
|
228
|
+
readonly connector_id: string
|
|
229
|
+
readonly webhook_id: string
|
|
230
|
+
readonly status: WebhookRunRecord["status"]
|
|
231
|
+
readonly method: string
|
|
232
|
+
readonly route: string
|
|
233
|
+
readonly started_at: string
|
|
234
|
+
readonly finished_at: string | null
|
|
235
|
+
readonly request_body_bytes: number | null
|
|
236
|
+
readonly response_status: number | null
|
|
237
|
+
readonly idempotency_key: string | null
|
|
238
|
+
readonly delivery_claim_result: WebhookRunRecord["deliveryClaimResult"] | null
|
|
239
|
+
readonly error: string | null
|
|
240
|
+
}
|