@sixb/sqlite 0.1.7 → 0.1.8

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 (49) hide show
  1. package/dist/execution-storage.d.ts +3 -2
  2. package/dist/execution-storage.d.ts.map +1 -1
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1492 -380
  6. package/dist/index.js.map +16 -10
  7. package/dist/migrations.d.ts.map +1 -1
  8. package/dist/objects/index.d.ts +3 -0
  9. package/dist/objects/index.d.ts.map +1 -0
  10. package/dist/{object-query-compiler.d.ts → objects/query-compiler.d.ts} +16 -1
  11. package/dist/objects/query-compiler.d.ts.map +1 -0
  12. package/dist/objects/read-scope.d.ts +17 -0
  13. package/dist/objects/read-scope.d.ts.map +1 -0
  14. package/dist/objects/reader.d.ts +51 -0
  15. package/dist/objects/reader.d.ts.map +1 -0
  16. package/dist/objects/rows.d.ts +33 -0
  17. package/dist/objects/rows.d.ts.map +1 -0
  18. package/dist/objects/selected-reader.d.ts +9 -0
  19. package/dist/objects/selected-reader.d.ts.map +1 -0
  20. package/dist/{object-storage.d.ts → objects/storage.d.ts} +12 -9
  21. package/dist/objects/storage.d.ts.map +1 -0
  22. package/dist/share-grant-storage.d.ts +20 -0
  23. package/dist/share-grant-storage.d.ts.map +1 -0
  24. package/dist/share-session-storage.d.ts +19 -0
  25. package/dist/share-session-storage.d.ts.map +1 -0
  26. package/dist/storage-errors.d.ts +1 -0
  27. package/dist/storage-errors.d.ts.map +1 -1
  28. package/dist/transactions.d.ts +8 -0
  29. package/dist/transactions.d.ts.map +1 -1
  30. package/package.json +3 -3
  31. package/src/execution-storage.ts +22 -3
  32. package/src/index.ts +18 -3
  33. package/src/migrations/035-share-grants.sql +66 -0
  34. package/src/migrations/036-share-sessions.sql +226 -0
  35. package/src/migrations.ts +4 -0
  36. package/src/objects/index.ts +2 -0
  37. package/src/{object-query-compiler.ts → objects/query-compiler.ts} +104 -54
  38. package/src/objects/read-scope.ts +316 -0
  39. package/src/objects/reader.ts +526 -0
  40. package/src/objects/rows.ts +123 -0
  41. package/src/objects/selected-reader.ts +119 -0
  42. package/src/objects/storage.ts +296 -0
  43. package/src/share-grant-storage.ts +252 -0
  44. package/src/share-session-storage.ts +189 -0
  45. package/src/storage-errors.ts +4 -0
  46. package/src/transactions.ts +32 -0
  47. package/dist/object-query-compiler.d.ts.map +0 -1
  48. package/dist/object-storage.d.ts.map +0 -1
  49. package/src/object-storage.ts +0 -709
