@sixb/sqlite 0.1.0 → 0.1.2

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 (110) hide show
  1. package/README.md +18 -13
  2. package/dist/action-run-storage.d.ts +5 -2
  3. package/dist/action-run-storage.d.ts.map +1 -1
  4. package/dist/agents/agent-storage.d.ts +4 -2
  5. package/dist/agents/agent-storage.d.ts.map +1 -1
  6. package/dist/agents/rows.d.ts +3 -10
  7. package/dist/agents/rows.d.ts.map +1 -1
  8. package/dist/agents/runs.d.ts +3 -2
  9. package/dist/agents/runs.d.ts.map +1 -1
  10. package/dist/ai-usage-storage.d.ts +22 -0
  11. package/dist/ai-usage-storage.d.ts.map +1 -0
  12. package/dist/auth-storage/service-accounts.d.ts.map +1 -1
  13. package/dist/auth-storage/shared.d.ts +1 -10
  14. package/dist/auth-storage/shared.d.ts.map +1 -1
  15. package/dist/connector-connection-storage.d.ts +9 -0
  16. package/dist/connector-connection-storage.d.ts.map +1 -0
  17. package/dist/execution-storage.d.ts +14 -0
  18. package/dist/execution-storage.d.ts.map +1 -0
  19. package/dist/index.d.ts +13 -9
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +4114 -1149
  22. package/dist/index.js.map +30 -27
  23. package/dist/latest-run-query.d.ts.map +1 -1
  24. package/dist/migrations.d.ts +0 -1
  25. package/dist/migrations.d.ts.map +1 -1
  26. package/dist/ontology-storage/materialization-session.d.ts +50 -8
  27. package/dist/ontology-storage/materialization-session.d.ts.map +1 -1
  28. package/dist/ontology-storage/materialization-state.d.ts +4 -5
  29. package/dist/ontology-storage/materialization-state.d.ts.map +1 -1
  30. package/dist/ontology-storage/materialization-writer.d.ts.map +1 -1
  31. package/dist/ontology-storage/materializations.d.ts +0 -2
  32. package/dist/ontology-storage/materializations.d.ts.map +1 -1
  33. package/dist/ontology-storage/outbox.d.ts.map +1 -1
  34. package/dist/ontology-storage/shared.d.ts +3 -3
  35. package/dist/ontology-storage/shared.d.ts.map +1 -1
  36. package/dist/ontology-storage/sources.d.ts.map +1 -1
  37. package/dist/pipeline-run-storage.d.ts +7 -2
  38. package/dist/pipeline-run-storage.d.ts.map +1 -1
  39. package/dist/projection-run-storage.d.ts +9 -2
  40. package/dist/projection-run-storage.d.ts.map +1 -1
  41. package/dist/run-list-query.d.ts +1 -1
  42. package/dist/run-list-query.d.ts.map +1 -1
  43. package/dist/sync-run-storage.d.ts +7 -2
  44. package/dist/sync-run-storage.d.ts.map +1 -1
  45. package/dist/testing.d.ts +6 -0
  46. package/dist/testing.d.ts.map +1 -0
  47. package/dist/transactions.d.ts +3 -0
  48. package/dist/transactions.d.ts.map +1 -1
  49. package/dist/webhook-run-storage.d.ts +9 -2
  50. package/dist/webhook-run-storage.d.ts.map +1 -1
  51. package/dist/workflow-run-storage.d.ts +7 -3
  52. package/dist/workflow-run-storage.d.ts.map +1 -1
  53. package/package.json +4 -3
  54. package/src/action-run-storage.ts +51 -74
  55. package/src/agents/agent-storage.ts +5 -3
  56. package/src/agents/rows.ts +8 -40
  57. package/src/agents/runs.ts +27 -21
  58. package/src/ai-usage-storage.ts +324 -0
  59. package/src/auth-storage/service-accounts.ts +14 -21
  60. package/src/auth-storage/shared.ts +0 -22
  61. package/src/connector-connection-storage.ts +784 -0
  62. package/src/execution-storage.ts +158 -0
  63. package/src/index.ts +63 -22
  64. package/src/latest-run-query.ts +7 -1
  65. package/src/migrations/002-workflow-run-output.sql +27 -0
  66. package/src/migrations/003-merge-sync-runs.sql +71 -0
  67. package/src/migrations/004-executions.sql +142 -0
  68. package/src/migrations/005-workflow-executions.sql +7 -0
  69. package/src/migrations/006-narrow-ontology-source-root-index.sql +4 -0
  70. package/src/migrations/007-split-overrides.sql +154 -0
  71. package/src/migrations/008-action-executions.sql +4 -0
  72. package/src/migrations/009-agent-executions.sql +115 -0
  73. package/src/migrations/010-ai-usage-accounting-foundation.sql +74 -0
  74. package/src/migrations/011-sync-failure-record.sql +22 -0
  75. package/src/migrations/012-pipeline-failure-record.sql +45 -0
  76. package/src/migrations/013-workflow-failure-record.sql +45 -0
  77. package/src/migrations/014-agent-failure-record.sql +59 -0
  78. package/src/migrations/015-projection-failure-record.sql +20 -0
  79. package/src/migrations/016-webhook-run-failure-record.sql +19 -0
  80. package/src/migrations/017-action-failure-record.sql +65 -0
  81. package/src/migrations/018-ontology-outbox-failure-record.sql +80 -0
  82. package/src/migrations/019-webhook-delivery-failure-record.sql +62 -0
  83. package/src/migrations/020-drop-run-usage-projections.sql +7 -0
  84. package/src/migrations/021-sync-pipeline-executions.sql +101 -0
  85. package/src/migrations/022-projection-executions.sql +244 -0
  86. package/src/migrations/023-webhook-executions.sql +51 -0
  87. package/src/migrations/024-ontology-commit-executions.sql +58 -0
  88. package/src/migrations/025-connector-connections.sql +257 -0
  89. package/src/migrations.ts +126 -5
  90. package/src/ontology-storage/materialization-session.ts +317 -143
  91. package/src/ontology-storage/materialization-state.ts +398 -150
  92. package/src/ontology-storage/materialization-writer.ts +142 -67
  93. package/src/ontology-storage/materializations.ts +67 -141
  94. package/src/ontology-storage/outbox.ts +10 -3
  95. package/src/ontology-storage/shared.ts +10 -4
  96. package/src/ontology-storage/sources.ts +4 -1
  97. package/src/pipeline-run-storage.ts +128 -43
  98. package/src/projection-run-storage.ts +209 -97
  99. package/src/run-list-query.ts +9 -4
  100. package/src/sync-run-storage.ts +120 -34
  101. package/src/testing.ts +20 -0
  102. package/src/transactions.ts +10 -3
  103. package/src/webhook-run-storage.ts +118 -74
  104. package/src/workflow-run-storage.ts +124 -133
  105. package/dist/validation.d.ts +0 -5
  106. package/dist/validation.d.ts.map +0 -1
  107. package/dist/webhook-delivery-storage.d.ts +0 -27
  108. package/dist/webhook-delivery-storage.d.ts.map +0 -1
  109. package/src/validation.ts +0 -30
  110. package/src/webhook-delivery-storage.ts +0 -272
