@tearleads/client-sdk 0.1.113 → 0.1.115

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 (66) hide show
  1. package/data/persistence/container-contents/containerContentsPersistence.js +3 -8
  2. package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
  3. package/data/persistence/container-contents/containerHydrationPersistence.js +15 -5
  4. package/data/persistence/container-contents/containerHydrationPersistence.js.map +1 -1
  5. package/data/persistence/container-contents/containerMetadataMutationPersistence.js +8 -2
  6. package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
  7. package/data/persistence/container-contents/containerMetadataRows.js +34 -2
  8. package/data/persistence/container-contents/containerMetadataRows.js.map +1 -1
  9. package/data/persistence/container-contents/containerPersistence.d.ts +231 -0
  10. package/data/persistence/container-contents/containerPersistence.js +20 -16
  11. package/data/persistence/container-contents/containerPersistence.js.map +1 -1
  12. package/data/persistence/container-contents/containerRecoveryPersistence.js +167 -51
  13. package/data/persistence/container-contents/containerRecoveryPersistence.js.map +1 -1
  14. package/data/persistence/container-contents/documentContainerProjectionPersistence.js +56 -19
  15. package/data/persistence/container-contents/documentContainerProjectionPersistence.js.map +1 -1
  16. package/data/persistence/container-contents/documentMoveIntentPersistence.js +29 -5
  17. package/data/persistence/container-contents/documentMoveIntentPersistence.js.map +1 -1
  18. package/data/persistence/container-contents/documentPlacement.js +24 -5
  19. package/data/persistence/container-contents/documentPlacement.js.map +1 -1
  20. package/data/persistence/container-contents/documentPlacementIntentEnqueue.js +1 -1
  21. package/data/persistence/container-contents/documentPlacementIntentEnqueue.js.map +1 -1
  22. package/data/persistence/documents/containerDocumentTombstoneHoldsPersistence.js +1 -1
  23. package/data/persistence/documents/containerDocumentTombstoneHoldsPersistence.js.map +1 -1
  24. package/data/persistence/documents/documentDiscoveryEvidencePersistence.js +43 -35
  25. package/data/persistence/documents/documentDiscoveryEvidencePersistence.js.map +1 -1
  26. package/data/persistence/documents/documentsPersistence.js +4 -58
  27. package/data/persistence/documents/documentsPersistence.js.map +1 -1
  28. package/data/persistence/documents/internal/containerDocumentTombstoneHolds.js +15 -11
  29. package/data/persistence/documents/internal/containerDocumentTombstoneHolds.js.map +1 -1
  30. package/data/persistence/documents/internal/containerDocumentTombstones.js +87 -35
  31. package/data/persistence/documents/internal/containerDocumentTombstones.js.map +1 -1
  32. package/data/persistence/documents/internal/discoveredDocumentPlacement.js +5 -2
  33. package/data/persistence/documents/internal/discoveredDocumentPlacement.js.map +1 -1
  34. package/data/persistence/documents/internal/discoveredDocumentUpsert.js +174 -0
  35. package/data/persistence/documents/internal/discoveredDocumentUpsert.js.map +1 -0
  36. package/data/persistence/documents/internal/documentMutationPersistence.js +15 -15
  37. package/data/persistence/documents/internal/documentMutationPersistence.js.map +1 -1
  38. package/data/persistence/documents/internal/documentRows.js +122 -11
  39. package/data/persistence/documents/internal/documentRows.js.map +1 -1
  40. package/data/persistence/documents/internal/rowQueryPersistence.js +70 -27
  41. package/data/persistence/documents/internal/rowQueryPersistence.js.map +1 -1
  42. package/data/persistence/documents/records/documentHistoryPersistence.js +17 -11
  43. package/data/persistence/documents/records/documentHistoryPersistence.js.map +1 -1
  44. package/data/persistence/documents/records/documentRecordPersistence.d.ts +5 -0
  45. package/data/persistence/documents/records/documentRecordPersistence.js +41 -6
  46. package/data/persistence/documents/records/documentRecordPersistence.js.map +1 -1
  47. package/data/persistence/documents/records/index.d.ts +1 -1
  48. package/data/persistence/documents/records/index.js +1 -1
  49. package/data/persistence/documents/records/index.js.map +1 -1
  50. package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.d.ts +2 -0
  51. package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.js +17 -1
  52. package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.js.map +1 -1
  53. package/package.json +1 -1
  54. package/workflows/container-contents/documentQueries/index.js +21 -6
  55. package/workflows/container-contents/documentQueries/index.js.map +1 -1
  56. package/workflows/container-contents/remoteReset/containerRemoteReset.js +12 -7
  57. package/workflows/container-contents/remoteReset/containerRemoteReset.js.map +1 -1
  58. package/workflows/documents/documentMutationCommit.js +11 -12
  59. package/workflows/documents/documentMutationCommit.js.map +1 -1
  60. package/workflows/documents/documentMutationPreparation.d.ts +1 -0
  61. package/workflows/documents/documentMutationPreparation.js +1 -0
  62. package/workflows/documents/documentMutationPreparation.js.map +1 -1
  63. package/workflows/documents/documentPersistGuards.js +24 -0
  64. package/workflows/documents/documentPersistGuards.js.map +1 -1
  65. package/workflows/documents/remoteReset/documentRemoteReset.js +31 -18
  66. package/workflows/documents/remoteReset/documentRemoteReset.js.map +1 -1
@@ -5,7 +5,7 @@ import { ensureSqlTables, runOncePerConnection, runSerializedSqlMutation, } from
5
5
  import { enqueueDocumentPendingUpdate, ensureDocumentProjectionTables, ensureDocumentTables, } from "../documents/records/index.js";
6
6
  import { hasQueuedDocumentUpdates, settleQueuedDocumentUpdatesInTransaction, } from "../documents/records/documentPendingUpdatePersistence.js";
7
7
  import { getLatestTimestamp } from "../latestTimestamp.js";
8
- import { ensureContainerTables, loadContainerById, loadContainers as loadContainerRecords, } from "./containerPersistence.js";
8
+ import { ensureContainerTables, loadContainerById, } from "./containerPersistence.js";
9
9
  import { sqlContainerSyncWatermarkPersistence } from "./containerSyncWatermarkPersistence.js";
10
10
  import { claimDormantMetadataSweepAttempt, completeDormantMetadataSweepRequest, listDormantMetadataSweepRequests, } from "./dormantMetadataSweep.js";
11
11
  export { CONTAINER_METADATA_APP_KIND } from "./dormantContainerMetadata.js";
@@ -14,7 +14,7 @@ import { commitStoredHydratedContainer, loadStoredHeldContainerBinding, } from "
14
14
  import { containerIntentPersistence } from "./containerIntentPersistence.js";
15
15
  import { commitStoredMetadataMutation, settleStoredMetadataPendingUpdates, } from "./containerMetadataMutationPersistence.js";
16
16
  import { containerMetadataPullContinuationPersistence } from "./containerMetadataPullContinuationPersistence.js";
17
- import { getContainerMetadataScope, saveContainerContentsContainerRows, selectContainerMetadataRecord, } from "./containerMetadataRows.js";
17
+ import { getContainerMetadataScope, saveContainerContentsContainerRows, selectContainerMetadataRecord, selectContainersWithMetadataRecords, } from "./containerMetadataRows.js";
18
18
  import { abandonStoredMoveIntentRevision } from "./containerMoveAbandonPersistence.js";
19
19
  import { containerPendingUpdatePersistence } from "./containerPendingUpdatePersistence.js";
20
20
  import { rebindStoredHeldContainer } from "./containerRebindPersistence.js";
@@ -134,12 +134,7 @@ export const sqlContainerContentsPersistence = {
134
134
  : []);
135
135
  },
136
136
  async loadContainers(execSql) {
137
- const containers = await loadContainerRecords(execSql);
138
- const storedContainers = await Promise.all(containers.map(async (container) => ({
139
- container,
140
- record: await selectContainerMetadataRecord(execSql, container.id),
141
- })));
142
- return storedContainers;
137
+ return selectContainersWithMetadataRecords(execSql);
143
138
  },