@@ -0,0 +1,189 @@
1
+ import type { Database } from "bun:sqlite"
2
+ import {
3
+ normalizeGetShareSessionByIdInput,
4
+ normalizeRenewShareSessionIfValidInput,
5
+ normalizeRevokeShareSessionInput,
6
+ normalizeShareSessionCreate,
7
+ parseShareSessionRecord,
8
+ } from "@sixb/core/internal/share-session-storage-provider"
9
+ import type {
10
+ CreateShareSessionInput,
11
+ GetShareSessionByIdInput,
12
+ RenewShareSessionIfValidInput,
13
+ RevokeShareSessionInput,
14
+ ShareSessionRecord,
15
+ ShareSessionStorage,
16
+ } from "@sixb/core/storage"
17
+ import { ShareSessionStorageError } from "@sixb/core/storage"
18
+ import { installFreshSqliteSchema } from "./migrations"
19
+ import { isForeignKeyConstraintError, isUniqueConstraintError } from "./storage-errors"
20
+ import {
21
+ closeSqliteStoreConnection,
22
+ openSqliteStoreConnection,
23
+ type SqliteStoreConnection,
24
+ } from "./transactions"
25
+
26
+ export interface SqliteShareSessionStorageOptions {
27
+ readonly path?: string
28
+ readonly connection?: SqliteStoreConnection
29
+ }
30
+
31
+ /** SQLite persistence for short-lived Share access sessions. */
32
+ export class SqliteShareSessionStorage implements ShareSessionStorage {
33
+ private readonly connection: SqliteStoreConnection
34
+ private readonly db: Database
35
+
36
+ constructor(options: SqliteShareSessionStorageOptions = {}) {
37
+ this.connection = openSqliteStoreConnection(options)
38
+ this.db = this.connection.db
39
+ if (this.connection.installFreshSchema) installFreshSqliteSchema(this.db)
40
+ }
41
+
42
+ async create(input: CreateShareSessionInput): Promise<ShareSessionRecord> {
43
+ const record = normalizeShareSessionCreate(input)
44
+ try {
45
+ const row = this.db
46
+ .query(
47
+ `
48
+ INSERT INTO share_sessions (
49
+ project_id, id, grant_id, token_hash, created_at, expires_at,
50
+ absolute_expires_at
51
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
52
+ RETURNING *
53
+ `
54
+ )
55
+ .get(
56
+ record.projectId,
57
+ record.id,
58
+ record.grantId,
59
+ record.tokenHash,
60
+ record.createdAt.toISOString(),
61
+ record.expiresAt.toISOString(),
62
+ record.absoluteExpiresAt.toISOString()
63
+ ) as SqliteShareSessionRow | null
64
+ if (!row) throw invalidRecord(`Share session '${record.id}' disappeared after create.`)
65
+ return rowToRecord(row)
66
+ } catch (error) {
67
+ if (isUniqueConstraintError(error)) {
68
+ throw new ShareSessionStorageError(
69
+ "duplicate",
70
+ `[SixbSqlite] Share session '${record.id}' conflicts with an existing record.`,
71
+ { cause: error }
72
+ )
73
+ }
74
+ if (isForeignKeyConstraintError(error)) {
75
+ throw new ShareSessionStorageError(
76
+ "invalid_input",
77
+ `[SixbSqlite] Share grant '${record.grantId}' does not exist in project '${record.projectId}'.`,
78
+ { cause: error }
79
+ )
80
+ }
81
+ throw error
82
+ }
83
+ }
84
+
85
+ async getById(input: GetShareSessionByIdInput): Promise<ShareSessionRecord | null> {
86
+ const normalized = normalizeGetShareSessionByIdInput(input)
87
+ return this.getRow(normalized.projectId, normalized.id)
88
+ }
89
+
90
+ async renewIfValid(input: RenewShareSessionIfValidInput): Promise<ShareSessionRecord | null> {
91
+ const normalized = normalizeRenewShareSessionIfValidInput(input)
92
+ const requestedExpiry = normalized.expiresAt.toISOString()
93
+ const now = normalized.now.toISOString()
94
+ const row = this.db
95
+ .query(
96
+ `
97
+ UPDATE share_sessions
98
+ SET expires_at = CASE
99
+ WHEN ? > absolute_expires_at THEN absolute_expires_at
100
+ WHEN ? > expires_at THEN ?
101
+ ELSE expires_at
102
+ END
103
+ WHERE project_id = ?
104
+ AND id = ?
105
+ AND grant_id = ?
106
+ AND token_hash = ?
107
+ AND revoked_at IS NULL
108
+ AND created_at <= ?
109
+ AND expires_at > ?
110
+ AND absolute_expires_at > ?
111
+ RETURNING *
112
+ `
113
+ )
114
+ .get(
115
+ requestedExpiry,
116
+ requestedExpiry,
117
+ requestedExpiry,
118
+ normalized.projectId,
119
+ normalized.id,
120
+ normalized.grantId,
121
+ normalized.tokenHash,
122
+ now,
123
+ now,
124
+ now
125
+ ) as SqliteShareSessionRow | null
126
+ return row ? rowToRecord(row) : null
127
+ }
128
+
129
+ async revoke(input: RevokeShareSessionInput): Promise<ShareSessionRecord | null> {
130
+ const normalized = normalizeRevokeShareSessionInput(input)
131
+ const current = this.getRow(normalized.projectId, normalized.id)
132
+ if (!current || current.revokedAt) return current
133
+ const revocation = normalizeRevokeShareSessionInput(normalized, current.createdAt)
134
+ const row = this.db
135
+ .query(
136
+ `
137
+ UPDATE share_sessions
138
+ SET revoked_at = ?
139
+ WHERE project_id = ? AND id = ? AND revoked_at IS NULL
140
+ RETURNING *
141
+ `
142
+ )
143
+ .get(
144
+ revocation.revokedAt.toISOString(),
145
+ revocation.projectId,
146
+ revocation.id
147
+ ) as SqliteShareSessionRow | null
148
+ return row ? rowToRecord(row) : this.getRow(revocation.projectId, revocation.id)
149
+ }
150
+
151
+ close(): void {
152
+ closeSqliteStoreConnection(this.connection)
153
+ }
154
+
155
+ private getRow(projectId: string, id: string): ShareSessionRecord | null {
156
+ const row = this.db
157
+ .query("SELECT * FROM share_sessions WHERE project_id = ? AND id = ?")
158
+ .get(projectId, id) as SqliteShareSessionRow | null
159
+ return row ? rowToRecord(row) : null
160
+ }
161
+ }
162
+
163
+ interface SqliteShareSessionRow {
164
+ readonly project_id: unknown
165
+ readonly id: unknown
166
+ readonly grant_id: unknown
167
+ readonly token_hash: unknown
168
+ readonly created_at: unknown
169
+ readonly expires_at: unknown
170
+ readonly absolute_expires_at: unknown
171
+ readonly revoked_at: unknown
172
+ }
173
+
174
+ function rowToRecord(row: SqliteShareSessionRow): ShareSessionRecord {
175
+ return parseShareSessionRecord({
176
+ projectId: row.project_id,
177
+ id: row.id,
178
+ grantId: row.grant_id,
179
+ tokenHash: row.token_hash,
180
+ createdAt: row.created_at,
181
+ expiresAt: row.expires_at,
182
+ absoluteExpiresAt: row.absolute_expires_at,
183
+ ...(row.revoked_at === null ? {} : { revokedAt: row.revoked_at }),
184
+ })
185
+ }
186
+
187
+ function invalidRecord(message: string): ShareSessionStorageError {
188
+ return new ShareSessionStorageError("invalid_record", `[SixbSqlite] ${message}`)
189
+ }
@@ -1,3 +1,7 @@
1
1
  export function isUniqueConstraintError(error: unknown): boolean {
2
2
  return error instanceof Error && error.message.includes("UNIQUE constraint failed")
3
3
  }