@@ -0,0 +1,158 @@
1
+ import type { Database } from "bun:sqlite"
2
+ import {
3
+ type ExecutionStorageRow,
4
+ executionRecordFromStorageRow,
5
+ executionRecordToStorageRow,
6
+ normalizeExecutionRecord,
7
+ validateExecutionRecordReferences,
8
+ } from "@sixb/core/internal/execution-storage"
9
+ import type { CreateExecutionInput, ExecutionRecord, ExecutionStorage } from "@sixb/core/storage"
10
+ import { ExecutionStorageError } from "@sixb/core/storage"
11
+ import type { SqliteAuthStorage } from "./auth-storage"
12
+
13
+ export class SqliteExecutionStorage implements ExecutionStorage {
14
+ constructor(
15
+ private readonly db: Database,
16
+ private readonly auth: SqliteAuthStorage
17
+ ) {}
18
+
19
+ async create(input: CreateExecutionInput): Promise<ExecutionRecord> {
20
+ const record = normalizeExecutionRecord(input)
21
+ await validateExecutionRecordReferences(record, {
22
+ auth: this.auth,
23
+ getExecution: (params) => this.getById(params),
24
+ })
25
+ const row = executionRecordToStorageRow(record)
26
+
27
+ try {
28
+ this.db
29
+ .query(
30
+ `
31
+ INSERT INTO executions (
32
+ project_id,
33
+ id,
34
+ executor_kind,
35
+ executor_id,
36
+ source_kind,
37
+ source_id,
38
+ requested_by_user_id,
39
+ requested_by_service_account_id,
40
+ correlation_id,
41
+ parent_execution_id,
42
+ authority_kind,
43
+ authority_user_id,
44
+ authority_service_account_id,
45
+ authority_session_id,
46
+ authority_access_token_id,
47
+ authority_primitive_kind,
48
+ authority_primitive_id,
49
+ authority_kernel_operation,
50
+ created_at
51
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
52
+ `
53
+ )
54
+ .run(
55
+ row.projectId,
56
+ row.id,
57
+ row.executorKind,
58
+ row.executorId,
59
+ row.sourceKind,
60
+ row.sourceId,
61
+ row.requestedByUserId,
62
+ row.requestedByServiceAccountId,
63
+ row.correlationId,
64
+ row.parentExecutionId,
65
+ row.authorityKind,
66
+ row.authorityUserId,
67
+ row.authorityServiceAccountId,
68
+ row.authoritySessionId,
69
+ row.authorityAccessTokenId,
70
+ row.authorityPrimitiveKind,
71
+ row.authorityPrimitiveId,
72
+ row.authorityKernelOperation,
73
+ row.createdAt.toISOString()
74
+ )
75
+ } catch (error) {
76
+ if (isDuplicateExecutionError(error)) {
77
+ throw new ExecutionStorageError(
78
+ "duplicate_execution",
79
+ `[SixbSqlite] Execution '${record.id}' already exists in project '${record.projectId}'.`,
80
+ { cause: error }
81
+ )
82
+ }
83
+ throw error
84
+ }
85
+
86
+ return executionRecordFromStorageRow(row)
87
+ }
88
+
89
+ async getById(params: {
90
+ readonly projectId: string
91
+ readonly id: string
92
+ }): Promise<ExecutionRecord | null> {
93
+ const row = this.db
94
+ .query(
95
+ `
96
+ SELECT *
97
+ FROM executions
98
+ WHERE project_id = ? AND id = ?
99
+ `
100
+ )
101
+ .get(params.projectId, params.id) as SqliteExecutionRow | null
102
+
103
+ return row ? executionRecordFromStorageRow(toStorageRow(row)) : null
104
+ }
105
+ }
106
+
107
+ function toStorageRow(row: SqliteExecutionRow): ExecutionStorageRow {
108
+ return {
109
+ projectId: row.project_id,
110
+ id: row.id,
111
+ executorKind: row.executor_kind,
112
+ executorId: row.executor_id,
113
+ sourceKind: row.source_kind,
114
+ sourceId: row.source_id,
115
+ requestedByUserId: row.requested_by_user_id,
116
+ requestedByServiceAccountId: row.requested_by_service_account_id,
117
+ correlationId: row.correlation_id,
118
+ parentExecutionId: row.parent_execution_id,
119
+ authorityKind: row.authority_kind,
120
+ authorityUserId: row.authority_user_id,
121
+ authorityServiceAccountId: row.authority_service_account_id,
122
+ authoritySessionId: row.authority_session_id,
123
+ authorityAccessTokenId: row.authority_access_token_id,
124
+ authorityPrimitiveKind: row.authority_primitive_kind,
125
+ authorityPrimitiveId: row.authority_primitive_id,
126
+ authorityKernelOperation: row.authority_kernel_operation,
127
+ createdAt: new Date(row.created_at),
128
+ }
129
+ }
130
+
131
+ function isDuplicateExecutionError(error: unknown): boolean {
132
+ return (
133
+ error instanceof Error &&
134
+ error.message.includes("UNIQUE constraint failed: executions.project_id, executions.id")
135
+ )
136
+ }
137
+
138
+ interface SqliteExecutionRow {
139
+ readonly project_id: string
140
+ readonly id: string
141
+ readonly executor_kind: ExecutionStorageRow["executorKind"]
142
+ readonly executor_id: string
143
+ readonly source_kind: ExecutionStorageRow["sourceKind"]
144
+ readonly source_id: string
145
+ readonly requested_by_user_id: string | null
146
+ readonly requested_by_service_account_id: string | null
147
+ readonly correlation_id: string
148
+ readonly parent_execution_id: string | null
149
+ readonly authority_kind: ExecutionStorageRow["authorityKind"]
150
+ readonly authority_user_id: string | null
151
+ readonly authority_service_account_id: string | null
152
+ readonly authority_session_id: string | null
153
+ readonly authority_access_token_id: string | null
154
+ readonly authority_primitive_kind: ExecutionStorageRow["authorityPrimitiveKind"]
155
+ readonly authority_primitive_id: string | null
156
+ readonly authority_kernel_operation: ExecutionStorageRow["authorityKernelOperation"]
157
+ readonly created_at: string
158
+ }
package/src/index.ts CHANGED
@@ -19,13 +19,17 @@ import {
19
19
  createWorkflowRunOperationScope,
20
20
  } from "@sixb/core/internal/storage-operation-scope"