144
139
  async loadContainerMetadataState(execSql, containerId) {
145
140
  await sqlContainerContentsPersistence.ensureSchema(execSql);
@@ -1 +1 @@
1
- {"version":3,"file":"containerContentsPersistence.js","sourceRoot":"","sources":["containerContentsPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { eq } from \"drizzle-orm\";\nimport {\n containerHydrationTombstones,\n containers,\n documentContainerProjectionTables,\n keyingCheckpointTables,\n localIntentTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n ensureSqlTables,\n runOncePerConnection,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentPendingUpdate,\n ensureDocumentProjectionTables,\n ensureDocumentTables,\n} from \"../documents/records\";\nimport {\n hasQueuedDocumentUpdates,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../documents/records/documentPendingUpdatePersistence\";\nimport { getLatestTimestamp } from \"../latestTimestamp\";\nimport {\n ensureContainerTables,\n loadContainerById,\n loadContainers as loadContainerRecords,\n} from \"./containerPersistence\";\nimport { sqlContainerSyncWatermarkPersistence } from \"./containerSyncWatermarkPersistence\";\nimport {\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n listDormantMetadataSweepRequests,\n} from \"./dormantMetadataSweep\";\n\nexport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n ContainerMoveIntentRecord,\n LocalRootDescendantReparentInput,\n StoredContainerState,\n} from \"./containerContentsPersistenceTypes\";\nexport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\n\nimport type {\n ContainerContentsPersistence,\n ContainerMetadataRecord,\n SaveContainerOptions,\n SaveContainerWithPendingUpdateOptions,\n} from \"./containerContentsPersistenceTypes\";\nimport { deleteStoredContainers } from \"./containerDeletionPersistence\";\nimport {\n commitStoredHydratedContainer,\n loadStoredHeldContainerBinding,\n} from \"./containerHydrationPersistence\";\nimport { containerIntentPersistence } from \"./containerIntentPersistence\";\nimport {\n commitStoredMetadataMutation,\n settleStoredMetadataPendingUpdates,\n} from \"./containerMetadataMutationPersistence\";\nimport { containerMetadataPullContinuationPersistence } from \"./containerMetadataPullContinuationPersistence\";\nimport {\n getContainerMetadataScope,\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport { abandonStoredMoveIntentRevision } from \"./containerMoveAbandonPersistence\";\nimport { containerPendingUpdatePersistence } from \"./containerPendingUpdatePersistence\";\nimport { rebindStoredHeldContainer } from \"./containerRebindPersistence\";\nimport { containerReconcilePersistence } from \"./containerReconcilePersistence\";\nimport { isSupersededContainerBinding } from \"./supersededContainerBindings\";\n\nasync function saveStoredContainer(\n execSql: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[0],\n container: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[1],\n record: ContainerMetadataRecord | null,\n options?:\n | SaveContainerOptions\n | SaveContainerWithPendingUpdateOptions\n | undefined,\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt =\n options?.localUpdatedAt ?? options?.updatedAt ?? new Date().toISOString();\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const save = async (tx: ClientSQLiteTransactionScope) => {\n const saved = await saveContainerContentsContainerRows({\n container,\n createIntent: options?.createIntent,\n moveIntent: options?.moveIntent,\n record,\n serverTimestamps: options?.serverTimestamps,\n tx,\n localUpdatedAt,\n });\n if (options && \"pendingUpdate\" in options) {\n await enqueueDocumentPendingUpdate(\n lockedExecSql,\n getContainerMetadataScope(container.id),\n options.pendingUpdate,\n );\n }\n return saved;\n };\n if (!options?.stillCurrent) {\n return runtime.transaction(save);\n }\n const outcome = await runtime.guardedTransaction(\n save,\n options.stillCurrent,\n { behavior: \"immediate\" },\n );\n // Guarded callers recheck the same monotonic generation immediately after\n // this await. Returning the untouched candidate keeps the long-standing\n // non-null save contract while the transaction itself remains rolled back.\n return outcome.result ?? container;\n });\n}\n\nexport const sqlContainerContentsPersistence: ContainerContentsPersistence = {\n ...containerIntentPersistence,\n ...containerMetadataPullContinuationPersistence,\n commitHydratedContainer: commitStoredHydratedContainer,\n commitMetadataMutation: commitStoredMetadataMutation,\n settleAcceptedMetadataPendingUpdates: settleStoredMetadataPendingUpdates,\n listDormantMetadataSweepRequests,\n ...containerPendingUpdatePersistence,\n ...containerReconcilePersistence,\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n async containerExists(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ id: containers.id })\n .from(containers)\n .where(eq(containers.id, containerId))\n .limit(1);\n return rows.length > 0;\n },\n async deleteContainer(execSql, containerId, options) {\n await sqlContainerContentsPersistence.deleteContainers(execSql, [\n {\n containerId,\n reason: options?.reason ?? \"deleted\",\n updatedAt: options?.updatedAt ?? new Date().toISOString(),\n },\n ]);\n },\n async deleteContainers(execSql, removals, options) {\n return deleteStoredContainers(execSql, removals, options);\n },\n async ensureSchema(execSql) {\n // Once ensured on this connection, skip the outer mutation lock entirely:\n // ensureSchema runs on every query path, and re-acquiring the lock just to\n // no-op would queue reads behind unrelated writes.\n await runOncePerConnection(execSql, \"ensure:container-contents\", () =>\n runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentTables(lockedExecSql);\n await ensureSqlTables(lockedExecSql, localIntentTables);\n // Metadata rows consult document purge checkpoints before writing.\n await ensureSqlTables(lockedExecSql, keyingCheckpointTables);\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n await ensureDocumentProjectionTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n }),\n );\n },\n async loadHeldContainerBinding(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return loadStoredHeldContainerBinding(execSql, containerId);\n },\n async isSupersededContainerBinding(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return isSupersededContainerBinding(execSql, input);\n },\n async abandonMoveIntentRevision(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return abandonStoredMoveIntentRevision(execSql, input);\n },\n async rebindHeldContainer(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return rebindStoredHeldContainer(execSql, input);\n },\n async loadContainerMetadataRecord(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return selectContainerMetadataRecord(execSql, containerId);\n },\n async loadContainerHydrationTombstones(execSql) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n containerId: containerHydrationTombstones.containerId,\n generation: containerHydrationTombstones.generation,\n reason: containerHydrationTombstones.reason,\n updatedAt: containerHydrationTombstones.updatedAt,\n })\n .from(containerHydrationTombstones)\n .where(eq(containerHydrationTombstones.cleared, false));\n return rows.flatMap((row) =>\n row.reason === \"access_revoked\" || row.reason === \"deleted\"\n ? [{ ...row, reason: row.reason }]\n : [],\n );\n },\n async loadContainers(execSql) {\n const containers = await loadContainerRecords(execSql);\n const storedContainers = await Promise.all(\n containers.map(async (container) => ({\n container,\n record: await selectContainerMetadataRecord(execSql, container.id),\n })),\n );\n\n return storedContainers;\n },\n async loadContainerMetadataState(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const container = await loadContainerById(execSql, containerId);\n if (!container) {\n return null;\n }\n return {\n container,\n record: await selectContainerMetadataRecord(execSql, containerId),\n };\n },\n async saveContainer(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerWithPendingUpdate(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerAndDeletePendingUpdates(\n execSql,\n container,\n record,\n pendingUpdateIds,\n ) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt = new Date().toISOString();\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const scope = getContainerMetadataScope(container.id);\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n scope,\n pendingUpdateIds,\n );\n const hasRemainingPendingUpdates = await hasQueuedDocumentUpdates(\n tx,\n scope,\n );\n const containerToSave = hasRemainingPendingUpdates\n ? container\n : {\n ...container,\n serverUpdatedAt: getLatestTimestamp(\n container.serverUpdatedAt,\n localUpdatedAt,\n ),\n };\n\n return saveContainerContentsContainerRows({\n container: containerToSave,\n record,\n tx,\n localUpdatedAt,\n });\n },\n );\n });\n },\n};\n"]}
1
+ {"version":3,"file":"containerContentsPersistence.js","sourceRoot":"","sources":["containerContentsPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { eq } from \"drizzle-orm\";\nimport {\n containerHydrationTombstones,\n containers,\n documentContainerProjectionTables,\n keyingCheckpointTables,\n localIntentTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n ensureSqlTables,\n runOncePerConnection,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentPendingUpdate,\n ensureDocumentProjectionTables,\n ensureDocumentTables,\n} from \"../documents/records\";\nimport {\n hasQueuedDocumentUpdates,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../documents/records/documentPendingUpdatePersistence\";\nimport { getLatestTimestamp } from \"../latestTimestamp\";\nimport {\n ensureContainerTables,\n loadContainerById,\n} from \"./containerPersistence\";\nimport { sqlContainerSyncWatermarkPersistence } from \"./containerSyncWatermarkPersistence\";\nimport {\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n listDormantMetadataSweepRequests,\n} from \"./dormantMetadataSweep\";\n\nexport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n ContainerMoveIntentRecord,\n LocalRootDescendantReparentInput,\n StoredContainerState,\n} from \"./containerContentsPersistenceTypes\";\nexport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\n\nimport type {\n ContainerContentsPersistence,\n ContainerMetadataRecord,\n SaveContainerOptions,\n SaveContainerWithPendingUpdateOptions,\n} from \"./containerContentsPersistenceTypes\";\nimport { deleteStoredContainers } from \"./containerDeletionPersistence\";\nimport {\n commitStoredHydratedContainer,\n loadStoredHeldContainerBinding,\n} from \"./containerHydrationPersistence\";\nimport { containerIntentPersistence } from \"./containerIntentPersistence\";\nimport {\n commitStoredMetadataMutation,\n settleStoredMetadataPendingUpdates,\n} from \"./containerMetadataMutationPersistence\";\nimport { containerMetadataPullContinuationPersistence } from \"./containerMetadataPullContinuationPersistence\";\nimport {\n getContainerMetadataScope,\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n selectContainersWithMetadataRecords,\n} from \"./containerMetadataRows\";\nimport { abandonStoredMoveIntentRevision } from \"./containerMoveAbandonPersistence\";\nimport { containerPendingUpdatePersistence } from \"./containerPendingUpdatePersistence\";\nimport { rebindStoredHeldContainer } from \"./containerRebindPersistence\";\nimport { containerReconcilePersistence } from \"./containerReconcilePersistence\";\nimport { isSupersededContainerBinding } from \"./supersededContainerBindings\";\n\nasync function saveStoredContainer(\n execSql: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[0],\n container: Parameters<ContainerContentsPersistence[\"saveContainer\"]>[1],\n record: ContainerMetadataRecord | null,\n options?:\n | SaveContainerOptions\n | SaveContainerWithPendingUpdateOptions\n | undefined,\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt =\n options?.localUpdatedAt ?? options?.updatedAt ?? new Date().toISOString();\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const save = async (tx: ClientSQLiteTransactionScope) => {\n const saved = await saveContainerContentsContainerRows({\n container,\n createIntent: options?.createIntent,\n moveIntent: options?.moveIntent,\n record,\n serverTimestamps: options?.serverTimestamps,\n tx,\n localUpdatedAt,\n });\n if (options && \"pendingUpdate\" in options) {\n await enqueueDocumentPendingUpdate(\n lockedExecSql,\n getContainerMetadataScope(container.id),\n options.pendingUpdate,\n );\n }\n return saved;\n };\n if (!options?.stillCurrent) {\n return runtime.transaction(save);\n }\n const outcome = await runtime.guardedTransaction(\n save,\n options.stillCurrent,\n { behavior: \"immediate\" },\n );\n // Guarded callers recheck the same monotonic generation immediately after\n // this await. Returning the untouched candidate keeps the long-standing\n // non-null save contract while the transaction itself remains rolled back.\n return outcome.result ?? container;\n });\n}\n\nexport const sqlContainerContentsPersistence: ContainerContentsPersistence = {\n ...containerIntentPersistence,\n ...containerMetadataPullContinuationPersistence,\n commitHydratedContainer: commitStoredHydratedContainer,\n commitMetadataMutation: commitStoredMetadataMutation,\n settleAcceptedMetadataPendingUpdates: settleStoredMetadataPendingUpdates,\n listDormantMetadataSweepRequests,\n ...containerPendingUpdatePersistence,\n ...containerReconcilePersistence,\n claimDormantMetadataSweepAttempt,\n completeDormantMetadataSweepRequest,\n async containerExists(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ id: containers.id })\n .from(containers)\n .where(eq(containers.id, containerId))\n .limit(1);\n return rows.length > 0;\n },\n async deleteContainer(execSql, containerId, options) {\n await sqlContainerContentsPersistence.deleteContainers(execSql, [\n {\n containerId,\n reason: options?.reason ?? \"deleted\",\n updatedAt: options?.updatedAt ?? new Date().toISOString(),\n },\n ]);\n },\n async deleteContainers(execSql, removals, options) {\n return deleteStoredContainers(execSql, removals, options);\n },\n async ensureSchema(execSql) {\n // Once ensured on this connection, skip the outer mutation lock entirely:\n // ensureSchema runs on every query path, and re-acquiring the lock just to\n // no-op would queue reads behind unrelated writes.\n await runOncePerConnection(execSql, \"ensure:container-contents\", () =>\n runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentTables(lockedExecSql);\n await ensureSqlTables(lockedExecSql, localIntentTables);\n // Metadata rows consult document purge checkpoints before writing.\n await ensureSqlTables(lockedExecSql, keyingCheckpointTables);\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n await ensureDocumentProjectionTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n }),\n );\n },\n async loadHeldContainerBinding(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return loadStoredHeldContainerBinding(execSql, containerId);\n },\n async isSupersededContainerBinding(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return isSupersededContainerBinding(execSql, input);\n },\n async abandonMoveIntentRevision(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return abandonStoredMoveIntentRevision(execSql, input);\n },\n async rebindHeldContainer(execSql, input) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return rebindStoredHeldContainer(execSql, input);\n },\n async loadContainerMetadataRecord(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n return selectContainerMetadataRecord(execSql, containerId);\n },\n async loadContainerHydrationTombstones(execSql) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n containerId: containerHydrationTombstones.containerId,\n generation: containerHydrationTombstones.generation,\n reason: containerHydrationTombstones.reason,\n updatedAt: containerHydrationTombstones.updatedAt,\n })\n .from(containerHydrationTombstones)\n .where(eq(containerHydrationTombstones.cleared, false));\n return rows.flatMap((row) =>\n row.reason === \"access_revoked\" || row.reason === \"deleted\"\n ? [{ ...row, reason: row.reason }]\n : [],\n );\n },\n async loadContainers(execSql) {\n return selectContainersWithMetadataRecords(execSql);\n },\n async loadContainerMetadataState(execSql, containerId) {\n await sqlContainerContentsPersistence.ensureSchema(execSql);\n const container = await loadContainerById(execSql, containerId);\n if (!container) {\n return null;\n }\n return {\n container,\n record: await selectContainerMetadataRecord(execSql, containerId),\n };\n },\n async saveContainer(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerWithPendingUpdate(execSql, container, record, options) {\n return saveStoredContainer(execSql, container, record, options);\n },\n async saveContainerAndDeletePendingUpdates(\n execSql,\n container,\n record,\n pendingUpdateIds,\n ) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const localUpdatedAt = new Date().toISOString();\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const scope = getContainerMetadataScope(container.id);\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n scope,\n pendingUpdateIds,\n );\n const hasRemainingPendingUpdates = await hasQueuedDocumentUpdates(\n tx,\n scope,\n );\n const containerToSave = hasRemainingPendingUpdates\n ? container\n : {\n ...container,\n serverUpdatedAt: getLatestTimestamp(\n container.serverUpdatedAt,\n localUpdatedAt,\n ),\n };\n\n return saveContainerContentsContainerRows({\n container: containerToSave,\n record,\n tx,\n localUpdatedAt,\n });\n },\n );\n });\n },\n};\n"]}
@@ -117,6 +117,20 @@ export async function loadStoredHeldContainerBinding(execSql, containerId) {
117
117
  metadataDocumentId: record.documentId,
118
118
  };
119
119
  }
120
+ /**
121
+ * Clears the tombstone the fence read found. That read covers the container's
122
+ * only row that is not yet cleared (one row per container), so without one
123
+ * there is nothing to clear.
124
+ */
125
+ async function clearObservedHydrationFence(tx, containerId, fence) {
126
+ if (!fence)
127
+ return;
128
+ await tx
129
+ .update(containerHydrationTombstones)
130
+ .set({ cleared: true })
131
+ .where(eq(containerHydrationTombstones.containerId, containerId))
132
+ .run();
133
+ }
120
134
  export async function commitStoredHydratedContainer(execSql, input) {
121
135
  return runSerializedSqlMutation(execSql, async (lockedExecSql) => {
122
136
  const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);
@@ -168,11 +182,7 @@ export async function commitStoredHydratedContainer(execSql, input) {
168
182
  ],
169
183
  });
170
184
  const container = await saveRecoveredContainer(tx, input);
171
- await tx
172
- .update(containerHydrationTombstones)
173
- .set({ cleared: true })
174
- .where(eq(containerHydrationTombstones.containerId, input.container.id))
175
- .run();
185
+ await clearObservedHydrationFence(tx, input.container.id, fence);
176
186
  return { committed: true, container };
177
187
  };