4
+
5
+ export function isForeignKeyConstraintError(error: unknown): boolean {
6
+ return error instanceof Error && error.message.includes("FOREIGN KEY constraint failed")
7
+ }
@@ -45,6 +45,7 @@ export function closeSqliteStoreConnection(connection: SqliteStoreConnection): v
45
45
  }
46
46
 
47
47
  const activeImmediateTransactions = new WeakSet<Database>()
48
+ const activeDeferredReadTransactions = new WeakSet<Database>()
48
49
 
49
50
  export function runImmediateTransaction<T>(db: Database, run: () => T): T {
50
51
  if (activeImmediateTransactions.has(db)) {
@@ -94,6 +95,37 @@ export async function runImmediateTransactionAsync<T>(
94
95
  }
95
96
  }
96
97
 
98
+ /**
99
+ * Run a multi-statement read against one proven SQLite snapshot.
100
+ *
101
+ * A provider-owned immediate transaction already has a stable snapshot and may be reused. Any
102
+ * other pre-existing transaction is rejected because this provider cannot prove who owns it or
103
+ * which lifecycle will close it.
104
+ */
105
+ export function runDeferredReadTransaction<T>(db: Database, run: () => T): T {
106
+ if (activeImmediateTransactions.has(db) || activeDeferredReadTransactions.has(db)) {
107
+ return run()
108
+ }
109
+ if (db.inTransaction) {
110
+ throw new Error(
111
+ "[SixbSqlite] Selected object reads cannot join an unverified SQLite transaction."
112
+ )
113
+ }
114
+
115
+ db.run("BEGIN DEFERRED")
116
+ activeDeferredReadTransactions.add(db)
117
+ try {
118
+ const result = run()
119
+ db.run("COMMIT")
120
+ return result
121
+ } catch (error) {
122
+ rollbackQuietly(db)
123
+ throw error
124
+ } finally {
125
+ activeDeferredReadTransactions.delete(db)
126
+ }
127
+ }
128
+
97
129
  function rollbackQuietly(db: Database): void {
98
130
  try {
99
131
  db.run("ROLLBACK")
@@ -1 +0,0 @@
1
- {"version":3,"file":"object-query-compiler.d.ts","sourceRoot":"","sources":["../src/object-query-compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAKjB,MAAM,YAAY,CAAA;AAEnB,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;AAEnE,MAAM,WAAW,wBAAwB;IACvC,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,WAAW,EAAE,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,WAAW,EAAE,CAAA;IACxB,KAAK,EAAE,aAAa,CAAA;IACpB,YAAY,CAAC,EAAE,oBAAoB,CAAA;IACnC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAClD,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAAA;IAC7C,aAAa,CAAC,IAAI,EAAE,SAAS,wBAAwB,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAC/F;AAED,UAAU,oBAAoB;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,WAAW,EAAE,CAAA;IACnB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;CACnC;AAWD,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,WAAW,EAAE,CAAA;IACnB,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACrC,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,KAAK,kBAAkB,GACnB;IACE,IAAI,EAAE,UAAU,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,KAAK,GAAG,MAAM,CAAA;CAC1B,GACD;IACE,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,gBAAgB,GAAG,YAAY,CAAA;IACvC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAA;CAC1B,CAAA;AAqBL,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,mBAAmB,CAGrB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"object-storage.d.ts","sourceRoot":"","sources":["../src/object-storage.ts"],"names":[],"mappings":"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"}