@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.
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/agents/agent-storage.d.ts +20 -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 +13 -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 +25 -0
- package/dist/agents/runs.d.ts.map +1 -0
- package/dist/agents/threads.d.ts +13 -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 +41 -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 +29 -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 +138 -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 +159 -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 +127 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11957 -0
- package/dist/index.js.map +55 -0
- package/dist/latest-run-query.d.ts +24 -0
- package/dist/latest-run-query.d.ts.map +1 -0
- package/dist/migrations.d.ts +20 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/ontology-storage/commits.d.ts +11 -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 +63 -0
- package/dist/ontology-storage/materialization-session.d.ts.map +1 -0
- package/dist/ontology-storage/materialization-state.d.ts +69 -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 +33 -0
- package/dist/ontology-storage/materializations.d.ts.map +1 -0
- package/dist/ontology-storage/outbox.d.ts +12 -0
- package/dist/ontology-storage/outbox.d.ts.map +1 -0
- package/dist/ontology-storage/shared.d.ts +95 -0
- package/dist/ontology-storage/shared.d.ts.map +1 -0
- package/dist/ontology-storage/sources.d.ts +25 -0
- package/dist/ontology-storage/sources.d.ts.map +1 -0
- package/dist/pg-action-run-storage.d.ts +21 -0
- package/dist/pg-action-run-storage.d.ts.map +1 -0
- package/dist/pg-client.d.ts +58 -0
- package/dist/pg-client.d.ts.map +1 -0
- package/dist/pg-object-query-compiler.d.ts +55 -0
- package/dist/pg-object-query-compiler.d.ts.map +1 -0
- package/dist/pg-object-storage.d.ts +90 -0
- package/dist/pg-object-storage.d.ts.map +1 -0
- package/dist/pg-pipeline-run-storage.d.ts +18 -0
- package/dist/pg-pipeline-run-storage.d.ts.map +1 -0
- package/dist/pg-projection-run-storage.d.ts +19 -0
- package/dist/pg-projection-run-storage.d.ts.map +1 -0
- package/dist/pg-rules-storage.d.ts +25 -0
- package/dist/pg-rules-storage.d.ts.map +1 -0
- package/dist/pg-sync-run-storage.d.ts +15 -0
- package/dist/pg-sync-run-storage.d.ts.map +1 -0
- package/dist/pg-timeseries-storage.d.ts +32 -0
- package/dist/pg-timeseries-storage.d.ts.map +1 -0
- package/dist/pg-webhook-delivery-storage.d.ts +17 -0
- package/dist/pg-webhook-delivery-storage.d.ts.map +1 -0
- package/dist/pg-webhook-run-storage.d.ts +14 -0
- package/dist/pg-webhook-run-storage.d.ts.map +1 -0
- package/dist/pg-workflow-intervention-storage.d.ts +16 -0
- package/dist/pg-workflow-intervention-storage.d.ts.map +1 -0
- package/dist/pg-workflow-run-storage.d.ts +49 -0
- package/dist/pg-workflow-run-storage.d.ts.map +1 -0
- package/dist/run-list-query.d.ts +30 -0
- package/dist/run-list-query.d.ts.map +1 -0
- package/dist/storage-errors.d.ts +16 -0
- package/dist/storage-errors.d.ts.map +1 -0
- package/dist/transactions.d.ts +10 -0
- package/dist/transactions.d.ts.map +1 -0
- package/dist/tsconfig.json +5 -0
- package/package.json +60 -0
- package/src/agents/agent-storage.ts +26 -0
- package/src/agents/index.ts +5 -0
- package/src/agents/messages.ts +144 -0
- package/src/agents/rows.ts +227 -0
- package/src/agents/runs.ts +330 -0
- package/src/agents/threads.ts +116 -0
- package/src/auth-storage/access-tokens.ts +254 -0
- package/src/auth-storage/auth-storage.ts +798 -0
- package/src/auth-storage/group-memberships.ts +47 -0
- package/src/auth-storage/identities.ts +85 -0
- package/src/auth-storage/index.ts +2 -0
- package/src/auth-storage/invitations.ts +280 -0
- package/src/auth-storage/magic-links.ts +140 -0
- package/src/auth-storage/oidc-attempts.ts +93 -0
- package/src/auth-storage/rows.ts +352 -0
- package/src/auth-storage/service-account-group-memberships.ts +150 -0
- package/src/auth-storage/service-accounts.ts +154 -0
- package/src/auth-storage/sessions.ts +193 -0
- package/src/auth-storage/shared.ts +799 -0
- package/src/auth-storage/users.ts +196 -0
- package/src/index.ts +367 -0
- package/src/latest-run-query.ts +42 -0
- package/src/migrations/001-initial-schema.sql +1195 -0
- package/src/migrations.ts +280 -0
- package/src/ontology-storage/commits.ts +115 -0
- package/src/ontology-storage/index.ts +35 -0
- package/src/ontology-storage/materialization-session.ts +426 -0
- package/src/ontology-storage/materialization-state.ts +1109 -0
- package/src/ontology-storage/materialization-writer.ts +509 -0
- package/src/ontology-storage/materializations.ts +939 -0
- package/src/ontology-storage/outbox.ts +216 -0
- package/src/ontology-storage/shared.ts +293 -0
- package/src/ontology-storage/sources.ts +658 -0
- package/src/pg-action-run-storage.ts +657 -0
- package/src/pg-client.ts +124 -0
- package/src/pg-object-query-compiler.ts +1545 -0
- package/src/pg-object-storage.ts +638 -0
- package/src/pg-pipeline-run-storage.ts +457 -0
- package/src/pg-projection-run-storage.ts +526 -0
- package/src/pg-rules-storage.ts +308 -0
- package/src/pg-sync-run-storage.ts +263 -0
- package/src/pg-timeseries-storage.ts +180 -0
- package/src/pg-webhook-delivery-storage.ts +180 -0
- package/src/pg-webhook-run-storage.ts +185 -0
- package/src/pg-workflow-intervention-storage.ts +339 -0
- package/src/pg-workflow-run-storage.ts +1065 -0
- package/src/run-list-query.ts +108 -0
- package/src/sql.d.ts +4 -0
- package/src/storage-errors.ts +72 -0
- package/src/transactions.ts +58 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { createHash } from "node:crypto"
|
|
2
|
+
import type { StorageMigrator } from "@sixb/core"
|
|
3
|
+
import type {
|
|
4
|
+
MigrationHistoryStore,
|
|
5
|
+
MigrationRecord,
|
|
6
|
+
MigrationSet,
|
|
7
|
+
MigrationStep,
|
|
8
|
+
MigrationStepOptions,
|
|
9
|
+
} from "@sixb/core/storage"
|
|
10
|
+
import {
|
|
11
|
+
defineMigrations,
|
|
12
|
+
describeMigrationHistory,
|
|
13
|
+
runMigrationSet,
|
|
14
|
+
step,
|
|
15
|
+
} from "@sixb/core/storage"
|
|
16
|
+
import initialSchemaSql from "./migrations/001-initial-schema.sql" with { type: "text" }
|
|
17
|
+
import type { SQL, SQLClient } from "./pg-client"
|
|
18
|
+
|
|
19
|
+
export interface PostgresMigrationContext {
|
|
20
|
+
exec(sqlText: string): Promise<void>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const POSTGRES_STORAGE_ADAPTER_ID = "SixbPostgresStorage"
|
|
24
|
+
|
|
25
|
+
export function pgSql(id: string, sqlText: string): MigrationStep<PostgresMigrationContext> {
|
|
26
|
+
return pgStep(id, (context) => context.exec(sqlText), { checksum: checksum(sqlText) })
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function pgStep(
|
|
30
|
+
id: string,
|
|
31
|
+
up: (context: PostgresMigrationContext) => void | Promise<void>,
|
|
32
|
+
options: MigrationStepOptions = {}
|
|
33
|
+
): MigrationStep<PostgresMigrationContext> {
|
|
34
|
+
return step<PostgresMigrationContext>(id, up, options)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function createPostgresStorageMigrators(
|
|
38
|
+
sql: SQL,
|
|
39
|
+
schemaName: string
|
|
40
|
+
): readonly StorageMigrator[] {
|
|
41
|
+
return [
|
|
42
|
+
createPostgresMigrator({
|
|
43
|
+
sql,
|
|
44
|
+
schemaName,
|
|
45
|
+
migrations: postgresStorageMigrations,
|
|
46
|
+
}),
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function createPostgresMigrator(params: {
|
|
51
|
+
readonly sql: SQL
|
|
52
|
+
readonly schemaName: string
|
|
53
|
+
readonly migrations: MigrationSet<PostgresMigrationContext>
|
|
54
|
+
}): StorageMigrator {
|
|
55
|
+
return {
|
|
56
|
+
adapterId: params.migrations.adapterId,
|
|
57
|
+
latestVersion: params.migrations.latestVersion,
|
|
58
|
+
async status() {
|
|
59
|
+
return describeMigrationHistory({
|
|
60
|
+
migrations: params.migrations,
|
|
61
|
+
rows: await readPostgresHistory(params.sql, params.schemaName, params.migrations.adapterId),
|
|
62
|
+
})
|
|
63
|
+
},
|
|
64
|
+
migrate() {
|
|
65
|
+
return withPostgresMigrationLock(params, async (sql) => {
|
|
66
|
+
const session = postgresMigrationSession(sql, params.schemaName)
|
|
67
|
+
return runMigrationSet({
|
|
68
|
+
context: session.context,
|
|
69
|
+
migrations: params.migrations,
|
|
70
|
+
state: session.state,
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function migratePostgresStorage(sql: SQL, schemaName: string): Promise<void> {
|
|
78
|
+
await createPostgresMigrator({
|
|
79
|
+
sql,
|
|
80
|
+
schemaName,
|
|
81
|
+
migrations: postgresStorageMigrations,
|
|
82
|
+
}).migrate()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function dropSchema(sql: SQL, schemaName: string): Promise<void> {
|
|
86
|
+
await sql.unsafe(`DROP SCHEMA IF EXISTS ${quoteIdent(schemaName)} CASCADE`)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function quoteIdent(identifier: string): string {
|
|
90
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier)) {
|
|
91
|
+
throw new Error(`[SixbPg] Invalid identifier: ${identifier}`)
|
|
92
|
+
}
|
|
93
|
+
return `"${identifier}"`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function postgresMigrationSession(
|
|
97
|
+
sql: SQL,
|
|
98
|
+
schemaName: string
|
|
99
|
+
): { context: PostgresMigrationContext; state: MigrationHistoryStore } {
|
|
100
|
+
const schema = quoteIdent(schemaName)
|
|
101
|
+
const migrationsTable = `${schema}.sixb_migrations`
|
|
102
|
+
// Migration steps receive a stable context; route exec() through the active transaction.
|
|
103
|
+
let active: SQLClient | null = null
|
|
104
|
+
const connection = (): SQLClient => active ?? sql
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
context: {
|
|
108
|
+
async exec(sqlText) {
|
|
109
|
+
await connection().unsafe(sqlText)
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
state: {
|
|
113
|
+
async ensure() {
|
|
114
|
+
await sql.unsafe(`
|
|
115
|
+
CREATE SCHEMA IF NOT EXISTS ${schema};
|
|
116
|
+
|
|
117
|
+
CREATE TABLE IF NOT EXISTS ${migrationsTable} (
|
|
118
|
+
adapter_id TEXT NOT NULL,
|
|
119
|
+
version INTEGER NOT NULL,
|
|
120
|
+
id TEXT NOT NULL,
|
|
121
|
+
checksum TEXT,
|
|
122
|
+
status TEXT NOT NULL CHECK (status IN ('started', 'applied')),
|
|
123
|
+
started_at TIMESTAMPTZ NOT NULL,
|
|
124
|
+
finished_at TIMESTAMPTZ,
|
|
125
|
+
PRIMARY KEY (adapter_id, version)
|
|
126
|
+
);
|
|
127
|
+
`)
|
|
128
|
+
},
|
|
129
|
+
async readHistory(adapterId) {
|
|
130
|
+
const rows = await sql.unsafe<PostgresMigrationRow[]>(
|
|
131
|
+
`SELECT * FROM ${migrationsTable} WHERE adapter_id = $1 ORDER BY version`,
|
|
132
|
+
[adapterId]
|
|
133
|
+
)
|
|
134
|
+
return rows.map(rowToMigrationRecord)
|
|
135
|
+
},
|
|
136
|
+
async markStarted(adapterId, migration, at) {
|
|
137
|
+
await connection().unsafe(
|
|
138
|
+
`
|
|
139
|
+
INSERT INTO ${migrationsTable} (
|
|
140
|
+
adapter_id, version, id, checksum, status, started_at, finished_at
|
|
141
|
+
) VALUES ($1, $2, $3, $4, 'started', $5, NULL)
|
|
142
|
+
ON CONFLICT(adapter_id, version) DO UPDATE SET
|
|
143
|
+
id = excluded.id,
|
|
144
|
+
checksum = excluded.checksum,
|
|
145
|
+
status = excluded.status,
|
|
146
|
+
started_at = excluded.started_at,
|
|
147
|
+
finished_at = NULL
|
|
148
|
+
`,
|
|
149
|
+
[adapterId, migration.version, migration.id, migration.checksum ?? null, at]
|
|
150
|
+
)
|
|
151
|
+
},
|
|
152
|
+
async markApplied(adapterId, migration, at) {
|
|
153
|
+
await connection().unsafe(
|
|
154
|
+
`
|
|
155
|
+
UPDATE ${migrationsTable}
|
|
156
|
+
SET status = 'applied', finished_at = $1
|
|
157
|
+
WHERE adapter_id = $2 AND version = $3
|
|
158
|
+
`,
|
|
159
|
+
[at, adapterId, migration.version]
|
|
160
|
+
)
|
|
161
|
+
},
|
|
162
|
+
async transaction(run) {
|
|
163
|
+
// `sql` is the reserved connection holding the migration advisory lock. porsager's
|
|
164
|
+
// reserved connection has no `.begin`, so drive the transaction manually on it and
|
|
165
|
+
// route exec()/markStarted()/markApplied() through the same connection via `active`.
|
|
166
|
+
const previous = active
|
|
167
|
+
active = sql
|
|
168
|
+
await sql.unsafe("BEGIN")
|
|
169
|
+
try {
|
|
170
|
+
await sql.unsafe(`SET LOCAL search_path TO ${schema}`)
|
|
171
|
+
const result = await run()
|
|
172
|
+
await sql.unsafe("COMMIT")
|
|
173
|
+
return result
|
|
174
|
+
} catch (error) {
|
|
175
|
+
await sql.unsafe("ROLLBACK")
|
|
176
|
+
throw error
|
|
177
|
+
} finally {
|
|
178
|
+
active = previous
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Reads migration history without DDL and without the advisory lock. `null` means the
|
|
187
|
+
* history table does not exist, which is a state and not a failure.
|
|
188
|
+
*
|
|
189
|
+
* `migrate()` cannot be used for this: it calls `ensure()` first, so it runs
|
|
190
|
+
* `CREATE SCHEMA`/`CREATE TABLE` and reserves a connection to hold
|
|
191
|
+
* `pg_advisory_lock`. A probe must need no DDL grant — `/ready` is public and
|
|
192
|
+
* unauthenticated — and must not serialize N replicas behind one lock.
|
|
193
|
+
*
|
|
194
|
+
* `to_regclass` returns NULL for a missing relation (including a missing schema)
|
|
195
|
+
* instead of raising, and needs no catalog privileges.
|
|
196
|
+
*/
|
|
197
|
+
async function readPostgresHistory(
|
|
198
|
+
sql: SQL,
|
|
199
|
+
schemaName: string,
|
|
200
|
+
adapterId: string
|
|
201
|
+
): Promise<readonly MigrationRecord[] | null> {
|
|
202
|
+
const schema = quoteIdent(schemaName)
|
|
203
|
+
const probe = await sql.unsafe<{ oid: string | null }[]>(`SELECT to_regclass($1) AS oid`, [
|
|
204
|
+
`${schema}.sixb_migrations`,
|
|
205
|
+
])
|
|
206
|
+
if (!probe[0]?.oid) {
|
|
207
|
+
return null
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const rows = await sql.unsafe<PostgresMigrationRow[]>(
|
|
211
|
+
`SELECT * FROM ${schema}.sixb_migrations WHERE adapter_id = $1 ORDER BY version`,
|
|
212
|
+
[adapterId]
|
|
213
|
+
)
|
|
214
|
+
return rows.map(rowToMigrationRecord)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function withPostgresMigrationLock<T>(
|
|
218
|
+
params: {
|
|
219
|
+
readonly sql: SQL
|
|
220
|
+
readonly schemaName: string
|
|
221
|
+
readonly migrations: MigrationSet<PostgresMigrationContext>
|
|
222
|
+
},
|
|
223
|
+
run: (sql: SQL) => Promise<T>
|
|
224
|
+
): Promise<T> {
|
|
225
|
+
const sql = await params.sql.reserve()
|
|
226
|
+
const [first, second] = advisoryLockParts(
|
|
227
|
+
`storage:migration:${params.schemaName}:${params.migrations.adapterId}`
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
await sql`SELECT pg_advisory_lock(${first}, ${second})`
|
|
232
|
+
try {
|
|
233
|
+
return await run(sql)
|
|
234
|
+
} finally {
|
|
235
|
+
await sql`SELECT pg_advisory_unlock(${first}, ${second})`
|
|
236
|
+
}
|
|
237
|
+
} finally {
|
|
238
|
+
sql.release()
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function advisoryLockParts(key: string): readonly [number, number] {
|
|
243
|
+
const hash = createHash("sha256").update(key).digest()
|
|
244
|
+
return [hash.readInt32BE(0), hash.readInt32BE(4)]
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function rowToMigrationRecord(row: PostgresMigrationRow): MigrationRecord {
|
|
248
|
+
return {
|
|
249
|
+
adapterId: row.adapter_id,
|
|
250
|
+
version: row.version,
|
|
251
|
+
id: row.id,
|
|
252
|
+
checksum: row.checksum ?? undefined,
|
|
253
|
+
status: row.status,
|
|
254
|
+
startedAt: toIsoString(row.started_at),
|
|
255
|
+
finishedAt: row.finished_at ? toIsoString(row.finished_at) : undefined,
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function toIsoString(value: Date | string): string {
|
|
260
|
+
return value instanceof Date ? value.toISOString() : new Date(value).toISOString()
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function checksum(value: string): string {
|
|
264
|
+
return createHash("sha256").update(value).digest("hex")
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const postgresStorageMigrations = defineMigrations<PostgresMigrationContext>({
|
|
268
|
+
adapterId: POSTGRES_STORAGE_ADAPTER_ID,
|
|
269
|
+
steps: [pgSql("001-initial-schema", initialSchemaSql)],
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
interface PostgresMigrationRow {
|
|
273
|
+
readonly adapter_id: string
|
|
274
|
+
readonly version: number
|
|
275
|
+
readonly id: string
|
|
276
|
+
readonly checksum: string | null
|
|
277
|
+
readonly status: MigrationRecord["status"]
|
|
278
|
+
readonly started_at: Date | string
|
|
279
|
+
readonly finished_at: Date | string | null
|
|
280
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GetOntologyCommitByIdempotencyKeyInput,
|
|
3
|
+
GetOntologyCommitByIdInput,
|
|
4
|
+
GetOntologyCommitByOriginInput,
|
|
5
|
+
ListOntologyCommitsInput,
|
|
6
|
+
ListOntologyCommitsResult,
|
|
7
|
+
OntologyCommitRecord,
|
|
8
|
+
OntologyCommitStorage,
|
|
9
|
+
} from "@sixb/core/storage"
|
|
10
|
+
import {
|
|
11
|
+
assertNonblank,
|
|
12
|
+
assertNonnegativeInteger,
|
|
13
|
+
commitRecord,
|
|
14
|
+
originWhere,
|
|
15
|
+
type PgOntologyCommitRow,
|
|
16
|
+
type PgRootOperation,
|
|
17
|
+
} from "./shared"
|
|
18
|
+
|
|
19
|
+
export class PgOntologyCommitStorage implements OntologyCommitStorage {
|
|
20
|
+
constructor(private readonly runRootOperation: PgRootOperation) {}
|
|
21
|
+
|
|
22
|
+
async getByIdempotencyKey(
|
|
23
|
+
input: GetOntologyCommitByIdempotencyKeyInput
|
|
24
|
+
): Promise<OntologyCommitRecord | null> {
|
|
25
|
+
return this.runRootOperation(async (sql) => {
|
|
26
|
+
assertNonblank(input.projectId, "Ontology commit project id")
|
|
27
|
+
assertNonblank(input.idempotencyKey, "Ontology commit idempotency key")
|
|
28
|
+
const [row] = await sql<PgOntologyCommitRow[]>`
|
|
29
|
+
SELECT * FROM ontology_commits
|
|
30
|
+
WHERE project_id = ${input.projectId} AND idempotency_key = ${input.idempotencyKey}
|
|
31
|
+
`
|
|
32
|
+
return row ? commitRecord(row) : null
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async getById(input: GetOntologyCommitByIdInput): Promise<OntologyCommitRecord | null> {
|
|
37
|
+
return this.runRootOperation(async (sql) => {
|
|
38
|
+
assertNonblank(input.projectId, "Ontology commit project id")
|
|
39
|
+
assertNonblank(input.id, "Ontology commit id")
|
|
40
|
+
const [row] = await sql<PgOntologyCommitRow[]>`
|
|
41
|
+
SELECT * FROM ontology_commits
|
|
42
|
+
WHERE project_id = ${input.projectId} AND id = ${input.id}
|
|
43
|
+
`
|
|
44
|
+
return row ? commitRecord(row) : null
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getByOrigin(input: GetOntologyCommitByOriginInput): Promise<OntologyCommitRecord | null> {
|
|
49
|
+
return this.runRootOperation(async (sql) => {
|
|
50
|
+
assertNonblank(input.projectId, "Ontology commit project id")
|
|
51
|
+
const origin = originWhere(input.origin)
|
|
52
|
+
assertNonblank(origin.runId, "Ontology commit origin run id")
|
|
53
|
+
if (origin.batchOrdinal !== null) {
|
|
54
|
+
assertNonnegativeInteger(origin.batchOrdinal, "Ontology telemetry commit batch ordinal")
|
|
55
|
+
}
|
|
56
|
+
const [row] =
|
|
57
|
+
origin.batchOrdinal === null
|
|
58
|
+
? await sql<PgOntologyCommitRow[]>`
|
|
59
|
+
SELECT * FROM ontology_commits
|
|
60
|
+
WHERE project_id = ${input.projectId}
|
|
61
|
+
AND origin_kind = ${origin.kind}
|
|
62
|
+
AND origin_run_id = ${origin.runId}
|
|
63
|
+
AND origin_batch_ordinal IS NULL
|
|
64
|
+
`
|
|
65
|
+
: await sql<PgOntologyCommitRow[]>`
|
|
66
|
+
SELECT * FROM ontology_commits
|
|
67
|
+
WHERE project_id = ${input.projectId}
|
|
68
|
+
AND origin_kind = ${origin.kind}
|
|
69
|
+
AND origin_run_id = ${origin.runId}
|
|
70
|
+
AND origin_batch_ordinal = ${origin.batchOrdinal}
|
|
71
|
+
`
|
|
72
|
+
return row ? commitRecord(row) : null
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async list(input: ListOntologyCommitsInput): Promise<ListOntologyCommitsResult> {
|
|
77
|
+
return this.runRootOperation(async (sql) => {
|
|
78
|
+
assertNonblank(input.projectId, "Ontology commit project id")
|
|
79
|
+
if (input.run) assertNonblank(input.run.id, "Ontology commit run id")
|
|
80
|
+
const offset = input.offset ?? 0
|
|
81
|
+
const limit = input.limit
|
|
82
|
+
assertNonnegativeInteger(offset, "Ontology commit list offset")
|
|
83
|
+
if (limit !== undefined) assertNonnegativeInteger(limit, "Ontology commit list limit")
|
|
84
|
+
|
|
85
|
+
const runFilter =
|
|
86
|
+
input.run?.kind === "action"
|
|
87
|
+
? sql`AND origin_kind = 'action' AND origin_run_id = ${input.run.id}`
|
|
88
|
+
: input.run?.kind === "projection"
|
|
89
|
+
? sql`AND origin_run_id = ${input.run.id}
|
|
90
|
+
AND origin_kind IN ('projection', 'telemetry')`
|
|
91
|
+
: sql``
|
|
92
|
+
const [{ total }] = await sql<{ readonly total: number | string }[]>`
|
|
93
|
+
SELECT COUNT(*) AS total
|
|
94
|
+
FROM ontology_commits
|
|
95
|
+
WHERE project_id = ${input.projectId} ${runFilter}
|
|
96
|
+
`
|
|
97
|
+
const direction = input.order === "desc" ? sql`DESC` : sql`ASC`
|
|
98
|
+
const order =
|
|
99
|
+
input.run?.kind === "projection"
|
|
100
|
+
? sql`CASE WHEN origin_kind = 'telemetry' THEN origin_batch_ordinal ELSE -1 END
|
|
101
|
+
${direction}, committed_at ${direction}, id ${direction}`
|
|
102
|
+
: sql`committed_at ${direction}, id ${direction}`
|
|
103
|
+
const limitFragment = limit === undefined ? sql`` : sql`LIMIT ${limit}`
|
|
104
|
+
const rows = await sql<PgOntologyCommitRow[]>`
|
|
105
|
+
SELECT * FROM ontology_commits
|
|
106
|
+
WHERE project_id = ${input.projectId} ${runFilter}
|
|
107
|
+
ORDER BY ${order}
|
|
108
|
+
${limitFragment} OFFSET ${offset}
|
|
109
|
+
`
|
|
110
|
+
const count = Number(total)
|
|
111
|
+
const commits = rows.map(commitRecord)
|
|
112
|
+
return { commits, total: count, hasMore: offset + commits.length < count }
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { OntologyStorage } from "@sixb/core/storage"
|
|
2
|
+
import type { SQLClient } from "../pg-client"
|
|
3
|
+
import { PgOntologyCommitStorage } from "./commits"
|
|
4
|
+
import type { PgOntologyTransactionContext } from "./materialization-session"
|
|
5
|
+
import { PgOntologyMaterializationStorage } from "./materializations"
|
|
6
|
+
import { PgOntologyOutboxStorage } from "./outbox"
|
|
7
|
+
import type { PgRootOperation } from "./shared"
|
|
8
|
+
import { PgOntologySourceStorage } from "./sources"
|
|
9
|
+
|
|
10
|
+
export class PgOntologyStorage implements OntologyStorage {
|
|
11
|
+
readonly commits: PgOntologyCommitStorage
|
|
12
|
+
readonly sources: PgOntologySourceStorage
|
|
13
|
+
readonly materializations: PgOntologyMaterializationStorage
|
|
14
|
+
readonly outbox: PgOntologyOutboxStorage
|
|
15
|
+
|
|
16
|
+
constructor(input: {
|
|
17
|
+
readonly sql: SQLClient
|
|
18
|
+
readonly runRootOperation: PgRootOperation
|
|
19
|
+
readonly transactionContext: PgOntologyTransactionContext | null
|
|
20
|
+
}) {
|
|
21
|
+
this.commits = new PgOntologyCommitStorage(input.runRootOperation)
|
|
22
|
+
this.sources = new PgOntologySourceStorage(input.runRootOperation)
|
|
23
|
+
this.materializations = new PgOntologyMaterializationStorage(
|
|
24
|
+
input.sql,
|
|
25
|
+
input.transactionContext
|
|
26
|
+
)
|
|
27
|
+
this.outbox = new PgOntologyOutboxStorage(input.runRootOperation)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
deactivateSessions(): void {
|
|
31
|
+
this.materializations.deactivateSessions()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type { PgOntologyTransactionContext } from "./materialization-session"
|