178
188
  if (!input.stillCurrent) {
@@ -1 +1 @@
1
- {"version":3,"file":"containerHydrationPersistence.js","sourceRoot":"","sources":["containerHydrationPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { and, eq, inArray, sql } from \"drizzle-orm\";\nimport { documentSyncPullContinuationsEqual } from \"../../documents/shared/sync/pullContinuation\";\nimport {\n containerHydrationTombstones,\n containers,\n dormantContainerMetadata,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport { loadStoredAccessManifestCheckpoint } from \"../keyingCheckpoints/keyingCheckpointPersistence\";\nimport {\n assertHeldContainerBinding,\n type HeldContainerBinding,\n heldContainerBinding,\n} from \"./containerBinding\";\nimport type {\n ContainerContentsPersistence,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n} from \"./containerContentsPersistenceTypes\";\nimport {\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport { loadContainerById } from \"./containerPersistence\";\nimport { isSupersededContainerBinding } from \"./supersededContainerBindings\";\n\nfunction sameNullableValue(\n left: string | null | undefined,\n right: string | null | undefined,\n): boolean {\n return (left ?? null) === (right ?? null);\n}\n\nfunction sameMetadataRecord(\n current: ContainerMetadataRecord | null,\n expected: ContainerMetadataRecord | null,\n): boolean {\n if (!current || !expected) return current === expected;\n return (\n current.id === expected.id &&\n current.documentId === expected.documentId &&\n current.accessEpoch === expected.accessEpoch &&\n sameNullableValue(current.accessStateHash, expected.accessStateHash) &&\n sameNullableValue(current.lastCommitLsn, expected.lastCommitLsn) &&\n sameNullableValue(current.contentKeyBundle, expected.contentKeyBundle) &&\n sameNullableValue(\n current.documentKekTargets,\n expected.documentKekTargets,\n ) &&\n sameNullableValue(\n current.documentManifestBundle,\n expected.documentManifestBundle,\n ) &&\n current.metadataUpdates === expected.metadataUpdates &&\n current.snapshotEndVersion === expected.snapshotEndVersion &&\n documentSyncPullContinuationsEqual(\n current.pullContinuation,\n expected.pullContinuation,\n ) &&\n Boolean(current.pullContinuationRecoveryRequired) ===\n Boolean(expected.pullContinuationRecoveryRequired)\n );\n}\n\nfunction sameHydrationTombstone(\n current:\n | { generation: number; reason: string; updatedAt: string }\n | undefined,\n expected: ContainerHydrationTombstone | null | undefined,\n): boolean {\n return (\n current !== undefined &&\n expected != null &&\n current.generation === expected.generation &&\n current.reason === expected.reason &&\n current.updatedAt === expected.updatedAt\n );\n}\n\nexport async function recordContainerHydrationTombstones(input: {\n removals: ReadonlyArray<{\n containerId: string;\n reason: \"access_revoked\" | \"deleted\";\n updatedAt: string;\n }>;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n if (input.removals.length === 0) return;\n await input.tx\n .insert(containerHydrationTombstones)\n .values([...input.removals])\n .onConflictDoUpdate({\n target: containerHydrationTombstones.containerId,\n set: {\n generation: sql`${containerHydrationTombstones.generation} + 1`,\n cleared: false,\n reason: sql`CASE\n WHEN ${containerHydrationTombstones.reason} = 'deleted' THEN 'deleted'\n ELSE excluded.reason\n END`,\n updatedAt: sql`MAX(${containerHydrationTombstones.updatedAt}, excluded.updated_at)`,\n },\n })\n .run();\n}\n\nasync function placementCheckpointMatches(\n tx: ClientSQLiteTransactionScope,\n input: Parameters<ContainerContentsPersistence[\"commitHydratedContainer\"]>[1],\n): Promise<boolean> {\n const expected = input.expectedPlacementCheckpoint;\n if (!expected) return true;\n const current = await loadStoredAccessManifestCheckpoint(tx, expected);\n return (\n expected.objectKind === \"container\" &&\n expected.objectId === input.container.id &&\n expected.organizationId === input.container.organizationId &&\n current?.epoch === expected.epoch &&\n current.manifestHash === expected.manifestHash\n );\n}\n\n/** A retained local move continues to own placement after verified reattachment. */\nasync function saveRecoveredContainer(\n tx: ClientSQLiteTransactionScope,\n input: Parameters<ContainerContentsPersistence[\"commitHydratedContainer\"]>[1],\n) {\n const [move] = await tx\n .select({\n parentContainerId: localIntents.targetContainerId,\n updatedAt: localIntents.updatedAt,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerMove),\n eq(localIntents.subjectId, input.container.id),\n inArray(localIntents.syncStatus, [\"pending\", \"blocked\"]),\n ),\n )\n .limit(1);\n return saveContainerContentsContainerRows({\n container: move\n ? { ...input.container, parentId: move.parentContainerId }\n : input.container,\n localUpdatedAt:\n move?.updatedAt ??\n input.saveOptions.localUpdatedAt ??\n input.remoteUpdatedAt,\n record: input.record,\n serverTimestamps: input.saveOptions.serverTimestamps,\n tx,\n });\n}\n\nasync function loadDormantOrganizationId(\n execSql: ExecSql,\n containerId: string,\n): Promise<string | null> {\n const [row] = await getClientSQLitePersistenceRuntime(execSql)\n .db.select({ organizationId: dormantContainerMetadata.organizationId })\n .from(dormantContainerMetadata)\n .where(eq(dormantContainerMetadata.containerId, containerId))\n .limit(1);\n return row?.organizationId ?? null;\n}\n\n/**\n * The binding this device holds for a container id: its live row, or the\n * organization and metadata target retained after access loss. Null when the\n * device has never held the container.\n */\nexport async function loadStoredHeldContainerBinding(\n execSql: ExecSql,\n containerId: string,\n): Promise<HeldContainerBinding | null> {\n const container = await loadContainerById(execSql, containerId);\n const record = await selectContainerMetadataRecord(execSql, containerId);\n if (container) return heldContainerBinding({ container, record });\n if (!record) return null;\n // Retention records the organization with each retained record; without one\n // the retained metadata target still pins the binding.\n const dormantOrganizationId = await loadDormantOrganizationId(\n execSql,\n containerId,\n );\n return {\n organizationId: dormantOrganizationId ?? \"\",\n metadataDocumentId: record.documentId,\n };\n}\n\nexport async function commitStoredHydratedContainer(\n execSql: Parameters<\n ContainerContentsPersistence[\"commitHydratedContainer\"]\n >[0],\n input: Parameters<ContainerContentsPersistence[\"commitHydratedContainer\"]>[1],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const commit = async (tx: ClientSQLiteTransactionScope) => {\n const existingContainers = await tx\n .select({ id: containers.id })\n .from(containers)\n .where(eq(containers.id, input.container.id))\n .limit(1);\n if (existingContainers.length > 0) return { committed: false as const };\n if (!(await placementCheckpointMatches(tx, input)))\n return { committed: false as const };\n\n const fences = await tx\n .select({\n generation: containerHydrationTombstones.generation,\n reason: containerHydrationTombstones.reason,\n updatedAt: containerHydrationTombstones.updatedAt,\n })\n .from(containerHydrationTombstones)\n .where(\n and(\n eq(containerHydrationTombstones.containerId, input.container.id),\n eq(containerHydrationTombstones.cleared, false),\n ),\n )\n .limit(1);\n const fence = fences[0];\n const currentDormantRecord = await selectContainerMetadataRecord(\n lockedExecSql,\n input.container.id,\n );\n if (\n fence &&\n !sameHydrationTombstone(fence, input.expectedHydrationTombstone)\n ) {\n return { committed: false as const };\n }\n if (\n !sameMetadataRecord(currentDormantRecord, input.expectedDormantRecord)\n ) {\n return { committed: false as const };\n }\n if (\n await isSupersededContainerBinding(lockedExecSql, {\n containerId: input.container.id,\n organizationId: input.container.organizationId,\n })\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"container was re-homed away from this organization\",\n );\n }\n // Retained metadata keeps its organization and target; a relisting that\n // names others rolls back rather than replacing retained private edits.\n const dormantOrganizationId = await loadDormantOrganizationId(\n lockedExecSql,\n input.container.id,\n );\n assertHeldContainerBinding(\n {\n organizationId: dormantOrganizationId ?? \"\",\n metadataDocumentId: currentDormantRecord?.documentId ?? null,\n },\n {\n organizationId: input.container.organizationId,\n metadataDocumentIds: [\n input.container.metadataDocumentId,\n input.record.documentId,\n ],\n },\n );\n\n const container = await saveRecoveredContainer(tx, input);\n await tx\n .update(containerHydrationTombstones)\n .set({ cleared: true })\n .where(eq(containerHydrationTombstones.containerId, input.container.id))\n .run();\n return { committed: true as const, container };\n };\n if (!input.stillCurrent) {\n return runtime.transaction(commit, { behavior: \"immediate\" });\n }\n const outcome = await runtime.guardedTransaction(\n commit,\n input.stillCurrent,\n { behavior: \"immediate\" },\n );\n return outcome.result ?? { committed: false as const };\n });\n}\n"]}
1
+ {"version":3,"file":"containerHydrationPersistence.js","sourceRoot":"","sources":["containerHydrationPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { and, eq, inArray, sql } from \"drizzle-orm\";\nimport { documentSyncPullContinuationsEqual } from \"../../documents/shared/sync/pullContinuation\";\nimport {\n containerHydrationTombstones,\n containers,\n dormantContainerMetadata,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport { loadStoredAccessManifestCheckpoint } from \"../keyingCheckpoints/keyingCheckpointPersistence\";\nimport {\n assertHeldContainerBinding,\n type HeldContainerBinding,\n heldContainerBinding,\n} from \"./containerBinding\";\nimport type {\n ContainerContentsPersistence,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n} from \"./containerContentsPersistenceTypes\";\nimport {\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport { loadContainerById } from \"./containerPersistence\";\nimport { isSupersededContainerBinding } from \"./supersededContainerBindings\";\n\nfunction sameNullableValue(\n left: string | null | undefined,\n right: string | null | undefined,\n): boolean {\n return (left ?? null) === (right ?? null);\n}\n\nfunction sameMetadataRecord(\n current: ContainerMetadataRecord | null,\n expected: ContainerMetadataRecord | null,\n): boolean {\n if (!current || !expected) return current === expected;\n return (\n current.id === expected.id &&\n current.documentId === expected.documentId &&\n current.accessEpoch === expected.accessEpoch &&\n sameNullableValue(current.accessStateHash, expected.accessStateHash) &&\n sameNullableValue(current.lastCommitLsn, expected.lastCommitLsn) &&\n sameNullableValue(current.contentKeyBundle, expected.contentKeyBundle) &&\n sameNullableValue(\n current.documentKekTargets,\n expected.documentKekTargets,\n ) &&\n sameNullableValue(\n current.documentManifestBundle,\n expected.documentManifestBundle,\n ) &&\n current.metadataUpdates === expected.metadataUpdates &&\n current.snapshotEndVersion === expected.snapshotEndVersion &&\n documentSyncPullContinuationsEqual(\n current.pullContinuation,\n expected.pullContinuation,\n ) &&\n Boolean(current.pullContinuationRecoveryRequired) ===\n Boolean(expected.pullContinuationRecoveryRequired)\n );\n}\n\nfunction sameHydrationTombstone(\n current:\n | { generation: number; reason: string; updatedAt: string }\n | undefined,\n expected: ContainerHydrationTombstone | null | undefined,\n): boolean {\n return (\n current !== undefined &&\n expected != null &&\n current.generation === expected.generation &&\n current.reason === expected.reason &&\n current.updatedAt === expected.updatedAt\n );\n}\n\nexport async function recordContainerHydrationTombstones(input: {\n removals: ReadonlyArray<{\n containerId: string;\n reason: \"access_revoked\" | \"deleted\";\n updatedAt: string;\n }>;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n if (input.removals.length === 0) return;\n await input.tx\n .insert(containerHydrationTombstones)\n .values([...input.removals])\n .onConflictDoUpdate({\n target: containerHydrationTombstones.containerId,\n set: {\n generation: sql`${containerHydrationTombstones.generation} + 1`,\n cleared: false,\n reason: sql`CASE\n WHEN ${containerHydrationTombstones.reason} = 'deleted' THEN 'deleted'\n ELSE excluded.reason\n END`,\n updatedAt: sql`MAX(${containerHydrationTombstones.updatedAt}, excluded.updated_at)`,\n },\n })\n .run();\n}\n\nasync function placementCheckpointMatches(\n tx: ClientSQLiteTransactionScope,\n input: Parameters<ContainerContentsPersistence[\"commitHydratedContainer\"]>[1],\n): Promise<boolean> {\n const expected = input.expectedPlacementCheckpoint;\n if (!expected) return true;\n const current = await loadStoredAccessManifestCheckpoint(tx, expected);\n return (\n expected.objectKind === \"container\" &&\n expected.objectId === input.container.id &&\n expected.organizationId === input.container.organizationId &&\n current?.epoch === expected.epoch &&\n current.manifestHash === expected.manifestHash\n );\n}\n\n/** A retained local move continues to own placement after verified reattachment. */\nasync function saveRecoveredContainer(\n tx: ClientSQLiteTransactionScope,\n input: Parameters<ContainerContentsPersistence[\"commitHydratedContainer\"]>[1],\n) {\n const [move] = await tx\n .select({\n parentContainerId: localIntents.targetContainerId,\n updatedAt: localIntents.updatedAt,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerMove),\n eq(localIntents.subjectId, input.container.id),\n inArray(localIntents.syncStatus, [\"pending\", \"blocked\"]),\n ),\n )\n .limit(1);\n return saveContainerContentsContainerRows({\n container: move\n ? { ...input.container, parentId: move.parentContainerId }\n : input.container,\n localUpdatedAt:\n move?.updatedAt ??\n input.saveOptions.localUpdatedAt ??\n input.remoteUpdatedAt,\n record: input.record,\n serverTimestamps: input.saveOptions.serverTimestamps,\n tx,\n });\n}\n\nasync function loadDormantOrganizationId(\n execSql: ExecSql,\n containerId: string,\n): Promise<string | null> {\n const [row] = await getClientSQLitePersistenceRuntime(execSql)\n .db.select({ organizationId: dormantContainerMetadata.organizationId })\n .from(dormantContainerMetadata)\n .where(eq(dormantContainerMetadata.containerId, containerId))\n .limit(1);\n return row?.organizationId ?? null;\n}\n\n/**\n * The binding this device holds for a container id: its live row, or the\n * organization and metadata target retained after access loss. Null when the\n * device has never held the container.\n */\nexport async function loadStoredHeldContainerBinding(\n execSql: ExecSql,\n containerId: string,\n): Promise<HeldContainerBinding | null> {\n const container = await loadContainerById(execSql, containerId);\n const record = await selectContainerMetadataRecord(execSql, containerId);\n if (container) return heldContainerBinding({ container, record });\n if (!record) return null;\n // Retention records the organization with each retained record; without one\n // the retained metadata target still pins the binding.\n const dormantOrganizationId = await loadDormantOrganizationId(\n execSql,\n containerId,\n );\n return {\n organizationId: dormantOrganizationId ?? \"\",\n metadataDocumentId: record.documentId,\n };\n}\n\n/**\n * Clears the tombstone the fence read found. That read covers the container's\n * only row that is not yet cleared (one row per container), so without one\n * there is nothing to clear.\n */\nasync function clearObservedHydrationFence(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n fence: object | undefined,\n): Promise<void> {\n if (!fence) return;\n await tx\n .update(containerHydrationTombstones)\n .set({ cleared: true })\n .where(eq(containerHydrationTombstones.containerId, containerId))\n .run();\n}\n\nexport async function commitStoredHydratedContainer(\n execSql: Parameters<\n ContainerContentsPersistence[\"commitHydratedContainer\"]\n >[0],\n input: Parameters<ContainerContentsPersistence[\"commitHydratedContainer\"]>[1],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const commit = async (tx: ClientSQLiteTransactionScope) => {\n const existingContainers = await tx\n .select({ id: containers.id })\n .from(containers)\n .where(eq(containers.id, input.container.id))\n .limit(1);\n if (existingContainers.length > 0) return { committed: false as const };\n if (!(await placementCheckpointMatches(tx, input)))\n return { committed: false as const };\n\n const fences = await tx\n .select({\n generation: containerHydrationTombstones.generation,\n reason: containerHydrationTombstones.reason,\n updatedAt: containerHydrationTombstones.updatedAt,\n })\n .from(containerHydrationTombstones)\n .where(\n and(\n eq(containerHydrationTombstones.containerId, input.container.id),\n eq(containerHydrationTombstones.cleared, false),\n ),\n )\n .limit(1);\n const fence = fences[0];\n const currentDormantRecord = await selectContainerMetadataRecord(\n lockedExecSql,\n input.container.id,\n );\n if (\n fence &&\n !sameHydrationTombstone(fence, input.expectedHydrationTombstone)\n ) {\n return { committed: false as const };\n }\n if (\n !sameMetadataRecord(currentDormantRecord, input.expectedDormantRecord)\n ) {\n return { committed: false as const };\n }\n if (\n await isSupersededContainerBinding(lockedExecSql, {\n containerId: input.container.id,\n organizationId: input.container.organizationId,\n })\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"container was re-homed away from this organization\",\n );\n }\n // Retained metadata keeps its organization and target; a relisting that\n // names others rolls back rather than replacing retained private edits.\n const dormantOrganizationId = await loadDormantOrganizationId(\n lockedExecSql,\n input.container.id,\n );\n assertHeldContainerBinding(\n {\n organizationId: dormantOrganizationId ?? \"\",\n metadataDocumentId: currentDormantRecord?.documentId ?? null,\n },\n {\n organizationId: input.container.organizationId,\n metadataDocumentIds: [\n input.container.metadataDocumentId,\n input.record.documentId,\n ],\n },\n );\n\n const container = await saveRecoveredContainer(tx, input);\n await clearObservedHydrationFence(tx, input.container.id, fence);\n return { committed: true as const, container };\n };\n if (!input.stillCurrent) {\n return runtime.transaction(commit, { behavior: \"immediate\" });\n }\n const outcome = await runtime.guardedTransaction(\n commit,\n input.stillCurrent,\n { behavior: \"immediate\" },\n );\n return outcome.result ?? { committed: false as const };\n });\n}\n"]}
@@ -129,8 +129,14 @@ async function prepareContainerMutationWrite(input) {
129
129
  const requestedLocalUpdatedAt = mutation.saveOptions?.localUpdatedAt ??
130
130
  mutation.saveOptions?.updatedAt ??
131
131
  new Date().toISOString();
132
+ // Nothing below writes local intents, so one read answers both checks.
133
+ let pendingStructuralIntent;
134
+ const hasStructuralIntent = async () => {
135
+ pendingStructuralIntent ??= await hasPendingStructuralIntent(tx, mutation.container.id);
136
+ return pendingStructuralIntent;
137
+ };
132
138
  const preserveDurableStructure = mutation.preserveDurableStructureWhenPending === true &&
133
- (await hasPendingStructuralIntent(tx, mutation.container.id));
139
+ (await hasStructuralIntent());
134
140
  // Syncing metadata is not a local edit. Preserve the durable edit clock,
135
141
  // including an edit another pane made while this request was in flight.
136
142
  const localUpdatedAt = preserveDurableStructure || mutation.settleAcceptedPendingOnConflict
@@ -141,7 +147,7 @@ async function prepareContainerMutationWrite(input) {
141
147
  : mutation.container;
142
148
  const didSettleOutgoing = mutation.acceptedPendingUpdateIds.length > 0 &&
143
149
  !(await hasPendingUpdates(tx, mutation.container.id)) &&
144
- !(await hasPendingStructuralIntent(tx, mutation.container.id));
150
+ !(await hasStructuralIntent());
145
151
  // Only container responses establish the server's structural clock. A
146
152
  // metadata acknowledgement supplies no such timestamp; marking it with our
147
153
  // wall clock would make later valid listings appear stale forever.
@@ -1 +1 @@
1
- {"version":3,"file":"containerMetadataMutationPersistence.js","sourceRoot":"","sources":["containerMetadataMutationPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, or } from \"drizzle-orm\";\nimport { documentSyncPullContinuationsEqual } from \"../../documents/shared/sync/pullContinuation\";\nimport {\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentPendingUpdate,\n type PendingUpdateFields,\n} from \"../documents/records\";\nimport {\n hasQueuedDocumentUpdates,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../documents/records/documentPendingUpdatePersistence\";\nimport { clearDocumentSyncFailureInTransaction } from \"../documents/records/documentSyncFailurePersistence\";\nimport { resetAccessDiscovery } from \"./containerAccessDiscovery\";\nimport {\n assertHeldContainerBinding,\n heldContainerBinding,\n} from \"./containerBinding\";\nimport type {\n ContainerContentsPersistence,\n ContainerMetadataRecord,\n StoredContainerState,\n} from \"./containerContentsPersistenceTypes\";\nimport { settleContainerCreateIntentRevision } from \"./containerCreateIntentSettlement\";\nimport {\n ContainerCreateIntentSupersededError,\n deleteContainerMoveIntentRevision,\n} from \"./containerIntentPersistence\";\nimport {\n getContainerMetadataScope,\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport {\n type ContainerRecord,\n loadContainerById,\n} from \"./containerPersistence\";\nimport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\n\nfunction sameNullableValue(\n left: string | null | undefined,\n right: string | null | undefined,\n): boolean {\n return (left ?? null) === (right ?? null);\n}\n\nfunction committedMetadataMutationResult(input: {\n container: ContainerRecord;\n createIntentSettled: boolean;\n moveIntentSettled: boolean;\n}) {\n return {\n committed: true as const,\n container: input.container,\n ...(input.createIntentSettled\n ? { createIntentSettled: true as const }\n : {}),\n ...(input.moveIntentSettled ? { moveIntentSettled: true as const } : {}),\n };\n}\n\nfunction sameMetadataSecurityIdentity(\n current: ContainerMetadataRecord,\n expected: ContainerMetadataRecord,\n): boolean {\n return (\n current.id === expected.id &&\n current.documentId === expected.documentId &&\n current.accessEpoch === expected.accessEpoch &&\n sameNullableValue(current.accessStateHash, expected.accessStateHash) &&\n sameNullableValue(current.contentKeyBundle, expected.contentKeyBundle) &&\n sameNullableValue(\n current.documentKekTargets,\n expected.documentKekTargets,\n ) &&\n sameNullableValue(\n current.documentManifestBundle,\n expected.documentManifestBundle,\n )\n );\n}\n\nfunction sameMetadataRecord(\n current: ContainerMetadataRecord,\n expected: ContainerMetadataRecord,\n): boolean {\n return (\n sameMetadataSecurityIdentity(current, expected) &&\n sameNullableValue(current.lastCommitLsn, expected.lastCommitLsn) &&\n current.metadataUpdates === expected.metadataUpdates &&\n current.snapshotEndVersion === expected.snapshotEndVersion &&\n documentSyncPullContinuationsEqual(\n current.pullContinuation,\n expected.pullContinuation,\n ) &&\n Boolean(current.pullContinuationRecoveryRequired) ===\n Boolean(expected.pullContinuationRecoveryRequired)\n );\n}\n\nfunction sameContainerRecord(\n current: ContainerRecord,\n expected: ContainerRecord,\n): boolean {\n return (\n current.id === expected.id &&\n current.parentId === expected.parentId &&\n current.organizationId === expected.organizationId &&\n current.metadataDocumentId === expected.metadataDocumentId &&\n (current.systemSlot ?? null) === (expected.systemSlot ?? null) &&\n (current.effectiveAccessLevel ?? null) ===\n (expected.effectiveAccessLevel ?? null) &&\n (current.localUpdatedAt ?? null) === (expected.localUpdatedAt ?? null) &&\n (current.serverCreatedAt ?? null) === (expected.serverCreatedAt ?? null) &&\n (current.serverUpdatedAt ?? null) === (expected.serverUpdatedAt ?? null) &&\n current.name === expected.name &&\n (current.icon ?? null) === (expected.icon ?? null)\n );\n}\n\nasync function loadStoredContainerState(\n execSql: Parameters<\n ContainerContentsPersistence[\"loadContainerMetadataState\"]\n >[0],\n containerId: string,\n): Promise<StoredContainerState | null> {\n const container = await loadContainerById(execSql, containerId);\n if (!container) return null;\n return {\n container,\n record: await selectContainerMetadataRecord(execSql, containerId),\n };\n}\n\nasync function deleteAcceptedPendingUpdates(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n ids: readonly string[],\n): Promise<void> {\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n getContainerMetadataScope(containerId),\n ids,\n );\n}\n\nfunction clearMetadataSyncFailure(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<void> {\n return clearDocumentSyncFailureInTransaction(tx, {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n });\n}\n\nasync function hasPendingUpdates(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<boolean> {\n return hasQueuedDocumentUpdates(tx, getContainerMetadataScope(containerId));\n}\n\nasync function hasPendingStructuralIntent(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<boolean> {\n // A pending create or any surviving (unsynced) move of this container.\n const structural = await tx\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(\n and(\n eq(localIntents.subjectId, containerId),\n or(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate),\n eq(localIntents.syncStatus, \"pending\"),\n ),\n inLocalIntentLane(LOCAL_INTENT_LANES.containerMove),\n ),\n ),\n )\n .limit(1);\n return structural.length > 0;\n}\n\nasync function enqueuePendingUpdate(\n execSql: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[0],\n containerId: string,\n pendingUpdate: PendingUpdateFields | undefined,\n): Promise<void> {\n if (!pendingUpdate) return;\n await enqueueDocumentPendingUpdate(\n execSql,\n getContainerMetadataScope(containerId),\n pendingUpdate,\n );\n}\n\ntype CommitMetadataMutationInput = Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n>[1];\n\nfunction staleServerMutationResult(\n currentState: StoredContainerState | null,\n input: CommitMetadataMutationInput,\n) {\n const incomingServerUpdatedAt =\n input.saveOptions?.serverTimestamps?.updatedAt;\n const hasOlderContainerTimestamp =\n incomingServerUpdatedAt != null &&\n currentState?.container.serverUpdatedAt != null &&\n currentState.container.serverUpdatedAt > incomingServerUpdatedAt;\n const hasOlderMetadataAccessEpoch =\n currentState?.record != null &&\n currentState.record.documentId === input.record.documentId &&\n currentState.record.accessEpoch > input.record.accessEpoch;\n return currentState?.record &&\n (hasOlderContainerTimestamp || hasOlderMetadataAccessEpoch)\n ? {\n committed: false as const,\n currentState,\n staleServerState: true as const,\n }\n : null;\n}\n\nfunction metadataMutationStateMatches(\n currentState: StoredContainerState | null,\n input: CommitMetadataMutationInput,\n): currentState is StoredContainerState & {\n record: ContainerMetadataRecord;\n} {\n return Boolean(\n currentState?.record &&\n sameContainerRecord(currentState.container, input.expectedContainer) &&\n sameMetadataRecord(currentState.record, input.expectedRecord),\n );\n}\n\nasync function rejectMetadataMutationConflict(input: {\n currentState: StoredContainerState | null;\n mutation: CommitMetadataMutationInput;\n tx: ClientSQLiteTransactionScope;\n}) {\n const { currentState, mutation, tx } = input;\n if (\n currentState?.record &&\n mutation.settleAcceptedPendingOnConflict &&\n sameMetadataSecurityIdentity(currentState.record, mutation.expectedRecord)\n ) {\n await deleteAcceptedPendingUpdates(\n tx,\n mutation.container.id,\n mutation.acceptedPendingUpdateIds,\n );\n }\n return { committed: false as const, currentState };\n}\n\nasync function prepareContainerMutationWrite(input: {\n currentContainer: ContainerRecord;\n mutation: CommitMetadataMutationInput;\n tx: ClientSQLiteTransactionScope;\n}): Promise<{ container: ContainerRecord; localUpdatedAt: string }> {\n const { currentContainer, mutation, tx } = input;\n const requestedLocalUpdatedAt =\n mutation.saveOptions?.localUpdatedAt ??\n mutation.saveOptions?.updatedAt ??\n new Date().toISOString();\n const preserveDurableStructure =\n mutation.preserveDurableStructureWhenPending === true &&\n (await hasPendingStructuralIntent(tx, mutation.container.id));\n // Syncing metadata is not a local edit. Preserve the durable edit clock,\n // including an edit another pane made while this request was in flight.\n const localUpdatedAt =\n preserveDurableStructure || mutation.settleAcceptedPendingOnConflict\n ? (currentContainer.localUpdatedAt ?? requestedLocalUpdatedAt)\n : requestedLocalUpdatedAt;\n const structurallyRebasedContainer = preserveDurableStructure\n ? { ...mutation.container, parentId: currentContainer.parentId }\n : mutation.container;\n const didSettleOutgoing =\n mutation.acceptedPendingUpdateIds.length > 0 &&\n !(await hasPendingUpdates(tx, mutation.container.id)) &&\n !(await hasPendingStructuralIntent(tx, mutation.container.id));\n // Only container responses establish the server's structural clock. A\n // metadata acknowledgement supplies no such timestamp; marking it with our\n // wall clock would make later valid listings appear stale forever.\n return {\n container: structurallyRebasedContainer,\n localUpdatedAt: didSettleOutgoing\n ? (currentContainer.serverUpdatedAt ?? localUpdatedAt)\n : localUpdatedAt,\n };\n}\n\n/** Server acknowledgements and listings cannot rebind a held folder. */\nfunction assertMutationKeepsHeldBinding(\n currentState: StoredContainerState & { record: ContainerMetadataRecord },\n input: CommitMetadataMutationInput,\n): void {\n const recordDocumentIds =\n input.record.documentId === currentState.record.documentId\n ? []\n : [input.record.documentId];\n assertHeldContainerBinding(heldContainerBinding(currentState), {\n organizationId: input.container.organizationId,\n metadataDocumentIds: [\n input.container.metadataDocumentId,\n ...recordDocumentIds,\n ],\n });\n}\n\nasync function settleMetadataStructuralIntents(\n tx: ClientSQLiteTransactionScope,\n input: CommitMetadataMutationInput,\n): Promise<void> {\n if (\n input.createIntentSettlement &&\n (await settleContainerCreateIntentRevision({\n ...input.createIntentSettlement,\n tx,\n })) === \"superseded\"\n ) {\n throw new ContainerCreateIntentSupersededError();\n }\n if (input.moveIntentSettlement) {\n if (\n !(await deleteContainerMoveIntentRevision({\n ...input.moveIntentSettlement,\n tx,\n }))\n ) {\n throw new Error(\n \"Container move intent was superseded before local settlement\",\n );\n }\n }\n}\n\nexport async function commitStoredMetadataMutation(\n execSql: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[0],\n input: Parameters<ContainerContentsPersistence[\"commitMetadataMutation\"]>[1],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const outcome = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async (tx) => {\n const currentState = await loadStoredContainerState(\n lockedExecSql,\n input.container.id,\n );\n const staleServerResult = staleServerMutationResult(\n currentState,\n input,\n );\n if (staleServerResult) return staleServerResult;\n if (!metadataMutationStateMatches(currentState, input)) {\n return rejectMetadataMutationConflict({\n currentState,\n mutation: input,\n tx,\n });\n }\n assertMutationKeepsHeldBinding(currentState, input);\n\n await enqueuePendingUpdate(\n lockedExecSql,\n input.container.id,\n input.pendingUpdate,\n );\n await deleteAcceptedPendingUpdates(\n tx,\n input.container.id,\n input.acceptedPendingUpdateIds,\n );\n if (input.clearSyncFailure) {\n await clearMetadataSyncFailure(tx, input.container.id);\n }\n const { container, localUpdatedAt } =\n await prepareContainerMutationWrite({\n currentContainer: currentState.container,\n mutation: input,\n tx,\n });\n const saved = await saveContainerContentsContainerRows({\n container,\n createIntent: input.saveOptions?.createIntent,\n localUpdatedAt,\n moveIntent: input.saveOptions?.moveIntent,\n record: input.record,\n serverTimestamps: input.saveOptions?.serverTimestamps,\n tx,\n });\n await resetAccessDiscovery(tx, currentState.container, saved);\n await settleMetadataStructuralIntents(tx, input);\n return committedMetadataMutationResult({\n container: saved,\n createIntentSettled: input.createIntentSettlement !== undefined,\n moveIntentSettled: input.moveIntentSettlement !== undefined,\n });\n },\n () => !input.stillCurrent || input.stillCurrent(),\n { behavior: \"immediate\" },\n );\n if (outcome.committed && outcome.result) {\n return outcome.result;\n }\n return {\n committed: false as const,\n currentState: await loadStoredContainerState(\n lockedExecSql,\n input.container.id,\n ),\n };\n });\n}\n\nexport async function settleStoredMetadataPendingUpdates(\n execSql: Parameters<\n ContainerContentsPersistence[\"settleAcceptedMetadataPendingUpdates\"]\n >[0],\n input: Parameters<\n ContainerContentsPersistence[\"settleAcceptedMetadataPendingUpdates\"]\n >[1],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const outcome = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async (tx) => {\n const currentState = await loadStoredContainerState(\n lockedExecSql,\n input.containerId,\n );\n if (\n currentState?.record &&\n sameMetadataSecurityIdentity(\n currentState.record,\n input.expectedRecord,\n )\n ) {\n await deleteAcceptedPendingUpdates(\n tx,\n input.containerId,\n input.pendingUpdateIds,\n );\n }\n return currentState;\n },\n () => !input.stillCurrent || input.stillCurrent(),\n { behavior: \"immediate\" },\n );\n return outcome.committed ? outcome.result : null;\n });\n}\n"]}
1
+ {"version":3,"file":"containerMetadataMutationPersistence.js","sourceRoot":"","sources":["containerMetadataMutationPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,kCAAkC,EAAE,MAAM,8CAA8C,CAAC;AAClG,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,GACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,iCAAiC,GAClC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EACL,4BAA4B,GAE7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,wBAAwB,EACxB,wCAAwC,GACzC,MAAM,uDAAuD,CAAC;AAC/D,OAAO,EAAE,qCAAqC,EAAE,MAAM,qDAAqD,CAAC;AAC5G,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAM5B,OAAO,EAAE,mCAAmC,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EACL,oCAAoC,EACpC,iCAAiC,GAClC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,yBAAyB,EACzB,kCAAkC,EAClC,6BAA6B,GAC9B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEL,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAEzE,SAAS,iBAAiB,CACxB,IAA+B,EAC/B,KAAgC;IAEhC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,+BAA+B,CAAC,KAIxC;IACC,OAAO;QACL,SAAS,EAAE,IAAa;QACxB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,GAAG,CAAC,KAAK,CAAC,mBAAmB;YAC3B,CAAC,CAAC,EAAE,mBAAmB,EAAE,IAAa,EAAE;YACxC,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzE,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B,CACnC,OAAgC,EAChC,QAAiC;IAEjC,OAAO,CACL,OAAO,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE;QAC1B,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;QAC1C,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW;QAC5C,iBAAiB,CAAC,OAAO,CAAC,eAAe,EAAE,QAAQ,CAAC,eAAe,CAAC;QACpE,iBAAiB,CAAC,OAAO,CAAC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,CAAC;QACtE,iBAAiB,CACf,OAAO,CAAC,kBAAkB,EAC1B,QAAQ,CAAC,kBAAkB,CAC5B;QACD,iBAAiB,CACf,OAAO,CAAC,sBAAsB,EAC9B,QAAQ,CAAC,sBAAsB,CAChC,CACF,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CACzB,OAAgC,EAChC,QAAiC;IAEjC,OAAO,CACL,4BAA4B,CAAC,OAAO,EAAE,QAAQ,CAAC;QAC/C,iBAAiB,CAAC,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC,aAAa,CAAC;QAChE,OAAO,CAAC,eAAe,KAAK,QAAQ,CAAC,eAAe;QACpD,OAAO,CAAC,kBAAkB,KAAK,QAAQ,CAAC,kBAAkB;QAC1D,kCAAkC,CAChC,OAAO,CAAC,gBAAgB,EACxB,QAAQ,CAAC,gBAAgB,CAC1B;QACD,OAAO,CAAC,OAAO,CAAC,gCAAgC,CAAC;YAC/C,OAAO,CAAC,QAAQ,CAAC,gCAAgC,CAAC,CACrD,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAC1B,OAAwB,EACxB,QAAyB;IAEzB,OAAO,CACL,OAAO,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE;QAC1B,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ;QACtC,OAAO,CAAC,cAAc,KAAK,QAAQ,CAAC,cAAc;QAClD,OAAO,CAAC,kBAAkB,KAAK,QAAQ,CAAC,kBAAkB;QAC1D,CAAC,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,IAAI,IAAI,CAAC;QAC9D,CAAC,OAAO,CAAC,oBAAoB,IAAI,IAAI,CAAC;YACpC,CAAC,QAAQ,CAAC,oBAAoB,IAAI,IAAI,CAAC;QACzC,CAAC,OAAO,CAAC,cAAc,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,cAAc,IAAI,IAAI,CAAC;QACtE,CAAC,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,IAAI,IAAI,CAAC;QACxE,CAAC,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,IAAI,IAAI,CAAC;QACxE,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI;QAC9B,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,CACnD,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,wBAAwB,CACrC,OAEI,EACJ,WAAmB;IAEnB,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAChE,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,OAAO;QACL,SAAS;QACT,MAAM,EAAE,MAAM,6BAA6B,CAAC,OAAO,EAAE,WAAW,CAAC;KAClE,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,4BAA4B,CACzC,EAAgC,EAChC,WAAmB,EACnB,GAAsB;IAEtB,MAAM,wCAAwC,CAC5C,EAAE,EACF,yBAAyB,CAAC,WAAW,CAAC,EACtC,GAAG,CACJ,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAC/B,EAAgC,EAChC,WAAmB;IAEnB,OAAO,qCAAqC,CAAC,EAAE,EAAE;QAC/C,OAAO,EAAE,2BAA2B;QACpC,OAAO,EAAE,WAAW;KACrB,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC9B,EAAgC,EAChC,WAAmB;IAEnB,OAAO,wBAAwB,CAAC,EAAE,EAAE,yBAAyB,CAAC,WAAW,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED,KAAK,UAAU,0BAA0B,CACvC,EAAgC,EAChC,WAAmB;IAEnB,uEAAuE;IACvE,MAAM,UAAU,GAAG,MAAM,EAAE;SACxB,MAAM,CAAC,EAAE,EAAE,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC;SAC/B,IAAI,CAAC,YAAY,CAAC;SAClB,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,WAAW,CAAC,EACvC,EAAE,CACA,GAAG,CACD,iBAAiB,CAAC,kBAAkB,CAAC,eAAe,CAAC,EACrD,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,CACvC,EACD,iBAAiB,CAAC,kBAAkB,CAAC,aAAa,CAAC,CACpD,CACF,CACF;SACA,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,OAEI,EACJ,WAAmB,EACnB,aAA8C;IAE9C,IAAI,CAAC,aAAa;QAAE,OAAO;IAC3B,MAAM,4BAA4B,CAChC,OAAO,EACP,yBAAyB,CAAC,WAAW,CAAC,EACtC,aAAa,CACd,CAAC;AACJ,CAAC;AAMD,SAAS,yBAAyB,CAChC,YAAyC,EACzC,KAAkC;IAElC,MAAM,uBAAuB,GAC3B,KAAK,CAAC,WAAW,EAAE,gBAAgB,EAAE,SAAS,CAAC;IACjD,MAAM,0BAA0B,GAC9B,uBAAuB,IAAI,IAAI;QAC/B,YAAY,EAAE,SAAS,CAAC,eAAe,IAAI,IAAI;QAC/C,YAAY,CAAC,SAAS,CAAC,eAAe,GAAG,uBAAuB,CAAC;IACnE,MAAM,2BAA2B,GAC/B,YAAY,EAAE,MAAM,IAAI,IAAI;QAC5B,YAAY,CAAC,MAAM,CAAC,UAAU,KAAK,KAAK,CAAC,MAAM,CAAC,UAAU;QAC1D,YAAY,CAAC,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC;IAC7D,OAAO,YAAY,EAAE,MAAM;QACzB,CAAC,0BAA0B,IAAI,2BAA2B,CAAC;QAC3D,CAAC,CAAC;YACE,SAAS,EAAE,KAAc;YACzB,YAAY;YACZ,gBAAgB,EAAE,IAAa;SAChC;QACH,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,SAAS,4BAA4B,CACnC,YAAyC,EACzC,KAAkC;IAIlC,OAAO,OAAO,CACZ,YAAY,EAAE,MAAM;QAClB,mBAAmB,CAAC,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,iBAAiB,CAAC;QACpE,kBAAkB,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,CAChE,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,8BAA8B,CAAC,KAI7C;IACC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;IAC7C,IACE,YAAY,EAAE,MAAM;QACpB,QAAQ,CAAC,+BAA+B;QACxC,4BAA4B,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC,EAC1E,CAAC;QACD,MAAM,4BAA4B,CAChC,EAAE,EACF,QAAQ,CAAC,SAAS,CAAC,EAAE,EACrB,QAAQ,CAAC,wBAAwB,CAClC,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,KAAc,EAAE,YAAY,EAAE,CAAC;AACrD,CAAC;AAED,KAAK,UAAU,6BAA6B,CAAC,KAI5C;IACC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;IACjD,MAAM,uBAAuB,GAC3B,QAAQ,CAAC,WAAW,EAAE,cAAc;QACpC,QAAQ,CAAC,WAAW,EAAE,SAAS;QAC/B,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3B,uEAAuE;IACvE,IAAI,uBAA4C,CAAC;IACjD,MAAM,mBAAmB,GAAG,KAAK,IAAI,EAAE;QACrC,uBAAuB,KAAK,MAAM,0BAA0B,CAC1D,EAAE,EACF,QAAQ,CAAC,SAAS,CAAC,EAAE,CACtB,CAAC;QACF,OAAO,uBAAuB,CAAC;IACjC,CAAC,CAAC;IACF,MAAM,wBAAwB,GAC5B,QAAQ,CAAC,mCAAmC,KAAK,IAAI;QACrD,CAAC,MAAM,mBAAmB,EAAE,CAAC,CAAC;IAChC,yEAAyE;IACzE,wEAAwE;IACxE,MAAM,cAAc,GAClB,wBAAwB,IAAI,QAAQ,CAAC,+BAA+B;QAClE,CAAC,CAAC,CAAC,gBAAgB,CAAC,cAAc,IAAI,uBAAuB,CAAC;QAC9D,CAAC,CAAC,uBAAuB,CAAC;IAC9B,MAAM,4BAA4B,GAAG,wBAAwB;QAC3D,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,SAAS,EAAE,QAAQ,EAAE,gBAAgB,CAAC,QAAQ,EAAE;QAChE,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;IACvB,MAAM,iBAAiB,GACrB,QAAQ,CAAC,wBAAwB,CAAC,MAAM,GAAG,CAAC;QAC5C,CAAC,CAAC,MAAM,iBAAiB,CAAC,EAAE,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACrD,CAAC,CAAC,MAAM,mBAAmB,EAAE,CAAC,CAAC;IACjC,sEAAsE;IACtE,2EAA2E;IAC3E,mEAAmE;IACnE,OAAO;QACL,SAAS,EAAE,4BAA4B;QACvC,cAAc,EAAE,iBAAiB;YAC/B,CAAC,CAAC,CAAC,gBAAgB,CAAC,eAAe,IAAI,cAAc,CAAC;YACtD,CAAC,CAAC,cAAc;KACnB,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,SAAS,8BAA8B,CACrC,YAAwE,EACxE,KAAkC;IAElC,MAAM,iBAAiB,GACrB,KAAK,CAAC,MAAM,CAAC,UAAU,KAAK,YAAY,CAAC,MAAM,CAAC,UAAU;QACxD,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAChC,0BAA0B,CAAC,oBAAoB,CAAC,YAAY,CAAC,EAAE;QAC7D,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc;QAC9C,mBAAmB,EAAE;YACnB,KAAK,CAAC,SAAS,CAAC,kBAAkB;YAClC,GAAG,iBAAiB;SACrB;KACF,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,+BAA+B,CAC5C,EAAgC,EAChC,KAAkC;IAElC,IACE,KAAK,CAAC,sBAAsB;QAC5B,CAAC,MAAM,mCAAmC,CAAC;YACzC,GAAG,KAAK,CAAC,sBAAsB;YAC/B,EAAE;SACH,CAAC,CAAC,KAAK,YAAY,EACpB,CAAC;QACD,MAAM,IAAI,oCAAoC,EAAE,CAAC;IACnD,CAAC;IACD,IAAI,KAAK,CAAC,oBAAoB,EAAE,CAAC;QAC/B,IACE,CAAC,CAAC,MAAM,iCAAiC,CAAC;YACxC,GAAG,KAAK,CAAC,oBAAoB;YAC7B,EAAE;SACH,CAAC,CAAC,EACH,CAAC;YACD,MAAM,IAAI,KAAK,CACb,8DAA8D,CAC/D,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,OAEI,EACJ,KAA4E;IAE5E,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;QAC/D,MAAM,OAAO,GAAG,MAAM,iCAAiC,CACrD,aAAa,CACd,CAAC,kBAAkB,CAClB,KAAK,EAAE,EAAE,EAAE,EAAE;YACX,MAAM,YAAY,GAAG,MAAM,wBAAwB,CACjD,aAAa,EACb,KAAK,CAAC,SAAS,CAAC,EAAE,CACnB,CAAC;YACF,MAAM,iBAAiB,GAAG,yBAAyB,CACjD,YAAY,EACZ,KAAK,CACN,CAAC;YACF,IAAI,iBAAiB;gBAAE,OAAO,iBAAiB,CAAC;YAChD,IAAI,CAAC,4BAA4B,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC;gBACvD,OAAO,8BAA8B,CAAC;oBACpC,YAAY;oBACZ,QAAQ,EAAE,KAAK;oBACf,EAAE;iBACH,CAAC,CAAC;YACL,CAAC;YACD,8BAA8B,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;YAEpD,MAAM,oBAAoB,CACxB,aAAa,EACb,KAAK,CAAC,SAAS,CAAC,EAAE,EAClB,KAAK,CAAC,aAAa,CACpB,CAAC;YACF,MAAM,4BAA4B,CAChC,EAAE,EACF,KAAK,CAAC,SAAS,CAAC,EAAE,EAClB,KAAK,CAAC,wBAAwB,CAC/B,CAAC;YACF,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC;gBAC3B,MAAM,wBAAwB,CAAC,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,GACjC,MAAM,6BAA6B,CAAC;gBAClC,gBAAgB,EAAE,YAAY,CAAC,SAAS;gBACxC,QAAQ,EAAE,KAAK;gBACf,EAAE;aACH,CAAC,CAAC;YACL,MAAM,KAAK,GAAG,MAAM,kCAAkC,CAAC;gBACrD,SAAS;gBACT,YAAY,EAAE,KAAK,CAAC,WAAW,EAAE,YAAY;gBAC7C,cAAc;gBACd,UAAU,EAAE,KAAK,CAAC,WAAW,EAAE,UAAU;gBACzC,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,gBAAgB,EAAE,KAAK,CAAC,WAAW,EAAE,gBAAgB;gBACrD,EAAE;aACH,CAAC,CAAC;YACH,MAAM,oBAAoB,CAAC,EAAE,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YAC9D,MAAM,+BAA+B,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YACjD,OAAO,+BAA+B,CAAC;gBACrC,SAAS,EAAE,KAAK;gBAChB,mBAAmB,EAAE,KAAK,CAAC,sBAAsB,KAAK,SAAS;gBAC/D,iBAAiB,EAAE,KAAK,CAAC,oBAAoB,KAAK,SAAS;aAC5D,CAAC,CAAC;QACL,CAAC,EACD,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,YAAY,EAAE,EACjD,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CAAC;QACF,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACxC,OAAO,OAAO,CAAC,MAAM,CAAC;QACxB,CAAC;QACD,OAAO;YACL,SAAS,EAAE,KAAc;YACzB,YAAY,EAAE,MAAM,wBAAwB,CAC1C,aAAa,EACb,KAAK,CAAC,SAAS,CAAC,EAAE,CACnB;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kCAAkC,CACtD,OAEI,EACJ,KAEI;IAEJ,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;QAC/D,MAAM,OAAO,GAAG,MAAM,iCAAiC,CACrD,aAAa,CACd,CAAC,kBAAkB,CAClB,KAAK,EAAE,EAAE,EAAE,EAAE;YACX,MAAM,YAAY,GAAG,MAAM,wBAAwB,CACjD,aAAa,EACb,KAAK,CAAC,WAAW,CAClB,CAAC;YACF,IACE,YAAY,EAAE,MAAM;gBACpB,4BAA4B,CAC1B,YAAY,CAAC,MAAM,EACnB,KAAK,CAAC,cAAc,CACrB,EACD,CAAC;gBACD,MAAM,4BAA4B,CAChC,EAAE,EACF,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,gBAAgB,CACvB,CAAC;YACJ,CAAC;YACD,OAAO,YAAY,CAAC;QACtB,CAAC,EACD,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,YAAY,EAAE,EACjD,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CAAC;QACF,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { and, eq, or } from \"drizzle-orm\";\nimport { documentSyncPullContinuationsEqual } from \"../../documents/shared/sync/pullContinuation\";\nimport {\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentPendingUpdate,\n type PendingUpdateFields,\n} from \"../documents/records\";\nimport {\n hasQueuedDocumentUpdates,\n settleQueuedDocumentUpdatesInTransaction,\n} from \"../documents/records/documentPendingUpdatePersistence\";\nimport { clearDocumentSyncFailureInTransaction } from \"../documents/records/documentSyncFailurePersistence\";\nimport { resetAccessDiscovery } from \"./containerAccessDiscovery\";\nimport {\n assertHeldContainerBinding,\n heldContainerBinding,\n} from \"./containerBinding\";\nimport type {\n ContainerContentsPersistence,\n ContainerMetadataRecord,\n StoredContainerState,\n} from \"./containerContentsPersistenceTypes\";\nimport { settleContainerCreateIntentRevision } from \"./containerCreateIntentSettlement\";\nimport {\n ContainerCreateIntentSupersededError,\n deleteContainerMoveIntentRevision,\n} from \"./containerIntentPersistence\";\nimport {\n getContainerMetadataScope,\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\nimport {\n type ContainerRecord,\n loadContainerById,\n} from \"./containerPersistence\";\nimport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\n\nfunction sameNullableValue(\n left: string | null | undefined,\n right: string | null | undefined,\n): boolean {\n return (left ?? null) === (right ?? null);\n}\n\nfunction committedMetadataMutationResult(input: {\n container: ContainerRecord;\n createIntentSettled: boolean;\n moveIntentSettled: boolean;\n}) {\n return {\n committed: true as const,\n container: input.container,\n ...(input.createIntentSettled\n ? { createIntentSettled: true as const }\n : {}),\n ...(input.moveIntentSettled ? { moveIntentSettled: true as const } : {}),\n };\n}\n\nfunction sameMetadataSecurityIdentity(\n current: ContainerMetadataRecord,\n expected: ContainerMetadataRecord,\n): boolean {\n return (\n current.id === expected.id &&\n current.documentId === expected.documentId &&\n current.accessEpoch === expected.accessEpoch &&\n sameNullableValue(current.accessStateHash, expected.accessStateHash) &&\n sameNullableValue(current.contentKeyBundle, expected.contentKeyBundle) &&\n sameNullableValue(\n current.documentKekTargets,\n expected.documentKekTargets,\n ) &&\n sameNullableValue(\n current.documentManifestBundle,\n expected.documentManifestBundle,\n )\n );\n}\n\nfunction sameMetadataRecord(\n current: ContainerMetadataRecord,\n expected: ContainerMetadataRecord,\n): boolean {\n return (\n sameMetadataSecurityIdentity(current, expected) &&\n sameNullableValue(current.lastCommitLsn, expected.lastCommitLsn) &&\n current.metadataUpdates === expected.metadataUpdates &&\n current.snapshotEndVersion === expected.snapshotEndVersion &&\n documentSyncPullContinuationsEqual(\n current.pullContinuation,\n expected.pullContinuation,\n ) &&\n Boolean(current.pullContinuationRecoveryRequired) ===\n Boolean(expected.pullContinuationRecoveryRequired)\n );\n}\n\nfunction sameContainerRecord(\n current: ContainerRecord,\n expected: ContainerRecord,\n): boolean {\n return (\n current.id === expected.id &&\n current.parentId === expected.parentId &&\n current.organizationId === expected.organizationId &&\n current.metadataDocumentId === expected.metadataDocumentId &&\n (current.systemSlot ?? null) === (expected.systemSlot ?? null) &&\n (current.effectiveAccessLevel ?? null) ===\n (expected.effectiveAccessLevel ?? null) &&\n (current.localUpdatedAt ?? null) === (expected.localUpdatedAt ?? null) &&\n (current.serverCreatedAt ?? null) === (expected.serverCreatedAt ?? null) &&\n (current.serverUpdatedAt ?? null) === (expected.serverUpdatedAt ?? null) &&\n current.name === expected.name &&\n (current.icon ?? null) === (expected.icon ?? null)\n );\n}\n\nasync function loadStoredContainerState(\n execSql: Parameters<\n ContainerContentsPersistence[\"loadContainerMetadataState\"]\n >[0],\n containerId: string,\n): Promise<StoredContainerState | null> {\n const container = await loadContainerById(execSql, containerId);\n if (!container) return null;\n return {\n container,\n record: await selectContainerMetadataRecord(execSql, containerId),\n };\n}\n\nasync function deleteAcceptedPendingUpdates(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n ids: readonly string[],\n): Promise<void> {\n await settleQueuedDocumentUpdatesInTransaction(\n tx,\n getContainerMetadataScope(containerId),\n ids,\n );\n}\n\nfunction clearMetadataSyncFailure(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<void> {\n return clearDocumentSyncFailureInTransaction(tx, {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n });\n}\n\nasync function hasPendingUpdates(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<boolean> {\n return hasQueuedDocumentUpdates(tx, getContainerMetadataScope(containerId));\n}\n\nasync function hasPendingStructuralIntent(\n tx: ClientSQLiteTransactionScope,\n containerId: string,\n): Promise<boolean> {\n // A pending create or any surviving (unsynced) move of this container.\n const structural = await tx\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(\n and(\n eq(localIntents.subjectId, containerId),\n or(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate),\n eq(localIntents.syncStatus, \"pending\"),\n ),\n inLocalIntentLane(LOCAL_INTENT_LANES.containerMove),\n ),\n ),\n )\n .limit(1);\n return structural.length > 0;\n}\n\nasync function enqueuePendingUpdate(\n execSql: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[0],\n containerId: string,\n pendingUpdate: PendingUpdateFields | undefined,\n): Promise<void> {\n if (!pendingUpdate) return;\n await enqueueDocumentPendingUpdate(\n execSql,\n getContainerMetadataScope(containerId),\n pendingUpdate,\n );\n}\n\ntype CommitMetadataMutationInput = Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n>[1];\n\nfunction staleServerMutationResult(\n currentState: StoredContainerState | null,\n input: CommitMetadataMutationInput,\n) {\n const incomingServerUpdatedAt =\n input.saveOptions?.serverTimestamps?.updatedAt;\n const hasOlderContainerTimestamp =\n incomingServerUpdatedAt != null &&\n currentState?.container.serverUpdatedAt != null &&\n currentState.container.serverUpdatedAt > incomingServerUpdatedAt;\n const hasOlderMetadataAccessEpoch =\n currentState?.record != null &&\n currentState.record.documentId === input.record.documentId &&\n currentState.record.accessEpoch > input.record.accessEpoch;\n return currentState?.record &&\n (hasOlderContainerTimestamp || hasOlderMetadataAccessEpoch)\n ? {\n committed: false as const,\n currentState,\n staleServerState: true as const,\n }\n : null;\n}\n\nfunction metadataMutationStateMatches(\n currentState: StoredContainerState | null,\n input: CommitMetadataMutationInput,\n): currentState is StoredContainerState & {\n record: ContainerMetadataRecord;\n} {\n return Boolean(\n currentState?.record &&\n sameContainerRecord(currentState.container, input.expectedContainer) &&\n sameMetadataRecord(currentState.record, input.expectedRecord),\n );\n}\n\nasync function rejectMetadataMutationConflict(input: {\n currentState: StoredContainerState | null;\n mutation: CommitMetadataMutationInput;\n tx: ClientSQLiteTransactionScope;\n}) {\n const { currentState, mutation, tx } = input;\n if (\n currentState?.record &&\n mutation.settleAcceptedPendingOnConflict &&\n sameMetadataSecurityIdentity(currentState.record, mutation.expectedRecord)\n ) {\n await deleteAcceptedPendingUpdates(\n tx,\n mutation.container.id,\n mutation.acceptedPendingUpdateIds,\n );\n }\n return { committed: false as const, currentState };\n}\n\nasync function prepareContainerMutationWrite(input: {\n currentContainer: ContainerRecord;\n mutation: CommitMetadataMutationInput;\n tx: ClientSQLiteTransactionScope;\n}): Promise<{ container: ContainerRecord; localUpdatedAt: string }> {\n const { currentContainer, mutation, tx } = input;\n const requestedLocalUpdatedAt =\n mutation.saveOptions?.localUpdatedAt ??\n mutation.saveOptions?.updatedAt ??\n new Date().toISOString();\n // Nothing below writes local intents, so one read answers both checks.\n let pendingStructuralIntent: boolean | undefined;\n const hasStructuralIntent = async () => {\n pendingStructuralIntent ??= await hasPendingStructuralIntent(\n tx,\n mutation.container.id,\n );\n return pendingStructuralIntent;\n };\n const preserveDurableStructure =\n mutation.preserveDurableStructureWhenPending === true &&\n (await hasStructuralIntent());\n // Syncing metadata is not a local edit. Preserve the durable edit clock,\n // including an edit another pane made while this request was in flight.\n const localUpdatedAt =\n preserveDurableStructure || mutation.settleAcceptedPendingOnConflict\n ? (currentContainer.localUpdatedAt ?? requestedLocalUpdatedAt)\n : requestedLocalUpdatedAt;\n const structurallyRebasedContainer = preserveDurableStructure\n ? { ...mutation.container, parentId: currentContainer.parentId }\n : mutation.container;\n const didSettleOutgoing =\n mutation.acceptedPendingUpdateIds.length > 0 &&\n !(await hasPendingUpdates(tx, mutation.container.id)) &&\n !(await hasStructuralIntent());\n // Only container responses establish the server's structural clock. A\n // metadata acknowledgement supplies no such timestamp; marking it with our\n // wall clock would make later valid listings appear stale forever.\n return {\n container: structurallyRebasedContainer,\n localUpdatedAt: didSettleOutgoing\n ? (currentContainer.serverUpdatedAt ?? localUpdatedAt)\n : localUpdatedAt,\n };\n}\n\n/** Server acknowledgements and listings cannot rebind a held folder. */\nfunction assertMutationKeepsHeldBinding(\n currentState: StoredContainerState & { record: ContainerMetadataRecord },\n input: CommitMetadataMutationInput,\n): void {\n const recordDocumentIds =\n input.record.documentId === currentState.record.documentId\n ? []\n : [input.record.documentId];\n assertHeldContainerBinding(heldContainerBinding(currentState), {\n organizationId: input.container.organizationId,\n metadataDocumentIds: [\n input.container.metadataDocumentId,\n ...recordDocumentIds,\n ],\n });\n}\n\nasync function settleMetadataStructuralIntents(\n tx: ClientSQLiteTransactionScope,\n input: CommitMetadataMutationInput,\n): Promise<void> {\n if (\n input.createIntentSettlement &&\n (await settleContainerCreateIntentRevision({\n ...input.createIntentSettlement,\n tx,\n })) === \"superseded\"\n ) {\n throw new ContainerCreateIntentSupersededError();\n }\n if (input.moveIntentSettlement) {\n if (\n !(await deleteContainerMoveIntentRevision({\n ...input.moveIntentSettlement,\n tx,\n }))\n ) {\n throw new Error(\n \"Container move intent was superseded before local settlement\",\n );\n }\n }\n}\n\nexport async function commitStoredMetadataMutation(\n execSql: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[0],\n input: Parameters<ContainerContentsPersistence[\"commitMetadataMutation\"]>[1],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const outcome = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async (tx) => {\n const currentState = await loadStoredContainerState(\n lockedExecSql,\n input.container.id,\n );\n const staleServerResult = staleServerMutationResult(\n currentState,\n input,\n );\n if (staleServerResult) return staleServerResult;\n if (!metadataMutationStateMatches(currentState, input)) {\n return rejectMetadataMutationConflict({\n currentState,\n mutation: input,\n tx,\n });\n }\n assertMutationKeepsHeldBinding(currentState, input);\n\n await enqueuePendingUpdate(\n lockedExecSql,\n input.container.id,\n input.pendingUpdate,\n );\n await deleteAcceptedPendingUpdates(\n tx,\n input.container.id,\n input.acceptedPendingUpdateIds,\n );\n if (input.clearSyncFailure) {\n await clearMetadataSyncFailure(tx, input.container.id);\n }\n const { container, localUpdatedAt } =\n await prepareContainerMutationWrite({\n currentContainer: currentState.container,\n mutation: input,\n tx,\n });\n const saved = await saveContainerContentsContainerRows({\n container,\n createIntent: input.saveOptions?.createIntent,\n localUpdatedAt,\n moveIntent: input.saveOptions?.moveIntent,\n record: input.record,\n serverTimestamps: input.saveOptions?.serverTimestamps,\n tx,\n });\n await resetAccessDiscovery(tx, currentState.container, saved);\n await settleMetadataStructuralIntents(tx, input);\n return committedMetadataMutationResult({\n container: saved,\n createIntentSettled: input.createIntentSettlement !== undefined,\n moveIntentSettled: input.moveIntentSettlement !== undefined,\n });\n },\n () => !input.stillCurrent || input.stillCurrent(),\n { behavior: \"immediate\" },\n );\n if (outcome.committed && outcome.result) {\n return outcome.result;\n }\n return {\n committed: false as const,\n currentState: await loadStoredContainerState(\n lockedExecSql,\n input.container.id,\n ),\n };\n });\n}\n\nexport async function settleStoredMetadataPendingUpdates(\n execSql: Parameters<\n ContainerContentsPersistence[\"settleAcceptedMetadataPendingUpdates\"]\n >[0],\n input: Parameters<\n ContainerContentsPersistence[\"settleAcceptedMetadataPendingUpdates\"]\n >[1],\n) {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const outcome = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async (tx) => {\n const currentState = await loadStoredContainerState(\n lockedExecSql,\n input.containerId,\n );\n if (\n currentState?.record &&\n sameMetadataSecurityIdentity(\n currentState.record,\n input.expectedRecord,\n )\n ) {\n await deleteAcceptedPendingUpdates(\n tx,\n input.containerId,\n input.pendingUpdateIds,\n );\n }\n return currentState;\n },\n () => !input.stillCurrent || input.stillCurrent(),\n { behavior: \"immediate\" },\n );\n return outcome.committed ? outcome.result : null;\n });\n}\n"]}
@@ -2,14 +2,14 @@ import { base64ToBytes } from "../../../internal/encoding/index.js";
2
2
  import { getImportBlobMetadata } from "../../../internal/loro/index.js";
3
3
  import { and, eq } from "drizzle-orm";
4
4
  import { serializeDocumentSyncPullContinuation } from "../../documents/shared/sync/pullContinuation.js";
5
- import { documentHistoryCheckpoints, documents } from "../../sqlite/schema/index.js";
5
+ import { containers, documentHistoryCheckpoints, documents, } from "../../sqlite/schema/index.js";
6
6
  import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
7
7
  import { documentRecordSelection, mapSelectedDocumentRecord, } from "../documents/records/index.js";
8
8
  import { upsertDocumentHistoryCheckpointInTransaction, upsertDocumentRowInTransaction, } from "../documents/records/documentRowsInTransaction.js";
9
9
  import { hasDocumentPurgeCheckpointInTransaction } from "../keyingCheckpoints/documentPurgeCheckpointPersistence.js";
10
10
  import { saveContainerCreateIntent } from "./containerIntentPersistence.js";
11
11
  import { saveContainerMoveIntent } from "./containerMoveIntentRows.js";
12
- import { saveContainerRows, } from "./containerPersistence.js";
12
+ import { containerListOrder, containerRecordSelection, mapSelectedContainerRecord, saveContainerRows, } from "./containerPersistence.js";
13
13
  import { CONTAINER_METADATA_APP_KIND, clearDormantContainerMetadataInTransaction, } from "./dormantContainerMetadata.js";
14
14
  export function getContainerMetadataScope(containerId) {
15
15
  return {
@@ -150,4 +150,36 @@ export async function selectContainerMetadataRecord(execSql, containerId) {
150
150
  metadataUpdates: metadataUpdates ?? "",
151
151
  };
152
152
  }
153
+ /**
154
+ * Every container with its metadata record, in container-list order, from ONE
155
+ * joined statement: the list and each record it pairs with come from one
156
+ * snapshot instead of one read per container.
157
+ */
158
+ export async function selectContainersWithMetadataRecords(execSql) {
159
+ const { db } = getClientSQLitePersistenceRuntime(execSql);
160
+ const rows = await db
161
+ .select({
162
+ container: containerRecordSelection,
163
+ metadataUpdates: documentHistoryCheckpoints.snapshot,
164
+ // Drizzle returns a left-joined table's object as null when its row is
165
+ // absent.
166
+ record: documentRecordSelection,
167
+ })
168
+ .from(containers)
169
+ .leftJoin(documents, and(eq(documents.appKind, CONTAINER_METADATA_APP_KIND), eq(documents.localId, containers.id)))
170
+ .leftJoin(documentHistoryCheckpoints, and(eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND), eq(documentHistoryCheckpoints.localId, documents.localId)))
171
+ .orderBy(containerListOrder);
172
+ return rows.map((row) => {
173
+ const container = mapSelectedContainerRecord(row.container);
174
+ if (!row.record)
175
+ return { container, record: null };
176
+ return {
177
+ container,
178
+ record: {
179
+ ...mapSelectedDocumentRecord(row.record),
180
+ metadataUpdates: row.metadataUpdates ?? "",
181
+ },
182
+ };
183
+ });
184
+ }
153
185
  //# sourceMappingURL=containerMetadataRows.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"containerMetadataRows.js","sourceRoot":"","sources":["containerMetadataRows.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes } from \"@tearleads/encoding\";\nimport { getImportBlobMetadata } from \"@tearleads/loro\";\nimport { and, eq } from \"drizzle-orm\";\nimport { serializeDocumentSyncPullContinuation } from \"../../documents/shared/sync/pullContinuation\";\nimport { documentHistoryCheckpoints, documents } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport {\n documentRecordSelection,\n mapSelectedDocumentRecord,\n} from \"../documents/records\";\nimport {\n upsertDocumentHistoryCheckpointInTransaction,\n upsertDocumentRowInTransaction,\n} from \"../documents/records/documentRowsInTransaction\";\nimport { hasDocumentPurgeCheckpointInTransaction } from \"../keyingCheckpoints/documentPurgeCheckpointPersistence\";\nimport type {\n ContainerCreateIntentInput,\n ContainerMetadataRecord,\n ContainerMoveIntentInput,\n} from \"./containerContentsPersistenceTypes\";\nimport { saveContainerCreateIntent } from \"./containerIntentPersistence\";\nimport { saveContainerMoveIntent } from \"./containerMoveIntentRows\";\nimport {\n type ContainerRecord,\n saveContainerRows,\n} from \"./containerPersistence\";\nimport {\n CONTAINER_METADATA_APP_KIND,\n clearDormantContainerMetadataInTransaction,\n} from \"./dormantContainerMetadata\";\n\nexport function getContainerMetadataScope(containerId: string) {\n return {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n };\n}\n\n// The metadata document's content frontier, derived from its full updates\n// export. Empty or undecodable blobs read as \"never hydrated\".\nfunction deriveMetadataEndVersion(metadataUpdates: string): string {\n if (metadataUpdates.length === 0) {\n return \"\";\n }\n\n try {\n return getImportBlobMetadata(base64ToBytes(metadataUpdates))\n .partialEndVersionVector;\n } catch {\n return \"\";\n }\n}\n\nasync function saveContainerMetadataRecord(input: {\n containerId: string;\n record: ContainerMetadataRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}) {\n const { containerId, record, tx, updatedAt } = input;\n // The API refuses a container whose metadata id was permanently purged, so\n // a served head naming one predates that rule or is dishonest. Keep the\n // purged id's row from returning, but still store the container itself:\n // its metadata is unavailable either way, and refusing would stall hydration.\n if (\n record.documentId !== null &&\n (await hasDocumentPurgeCheckpointInTransaction(tx, record.documentId))\n ) {\n return;\n }\n const snapshotEndVersion = deriveMetadataEndVersion(record.metadataUpdates);\n const nextRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n documentId: record.documentId,\n snapshotEndVersion,\n accessEpoch: record.accessEpoch,\n accessStateHash: record.accessStateHash ?? null,\n lastCommitLsn: record.lastCommitLsn ?? null,\n documentManifestBundle: record.documentManifestBundle ?? null,\n contentKeyBundle: record.contentKeyBundle ?? null,\n documentKekTargets: record.documentKekTargets ?? null,\n ...(record.pullContinuationRecoveryRequired === true &&\n record.pullContinuation === undefined\n ? {}\n : {\n pullContinuation: serializeDocumentSyncPullContinuation(\n record.pullContinuation ?? null,\n ),\n }),\n updatedAt,\n };\n\n await upsertDocumentRowInTransaction(tx, nextRow);\n\n // The metadata document's content lives in the shared history-checkpoint\n // table, like every other document kind. The whole updates export IS the\n // checkpoint: metadata documents are tiny registries, so no tail/compaction\n // machinery is needed, and the row replaces atomically with the record.\n const checkpointRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n snapshot: record.metadataUpdates,\n endVersionVector: snapshotEndVersion,\n revision: updatedAt,\n updatedAt,\n };\n await upsertDocumentHistoryCheckpointInTransaction(tx, checkpointRow);\n}\n\nexport async function saveContainerContentsContainerRows(input: {\n container: ContainerRecord;\n createIntent?: ContainerCreateIntentInput | undefined;\n moveIntent?: ContainerMoveIntentInput | undefined;\n record: ContainerMetadataRecord | null;\n tx: ClientSQLiteTransactionScope;\n localUpdatedAt: string;\n serverTimestamps?:\n | {\n createdAt?: string | null;\n updatedAt?: string | null;\n }\n | undefined;\n}): Promise<ContainerRecord> {\n const {\n container,\n createIntent,\n moveIntent,\n localUpdatedAt,\n record,\n serverTimestamps,\n tx,\n } = input;\n const nextContainer = {\n ...container,\n ...(serverTimestamps\n ? {\n serverCreatedAt:\n serverTimestamps.createdAt ?? container.serverCreatedAt ?? null,\n serverUpdatedAt:\n serverTimestamps.updatedAt ?? container.serverUpdatedAt ?? null,\n }\n : {}),\n };\n\n const savedContainer = await saveContainerRows({\n record: nextContainer,\n tx,\n localUpdatedAt,\n });\n await clearDormantContainerMetadataInTransaction(tx, [container.id]);\n\n if (record) {\n await saveContainerMetadataRecord({\n containerId: container.id,\n record: {\n ...record,\n id: container.id,\n },\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (createIntent) {\n await saveContainerCreateIntent({\n containerId: container.id,\n createIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (moveIntent) {\n await saveContainerMoveIntent({\n containerId: container.id,\n moveIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n return savedContainer;\n}\n\n/**\n * ONE joined statement, so the record's key/access fields and the checkpoint\n * content it pairs with come from a single consistent read — two separate\n * queries could interleave with a concurrent metadata save (record +\n * checkpoint written in one transaction) and pair stale keying state with\n * newer content.\n */\nexport async function selectContainerMetadataRecord(\n execSql: ExecSql,\n containerId: string,\n): Promise<ContainerMetadataRecord | null> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n ...documentRecordSelection,\n metadataUpdates: documentHistoryCheckpoints.snapshot,\n })\n .from(documents)\n .leftJoin(\n documentHistoryCheckpoints,\n and(\n eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documentHistoryCheckpoints.localId, documents.localId),\n ),\n )\n .where(\n and(\n eq(documents.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documents.localId, containerId),\n ),\n )\n .limit(1);\n const row = rows[0];\n if (!row) {\n return null;\n }\n const { metadataUpdates, ...recordRow } = row;\n return {\n ...mapSelectedDocumentRecord(recordRow),\n metadataUpdates: metadataUpdates ?? \"\",\n };\n}\n"]}
1
+ {"version":3,"file":"containerMetadataRows.js","sourceRoot":"","sources":["containerMetadataRows.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes } from \"@tearleads/encoding\";\nimport { getImportBlobMetadata } from \"@tearleads/loro\";\nimport { and, eq } from \"drizzle-orm\";\nimport { serializeDocumentSyncPullContinuation } from \"../../documents/shared/sync/pullContinuation\";\nimport {\n containers,\n documentHistoryCheckpoints,\n documents,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport {\n documentRecordSelection,\n mapSelectedDocumentRecord,\n} from \"../documents/records\";\nimport {\n upsertDocumentHistoryCheckpointInTransaction,\n upsertDocumentRowInTransaction,\n} from \"../documents/records/documentRowsInTransaction\";\nimport { hasDocumentPurgeCheckpointInTransaction } from \"../keyingCheckpoints/documentPurgeCheckpointPersistence\";\nimport type {\n ContainerCreateIntentInput,\n ContainerMetadataRecord,\n ContainerMoveIntentInput,\n} from \"./containerContentsPersistenceTypes\";\nimport { saveContainerCreateIntent } from \"./containerIntentPersistence\";\nimport { saveContainerMoveIntent } from \"./containerMoveIntentRows\";\nimport {\n type ContainerRecord,\n containerListOrder,\n containerRecordSelection,\n mapSelectedContainerRecord,\n saveContainerRows,\n} from \"./containerPersistence\";\nimport {\n CONTAINER_METADATA_APP_KIND,\n clearDormantContainerMetadataInTransaction,\n} from \"./dormantContainerMetadata\";\n\nexport function getContainerMetadataScope(containerId: string) {\n return {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n };\n}\n\n// The metadata document's content frontier, derived from its full updates\n// export. Empty or undecodable blobs read as \"never hydrated\".\nfunction deriveMetadataEndVersion(metadataUpdates: string): string {\n if (metadataUpdates.length === 0) {\n return \"\";\n }\n\n try {\n return getImportBlobMetadata(base64ToBytes(metadataUpdates))\n .partialEndVersionVector;\n } catch {\n return \"\";\n }\n}\n\nasync function saveContainerMetadataRecord(input: {\n containerId: string;\n record: ContainerMetadataRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}) {\n const { containerId, record, tx, updatedAt } = input;\n // The API refuses a container whose metadata id was permanently purged, so\n // a served head naming one predates that rule or is dishonest. Keep the\n // purged id's row from returning, but still store the container itself:\n // its metadata is unavailable either way, and refusing would stall hydration.\n if (\n record.documentId !== null &&\n (await hasDocumentPurgeCheckpointInTransaction(tx, record.documentId))\n ) {\n return;\n }\n const snapshotEndVersion = deriveMetadataEndVersion(record.metadataUpdates);\n const nextRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n documentId: record.documentId,\n snapshotEndVersion,\n accessEpoch: record.accessEpoch,\n accessStateHash: record.accessStateHash ?? null,\n lastCommitLsn: record.lastCommitLsn ?? null,\n documentManifestBundle: record.documentManifestBundle ?? null,\n contentKeyBundle: record.contentKeyBundle ?? null,\n documentKekTargets: record.documentKekTargets ?? null,\n ...(record.pullContinuationRecoveryRequired === true &&\n record.pullContinuation === undefined\n ? {}\n : {\n pullContinuation: serializeDocumentSyncPullContinuation(\n record.pullContinuation ?? null,\n ),\n }),\n updatedAt,\n };\n\n await upsertDocumentRowInTransaction(tx, nextRow);\n\n // The metadata document's content lives in the shared history-checkpoint\n // table, like every other document kind. The whole updates export IS the\n // checkpoint: metadata documents are tiny registries, so no tail/compaction\n // machinery is needed, and the row replaces atomically with the record.\n const checkpointRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n snapshot: record.metadataUpdates,\n endVersionVector: snapshotEndVersion,\n revision: updatedAt,\n updatedAt,\n };\n await upsertDocumentHistoryCheckpointInTransaction(tx, checkpointRow);\n}\n\nexport async function saveContainerContentsContainerRows(input: {\n container: ContainerRecord;\n createIntent?: ContainerCreateIntentInput | undefined;\n moveIntent?: ContainerMoveIntentInput | undefined;\n record: ContainerMetadataRecord | null;\n tx: ClientSQLiteTransactionScope;\n localUpdatedAt: string;\n serverTimestamps?:\n | {\n createdAt?: string | null;\n updatedAt?: string | null;\n }\n | undefined;\n}): Promise<ContainerRecord> {\n const {\n container,\n createIntent,\n moveIntent,\n localUpdatedAt,\n record,\n serverTimestamps,\n tx,\n } = input;\n const nextContainer = {\n ...container,\n ...(serverTimestamps\n ? {\n serverCreatedAt:\n serverTimestamps.createdAt ?? container.serverCreatedAt ?? null,\n serverUpdatedAt:\n serverTimestamps.updatedAt ?? container.serverUpdatedAt ?? null,\n }\n : {}),\n };\n\n const savedContainer = await saveContainerRows({\n record: nextContainer,\n tx,\n localUpdatedAt,\n });\n await clearDormantContainerMetadataInTransaction(tx, [container.id]);\n\n if (record) {\n await saveContainerMetadataRecord({\n containerId: container.id,\n record: {\n ...record,\n id: container.id,\n },\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (createIntent) {\n await saveContainerCreateIntent({\n containerId: container.id,\n createIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (moveIntent) {\n await saveContainerMoveIntent({\n containerId: container.id,\n moveIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n return savedContainer;\n}\n\n/**\n * ONE joined statement, so the record's key/access fields and the checkpoint\n * content it pairs with come from a single consistent read — two separate\n * queries could interleave with a concurrent metadata save (record +\n * checkpoint written in one transaction) and pair stale keying state with\n * newer content.\n */\nexport async function selectContainerMetadataRecord(\n execSql: ExecSql,\n containerId: string,\n): Promise<ContainerMetadataRecord | null> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n ...documentRecordSelection,\n metadataUpdates: documentHistoryCheckpoints.snapshot,\n })\n .from(documents)\n .leftJoin(\n documentHistoryCheckpoints,\n and(\n eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documentHistoryCheckpoints.localId, documents.localId),\n ),\n )\n .where(\n and(\n eq(documents.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documents.localId, containerId),\n ),\n )\n .limit(1);\n const row = rows[0];\n if (!row) {\n return null;\n }\n const { metadataUpdates, ...recordRow } = row;\n return {\n ...mapSelectedDocumentRecord(recordRow),\n metadataUpdates: metadataUpdates ?? \"\",\n };\n}\n\n/**\n * Every container with its metadata record, in container-list order, from ONE\n * joined statement: the list and each record it pairs with come from one\n * snapshot instead of one read per container.\n */\nexport async function selectContainersWithMetadataRecords(\n execSql: ExecSql,\n): Promise<\n Array<{ container: ContainerRecord; record: ContainerMetadataRecord | null }>\n> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n container: containerRecordSelection,\n metadataUpdates: documentHistoryCheckpoints.snapshot,\n // Drizzle returns a left-joined table's object as null when its row is\n // absent.\n record: documentRecordSelection,\n })\n .from(containers)\n .leftJoin(\n documents,\n and(\n eq(documents.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documents.localId, containers.id),\n ),\n )\n .leftJoin(\n documentHistoryCheckpoints,\n and(\n eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documentHistoryCheckpoints.localId, documents.localId),\n ),\n )\n .orderBy(containerListOrder);\n return rows.map((row) => {\n const container = mapSelectedContainerRecord(row.container);\n if (!row.record) return { container, record: null };\n return {\n container,\n record: {\n ...mapSelectedDocumentRecord(row.record),\n metadataUpdates: row.metadataUpdates ?? \"\",\n },\n };\n });\n}\n"]}