@tearleads/client-sdk 0.1.116 → 0.1.118
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/data/keyingProjectionVerification/checkpoints/checkpointContext.d.ts +8 -1
- package/data/keyingProjectionVerification/checkpoints/checkpointContext.js +15 -0
- package/data/keyingProjectionVerification/checkpoints/checkpointContext.js.map +1 -1
- package/data/keyingProjectionVerification/container/containerProjectionVerification.js +3 -1
- package/data/keyingProjectionVerification/container/containerProjectionVerification.js.map +1 -1
- package/data/keyingProjectionVerification/document/documentProjectionVerification.js +2 -1
- package/data/keyingProjectionVerification/document/documentProjectionVerification.js.map +1 -1
- package/data/persistence/container-contents/containerContentsPersistence.js +14 -13
- package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
- package/data/persistence/documents/internal/ensureDocumentsSchema.js +10 -12
- package/data/persistence/documents/internal/ensureDocumentsSchema.js.map +1 -1
- package/data/persistence/verifiedCitations/citationScope.js +121 -0
- package/data/persistence/verifiedCitations/citationScope.js.map +1 -0
- package/data/persistence/verifiedCitations/kekLevelCitations.d.ts +33 -0
- package/data/persistence/verifiedCitations/kekLevelCitations.js +44 -0
- package/data/persistence/verifiedCitations/kekLevelCitations.js.map +1 -0
- package/data/persistence/verifiedManifestBundlePersistence.d.ts +17 -12
- package/data/persistence/verifiedManifestBundlePersistence.js +75 -104
- package/data/persistence/verifiedManifestBundlePersistence.js.map +1 -1
- package/data/sqlite/schema/containerSchema.d.ts +1 -0
- package/data/sqlite/schema/containerSchema.js +8 -1
- package/data/sqlite/schema/containerSchema.js.map +1 -1
- package/data/sqlite/schema/documentAttachmentSchema.d.ts +0 -2
- package/data/sqlite/schema/documentAttachmentSchema.js +0 -4
- package/data/sqlite/schema/documentAttachmentSchema.js.map +1 -1
- package/data/sqlite/schema/index.d.ts +2 -0
- package/data/sqlite/schema/index.js +10 -1
- package/data/sqlite/schema/index.js.map +1 -1
- package/data/sqlite/schema/principalPolicySchema.js +0 -1
- package/data/sqlite/schema/principalPolicySchema.js.map +1 -1
- package/data/sqlite/schema/verifiedManifestBundleSchema.d.ts +7 -2
- package/data/sqlite/schema/verifiedManifestBundleSchema.js +11 -4
- package/data/sqlite/schema/verifiedManifestBundleSchema.js.map +1 -1
- package/data/sqlite/sqlExec.d.ts +12 -0
- package/data/sqlite/sqlExec.js +47 -3
- package/data/sqlite/sqlExec.js.map +1 -1
- package/data/sqlite/sqlTableSchema.js +40 -15
- package/data/sqlite/sqlTableSchema.js.map +1 -1
- package/data/sqlite/sqlitePersistenceRuntime.js +18 -10
- package/data/sqlite/sqlitePersistenceRuntime.js.map +1 -1
- package/internal/api-client/ApiClient.d.ts +3 -2
- package/internal/api-client/ApiClient.js +3 -2
- package/internal/api-client/ApiClient.js.map +1 -1
- package/internal/api-client/index.d.ts +1 -1
- package/internal/api-client/index.js.map +1 -1
- package/internal/api-client/types.d.ts +7 -0
- package/internal/api-client/types.js.map +1 -1
- package/internal/api-client/verifiedManifestCitations.d.ts +26 -9
- package/internal/api-client/verifiedManifestCitations.js +44 -25
- package/internal/api-client/verifiedManifestCitations.js.map +1 -1
- package/internal/api-client/writerProjectionRequests.js +21 -10
- package/internal/api-client/writerProjectionRequests.js.map +1 -1
- package/internal/crypto/index.d.ts +1 -1
- package/internal/crypto/index.js +1 -1
- package/internal/crypto/index.js.map +1 -1
- package/internal/crypto/keying/canonicalJsonGuard.d.ts +7 -0
- package/internal/crypto/keying/canonicalJsonGuard.js +20 -0
- package/internal/crypto/keying/canonicalJsonGuard.js.map +1 -1
- package/internal/crypto/keying/index.d.ts +1 -1
- package/internal/crypto/keying/index.js +1 -1
- package/internal/crypto/keying/index.js.map +1 -1
- package/internal/validators/knownManifestsRefinements.js +1 -1
- package/internal/validators/knownManifestsRefinements.js.map +1 -1
- package/internal/validators/operation/writerProjections.d.ts +42 -18
- package/internal/validators/response/container.d.ts +137 -16
- package/internal/validators/response/container.js +23 -2
- package/internal/validators/response/container.js.map +1 -1
- package/internal/validators/response/documentMutation.d.ts +20 -8
- package/internal/validators/response/index.d.ts +2 -2
- package/internal/validators/response/index.js +2 -2
- package/internal/validators/response/index.js.map +1 -1
- package/internal/validators/response/writerProjectionWire.d.ts +19 -8
- package/internal/validators/response/writerProjectionWire.js +78 -20
- package/internal/validators/response/writerProjectionWire.js.map +1 -1
- package/internal/validators/util/index.d.ts +1 -1
- package/internal/validators/util/index.js +1 -1
- package/internal/validators/util/index.js.map +1 -1
- package/internal/validators/util/knownManifestWire.d.ts +13 -3
- package/internal/validators/util/knownManifestWire.js +37 -10
- package/internal/validators/util/knownManifestWire.js.map +1 -1
- package/package.json +1 -1
- package/workflows/container-contents/remoteHydration/destination/destinationPrefetch.js +10 -5
- package/workflows/container-contents/remoteHydration/destination/destinationPrefetch.js.map +1 -1
- package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerPageHydration.js +4 -6
- package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerPageHydration.js.map +1 -1
- package/workflows/container-contents/remoteHydration/types.d.ts +2 -2
- package/workflows/container-contents/remoteHydration/types.js.map +1 -1
|
@@ -8,10 +8,15 @@ import { defineSqlTableSchema } from "../sqlTableSchema.js";
|
|
|
8
8
|
* verifies the rebuilt projection from scratch. Rows are neither trust nor
|
|
9
9
|
* checkpoint state: a missing, evicted, or damaged row only costs bytes.
|
|
10
10
|
*
|
|
11
|
+
* Container KEK levels share the table as typed rows (`objectKind`
|
|
12
|
+
* `container-kek-level`), keyed by the SHA-256 of their canonical wire bytes.
|
|
13
|
+
*
|
|
11
14
|
* Columns:
|
|
12
|
-
* - `manifestHash`:
|
|
15
|
+
* - `manifestHash`: Citation key: the stored bundle's `manifestHash`, or a
|
|
16
|
+
* level row's digest.
|
|
13
17
|
* - `organizationId`, `objectKind`, `objectId`, `epoch`: The verified
|
|
14
|
-
* manifest's object
|
|
18
|
+
* manifest's object (or the level's container and key epoch), used to cite
|
|
19
|
+
* the lineage relevant to a request.
|
|
15
20
|
* - `bundleJson`: The exact verified bundle as JSON.
|
|
16
21
|
* - `bundleDigest`: SHA-256 hex of `bundleJson`; a mismatch drops the row.
|
|
17
22
|
* - `verifiedAt`: Latest local verification time; oldest rows evict first.
|
|
@@ -27,8 +32,10 @@ export const verifiedAccessManifestBundles = sqliteTable("verified_access_manife
|
|
|
27
32
|
verifiedAt: text("verified_at").notNull(),
|
|
28
33
|
}, (table) => [
|
|
29
34
|
index("verified_access_manifest_bundles_object_idx").on(table.objectKind, table.objectId, desc(table.epoch)),
|
|
30
|
-
|
|
31
|
-
index("
|
|
35
|
+
// Eviction order: oldest verification first, hash as the tiebreak.
|
|
36
|
+
index("verified_access_manifest_bundles_eviction_idx").on(table.verifiedAt, table.manifestHash),
|
|
37
|
+
// Recent citations: an organization's newest verifications first.
|
|
38
|
+
index("verified_access_manifest_bundles_organization_recent_idx").on(table.organizationId, desc(table.verifiedAt), table.manifestHash),
|
|
32
39
|
]);
|
|
33
40
|
export const verifiedManifestBundleTables = [
|
|
34
41
|
defineSqlTableSchema(verifiedAccessManifestBundles),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifiedManifestBundleSchema.js","sourceRoot":"","sources":["verifiedManifestBundleSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAuB,MAAM,mBAAmB,CAAC;AAE9E
|
|
1
|
+
{"version":3,"file":"verifiedManifestBundleSchema.js","sourceRoot":"","sources":["verifiedManifestBundleSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAuB,MAAM,mBAAmB,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,WAAW,CACtD,kCAAkC,EAClC;IACE,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,UAAU,EAAE;IAChD,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,EAAE;IACjD,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE;IACrC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;IACjC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;IACzC,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE;IAC7C,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;CAC1C,EACD,CAAC,KAAK,EAAE,EAAE,CAAC;IACT,KAAK,CAAC,6CAA6C,CAAC,CAAC,EAAE,CACrD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,QAAQ,EACd,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAClB;IACD,mEAAmE;IACnE,KAAK,CAAC,+CAA+C,CAAC,CAAC,EAAE,CACvD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,YAAY,CACnB;IACD,kEAAkE;IAClE,KAAK,CAAC,0DAA0D,CAAC,CAAC,EAAE,CAClE,KAAK,CAAC,cAAc,EACpB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EACtB,KAAK,CAAC,YAAY,CACnB;CACF,CACF,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAkC;IACzE,oBAAoB,CAAC,6BAA6B,CAAC;CACpD,CAAC","sourcesContent":["import { desc } from \"drizzle-orm\";\nimport { index, integer, sqliteTable, text } from \"drizzle-orm/sqlite-core\";\nimport { defineSqlTableSchema, type SqlTableSchema } from \"../sqlTableSchema\";\n\n/**\n * Access-manifest bundles this device verified inside a writer projection,\n * keyed by manifest hash. A later projection read cites them so the API can\n * omit their bytes; the client refills each omission from this row and\n * verifies the rebuilt projection from scratch. Rows are neither trust nor\n * checkpoint state: a missing, evicted, or damaged row only costs bytes.\n *\n * Container KEK levels share the table as typed rows (`objectKind`\n * `container-kek-level`), keyed by the SHA-256 of their canonical wire bytes.\n *\n * Columns:\n * - `manifestHash`: Citation key: the stored bundle's `manifestHash`, or a\n * level row's digest.\n * - `organizationId`, `objectKind`, `objectId`, `epoch`: The verified\n * manifest's object (or the level's container and key epoch), used to cite\n * the lineage relevant to a request.\n * - `bundleJson`: The exact verified bundle as JSON.\n * - `bundleDigest`: SHA-256 hex of `bundleJson`; a mismatch drops the row.\n * - `verifiedAt`: Latest local verification time; oldest rows evict first.\n */\nexport const verifiedAccessManifestBundles = sqliteTable(\n \"verified_access_manifest_bundles\",\n {\n manifestHash: text(\"manifest_hash\").primaryKey(),\n organizationId: text(\"organization_id\").notNull(),\n objectKind: text(\"object_kind\").notNull(),\n objectId: text(\"object_id\").notNull(),\n epoch: integer(\"epoch\").notNull(),\n bundleJson: text(\"bundle_json\").notNull(),\n bundleDigest: text(\"bundle_digest\").notNull(),\n verifiedAt: text(\"verified_at\").notNull(),\n },\n (table) => [\n index(\"verified_access_manifest_bundles_object_idx\").on(\n table.objectKind,\n table.objectId,\n desc(table.epoch),\n ),\n // Eviction order: oldest verification first, hash as the tiebreak.\n index(\"verified_access_manifest_bundles_eviction_idx\").on(\n table.verifiedAt,\n table.manifestHash,\n ),\n // Recent citations: an organization's newest verifications first.\n index(\"verified_access_manifest_bundles_organization_recent_idx\").on(\n table.organizationId,\n desc(table.verifiedAt),\n table.manifestHash,\n ),\n ],\n);\n\nexport const verifiedManifestBundleTables: ReadonlyArray<SqlTableSchema> = [\n defineSqlTableSchema(verifiedAccessManifestBundles),\n];\n"]}
|
package/data/sqlite/sqlExec.d.ts
CHANGED
|
@@ -27,6 +27,18 @@ export declare const unavailableExecSql: ExecSql;
|
|
|
27
27
|
export declare function resolveCanonicalExecSql(execSql: ExecSql): ExecSql;
|
|
28
28
|
export declare function hasCompletedConnectionOnce(execSql: ExecSql, key: string): boolean;
|
|
29
29
|
export declare function markCompletedConnectionOnce(execSql: ExecSql, key: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Start recording the once-per-connection keys completed inside a transaction
|
|
32
|
+
* scope (a transaction or one of its savepoints). Every open must be paired
|
|
33
|
+
* with exactly one `closeTransactionSchemaMemoScope`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function openTransactionSchemaMemoScope(execSql: ExecSql): void;
|
|
36
|
+
/**
|
|
37
|
+
* Close the innermost transaction scope. A rollback forgets only the keys
|
|
38
|
+
* completed inside it, so the next query re-ensures exactly the schema the
|
|
39
|
+
* rollback undid; a commit keeps them, handing them to the enclosing scope.
|
|
40
|
+
*/
|
|
41
|
+
export declare function closeTransactionSchemaMemoScope(execSql: ExecSql, committed: boolean): void;
|
|
30
42
|
export declare function runOncePerConnection(execSql: ExecSql, key: string, run: () => Promise<void>): Promise<void>;
|
|
31
43
|
/**
|
|
32
44
|
* Forget every completed once-per-connection key for this connection. Required
|
package/data/sqlite/sqlExec.js
CHANGED
|
@@ -24,6 +24,11 @@ export function resolveCanonicalExecSql(execSql) {
|
|
|
24
24
|
// awaiting a promise that can only settle after the lock the other context is
|
|
25
25
|
// already holding.
|
|
26
26
|
const completedConnectionOnceKeys = new WeakMap();
|
|
27
|
+
// Keys first completed inside each open transaction scope on a connection,
|
|
28
|
+
// innermost scope last. A rolled-back scope undid that schema work, so it
|
|
29
|
+
// forgets exactly its own keys; a committed savepoint hands its keys to the
|
|
30
|
+
// enclosing scope, whose rollback would undo them too.
|
|
31
|
+
const transactionSchemaMemoScopes = new WeakMap();
|
|
27
32
|
export function hasCompletedConnectionOnce(execSql, key) {
|
|
28
33
|
return (completedConnectionOnceKeys
|
|
29
34
|
.get(resolveCanonicalExecSql(execSql))
|
|
@@ -32,12 +37,51 @@ export function hasCompletedConnectionOnce(execSql, key) {
|
|
|
32
37
|
export function markCompletedConnectionOnce(execSql, key) {
|
|
33
38
|
const canonical = resolveCanonicalExecSql(execSql);
|
|
34
39
|
const keys = completedConnectionOnceKeys.get(canonical) ?? new Set();
|
|
40
|
+
if (keys.has(key))
|
|
41
|
+
return;
|
|
35
42
|
keys.add(key);
|
|
36
43
|
completedConnectionOnceKeys.set(canonical, keys);
|
|
44
|
+
transactionSchemaMemoScopes.get(canonical)?.at(-1)?.add(key);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Start recording the once-per-connection keys completed inside a transaction
|
|
48
|
+
* scope (a transaction or one of its savepoints). Every open must be paired
|
|
49
|
+
* with exactly one `closeTransactionSchemaMemoScope`.
|
|
50
|
+
*/
|
|
51
|
+
export function openTransactionSchemaMemoScope(execSql) {
|
|
52
|
+
const canonical = resolveCanonicalExecSql(execSql);
|
|
53
|
+
const scopes = transactionSchemaMemoScopes.get(canonical) ?? [];
|
|
54
|
+
scopes.push(new Set());
|
|
55
|
+
transactionSchemaMemoScopes.set(canonical, scopes);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Close the innermost transaction scope. A rollback forgets only the keys
|
|
59
|
+
* completed inside it, so the next query re-ensures exactly the schema the
|
|
60
|
+
* rollback undid; a commit keeps them, handing them to the enclosing scope.
|
|
61
|
+
*/
|
|
62
|
+
export function closeTransactionSchemaMemoScope(execSql, committed) {
|
|
63
|
+
const canonical = resolveCanonicalExecSql(execSql);
|
|
64
|
+
const scopes = transactionSchemaMemoScopes.get(canonical);
|
|
65
|
+
const scopeKeys = scopes?.pop();
|
|
66
|
+
if (!scopes || !scopeKeys) {
|
|
67
|
+
throw new Error("No open transaction schema memo scope to close");
|
|
68
|
+
}
|
|
69
|
+
if (scopes.length === 0)
|
|
70
|
+
transactionSchemaMemoScopes.delete(canonical);
|
|
71
|
+
if (committed) {
|
|
72
|
+
const parent = scopes.at(-1);
|
|
73
|
+
for (const key of scopeKeys)
|
|
74
|
+
parent?.add(key);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const completed = completedConnectionOnceKeys.get(canonical);
|
|
78
|
+
for (const key of scopeKeys)
|
|
79
|
+
completed?.delete(key);
|
|
37
80
|
}
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
81
|
+
// Completion is recorded as soon as `run` resolves. Inside a runtime
|
|
82
|
+
// transaction the key joins the open schema memo scope, so a later ROLLBACK
|
|
83
|
+
// forgets it; an explicit transaction outside the runtime must instead call
|
|
84
|
+
// `resetConnectionSchemaMemo` after rolling back.
|
|
41
85
|
export async function runOncePerConnection(execSql, key, run) {
|
|
42
86
|
if (hasCompletedConnectionOnce(execSql, key)) {
|
|
43
87
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlExec.js","sourceRoot":"","sources":["sqlExec.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n readUserTimingClock,\n recordUserTimingEntry,\n} from \"../shared/userTiming\";\n\nexport type SqlRowValue = string | number | null;\nexport type SqlRow = Record<string, SqlRowValue>;\nexport type SqlArrayRow = SqlRowValue[];\nexport type SqlBind = Record<string, SqlRowValue> | ReadonlyArray<SqlRowValue>;\nexport type SqlRowMode = \"object\" | \"array\";\n\nexport interface ExecSql {\n (\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: \"object\" },\n ): Promise<SqlRow[]>;\n (\n sql: string,\n bind: SqlBind | undefined,\n options: { rowMode: \"array\" },\n ): Promise<SqlArrayRow[]>;\n (\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: SqlRowMode },\n ): Promise<Array<SqlRow | SqlArrayRow>>;\n}\n\nexport interface ExecSqlClientLike {\n exec(options: {\n sql: string;\n bind?: SqlBind;\n rowMode?: SqlRowMode;\n }): Promise<{ rows: Array<SqlRow | SqlArrayRow> }>;\n}\n\nexport const unavailableExecSql: ExecSql = async () => {\n throw new Error(\"Database client is unavailable.\");\n};\n\n// Tracks the active serialized mutation chain for each canonical SQL executor\n// so callers sharing one connection cannot interleave transactional statements.\nconst sqlMutationQueue = new WeakMap<ExecSql, Promise<void>>();\n\n// Marks executors that already run inside the shared mutation lock so nested\n// callers can reuse the current lock instead of queueing a second time.\nconst serializedSqlExecs = new WeakSet<ExecSql>();\n\n// Reuses one ExecSql adapter per client so all wrappers around the same\n// connection share the same serialized mutation queue.\nconst clientExecSqls = new WeakMap<ExecSqlClientLike, ExecSql>();\n\n// Maps each locked executor back to the canonical executor whose lock it runs\n// under, so per-connection caches key on one stable identity no matter which\n// wrapper a caller holds.\nconst serializedSqlExecCanonicals = new WeakMap<ExecSql, ExecSql>();\n\nexport function resolveCanonicalExecSql(execSql: ExecSql): ExecSql {\n return serializedSqlExecCanonicals.get(execSql) ?? execSql;\n}\n\n// Completed once-per-connection keys (schema ensures).\n// Only COMPLETED runs are recorded — never in-flight promises — so two lock\n// contexts racing the same key both run the (idempotent) work instead of one\n// awaiting a promise that can only settle after the lock the other context is\n// already holding.\nconst completedConnectionOnceKeys = new WeakMap<ExecSql, Set<string>>();\n\nexport function hasCompletedConnectionOnce(\n execSql: ExecSql,\n key: string,\n): boolean {\n return (\n completedConnectionOnceKeys\n .get(resolveCanonicalExecSql(execSql))\n ?.has(key) ?? false\n );\n}\n\nexport function markCompletedConnectionOnce(\n execSql: ExecSql,\n key: string,\n): void {\n const canonical = resolveCanonicalExecSql(execSql);\n const keys = completedConnectionOnceKeys.get(canonical) ?? new Set<string>();\n keys.add(key);\n completedConnectionOnceKeys.set(canonical, keys);\n}\n\n// Callers must not invoke this inside an explicit SQL transaction that can\n// roll back: completion is recorded as soon as `run` resolves, so a later\n// ROLLBACK would leave the memo asserting schema that no longer exists.\nexport async function runOncePerConnection(\n execSql: ExecSql,\n key: string,\n run: () => Promise<void>,\n): Promise<void> {\n if (hasCompletedConnectionOnce(execSql, key)) {\n return;\n }\n await run();\n markCompletedConnectionOnce(execSql, key);\n}\n\n/**\n * Forget every completed once-per-connection key for this connection. Required\n * after an operation that rebuilds the schema out from under the runtime (the\n * local backup restore drops and recreates all user tables) so the next query\n * re-runs its schema ensures.\n */\nexport function resetConnectionSchemaMemo(execSql: ExecSql): void {\n completedConnectionOnceKeys.delete(resolveCanonicalExecSql(execSql));\n}\n\nfunction defineExecSql(\n run: (\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: SqlRowMode },\n ) => Promise<Array<SqlRow | SqlArrayRow>>,\n): ExecSql {\n function execSql(\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: \"object\" },\n ): Promise<SqlRow[]>;\n function execSql(\n sql: string,\n bind: SqlBind | undefined,\n options: { rowMode: \"array\" },\n ): Promise<SqlArrayRow[]>;\n function execSql(\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: SqlRowMode },\n ): Promise<Array<SqlRow | SqlArrayRow>>;\n async function execSql(\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: SqlRowMode },\n ): Promise<Array<SqlRow | SqlArrayRow>> {\n return run(sql, bind, options);\n }\n\n return execSql;\n}\n\nexport function createExecSql(client: ExecSqlClientLike): ExecSql {\n const existingExecSql = clientExecSqls.get(client);\n if (existingExecSql) {\n return existingExecSql;\n }\n\n const execSql = defineExecSql(async (sql, bind, options) => {\n const result = await client.exec({\n sql,\n ...(bind !== undefined ? { bind } : {}),\n ...(options?.rowMode ? { rowMode: options.rowMode } : {}),\n });\n return result.rows;\n });\n\n clientExecSqls.set(client, execSql);\n return execSql;\n}\n\nfunction isSerializedSqlExec(execSql: ExecSql): boolean {\n return serializedSqlExecs.has(execSql);\n}\n\nfunction createSerializedSqlExec(execSql: ExecSql): ExecSql {\n if (isSerializedSqlExec(execSql)) {\n return execSql;\n }\n\n const serializedExecSql = defineExecSql(execSql);\n serializedSqlExecs.add(serializedExecSql);\n serializedSqlExecCanonicals.set(\n serializedExecSql,\n resolveCanonicalExecSql(execSql),\n );\n return serializedExecSql;\n}\n\n// SQLite transactions are scoped to the shared connection, so we must hold a\n// connection-level lock across multi-statement mutations to prevent interleaving.\n// Nested callers must keep passing the provided locked executor. Re-entering\n// with the original canonical executor will wait on the current mutation and\n// deadlock.\n// Each outermost mutation is drawn on DevTools tracks: time spent queued\n// behind another mutation, then time holding the lock.\nexport async function runSerializedSqlMutation<T>(\n execSql: ExecSql,\n operation: (execSql: ExecSql) => Promise<T> | T,\n): Promise<T> {\n if (isSerializedSqlExec(execSql)) {\n return operation(execSql);\n }\n\n const queuedAtMs = readUserTimingClock();\n const previous = sqlMutationQueue.get(execSql);\n let releaseCurrent = () => {};\n const current = new Promise<void>((resolve) => {\n releaseCurrent = resolve;\n });\n const waitForPrevious = (previous ?? Promise.resolve()).catch(\n () => undefined,\n );\n const queuedCurrent = waitForPrevious.then(() => current);\n\n sqlMutationQueue.set(execSql, queuedCurrent);\n await waitForPrevious;\n const acquiredAtMs = readUserTimingClock();\n if (previous) {\n recordUserTimingEntry({\n endMs: acquiredAtMs,\n name: \"SQLite mutation wait\",\n startMs: queuedAtMs,\n track: \"SQLite mutation queue\",\n });\n }\n\n let failed = true;\n try {\n const result = await operation(createSerializedSqlExec(execSql));\n failed = false;\n return result;\n } finally {\n const releasedAtMs = readUserTimingClock();\n releaseCurrent();\n if (sqlMutationQueue.get(execSql) === queuedCurrent) {\n sqlMutationQueue.delete(execSql);\n }\n recordUserTimingEntry({\n endMs: releasedAtMs,\n failed,\n name: \"SQLite mutation\",\n startMs: acquiredAtMs,\n track: \"SQLite mutations\",\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sqlExec.js","sourceRoot":"","sources":["sqlExec.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n readUserTimingClock,\n recordUserTimingEntry,\n} from \"../shared/userTiming\";\n\nexport type SqlRowValue = string | number | null;\nexport type SqlRow = Record<string, SqlRowValue>;\nexport type SqlArrayRow = SqlRowValue[];\nexport type SqlBind = Record<string, SqlRowValue> | ReadonlyArray<SqlRowValue>;\nexport type SqlRowMode = \"object\" | \"array\";\n\nexport interface ExecSql {\n (\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: \"object\" },\n ): Promise<SqlRow[]>;\n (\n sql: string,\n bind: SqlBind | undefined,\n options: { rowMode: \"array\" },\n ): Promise<SqlArrayRow[]>;\n (\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: SqlRowMode },\n ): Promise<Array<SqlRow | SqlArrayRow>>;\n}\n\nexport interface ExecSqlClientLike {\n exec(options: {\n sql: string;\n bind?: SqlBind;\n rowMode?: SqlRowMode;\n }): Promise<{ rows: Array<SqlRow | SqlArrayRow> }>;\n}\n\nexport const unavailableExecSql: ExecSql = async () => {\n throw new Error(\"Database client is unavailable.\");\n};\n\n// Tracks the active serialized mutation chain for each canonical SQL executor\n// so callers sharing one connection cannot interleave transactional statements.\nconst sqlMutationQueue = new WeakMap<ExecSql, Promise<void>>();\n\n// Marks executors that already run inside the shared mutation lock so nested\n// callers can reuse the current lock instead of queueing a second time.\nconst serializedSqlExecs = new WeakSet<ExecSql>();\n\n// Reuses one ExecSql adapter per client so all wrappers around the same\n// connection share the same serialized mutation queue.\nconst clientExecSqls = new WeakMap<ExecSqlClientLike, ExecSql>();\n\n// Maps each locked executor back to the canonical executor whose lock it runs\n// under, so per-connection caches key on one stable identity no matter which\n// wrapper a caller holds.\nconst serializedSqlExecCanonicals = new WeakMap<ExecSql, ExecSql>();\n\nexport function resolveCanonicalExecSql(execSql: ExecSql): ExecSql {\n return serializedSqlExecCanonicals.get(execSql) ?? execSql;\n}\n\n// Completed once-per-connection keys (schema ensures).\n// Only COMPLETED runs are recorded — never in-flight promises — so two lock\n// contexts racing the same key both run the (idempotent) work instead of one\n// awaiting a promise that can only settle after the lock the other context is\n// already holding.\nconst completedConnectionOnceKeys = new WeakMap<ExecSql, Set<string>>();\n\n// Keys first completed inside each open transaction scope on a connection,\n// innermost scope last. A rolled-back scope undid that schema work, so it\n// forgets exactly its own keys; a committed savepoint hands its keys to the\n// enclosing scope, whose rollback would undo them too.\nconst transactionSchemaMemoScopes = new WeakMap<ExecSql, Set<string>[]>();\n\nexport function hasCompletedConnectionOnce(\n execSql: ExecSql,\n key: string,\n): boolean {\n return (\n completedConnectionOnceKeys\n .get(resolveCanonicalExecSql(execSql))\n ?.has(key) ?? false\n );\n}\n\nexport function markCompletedConnectionOnce(\n execSql: ExecSql,\n key: string,\n): void {\n const canonical = resolveCanonicalExecSql(execSql);\n const keys = completedConnectionOnceKeys.get(canonical) ?? new Set<string>();\n if (keys.has(key)) return;\n keys.add(key);\n completedConnectionOnceKeys.set(canonical, keys);\n transactionSchemaMemoScopes.get(canonical)?.at(-1)?.add(key);\n}\n\n/**\n * Start recording the once-per-connection keys completed inside a transaction\n * scope (a transaction or one of its savepoints). Every open must be paired\n * with exactly one `closeTransactionSchemaMemoScope`.\n */\nexport function openTransactionSchemaMemoScope(execSql: ExecSql): void {\n const canonical = resolveCanonicalExecSql(execSql);\n const scopes = transactionSchemaMemoScopes.get(canonical) ?? [];\n scopes.push(new Set<string>());\n transactionSchemaMemoScopes.set(canonical, scopes);\n}\n\n/**\n * Close the innermost transaction scope. A rollback forgets only the keys\n * completed inside it, so the next query re-ensures exactly the schema the\n * rollback undid; a commit keeps them, handing them to the enclosing scope.\n */\nexport function closeTransactionSchemaMemoScope(\n execSql: ExecSql,\n committed: boolean,\n): void {\n const canonical = resolveCanonicalExecSql(execSql);\n const scopes = transactionSchemaMemoScopes.get(canonical);\n const scopeKeys = scopes?.pop();\n if (!scopes || !scopeKeys) {\n throw new Error(\"No open transaction schema memo scope to close\");\n }\n if (scopes.length === 0) transactionSchemaMemoScopes.delete(canonical);\n if (committed) {\n const parent = scopes.at(-1);\n for (const key of scopeKeys) parent?.add(key);\n return;\n }\n const completed = completedConnectionOnceKeys.get(canonical);\n for (const key of scopeKeys) completed?.delete(key);\n}\n\n// Completion is recorded as soon as `run` resolves. Inside a runtime\n// transaction the key joins the open schema memo scope, so a later ROLLBACK\n// forgets it; an explicit transaction outside the runtime must instead call\n// `resetConnectionSchemaMemo` after rolling back.\nexport async function runOncePerConnection(\n execSql: ExecSql,\n key: string,\n run: () => Promise<void>,\n): Promise<void> {\n if (hasCompletedConnectionOnce(execSql, key)) {\n return;\n }\n await run();\n markCompletedConnectionOnce(execSql, key);\n}\n\n/**\n * Forget every completed once-per-connection key for this connection. Required\n * after an operation that rebuilds the schema out from under the runtime (the\n * local backup restore drops and recreates all user tables) so the next query\n * re-runs its schema ensures.\n */\nexport function resetConnectionSchemaMemo(execSql: ExecSql): void {\n completedConnectionOnceKeys.delete(resolveCanonicalExecSql(execSql));\n}\n\nfunction defineExecSql(\n run: (\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: SqlRowMode },\n ) => Promise<Array<SqlRow | SqlArrayRow>>,\n): ExecSql {\n function execSql(\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: \"object\" },\n ): Promise<SqlRow[]>;\n function execSql(\n sql: string,\n bind: SqlBind | undefined,\n options: { rowMode: \"array\" },\n ): Promise<SqlArrayRow[]>;\n function execSql(\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: SqlRowMode },\n ): Promise<Array<SqlRow | SqlArrayRow>>;\n async function execSql(\n sql: string,\n bind?: SqlBind,\n options?: { rowMode?: SqlRowMode },\n ): Promise<Array<SqlRow | SqlArrayRow>> {\n return run(sql, bind, options);\n }\n\n return execSql;\n}\n\nexport function createExecSql(client: ExecSqlClientLike): ExecSql {\n const existingExecSql = clientExecSqls.get(client);\n if (existingExecSql) {\n return existingExecSql;\n }\n\n const execSql = defineExecSql(async (sql, bind, options) => {\n const result = await client.exec({\n sql,\n ...(bind !== undefined ? { bind } : {}),\n ...(options?.rowMode ? { rowMode: options.rowMode } : {}),\n });\n return result.rows;\n });\n\n clientExecSqls.set(client, execSql);\n return execSql;\n}\n\nfunction isSerializedSqlExec(execSql: ExecSql): boolean {\n return serializedSqlExecs.has(execSql);\n}\n\nfunction createSerializedSqlExec(execSql: ExecSql): ExecSql {\n if (isSerializedSqlExec(execSql)) {\n return execSql;\n }\n\n const serializedExecSql = defineExecSql(execSql);\n serializedSqlExecs.add(serializedExecSql);\n serializedSqlExecCanonicals.set(\n serializedExecSql,\n resolveCanonicalExecSql(execSql),\n );\n return serializedExecSql;\n}\n\n// SQLite transactions are scoped to the shared connection, so we must hold a\n// connection-level lock across multi-statement mutations to prevent interleaving.\n// Nested callers must keep passing the provided locked executor. Re-entering\n// with the original canonical executor will wait on the current mutation and\n// deadlock.\n// Each outermost mutation is drawn on DevTools tracks: time spent queued\n// behind another mutation, then time holding the lock.\nexport async function runSerializedSqlMutation<T>(\n execSql: ExecSql,\n operation: (execSql: ExecSql) => Promise<T> | T,\n): Promise<T> {\n if (isSerializedSqlExec(execSql)) {\n return operation(execSql);\n }\n\n const queuedAtMs = readUserTimingClock();\n const previous = sqlMutationQueue.get(execSql);\n let releaseCurrent = () => {};\n const current = new Promise<void>((resolve) => {\n releaseCurrent = resolve;\n });\n const waitForPrevious = (previous ?? Promise.resolve()).catch(\n () => undefined,\n );\n const queuedCurrent = waitForPrevious.then(() => current);\n\n sqlMutationQueue.set(execSql, queuedCurrent);\n await waitForPrevious;\n const acquiredAtMs = readUserTimingClock();\n if (previous) {\n recordUserTimingEntry({\n endMs: acquiredAtMs,\n name: \"SQLite mutation wait\",\n startMs: queuedAtMs,\n track: \"SQLite mutation queue\",\n });\n }\n\n let failed = true;\n try {\n const result = await operation(createSerializedSqlExec(execSql));\n failed = false;\n return result;\n } finally {\n const releasedAtMs = readUserTimingClock();\n releaseCurrent();\n if (sqlMutationQueue.get(execSql) === queuedCurrent) {\n sqlMutationQueue.delete(execSql);\n }\n recordUserTimingEntry({\n endMs: releasedAtMs,\n failed,\n name: \"SQLite mutation\",\n startMs: acquiredAtMs,\n track: \"SQLite mutations\",\n });\n }\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SQL } from "drizzle-orm";
|
|
2
2
|
import { getTableConfig, SQLiteSyncDialect, } from "drizzle-orm/sqlite-core";
|
|
3
3
|
import { hasCompletedConnectionOnce, markCompletedConnectionOnce, runSerializedSqlMutation, } from "./sqlExec.js";
|
|
4
|
+
import { sqliteRowsPerStatement } from "./sqliteBindBudget.js";
|
|
4
5
|
const sqliteDialect = new SQLiteSyncDialect();
|
|
5
6
|
function renderIdentifier(name) {
|
|
6
7
|
return `"${name.replaceAll('"', '""')}"`;
|
|
@@ -100,15 +101,30 @@ export function defineSqlTableSchema(table) {
|
|
|
100
101
|
function getTableEnsureKey(table) {
|
|
101
102
|
return `sql-table:${table.name}`;
|
|
102
103
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
// Two binds per table; the schema has far fewer tables than this.
|
|
105
|
+
const REQUIRED_COLUMN_TABLES_PER_STATEMENT = sqliteRowsPerStatement(2);
|
|
106
|
+
// One read of every checked table's stored columns. The first obsolete table,
|
|
107
|
+
// in ensure order, fails before any index DDL runs.
|
|
108
|
+
async function assertRequiredColumns(execSql, tables) {
|
|
109
|
+
const checked = tables.filter((table) => (table.requiredColumns?.length ?? 0) > 0);
|
|
110
|
+
const storedColumns = new Map();
|
|
111
|
+
for (let offset = 0; offset < checked.length; offset += REQUIRED_COLUMN_TABLES_PER_STATEMENT) {
|
|
112
|
+
const batch = checked.slice(offset, offset + REQUIRED_COLUMN_TABLES_PER_STATEMENT);
|
|
113
|
+
const rows = await execSql(batch
|
|
114
|
+
.map(() => "SELECT ? AS table_name, name FROM pragma_table_info(?)")
|
|
115
|
+
.join(" UNION ALL "), batch.flatMap((table) => [table.name, table.name]));
|
|
116
|
+
for (const { name, table_name: tableName } of rows) {
|
|
117
|
+
const tableKey = String(tableName);
|
|
118
|
+
const columns = storedColumns.get(tableKey) ?? new Set();
|
|
119
|
+
columns.add(String(name));
|
|
120
|
+
storedColumns.set(tableKey, columns);
|
|
121
|
+
}
|
|
106
122
|
}
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
123
|
+
for (const table of checked) {
|
|
124
|
+
const stored = storedColumns.get(table.name);
|
|
125
|
+
if (table.requiredColumns?.some((column) => !stored?.has(column))) {
|
|
126
|
+
throw new Error(`Local database schema for ${table.name} is obsolete; reset the local database before continuing`);
|
|
127
|
+
}
|
|
112
128
|
}
|
|
113
129
|
}
|
|
114
130
|
// Table ensures run on every query path, so completed ones are
|
|
@@ -118,17 +134,26 @@ async function assertRequiredColumns(execSql, table) {
|
|
|
118
134
|
// EXISTS no-ops. `resetConnectionSchemaMemo` forgets the memo when the schema
|
|
119
135
|
// is rebuilt out from under the runtime (local backup restore).
|
|
120
136
|
export async function ensureSqlTables(execSql, tables) {
|
|
121
|
-
const
|
|
137
|
+
const pendingByName = new Map();
|
|
138
|
+
for (const table of tables) {
|
|
139
|
+
if (!pendingByName.has(table.name) &&
|
|
140
|
+
!hasCompletedConnectionOnce(execSql, getTableEnsureKey(table))) {
|
|
141
|
+
pendingByName.set(table.name, table);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const pendingTables = [...pendingByName.values()];
|
|
122
145
|
if (pendingTables.length === 0) {
|
|
123
146
|
return;
|
|
124
147
|
}
|
|
148
|
+
// Each statement is a worker message, so the DDL travels in three: every
|
|
149
|
+
// CREATE TABLE, then the required-column read (an obsolete table fails
|
|
150
|
+
// before an index can trip over a missing column), then every CREATE INDEX.
|
|
125
151
|
await runSerializedSqlMutation(execSql, async (lockedExecSql) => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
152
|
+
await lockedExecSql(pendingTables.map((table) => table.createSql).join(";\n"));
|
|
153
|
+
await assertRequiredColumns(lockedExecSql, pendingTables);
|
|
154
|
+
const indexSql = pendingTables.flatMap((table) => table.indexes ?? []);
|
|
155
|
+
if (indexSql.length > 0) {
|
|
156
|
+
await lockedExecSql(indexSql.join(";\n"));
|
|
132
157
|
}
|
|
133
158
|
});
|
|
134
159
|
for (const table of pendingTables) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlTableSchema.js","sourceRoot":"","sources":["sqlTableSchema.ts"],"names":[],"mappings":"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","sourcesContent":["import { SQL } from \"drizzle-orm\";\nimport {\n getTableConfig,\n type Index,\n type IndexColumn,\n type SQLiteColumn,\n SQLiteSyncDialect,\n type SQLiteTable,\n} from \"drizzle-orm/sqlite-core\";\nimport type { ExecSql } from \"./sqlExec\";\nimport {\n hasCompletedConnectionOnce,\n markCompletedConnectionOnce,\n runSerializedSqlMutation,\n} from \"./sqlExec\";\n\nexport interface SqlTableSchema {\n name: string;\n createSql: string;\n indexes?: ReadonlyArray<string>;\n requiredColumns?: ReadonlyArray<string>;\n}\n\nconst sqliteDialect = new SQLiteSyncDialect();\n\nfunction renderIdentifier(name: string): string {\n return `\"${name.replaceAll('\"', '\"\"')}\"`;\n}\n\nfunction renderDefaultValue(value: unknown): string {\n if (value === null) {\n return \"NULL\";\n }\n\n if (typeof value === \"boolean\") {\n return value ? \"1\" : \"0\";\n }\n\n if (value instanceof SQL) {\n return sqliteDialect.sqlToQuery(value).sql;\n }\n\n if (typeof value === \"number\") {\n return String(value);\n }\n\n if (typeof value === \"string\") {\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n }\n\n throw new Error(`Unsupported SQLite default value: ${String(value)}`);\n}\n\nfunction renderColumn(\n column: SQLiteColumn,\n inlinePrimaryKeyColumnName?: string,\n): string {\n const parts = [\n renderIdentifier(column.name),\n column.getSQLType().toUpperCase(),\n ];\n\n if (column.notNull) {\n parts.push(\"NOT NULL\");\n }\n\n if (column.hasDefault) {\n parts.push(\"DEFAULT\", renderDefaultValue(column.default));\n }\n\n if (column.primary || column.name === inlinePrimaryKeyColumnName) {\n parts.push(\"PRIMARY KEY\");\n }\n\n if (column.isUnique) {\n parts.push(\"UNIQUE\");\n }\n\n return parts.join(\" \");\n}\n\nfunction renderPrimaryKey(columns: ReadonlyArray<SQLiteColumn>): string {\n const columnNames = columns.map((column) => renderIdentifier(column.name));\n return `PRIMARY KEY (${columnNames.join(\", \")})`;\n}\n\nfunction renderUniqueConstraint(columns: ReadonlyArray<SQLiteColumn>): string {\n const columnNames = columns.map((column) => renderIdentifier(column.name));\n return `UNIQUE (${columnNames.join(\", \")})`;\n}\n\nfunction isSQLiteColumn(value: IndexColumn): value is SQLiteColumn {\n return (\n \"name\" in value && typeof value.name === \"string\" && \"getSQLType\" in value\n );\n}\n\nfunction renderIndexColumn(column: IndexColumn): string {\n if (isSQLiteColumn(column)) {\n return renderIdentifier(column.name);\n }\n\n return sqliteDialect.sqlToQuery(column.getSQL(), \"indexes\").sql;\n}\n\nfunction renderIndex(indexDefinition: Index): string {\n const config = indexDefinition.config;\n const unique = config.unique ? \"UNIQUE \" : \"\";\n const columns = config.columns.map(renderIndexColumn).join(\", \");\n const where = config.where\n ? ` WHERE ${sqliteDialect.sqlToQuery(config.where, \"indexes\").sql}`\n : \"\";\n\n return `CREATE ${unique}INDEX IF NOT EXISTS ${renderIdentifier(\n config.name,\n )} ON ${renderIdentifier(getTableConfig(config.table).name)} (${columns})${where}`;\n}\n\n/**\n * Render a Drizzle SQLite table definition into executable schema SQL.\n *\n * The client runtime creates local tables through `ensureSqlTables`, which works\n * with raw SQL strings rather than Drizzle migrations. This helper keeps the\n * Drizzle table definitions as the source of truth while preserving primary\n * keys, table-level unique constraints, partial indexes, default values, and\n * identifier quoting.\n */\nexport function defineSqlTableSchema(table: SQLiteTable): SqlTableSchema {\n const config = getTableConfig(table);\n const inlinePrimaryKeyColumnName =\n config.primaryKeys.length === 1 &&\n config.primaryKeys[0]?.columns.length === 1\n ? config.primaryKeys[0].columns[0]?.name\n : undefined;\n const columns = config.columns.map((column) =>\n renderColumn(column, inlinePrimaryKeyColumnName),\n );\n const primaryKeys = inlinePrimaryKeyColumnName\n ? []\n : config.primaryKeys.map((key) => renderPrimaryKey(key.columns));\n const uniqueConstraints = config.uniqueConstraints.map((constraint) =>\n renderUniqueConstraint(constraint.columns),\n );\n const columnDefinitions = [...columns, ...primaryKeys, ...uniqueConstraints];\n\n return {\n name: config.name,\n createSql: `CREATE TABLE IF NOT EXISTS ${renderIdentifier(config.name)} (\n ${columnDefinitions.join(\",\\n \")}\n)`,\n indexes: config.indexes.map(renderIndex),\n };\n}\n\nfunction getTableEnsureKey(table: SqlTableSchema): string {\n return `sql-table:${table.name}`;\n}\n\nasync function assertRequiredColumns(\n execSql: ExecSql,\n table: SqlTableSchema,\n): Promise<void> {\n if (!table.requiredColumns || table.requiredColumns.length === 0) {\n return;\n }\n const rows = await execSql(\n `PRAGMA table_info(${renderIdentifier(table.name)})`,\n );\n const storedColumns = new Set(rows.map(({ name }) => name));\n const missingColumns = table.requiredColumns.filter(\n (column) => !storedColumns.has(column),\n );\n if (missingColumns.length > 0) {\n throw new Error(\n `Local database schema for ${table.name} is obsolete; reset the local database before continuing`,\n );\n }\n}\n\n// Table ensures run on every query path, so completed ones are\n// remembered per connection: a re-ensure of an already-ensured table skips the\n// DDL round-trips AND the serialized mutation lock, keeping diagnostic reads\n// from queueing behind bulk-import writes just to re-issue CREATE IF NOT\n// EXISTS no-ops. `resetConnectionSchemaMemo` forgets the memo when the schema\n// is rebuilt out from under the runtime (local backup restore).\nexport async function ensureSqlTables(\n execSql: ExecSql,\n tables: ReadonlyArray<SqlTableSchema>,\n): Promise<void> {\n const pendingTables = tables.filter(\n (table) => !hasCompletedConnectionOnce(execSql, getTableEnsureKey(table)),\n );\n if (pendingTables.length === 0) {\n return;\n }\n\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n for (const table of pendingTables) {\n await lockedExecSql(table.createSql);\n await assertRequiredColumns(lockedExecSql, table);\n for (const indexSql of table.indexes ?? []) {\n await lockedExecSql(indexSql);\n }\n }\n });\n for (const table of pendingTables) {\n markCompletedConnectionOnce(execSql, getTableEnsureKey(table));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sqlTableSchema.js","sourceRoot":"","sources":["sqlTableSchema.ts"],"names":[],"mappings":"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","sourcesContent":["import { SQL } from \"drizzle-orm\";\nimport {\n getTableConfig,\n type Index,\n type IndexColumn,\n type SQLiteColumn,\n SQLiteSyncDialect,\n type SQLiteTable,\n} from \"drizzle-orm/sqlite-core\";\nimport type { ExecSql } from \"./sqlExec\";\nimport {\n hasCompletedConnectionOnce,\n markCompletedConnectionOnce,\n runSerializedSqlMutation,\n} from \"./sqlExec\";\nimport { sqliteRowsPerStatement } from \"./sqliteBindBudget\";\n\nexport interface SqlTableSchema {\n name: string;\n createSql: string;\n indexes?: ReadonlyArray<string>;\n requiredColumns?: ReadonlyArray<string>;\n}\n\nconst sqliteDialect = new SQLiteSyncDialect();\n\nfunction renderIdentifier(name: string): string {\n return `\"${name.replaceAll('\"', '\"\"')}\"`;\n}\n\nfunction renderDefaultValue(value: unknown): string {\n if (value === null) {\n return \"NULL\";\n }\n\n if (typeof value === \"boolean\") {\n return value ? \"1\" : \"0\";\n }\n\n if (value instanceof SQL) {\n return sqliteDialect.sqlToQuery(value).sql;\n }\n\n if (typeof value === \"number\") {\n return String(value);\n }\n\n if (typeof value === \"string\") {\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n }\n\n throw new Error(`Unsupported SQLite default value: ${String(value)}`);\n}\n\nfunction renderColumn(\n column: SQLiteColumn,\n inlinePrimaryKeyColumnName?: string,\n): string {\n const parts = [\n renderIdentifier(column.name),\n column.getSQLType().toUpperCase(),\n ];\n\n if (column.notNull) {\n parts.push(\"NOT NULL\");\n }\n\n if (column.hasDefault) {\n parts.push(\"DEFAULT\", renderDefaultValue(column.default));\n }\n\n if (column.primary || column.name === inlinePrimaryKeyColumnName) {\n parts.push(\"PRIMARY KEY\");\n }\n\n if (column.isUnique) {\n parts.push(\"UNIQUE\");\n }\n\n return parts.join(\" \");\n}\n\nfunction renderPrimaryKey(columns: ReadonlyArray<SQLiteColumn>): string {\n const columnNames = columns.map((column) => renderIdentifier(column.name));\n return `PRIMARY KEY (${columnNames.join(\", \")})`;\n}\n\nfunction renderUniqueConstraint(columns: ReadonlyArray<SQLiteColumn>): string {\n const columnNames = columns.map((column) => renderIdentifier(column.name));\n return `UNIQUE (${columnNames.join(\", \")})`;\n}\n\nfunction isSQLiteColumn(value: IndexColumn): value is SQLiteColumn {\n return (\n \"name\" in value && typeof value.name === \"string\" && \"getSQLType\" in value\n );\n}\n\nfunction renderIndexColumn(column: IndexColumn): string {\n if (isSQLiteColumn(column)) {\n return renderIdentifier(column.name);\n }\n\n return sqliteDialect.sqlToQuery(column.getSQL(), \"indexes\").sql;\n}\n\nfunction renderIndex(indexDefinition: Index): string {\n const config = indexDefinition.config;\n const unique = config.unique ? \"UNIQUE \" : \"\";\n const columns = config.columns.map(renderIndexColumn).join(\", \");\n const where = config.where\n ? ` WHERE ${sqliteDialect.sqlToQuery(config.where, \"indexes\").sql}`\n : \"\";\n\n return `CREATE ${unique}INDEX IF NOT EXISTS ${renderIdentifier(\n config.name,\n )} ON ${renderIdentifier(getTableConfig(config.table).name)} (${columns})${where}`;\n}\n\n/**\n * Render a Drizzle SQLite table definition into executable schema SQL.\n *\n * The client runtime creates local tables through `ensureSqlTables`, which works\n * with raw SQL strings rather than Drizzle migrations. This helper keeps the\n * Drizzle table definitions as the source of truth while preserving primary\n * keys, table-level unique constraints, partial indexes, default values, and\n * identifier quoting.\n */\nexport function defineSqlTableSchema(table: SQLiteTable): SqlTableSchema {\n const config = getTableConfig(table);\n const inlinePrimaryKeyColumnName =\n config.primaryKeys.length === 1 &&\n config.primaryKeys[0]?.columns.length === 1\n ? config.primaryKeys[0].columns[0]?.name\n : undefined;\n const columns = config.columns.map((column) =>\n renderColumn(column, inlinePrimaryKeyColumnName),\n );\n const primaryKeys = inlinePrimaryKeyColumnName\n ? []\n : config.primaryKeys.map((key) => renderPrimaryKey(key.columns));\n const uniqueConstraints = config.uniqueConstraints.map((constraint) =>\n renderUniqueConstraint(constraint.columns),\n );\n const columnDefinitions = [...columns, ...primaryKeys, ...uniqueConstraints];\n\n return {\n name: config.name,\n createSql: `CREATE TABLE IF NOT EXISTS ${renderIdentifier(config.name)} (\n ${columnDefinitions.join(\",\\n \")}\n)`,\n indexes: config.indexes.map(renderIndex),\n };\n}\n\nfunction getTableEnsureKey(table: SqlTableSchema): string {\n return `sql-table:${table.name}`;\n}\n\n// Two binds per table; the schema has far fewer tables than this.\nconst REQUIRED_COLUMN_TABLES_PER_STATEMENT = sqliteRowsPerStatement(2);\n\n// One read of every checked table's stored columns. The first obsolete table,\n// in ensure order, fails before any index DDL runs.\nasync function assertRequiredColumns(\n execSql: ExecSql,\n tables: ReadonlyArray<SqlTableSchema>,\n): Promise<void> {\n const checked = tables.filter(\n (table) => (table.requiredColumns?.length ?? 0) > 0,\n );\n const storedColumns = new Map<string, Set<string>>();\n for (\n let offset = 0;\n offset < checked.length;\n offset += REQUIRED_COLUMN_TABLES_PER_STATEMENT\n ) {\n const batch = checked.slice(\n offset,\n offset + REQUIRED_COLUMN_TABLES_PER_STATEMENT,\n );\n const rows = await execSql(\n batch\n .map(() => \"SELECT ? AS table_name, name FROM pragma_table_info(?)\")\n .join(\" UNION ALL \"),\n batch.flatMap((table) => [table.name, table.name]),\n );\n for (const { name, table_name: tableName } of rows) {\n const tableKey = String(tableName);\n const columns = storedColumns.get(tableKey) ?? new Set<string>();\n columns.add(String(name));\n storedColumns.set(tableKey, columns);\n }\n }\n for (const table of checked) {\n const stored = storedColumns.get(table.name);\n if (table.requiredColumns?.some((column) => !stored?.has(column))) {\n throw new Error(\n `Local database schema for ${table.name} is obsolete; reset the local database before continuing`,\n );\n }\n }\n}\n\n// Table ensures run on every query path, so completed ones are\n// remembered per connection: a re-ensure of an already-ensured table skips the\n// DDL round-trips AND the serialized mutation lock, keeping diagnostic reads\n// from queueing behind bulk-import writes just to re-issue CREATE IF NOT\n// EXISTS no-ops. `resetConnectionSchemaMemo` forgets the memo when the schema\n// is rebuilt out from under the runtime (local backup restore).\nexport async function ensureSqlTables(\n execSql: ExecSql,\n tables: ReadonlyArray<SqlTableSchema>,\n): Promise<void> {\n const pendingByName = new Map<string, SqlTableSchema>();\n for (const table of tables) {\n if (\n !pendingByName.has(table.name) &&\n !hasCompletedConnectionOnce(execSql, getTableEnsureKey(table))\n ) {\n pendingByName.set(table.name, table);\n }\n }\n const pendingTables = [...pendingByName.values()];\n if (pendingTables.length === 0) {\n return;\n }\n\n // Each statement is a worker message, so the DDL travels in three: every\n // CREATE TABLE, then the required-column read (an obsolete table fails\n // before an index can trip over a missing column), then every CREATE INDEX.\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await lockedExecSql(\n pendingTables.map((table) => table.createSql).join(\";\\n\"),\n );\n await assertRequiredColumns(lockedExecSql, pendingTables);\n const indexSql = pendingTables.flatMap((table) => table.indexes ?? []);\n if (indexSql.length > 0) {\n await lockedExecSql(indexSql.join(\";\\n\"));\n }\n });\n for (const table of pendingTables) {\n markCompletedConnectionOnce(execSql, getTableEnsureKey(table));\n }\n}\n"]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { drizzle } from "drizzle-orm/sqlite-proxy";
|
|
2
2
|
import { clientSQLiteSchema } from "./schema/index.js";
|
|
3
|
+
import { closeTransactionSchemaMemoScope, openTransactionSchemaMemoScope, } from "./sqlExec.js";
|
|
3
4
|
import { assertClientSQLiteCommitAllowed, beginClientSQLiteCommitGuards, captureClientSQLiteCommitGuards, clearClientSQLiteCommitGuards, rethrowClientSQLiteCommitGuardFailure, } from "./sqliteCommitGuards.js";
|
|
4
|
-
import { createExecSql,
|
|
5
|
+
import { createExecSql, resolveCanonicalExecSql, runSerializedSqlMutation, } from "./sqlSchema.js";
|
|
5
6
|
const runtimeByExecSql = new WeakMap();
|
|
6
7
|
function transactionBeginStatement(config) {
|
|
7
8
|
if (config?.behavior === "immediate")
|
|
@@ -86,19 +87,23 @@ async function runNestedSavepointScope(canonical, lockedExecSql, operation) {
|
|
|
86
87
|
transactionDepthByCanonicalExecSql.set(canonical, depth + 1);
|
|
87
88
|
const savepoint = `runtime_transaction_sp_${depth}`;
|
|
88
89
|
const discardNestedGuards = captureClientSQLiteCommitGuards(canonical);
|
|
90
|
+
openTransactionSchemaMemoScope(canonical);
|
|
91
|
+
let released = false;
|
|
89
92
|
try {
|
|
90
93
|
await lockedExecSql(`SAVEPOINT ${savepoint}`);
|
|
91
94
|
const result = await operation();
|
|
92
95
|
await lockedExecSql(`RELEASE SAVEPOINT ${savepoint}`);
|
|
96
|
+
released = true;
|
|
93
97
|
return result;
|
|
94
98
|
}
|
|
95
99
|
catch (error) {
|
|
96
100
|
await lockedExecSql(`ROLLBACK TO SAVEPOINT ${savepoint}`).then(discardNestedGuards, () => undefined);
|
|
97
101
|
await lockedExecSql(`RELEASE SAVEPOINT ${savepoint}`).catch(() => undefined);
|
|
98
|
-
resetConnectionSchemaMemo(canonical);
|
|
99
102
|
throw error;
|
|
100
103
|
}
|
|
101
104
|
finally {
|
|
105
|
+
// The savepoint's rollback undid only the schema it first ensured.
|
|
106
|
+
closeTransactionSchemaMemoScope(canonical, released);
|
|
102
107
|
transactionDepthByCanonicalExecSql.set(canonical, depth);
|
|
103
108
|
if (chains.get(depthAtEntry) === tracked) {
|
|
104
109
|
chains.delete(depthAtEntry);
|
|
@@ -142,19 +147,20 @@ function createRuntimeForExecSql(execSql) {
|
|
|
142
147
|
}
|
|
143
148
|
transactionDepthByCanonicalExecSql.set(canonical, 1);
|
|
144
149
|
beginClientSQLiteCommitGuards(canonical);
|
|
150
|
+
openTransactionSchemaMemoScope(canonical);
|
|
151
|
+
let committed = false;
|
|
145
152
|
try {
|
|
146
|
-
|
|
153
|
+
const result = await db.transaction(operation, config);
|
|
154
|
+
committed = true;
|
|
155
|
+
return result;
|
|
147
156
|
}
|
|
148
157
|
catch (error) {
|
|
149
|
-
// The rollback undid any DDL this transaction ran, but the
|
|
150
|
-
// per-connection ensure memo still claims it exists — reset it
|
|
151
|
-
// so later operations re-ensure instead of hitting missing
|
|
152
|
-
// tables.
|
|
153
|
-
resetConnectionSchemaMemo(canonical);
|
|
154
158
|
rethrowClientSQLiteCommitGuardFailure(canonical);
|
|
155
159
|
throw error;
|
|
156
160
|
}
|
|
157
161
|
finally {
|
|
162
|
+
// A rollback forgets the ensures it undid (see sqlExec).
|
|
163
|
+
closeTransactionSchemaMemoScope(canonical, committed);
|
|
158
164
|
clearClientSQLiteCommitGuards(canonical);
|
|
159
165
|
transactionDepthByCanonicalExecSql.delete(canonical);
|
|
160
166
|
}
|
|
@@ -168,12 +174,13 @@ function createRuntimeForExecSql(execSql) {
|
|
|
168
174
|
}
|
|
169
175
|
transactionDepthByCanonicalExecSql.set(canonical, 1);
|
|
170
176
|
beginClientSQLiteCommitGuards(canonical);
|
|
177
|
+
openTransactionSchemaMemoScope(canonical);
|
|
178
|
+
let committed = false;
|
|
171
179
|
try {
|
|
172
180
|
await lockedExecSql(transactionBeginStatement(config));
|
|
173
181
|
const result = await operation(db);
|
|
174
182
|
if (!canCommit()) {
|
|
175
183
|
await lockedExecSql("ROLLBACK");
|
|
176
|
-
resetConnectionSchemaMemo(canonical);
|
|
177
184
|
return { committed: false, result: null };
|
|
178
185
|
}
|
|
179
186
|
// The guard passed in THIS synchronous slice and the COMMIT is
|
|
@@ -184,15 +191,16 @@ function createRuntimeForExecSql(execSql) {
|
|
|
184
191
|
assertClientSQLiteCommitAllowed(lockedExecSql);
|
|
185
192
|
const commit = lockedExecSql("COMMIT");
|
|
186
193
|
await commit;
|
|
194
|
+
committed = true;
|
|
187
195
|
return { committed: true, result };
|
|
188
196
|
}
|
|
189
197
|
catch (error) {
|
|
190
198
|
await lockedExecSql("ROLLBACK").catch(() => undefined);
|
|
191
|
-
resetConnectionSchemaMemo(canonical);
|
|
192
199
|
rethrowClientSQLiteCommitGuardFailure(canonical);
|
|
193
200
|
throw error;
|
|
194
201
|
}
|
|
195
202
|
finally {
|
|
203
|
+
closeTransactionSchemaMemoScope(canonical, committed);
|
|
196
204
|
clearClientSQLiteCommitGuards(canonical);
|
|
197
205
|
transactionDepthByCanonicalExecSql.delete(canonical);
|
|
198
206
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlitePersistenceRuntime.js","sourceRoot":"","sources":["sqlitePersistenceRuntime.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EACL,+BAA+B,EAC/B,6BAA6B,EAC7B,+BAA+B,EAC/B,6BAA6B,EAC7B,qCAAqC,GACtC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,aAAa,EAGb,yBAAyB,EACzB,uBAAuB,EACvB,wBAAwB,GAGzB,MAAM,aAAa,CAAC;AAqDrB,MAAM,gBAAgB,GAAG,IAAI,OAAO,EAA2C,CAAC;AAEhF,SAAS,yBAAyB,CAChC,MAAiD;IAEjD,IAAI,MAAM,EAAE,QAAQ,KAAK,WAAW;QAAE,OAAO,iBAAiB,CAAC;IAC/D,IAAI,MAAM,EAAE,QAAQ,KAAK,WAAW;QAAE,OAAO,iBAAiB,CAAC;IAC/D,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,6EAA6E;AAC7E,6EAA6E;AAC7E,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,MAAM,kCAAkC,GAAG,IAAI,OAAO,EAAmB,CAAC;AAC1E,sEAAsE;AACtE,wEAAwE;AACxE,qEAAqE;AACrE,4EAA4E;AAC5E,4EAA4E;AAC5E,uCAAuC;AACvC,MAAM,mCAAmC,GAAG,IAAI,OAAO,EAGpD,CAAC;AACJ,MAAM,eAAe,GAAG,IAAI,OAAO,EAA8B,CAAC;AAElE,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC3D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,kCAAkC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,oBAAoB,CAAC,UAAyB;IAGrD,SAAS,eAAe,CAAC,IAA4B;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO,EAAE,IAAI,EAAE,GAAG,EAAyB,CAAC;QAC9C,CAAC;QAED,wEAAwE;QACxE,2DAA2D;QAC3D,OAAO,MAAM,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE,EAAyB,EAAE,MAAM,EAAE;YACxE,UAAU,EAAE,IAAI;YAChB,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;QACnC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,QAAQ;YACvC,+BAA+B,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE;YACzD,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC;QAEH,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACrB,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QAED,OAAO,EAAE,IAAI,EAAyB,CAAC;IACzC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,0BAA0B,CACjC,UAAyB;IAEzB,OAAO,OAAO,CAAC,oBAAoB,CAAC,UAAU,CAAC,EAAE;QAC/C,MAAM,EAAE,kBAAkB;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,wEAAwE;AACxE,sEAAsE;AACtE,qEAAqE;AACrE,0EAA0E;AAC1E,4EAA4E;AAC5E,KAAK,UAAU,uBAAuB,CACpC,SAAkB,EAClB,aAAsB,EACtB,SAA2B;IAE3B,MAAM,YAAY,GAAG,kCAAkC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5E,MAAM,MAAM,GACV,mCAAmC,CAAC,GAAG,CAAC,SAAS,CAAC;QAClD,IAAI,GAAG,EAA4B,CAAC;IACtC,mCAAmC,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAC/D,IAAI,OAAO,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IAClD,MAAM,KAAK,GAAG,QAAQ;SACnB,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;SACtB,IAAI,CAAC,KAAK,IAAI,EAAE;QACf,MAAM,KAAK,GAAG,kCAAkC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACrE,kCAAkC,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QAC7D,MAAM,SAAS,GAAG,0BAA0B,KAAK,EAAE,CAAC;QACpD,MAAM,mBAAmB,GAAG,+BAA+B,CAAC,SAAS,CAAC,CAAC;QACvE,IAAI,CAAC;YACH,MAAM,aAAa,CAAC,aAAa,SAAS,EAAE,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;YACjC,MAAM,aAAa,CAAC,qBAAqB,SAAS,EAAE,CAAC,CAAC;YACtD,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,aAAa,CAAC,yBAAyB,SAAS,EAAE,CAAC,CAAC,IAAI,CAC5D,mBAAmB,EACnB,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;YACF,MAAM,aAAa,CAAC,qBAAqB,SAAS,EAAE,CAAC,CAAC,KAAK,CACzD,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;YACF,yBAAyB,CAAC,SAAS,CAAC,CAAC;YACrC,MAAM,KAAK,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,kCAAkC,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YACzD,IAAI,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,OAAO,EAAE,CAAC;gBACzC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IACL,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,uBAAuB,CAC9B,OAAgB;IAEhB,IAAI,aAAa,GAAG,OAAO,CAAC;IAC5B,MAAM,EAAE,GAAG,0BAA0B,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC;IAE3D,KAAK,UAAU,iBAAiB,CAC9B,WAAoB,EACpB,SAA+B;QAE/B,MAAM,eAAe,GAAG,aAAa,CAAC;QACtC,aAAa,GAAG,WAAW,CAAC;QAE5B,IAAI,CAAC;YACH,OAAO,MAAM,SAAS,EAAE,CAAC;QAC3B,CAAC;gBAAS,CAAC;YACT,aAAa,GAAG,eAAe,CAAC;QAClC,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAmC;QAC9C,EAAE;QACF,OAAO;QACP,WAAW,CAAC,SAAS;YACnB,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,CAC/D,iBAAiB,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CACtD,CAAC;QACJ,CAAC;QACD,WAAW,CAAC,SAAS,EAAE,MAAM;YAC3B,sEAAsE;YACtE,uEAAuE;YACvE,uEAAuE;YACvE,oEAAoE;YACpE,kEAAkE;YAClE,+DAA+D;YAC/D,MAAM,SAAS,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;YACnD,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,CAC/D,iBAAiB,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;gBAC1C,IAAI,CAAC,kCAAkC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;oBACjE,OAAO,uBAAuB,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,EAAE,CAC5D,SAAS,CAAC,EAAE,CAAC,CACd,CAAC;gBACJ,CAAC;gBAED,kCAAkC,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;gBACrD,6BAA6B,CAAC,SAAS,CAAC,CAAC;gBACzC,IAAI,CAAC;oBACH,OAAO,MAAM,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;gBACjD,CAAC;gBAAC,OAAO,KAAc,EAAE,CAAC;oBACxB,2DAA2D;oBAC3D,+DAA+D;oBAC/D,2DAA2D;oBAC3D,UAAU;oBACV,yBAAyB,CAAC,SAAS,CAAC,CAAC;oBACrC,qCAAqC,CAAC,SAAS,CAAC,CAAC;oBACjD,MAAM,KAAK,CAAC;gBACd,CAAC;wBAAS,CAAC;oBACT,6BAA6B,CAAC,SAAS,CAAC,CAAC;oBACzC,kCAAkC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACvD,CAAC;YACH,CAAC,CAAC,CACH,CAAC;QACJ,CAAC;QACD,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM;YAC7C,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,CAC/D,iBAAiB,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;gBAC1C,MAAM,SAAS,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;gBACnD,IAAI,CAAC,kCAAkC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;oBACjE,MAAM,IAAI,KAAK,CACb,2DAA2D,CAC5D,CAAC;gBACJ,CAAC;gBAED,kCAAkC,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;gBACrD,6BAA6B,CAAC,SAAS,CAAC,CAAC;gBACzC,IAAI,CAAC;oBACH,MAAM,aAAa,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAC;oBACvD,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,EAAE,CAAC,CAAC;oBACnC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;wBACjB,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC;wBAChC,yBAAyB,CAAC,SAAS,CAAC,CAAC;wBACrC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;oBAC5C,CAAC;oBACD,+DAA+D;oBAC/D,8DAA8D;oBAC9D,+DAA+D;oBAC/D,+DAA+D;oBAC/D,+DAA+D;oBAC/D,+BAA+B,CAAC,aAAa,CAAC,CAAC;oBAC/C,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;oBACvC,MAAM,MAAM,CAAC;oBACb,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gBACrC,CAAC;gBAAC,OAAO,KAAc,EAAE,CAAC;oBACxB,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;oBACvD,yBAAyB,CAAC,SAAS,CAAC,CAAC;oBACrC,qCAAqC,CAAC,SAAS,CAAC,CAAC;oBACjD,MAAM,KAAK,CAAC;gBACd,CAAC;wBAAS,CAAC;oBACT,6BAA6B,CAAC,SAAS,CAAC,CAAC;oBACzC,kCAAkC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACvD,CAAC;YACH,CAAC,CAAC,CACH,CAAC;QACJ,CAAC;KACF,CAAC;IAEF,gBAAgB,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,iCAAiC,CAC/C,OAAgB;IAEhB,OAAO,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,uBAAuB,CAAC,OAAO,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,oCAAoC,CAClD,MAAyB;IAEzB,IAAI,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QAChC,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,iCAAiC,CAAC,OAAO,CAAC,CAAC;AACpD,CAAC","sourcesContent":["import type {\n RemoteCallback,\n SqliteRemoteDatabase,\n} from \"drizzle-orm/sqlite-proxy\";\nimport { drizzle } from \"drizzle-orm/sqlite-proxy\";\nimport { clientSQLiteSchema } from \"./schema\";\nimport {\n assertClientSQLiteCommitAllowed,\n beginClientSQLiteCommitGuards,\n captureClientSQLiteCommitGuards,\n clearClientSQLiteCommitGuards,\n rethrowClientSQLiteCommitGuardFailure,\n} from \"./sqliteCommitGuards\";\nimport {\n createExecSql,\n type ExecSql,\n type ExecSqlClientLike,\n resetConnectionSchemaMemo,\n resolveCanonicalExecSql,\n runSerializedSqlMutation,\n type SqlArrayRow,\n type SqlRowValue,\n} from \"./sqlSchema\";\n\nexport type ClientSQLiteSchema = typeof clientSQLiteSchema;\nexport type ClientSQLiteDatabase = SqliteRemoteDatabase<ClientSQLiteSchema>;\nexport type ClientSQLiteTransactionConfig = NonNullable<\n Parameters<ClientSQLiteDatabase[\"transaction\"]>[1]\n>;\n/**\n * The handle a runtime transaction callback receives: the transaction's\n * query interface WITHOUT `rollback` (throw instead — every scope maps an\n * exception to its own rollback) and WITHOUT `transaction` (nest by calling\n * `runtime.transaction` again, which scopes to a savepoint). Narrowing the\n * contract here is what lets a nested scope share the connection-backed\n * database handle safely.\n */\nexport type ClientSQLiteTransactionScope = Pick<\n ClientSQLiteDatabase,\n | \"all\"\n | \"delete\"\n | \"get\"\n | \"insert\"\n | \"query\"\n | \"run\"\n | \"select\"\n | \"selectDistinct\"\n | \"update\"\n | \"values\"\n>;\n\nexport interface ClientSQLitePersistenceRuntime {\n db: ClientSQLiteDatabase;\n execSql: ExecSql;\n runMutation<T>(\n operation: (db: ClientSQLiteDatabase) => Promise<T> | T,\n ): Promise<T>;\n transaction<T>(\n operation: (tx: ClientSQLiteTransactionScope) => Promise<T>,\n config?: ClientSQLiteTransactionConfig,\n ): Promise<T>;\n /**\n * A transaction whose commit is gated on a SYNCHRONOUS guard: the guard\n * runs and — when it passes — the COMMIT is dispatched in the same\n * synchronous slice, so no JavaScript (an identity replacement included)\n * can interleave between the decision and the commit dispatch. Resolves\n * with `committed: false` (all writes rolled back) when the guard refuses.\n */\n guardedTransaction<T>(\n operation: (tx: ClientSQLiteTransactionScope) => Promise<T>,\n canCommit: () => boolean,\n config?: ClientSQLiteTransactionConfig,\n ): Promise<{ committed: boolean; result: T | null }>;\n}\n\nconst runtimeByExecSql = new WeakMap<ExecSql, ClientSQLitePersistenceRuntime>();\n\nfunction transactionBeginStatement(\n config: ClientSQLiteTransactionConfig | undefined,\n): \"BEGIN\" | \"BEGIN EXCLUSIVE\" | \"BEGIN IMMEDIATE\" {\n if (config?.behavior === \"immediate\") return \"BEGIN IMMEDIATE\";\n if (config?.behavior === \"exclusive\") return \"BEGIN EXCLUSIVE\";\n return \"BEGIN\";\n}\n\n// One transaction depth per underlying connection (canonical executor), so a\n// runtime.transaction call made while another transaction is already open on\n// the same connection degrades to a SAVEPOINT scope instead of issuing a\n// second BEGIN. Callers inside a serialized mutation get separate runtime\n// instances per locked executor, so the depth cannot live on the runtime.\nconst transactionDepthByCanonicalExecSql = new WeakMap<ExecSql, number>();\n// Sibling nested scopes on one connection must not interleave: SQLite\n// savepoints form a stack, so releasing an earlier savepoint would also\n// release a later sibling started concurrently. Scopes serialize per\n// NESTING DEPTH — siblings share a depth and queue behind each other, while\n// a descendant runs one level deeper and must NOT wait on its own enclosing\n// scope (that wait can never resolve).\nconst nestedScopeChainsByCanonicalExecSql = new WeakMap<\n ExecSql,\n Map<number, Promise<unknown>>\n>();\nconst execSqlByClient = new WeakMap<ExecSqlClientLike, ExecSql>();\n\nfunction toSqlRowValue(value: unknown): SqlRowValue {\n if (value === null || value === undefined) {\n return null;\n }\n\n if (typeof value === \"string\" || typeof value === \"number\") {\n return value;\n }\n\n if (typeof value === \"boolean\") {\n return value ? 1 : 0;\n }\n\n throw new Error(`Unsupported SQLite bind value: ${String(value)}`);\n}\n\nfunction createRemoteCallback(getExecSql: () => ExecSql): RemoteCallback {\n type RemoteResult = Awaited<ReturnType<RemoteCallback>>;\n\n function getRemoteResult(rows: readonly SqlArrayRow[]): RemoteResult {\n const row = rows[0];\n if (row !== undefined) {\n return { rows: row } satisfies RemoteResult;\n }\n\n // Drizzle's RemoteCallback type requires rows, but sqlite-proxy's get()\n // mapper treats an undefined rows value as \"no row found\".\n return Object.defineProperty({ rows: [] } satisfies RemoteResult, \"rows\", {\n enumerable: true,\n value: undefined,\n });\n }\n\n return async (sql, params, method) => {\n const execSql = getExecSql();\n if (sql.trim().toLowerCase() === \"commit\")\n assertClientSQLiteCommitAllowed(execSql);\n const rows = await execSql(sql, params.map(toSqlRowValue), {\n rowMode: \"array\",\n });\n\n if (method === \"get\") {\n return getRemoteResult(rows);\n }\n\n return { rows } satisfies RemoteResult;\n };\n}\n\nfunction createClientSQLiteDatabase(\n getExecSql: () => ExecSql,\n): ClientSQLiteDatabase {\n return drizzle(createRemoteCallback(getExecSql), {\n schema: clientSQLiteSchema,\n });\n}\n\n// Serialize nested savepoint scopes per connection AND depth: a sibling\n// (same depth) queues behind the previous sibling so their savepoints\n// strictly nest instead of interleaving on the shared stack, while a\n// descendant (called from inside an executing scope, so one level deeper)\n// starts on its own depth's chain and never waits on its unresolved parent.\nasync function runNestedSavepointScope<T>(\n canonical: ExecSql,\n lockedExecSql: ExecSql,\n operation: () => Promise<T>,\n): Promise<T> {\n const depthAtEntry = transactionDepthByCanonicalExecSql.get(canonical) ?? 0;\n const chains =\n nestedScopeChainsByCanonicalExecSql.get(canonical) ??\n new Map<number, Promise<unknown>>();\n nestedScopeChainsByCanonicalExecSql.set(canonical, chains);\n const previous = chains.get(depthAtEntry) ?? Promise.resolve();\n let tracked: Promise<unknown> = Promise.resolve();\n const scope = previous\n .catch(() => undefined)\n .then(async () => {\n const depth = transactionDepthByCanonicalExecSql.get(canonical) ?? 0;\n transactionDepthByCanonicalExecSql.set(canonical, depth + 1);\n const savepoint = `runtime_transaction_sp_${depth}`;\n const discardNestedGuards = captureClientSQLiteCommitGuards(canonical);\n try {\n await lockedExecSql(`SAVEPOINT ${savepoint}`);\n const result = await operation();\n await lockedExecSql(`RELEASE SAVEPOINT ${savepoint}`);\n return result;\n } catch (error: unknown) {\n await lockedExecSql(`ROLLBACK TO SAVEPOINT ${savepoint}`).then(\n discardNestedGuards,\n () => undefined,\n );\n await lockedExecSql(`RELEASE SAVEPOINT ${savepoint}`).catch(\n () => undefined,\n );\n resetConnectionSchemaMemo(canonical);\n throw error;\n } finally {\n transactionDepthByCanonicalExecSql.set(canonical, depth);\n if (chains.get(depthAtEntry) === tracked) {\n chains.delete(depthAtEntry);\n }\n }\n });\n tracked = scope.catch(() => undefined);\n chains.set(depthAtEntry, tracked);\n return scope;\n}\n\nfunction createRuntimeForExecSql(\n execSql: ExecSql,\n): ClientSQLitePersistenceRuntime {\n let activeExecSql = execSql;\n const db = createClientSQLiteDatabase(() => activeExecSql);\n\n async function withActiveExecSql<T>(\n nextExecSql: ExecSql,\n operation: () => Promise<T> | T,\n ): Promise<T> {\n const previousExecSql = activeExecSql;\n activeExecSql = nextExecSql;\n\n try {\n return await operation();\n } finally {\n activeExecSql = previousExecSql;\n }\n }\n\n const runtime: ClientSQLitePersistenceRuntime = {\n db,\n execSql,\n runMutation(operation) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n withActiveExecSql(lockedExecSql, () => operation(db)),\n );\n },\n transaction(operation, config) {\n // A caller already inside an open transaction on this connection MUST\n // reach us through the transaction's locked executor (the runtime that\n // executor resolves to detects the open transaction below and joins it\n // as a savepoint scope). Re-entering through the canonical executor\n // queues here for full isolation — the contract every persistence\n // helper follows by passing its received locked executor down.\n const canonical = resolveCanonicalExecSql(execSql);\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n withActiveExecSql(lockedExecSql, async () => {\n if ((transactionDepthByCanonicalExecSql.get(canonical) ?? 0) > 0) {\n return runNestedSavepointScope(canonical, lockedExecSql, () =>\n operation(db),\n );\n }\n\n transactionDepthByCanonicalExecSql.set(canonical, 1);\n beginClientSQLiteCommitGuards(canonical);\n try {\n return await db.transaction(operation, config);\n } catch (error: unknown) {\n // The rollback undid any DDL this transaction ran, but the\n // per-connection ensure memo still claims it exists — reset it\n // so later operations re-ensure instead of hitting missing\n // tables.\n resetConnectionSchemaMemo(canonical);\n rethrowClientSQLiteCommitGuardFailure(canonical);\n throw error;\n } finally {\n clearClientSQLiteCommitGuards(canonical);\n transactionDepthByCanonicalExecSql.delete(canonical);\n }\n }),\n );\n },\n guardedTransaction(operation, canCommit, config) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n withActiveExecSql(lockedExecSql, async () => {\n const canonical = resolveCanonicalExecSql(execSql);\n if ((transactionDepthByCanonicalExecSql.get(canonical) ?? 0) > 0) {\n throw new Error(\n \"guardedTransaction cannot nest inside an open transaction\",\n );\n }\n\n transactionDepthByCanonicalExecSql.set(canonical, 1);\n beginClientSQLiteCommitGuards(canonical);\n try {\n await lockedExecSql(transactionBeginStatement(config));\n const result = await operation(db);\n if (!canCommit()) {\n await lockedExecSql(\"ROLLBACK\");\n resetConnectionSchemaMemo(canonical);\n return { committed: false, result: null };\n }\n // The guard passed in THIS synchronous slice and the COMMIT is\n // dispatched in the same slice: no JavaScript can run between\n // the two, so the decision cannot go stale before dispatch. (A\n // host tearing the connection down concurrently is resolved by\n // SQLite's own commit atomicity, not by anything client-side.)\n assertClientSQLiteCommitAllowed(lockedExecSql);\n const commit = lockedExecSql(\"COMMIT\");\n await commit;\n return { committed: true, result };\n } catch (error: unknown) {\n await lockedExecSql(\"ROLLBACK\").catch(() => undefined);\n resetConnectionSchemaMemo(canonical);\n rethrowClientSQLiteCommitGuardFailure(canonical);\n throw error;\n } finally {\n clearClientSQLiteCommitGuards(canonical);\n transactionDepthByCanonicalExecSql.delete(canonical);\n }\n }),\n );\n },\n };\n\n runtimeByExecSql.set(execSql, runtime);\n return runtime;\n}\n\nexport function getClientSQLitePersistenceRuntime(\n execSql: ExecSql,\n): ClientSQLitePersistenceRuntime {\n return runtimeByExecSql.get(execSql) ?? createRuntimeForExecSql(execSql);\n}\n\nexport function createClientSQLitePersistenceRuntime(\n client: ExecSqlClientLike,\n): ClientSQLitePersistenceRuntime {\n let execSql = execSqlByClient.get(client);\n if (!execSql) {\n execSql = createExecSql(client);\n execSqlByClient.set(client, execSql);\n }\n\n return getClientSQLitePersistenceRuntime(execSql);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sqlitePersistenceRuntime.js","sourceRoot":"","sources":["sqlitePersistenceRuntime.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RemoteCallback,\n SqliteRemoteDatabase,\n} from \"drizzle-orm/sqlite-proxy\";\nimport { drizzle } from \"drizzle-orm/sqlite-proxy\";\nimport { clientSQLiteSchema } from \"./schema\";\nimport {\n closeTransactionSchemaMemoScope,\n openTransactionSchemaMemoScope,\n} from \"./sqlExec\";\nimport {\n assertClientSQLiteCommitAllowed,\n beginClientSQLiteCommitGuards,\n captureClientSQLiteCommitGuards,\n clearClientSQLiteCommitGuards,\n rethrowClientSQLiteCommitGuardFailure,\n} from \"./sqliteCommitGuards\";\nimport {\n createExecSql,\n type ExecSql,\n type ExecSqlClientLike,\n resolveCanonicalExecSql,\n runSerializedSqlMutation,\n type SqlArrayRow,\n type SqlRowValue,\n} from \"./sqlSchema\";\n\nexport type ClientSQLiteSchema = typeof clientSQLiteSchema;\nexport type ClientSQLiteDatabase = SqliteRemoteDatabase<ClientSQLiteSchema>;\nexport type ClientSQLiteTransactionConfig = NonNullable<\n Parameters<ClientSQLiteDatabase[\"transaction\"]>[1]\n>;\n/**\n * The handle a runtime transaction callback receives: the transaction's\n * query interface WITHOUT `rollback` (throw instead — every scope maps an\n * exception to its own rollback) and WITHOUT `transaction` (nest by calling\n * `runtime.transaction` again, which scopes to a savepoint). Narrowing the\n * contract here is what lets a nested scope share the connection-backed\n * database handle safely.\n */\nexport type ClientSQLiteTransactionScope = Pick<\n ClientSQLiteDatabase,\n | \"all\"\n | \"delete\"\n | \"get\"\n | \"insert\"\n | \"query\"\n | \"run\"\n | \"select\"\n | \"selectDistinct\"\n | \"update\"\n | \"values\"\n>;\n\nexport interface ClientSQLitePersistenceRuntime {\n db: ClientSQLiteDatabase;\n execSql: ExecSql;\n runMutation<T>(\n operation: (db: ClientSQLiteDatabase) => Promise<T> | T,\n ): Promise<T>;\n transaction<T>(\n operation: (tx: ClientSQLiteTransactionScope) => Promise<T>,\n config?: ClientSQLiteTransactionConfig,\n ): Promise<T>;\n /**\n * A transaction whose commit is gated on a SYNCHRONOUS guard: the guard\n * runs and — when it passes — the COMMIT is dispatched in the same\n * synchronous slice, so no JavaScript (an identity replacement included)\n * can interleave between the decision and the commit dispatch. Resolves\n * with `committed: false` (all writes rolled back) when the guard refuses.\n */\n guardedTransaction<T>(\n operation: (tx: ClientSQLiteTransactionScope) => Promise<T>,\n canCommit: () => boolean,\n config?: ClientSQLiteTransactionConfig,\n ): Promise<{ committed: boolean; result: T | null }>;\n}\n\nconst runtimeByExecSql = new WeakMap<ExecSql, ClientSQLitePersistenceRuntime>();\n\nfunction transactionBeginStatement(\n config: ClientSQLiteTransactionConfig | undefined,\n): \"BEGIN\" | \"BEGIN EXCLUSIVE\" | \"BEGIN IMMEDIATE\" {\n if (config?.behavior === \"immediate\") return \"BEGIN IMMEDIATE\";\n if (config?.behavior === \"exclusive\") return \"BEGIN EXCLUSIVE\";\n return \"BEGIN\";\n}\n\n// One transaction depth per underlying connection (canonical executor), so a\n// runtime.transaction call made while another transaction is already open on\n// the same connection degrades to a SAVEPOINT scope instead of issuing a\n// second BEGIN. Callers inside a serialized mutation get separate runtime\n// instances per locked executor, so the depth cannot live on the runtime.\nconst transactionDepthByCanonicalExecSql = new WeakMap<ExecSql, number>();\n// Sibling nested scopes on one connection must not interleave: SQLite\n// savepoints form a stack, so releasing an earlier savepoint would also\n// release a later sibling started concurrently. Scopes serialize per\n// NESTING DEPTH — siblings share a depth and queue behind each other, while\n// a descendant runs one level deeper and must NOT wait on its own enclosing\n// scope (that wait can never resolve).\nconst nestedScopeChainsByCanonicalExecSql = new WeakMap<\n ExecSql,\n Map<number, Promise<unknown>>\n>();\nconst execSqlByClient = new WeakMap<ExecSqlClientLike, ExecSql>();\n\nfunction toSqlRowValue(value: unknown): SqlRowValue {\n if (value === null || value === undefined) {\n return null;\n }\n\n if (typeof value === \"string\" || typeof value === \"number\") {\n return value;\n }\n\n if (typeof value === \"boolean\") {\n return value ? 1 : 0;\n }\n\n throw new Error(`Unsupported SQLite bind value: ${String(value)}`);\n}\n\nfunction createRemoteCallback(getExecSql: () => ExecSql): RemoteCallback {\n type RemoteResult = Awaited<ReturnType<RemoteCallback>>;\n\n function getRemoteResult(rows: readonly SqlArrayRow[]): RemoteResult {\n const row = rows[0];\n if (row !== undefined) {\n return { rows: row } satisfies RemoteResult;\n }\n\n // Drizzle's RemoteCallback type requires rows, but sqlite-proxy's get()\n // mapper treats an undefined rows value as \"no row found\".\n return Object.defineProperty({ rows: [] } satisfies RemoteResult, \"rows\", {\n enumerable: true,\n value: undefined,\n });\n }\n\n return async (sql, params, method) => {\n const execSql = getExecSql();\n if (sql.trim().toLowerCase() === \"commit\")\n assertClientSQLiteCommitAllowed(execSql);\n const rows = await execSql(sql, params.map(toSqlRowValue), {\n rowMode: \"array\",\n });\n\n if (method === \"get\") {\n return getRemoteResult(rows);\n }\n\n return { rows } satisfies RemoteResult;\n };\n}\n\nfunction createClientSQLiteDatabase(\n getExecSql: () => ExecSql,\n): ClientSQLiteDatabase {\n return drizzle(createRemoteCallback(getExecSql), {\n schema: clientSQLiteSchema,\n });\n}\n\n// Serialize nested savepoint scopes per connection AND depth: a sibling\n// (same depth) queues behind the previous sibling so their savepoints\n// strictly nest instead of interleaving on the shared stack, while a\n// descendant (called from inside an executing scope, so one level deeper)\n// starts on its own depth's chain and never waits on its unresolved parent.\nasync function runNestedSavepointScope<T>(\n canonical: ExecSql,\n lockedExecSql: ExecSql,\n operation: () => Promise<T>,\n): Promise<T> {\n const depthAtEntry = transactionDepthByCanonicalExecSql.get(canonical) ?? 0;\n const chains =\n nestedScopeChainsByCanonicalExecSql.get(canonical) ??\n new Map<number, Promise<unknown>>();\n nestedScopeChainsByCanonicalExecSql.set(canonical, chains);\n const previous = chains.get(depthAtEntry) ?? Promise.resolve();\n let tracked: Promise<unknown> = Promise.resolve();\n const scope = previous\n .catch(() => undefined)\n .then(async () => {\n const depth = transactionDepthByCanonicalExecSql.get(canonical) ?? 0;\n transactionDepthByCanonicalExecSql.set(canonical, depth + 1);\n const savepoint = `runtime_transaction_sp_${depth}`;\n const discardNestedGuards = captureClientSQLiteCommitGuards(canonical);\n openTransactionSchemaMemoScope(canonical);\n let released = false;\n try {\n await lockedExecSql(`SAVEPOINT ${savepoint}`);\n const result = await operation();\n await lockedExecSql(`RELEASE SAVEPOINT ${savepoint}`);\n released = true;\n return result;\n } catch (error: unknown) {\n await lockedExecSql(`ROLLBACK TO SAVEPOINT ${savepoint}`).then(\n discardNestedGuards,\n () => undefined,\n );\n await lockedExecSql(`RELEASE SAVEPOINT ${savepoint}`).catch(\n () => undefined,\n );\n throw error;\n } finally {\n // The savepoint's rollback undid only the schema it first ensured.\n closeTransactionSchemaMemoScope(canonical, released);\n transactionDepthByCanonicalExecSql.set(canonical, depth);\n if (chains.get(depthAtEntry) === tracked) {\n chains.delete(depthAtEntry);\n }\n }\n });\n tracked = scope.catch(() => undefined);\n chains.set(depthAtEntry, tracked);\n return scope;\n}\n\nfunction createRuntimeForExecSql(\n execSql: ExecSql,\n): ClientSQLitePersistenceRuntime {\n let activeExecSql = execSql;\n const db = createClientSQLiteDatabase(() => activeExecSql);\n\n async function withActiveExecSql<T>(\n nextExecSql: ExecSql,\n operation: () => Promise<T> | T,\n ): Promise<T> {\n const previousExecSql = activeExecSql;\n activeExecSql = nextExecSql;\n\n try {\n return await operation();\n } finally {\n activeExecSql = previousExecSql;\n }\n }\n\n const runtime: ClientSQLitePersistenceRuntime = {\n db,\n execSql,\n runMutation(operation) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n withActiveExecSql(lockedExecSql, () => operation(db)),\n );\n },\n transaction(operation, config) {\n // A caller already inside an open transaction on this connection MUST\n // reach us through the transaction's locked executor (the runtime that\n // executor resolves to detects the open transaction below and joins it\n // as a savepoint scope). Re-entering through the canonical executor\n // queues here for full isolation — the contract every persistence\n // helper follows by passing its received locked executor down.\n const canonical = resolveCanonicalExecSql(execSql);\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n withActiveExecSql(lockedExecSql, async () => {\n if ((transactionDepthByCanonicalExecSql.get(canonical) ?? 0) > 0) {\n return runNestedSavepointScope(canonical, lockedExecSql, () =>\n operation(db),\n );\n }\n\n transactionDepthByCanonicalExecSql.set(canonical, 1);\n beginClientSQLiteCommitGuards(canonical);\n openTransactionSchemaMemoScope(canonical);\n let committed = false;\n try {\n const result = await db.transaction(operation, config);\n committed = true;\n return result;\n } catch (error: unknown) {\n rethrowClientSQLiteCommitGuardFailure(canonical);\n throw error;\n } finally {\n // A rollback forgets the ensures it undid (see sqlExec).\n closeTransactionSchemaMemoScope(canonical, committed);\n clearClientSQLiteCommitGuards(canonical);\n transactionDepthByCanonicalExecSql.delete(canonical);\n }\n }),\n );\n },\n guardedTransaction(operation, canCommit, config) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) =>\n withActiveExecSql(lockedExecSql, async () => {\n const canonical = resolveCanonicalExecSql(execSql);\n if ((transactionDepthByCanonicalExecSql.get(canonical) ?? 0) > 0) {\n throw new Error(\n \"guardedTransaction cannot nest inside an open transaction\",\n );\n }\n\n transactionDepthByCanonicalExecSql.set(canonical, 1);\n beginClientSQLiteCommitGuards(canonical);\n openTransactionSchemaMemoScope(canonical);\n let committed = false;\n try {\n await lockedExecSql(transactionBeginStatement(config));\n const result = await operation(db);\n if (!canCommit()) {\n await lockedExecSql(\"ROLLBACK\");\n return { committed: false, result: null };\n }\n // The guard passed in THIS synchronous slice and the COMMIT is\n // dispatched in the same slice: no JavaScript can run between\n // the two, so the decision cannot go stale before dispatch. (A\n // host tearing the connection down concurrently is resolved by\n // SQLite's own commit atomicity, not by anything client-side.)\n assertClientSQLiteCommitAllowed(lockedExecSql);\n const commit = lockedExecSql(\"COMMIT\");\n await commit;\n committed = true;\n return { committed: true, result };\n } catch (error: unknown) {\n await lockedExecSql(\"ROLLBACK\").catch(() => undefined);\n rethrowClientSQLiteCommitGuardFailure(canonical);\n throw error;\n } finally {\n closeTransactionSchemaMemoScope(canonical, committed);\n clearClientSQLiteCommitGuards(canonical);\n transactionDepthByCanonicalExecSql.delete(canonical);\n }\n }),\n );\n },\n };\n\n runtimeByExecSql.set(execSql, runtime);\n return runtime;\n}\n\nexport function getClientSQLitePersistenceRuntime(\n execSql: ExecSql,\n): ClientSQLitePersistenceRuntime {\n return runtimeByExecSql.get(execSql) ?? createRuntimeForExecSql(execSql);\n}\n\nexport function createClientSQLitePersistenceRuntime(\n client: ExecSqlClientLike,\n): ClientSQLitePersistenceRuntime {\n let execSql = execSqlByClient.get(client);\n if (!execSql) {\n execSql = createExecSql(client);\n execSqlByClient.set(client, execSql);\n }\n\n return getClientSQLitePersistenceRuntime(execSql);\n}\n"]}
|
|
@@ -11,7 +11,7 @@ import { type UploadMultipartBlobPartBytesRequest } from "./routes/blobs/get.js"
|
|
|
11
11
|
import { type ContainerKekLogOptions } from "./routes/containers/reads.js";
|
|
12
12
|
import { type DocumentPurgeProofOptions } from "./routes/documents/mutations.js";
|
|
13
13
|
import type { HttpMethod, ListDocumentEditAttributionRangesOptions, RequestResult, RequestResultOptions } from "./types.js";
|
|
14
|
-
import type { VerifiedManifestCitations } from "./verifiedManifestCitations.js";
|
|
14
|
+
import type { VerifiedManifestCitations, WriterProjectionListing } from "./verifiedManifestCitations.js";
|
|
15
15
|
type ExpiredHandler = () => boolean | Promise<boolean>;
|
|
16
16
|
type PaymentRequiredHandler = (organizationId: string | null) => void;
|
|
17
17
|
export declare class ApiClient {
|
|
@@ -2827,7 +2827,8 @@ export declare class ApiClient {
|
|
|
2827
2827
|
} | undefined;
|
|
2828
2828
|
} | null>;
|
|
2829
2829
|
createDocumentResult(input: DocumentCreateRequest, options?: RequestResultOptions): Promise<RequestResult<DocumentCreateResponse>>;
|
|
2830
|
-
|
|
2830
|
+
/** `listing` only picks which held bytes an unstored container cites. */
|
|
2831
|
+
getContainerWriterProjection(containerId: string, listing?: WriterProjectionListing): Promise<{
|
|
2831
2832
|
policyEvidence: {
|
|
2832
2833
|
organization: {
|
|
2833
2834
|
head: {
|
|
@@ -435,8 +435,9 @@ export class ApiClient {
|
|
|
435
435
|
createDocumentResult(input, options = {}) {
|
|
436
436
|
return this.requestResult(documentCreate.path, documentCreate.isResponse, documentCreate.method, JSON.stringify(input), options, createDocumentOperation);
|
|
437
437
|
}
|
|
438
|
-
|
|
439
|
-
|
|
438
|
+
/** `listing` only picks which held bytes an unstored container cites. */
|
|
439
|
+
getContainerWriterProjection(containerId, listing) {
|
|
440
|
+
return cachedRequest(this.containerWriterProjectionRequestsByContainerId, containerId, () => this.request(containerWriterProjection.path(containerId), containerWriterProjection.isResponseFor(containerId), containerWriterProjection.method, undefined, listing ? { projectionListing: listing } : undefined, getContainerWriterProjectionOperation));
|
|
440
441
|
}
|
|
441
442
|
getContainerReplacementAuthorizationsResult(containerId, replacesOrganizationId) {
|
|
442
443
|
return this.transport.requestResult(getContainerReplacementAuthorizationsOperation, { params: { containerId, replacesOrganizationId } }, { reportErrors: false });
|