21
21
  import {
22
+ type AiUsageStorage,
22
23
  createTransactionStorageProxy,
23
24
  StorageTransactionError,
24
25
  throwNestedStorageTransaction,
25
26
  } from "@sixb/core/storage"
26
27
  import { SqliteActionRunStorage } from "./action-run-storage"
27
28
  import { SqliteAgentStorage } from "./agents"
29
+ import { SqliteAiUsageStorage } from "./ai-usage-storage"
28
30
  import { SqliteAuthStorage } from "./auth-storage"
31
+ import { SqliteConnectorConnectionStorage } from "./connector-connection-storage"
32
+ import { SqliteExecutionStorage } from "./execution-storage"
29
33
  import {
30
34
  createSqliteStorageMigrators,
31
35
  installFreshSqliteSchema,
@@ -37,6 +41,7 @@ import { SqlitePipelineRunStorage } from "./pipeline-run-storage"
37
41
  import { SqliteProjectionRunStorage } from "./projection-run-storage"
38
42
  import { SqliteRulesStorage } from "./rules-storage"
39
43
  import { SqliteSyncRunStorage } from "./sync-run-storage"
44
+ import { registerSqliteStorageTestingAdapter } from "./testing"
40
45
  import { SqliteTimeseriesStorage } from "./timeseries-storage"
41
46
  import {
42
47
  closeSqliteStoreConnection,
@@ -44,7 +49,6 @@ import {
44
49
  runImmediateTransactionAsync,
45
50
  type SqliteStoreConnection,
46
51
  } from "./transactions"
47
- import { SqliteWebhookDeliveryStorage } from "./webhook-delivery-storage"
48
52
  import { SqliteWebhookRunStorage } from "./webhook-run-storage"
49
53
  import { SqliteWorkflowInterventionStorage } from "./workflow-intervention-storage"
50
54
  import { SqliteWorkflowRunStorage } from "./workflow-run-storage"
@@ -57,8 +61,8 @@ export interface SqliteStorageOptions {
57
61
  /**
58
62
  * SQLite storage provider for Sixb.
59
63
  *
60
- * Bundles object, timeseries, auth, sync run, pipeline run, projection run, workflow run, webhook
61
- * run, and webhook delivery storage backed by SQLite.
64
+ * Bundles object, timeseries, auth, execution, AI usage, sync run, pipeline run, projection run,
65
+ * workflow run, webhook run, and connector connection storage backed by SQLite.
62
66
  *
63
67
  * Usage:
64
68
  * ```ts
@@ -74,7 +78,9 @@ export class SqliteStorage implements MigrationCapableStorage {
74
78
  readonly objects: Storage["objects"]
75
79
  readonly ontology: SqliteOntologyStorage
76
80
  readonly auth: SqliteAuthStorage
81
+ readonly executions: SqliteExecutionStorage
77
82
  readonly agents: SqliteAgentStorage
83
+ readonly aiUsage: AiUsageStorage
78
84
  readonly actionRuns: SqliteActionRunStorage
79
85
  readonly pipelineRuns: SqlitePipelineRunStorage
80
86
  readonly syncRuns: SqliteSyncRunStorage
@@ -82,18 +88,25 @@ export class SqliteStorage implements MigrationCapableStorage {
82
88
  readonly workflowRuns: SqliteWorkflowRunStorage
83
89
  readonly workflowInterventions: SqliteWorkflowInterventionStorage
84
90
  readonly timeseries: Storage["timeseries"]
85
- readonly webhookDeliveries: SqliteWebhookDeliveryStorage
86
91
  readonly webhookRuns: SqliteWebhookRunStorage
87
92
  readonly rules: SqliteRulesStorage
93
+ readonly connectorConnections: SqliteConnectorConnectionStorage
88
94
  readonly migrators: readonly StorageMigrator[]
89
95
 
90
96
  private readonly connection: SqliteStoreConnection
97
+ private readonly readConnection: SqliteStoreConnection
91
98
  private readonly transactionScope = new AsyncLocalStorage<{ active: boolean }>()
92
99
  private transactionTail: Promise<void> = Promise.resolve()
93
100
 
94
101
  constructor(options: SqliteStorageOptions = {}) {
95
102
  const path = options.path ? sqliteStoragePath(options.path) : undefined
96
103
  this.connection = openSqliteStoreConnection({ path })
104
+ if (path) {
105
+ this.connection.db.run("PRAGMA journal_mode = WAL")
106
+ this.readConnection = openSqliteStoreConnection({ path, readonly: true })
107
+ } else {
108
+ this.readConnection = this.connection
109
+ }
97
110
 
98
111
  if (this.connection.installFreshSchema) {
99
112
  installFreshSqliteSchema(this.connection.db)
@@ -112,35 +125,56 @@ export class SqliteStorage implements MigrationCapableStorage {
112
125
  (run) => this.runRootOntologyOperation(run),
113
126
  assertRootOperationAvailable
114
127
  )
115
- this.objects = createOperationScopedFacade(stores.objects, scope)
128
+ const readScope = path
129
+ ? createStorageOperationScope(async (run) => {
130
+ this.assertRootOperationAvailable()
131
+ return run()
132
+ }, assertRootOperationAvailable)
133
+ : scope
134
+ const readObjects = path
135
+ ? new SqliteObjectStorage({ connection: this.readConnection })
136
+ : stores.objects
137
+ const readTimeseries = path
138
+ ? new SqliteTimeseriesStorage({ connection: this.readConnection })
139
+ : stores.timeseries
140
+ this.objects = createOperationScopedFacade(readObjects, readScope)
116
141
  this.ontology = createOntologyOperationScope(stores.ontology, ontologyScope)
117
142
  this.auth = createAuthOperationScope(stores.auth, scope)
143
+ this.executions = createOperationScopedFacade(stores.executions, scope)
118
144
  this.agents = createAgentOperationScope(stores.agents, scope)
145
+ this.aiUsage = createOperationScopedFacade(stores.aiUsage, scope)
119
146
  this.actionRuns = createOperationScopedFacade(stores.actionRuns, scope)
120
147
  this.pipelineRuns = createOperationScopedFacade(stores.pipelineRuns, scope)
121
- this.timeseries = createOperationScopedFacade(stores.timeseries, scope)
148
+ this.timeseries = createOperationScopedFacade(readTimeseries, readScope)
122
149
  this.syncRuns = createOperationScopedFacade(stores.syncRuns, scope)
123
150
  this.projectionRuns = createOperationScopedFacade(stores.projectionRuns, scope)
124
151
  this.workflowRuns = createWorkflowRunOperationScope(stores.workflowRuns, scope)
125
152
  this.workflowInterventions = createOperationScopedFacade(stores.workflowInterventions, scope)
126
- this.webhookDeliveries = createOperationScopedFacade(stores.webhookDeliveries, scope)
127
153
  this.webhookRuns = createOperationScopedFacade(stores.webhookRuns, scope)
128
154
  this.rules = createOperationScopedFacade(stores.rules, scope)
155
+ this.connectorConnections = createOperationScopedFacade(stores.connectorConnections, scope)
129
156
  this.migrators = options.path ? createSqliteStorageMigrators(options.path) : []
157
+ registerSqliteStorageTestingAdapter(this, (durationMs) =>
158
+ stores.connectorConnections.advanceTimeForTesting(durationMs)
159
+ )
130
160
  }
131
161
 
132
162
  async ping(): Promise<void> {
163
+ this.assertRootOperationAvailable()
164
+ if (this.readConnection !== this.connection) {
165
+ this.readConnection.db.query("SELECT 1").get()
166
+ return
167
+ }
133
168
  await this.runRootStorageOperation(() => {
134
169
  this.connection.db.query("SELECT 1").get()
135
170
  })
136
171
  }
137
172
 
138
173
  /**
139
- * The `isolation` option is intentionally ignored. SQLite runs every transaction through one
140
- * shared connection serialized by {@link withTransactionLock} and a `BEGIN IMMEDIATE`, so there is
141
- * no concurrent transaction to isolate against — the lock already provides serializable semantics
142
- * for transaction-vs-transaction races. Postgres, which has true concurrent connections, is where
143
- * `isolation: "serializable"` translates to a real isolation level.
174
+ * The `isolation` option is intentionally ignored. SQLite runs every write transaction through
175
+ * one connection serialized by {@link withTransactionLock} and a `BEGIN IMMEDIATE`, so there is no
176
+ * concurrent writer to isolate against. File-backed object and timeseries reads use a separate WAL
177
+ * snapshot connection; Postgres is where `isolation: "serializable"` maps to a real isolation level.
144
178
  */
145
179
  async transaction<T>(
146
180
  run: (tx: Storage) => Promise<T> | T,
@@ -183,6 +217,7 @@ export class SqliteStorage implements MigrationCapableStorage {
183
217
  }
184
218
 
185
219
  close(): void {
220
+ if (this.readConnection !== this.connection) closeSqliteStoreConnection(this.readConnection)
186
221
  closeSqliteStoreConnection(this.connection)
187
222
  }
188
223
 
@@ -250,6 +285,8 @@ function createSqliteStores(
250
285
  readonly transactionContext: SqliteOntologyTransactionContext | null
251
286
  }
252
287
  ): SqliteStoreSet {
288
+ const auth = new SqliteAuthStorage({ connection })
289
+ const executions = new SqliteExecutionStorage(connection.db, auth)
253
290
  return {
254
291
  objects: new SqliteObjectStorage({ connection }),
255
292
  ontology: new SqliteOntologyStorage({
@@ -257,18 +294,20 @@ function createSqliteStores(
257
294
  runRootOperation: options.runOntologyOperation,
258
295
  transactionContext: options.transactionContext,
259
296
  }),
260
- auth: new SqliteAuthStorage({ connection }),
261
- agents: new SqliteAgentStorage({ connection }),
262
- actionRuns: new SqliteActionRunStorage({ connection }),
263
- pipelineRuns: new SqlitePipelineRunStorage({ connection }),
297
+ auth,
298
+ executions,
299
+ agents: new SqliteAgentStorage({ connection, executions }),
300
+ aiUsage: new SqliteAiUsageStorage({ connection }),
301
+ actionRuns: new SqliteActionRunStorage({ connection, executions }),
302
+ pipelineRuns: new SqlitePipelineRunStorage({ connection, executions }),
264
303
  timeseries: new SqliteTimeseriesStorage({ connection }),
265
- syncRuns: new SqliteSyncRunStorage({ connection }),
266
- projectionRuns: new SqliteProjectionRunStorage({ connection }),
267
- workflowRuns: new SqliteWorkflowRunStorage({ connection }),
304
+ syncRuns: new SqliteSyncRunStorage({ connection, executions }),
305
+ projectionRuns: new SqliteProjectionRunStorage({ connection, executions }),
306
+ workflowRuns: new SqliteWorkflowRunStorage({ connection, executions }),
268
307
  workflowInterventions: new SqliteWorkflowInterventionStorage({ connection }),
269
- webhookDeliveries: new SqliteWebhookDeliveryStorage({ connection }),
270
- webhookRuns: new SqliteWebhookRunStorage({ connection }),
308
+ webhookRuns: new SqliteWebhookRunStorage({ connection, executions }),
271
309
  rules: new SqliteRulesStorage({ connection }),
310
+ connectorConnections: new SqliteConnectorConnectionStorage(connection),
272
311
  }
273
312
  }
274
313
 
@@ -276,7 +315,9 @@ interface SqliteStoreSet {
276
315
  readonly objects: SqliteObjectStorage
277
316
  readonly ontology: SqliteOntologyStorage
278
317
  readonly auth: SqliteAuthStorage
318
+ readonly executions: SqliteExecutionStorage
279
319
  readonly agents: SqliteAgentStorage
320
+ readonly aiUsage: SqliteAiUsageStorage
280
321
  readonly actionRuns: SqliteActionRunStorage
281
322
  readonly pipelineRuns: SqlitePipelineRunStorage
282
323
  readonly syncRuns: SqliteSyncRunStorage
@@ -284,9 +325,9 @@ interface SqliteStoreSet {
284
325
  readonly workflowRuns: SqliteWorkflowRunStorage
285
326
  readonly workflowInterventions: SqliteWorkflowInterventionStorage
286
327
  readonly timeseries: SqliteTimeseriesStorage
287
- readonly webhookDeliveries: SqliteWebhookDeliveryStorage
288
328
  readonly webhookRuns: SqliteWebhookRunStorage
289
329
  readonly rules: SqliteRulesStorage
330
+ readonly connectorConnections: SqliteConnectorConnectionStorage
290
331
  }
291
332
 
292
333
  export { migrateSqliteStorage } from "./migrations"
@@ -21,6 +21,12 @@ export function queryLatestRunsByOwnerId<TRow>(
21
21
  }
22
22
 
23
23
  const placeholders = ownerIds.map(() => "?").join(", ")
24
+ const orderColumn =
25
+ input.tableName === "sync_runs" ||
26
+ input.tableName === "pipeline_runs" ||
27
+ input.tableName === "projection_runs"
28
+ ? "COALESCE(started_at, queued_at)"
29
+ : "started_at"
24
30
  const rows = input.db
25
31
  .query(
26
32
  `
@@ -30,7 +36,7 @@ export function queryLatestRunsByOwnerId<TRow>(
30
36
  *,
31
37
  ROW_NUMBER() OVER (
32
38
  PARTITION BY ${input.ownerColumn}
33
- ORDER BY started_at DESC, id DESC
39
+ ORDER BY ${orderColumn} DESC, id DESC
34
40
  ) AS sixb_latest_rank
35
41
  FROM ${input.tableName}
36
42
  WHERE project_id = ? AND ${input.ownerColumn} IN (${placeholders})
@@ -0,0 +1,27 @@
1
+ ALTER TABLE workflow_runs ADD COLUMN output TEXT;
2
+
3
+ UPDATE workflow_runs AS run
4
+ SET output = CASE
5
+ WHEN EXISTS (
6
+ SELECT 1
7
+ FROM workflow_node_runs AS node
8
+ WHERE node.project_id = run.project_id
9
+ AND node.workflow_run_id = run.id
10
+ AND node.status = 'succeeded'
11
+ AND node.node_type <> 'action'
12
+ ) THEN COALESCE(
13
+ (
14
+ SELECT node.output
15
+ FROM workflow_node_runs AS node
16
+ WHERE node.project_id = run.project_id
17
+ AND node.workflow_run_id = run.id
18
+ AND node.status = 'succeeded'
19
+ AND node.node_type <> 'action'
20
+ ORDER BY node.node_index DESC, node.id DESC
21
+ LIMIT 1
22
+ ),
23
+ '{}'
24
+ )
25
+ ELSE run.input
26
+ END
27
+ WHERE run.status = 'succeeded';
@@ -0,0 +1,71 @@
1
+ DROP INDEX idx_sync_runs_project_started;
2
+ DROP INDEX idx_sync_runs_project_dataset_started;
3
+ DROP INDEX idx_sync_runs_project_sync_started;
4
+ DROP INDEX idx_sync_runs_project_status_started;
5
+
6
+ ALTER TABLE sync_runs RENAME TO sync_runs_before_merge_mode;
7
+
8
+ CREATE TABLE sync_runs (
9
+ project_id TEXT NOT NULL,
10
+ id TEXT NOT NULL,
11
+ sync_id TEXT NOT NULL,
12
+ dataset_id TEXT NOT NULL,
13
+ mode TEXT NOT NULL CHECK (mode IN ('snapshot', 'append', 'merge')),
14
+ status TEXT NOT NULL CHECK (status IN ('running', 'succeeded', 'failed', 'cancelled')),
15
+ started_at TEXT NOT NULL,
16
+ finished_at TEXT,
17
+ rows_read INTEGER CHECK (rows_read IS NULL OR rows_read >= 0),
18
+ output_version_id TEXT,
19
+ expected_latest_version_id TEXT,
20
+ commit_message TEXT,
21
+ error_name TEXT,
22
+ error_message TEXT,
23
+ checkpoint TEXT,
24
+ PRIMARY KEY (project_id, id)
25
+ );
26
+
27
+ INSERT INTO sync_runs (
28
+ project_id,
29
+ id,
30
+ sync_id,
31
+ dataset_id,
32
+ mode,
33
+ status,
34
+ started_at,
35
+ finished_at,
36
+ rows_read,
37
+ output_version_id,
38
+ expected_latest_version_id,
39
+ commit_message,
40
+ error_name,
41
+ error_message,
42
+ checkpoint
43
+ )
44
+ SELECT
45
+ project_id,
46
+ id,
47
+ sync_id,
48
+ dataset_id,
49
+ mode,
50
+ status,
51
+ started_at,
52
+ finished_at,
53
+ rows_read,
54
+ output_version_id,
55
+ expected_latest_version_id,
56
+ commit_message,
57
+ error_name,
58
+ error_message,
59
+ checkpoint
60
+ FROM sync_runs_before_merge_mode;
61
+
62
+ DROP TABLE sync_runs_before_merge_mode;
63
+
64
+ CREATE INDEX idx_sync_runs_project_started
65
+ ON sync_runs(project_id, started_at DESC);
66
+ CREATE INDEX idx_sync_runs_project_dataset_started
67
+ ON sync_runs(project_id, dataset_id, started_at DESC);
68
+ CREATE INDEX idx_sync_runs_project_sync_started
69
+ ON sync_runs(project_id, sync_id, started_at DESC);
70
+ CREATE INDEX idx_sync_runs_project_status_started
71
+ ON sync_runs(project_id, status, started_at DESC);
@@ -0,0 +1,142 @@
1
+ CREATE TABLE executions (
2
+ project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
3
+ id TEXT NOT NULL CHECK (length(trim(id)) > 0),
4
+
5
+ executor_kind TEXT NOT NULL CHECK (
6
+ executor_kind IN (
7
+ 'request', 'action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow',
8
+ 'agent', 'kernel'
9
+ )
10
+ ),
11
+ executor_id TEXT NOT NULL CHECK (length(trim(executor_id)) > 0),
12
+ source_kind TEXT NOT NULL CHECK (
13
+ source_kind IN ('http', 'webhook', 'schedule', 'event', 'execution')
14
+ ),
15
+ source_id TEXT NOT NULL CHECK (length(trim(source_id)) > 0),
16
+
17
+ requested_by_user_id TEXT,
18
+ requested_by_service_account_id TEXT,
19
+ correlation_id TEXT NOT NULL CHECK (length(trim(correlation_id)) > 0),
20
+ parent_execution_id TEXT,
21
+
22
+ authority_kind TEXT NOT NULL CHECK (
23
+ authority_kind IN ('principal', 'trustedPrimitive', 'kernel', 'disabled')
24
+ ),
25
+ authority_user_id TEXT,
26
+ authority_service_account_id TEXT,
27
+ authority_session_id TEXT,
28
+ authority_access_token_id TEXT,
29
+ authority_primitive_kind TEXT CHECK (
30
+ authority_primitive_kind IS NULL
31
+ OR authority_primitive_kind IN (
32
+ 'action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow'
33
+ )
34
+ ),
35
+ authority_primitive_id TEXT,
36
+ authority_kernel_operation TEXT CHECK (
37
+ authority_kernel_operation IS NULL OR authority_kernel_operation = 'ontology.recover'
38
+ ),
39
+
40
+ created_at TEXT NOT NULL,
41
+
42
+ PRIMARY KEY (project_id, id),
43
+ FOREIGN KEY (project_id, parent_execution_id)
44
+ REFERENCES executions (project_id, id),
45
+ FOREIGN KEY (project_id, requested_by_user_id)
46
+ REFERENCES auth_users (project_id, id),
47
+ FOREIGN KEY (project_id, requested_by_service_account_id)
48
+ REFERENCES auth_service_accounts (project_id, id),
49
+ FOREIGN KEY (project_id, authority_user_id)
50
+ REFERENCES auth_users (project_id, id),
51
+ FOREIGN KEY (project_id, authority_service_account_id)
52
+ REFERENCES auth_service_accounts (project_id, id),
53
+ FOREIGN KEY (project_id, authority_session_id)
54
+ REFERENCES auth_sessions (project_id, id),
55
+ FOREIGN KEY (project_id, authority_access_token_id)
56
+ REFERENCES auth_access_tokens (project_id, id),
57
+
58
+ CHECK (
59
+ (requested_by_user_id IS NOT NULL) + (requested_by_service_account_id IS NOT NULL) <= 1
60
+ ),
61
+ CHECK (
62
+ (source_kind = 'execution' AND parent_execution_id = source_id)
63
+ OR (source_kind <> 'execution' AND parent_execution_id IS NULL)
64
+ ),
65
+ CHECK (
66
+ (
67
+ authority_kind = 'principal'
68
+ AND (authority_user_id IS NOT NULL) + (authority_service_account_id IS NOT NULL) = 1
69
+ AND (authority_session_id IS NOT NULL) + (authority_access_token_id IS NOT NULL) <= 1
70
+ AND (authority_session_id IS NULL OR authority_user_id IS NOT NULL)
71
+ AND authority_primitive_kind IS NULL
72
+ AND authority_primitive_id IS NULL
73
+ AND authority_kernel_operation IS NULL
74
+ )
75
+ OR (
76
+ authority_kind = 'trustedPrimitive'
77
+ AND authority_user_id IS NULL
78
+ AND authority_service_account_id IS NULL
79
+ AND authority_session_id IS NULL
80
+ AND authority_access_token_id IS NULL
81
+ AND authority_primitive_kind IS NOT NULL
82
+ AND authority_primitive_id IS NOT NULL
83
+ AND length(trim(authority_primitive_id)) > 0
84
+ AND authority_kernel_operation IS NULL
85
+ )
86
+ OR (
87
+ authority_kind = 'kernel'
88
+ AND authority_user_id IS NULL
89
+ AND authority_service_account_id IS NULL
90
+ AND authority_session_id IS NULL
91
+ AND authority_access_token_id IS NULL
92
+ AND authority_primitive_kind IS NULL
93
+ AND authority_primitive_id IS NULL
94
+ AND authority_kernel_operation IS NOT NULL
95
+ )
96
+ OR (
97
+ authority_kind = 'disabled'
98
+ AND authority_user_id IS NULL
99
+ AND authority_service_account_id IS NULL
100
+ AND authority_session_id IS NULL
101
+ AND authority_access_token_id IS NULL
102
+ AND authority_primitive_kind IS NULL
103
+ AND authority_primitive_id IS NULL
104
+ AND authority_kernel_operation IS NULL
105
+ )
106
+ ),
107
+ CHECK (
108
+ (
109
+ executor_kind = 'request'
110
+ AND source_kind = 'http'
111
+ AND executor_id = source_id
112
+ AND (
113
+ (
114
+ authority_kind = 'principal'
115
+ AND requested_by_user_id IS authority_user_id
116
+ AND requested_by_service_account_id IS authority_service_account_id
117
+ )
118
+ OR (
119
+ authority_kind = 'disabled'
120
+ AND requested_by_user_id IS NULL
121
+ AND requested_by_service_account_id IS NULL
122
+ )
123
+ )
124
+ )
125
+ OR (
126
+ executor_kind IN ('action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow')
127
+ AND authority_kind = 'trustedPrimitive'
128
+ AND executor_kind = authority_primitive_kind
129
+ )
130
+ OR (
131
+ executor_kind = 'agent'
132
+ AND authority_kind = 'principal'
133
+ AND authority_service_account_id IS NOT NULL
134
+ AND authority_session_id IS NULL
135
+ AND authority_access_token_id IS NULL
136
+ )
137
+ OR (
138
+ executor_kind = 'kernel'
139
+ AND authority_kind = 'kernel'
140
+ )
141
+ )
142
+ );
@@ -0,0 +1,7 @@
1
+ ALTER TABLE workflow_runs ADD COLUMN execution_id TEXT NOT NULL;
2
+ ALTER TABLE workflow_runs DROP COLUMN source;
3
+ ALTER TABLE workflow_runs DROP COLUMN requested_by_principal_type;
4
+ ALTER TABLE workflow_runs DROP COLUMN requested_by_principal_id;
5
+
6
+ CREATE UNIQUE INDEX idx_workflow_runs_project_execution
7
+ ON workflow_runs(project_id, execution_id);
@@ -0,0 +1,4 @@
1
+ DROP INDEX idx_ontology_source_rows_root;
2
+
3
+ CREATE INDEX idx_ontology_source_rows_root
4
+ ON ontology_source_rows(project_id, source_id, materialization_id, root_sort_key);