@tearleads/client-sdk 0.1.44 → 0.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/Tearleads.js +3 -0
- package/client/Tearleads.js.map +1 -1
- package/client/userIdentities.d.ts +11 -1
- package/client/userIdentities.js +26 -8
- package/client/userIdentities.js.map +1 -1
- package/client/workflowRuntime.d.ts +2 -1
- package/client/workflowRuntime.js +1 -0
- package/client/workflowRuntime.js.map +1 -1
- package/data/persistence/container-contents/containerContentsPersistence.js +2 -3
- package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerCreateIntentSettlement.js +16 -16
- package/data/persistence/container-contents/containerCreateIntentSettlement.js.map +1 -1
- package/data/persistence/container-contents/containerDeletionPersistence.js +5 -11
- package/data/persistence/container-contents/containerDeletionPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerDocumentReassignment.js +13 -13
- package/data/persistence/container-contents/containerDocumentReassignment.js.map +1 -1
- package/data/persistence/container-contents/containerHydrationPersistence.js +7 -4
- package/data/persistence/container-contents/containerHydrationPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerIntentPersistence.js +43 -42
- package/data/persistence/container-contents/containerIntentPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js +15 -20
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMoveAbandonPersistence.js +1 -6
- package/data/persistence/container-contents/containerMoveAbandonPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMoveIntentRows.js +9 -8
- package/data/persistence/container-contents/containerMoveIntentRows.js.map +1 -1
- package/data/persistence/container-contents/containerReconcilePersistence.js +4 -8
- package/data/persistence/container-contents/containerReconcilePersistence.js.map +1 -1
- package/data/persistence/container-contents/containerRecoveryPersistence.js +20 -19
- package/data/persistence/container-contents/containerRecoveryPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerRemovalWork.js +10 -10
- package/data/persistence/container-contents/containerRemovalWork.js.map +1 -1
- package/data/persistence/container-contents/containerStructuralRepair.js +21 -44
- package/data/persistence/container-contents/containerStructuralRepair.js.map +1 -1
- package/data/persistence/container-contents/documentLinkRemovalRepair.js +12 -11
- package/data/persistence/container-contents/documentLinkRemovalRepair.js.map +1 -1
- package/data/persistence/container-contents/documentMoveIntentPersistence.js +53 -28
- package/data/persistence/container-contents/documentMoveIntentPersistence.js.map +1 -1
- package/data/persistence/container-contents/documentPlacementIntentEnqueue.js +9 -8
- package/data/persistence/container-contents/documentPlacementIntentEnqueue.js.map +1 -1
- package/data/persistence/container-contents/documentPurgePlacement.js +6 -6
- package/data/persistence/container-contents/documentPurgePlacement.js.map +1 -1
- package/data/persistence/container-contents/dormantContainerMetadata.js +1 -5
- package/data/persistence/container-contents/dormantContainerMetadata.js.map +1 -1
- package/data/persistence/container-contents/pendingContainerCreate.js +5 -5
- package/data/persistence/container-contents/pendingContainerCreate.js.map +1 -1
- package/data/persistence/containers/containerPersistence.js +10 -27
- package/data/persistence/containers/containerPersistence.js.map +1 -1
- package/data/persistence/containers/documentContainerProjectionPersistence.js +2 -2
- package/data/persistence/containers/documentContainerProjectionPersistence.js.map +1 -1
- package/data/persistence/containers/documentPlacement.js +10 -10
- package/data/persistence/containers/documentPlacement.js.map +1 -1
- package/data/persistence/documents/internal/discardDocument.js +5 -5
- package/data/persistence/documents/internal/discardDocument.js.map +1 -1
- package/data/persistence/documents/internal/documentDeletionPersistence.js +7 -9
- package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
- package/data/persistence/documents/internal/documentMutationPersistence.js +6 -8
- package/data/persistence/documents/internal/documentMutationPersistence.js.map +1 -1
- package/data/persistence/documents/internal/ensureDocumentsSchema.js +2 -2
- package/data/persistence/documents/internal/ensureDocumentsSchema.js.map +1 -1
- package/data/persistence/documents/internal/orphanSideRows.js +3 -22
- package/data/persistence/documents/internal/orphanSideRows.js.map +1 -1
- package/data/persistence/organizations/organizationDataUsagePersistence.js +31 -64
- package/data/persistence/organizations/organizationDataUsagePersistence.js.map +1 -1
- package/data/persistence/organizations/organizationReadModelPurge.js +1 -5
- package/data/persistence/organizations/organizationReadModelPurge.js.map +1 -1
- package/data/persistence/principalCurrentAcknowledgementPersistence.js +24 -17
- package/data/persistence/principalCurrentAcknowledgementPersistence.js.map +1 -1
- package/data/persistence/principalHistoryIncompleteRetention.js +3 -9
- package/data/persistence/principalHistoryIncompleteRetention.js.map +1 -1
- package/data/persistence/principalHistoryStagePersistence.d.ts +3 -9
- package/data/persistence/principalHistoryStagePersistence.js +15 -25
- package/data/persistence/principalHistoryStagePersistence.js.map +1 -1
- package/data/persistence/principalHistoryStageRetention.js +16 -15
- package/data/persistence/principalHistoryStageRetention.js.map +1 -1
- package/data/persistence/principalHistoryUnpublishedRetention.js +6 -13
- package/data/persistence/principalHistoryUnpublishedRetention.js.map +1 -1
- package/data/sqlite/containerSchema.d.ts +84 -180
- package/data/sqlite/containerSchema.js +14 -23
- package/data/sqlite/containerSchema.js.map +1 -1
- package/data/sqlite/documentPlacementIntentSchema.js +3 -50
- package/data/sqlite/documentPlacementIntentSchema.js.map +1 -1
- package/data/sqlite/documentSyncFailurePersistence.d.ts +16 -6
- package/data/sqlite/documentSyncFailurePersistence.js +44 -54
- package/data/sqlite/documentSyncFailurePersistence.js.map +1 -1
- package/data/sqlite/{documentPlacementIntentSchema.d.ts → localIntentSchema.d.ts} +162 -88
- package/data/sqlite/localIntentSchema.js +90 -0
- package/data/sqlite/localIntentSchema.js.map +1 -0
- package/data/sqlite/organizationDataUsageSchema.js +11 -17
- package/data/sqlite/organizationDataUsageSchema.js.map +1 -1
- package/data/sqlite/principalHistoryRetentionSchema.js +0 -15
- package/data/sqlite/principalHistoryRetentionSchema.js.map +1 -1
- package/data/sqlite/principalHistoryStageSchema.d.ts +44 -2
- package/data/sqlite/principalHistoryStageSchema.js +14 -2
- package/data/sqlite/principalHistoryStageSchema.js.map +1 -1
- package/data/sqlite/schema.d.ts +947 -2112
- package/data/sqlite/schema.js +26 -134
- package/data/sqlite/schema.js.map +1 -1
- package/data/trustedUserIdentity/identityLink.d.ts +12 -0
- package/data/trustedUserIdentity/identityLink.js +117 -0
- package/data/trustedUserIdentity/identityLink.js.map +1 -0
- package/data/trustedUserIdentity/index.d.ts +2 -1
- package/data/trustedUserIdentity/index.js +1 -0
- package/data/trustedUserIdentity/index.js.map +1 -1
- package/data/trustedUserIdentity/service.js +48 -2
- package/data/trustedUserIdentity/service.js.map +1 -1
- package/data/trustedUserIdentity/types.d.ts +3 -0
- package/data/trustedUserIdentity/types.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/workflows/container-contents/documentPriming.js +6 -10
- package/workflows/container-contents/documentPriming.js.map +1 -1
- package/workflows/container-contents/documentQueries/sql.js +11 -10
- package/workflows/container-contents/documentQueries/sql.js.map +1 -1
- package/workflows/container-contents/documentQueries.js +2 -2
- package/workflows/container-contents/documentQueries.js.map +1 -1
- package/workflows/container-contents/pendingDownloads.js +2 -5
- package/workflows/container-contents/pendingDownloads.js.map +1 -1
- package/workflows/container-contents/pendingWrites/deferredTails.js +3 -11
- package/workflows/container-contents/pendingWrites/deferredTails.js.map +1 -1
- package/workflows/container-contents/pendingWrites/sources.js +56 -80
- package/workflows/container-contents/pendingWrites/sources.js.map +1 -1
- package/workflows/container-contents/pendingWrites.js +6 -4
- package/workflows/container-contents/pendingWrites.js.map +1 -1
- package/workflows/container-contents/remoteContainerState.js +2 -1
- package/workflows/container-contents/remoteContainerState.js.map +1 -1
- package/workflows/sync/remoteReset.js +16 -23
- package/workflows/sync/remoteReset.js.map +1 -1
- package/workflows/sync/remoteResetPrincipalScope.js +1 -2
- package/workflows/sync/remoteResetPrincipalScope.js.map +1 -1
- package/workflows/sync/remoteResetScope.js +15 -8
- package/workflows/sync/remoteResetScope.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerCreateIntentSettlement.js","sourceRoot":"","sources":["containerCreateIntentSettlement.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport {\n containerCreateIntents,\n containerMoveIntents,\n} from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n CONTAINER_CREATE_INTENT_TYPE,\n CONTAINER_MOVE_INTENT_TYPE,\n type ContainerMoveIntentInput,\n} from \"./containerContentsPersistenceTypes\";\nimport { saveContainerMoveIntent } from \"./containerMoveIntentRows\";\n\nasync function markContainerCreateIntentRevisionSynced(input: {\n containerId: string;\n expectedIntentId: string;\n expectedUpdatedAt: string;\n remoteContainerId: string;\n remoteMetadataAccessStateHash: string;\n remoteMetadataDocumentId: string;\n tx: ClientSQLiteTransactionScope;\n}): Promise<boolean> {\n const updated = await input.tx\n .update(containerCreateIntents)\n .set({\n syncStatus: \"synced\",\n remoteContainerId: input.remoteContainerId,\n remoteMetadataDocumentId: input.remoteMetadataDocumentId,\n remoteMetadataAccessStateHash: input.remoteMetadataAccessStateHash,\n lastError: null,\n updatedAt: new Date().toISOString(),\n })\n .where(\n and(\n eq(containerCreateIntents.containerId, input.containerId),\n eq(containerCreateIntents.intentType, CONTAINER_CREATE_INTENT_TYPE),\n eq(containerCreateIntents.syncStatus, \"pending\"),\n eq(containerCreateIntents.id, input.expectedIntentId),\n eq(containerCreateIntents.updatedAt, input.expectedUpdatedAt),\n ),\n )\n .returning({ containerId: containerCreateIntents.containerId });\n return updated.length > 0;\n}\n\n/**\n * The move an adopted create still owes: to the desired parent, citing where\n * the folder is now. A desired parent equal to the created one means the user\n * never moved it away, so a later remote move by another writer stands.\n */\nfunction owedCreateMove(input: {\n readonly createdParentContainerId?: string | undefined;\n readonly desiredParentContainerId: string;\n readonly supersededMovePreviousParentId: string;\n}): ContainerMoveIntentInput | null {\n const desired = input.desiredParentContainerId;\n if (\n desired === input.supersededMovePreviousParentId ||\n desired === input.createdParentContainerId\n ) {\n return null;\n }\n return {\n parentContainerId: desired,\n previousParentContainerId: input.supersededMovePreviousParentId,\n };\n}\n\n/**\n * A local move of a folder the listing already carried queues its own move\n * intent, which is newer than the create's parent: it keeps its destination,\n * and now cites where the folder sits remotely rather than the parent it was\n * queued from, which never committed. One whose destination is already that\n * parent is done and is dropped.\n */\nasync function rebaseQueuedMove(input: {\n readonly containerId: string;\n readonly supersededMovePreviousParentId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<\"converted-to-move\" | \"none\" | \"synced\"> {\n const queued = and(\n eq(containerMoveIntents.containerId, input.containerId),\n eq(containerMoveIntents.intentType, CONTAINER_MOVE_INTENT_TYPE),\n );\n const arrived = await input.tx\n .delete(containerMoveIntents)\n .where(\n and(\n queued,\n eq(\n containerMoveIntents.parentContainerId,\n input.supersededMovePreviousParentId,\n ),\n ),\n )\n .returning({ containerId: containerMoveIntents.containerId });\n if (arrived.length > 0) return \"synced\";\n const rebased = await input.tx\n .update(containerMoveIntents)\n .set({ previousParentContainerId: input.supersededMovePreviousParentId })\n .where(queued)\n .returning({ containerId: containerMoveIntents.containerId });\n return rebased.length > 0 ? \"converted-to-move\" : \"none\";\n}\n\nasync function queueOwedCreateMove(input: {\n readonly containerId: string;\n readonly createdParentContainerId?: string | undefined;\n readonly desiredParentContainerId: string;\n readonly supersededMovePreviousParentId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<\"converted-to-move\" | \"synced\"> {\n const queued = await rebaseQueuedMove(input);\n if (queued !== \"none\") return queued;\n const moveIntent = owedCreateMove(input);\n if (!moveIntent) return \"synced\";\n await saveContainerMoveIntent({\n containerId: input.containerId,\n moveIntent,\n tx: input.tx,\n updatedAt: new Date().toISOString(),\n });\n return \"converted-to-move\";\n}\n\nexport async function settleContainerCreateIntentRevision(input: {\n containerId: string;\n expectedIntentId: string;\n expectedUpdatedAt: string;\n remoteContainerId: string;\n remoteMetadataAccessStateHash: string;\n remoteMetadataDocumentId: string;\n supersededMovePreviousParentId?: string | undefined;\n createdParentContainerId?: string | undefined;\n desiredParentContainerId?: string | undefined;\n tx: ClientSQLiteTransactionScope;\n}): Promise<\"converted-to-move\" | \"superseded\" | \"synced\"> {\n const previousParentId = input.supersededMovePreviousParentId;\n if (await markContainerCreateIntentRevisionSynced(input)) {\n // An adopted create may still owe a move: the user can move the folder\n // after its create committed.\n if (\n previousParentId === undefined ||\n input.desiredParentContainerId === undefined\n ) {\n return \"synced\";\n }\n return queueOwedCreateMove({\n ...input,\n desiredParentContainerId: input.desiredParentContainerId,\n supersededMovePreviousParentId: previousParentId,\n });\n }\n if (previousParentId === undefined) {\n return \"superseded\";\n }\n\n const [currentIntent] = await input.tx\n .select({\n id: containerCreateIntents.id,\n parentContainerId: containerCreateIntents.parentContainerId,\n updatedAt: containerCreateIntents.updatedAt,\n })\n .from(containerCreateIntents)\n .where(\n and(\n eq(containerCreateIntents.containerId, input.containerId),\n eq(containerCreateIntents.intentType, CONTAINER_CREATE_INTENT_TYPE),\n eq(containerCreateIntents.syncStatus, \"pending\"),\n ),\n )\n .limit(1);\n if (!currentIntent?.id) return \"superseded\";\n\n const adopted = await markContainerCreateIntentRevisionSynced({\n ...input,\n expectedIntentId: currentIntent.id,\n expectedUpdatedAt: currentIntent.updatedAt,\n });\n if (!adopted) return \"superseded\";\n return queueOwedCreateMove({\n ...input,\n desiredParentContainerId: currentIntent.parentContainerId,\n supersededMovePreviousParentId: previousParentId,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"containerCreateIntentSettlement.js","sourceRoot":"","sources":["containerCreateIntentSettlement.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport {\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n CONTAINER_CREATE_INTENT_TYPE,\n CONTAINER_MOVE_INTENT_TYPE,\n type ContainerMoveIntentInput,\n} from \"./containerContentsPersistenceTypes\";\nimport { saveContainerMoveIntent } from \"./containerMoveIntentRows\";\n\nasync function markContainerCreateIntentRevisionSynced(input: {\n containerId: string;\n expectedIntentId: string;\n expectedUpdatedAt: string;\n remoteContainerId: string;\n remoteMetadataAccessStateHash: string;\n remoteMetadataDocumentId: string;\n tx: ClientSQLiteTransactionScope;\n}): Promise<boolean> {\n const updated = await input.tx\n .update(localIntents)\n .set({\n syncStatus: \"synced\",\n remoteContainerId: input.remoteContainerId,\n remoteMetadataDocumentId: input.remoteMetadataDocumentId,\n remoteMetadataAccessStateHash: input.remoteMetadataAccessStateHash,\n lastError: null,\n updatedAt: new Date().toISOString(),\n })\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate),\n eq(localIntents.subjectId, input.containerId),\n eq(localIntents.intentType, CONTAINER_CREATE_INTENT_TYPE),\n eq(localIntents.syncStatus, \"pending\"),\n eq(localIntents.id, input.expectedIntentId),\n eq(localIntents.updatedAt, input.expectedUpdatedAt),\n ),\n )\n .returning({ containerId: localIntents.subjectId });\n return updated.length > 0;\n}\n\n/**\n * The move an adopted create still owes: to the desired parent, citing where\n * the folder is now. A desired parent equal to the created one means the user\n * never moved it away, so a later remote move by another writer stands.\n */\nfunction owedCreateMove(input: {\n readonly createdParentContainerId?: string | undefined;\n readonly desiredParentContainerId: string;\n readonly supersededMovePreviousParentId: string;\n}): ContainerMoveIntentInput | null {\n const desired = input.desiredParentContainerId;\n if (\n desired === input.supersededMovePreviousParentId ||\n desired === input.createdParentContainerId\n ) {\n return null;\n }\n return {\n parentContainerId: desired,\n previousParentContainerId: input.supersededMovePreviousParentId,\n };\n}\n\n/**\n * A local move of a folder the listing already carried queues its own move\n * intent, which is newer than the create's parent: it keeps its destination,\n * and now cites where the folder sits remotely rather than the parent it was\n * queued from, which never committed. One whose destination is already that\n * parent is done and is dropped.\n */\nasync function rebaseQueuedMove(input: {\n readonly containerId: string;\n readonly supersededMovePreviousParentId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<\"converted-to-move\" | \"none\" | \"synced\"> {\n const queued = and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerMove),\n eq(localIntents.subjectId, input.containerId),\n eq(localIntents.intentType, CONTAINER_MOVE_INTENT_TYPE),\n );\n const arrived = await input.tx\n .delete(localIntents)\n .where(\n and(\n queued,\n eq(\n localIntents.targetContainerId,\n input.supersededMovePreviousParentId,\n ),\n ),\n )\n .returning({ containerId: localIntents.subjectId });\n if (arrived.length > 0) return \"synced\";\n const rebased = await input.tx\n .update(localIntents)\n .set({ sourceContainerId: input.supersededMovePreviousParentId })\n .where(queued)\n .returning({ containerId: localIntents.subjectId });\n return rebased.length > 0 ? \"converted-to-move\" : \"none\";\n}\n\nasync function queueOwedCreateMove(input: {\n readonly containerId: string;\n readonly createdParentContainerId?: string | undefined;\n readonly desiredParentContainerId: string;\n readonly supersededMovePreviousParentId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<\"converted-to-move\" | \"synced\"> {\n const queued = await rebaseQueuedMove(input);\n if (queued !== \"none\") return queued;\n const moveIntent = owedCreateMove(input);\n if (!moveIntent) return \"synced\";\n await saveContainerMoveIntent({\n containerId: input.containerId,\n moveIntent,\n tx: input.tx,\n updatedAt: new Date().toISOString(),\n });\n return \"converted-to-move\";\n}\n\nexport async function settleContainerCreateIntentRevision(input: {\n containerId: string;\n expectedIntentId: string;\n expectedUpdatedAt: string;\n remoteContainerId: string;\n remoteMetadataAccessStateHash: string;\n remoteMetadataDocumentId: string;\n supersededMovePreviousParentId?: string | undefined;\n createdParentContainerId?: string | undefined;\n desiredParentContainerId?: string | undefined;\n tx: ClientSQLiteTransactionScope;\n}): Promise<\"converted-to-move\" | \"superseded\" | \"synced\"> {\n const previousParentId = input.supersededMovePreviousParentId;\n if (await markContainerCreateIntentRevisionSynced(input)) {\n // An adopted create may still owe a move: the user can move the folder\n // after its create committed.\n if (\n previousParentId === undefined ||\n input.desiredParentContainerId === undefined\n ) {\n return \"synced\";\n }\n return queueOwedCreateMove({\n ...input,\n desiredParentContainerId: input.desiredParentContainerId,\n supersededMovePreviousParentId: previousParentId,\n });\n }\n if (previousParentId === undefined) {\n return \"superseded\";\n }\n\n const [currentIntent] = await input.tx\n .select({\n id: localIntents.id,\n parentContainerId: localIntents.targetContainerId,\n updatedAt: localIntents.updatedAt,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate),\n eq(localIntents.subjectId, input.containerId),\n eq(localIntents.intentType, CONTAINER_CREATE_INTENT_TYPE),\n eq(localIntents.syncStatus, \"pending\"),\n ),\n )\n .limit(1);\n if (!currentIntent?.id) return \"superseded\";\n\n const adopted = await markContainerCreateIntentRevisionSynced({\n ...input,\n expectedIntentId: currentIntent.id,\n expectedUpdatedAt: currentIntent.updatedAt,\n });\n if (!adopted) return \"superseded\";\n return queueOwedCreateMove({\n ...input,\n desiredParentContainerId: currentIntent.parentContainerId,\n supersededMovePreviousParentId: previousParentId,\n });\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { inArray } from "drizzle-orm";
|
|
1
|
+
import { and, inArray } from "drizzle-orm";
|
|
2
2
|
import { ensureDocumentProjectionTables } from "../../sqlite/documentPersistence.js";
|
|
3
|
-
import {
|
|
3
|
+
import { containers, documentContainerProjectionTables, documentPlacementIntentTables, inContainerIntentLanes, localIntents, } from "../../sqlite/schema.js";
|
|
4
4
|
import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
|
|
5
5
|
import { ensureSqlTables, runSerializedSqlMutation, } from "../../sqlite/sqlSchema.js";
|
|
6
6
|
import { deleteContainerRowsInTransaction, ensureContainerTables, } from "../containers/containerPersistence.js";
|
|
@@ -111,12 +111,8 @@ async function applyContainerRemovals(input) {
|
|
|
111
111
|
else {
|
|
112
112
|
await repairLinkIntentsForRemovedContainers({ containerIds, tx: input.tx });
|
|
113
113
|
await input.tx
|
|
114
|
-
.delete(
|
|
115
|
-
.where(inArray(
|
|
116
|
-
.run();
|
|
117
|
-
await input.tx
|
|
118
|
-
.delete(containerMoveIntents)
|
|
119
|
-
.where(inArray(containerMoveIntents.containerId, containerIds))
|
|
114
|
+
.delete(localIntents)
|
|
115
|
+
.where(and(inContainerIntentLanes(), inArray(localIntents.subjectId, containerIds)))
|
|
120
116
|
.run();
|
|
121
117
|
}
|
|
122
118
|
await deleteContainerRowsInTransaction(input.tx, containerIds);
|
|
@@ -130,10 +126,8 @@ export async function deleteStoredContainers(execSql, removals, options) {
|
|
|
130
126
|
return [];
|
|
131
127
|
return runSerializedSqlMutation(execSql, async (lockedExecSql) => {
|
|
132
128
|
await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);
|
|
133
|
-
await ensureSqlTables(lockedExecSql,
|
|
129
|
+
await ensureSqlTables(lockedExecSql, documentPlacementIntentTables);
|
|
134
130
|
await ensureContainerTables(lockedExecSql);
|
|
135
|
-
await ensureSqlTables(lockedExecSql, containerCreateIntentTables);
|
|
136
|
-
await ensureSqlTables(lockedExecSql, containerMoveIntentTables);
|
|
137
131
|
await ensureDocumentProjectionTables(lockedExecSql);
|
|
138
132
|
await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);
|
|
139
133
|
const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerDeletionPersistence.js","sourceRoot":"","sources":["containerDeletionPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { inArray } from \"drizzle-orm\";\nimport { ensureDocumentProjectionTables } from \"../../sqlite/documentPersistence\";\nimport {\n containerCreateIntents,\n containerCreateIntentTables,\n containerMoveIntents,\n containerMoveIntentTables,\n containers,\n documentContainerProjectionTables,\n documentMoveIntentTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n deleteContainerRowsInTransaction,\n ensureContainerTables,\n} from \"../containers/containerPersistence\";\nimport {\n deleteContainerWatermarksInTransaction,\n sqlContainerSyncWatermarkPersistence,\n} from \"../containers/containerSyncWatermarkPersistence\";\nimport type {\n ContainerContentsPersistence,\n ContainerDeletionGuard,\n ContainerRemoval,\n} from \"./containerContentsPersistenceTypes\";\nimport { recordContainerHydrationTombstones } from \"./containerHydrationPersistence\";\nimport { containerIdsWithRemovalWork } from \"./containerRemovalWork\";\nimport { repairDocumentsForRemovedContainersInTransaction } from \"./containerStructuralRepair\";\nimport {\n refreshPlacementIntentsAfterRemoval,\n repairLinkIntentsForRemovedContainers,\n} from \"./documentLinkRemovalRepair\";\nimport {\n deleteContainerMetadataDocumentRowsInTransaction,\n retainDormantContainerMetadataInTransaction,\n} from \"./dormantContainerMetadata\";\n\ntype DeleteContainerOptions = NonNullable<\n Parameters<ContainerContentsPersistence[\"deleteContainers\"]>[2]\n>;\n\nfunction normalizeContainerRemovals(\n removals: ReadonlyArray<ContainerRemoval>,\n): ContainerRemoval[] {\n const removalByContainerId = new Map<string, ContainerRemoval>();\n for (const removal of removals) {\n const current = removalByContainerId.get(removal.containerId);\n if (\n !current ||\n (current.reason === \"access_revoked\" && removal.reason === \"deleted\") ||\n (current.reason === removal.reason &&\n current.updatedAt < removal.updatedAt)\n ) {\n removalByContainerId.set(removal.containerId, removal);\n }\n }\n return Array.from(removalByContainerId.values());\n}\n\nfunction loadGuardedContainerRows(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n) {\n return tx\n .select({\n effectiveAccessLevel: containers.effectiveAccessLevel,\n id: containers.id,\n metadataDocumentId: containers.metadataDocumentId,\n organizationId: containers.organizationId,\n parentId: containers.parentId,\n serverCreatedAt: containers.serverCreatedAt,\n serverUpdatedAt: containers.serverUpdatedAt,\n systemSlot: containers.systemSlot,\n })\n .from(containers)\n .where(inArray(containers.id, [...containerIds]));\n}\n\ntype GuardedContainerRow = Awaited<\n ReturnType<typeof loadGuardedContainerRows>\n>[number];\n\nfunction sameGuardedContainerRow(\n row: GuardedContainerRow | undefined,\n guard: ContainerDeletionGuard,\n): boolean {\n const expected = guard.expectedContainer;\n if (!row || !expected) return row === undefined && expected === null;\n return (\n row.id === expected.id &&\n row.organizationId === expected.organizationId &&\n row.parentId === expected.parentId &&\n row.metadataDocumentId === expected.metadataDocumentId &&\n row.systemSlot === (expected.systemSlot ?? null) &&\n row.effectiveAccessLevel === (expected.effectiveAccessLevel ?? \"read\") &&\n row.serverCreatedAt === (expected.serverCreatedAt ?? null) &&\n row.serverUpdatedAt === (expected.serverUpdatedAt ?? null)\n );\n}\n\nasync function canApplyContainerRemovals(input: {\n expectedContainers: DeleteContainerOptions[\"expectedContainers\"];\n removals: ReadonlyArray<ContainerRemoval>;\n tx: ClientSQLiteTransactionScope;\n}): Promise<boolean> {\n const currentRows = await loadGuardedContainerRows(\n input.tx,\n input.removals.map((removal) => removal.containerId),\n );\n const currentRowById = new Map(\n currentRows.flatMap((row) => (row.id ? ([[row.id, row]] as const) : [])),\n );\n const guardById = new Map(\n input.expectedContainers?.map((guard) => [guard.containerId, guard]) ?? [],\n );\n return input.removals.every((removal) => {\n const currentRow = currentRowById.get(removal.containerId);\n const guard = guardById.get(removal.containerId);\n return (\n (!guard || sameGuardedContainerRow(currentRow, guard)) &&\n (currentRow?.serverUpdatedAt == null ||\n currentRow.serverUpdatedAt <= removal.updatedAt)\n );\n });\n}\n\nasync function retainRemovedContainerMetadata(input: {\n containerIds: ReadonlyArray<string>;\n retainedAt: string;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n if (input.containerIds.length === 0) return;\n const retainedContainers = await input.tx\n .select({\n containerId: containers.id,\n organizationId: containers.organizationId,\n })\n .from(containers)\n .where(inArray(containers.id, [...input.containerIds]));\n await retainDormantContainerMetadataInTransaction(\n input.tx,\n retainedContainers.flatMap((container) =>\n container.containerId\n ? [\n {\n containerId: container.containerId,\n organizationId: container.organizationId,\n retainedAt: input.retainedAt,\n },\n ]\n : [],\n ),\n );\n}\n\nasync function applyContainerRemovals(input: {\n discoveryOnly: DeleteContainerOptions[\"discoveryOnly\"];\n metadataDeleteIds: ReadonlyArray<string>;\n removals: ReadonlyArray<ContainerRemoval>;\n retainedMetadataIds: ReadonlyArray<string>;\n tx: ClientSQLiteTransactionScope;\n}): Promise<ReadonlyArray<string>> {\n const containerIds = input.removals.map((removal) => removal.containerId);\n await recordContainerHydrationTombstones({\n removals: input.removals,\n tx: input.tx,\n });\n await retainRemovedContainerMetadata({\n containerIds: input.retainedMetadataIds,\n retainedAt: new Date().toISOString(),\n tx: input.tx,\n });\n // Listing clocks are untrusted; they cannot advance local projection clocks.\n const localObservedAt = new Date().toISOString();\n await repairDocumentsForRemovedContainersInTransaction({\n removals: input.discoveryOnly\n ? input.removals.map((removal) => ({\n ...removal,\n updatedAt: localObservedAt,\n }))\n : input.removals,\n tx: input.tx,\n });\n if (input.discoveryOnly) {\n await refreshPlacementIntentsAfterRemoval({ containerIds, tx: input.tx });\n } else {\n await repairLinkIntentsForRemovedContainers({ containerIds, tx: input.tx });\n await input.tx\n .delete(containerCreateIntents)\n .where(inArray(containerCreateIntents.containerId, containerIds))\n .run();\n await input.tx\n .delete(containerMoveIntents)\n .where(inArray(containerMoveIntents.containerId, containerIds))\n .run();\n }\n await deleteContainerRowsInTransaction(input.tx, containerIds);\n await deleteContainerMetadataDocumentRowsInTransaction(\n input.tx,\n input.metadataDeleteIds,\n );\n await deleteContainerWatermarksInTransaction(input.tx, containerIds);\n return containerIds;\n}\n\nexport async function deleteStoredContainers(\n execSql: Parameters<ContainerContentsPersistence[\"deleteContainers\"]>[0],\n removals: Parameters<ContainerContentsPersistence[\"deleteContainers\"]>[1],\n options?: Parameters<ContainerContentsPersistence[\"deleteContainers\"]>[2],\n): Promise<ReadonlyArray<string>> {\n const uniqueRemovals = normalizeContainerRemovals(removals);\n if (uniqueRemovals.length === 0) return [];\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n await ensureSqlTables(lockedExecSql, documentMoveIntentTables);\n await ensureContainerTables(lockedExecSql);\n await ensureSqlTables(lockedExecSql, containerCreateIntentTables);\n await ensureSqlTables(lockedExecSql, containerMoveIntentTables);\n await ensureDocumentProjectionTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const remove = async (tx: ClientSQLiteTransactionScope) => {\n if (\n options?.beforeDeleteInTransaction &&\n !(await options.beforeDeleteInTransaction(lockedExecSql))\n )\n return [];\n if (\n !(await canApplyContainerRemovals({\n expectedContainers: options?.expectedContainers,\n removals: uniqueRemovals,\n tx,\n }))\n ) {\n return [];\n }\n const retainedMetadataIds = options?.discoveryOnly\n ? await containerIdsWithRemovalWork(\n tx,\n uniqueRemovals.map((removal) => removal.containerId),\n )\n : [];\n return applyContainerRemovals({\n discoveryOnly: options?.discoveryOnly,\n metadataDeleteIds: uniqueRemovals.flatMap((removal) =>\n retainedMetadataIds.includes(removal.containerId)\n ? []\n : [removal.containerId],\n ),\n removals: uniqueRemovals,\n retainedMetadataIds,\n tx,\n });\n };\n if (!options?.stillCurrent) {\n return runtime.transaction(remove, { behavior: \"immediate\" });\n }\n const outcome = await runtime.guardedTransaction(\n remove,\n options.stillCurrent,\n { behavior: \"immediate\" },\n );\n return outcome.result ?? [];\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"containerDeletionPersistence.js","sourceRoot":"","sources":["containerDeletionPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, inArray } from \"drizzle-orm\";\nimport { ensureDocumentProjectionTables } from \"../../sqlite/documentPersistence\";\nimport {\n containers,\n documentContainerProjectionTables,\n documentPlacementIntentTables,\n inContainerIntentLanes,\n localIntents,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n deleteContainerRowsInTransaction,\n ensureContainerTables,\n} from \"../containers/containerPersistence\";\nimport {\n deleteContainerWatermarksInTransaction,\n sqlContainerSyncWatermarkPersistence,\n} from \"../containers/containerSyncWatermarkPersistence\";\nimport type {\n ContainerContentsPersistence,\n ContainerDeletionGuard,\n ContainerRemoval,\n} from \"./containerContentsPersistenceTypes\";\nimport { recordContainerHydrationTombstones } from \"./containerHydrationPersistence\";\nimport { containerIdsWithRemovalWork } from \"./containerRemovalWork\";\nimport { repairDocumentsForRemovedContainersInTransaction } from \"./containerStructuralRepair\";\nimport {\n refreshPlacementIntentsAfterRemoval,\n repairLinkIntentsForRemovedContainers,\n} from \"./documentLinkRemovalRepair\";\nimport {\n deleteContainerMetadataDocumentRowsInTransaction,\n retainDormantContainerMetadataInTransaction,\n} from \"./dormantContainerMetadata\";\n\ntype DeleteContainerOptions = NonNullable<\n Parameters<ContainerContentsPersistence[\"deleteContainers\"]>[2]\n>;\n\nfunction normalizeContainerRemovals(\n removals: ReadonlyArray<ContainerRemoval>,\n): ContainerRemoval[] {\n const removalByContainerId = new Map<string, ContainerRemoval>();\n for (const removal of removals) {\n const current = removalByContainerId.get(removal.containerId);\n if (\n !current ||\n (current.reason === \"access_revoked\" && removal.reason === \"deleted\") ||\n (current.reason === removal.reason &&\n current.updatedAt < removal.updatedAt)\n ) {\n removalByContainerId.set(removal.containerId, removal);\n }\n }\n return Array.from(removalByContainerId.values());\n}\n\nfunction loadGuardedContainerRows(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n) {\n return tx\n .select({\n effectiveAccessLevel: containers.effectiveAccessLevel,\n id: containers.id,\n metadataDocumentId: containers.metadataDocumentId,\n organizationId: containers.organizationId,\n parentId: containers.parentId,\n serverCreatedAt: containers.serverCreatedAt,\n serverUpdatedAt: containers.serverUpdatedAt,\n systemSlot: containers.systemSlot,\n })\n .from(containers)\n .where(inArray(containers.id, [...containerIds]));\n}\n\ntype GuardedContainerRow = Awaited<\n ReturnType<typeof loadGuardedContainerRows>\n>[number];\n\nfunction sameGuardedContainerRow(\n row: GuardedContainerRow | undefined,\n guard: ContainerDeletionGuard,\n): boolean {\n const expected = guard.expectedContainer;\n if (!row || !expected) return row === undefined && expected === null;\n return (\n row.id === expected.id &&\n row.organizationId === expected.organizationId &&\n row.parentId === expected.parentId &&\n row.metadataDocumentId === expected.metadataDocumentId &&\n row.systemSlot === (expected.systemSlot ?? null) &&\n row.effectiveAccessLevel === (expected.effectiveAccessLevel ?? \"read\") &&\n row.serverCreatedAt === (expected.serverCreatedAt ?? null) &&\n row.serverUpdatedAt === (expected.serverUpdatedAt ?? null)\n );\n}\n\nasync function canApplyContainerRemovals(input: {\n expectedContainers: DeleteContainerOptions[\"expectedContainers\"];\n removals: ReadonlyArray<ContainerRemoval>;\n tx: ClientSQLiteTransactionScope;\n}): Promise<boolean> {\n const currentRows = await loadGuardedContainerRows(\n input.tx,\n input.removals.map((removal) => removal.containerId),\n );\n const currentRowById = new Map(\n currentRows.flatMap((row) => (row.id ? ([[row.id, row]] as const) : [])),\n );\n const guardById = new Map(\n input.expectedContainers?.map((guard) => [guard.containerId, guard]) ?? [],\n );\n return input.removals.every((removal) => {\n const currentRow = currentRowById.get(removal.containerId);\n const guard = guardById.get(removal.containerId);\n return (\n (!guard || sameGuardedContainerRow(currentRow, guard)) &&\n (currentRow?.serverUpdatedAt == null ||\n currentRow.serverUpdatedAt <= removal.updatedAt)\n );\n });\n}\n\nasync function retainRemovedContainerMetadata(input: {\n containerIds: ReadonlyArray<string>;\n retainedAt: string;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n if (input.containerIds.length === 0) return;\n const retainedContainers = await input.tx\n .select({\n containerId: containers.id,\n organizationId: containers.organizationId,\n })\n .from(containers)\n .where(inArray(containers.id, [...input.containerIds]));\n await retainDormantContainerMetadataInTransaction(\n input.tx,\n retainedContainers.flatMap((container) =>\n container.containerId\n ? [\n {\n containerId: container.containerId,\n organizationId: container.organizationId,\n retainedAt: input.retainedAt,\n },\n ]\n : [],\n ),\n );\n}\n\nasync function applyContainerRemovals(input: {\n discoveryOnly: DeleteContainerOptions[\"discoveryOnly\"];\n metadataDeleteIds: ReadonlyArray<string>;\n removals: ReadonlyArray<ContainerRemoval>;\n retainedMetadataIds: ReadonlyArray<string>;\n tx: ClientSQLiteTransactionScope;\n}): Promise<ReadonlyArray<string>> {\n const containerIds = input.removals.map((removal) => removal.containerId);\n await recordContainerHydrationTombstones({\n removals: input.removals,\n tx: input.tx,\n });\n await retainRemovedContainerMetadata({\n containerIds: input.retainedMetadataIds,\n retainedAt: new Date().toISOString(),\n tx: input.tx,\n });\n // Listing clocks are untrusted; they cannot advance local projection clocks.\n const localObservedAt = new Date().toISOString();\n await repairDocumentsForRemovedContainersInTransaction({\n removals: input.discoveryOnly\n ? input.removals.map((removal) => ({\n ...removal,\n updatedAt: localObservedAt,\n }))\n : input.removals,\n tx: input.tx,\n });\n if (input.discoveryOnly) {\n await refreshPlacementIntentsAfterRemoval({ containerIds, tx: input.tx });\n } else {\n await repairLinkIntentsForRemovedContainers({ containerIds, tx: input.tx });\n await input.tx\n .delete(localIntents)\n .where(\n and(\n inContainerIntentLanes(),\n inArray(localIntents.subjectId, containerIds),\n ),\n )\n .run();\n }\n await deleteContainerRowsInTransaction(input.tx, containerIds);\n await deleteContainerMetadataDocumentRowsInTransaction(\n input.tx,\n input.metadataDeleteIds,\n );\n await deleteContainerWatermarksInTransaction(input.tx, containerIds);\n return containerIds;\n}\n\nexport async function deleteStoredContainers(\n execSql: Parameters<ContainerContentsPersistence[\"deleteContainers\"]>[0],\n removals: Parameters<ContainerContentsPersistence[\"deleteContainers\"]>[1],\n options?: Parameters<ContainerContentsPersistence[\"deleteContainers\"]>[2],\n): Promise<ReadonlyArray<string>> {\n const uniqueRemovals = normalizeContainerRemovals(removals);\n if (uniqueRemovals.length === 0) return [];\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n await ensureSqlTables(lockedExecSql, documentPlacementIntentTables);\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentProjectionTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const remove = async (tx: ClientSQLiteTransactionScope) => {\n if (\n options?.beforeDeleteInTransaction &&\n !(await options.beforeDeleteInTransaction(lockedExecSql))\n )\n return [];\n if (\n !(await canApplyContainerRemovals({\n expectedContainers: options?.expectedContainers,\n removals: uniqueRemovals,\n tx,\n }))\n ) {\n return [];\n }\n const retainedMetadataIds = options?.discoveryOnly\n ? await containerIdsWithRemovalWork(\n tx,\n uniqueRemovals.map((removal) => removal.containerId),\n )\n : [];\n return applyContainerRemovals({\n discoveryOnly: options?.discoveryOnly,\n metadataDeleteIds: uniqueRemovals.flatMap((removal) =>\n retainedMetadataIds.includes(removal.containerId)\n ? []\n : [removal.containerId],\n ),\n removals: uniqueRemovals,\n retainedMetadataIds,\n tx,\n });\n };\n if (!options?.stillCurrent) {\n return runtime.transaction(remove, { behavior: \"immediate\" });\n }\n const outcome = await runtime.guardedTransaction(\n remove,\n options.stillCurrent,\n { behavior: \"immediate\" },\n );\n return outcome.result ?? [];\n });\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { and, eq, inArray, or, sql } from "drizzle-orm";
|
|
2
2
|
import { uniqueSortedStrings } from "../../documents/shared/readers.js";
|
|
3
3
|
import { documentIntentLinkTargets } from "../../sqlite/documentPlacementIntentSchema.js";
|
|
4
|
-
import { documentContainerProjection,
|
|
4
|
+
import { documentContainerProjection, documentProjection, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../sqlite/schema.js";
|
|
5
5
|
import { deleteContainerDocumentTombstoneHoldRowsForLinks, deleteContainerDocumentTombstoneHoldsForContainers, } from "../documents/containerDocumentTombstoneHoldsPersistence.js";
|
|
6
6
|
import { DOCUMENT_LINK_INTENT_TYPE, DOCUMENT_MOVE_INTENT_TYPE, } from "./documentMoveIntentPersistence.js";
|
|
7
7
|
async function reassignDocumentLinksForContainer(input) {
|
|
@@ -61,14 +61,14 @@ async function reassignDocumentMoveIntentsForContainer(input) {
|
|
|
61
61
|
const { fromContainerId, toContainerId, tx, updatedAt } = input;
|
|
62
62
|
const affectedIntents = await tx
|
|
63
63
|
.select({
|
|
64
|
-
documentId:
|
|
65
|
-
id:
|
|
64
|
+
documentId: localIntents.subjectId,
|
|
65
|
+
id: localIntents.id,
|
|
66
66
|
})
|
|
67
|
-
.from(
|
|
68
|
-
.where(and(inArray(
|
|
67
|
+
.from(localIntents)
|
|
68
|
+
.where(and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), inArray(localIntents.intentType, [
|
|
69
69
|
DOCUMENT_MOVE_INTENT_TYPE,
|
|
70
70
|
DOCUMENT_LINK_INTENT_TYPE,
|
|
71
|
-
]), or(eq(
|
|
71
|
+
]), or(eq(localIntents.sourceContainerId, fromContainerId), eq(localIntents.targetContainerId, fromContainerId), inArray(localIntents.id, tx
|
|
72
72
|
.select({ id: documentIntentLinkTargets.intentId })
|
|
73
73
|
.from(documentIntentLinkTargets)
|
|
74
74
|
.where(and(eq(documentIntentLinkTargets.containerId, fromContainerId), eq(documentIntentLinkTargets.operation, "link")))))));
|
|
@@ -97,25 +97,25 @@ async function reassignDocumentMoveIntentsForContainer(input) {
|
|
|
97
97
|
})))
|
|
98
98
|
.run();
|
|
99
99
|
await tx
|
|
100
|
-
.update(
|
|
100
|
+
.update(localIntents)
|
|
101
101
|
.set({
|
|
102
102
|
id,
|
|
103
103
|
lastAttemptedAt: null,
|
|
104
104
|
lastError: null,
|
|
105
105
|
sourceContainerId: sql `CASE
|
|
106
|
-
WHEN ${
|
|
106
|
+
WHEN ${localIntents.sourceContainerId} = ${fromContainerId}
|
|
107
107
|
THEN ${toContainerId}
|
|
108
|
-
ELSE ${
|
|
108
|
+
ELSE ${localIntents.sourceContainerId}
|
|
109
109
|
END`,
|
|
110
110
|
syncStatus: "pending",
|
|
111
111
|
targetContainerId: sql `CASE
|
|
112
|
-
WHEN ${
|
|
112
|
+
WHEN ${localIntents.targetContainerId} = ${fromContainerId}
|
|
113
113
|
THEN ${toContainerId}
|
|
114
|
-
ELSE ${
|
|
114
|
+
ELSE ${localIntents.targetContainerId}
|
|
115
115
|
END`,
|
|
116
|
-
updatedAt: sql `max(${
|
|
116
|
+
updatedAt: sql `max(${localIntents.updatedAt}, ${updatedAt})`,
|
|
117
117
|
})
|
|
118
|
-
.where(eq(
|
|
118
|
+
.where(and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), eq(localIntents.subjectId, intent.documentId)))
|
|
119
119
|
.run();
|
|
120
120
|
}
|
|
121
121
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerDocumentReassignment.js","sourceRoot":"","sources":["containerDocumentReassignment.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, inArray, or, sql } from \"drizzle-orm\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport { documentIntentLinkTargets } from \"../../sqlite/documentPlacementIntentSchema\";\nimport {\n documentContainerProjection,\n documentMoveIntents,\n documentProjection,\n} from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n deleteContainerDocumentTombstoneHoldRowsForLinks,\n deleteContainerDocumentTombstoneHoldsForContainers,\n} from \"../documents/containerDocumentTombstoneHoldsPersistence\";\nimport {\n DOCUMENT_LINK_INTENT_TYPE,\n DOCUMENT_MOVE_INTENT_TYPE,\n} from \"./documentMoveIntentPersistence\";\n\ninterface ContainerDocumentReassignmentInput {\n readonly fromContainerId: string;\n readonly toContainerId: string;\n readonly tx: ClientSQLiteTransactionScope;\n readonly updatedAt: string;\n}\n\nasync function reassignDocumentLinksForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n await tx.run(sql`\n INSERT INTO ${documentContainerProjection} (\n ${sql.raw(\"document_id\")},\n ${sql.raw(\"container_id\")},\n ${sql.raw(\"updated_at\")}\n )\n SELECT\n ${documentContainerProjection.documentId},\n ${toContainerId},\n max(${documentContainerProjection.updatedAt}, ${updatedAt})\n FROM ${documentContainerProjection}\n WHERE ${documentContainerProjection.containerId} = ${fromContainerId}\n ON CONFLICT (\n ${sql.raw(\"document_id\")},\n ${sql.raw(\"container_id\")}\n ) DO UPDATE SET\n ${sql.raw(\"updated_at\")} = max(\n ${sql.raw(\"updated_at\")},\n excluded.${sql.raw(\"updated_at\")}\n )\n `);\n\n // The reassigned placements are asserted locally: a stale hold on the\n // destination would hide them, and holds on the vacated container have\n // nothing left to hide.\n const movedDocumentIds = (\n await tx\n .select({ documentId: documentContainerProjection.documentId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.containerId, fromContainerId))\n ).map((row) => row.documentId);\n await deleteContainerDocumentTombstoneHoldRowsForLinks(\n tx,\n movedDocumentIds.map((documentId) => ({\n containerIds: [toContainerId],\n documentId,\n })),\n );\n await deleteContainerDocumentTombstoneHoldsForContainers(tx, [\n fromContainerId,\n ]);\n await tx\n .delete(documentContainerProjection)\n .where(eq(documentContainerProjection.containerId, fromContainerId))\n .run();\n}\n\nasync function reassignPrimaryDocumentsForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n await tx\n .update(documentProjection)\n .set({\n containerId: toContainerId,\n updatedAt: sql`max(${documentProjection.updatedAt}, ${updatedAt})`,\n })\n .where(eq(documentProjection.containerId, fromContainerId))\n .run();\n}\n\nasync function reassignDocumentMoveIntentsForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n const affectedIntents = await tx\n .select({\n documentId: documentMoveIntents.documentId,\n id: documentMoveIntents.id,\n })\n .from(documentMoveIntents)\n .where(\n and(\n inArray(documentMoveIntents.intentType, [\n DOCUMENT_MOVE_INTENT_TYPE,\n DOCUMENT_LINK_INTENT_TYPE,\n ]),\n or(\n eq(documentMoveIntents.sourceContainerId, fromContainerId),\n eq(documentMoveIntents.targetContainerId, fromContainerId),\n inArray(\n documentMoveIntents.id,\n tx\n .select({ id: documentIntentLinkTargets.intentId })\n .from(documentIntentLinkTargets)\n .where(\n and(\n eq(documentIntentLinkTargets.containerId, fromContainerId),\n eq(documentIntentLinkTargets.operation, \"link\"),\n ),\n ),\n ),\n ),\n ),\n );\n for (const intent of affectedIntents) {\n const id = crypto.randomUUID();\n const targets = await tx\n .select()\n .from(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, intent.id ?? \"\"));\n await tx\n .delete(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, intent.id ?? \"\"))\n .run();\n const retained = new Map(\n targets.map((target) => [target.containerId, target.operation]),\n );\n if (retained.get(fromContainerId) === \"link\") {\n retained.delete(fromContainerId);\n retained.set(toContainerId, \"link\");\n }\n if (retained.size)\n await tx\n .insert(documentIntentLinkTargets)\n .values(\n uniqueSortedStrings([...retained.keys()]).map((containerId) => ({\n intentId: id,\n containerId,\n operation: retained.get(containerId) ?? \"link\",\n })),\n )\n .run();\n await tx\n .update(documentMoveIntents)\n .set({\n id,\n lastAttemptedAt: null,\n lastError: null,\n sourceContainerId: sql`CASE\n WHEN ${documentMoveIntents.sourceContainerId} = ${fromContainerId}\n THEN ${toContainerId}\n ELSE ${documentMoveIntents.sourceContainerId}\n END`,\n syncStatus: \"pending\",\n targetContainerId: sql`CASE\n WHEN ${documentMoveIntents.targetContainerId} = ${fromContainerId}\n THEN ${toContainerId}\n ELSE ${documentMoveIntents.targetContainerId}\n END`,\n updatedAt: sql`max(${documentMoveIntents.updatedAt}, ${updatedAt})`,\n })\n .where(eq(documentMoveIntents.documentId, intent.documentId))\n .run();\n }\n}\n\nexport async function reassignContainerDocumentsInTransaction(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n await reassignDocumentLinksForContainer(input);\n await reassignPrimaryDocumentsForContainer(input);\n await reassignDocumentMoveIntentsForContainer(input);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"containerDocumentReassignment.js","sourceRoot":"","sources":["containerDocumentReassignment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,GACb,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,gDAAgD,EAChD,kDAAkD,GACnD,MAAM,yDAAyD,CAAC;AACjE,OAAO,EACL,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,iCAAiC,CAAC;AASzC,KAAK,UAAU,iCAAiC,CAC9C,KAAyC;IAEzC,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAChE,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAA;kBACA,2BAA2B;QACrC,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC;QACtB,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;QACvB,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;;;QAGrB,2BAA2B,CAAC,UAAU;QACtC,aAAa;YACT,2BAA2B,CAAC,SAAS,KAAK,SAAS;WACpD,2BAA2B;YAC1B,2BAA2B,CAAC,WAAW,MAAM,eAAe;;QAEhE,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC;QACtB,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;;QAEvB,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;UACnB,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;mBACZ,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;;GAErC,CAAC,CAAC;IAEH,sEAAsE;IACtE,uEAAuE;IACvE,wBAAwB;IACxB,MAAM,gBAAgB,GAAG,CACvB,MAAM,EAAE;SACL,MAAM,CAAC,EAAE,UAAU,EAAE,2BAA2B,CAAC,UAAU,EAAE,CAAC;SAC9D,IAAI,CAAC,2BAA2B,CAAC;SACjC,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC,CACvE,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC/B,MAAM,gDAAgD,CACpD,EAAE,EACF,gBAAgB,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACpC,YAAY,EAAE,CAAC,aAAa,CAAC;QAC7B,UAAU;KACX,CAAC,CAAC,CACJ,CAAC;IACF,MAAM,kDAAkD,CAAC,EAAE,EAAE;QAC3D,eAAe;KAChB,CAAC,CAAC;IACH,MAAM,EAAE;SACL,MAAM,CAAC,2BAA2B,CAAC;SACnC,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;SACnE,GAAG,EAAE,CAAC;AACX,CAAC;AAED,KAAK,UAAU,oCAAoC,CACjD,KAAyC;IAEzC,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAChE,MAAM,EAAE;SACL,MAAM,CAAC,kBAAkB,CAAC;SAC1B,GAAG,CAAC;QACH,WAAW,EAAE,aAAa;QAC1B,SAAS,EAAE,GAAG,CAAA,OAAO,kBAAkB,CAAC,SAAS,KAAK,SAAS,GAAG;KACnE,CAAC;SACD,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;SAC1D,GAAG,EAAE,CAAC;AACX,CAAC;AAED,KAAK,UAAU,uCAAuC,CACpD,KAAyC;IAEzC,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAChE,MAAM,eAAe,GAAG,MAAM,EAAE;SAC7B,MAAM,CAAC;QACN,UAAU,EAAE,YAAY,CAAC,SAAS;QAClC,EAAE,EAAE,YAAY,CAAC,EAAE;KACpB,CAAC;SACD,IAAI,CAAC,YAAY,CAAC;SAClB,KAAK,CACJ,GAAG,CACD,iBAAiB,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,EACvD,OAAO,CAAC,YAAY,CAAC,UAAU,EAAE;QAC/B,yBAAyB;QACzB,yBAAyB;KAC1B,CAAC,EACF,EAAE,CACA,EAAE,CAAC,YAAY,CAAC,iBAAiB,EAAE,eAAe,CAAC,EACnD,EAAE,CAAC,YAAY,CAAC,iBAAiB,EAAE,eAAe,CAAC,EACnD,OAAO,CACL,YAAY,CAAC,EAAE,EACf,EAAE;SACC,MAAM,CAAC,EAAE,EAAE,EAAE,yBAAyB,CAAC,QAAQ,EAAE,CAAC;SAClD,IAAI,CAAC,yBAAyB,CAAC;SAC/B,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,yBAAyB,CAAC,WAAW,EAAE,eAAe,CAAC,EAC1D,EAAE,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,CAAC,CAChD,CACF,CACJ,CACF,CACF,CACF,CAAC;IACJ,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;QACrC,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,MAAM,EAAE;aACrB,MAAM,EAAE;aACR,IAAI,CAAC,yBAAyB,CAAC;aAC/B,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAClE,MAAM,EAAE;aACL,MAAM,CAAC,yBAAyB,CAAC;aACjC,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;aAC9D,GAAG,EAAE,CAAC;QACT,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAChE,CAAC;QACF,IAAI,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,MAAM,EAAE,CAAC;YAC7C,QAAQ,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;YACjC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,QAAQ,CAAC,IAAI;YACf,MAAM,EAAE;iBACL,MAAM,CAAC,yBAAyB,CAAC;iBACjC,MAAM,CACL,mBAAmB,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;gBAC9D,QAAQ,EAAE,EAAE;gBACZ,WAAW;gBACX,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,MAAM;aAC/C,CAAC,CAAC,CACJ;iBACA,GAAG,EAAE,CAAC;QACX,MAAM,EAAE;aACL,MAAM,CAAC,YAAY,CAAC;aACpB,GAAG,CAAC;YACH,EAAE;YACF,eAAe,EAAE,IAAI;YACrB,SAAS,EAAE,IAAI;YACf,iBAAiB,EAAE,GAAG,CAAA;iBACb,YAAY,CAAC,iBAAiB,MAAM,eAAe;iBACnD,aAAa;iBACb,YAAY,CAAC,iBAAiB;YACnC;YACJ,UAAU,EAAE,SAAS;YACrB,iBAAiB,EAAE,GAAG,CAAA;iBACb,YAAY,CAAC,iBAAiB,MAAM,eAAe;iBACnD,aAAa;iBACb,YAAY,CAAC,iBAAiB;YACnC;YACJ,SAAS,EAAE,GAAG,CAAA,OAAO,YAAY,CAAC,SAAS,KAAK,SAAS,GAAG;SAC7D,CAAC;aACD,KAAK,CACJ,GAAG,CACD,iBAAiB,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,EACvD,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAC9C,CACF;aACA,GAAG,EAAE,CAAC;IACX,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uCAAuC,CAC3D,KAAyC;IAEzC,MAAM,iCAAiC,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,oCAAoC,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,uCAAuC,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC","sourcesContent":["import { and, eq, inArray, or, sql } from \"drizzle-orm\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport { documentIntentLinkTargets } from \"../../sqlite/documentPlacementIntentSchema\";\nimport {\n documentContainerProjection,\n documentProjection,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n deleteContainerDocumentTombstoneHoldRowsForLinks,\n deleteContainerDocumentTombstoneHoldsForContainers,\n} from \"../documents/containerDocumentTombstoneHoldsPersistence\";\nimport {\n DOCUMENT_LINK_INTENT_TYPE,\n DOCUMENT_MOVE_INTENT_TYPE,\n} from \"./documentMoveIntentPersistence\";\n\ninterface ContainerDocumentReassignmentInput {\n readonly fromContainerId: string;\n readonly toContainerId: string;\n readonly tx: ClientSQLiteTransactionScope;\n readonly updatedAt: string;\n}\n\nasync function reassignDocumentLinksForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n await tx.run(sql`\n INSERT INTO ${documentContainerProjection} (\n ${sql.raw(\"document_id\")},\n ${sql.raw(\"container_id\")},\n ${sql.raw(\"updated_at\")}\n )\n SELECT\n ${documentContainerProjection.documentId},\n ${toContainerId},\n max(${documentContainerProjection.updatedAt}, ${updatedAt})\n FROM ${documentContainerProjection}\n WHERE ${documentContainerProjection.containerId} = ${fromContainerId}\n ON CONFLICT (\n ${sql.raw(\"document_id\")},\n ${sql.raw(\"container_id\")}\n ) DO UPDATE SET\n ${sql.raw(\"updated_at\")} = max(\n ${sql.raw(\"updated_at\")},\n excluded.${sql.raw(\"updated_at\")}\n )\n `);\n\n // The reassigned placements are asserted locally: a stale hold on the\n // destination would hide them, and holds on the vacated container have\n // nothing left to hide.\n const movedDocumentIds = (\n await tx\n .select({ documentId: documentContainerProjection.documentId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.containerId, fromContainerId))\n ).map((row) => row.documentId);\n await deleteContainerDocumentTombstoneHoldRowsForLinks(\n tx,\n movedDocumentIds.map((documentId) => ({\n containerIds: [toContainerId],\n documentId,\n })),\n );\n await deleteContainerDocumentTombstoneHoldsForContainers(tx, [\n fromContainerId,\n ]);\n await tx\n .delete(documentContainerProjection)\n .where(eq(documentContainerProjection.containerId, fromContainerId))\n .run();\n}\n\nasync function reassignPrimaryDocumentsForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n await tx\n .update(documentProjection)\n .set({\n containerId: toContainerId,\n updatedAt: sql`max(${documentProjection.updatedAt}, ${updatedAt})`,\n })\n .where(eq(documentProjection.containerId, fromContainerId))\n .run();\n}\n\nasync function reassignDocumentMoveIntentsForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n const affectedIntents = await tx\n .select({\n documentId: localIntents.subjectId,\n id: localIntents.id,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n inArray(localIntents.intentType, [\n DOCUMENT_MOVE_INTENT_TYPE,\n DOCUMENT_LINK_INTENT_TYPE,\n ]),\n or(\n eq(localIntents.sourceContainerId, fromContainerId),\n eq(localIntents.targetContainerId, fromContainerId),\n inArray(\n localIntents.id,\n tx\n .select({ id: documentIntentLinkTargets.intentId })\n .from(documentIntentLinkTargets)\n .where(\n and(\n eq(documentIntentLinkTargets.containerId, fromContainerId),\n eq(documentIntentLinkTargets.operation, \"link\"),\n ),\n ),\n ),\n ),\n ),\n );\n for (const intent of affectedIntents) {\n const id = crypto.randomUUID();\n const targets = await tx\n .select()\n .from(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, intent.id ?? \"\"));\n await tx\n .delete(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, intent.id ?? \"\"))\n .run();\n const retained = new Map(\n targets.map((target) => [target.containerId, target.operation]),\n );\n if (retained.get(fromContainerId) === \"link\") {\n retained.delete(fromContainerId);\n retained.set(toContainerId, \"link\");\n }\n if (retained.size)\n await tx\n .insert(documentIntentLinkTargets)\n .values(\n uniqueSortedStrings([...retained.keys()]).map((containerId) => ({\n intentId: id,\n containerId,\n operation: retained.get(containerId) ?? \"link\",\n })),\n )\n .run();\n await tx\n .update(localIntents)\n .set({\n id,\n lastAttemptedAt: null,\n lastError: null,\n sourceContainerId: sql`CASE\n WHEN ${localIntents.sourceContainerId} = ${fromContainerId}\n THEN ${toContainerId}\n ELSE ${localIntents.sourceContainerId}\n END`,\n syncStatus: \"pending\",\n targetContainerId: sql`CASE\n WHEN ${localIntents.targetContainerId} = ${fromContainerId}\n THEN ${toContainerId}\n ELSE ${localIntents.targetContainerId}\n END`,\n updatedAt: sql`max(${localIntents.updatedAt}, ${updatedAt})`,\n })\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.subjectId, intent.documentId),\n ),\n )\n .run();\n }\n}\n\nexport async function reassignContainerDocumentsInTransaction(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n await reassignDocumentLinksForContainer(input);\n await reassignPrimaryDocumentsForContainer(input);\n await reassignDocumentMoveIntentsForContainer(input);\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { KeyingVerificationError } from "../../../internal/crypto/index.js";
|
|
|
2
2
|
import { and, eq, inArray, sql } from "drizzle-orm";
|
|
3
3
|
import { assertHeldContainerBinding, heldContainerBinding, } from "../../containers/containerBinding.js";
|
|
4
4
|
import { documentSyncPullContinuationsEqual } from "../../documents/shared/pullContinuation.js";
|
|
5
|
-
import { containerHydrationTombstones,
|
|
5
|
+
import { containerHydrationTombstones, containers, dormantContainerMetadata, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../sqlite/schema.js";
|
|
6
6
|
import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
|
|
7
7
|
import { runSerializedSqlMutation } from "../../sqlite/sqlSchema.js";
|
|
8
8
|
import { loadContainerById } from "../containers/containerPersistence.js";
|
|
@@ -70,9 +70,12 @@ async function placementCheckpointMatches(tx, input) {
|
|
|
70
70
|
/** A retained local move continues to own placement after verified reattachment. */
|
|
71
71
|
async function saveRecoveredContainer(tx, input) {
|
|
72
72
|
const [move] = await tx
|
|
73
|
-
.select(
|
|
74
|
-
.
|
|
75
|
-
|
|
73
|
+
.select({
|
|
74
|
+
parentContainerId: localIntents.targetContainerId,
|
|
75
|
+
updatedAt: localIntents.updatedAt,
|
|
76
|
+
})
|
|
77
|
+
.from(localIntents)
|
|
78
|
+
.where(and(inLocalIntentLane(LOCAL_INTENT_LANES.containerMove), eq(localIntents.subjectId, input.container.id), inArray(localIntents.syncStatus, ["pending", "blocked"])))
|
|
76
79
|
.limit(1);
|
|
77
80
|
return saveContainerContentsContainerRows({
|
|
78
81
|
container: move
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerHydrationPersistence.js","sourceRoot":"","sources":["containerHydrationPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,0BAA0B,EAE1B,oBAAoB,GACrB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,kCAAkC,EAAE,MAAM,yCAAyC,CAAC;AAC7F,OAAO,EACL,4BAA4B,EAC5B,oBAAoB,EACpB,UAAU,EACV,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,iCAAiC,GAClC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAgB,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,kCAAkC,EAAE,MAAM,gCAAgC,CAAC;AAMpF,OAAO,EACL,kCAAkC,EAClC,6BAA6B,GAC9B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAE7E,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,kBAAkB,CACzB,OAAuC,EACvC,QAAwC;IAExC,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ;QAAE,OAAO,OAAO,KAAK,QAAQ,CAAC;IACvD,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,aAAa,EAAE,QAAQ,CAAC,aAAa,CAAC;QAChE,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;QACD,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,sBAAsB,CAC7B,OAEa,EACb,QAAwD;IAExD,OAAO,CACL,OAAO,KAAK,SAAS;QACrB,QAAQ,IAAI,IAAI;QAChB,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;QAC1C,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;QAClC,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,SAAS,CACzC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kCAAkC,CAAC,KAOxD;IACC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACxC,MAAM,KAAK,CAAC,EAAE;SACX,MAAM,CAAC,4BAA4B,CAAC;SACpC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;SAC3B,kBAAkB,CAAC;QAClB,MAAM,EAAE,4BAA4B,CAAC,WAAW;QAChD,GAAG,EAAE;YACH,UAAU,EAAE,GAAG,CAAA,GAAG,4BAA4B,CAAC,UAAU,MAAM;YAC/D,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,GAAG,CAAA;iBACF,4BAA4B,CAAC,MAAM;;YAExC;YACJ,SAAS,EAAE,GAAG,CAAA,OAAO,4BAA4B,CAAC,SAAS,wBAAwB;SACpF;KACF,CAAC;SACD,GAAG,EAAE,CAAC;AACX,CAAC;AAED,KAAK,UAAU,0BAA0B,CACvC,EAAgC,EAChC,KAA6E;IAE7E,MAAM,QAAQ,GAAG,KAAK,CAAC,2BAA2B,CAAC;IACnD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,OAAO,GAAG,MAAM,kCAAkC,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IACvE,OAAO,CACL,QAAQ,CAAC,UAAU,KAAK,WAAW;QACnC,QAAQ,CAAC,QAAQ,KAAK,KAAK,CAAC,SAAS,CAAC,EAAE;QACxC,QAAQ,CAAC,cAAc,KAAK,KAAK,CAAC,SAAS,CAAC,cAAc;QAC1D,OAAO,EAAE,KAAK,KAAK,QAAQ,CAAC,KAAK;QACjC,OAAO,CAAC,YAAY,KAAK,QAAQ,CAAC,YAAY,CAC/C,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,KAAK,UAAU,sBAAsB,CACnC,EAAgC,EAChC,KAA6E;IAE7E,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,EAAE;SACpB,MAAM,EAAE;SACR,IAAI,CAAC,oBAAoB,CAAC;SAC1B,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,oBAAoB,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,EACxD,OAAO,CAAC,oBAAoB,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CACjE,CACF;SACA,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,kCAAkC,CAAC;QACxC,SAAS,EAAE,IAAI;YACb,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE;YAC1D,CAAC,CAAC,KAAK,CAAC,SAAS;QACnB,cAAc,EACZ,IAAI,EAAE,SAAS;YACf,KAAK,CAAC,WAAW,CAAC,cAAc;YAChC,KAAK,CAAC,eAAe;QACvB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,gBAAgB,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB;QACpD,EAAE;KACH,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,yBAAyB,CACtC,OAAgB,EAChB,WAAmB;IAEnB,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,iCAAiC,CAAC,OAAO,CAAC;SAC3D,EAAE,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,wBAAwB,CAAC,cAAc,EAAE,CAAC;SACtE,IAAI,CAAC,wBAAwB,CAAC;SAC9B,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;SAC5D,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,GAAG,EAAE,cAAc,IAAI,IAAI,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,OAAgB,EAChB,WAAmB;IAEnB,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,6BAA6B,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACzE,IAAI,SAAS;QAAE,OAAO,oBAAoB,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IAClE,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,4EAA4E;IAC5E,uDAAuD;IACvD,MAAM,qBAAqB,GAAG,MAAM,yBAAyB,CAC3D,OAAO,EACP,WAAW,CACZ,CAAC;IACF,OAAO;QACL,cAAc,EAAE,qBAAqB,IAAI,EAAE;QAC3C,kBAAkB,EAAE,MAAM,CAAC,UAAU;KACtC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAEI,EACJ,KAA6E;IAE7E,OAAO,wBAAwB,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;QAC/D,MAAM,OAAO,GAAG,iCAAiC,CAAC,aAAa,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,KAAK,EAAE,EAAgC,EAAE,EAAE;YACxD,MAAM,kBAAkB,GAAG,MAAM,EAAE;iBAChC,MAAM,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC;iBAC7B,IAAI,CAAC,UAAU,CAAC;iBAChB,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;iBAC5C,KAAK,CAAC,CAAC,CAAC,CAAC;YACZ,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,EAAE,SAAS,EAAE,KAAc,EAAE,CAAC;YACxE,IAAI,CAAC,CAAC,MAAM,0BAA0B,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;gBAChD,OAAO,EAAE,SAAS,EAAE,KAAc,EAAE,CAAC;YAEvC,MAAM,MAAM,GAAG,MAAM,EAAE;iBACpB,MAAM,CAAC;gBACN,UAAU,EAAE,4BAA4B,CAAC,UAAU;gBACnD,MAAM,EAAE,4BAA4B,CAAC,MAAM;gBAC3C,SAAS,EAAE,4BAA4B,CAAC,SAAS;aAClD,CAAC;iBACD,IAAI,CAAC,4BAA4B,CAAC;iBAClC,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,4BAA4B,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,EAChE,EAAE,CAAC,4BAA4B,CAAC,OAAO,EAAE,KAAK,CAAC,CAChD,CACF;iBACA,KAAK,CAAC,CAAC,CAAC,CAAC;YACZ,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,oBAAoB,GAAG,MAAM,6BAA6B,CAC9D,aAAa,EACb,KAAK,CAAC,SAAS,CAAC,EAAE,CACnB,CAAC;YACF,IACE,KAAK;gBACL,CAAC,sBAAsB,CAAC,KAAK,EAAE,KAAK,CAAC,0BAA0B,CAAC,EAChE,CAAC;gBACD,OAAO,EAAE,SAAS,EAAE,KAAc,EAAE,CAAC;YACvC,CAAC;YACD,IACE,CAAC,kBAAkB,CAAC,oBAAoB,EAAE,KAAK,CAAC,qBAAqB,CAAC,EACtE,CAAC;gBACD,OAAO,EAAE,SAAS,EAAE,KAAc,EAAE,CAAC;YACvC,CAAC;YACD,IACE,MAAM,4BAA4B,CAAC,aAAa,EAAE;gBAChD,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE;gBAC/B,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc;aAC/C,CAAC,EACF,CAAC;gBACD,MAAM,IAAI,uBAAuB,CAC/B,iBAAiB,EACjB,oDAAoD,CACrD,CAAC;YACJ,CAAC;YACD,wEAAwE;YACxE,wEAAwE;YACxE,MAAM,qBAAqB,GAAG,MAAM,yBAAyB,CAC3D,aAAa,EACb,KAAK,CAAC,SAAS,CAAC,EAAE,CACnB,CAAC;YACF,0BAA0B,CACxB;gBACE,cAAc,EAAE,qBAAqB,IAAI,EAAE;gBAC3C,kBAAkB,EAAE,oBAAoB,EAAE,UAAU,IAAI,IAAI;aAC7D,EACD;gBACE,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc;gBAC9C,mBAAmB,EAAE;oBACnB,KAAK,CAAC,SAAS,CAAC,kBAAkB;oBAClC,KAAK,CAAC,MAAM,CAAC,UAAU;iBACxB;aACF,CACF,CAAC;YAEF,MAAM,SAAS,GAAG,MAAM,sBAAsB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC1D,MAAM,EAAE;iBACL,MAAM,CAAC,4BAA4B,CAAC;iBACpC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;iBACtB,KAAK,CAAC,EAAE,CAAC,4BAA4B,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;iBACvE,GAAG,EAAE,CAAC;YACT,OAAO,EAAE,SAAS,EAAE,IAAa,EAAE,SAAS,EAAE,CAAC;QACjD,CAAC,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;YACxB,OAAO,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,kBAAkB,CAC9C,MAAM,EACN,KAAK,CAAC,YAAY,EAClB,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CAAC;QACF,OAAO,OAAO,CAAC,MAAM,IAAI,EAAE,SAAS,EAAE,KAAc,EAAE,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { and, eq, inArray, sql } from \"drizzle-orm\";\nimport {\n assertHeldContainerBinding,\n type HeldContainerBinding,\n heldContainerBinding,\n} from \"../../containers/containerBinding\";\nimport { documentSyncPullContinuationsEqual } from \"../../documents/shared/pullContinuation\";\nimport {\n containerHydrationTombstones,\n containerMoveIntents,\n containers,\n dormantContainerMetadata,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport { loadContainerById } from \"../containers/containerPersistence\";\nimport { loadStoredAccessManifestCheckpoint } from \"../keyingCheckpointPersistence\";\nimport type {\n ContainerContentsPersistence,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n} from \"./containerContentsPersistenceTypes\";\nimport {\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\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 .from(containerMoveIntents)\n .where(\n and(\n eq(containerMoveIntents.containerId, input.container.id),\n inArray(containerMoveIntents.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 {\n assertHeldContainerBinding,\n type HeldContainerBinding,\n heldContainerBinding,\n} from \"../../containers/containerBinding\";\nimport { documentSyncPullContinuationsEqual } from \"../../documents/shared/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 { loadContainerById } from \"../containers/containerPersistence\";\nimport { loadStoredAccessManifestCheckpoint } from \"../keyingCheckpointPersistence\";\nimport type {\n ContainerContentsPersistence,\n ContainerHydrationTombstone,\n ContainerMetadataRecord,\n} from \"./containerContentsPersistenceTypes\";\nimport {\n saveContainerContentsContainerRows,\n selectContainerMetadataRecord,\n} from \"./containerMetadataRows\";\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,5 +1,5 @@
|
|
|
1
1
|
import { and, asc, eq, inArray } from "drizzle-orm";
|
|
2
|
-
import {
|
|
2
|
+
import { inLocalIntentLane, LOCAL_INTENT_LANES, LOCAL_INTENT_SUBJECT, localIntents, } from "../../sqlite/schema.js";
|
|
3
3
|
import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
|
|
4
4
|
import { CONTAINER_CREATE_INTENT_TYPE, CONTAINER_MOVE_INTENT_TYPE, } from "./containerContentsPersistenceTypes.js";
|
|
5
5
|
import { settleContainerCreateIntentRevision } from "./containerCreateIntentSettlement.js";
|
|
@@ -43,11 +43,12 @@ export async function saveContainerCreateIntent(input) {
|
|
|
43
43
|
const { containerId, createIntent, tx, updatedAt } = input;
|
|
44
44
|
const id = createIntent.id ?? crypto.randomUUID();
|
|
45
45
|
await tx
|
|
46
|
-
.insert(
|
|
46
|
+
.insert(localIntents)
|
|
47
47
|
.values({
|
|
48
48
|
id,
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
lane: LOCAL_INTENT_LANES.containerCreate,
|
|
50
|
+
subjectId: containerId,
|
|
51
|
+
targetContainerId: createIntent.parentContainerId,
|
|
51
52
|
intentType: CONTAINER_CREATE_INTENT_TYPE,
|
|
52
53
|
syncStatus: "pending",
|
|
53
54
|
remoteContainerId: null,
|
|
@@ -59,12 +60,12 @@ export async function saveContainerCreateIntent(input) {
|
|
|
59
60
|
updatedAt,
|
|
60
61
|
})
|
|
61
62
|
.onConflictDoUpdate({
|
|
62
|
-
target:
|
|
63
|
+
target: LOCAL_INTENT_SUBJECT,
|
|
63
64
|
set: {
|
|
64
65
|
// Timestamps can collide when a create is re-queued in one clock tick,
|
|
65
66
|
// so every enqueue owns a fresh revision token too.
|
|
66
67
|
id,
|
|
67
|
-
|
|
68
|
+
targetContainerId: createIntent.parentContainerId,
|
|
68
69
|
intentType: CONTAINER_CREATE_INTENT_TYPE,
|
|
69
70
|
syncStatus: "pending",
|
|
70
71
|
remoteContainerId: null,
|
|
@@ -84,9 +85,9 @@ export class ContainerCreateIntentSupersededError extends Error {
|
|
|
84
85
|
}
|
|
85
86
|
export async function deleteContainerMoveIntentRevision(input) {
|
|
86
87
|
const deleted = await input.tx
|
|
87
|
-
.delete(
|
|
88
|
-
.where(and(eq(
|
|
89
|
-
.returning({ containerId:
|
|
88
|
+
.delete(localIntents)
|
|
89
|
+
.where(and(inLocalIntentLane(LOCAL_INTENT_LANES.containerMove), eq(localIntents.subjectId, input.containerId), eq(localIntents.intentType, CONTAINER_MOVE_INTENT_TYPE), eq(localIntents.id, input.expectedIntentId), eq(localIntents.updatedAt, input.expectedUpdatedAt)))
|
|
90
|
+
.returning({ containerId: localIntents.subjectId });
|
|
90
91
|
return deleted.length > 0;
|
|
91
92
|
}
|
|
92
93
|
async function recordCreateIntentErrorAttempt(execSql, input) {
|
|
@@ -97,13 +98,13 @@ async function recordCreateIntentErrorAttempt(execSql, input) {
|
|
|
97
98
|
const record = async (tx) => {
|
|
98
99
|
const updatedAt = new Date().toISOString();
|
|
99
100
|
await tx
|
|
100
|
-
.update(
|
|
101
|
+
.update(localIntents)
|
|
101
102
|
.set({
|
|
102
103
|
lastAttemptedAt: updatedAt,
|
|
103
104
|
lastError: message,
|
|
104
105
|
updatedAt,
|
|
105
106
|
})
|
|
106
|
-
.where(and(eq(
|
|
107
|
+
.where(and(inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate), eq(localIntents.subjectId, containerId), eq(localIntents.syncStatus, "pending"), eq(localIntents.intentType, CONTAINER_CREATE_INTENT_TYPE), eq(localIntents.id, expectedIntentId), eq(localIntents.updatedAt, expectedUpdatedAt)))
|
|
107
108
|
.run();
|
|
108
109
|
};
|
|
109
110
|
if (stillCurrent) {
|
|
@@ -129,21 +130,21 @@ export const containerIntentPersistence = {
|
|
|
129
130
|
const { db } = getClientSQLitePersistenceRuntime(execSql);
|
|
130
131
|
const rows = await db
|
|
131
132
|
.select({
|
|
132
|
-
id:
|
|
133
|
-
containerId:
|
|
134
|
-
parentContainerId:
|
|
135
|
-
syncStatus:
|
|
136
|
-
remoteContainerId:
|
|
137
|
-
remoteMetadataDocumentId:
|
|
138
|
-
remoteMetadataAccessStateHash:
|
|
139
|
-
lastError:
|
|
140
|
-
lastAttemptedAt:
|
|
141
|
-
createdAt:
|
|
142
|
-
updatedAt:
|
|
133
|
+
id: localIntents.id,
|
|
134
|
+
containerId: localIntents.subjectId,
|
|
135
|
+
parentContainerId: localIntents.targetContainerId,
|
|
136
|
+
syncStatus: localIntents.syncStatus,
|
|
137
|
+
remoteContainerId: localIntents.remoteContainerId,
|
|
138
|
+
remoteMetadataDocumentId: localIntents.remoteMetadataDocumentId,
|
|
139
|
+
remoteMetadataAccessStateHash: localIntents.remoteMetadataAccessStateHash,
|
|
140
|
+
lastError: localIntents.lastError,
|
|
141
|
+
lastAttemptedAt: localIntents.lastAttemptedAt,
|
|
142
|
+
createdAt: localIntents.createdAt,
|
|
143
|
+
updatedAt: localIntents.updatedAt,
|
|
143
144
|
})
|
|
144
|
-
.from(
|
|
145
|
-
.where(and(eq(
|
|
146
|
-
.orderBy(asc(
|
|
145
|
+
.from(localIntents)
|
|
146
|
+
.where(and(inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate), eq(localIntents.syncStatus, "pending"), eq(localIntents.intentType, CONTAINER_CREATE_INTENT_TYPE)))
|
|
147
|
+
.orderBy(asc(localIntents.createdAt));
|
|
147
148
|
return rows.map((row) => mapContainerCreateIntentRecord(row));
|
|
148
149
|
},
|
|
149
150
|
async listUnsyncedMoveIntents(execSql) {
|
|
@@ -155,19 +156,19 @@ export const containerIntentPersistence = {
|
|
|
155
156
|
// the move completes — and hydration must still not revert them.
|
|
156
157
|
const rows = await db
|
|
157
158
|
.select({
|
|
158
|
-
id:
|
|
159
|
-
containerId:
|
|
160
|
-
parentContainerId:
|
|
161
|
-
previousParentContainerId:
|
|
162
|
-
syncStatus:
|
|
163
|
-
lastError:
|
|
164
|
-
lastAttemptedAt:
|
|
165
|
-
createdAt:
|
|
166
|
-
updatedAt:
|
|
159
|
+
id: localIntents.id,
|
|
160
|
+
containerId: localIntents.subjectId,
|
|
161
|
+
parentContainerId: localIntents.targetContainerId,
|
|
162
|
+
previousParentContainerId: localIntents.sourceContainerId,
|
|
163
|
+
syncStatus: localIntents.syncStatus,
|
|
164
|
+
lastError: localIntents.lastError,
|
|
165
|
+
lastAttemptedAt: localIntents.lastAttemptedAt,
|
|
166
|
+
createdAt: localIntents.createdAt,
|
|
167
|
+
updatedAt: localIntents.updatedAt,
|
|
167
168
|
})
|
|
168
|
-
.from(
|
|
169
|
-
.where(eq(
|
|
170
|
-
.orderBy(asc(
|
|
169
|
+
.from(localIntents)
|
|
170
|
+
.where(and(inLocalIntentLane(LOCAL_INTENT_LANES.containerMove), eq(localIntents.intentType, CONTAINER_MOVE_INTENT_TYPE)))
|
|
171
|
+
.orderBy(asc(localIntents.createdAt));
|
|
171
172
|
return rows.map((row) => mapContainerMoveIntentRecord(row));
|
|
172
173
|
},
|
|
173
174
|
recordCreateIntentRevisionError: (execSql, input) => recordCreateIntentErrorAttempt(execSql, input),
|
|
@@ -176,20 +177,20 @@ export const containerIntentPersistence = {
|
|
|
176
177
|
const record = async (tx) => {
|
|
177
178
|
const updatedAt = new Date().toISOString();
|
|
178
179
|
await tx
|
|
179
|
-
.update(
|
|
180
|
+
.update(localIntents)
|
|
180
181
|
.set({
|
|
181
182
|
lastAttemptedAt: updatedAt,
|
|
182
183
|
lastError: input.message,
|
|
183
184
|
syncStatus: input.blocked ? "blocked" : "pending",
|
|
184
185
|
updatedAt,
|
|
185
186
|
})
|
|
186
|
-
.where(and(eq(
|
|
187
|
+
.where(and(inLocalIntentLane(LOCAL_INTENT_LANES.containerMove), eq(localIntents.subjectId, input.containerId),
|
|
187
188
|
// Blocked rows stay updatable so a retried intent records its
|
|
188
189
|
// fresh outcome and a transient failure unblocks it.
|
|
189
|
-
inArray(
|
|
190
|
-
? [eq(
|
|
190
|
+
inArray(localIntents.syncStatus, ["pending", "blocked"]), eq(localIntents.intentType, CONTAINER_MOVE_INTENT_TYPE), ...(input.expectedIntentId
|
|
191
|
+
? [eq(localIntents.id, input.expectedIntentId)]
|
|
191
192
|
: []), ...(input.expectedUpdatedAt
|
|
192
|
-
? [eq(
|
|
193
|
+
? [eq(localIntents.updatedAt, input.expectedUpdatedAt)]
|
|
193
194
|
: [])))
|
|
194
195
|
.run();
|
|
195
196
|
};
|