@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,116 @@
1
+ import {
2
+ AgentStorageError,
3
+ type AgentThreadRecord,
4
+ type AgentThreadStore,
5
+ type CreateAgentThreadInput,
6
+ type ListAgentThreadsInput,
7
+ type ListAgentThreadsResult,
8
+ } from "@sixb/core/storage"
9
+ import type { SqlParameter } from "../pg-client"
10
+ import { isUniqueViolation } from "../storage-errors"
11
+ import type { PgStoreClient } from "../transactions"
12
+ import { type AgentThreadRow, queryAgentList, rowToThreadRecord } from "./rows"
13
+
14
+ export class PgAgentThreadStore implements AgentThreadStore {
15
+ constructor(private readonly sql: PgStoreClient) {}
16
+
17
+ async create(input: CreateAgentThreadInput): Promise<AgentThreadRecord> {
18
+ const createdAt = input.createdAt ?? new Date()
19
+ const updatedAt = input.updatedAt ?? createdAt
20
+
21
+ try {
22
+ const [row] = await this.sql<AgentThreadRow[]>`
23
+ INSERT INTO agent_threads (
24
+ project_id,
25
+ id,
26
+ agent_id,
27
+ owner_principal_type,
28
+ owner_principal_id,
29
+ title,
30
+ status,
31
+ created_at,
32
+ updated_at
33
+ ) VALUES (
34
+ ${input.projectId},
35
+ ${input.id},
36
+ ${input.agentId},
37
+ ${input.ownerPrincipal.type},
38
+ ${input.ownerPrincipal.id},
39
+ ${input.title ?? null},
40
+ ${input.status ?? "active"},
41
+ ${createdAt},
42
+ ${updatedAt}
43
+ )
44
+ RETURNING *
45
+ `
46
+
47
+ return rowToThreadRecord(row)
48
+ } catch (error) {
49
+ if (isUniqueViolation(error)) {
50
+ throw new AgentStorageError(
51
+ "duplicate_id",
52
+ `[SixbPg] Agent thread '${input.id}' already exists for project '${input.projectId}'.`
53
+ )
54
+ }
55
+
56
+ throw error
57
+ }
58
+ }
59
+
60
+ async getById(params: { projectId: string; id: string }): Promise<AgentThreadRecord | null> {
61
+ const [row] = await this.sql<AgentThreadRow[]>`
62
+ SELECT * FROM agent_threads WHERE project_id = ${params.projectId} AND id = ${params.id}
63
+ `
64
+
65
+ return row ? rowToThreadRecord(row) : null
66
+ }
67
+
68
+ async list(input: ListAgentThreadsInput): Promise<ListAgentThreadsResult> {
69
+ if (
70
+ (input.statuses !== undefined && input.statuses.length === 0) ||
71
+ (input.agentIds !== undefined && input.agentIds.length === 0)
72
+ ) {
73
+ return { threads: [], hasMore: false, total: 0 }
74
+ }
75
+
76
+ const whereClauses = ["project_id = $1"]
77
+ const params: SqlParameter[] = [input.projectId]
78
+ let index = 2
79
+
80
+ if (input.agentId) {
81
+ whereClauses.push(`agent_id = $${index++}`)
82
+ params.push(input.agentId)
83
+ }
84
+
85
+ if (input.agentIds) {
86
+ const placeholders = input.agentIds.map(() => `$${index++}`)
87
+ whereClauses.push(`agent_id IN (${placeholders.join(", ")})`)
88
+ params.push(...input.agentIds)
89
+ }
90
+
91
+ if (input.statuses) {
92
+ const placeholders = input.statuses.map(() => `$${index++}`)
93
+ whereClauses.push(`status IN (${placeholders.join(", ")})`)
94
+ params.push(...input.statuses)
95
+ }
96
+
97
+ if (input.ownerPrincipal) {
98
+ whereClauses.push(`owner_principal_type = $${index++} AND owner_principal_id = $${index++}`)
99
+ params.push(input.ownerPrincipal.type, input.ownerPrincipal.id)
100
+ }
101
+
102
+ const dir = input.order === "asc" ? "ASC" : "DESC"
103
+ const { rows, total, hasMore } = await queryAgentList<AgentThreadRow>({
104
+ sql: this.sql,
105
+ table: "agent_threads",
106
+ whereClauses,
107
+ params,
108
+ nextIndex: index,
109
+ orderBy: `COALESCE(last_message_at, created_at) ${dir}, id ${dir}`,
110
+ limit: input.limit,
111
+ offset: input.offset,
112
+ })
113
+
114
+ return { threads: rows.map(rowToThreadRecord), hasMore, total }
115
+ }
116
+ }
@@ -0,0 +1,254 @@
1
+ import type {
2
+ AccessTokenRecord,
3
+ AuthAccessTokenStore,
4
+ CreateAuthAccessTokenInput,
5
+ ListAuthAccessTokensInput,
6
+ ListAuthAccessTokensResult,
7
+ } from "@sixb/core/storage"
8
+ import { AuthStorageError } from "@sixb/core/storage"
9
+ import type { PgStoreClient } from "../transactions"
10
+ import type { PgAuthAccessTokenRow, PgAuthServiceAccountRow, PgAuthUserRow } from "./rows"
11
+ import { rowToAccessTokenRecord, serializeOptionalStringArray } from "./rows"
12
+ import {
13
+ appendPagination,
14
+ assertNonEmpty,
15
+ mapUniqueConstraintError,
16
+ normalizeGroupIds,
17
+ type PgValue,
18
+ } from "./shared"
19
+
20
+ export class PgAuthAccessTokenStore implements AuthAccessTokenStore {
21
+ constructor(private readonly sql: PgStoreClient) {}
22
+
23
+ async create(input: CreateAuthAccessTokenInput): Promise<AccessTokenRecord> {
24
+ const id = assertNonEmpty(input.id, "Access token id")
25
+ const projectId = assertNonEmpty(input.projectId, "Project id")
26
+ const name = assertNonEmpty(input.name, "Access token name")
27
+ const subjectId = assertNonEmpty(input.subjectId, "Access token subject id")
28
+ const tokenHash = assertNonEmpty(input.tokenHash, "Access token hash")
29
+ assertKindMatchesSubject(input)
30
+ await this.assertSubjectExists(projectId, input.subjectType, subjectId)
31
+ const groupIds = input.groupIds === undefined ? undefined : normalizeGroupIds(input.groupIds)
32
+
33
+ try {
34
+ const [row] = await this.sql<PgAuthAccessTokenRow[]>`
35
+ INSERT INTO auth_access_tokens (
36
+ project_id,
37
+ id,
38
+ name,
39
+ kind,
40
+ subject_type,
41
+ subject_id,
42
+ token_hash,
43
+ group_ids,
44
+ created_by_user_id,
45
+ created_by_service_account_id,
46
+ created_by_system_id,
47
+ created_by_session_id,
48
+ created_at,
49
+ expires_at
50
+ ) VALUES (
51
+ ${projectId},
52
+ ${id},
53
+ ${name},
54
+ ${input.kind},
55
+ ${input.subjectType},
56
+ ${subjectId},
57
+ ${tokenHash},
58
+ ${serializeOptionalStringArray(groupIds)},
59
+ ${input.createdByPrincipal?.type === "user" ? input.createdByPrincipal.id : null},
60
+ ${
61
+ input.createdByPrincipal?.type === "serviceAccount" ? input.createdByPrincipal.id : null
62
+ },
63
+ ${input.createdByPrincipal?.type === "system" ? input.createdByPrincipal.id : null},
64
+ ${input.createdBySessionId ?? null},
65
+ ${input.createdAt},
66
+ ${input.expiresAt}
67
+ )
68
+ RETURNING *
69
+ `
70
+
71
+ return rowToAccessTokenRecord(row)
72
+ } catch (error) {
73
+ mapUniqueConstraintError(
74
+ error,
75
+ "duplicate_access_token",
76
+ `[Sixb] Access token '${id}' already exists for project '${projectId}'.`
77
+ )
78
+ }
79
+ }
80
+
81
+ async getById(params: {
82
+ readonly projectId: string
83
+ readonly id: string
84
+ }): Promise<AccessTokenRecord | null> {
85
+ const [row] = await this.sql<PgAuthAccessTokenRow[]>`
86
+ SELECT *
87
+ FROM auth_access_tokens
88
+ WHERE project_id = ${params.projectId}
89
+ AND id = ${params.id}
90
+ `
91
+ return row ? rowToAccessTokenRecord(row) : null
92
+ }
93
+
94
+ async list(input: ListAuthAccessTokensInput): Promise<ListAuthAccessTokensResult> {
95
+ const whereClauses = ["project_id = $1"]
96
+ const params: PgValue[] = [input.projectId]
97
+ let nextIndex = 2
98
+ if (input.kind) {
99
+ whereClauses.push(`kind = $${nextIndex++}`)
100
+ params.push(input.kind)
101
+ }
102
+ if (input.subjectType) {
103
+ whereClauses.push(`subject_type = $${nextIndex++}`)
104
+ params.push(input.subjectType)
105
+ }
106
+ if (input.subjectId) {
107
+ whereClauses.push(`subject_id = $${nextIndex++}`)
108
+ params.push(input.subjectId)
109
+ }
110
+ if (!input.includeRevoked) {
111
+ whereClauses.push("revoked_at IS NULL")
112
+ }
113
+
114
+ const where = `WHERE ${whereClauses.join(" AND ")}`
115
+ const order = input.order === "desc" ? "DESC" : "ASC"
116
+ const [totalRow] = await this.sql.unsafe<{ readonly count: string | number }[]>(
117
+ `SELECT COUNT(*)::bigint AS count FROM auth_access_tokens ${where}`,
118
+ [...params]
119
+ )
120
+ const queryParams = [...params]
121
+ const query = appendPagination(
122
+ `
123
+ SELECT *
124
+ FROM auth_access_tokens
125
+ ${where}
126
+ ORDER BY created_at ${order}, id ${order}
127
+ `,
128
+ queryParams,
129
+ nextIndex,
130
+ input
131
+ )
132
+ const rows = await this.sql.unsafe<PgAuthAccessTokenRow[]>(query, queryParams)
133
+ const total = Number(totalRow?.count ?? 0)
134
+
135
+ return {
136
+ accessTokens: rows.map(rowToAccessTokenRecord),
137
+ hasMore: input.limit === undefined ? false : (input.offset ?? 0) + input.limit < total,
138
+ total,
139
+ }
140
+ }
141
+
142
+ async findValidByTokenHash(params: {
143
+ readonly projectId: string
144
+ readonly id: string
145
+ readonly kind: AccessTokenRecord["kind"]
146
+ readonly tokenHash: string
147
+ readonly now: Date
148
+ }): Promise<AccessTokenRecord | null> {
149
+ const [row] = await this.sql<PgAuthAccessTokenRow[]>`
150
+ SELECT *
151
+ FROM auth_access_tokens
152
+ WHERE project_id = ${params.projectId}
153
+ AND id = ${params.id}
154
+ AND kind = ${params.kind}
155
+ AND token_hash = ${params.tokenHash}
156
+ AND revoked_at IS NULL
157
+ AND expires_at > ${params.now}
158
+ `
159
+
160
+ return row ? rowToAccessTokenRecord(row) : null
161
+ }
162
+
163
+ async revoke(params: {
164
+ readonly projectId: string
165
+ readonly id: string
166
+ readonly revokedAt: Date
167
+ }): Promise<AccessTokenRecord> {
168
+ const [row] = await this.sql<PgAuthAccessTokenRow[]>`
169
+ UPDATE auth_access_tokens
170
+ SET revoked_at = ${params.revokedAt}
171
+ WHERE project_id = ${params.projectId}
172
+ AND id = ${params.id}
173
+ RETURNING *
174
+ `
175
+ if (!row) {
176
+ throw new AuthStorageError(
177
+ "missing_access_token",
178
+ `[Sixb] Access token '${params.id}' not found for project '${params.projectId}'.`
179
+ )
180
+ }
181
+ return rowToAccessTokenRecord(row)
182
+ }
183
+
184
+ async touch(params: {
185
+ readonly projectId: string
186
+ readonly id: string
187
+ readonly lastUsedAt: Date
188
+ readonly userAgent?: string
189
+ readonly ipAddress?: string
190
+ }): Promise<AccessTokenRecord> {
191
+ const [row] = await this.sql<PgAuthAccessTokenRow[]>`
192
+ UPDATE auth_access_tokens
193
+ SET last_used_at = ${params.lastUsedAt},
194
+ last_used_user_agent = COALESCE(${params.userAgent ?? null}, last_used_user_agent),
195
+ last_used_ip_address = COALESCE(${params.ipAddress ?? null}, last_used_ip_address)
196
+ WHERE project_id = ${params.projectId}
197
+ AND id = ${params.id}
198
+ RETURNING *
199
+ `
200
+ if (!row) {
201
+ throw new AuthStorageError(
202
+ "missing_access_token",
203
+ `[Sixb] Access token '${params.id}' not found for project '${params.projectId}'.`
204
+ )
205
+ }
206
+ return rowToAccessTokenRecord(row)
207
+ }
208
+
209
+ private async assertSubjectExists(
210
+ projectId: string,
211
+ subjectType: CreateAuthAccessTokenInput["subjectType"],
212
+ subjectId: string
213
+ ): Promise<void> {
214
+ if (subjectType === "user") {
215
+ const [row] = await this.sql<PgAuthUserRow[]>`
216
+ SELECT *
217
+ FROM auth_users
218
+ WHERE project_id = ${projectId}
219
+ AND id = ${subjectId}
220
+ `
221
+ if (row) return
222
+ throw new AuthStorageError(
223
+ "missing_user",
224
+ `[Sixb] User '${subjectId}' not found for project '${projectId}'.`
225
+ )
226
+ }
227
+
228
+ const [row] = await this.sql<PgAuthServiceAccountRow[]>`
229
+ SELECT *
230
+ FROM auth_service_accounts
231
+ WHERE project_id = ${projectId}
232
+ AND id = ${subjectId}
233
+ `
234
+ if (row) return
235
+ throw new AuthStorageError(
236
+ "missing_service_account",
237
+ `[Sixb] Service account '${subjectId}' not found for project '${projectId}'.`
238
+ )
239
+ }
240
+ }
241
+
242
+ function assertKindMatchesSubject(input: CreateAuthAccessTokenInput): void {
243
+ if (
244
+ (input.kind === "personal" && input.subjectType === "user") ||
245
+ (input.kind === "serviceAccount" && input.subjectType === "serviceAccount")
246
+ ) {
247
+ return
248
+ }
249
+
250
+ throw new AuthStorageError(
251
+ "invalid_input",
252
+ `[Sixb] Access token kind '${input.kind}' cannot target subject type '${input.subjectType}'.`
253
+ )
254
+ }