@tearleads/client-sdk 0.1.66 → 0.1.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/package.json +1 -1
  2. package/stores/container-contents/syncAgent/acceptedMoveRetry.js +40 -0
  3. package/stores/container-contents/syncAgent/acceptedMoveRetry.js.map +1 -0
  4. package/stores/container-contents/syncAgent/syncLaneIteration.js +9 -2
  5. package/stores/container-contents/syncAgent/syncLaneIteration.js.map +1 -1
  6. package/workflows/container-contents/container-state/move/acceptedMoveSettlement.js +59 -0
  7. package/workflows/container-contents/container-state/move/acceptedMoveSettlement.js.map +1 -0
  8. package/workflows/container-contents/container-state/{moveIntentSync.js → move/moveIntentSync.js} +71 -71
  9. package/workflows/container-contents/container-state/move/moveIntentSync.js.map +1 -0
  10. package/workflows/container-contents/container-state/remote.js +22 -19
  11. package/workflows/container-contents/container-state/remote.js.map +1 -1
  12. package/workflows/container-contents/container-state/types.js.map +1 -1
  13. package/workflows/containers/child/move/index.d.ts +24 -3
  14. package/workflows/containers/child/move/index.js +62 -71
  15. package/workflows/containers/child/move/index.js.map +1 -1
  16. package/workflows/containers/child/move/moveRequest.js +47 -0
  17. package/workflows/containers/child/move/moveRequest.js.map +1 -0
  18. package/workflows/containers/child/mutationSubmit.js +2 -3
  19. package/workflows/containers/child/mutationSubmit.js.map +1 -1
  20. package/workflows/containers/index.d.ts +1 -1
  21. package/workflows/containers/index.js +1 -1
  22. package/workflows/containers/index.js.map +1 -1
  23. package/workflows/container-contents/container-state/moveIntentSync.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tearleads/client-sdk",
3
- "version": "0.1.66",
3
+ "version": "0.1.67",
4
4
  "description": "React-free Tearleads client runtime: local SQLite persistence, keys, sync, encrypted blobs, and domain workflows.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -0,0 +1,40 @@
1
+ /** Delays before each re-armed replay of one accepted but unsettled move. */
2
+ const ACCEPTED_MOVE_RETRY_DELAYS_MS = [1_000, 5_000, 30_000];
3
+ const attemptsByState = new WeakMap();
4
+ /**
5
+ * A replay settles an accepted move from its verified head, so one pass
6
+ * normally suffices. The budget is the intent's, across passes: a replay that
7
+ * fails before reaching the server, say on a projection read, resumes it. A
8
+ * failure that repeats past it waits for an unrelated pass, as before, instead
9
+ * of re-arming the lane indefinitely; each attempt has already been reported.
10
+ * One pass schedules each intent at most once.
11
+ */
12
+ export function createAcceptedMoveRetry(state) {
13
+ const syncLane = state.syncLane;
14
+ let attempts = attemptsByState.get(state);
15
+ if (!attempts) {
16
+ attempts = new Map();
17
+ attemptsByState.set(state, attempts);
18
+ }
19
+ const intentAttempts = attempts;
20
+ const scheduledThisPass = new Set();
21
+ const schedule = (intentId) => {
22
+ if (scheduledThisPass.has(intentId))
23
+ return;
24
+ scheduledThisPass.add(intentId);
25
+ const attempt = intentAttempts.get(intentId) ?? 0;
26
+ const delayMs = ACCEPTED_MOVE_RETRY_DELAYS_MS[attempt];
27
+ if (delayMs === undefined)
28
+ return;
29
+ intentAttempts.set(intentId, attempt + 1);
30
+ syncLane?.requestSyncAfter(delayMs);
31
+ };
32
+ return {
33
+ arm: schedule,
34
+ resume: (intentId) => {
35
+ if (intentAttempts.has(intentId))
36
+ schedule(intentId);
37
+ },
38
+ };
39
+ }
40
+ //# sourceMappingURL=acceptedMoveRetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acceptedMoveRetry.js","sourceRoot":"","sources":["acceptedMoveRetry.ts"],"names":[],"mappings":"AAGA,6EAA6E;AAC7E,MAAM,6BAA6B,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAE7D,MAAM,eAAe,GAAG,IAAI,OAAO,EAGhC,CAAC;AAEJ;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAwD;IAExD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAChC,IAAI,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;QACrB,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,cAAc,GAAG,QAAQ,CAAC;IAChC,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC5C,MAAM,QAAQ,GAAG,CAAC,QAAgB,EAAE,EAAE;QACpC,IAAI,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO;QAC5C,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChC,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,6BAA6B,CAAC,OAAO,CAAC,CAAC;QACvD,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;QAC1C,QAAQ,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC,CAAC;IACF,OAAO;QACL,GAAG,EAAE,QAAQ;QACb,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE;YACnB,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACvD,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { AcceptedMoveRetry } from \"../../../workflows/container-contents/container-state/types\";\nimport type { ContainerContentsStoreSyncState } from \"./syncAgentTypes\";\n\n/** Delays before each re-armed replay of one accepted but unsettled move. */\nconst ACCEPTED_MOVE_RETRY_DELAYS_MS = [1_000, 5_000, 30_000];\n\nconst attemptsByState = new WeakMap<\n Pick<ContainerContentsStoreSyncState, \"syncLane\">,\n Map<string, number>\n>();\n\n/**\n * A replay settles an accepted move from its verified head, so one pass\n * normally suffices. The budget is the intent's, across passes: a replay that\n * fails before reaching the server, say on a projection read, resumes it. A\n * failure that repeats past it waits for an unrelated pass, as before, instead\n * of re-arming the lane indefinitely; each attempt has already been reported.\n * One pass schedules each intent at most once.\n */\nexport function createAcceptedMoveRetry(\n state: Pick<ContainerContentsStoreSyncState, \"syncLane\">,\n): AcceptedMoveRetry {\n const syncLane = state.syncLane;\n let attempts = attemptsByState.get(state);\n if (!attempts) {\n attempts = new Map();\n attemptsByState.set(state, attempts);\n }\n const intentAttempts = attempts;\n const scheduledThisPass = new Set<string>();\n const schedule = (intentId: string) => {\n if (scheduledThisPass.has(intentId)) return;\n scheduledThisPass.add(intentId);\n const attempt = intentAttempts.get(intentId) ?? 0;\n const delayMs = ACCEPTED_MOVE_RETRY_DELAYS_MS[attempt];\n if (delayMs === undefined) return;\n intentAttempts.set(intentId, attempt + 1);\n syncLane?.requestSyncAfter(delayMs);\n };\n return {\n arm: schedule,\n resume: (intentId) => {\n if (intentAttempts.has(intentId)) schedule(intentId);\n },\n };\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { syncPendingContainerCreateIntents } from "../../../workflows/container-contents/container-state/create/createIntentSync.js";
2
- import { syncPendingContainerMoveIntents } from "../../../workflows/container-contents/container-state/moveIntentSync.js";
2
+ import { syncPendingContainerMoveIntents } from "../../../workflows/container-contents/container-state/move/moveIntentSync.js";
3
3
  import { syncPendingDocumentMoveIntents, } from "../../../workflows/container-contents/documentMove/documentMoveIntentSync.js";
4
4
  import { syncContainerMetadataState } from "../../../workflows/container-contents/metadata/index.js";
5
5
  import { createContainerContentsDocumentsRuntime } from "../../../workflows/container-contents/runtime.js";
@@ -9,6 +9,7 @@ import { DocumentRemoteWorkBusyError } from "../../documents/documentStore/sync/
9
9
  import { removeMissingSyncedContainerState } from "../containerState/missingSyncedContainerState.js";
10
10
  import { runContainerDocumentWork } from "../documentWork/index.js";
11
11
  import { primeStoreDocuments, recoverStoreStaleRoot, } from "../documentWork/documentRecovery.js";
12
+ import { createAcceptedMoveRetry } from "./acceptedMoveRetry.js";
12
13
  import { bumpMetadataSyncSeq, clearMetadataSyncQueueIfUnchanged, readMetadataSyncSeq, } from "./metadataSyncSignal.js";
13
14
  function requestContainerContentsStoreSync(state) {
14
15
  state.syncLane?.requestSync();
@@ -71,6 +72,12 @@ function syncPendingStoreDocumentMoves(input) {
71
72
  state: input.state,
72
73
  });
73
74
  }
75
+ function syncPendingStoreContainerMoves(input) {
76
+ return syncPendingContainerMoveIntents({
77
+ ...input,
78
+ acceptedMoveRetry: createAcceptedMoveRetry(input.state),
79
+ });
80
+ }
74
81
  async function syncSingleContainerMetadata(input) {
75
82
  const { containerState, encapsulationKeyPair, host, isCurrent, state } = input;
76
83
  const metadataDocumentId = containerState.record.documentId;
@@ -171,7 +178,7 @@ export async function runContainerContentsStoreSyncIteration(input) {
171
178
  if (!isCurrent()) {
172
179
  return;
173
180
  }
174
- const movedContainerCount = await syncPendingContainerMoveIntents({
181
+ const movedContainerCount = await syncPendingStoreContainerMoves({
175
182
  host,
176
183
  isCreatePending,
177
184
  isCurrent,
@@ -1 +1 @@
1
- {"version":3,"file":"syncLaneIteration.js","sourceRoot":"","sources":["syncLaneIteration.ts"],"names":[],"mappings":"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","sourcesContent":["import { syncPendingContainerCreateIntents } from \"../../../workflows/container-contents/container-state/create/createIntentSync\";\nimport { syncPendingContainerMoveIntents } from \"../../../workflows/container-contents/container-state/moveIntentSync\";\nimport {\n type DocumentMoveIntentSyncHost,\n syncPendingDocumentMoveIntents,\n} from \"../../../workflows/container-contents/documentMove/documentMoveIntentSync\";\nimport { syncContainerMetadataState } from \"../../../workflows/container-contents/metadata\";\nimport type {\n ContainerState,\n RemoteContainerHydrationHost,\n} from \"../../../workflows/container-contents/remoteHydration\";\nimport { createContainerContentsDocumentsRuntime } from \"../../../workflows/container-contents/runtime\";\nimport { openDocumentStore, requestDomainDocumentSync } from \"../../documents\";\nimport { relinkDocumentStoreWithCommitSideEffect } from \"../../documents/documentStore/internalRelink\";\nimport { DocumentRemoteWorkBusyError } from \"../../documents/documentStore/sync/remoteWork\";\nimport { removeMissingSyncedContainerState } from \"../containerState/missingSyncedContainerState\";\nimport { runContainerDocumentWork } from \"../documentWork\";\nimport {\n primeStoreDocuments,\n recoverStoreStaleRoot,\n} from \"../documentWork/documentRecovery\";\nimport {\n bumpMetadataSyncSeq,\n clearMetadataSyncQueueIfUnchanged,\n readMetadataSyncSeq,\n} from \"./metadataSyncSignal\";\nimport type {\n ContainerContentsStoreRuntime,\n ContainerContentsStoreSyncState,\n} from \"./syncAgentTypes\";\n\ntype ContainerContentsStorePrimeDocumentRuntime = ReturnType<\n typeof createContainerContentsDocumentsRuntime\n>;\ntype SyncContainerMetadata = typeof syncContainerMetadataState;\n\nfunction requestContainerContentsStoreSync(\n state: ContainerContentsStoreSyncState,\n) {\n state.syncLane?.requestSync();\n}\n\nfunction createRemoteSyncBlocker(runtime: ContainerContentsStoreRuntime) {\n return (organizationId: string) =>\n runtime.util.isRemoteSyncBlocked?.(organizationId) ?? false;\n}\n\nfunction captureStructuralSyncGeneration(\n state: ContainerContentsStoreSyncState,\n runtime: ContainerContentsStoreRuntime,\n) {\n const activeContainerId = runtime.state.containerId;\n const activeOrganizationId = runtime.auth.organizationId;\n const lifecycleGeneration = state.lifecycleGeneration;\n const structuralGeneration = state.structuralGeneration;\n const domainScope = runtime.state.domainScope;\n const execSql = runtime.infra.execSql;\n const resolveProjectionUserKey = state.resolveProjectionUserKey;\n const syncLane = state.syncLane;\n return () =>\n state.lifecycleGeneration === lifecycleGeneration &&\n state.structuralGeneration === structuralGeneration &&\n state.runtime.auth.organizationId === activeOrganizationId &&\n state.runtime.state.containerId === activeContainerId &&\n state.runtime.state.domainScope === domainScope &&\n state.runtime.infra.execSql === execSql &&\n state.resolveProjectionUserKey === resolveProjectionUserKey &&\n state.syncLane === syncLane &&\n !syncLane?.isDisposed?.();\n}\n\nfunction createContainerContentsStoreDocumentMoveHost(\n state: ContainerContentsStoreSyncState,\n isCurrent: () => boolean,\n): DocumentMoveIntentSyncHost<ContainerContentsStorePrimeDocumentRuntime> {\n const runtime = state.runtime;\n const domainScope = runtime.state.domainScope;\n return {\n documentWorkflowRuntime: (containerId) =>\n createContainerContentsDocumentsRuntime(runtime, containerId),\n openDocumentStore: ({ containerId, documentId, localId }) => {\n const store = openDocumentStore(\n domainScope,\n localId,\n createContainerContentsDocumentsRuntime(runtime, containerId),\n documentId,\n );\n return {\n assertCanRotateContentKey: () =>\n store.assertCanRotateContentKey().catch((error: unknown) => {\n if (error instanceof DocumentRemoteWorkBusyError) {\n void error.whenIdle.then(() => {\n if (isCurrent()) requestContainerContentsStoreSync(state);\n });\n }\n throw error;\n }),\n ensureInitialized: () => store.ensureInitialized(),\n relink: ({ commitSideEffect, ...input }) =>\n commitSideEffect\n ? relinkDocumentStoreWithCommitSideEffect(\n store,\n input,\n commitSideEffect,\n )\n : store.relink(input),\n requestSync: () => store.requestSync(),\n updateRuntime: (nextRuntime) => store.updateRuntime(nextRuntime),\n };\n },\n };\n}\n\nfunction syncPendingStoreDocumentMoves(input: {\n isCreatePending: (containerId: string) => boolean;\n isCurrent: () => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n state: ContainerContentsStoreSyncState;\n}) {\n return syncPendingDocumentMoveIntents({\n host: createContainerContentsStoreDocumentMoveHost(\n input.state,\n input.isCurrent,\n ),\n isCreatePending: input.isCreatePending,\n isCurrent: input.isCurrent,\n isRemoteSyncBlocked: input.isRemoteSyncBlocked,\n state: input.state,\n });\n}\n\nasync function syncSingleContainerMetadata(input: {\n host: RemoteContainerHydrationHost;\n isCurrent: () => boolean;\n state: ContainerContentsStoreSyncState;\n containerState: ContainerState;\n encapsulationKeyPair: NonNullable<\n ContainerContentsStoreRuntime[\"crypto\"][\"encapsulationKeyPair\"]\n >;\n syncContainerMetadata?: SyncContainerMetadata | undefined;\n}) {\n const { containerState, encapsulationKeyPair, host, isCurrent, state } =\n input;\n const metadataDocumentId = containerState.record.documentId;\n // Snapshot this signal before the GET so a mid-pass event survives clearing.\n const consumedSeqById = new Map<string, number>();\n if (typeof metadataDocumentId === \"string\") {\n consumedSeqById.set(\n metadataDocumentId,\n readMetadataSyncSeq(state.metadataSyncSignalSeqById, metadataDocumentId),\n );\n }\n const synced = await (\n input.syncContainerMetadata ?? syncContainerMetadataState\n )({\n forceReadSync:\n typeof metadataDocumentId === \"string\" &&\n state.metadataDocumentIdsNeedingSync.has(metadataDocumentId),\n locallyAcceptedUpdateIds: state.locallyAcceptedMetadataUpdateIds,\n isCurrent,\n metadataState: containerState,\n onDurableStateNeedsReload: () => {\n const currentDocumentId = state.containersById.get(\n containerState.container.id,\n )?.record.documentId;\n if (typeof currentDocumentId === \"string\") {\n state.metadataDocumentIdsNeedingSync.add(currentDocumentId);\n bumpMetadataSyncSeq(state.metadataSyncSignalSeqById, currentDocumentId);\n }\n requestContainerContentsStoreSync(state);\n },\n persistence: state.persistence,\n resolveProjectionUserKey: state.resolveProjectionUserKey,\n runtime: state.runtime,\n targetSecretKey: encapsulationKeyPair.secretKey,\n });\n if (!isCurrent() || !synced) {\n return;\n }\n if (\"missing\" in synced) {\n removeMissingSyncedContainerState(\n state,\n containerState,\n host.updateSnapshot,\n );\n return;\n }\n\n for (const id of [metadataDocumentId, synced.record.documentId]) {\n if (typeof id === \"string\") {\n clearMetadataSyncQueueIfUnchanged({\n consumedSeqById,\n id,\n needingSync: state.metadataDocumentIdsNeedingSync,\n seqById: state.metadataSyncSignalSeqById,\n });\n }\n }\n // syncContainerMetadataState settles its detached candidate into this live\n // object while preserving any metadata edit that completed or queued during\n // the remote pass. Do not install its returned detached record again here:\n // doing so would overwrite that preserved live edit.\n host.updateSnapshot();\n\n if (synced.shouldRequestFollowupSync) {\n requestContainerContentsStoreSync(state);\n }\n}\n\n/**\n * A folder whose create has not settled can carry a listed identity that\n * adoption has not verified, or has refused. Folder moves, document moves and\n * metadata edits wait for it; child and document creates wait in their own\n * create paths.\n */\nasync function loadPendingCreatePredicate(\n state: ContainerContentsStoreSyncState,\n): Promise<(containerId: string) => boolean> {\n const pending = await state.persistence.listPendingCreateIntents(\n state.runtime.infra.execSql,\n );\n const pendingIds = new Set(pending.map((intent) => intent.containerId));\n return (containerId) => pendingIds.has(containerId);\n}\n\ninterface ContainerContentsStoreSyncIterationInput {\n host: RemoteContainerHydrationHost;\n reconcileRestoredAccess: (isCurrent: () => boolean) => Promise<void>;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerContentsStoreSyncState;\n syncContainerMetadata?: SyncContainerMetadata | undefined;\n}\n\nexport async function runContainerContentsStoreSyncIteration(\n input: ContainerContentsStoreSyncIterationInput,\n) {\n const { host, reconcileRestoredAccess, requestRemoteReconciliation, state } =\n input;\n const runtime = state.runtime;\n const encapsulationKeyPair = runtime.crypto.encapsulationKeyPair;\n if (\n runtime.infra.dbStatus !== \"ready\" ||\n !state.snapshot.ready ||\n !runtime.state.online ||\n !runtime.auth.isAuthenticated ||\n !encapsulationKeyPair\n ) {\n return;\n }\n const domainScope = runtime.state.domainScope;\n const isCurrent = captureStructuralSyncGeneration(state, runtime);\n\n await reconcileRestoredAccess(isCurrent);\n if (!isCurrent()) {\n return;\n }\n const isOrganizationBlocked = createRemoteSyncBlocker(runtime);\n\n const createdContainerCount = await syncPendingContainerCreateIntents({\n host,\n isCurrent,\n isRemoteSyncBlocked: isOrganizationBlocked,\n requestRemoteReconciliation,\n state,\n });\n if (!isCurrent()) {\n return;\n }\n if (createdContainerCount > 0) {\n state.documentStoresNeedPriming = true;\n host.updateSnapshot();\n }\n const isCreatePending = await loadPendingCreatePredicate(state);\n if (!isCurrent()) {\n return;\n }\n\n const movedContainerCount = await syncPendingContainerMoveIntents({\n host,\n isCreatePending,\n isCurrent,\n isRemoteSyncBlocked: isOrganizationBlocked,\n requestRemoteReconciliation,\n state,\n });\n if (!isCurrent()) {\n return;\n }\n if (movedContainerCount > 0) {\n state.documentStoresNeedPriming = true;\n host.updateSnapshot();\n requestDomainDocumentSync(domainScope);\n requestContainerContentsStoreSync(state);\n }\n\n for (const containerState of Array.from(state.containersById.values())) {\n if (isCreatePending(containerState.container.id)) continue;\n await syncSingleContainerMetadata({\n containerState,\n encapsulationKeyPair,\n host,\n isCurrent,\n state,\n syncContainerMetadata: input.syncContainerMetadata,\n });\n if (!isCurrent()) {\n return;\n }\n }\n\n // Root adoption notifies session consumers synchronously. Let container\n // metadata converge first so a recovered system container is never exposed\n // under its placeholder name when the active root changes.\n await runContainerDocumentWork({\n isCurrent,\n onContextChanged: () => {\n if (isCurrent()) {\n requestContainerContentsStoreSync(state);\n }\n },\n onDocumentsMoved: () => {\n if (isCurrent()) {\n requestDomainDocumentSync(domainScope);\n requestContainerContentsStoreSync(state);\n }\n },\n primeDocuments: () => primeStoreDocuments(state, isCurrent),\n recoverStaleRoot: () => recoverStoreStaleRoot(state, isCurrent),\n shouldPrimeDocuments: () => state.documentStoresNeedPriming,\n // Document move intents live in the structural phase because they may\n // target containers created locally in the same session, such as Trash.\n // Root recovery rewrites stale endpoints and returns their intents to\n // pending, so replay follows recovery in this same pass.\n syncPendingDocumentMoves: () =>\n syncPendingStoreDocumentMoves({\n isCreatePending,\n isCurrent,\n isRemoteSyncBlocked: isOrganizationBlocked,\n state,\n }),\n });\n}\n"]}
1
+ {"version":3,"file":"syncLaneIteration.js","sourceRoot":"","sources":["syncLaneIteration.ts"],"names":[],"mappings":"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","sourcesContent":["import { syncPendingContainerCreateIntents } from \"../../../workflows/container-contents/container-state/create/createIntentSync\";\nimport { syncPendingContainerMoveIntents } from \"../../../workflows/container-contents/container-state/move/moveIntentSync\";\nimport {\n type DocumentMoveIntentSyncHost,\n syncPendingDocumentMoveIntents,\n} from \"../../../workflows/container-contents/documentMove/documentMoveIntentSync\";\nimport { syncContainerMetadataState } from \"../../../workflows/container-contents/metadata\";\nimport type {\n ContainerState,\n RemoteContainerHydrationHost,\n} from \"../../../workflows/container-contents/remoteHydration\";\nimport { createContainerContentsDocumentsRuntime } from \"../../../workflows/container-contents/runtime\";\nimport { openDocumentStore, requestDomainDocumentSync } from \"../../documents\";\nimport { relinkDocumentStoreWithCommitSideEffect } from \"../../documents/documentStore/internalRelink\";\nimport { DocumentRemoteWorkBusyError } from \"../../documents/documentStore/sync/remoteWork\";\nimport { removeMissingSyncedContainerState } from \"../containerState/missingSyncedContainerState\";\nimport { runContainerDocumentWork } from \"../documentWork\";\nimport {\n primeStoreDocuments,\n recoverStoreStaleRoot,\n} from \"../documentWork/documentRecovery\";\nimport { createAcceptedMoveRetry } from \"./acceptedMoveRetry\";\nimport {\n bumpMetadataSyncSeq,\n clearMetadataSyncQueueIfUnchanged,\n readMetadataSyncSeq,\n} from \"./metadataSyncSignal\";\nimport type {\n ContainerContentsStoreRuntime,\n ContainerContentsStoreSyncState,\n} from \"./syncAgentTypes\";\n\ntype ContainerContentsStorePrimeDocumentRuntime = ReturnType<\n typeof createContainerContentsDocumentsRuntime\n>;\ntype SyncContainerMetadata = typeof syncContainerMetadataState;\n\nfunction requestContainerContentsStoreSync(\n state: ContainerContentsStoreSyncState,\n) {\n state.syncLane?.requestSync();\n}\n\nfunction createRemoteSyncBlocker(runtime: ContainerContentsStoreRuntime) {\n return (organizationId: string) =>\n runtime.util.isRemoteSyncBlocked?.(organizationId) ?? false;\n}\n\nfunction captureStructuralSyncGeneration(\n state: ContainerContentsStoreSyncState,\n runtime: ContainerContentsStoreRuntime,\n) {\n const activeContainerId = runtime.state.containerId;\n const activeOrganizationId = runtime.auth.organizationId;\n const lifecycleGeneration = state.lifecycleGeneration;\n const structuralGeneration = state.structuralGeneration;\n const domainScope = runtime.state.domainScope;\n const execSql = runtime.infra.execSql;\n const resolveProjectionUserKey = state.resolveProjectionUserKey;\n const syncLane = state.syncLane;\n return () =>\n state.lifecycleGeneration === lifecycleGeneration &&\n state.structuralGeneration === structuralGeneration &&\n state.runtime.auth.organizationId === activeOrganizationId &&\n state.runtime.state.containerId === activeContainerId &&\n state.runtime.state.domainScope === domainScope &&\n state.runtime.infra.execSql === execSql &&\n state.resolveProjectionUserKey === resolveProjectionUserKey &&\n state.syncLane === syncLane &&\n !syncLane?.isDisposed?.();\n}\n\nfunction createContainerContentsStoreDocumentMoveHost(\n state: ContainerContentsStoreSyncState,\n isCurrent: () => boolean,\n): DocumentMoveIntentSyncHost<ContainerContentsStorePrimeDocumentRuntime> {\n const runtime = state.runtime;\n const domainScope = runtime.state.domainScope;\n return {\n documentWorkflowRuntime: (containerId) =>\n createContainerContentsDocumentsRuntime(runtime, containerId),\n openDocumentStore: ({ containerId, documentId, localId }) => {\n const store = openDocumentStore(\n domainScope,\n localId,\n createContainerContentsDocumentsRuntime(runtime, containerId),\n documentId,\n );\n return {\n assertCanRotateContentKey: () =>\n store.assertCanRotateContentKey().catch((error: unknown) => {\n if (error instanceof DocumentRemoteWorkBusyError) {\n void error.whenIdle.then(() => {\n if (isCurrent()) requestContainerContentsStoreSync(state);\n });\n }\n throw error;\n }),\n ensureInitialized: () => store.ensureInitialized(),\n relink: ({ commitSideEffect, ...input }) =>\n commitSideEffect\n ? relinkDocumentStoreWithCommitSideEffect(\n store,\n input,\n commitSideEffect,\n )\n : store.relink(input),\n requestSync: () => store.requestSync(),\n updateRuntime: (nextRuntime) => store.updateRuntime(nextRuntime),\n };\n },\n };\n}\n\nfunction syncPendingStoreDocumentMoves(input: {\n isCreatePending: (containerId: string) => boolean;\n isCurrent: () => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n state: ContainerContentsStoreSyncState;\n}) {\n return syncPendingDocumentMoveIntents({\n host: createContainerContentsStoreDocumentMoveHost(\n input.state,\n input.isCurrent,\n ),\n isCreatePending: input.isCreatePending,\n isCurrent: input.isCurrent,\n isRemoteSyncBlocked: input.isRemoteSyncBlocked,\n state: input.state,\n });\n}\n\nfunction syncPendingStoreContainerMoves(\n input: Omit<\n Parameters<typeof syncPendingContainerMoveIntents>[0],\n \"acceptedMoveRetry\" | \"state\"\n > & { state: ContainerContentsStoreSyncState },\n) {\n return syncPendingContainerMoveIntents({\n ...input,\n acceptedMoveRetry: createAcceptedMoveRetry(input.state),\n });\n}\n\nasync function syncSingleContainerMetadata(input: {\n host: RemoteContainerHydrationHost;\n isCurrent: () => boolean;\n state: ContainerContentsStoreSyncState;\n containerState: ContainerState;\n encapsulationKeyPair: NonNullable<\n ContainerContentsStoreRuntime[\"crypto\"][\"encapsulationKeyPair\"]\n >;\n syncContainerMetadata?: SyncContainerMetadata | undefined;\n}) {\n const { containerState, encapsulationKeyPair, host, isCurrent, state } =\n input;\n const metadataDocumentId = containerState.record.documentId;\n // Snapshot this signal before the GET so a mid-pass event survives clearing.\n const consumedSeqById = new Map<string, number>();\n if (typeof metadataDocumentId === \"string\") {\n consumedSeqById.set(\n metadataDocumentId,\n readMetadataSyncSeq(state.metadataSyncSignalSeqById, metadataDocumentId),\n );\n }\n const synced = await (\n input.syncContainerMetadata ?? syncContainerMetadataState\n )({\n forceReadSync:\n typeof metadataDocumentId === \"string\" &&\n state.metadataDocumentIdsNeedingSync.has(metadataDocumentId),\n locallyAcceptedUpdateIds: state.locallyAcceptedMetadataUpdateIds,\n isCurrent,\n metadataState: containerState,\n onDurableStateNeedsReload: () => {\n const currentDocumentId = state.containersById.get(\n containerState.container.id,\n )?.record.documentId;\n if (typeof currentDocumentId === \"string\") {\n state.metadataDocumentIdsNeedingSync.add(currentDocumentId);\n bumpMetadataSyncSeq(state.metadataSyncSignalSeqById, currentDocumentId);\n }\n requestContainerContentsStoreSync(state);\n },\n persistence: state.persistence,\n resolveProjectionUserKey: state.resolveProjectionUserKey,\n runtime: state.runtime,\n targetSecretKey: encapsulationKeyPair.secretKey,\n });\n if (!isCurrent() || !synced) {\n return;\n }\n if (\"missing\" in synced) {\n removeMissingSyncedContainerState(\n state,\n containerState,\n host.updateSnapshot,\n );\n return;\n }\n\n for (const id of [metadataDocumentId, synced.record.documentId]) {\n if (typeof id === \"string\") {\n clearMetadataSyncQueueIfUnchanged({\n consumedSeqById,\n id,\n needingSync: state.metadataDocumentIdsNeedingSync,\n seqById: state.metadataSyncSignalSeqById,\n });\n }\n }\n // syncContainerMetadataState settles its detached candidate into this live\n // object while preserving any metadata edit that completed or queued during\n // the remote pass. Do not install its returned detached record again here:\n // doing so would overwrite that preserved live edit.\n host.updateSnapshot();\n\n if (synced.shouldRequestFollowupSync) {\n requestContainerContentsStoreSync(state);\n }\n}\n\n/**\n * A folder whose create has not settled can carry a listed identity that\n * adoption has not verified, or has refused. Folder moves, document moves and\n * metadata edits wait for it; child and document creates wait in their own\n * create paths.\n */\nasync function loadPendingCreatePredicate(\n state: ContainerContentsStoreSyncState,\n): Promise<(containerId: string) => boolean> {\n const pending = await state.persistence.listPendingCreateIntents(\n state.runtime.infra.execSql,\n );\n const pendingIds = new Set(pending.map((intent) => intent.containerId));\n return (containerId) => pendingIds.has(containerId);\n}\n\ninterface ContainerContentsStoreSyncIterationInput {\n host: RemoteContainerHydrationHost;\n reconcileRestoredAccess: (isCurrent: () => boolean) => Promise<void>;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerContentsStoreSyncState;\n syncContainerMetadata?: SyncContainerMetadata | undefined;\n}\n\nexport async function runContainerContentsStoreSyncIteration(\n input: ContainerContentsStoreSyncIterationInput,\n) {\n const { host, reconcileRestoredAccess, requestRemoteReconciliation, state } =\n input;\n const runtime = state.runtime;\n const encapsulationKeyPair = runtime.crypto.encapsulationKeyPair;\n if (\n runtime.infra.dbStatus !== \"ready\" ||\n !state.snapshot.ready ||\n !runtime.state.online ||\n !runtime.auth.isAuthenticated ||\n !encapsulationKeyPair\n ) {\n return;\n }\n const domainScope = runtime.state.domainScope;\n const isCurrent = captureStructuralSyncGeneration(state, runtime);\n\n await reconcileRestoredAccess(isCurrent);\n if (!isCurrent()) {\n return;\n }\n const isOrganizationBlocked = createRemoteSyncBlocker(runtime);\n\n const createdContainerCount = await syncPendingContainerCreateIntents({\n host,\n isCurrent,\n isRemoteSyncBlocked: isOrganizationBlocked,\n requestRemoteReconciliation,\n state,\n });\n if (!isCurrent()) {\n return;\n }\n if (createdContainerCount > 0) {\n state.documentStoresNeedPriming = true;\n host.updateSnapshot();\n }\n const isCreatePending = await loadPendingCreatePredicate(state);\n if (!isCurrent()) {\n return;\n }\n\n const movedContainerCount = await syncPendingStoreContainerMoves({\n host,\n isCreatePending,\n isCurrent,\n isRemoteSyncBlocked: isOrganizationBlocked,\n requestRemoteReconciliation,\n state,\n });\n if (!isCurrent()) {\n return;\n }\n if (movedContainerCount > 0) {\n state.documentStoresNeedPriming = true;\n host.updateSnapshot();\n requestDomainDocumentSync(domainScope);\n requestContainerContentsStoreSync(state);\n }\n\n for (const containerState of Array.from(state.containersById.values())) {\n if (isCreatePending(containerState.container.id)) continue;\n await syncSingleContainerMetadata({\n containerState,\n encapsulationKeyPair,\n host,\n isCurrent,\n state,\n syncContainerMetadata: input.syncContainerMetadata,\n });\n if (!isCurrent()) {\n return;\n }\n }\n\n // Root adoption notifies session consumers synchronously. Let container\n // metadata converge first so a recovered system container is never exposed\n // under its placeholder name when the active root changes.\n await runContainerDocumentWork({\n isCurrent,\n onContextChanged: () => {\n if (isCurrent()) {\n requestContainerContentsStoreSync(state);\n }\n },\n onDocumentsMoved: () => {\n if (isCurrent()) {\n requestDomainDocumentSync(domainScope);\n requestContainerContentsStoreSync(state);\n }\n },\n primeDocuments: () => primeStoreDocuments(state, isCurrent),\n recoverStaleRoot: () => recoverStoreStaleRoot(state, isCurrent),\n shouldPrimeDocuments: () => state.documentStoresNeedPriming,\n // Document move intents live in the structural phase because they may\n // target containers created locally in the same session, such as Trash.\n // Root recovery rewrites stale endpoints and returns their intents to\n // pending, so replay follows recovery in this same pass.\n syncPendingDocumentMoves: () =>\n syncPendingStoreDocumentMoves({\n isCreatePending,\n isCurrent,\n isRemoteSyncBlocked: isOrganizationBlocked,\n state,\n }),\n });\n}\n"]}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The server holds a move this pass did not settle. Keep the intent queued,
3
+ * report it with a fixed message (the error's call site names the path), and
4
+ * ask for a later pass: its replay finds the move applied and settles it
5
+ * without posting again, so no reload or unrelated write is needed.
6
+ */
7
+ export function leaveAcceptedMoveQueued(syncInput, message, error) {
8
+ try {
9
+ const report = syncInput.state.runtime.util.logError;
10
+ if (report)
11
+ void Promise.resolve(report(message, error)).catch(() => { });
12
+ }
13
+ catch {
14
+ // A host logger must not change the replay result.
15
+ }
16
+ syncInput.acceptedMoveRetry.arm(syncInput.intent.id);
17
+ }
18
+ export const STALE_ACCEPTED_MOVE = "Container contents: a stale sync pass dropped an accepted container move";
19
+ /** persistAcceptedMoveIntent already asked to reconcile a stale result. */
20
+ export function unpersistedAcceptedMove(syncInput) {
21
+ if (!syncInput.isCurrent()) {
22
+ leaveAcceptedMoveQueued(syncInput, STALE_ACCEPTED_MOVE, new Error("Stale pass dropped a persisting container move"));
23
+ return "abandoned";
24
+ }
25
+ leaveAcceptedMoveQueued(syncInput, "Container contents: an accepted container move did not persist", new Error("Accepted container move did not persist"));
26
+ return "failed";
27
+ }
28
+ /** The server accepted, but this current pass got no result to settle. */
29
+ export function unacknowledgedAcceptedMove(syncInput) {
30
+ leaveAcceptedMoveQueued(syncInput, "Container contents: an accepted container move was not acknowledged", new Error("Accepted container move was not acknowledged"));
31
+ return "failed";
32
+ }
33
+ /**
34
+ * Settles from the verified head the server already holds. A head carries no
35
+ * server timestamps, so the container keeps the ones this device holds.
36
+ */
37
+ export function appliedRemoteContainer(containerState, applied) {
38
+ const { container } = containerState;
39
+ const createdAt = container.serverCreatedAt ?? container.createdAt;
40
+ const updatedAt = container.serverUpdatedAt ?? container.updatedAt;
41
+ if (!createdAt || !updatedAt) {
42
+ throw new Error("A synced container must carry its server timestamps");
43
+ }
44
+ const { state } = applied;
45
+ return {
46
+ createdAt,
47
+ effectiveAccessLevel: "admin",
48
+ id: state.containerId,
49
+ metadataAccessEpoch: state.epoch,
50
+ metadataAccessStateHash: applied.manifestHash,
51
+ metadataDocumentId: state.metadataDocumentId,
52
+ // Verifying the head already warmed the policies it references.
53
+ metadataReferencedPrincipals: [],
54
+ organizationId: state.organizationId,
55
+ parentId: state.parentContainerId,
56
+ updatedAt,
57
+ };
58
+ }
59
+ //# sourceMappingURL=acceptedMoveSettlement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acceptedMoveSettlement.js","sourceRoot":"","sources":["acceptedMoveSettlement.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,SAA4B,EAC5B,OAAe,EACf,KAAY;IAEZ,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;QACrD,IAAI,MAAM;YAAE,KAAK,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,mDAAmD;IACrD,CAAC;IACD,SAAS,CAAC,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAC9B,0EAA0E,CAAC;AAE7E,2EAA2E;AAC3E,MAAM,UAAU,uBAAuB,CACrC,SAA4B;IAE5B,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,CAAC;QAC3B,uBAAuB,CACrB,SAAS,EACT,mBAAmB,EACnB,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAC5D,CAAC;QACF,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,uBAAuB,CACrB,SAAS,EACT,gEAAgE,EAChE,IAAI,KAAK,CAAC,yCAAyC,CAAC,CACrD,CAAC;IACF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,0BAA0B,CACxC,SAA4B;IAE5B,uBAAuB,CACrB,SAAS,EACT,qEAAqE,EACrE,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAC1D,CAAC;IACF,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,cAA8B,EAC9B,OAA6B;IAE7B,MAAM,EAAE,SAAS,EAAE,GAAG,cAAc,CAAC;IACrC,MAAM,SAAS,GAAG,SAAS,CAAC,eAAe,IAAI,SAAS,CAAC,SAAS,CAAC;IACnE,MAAM,SAAS,GAAG,SAAS,CAAC,eAAe,IAAI,SAAS,CAAC,SAAS,CAAC;IACnE,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,OAAO;QACL,SAAS;QACT,oBAAoB,EAAE,OAAO;QAC7B,EAAE,EAAE,KAAK,CAAC,WAAW;QACrB,mBAAmB,EAAE,KAAK,CAAC,KAAK;QAChC,uBAAuB,EAAE,OAAO,CAAC,YAAY;QAC7C,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,gEAAgE;QAChE,4BAA4B,EAAE,EAAE;QAChC,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,QAAQ,EAAE,KAAK,CAAC,iBAAiB;QACjC,SAAS;KACV,CAAC;AACJ,CAAC","sourcesContent":["import type { AppliedContainerMove } from \"../../../containers\";\nimport type { ContainerState, RemoteContainer } from \"../../remoteHydration\";\nimport type { ContainerMoveIntentSyncInput } from \"../types\";\n\ntype AcceptedMoveInput = Pick<\n ContainerMoveIntentSyncInput,\n \"acceptedMoveRetry\" | \"intent\" | \"isCurrent\" | \"state\"\n>;\n\n/**\n * The server holds a move this pass did not settle. Keep the intent queued,\n * report it with a fixed message (the error's call site names the path), and\n * ask for a later pass: its replay finds the move applied and settles it\n * without posting again, so no reload or unrelated write is needed.\n */\nexport function leaveAcceptedMoveQueued(\n syncInput: AcceptedMoveInput,\n message: string,\n error: Error,\n): void {\n try {\n const report = syncInput.state.runtime.util.logError;\n if (report) void Promise.resolve(report(message, error)).catch(() => {});\n } catch {\n // A host logger must not change the replay result.\n }\n syncInput.acceptedMoveRetry.arm(syncInput.intent.id);\n}\n\nexport const STALE_ACCEPTED_MOVE =\n \"Container contents: a stale sync pass dropped an accepted container move\";\n\n/** persistAcceptedMoveIntent already asked to reconcile a stale result. */\nexport function unpersistedAcceptedMove(\n syncInput: AcceptedMoveInput,\n): \"abandoned\" | \"failed\" {\n if (!syncInput.isCurrent()) {\n leaveAcceptedMoveQueued(\n syncInput,\n STALE_ACCEPTED_MOVE,\n new Error(\"Stale pass dropped a persisting container move\"),\n );\n return \"abandoned\";\n }\n leaveAcceptedMoveQueued(\n syncInput,\n \"Container contents: an accepted container move did not persist\",\n new Error(\"Accepted container move did not persist\"),\n );\n return \"failed\";\n}\n\n/** The server accepted, but this current pass got no result to settle. */\nexport function unacknowledgedAcceptedMove(\n syncInput: AcceptedMoveInput,\n): \"failed\" {\n leaveAcceptedMoveQueued(\n syncInput,\n \"Container contents: an accepted container move was not acknowledged\",\n new Error(\"Accepted container move was not acknowledged\"),\n );\n return \"failed\";\n}\n\n/**\n * Settles from the verified head the server already holds. A head carries no\n * server timestamps, so the container keeps the ones this device holds.\n */\nexport function appliedRemoteContainer(\n containerState: ContainerState,\n applied: AppliedContainerMove,\n): RemoteContainer {\n const { container } = containerState;\n const createdAt = container.serverCreatedAt ?? container.createdAt;\n const updatedAt = container.serverUpdatedAt ?? container.updatedAt;\n if (!createdAt || !updatedAt) {\n throw new Error(\"A synced container must carry its server timestamps\");\n }\n const { state } = applied;\n return {\n createdAt,\n effectiveAccessLevel: \"admin\",\n id: state.containerId,\n metadataAccessEpoch: state.epoch,\n metadataAccessStateHash: applied.manifestHash,\n metadataDocumentId: state.metadataDocumentId,\n // Verifying the head already warmed the policies it references.\n metadataReferencedPrincipals: [],\n organizationId: state.organizationId,\n parentId: state.parentContainerId,\n updatedAt,\n };\n}\n"]}
@@ -1,45 +1,14 @@
1
- import { ContainerPathTooDeepError } from "../../../data/containers/shared/containerPathLimits.js";
2
- import { reportAndRethrowKeyingVerificationError } from "../../../data/keyingProjectionVerification/error.js";
3
- import { errorMessage } from "../../../data/shared/errorMessage.js";
4
- import { createRuntimePrincipalPolicyWarmer } from "../../principals/runtimePolicyWarmer.js";
5
- import { createDetachedContainerMetadataState, installDetachedContainerMetadataState, } from "../metadata/metadataStateIsolation.js";
6
- import { hasRemoteContainerMetadataState } from "../remoteHydration/reconciliation.js";
7
- import { moveRemoteContainer } from "./remote.js";
1
+ import { ContainerPathTooDeepError } from "../../../../data/containers/shared/containerPathLimits.js";
2
+ import { reportAndRethrowKeyingVerificationError } from "../../../../data/keyingProjectionVerification/error.js";
3
+ import { errorMessage } from "../../../../data/shared/errorMessage.js";
4
+ import { createRuntimePrincipalPolicyWarmer } from "../../../principals/runtimePolicyWarmer.js";
5
+ import { createDetachedContainerMetadataState, installDetachedContainerMetadataState, } from "../../metadata/metadataStateIsolation.js";
6
+ import { hasRemoteContainerMetadataState } from "../../remoteHydration/reconciliation.js";
7
+ import { moveRemoteContainer } from "../remote.js";
8
+ import { appliedRemoteContainer, leaveAcceptedMoveQueued, STALE_ACCEPTED_MOVE, unacknowledgedAcceptedMove, unpersistedAcceptedMove, } from "./acceptedMoveSettlement.js";
8
9
  function currentMoveResult(isCurrent, result) {
9
10
  return isCurrent() ? result : "abandoned";
10
11
  }
11
- /**
12
- * The server accepted a move this pass did not settle, so the intent stays
13
- * queued: a later pass re-posts it and a purge of its subtree waits. Fixed
14
- * messages only; the error's call site names the path.
15
- */
16
- function reportUnsettledAcceptedMove(state, message, error) {
17
- try {
18
- const report = state.runtime.util.logError;
19
- if (report)
20
- void Promise.resolve(report(message, error)).catch(() => { });
21
- }
22
- catch {
23
- // A host logger must not change the replay result.
24
- }
25
- }
26
- function reportStaleAcceptedMove(state, error) {
27
- reportUnsettledAcceptedMove(state, "Container contents: a stale sync pass dropped an accepted container move", error);
28
- }
29
- function reportFailedAcceptedMove(state, error) {
30
- if (!(error instanceof Error))
31
- return;
32
- reportUnsettledAcceptedMove(state, "Container contents: an accepted container move failed to settle", error);
33
- }
34
- /** persistAcceptedMoveIntent already asked to reconcile a stale result. */
35
- function unpersistedMoveResult(syncInput) {
36
- if (!syncInput.isCurrent()) {
37
- reportStaleAcceptedMove(syncInput.state, new Error("Stale pass dropped a persisting container move"));
38
- return "abandoned";
39
- }
40
- reportUnsettledAcceptedMove(syncInput.state, "Container contents: an accepted container move did not persist", new Error("Accepted container move did not persist"));
41
- return "failed";
42
- }
43
12
  async function recordPendingMoveIntentError(input) {
44
13
  if (!input.isCurrent()) {
45
14
  return false;
@@ -195,22 +164,54 @@ async function abandonTooDeepMove(syncInput) {
195
164
  state.runtime.util.log(`Container contents: abandoned queued move of ${intent.containerId}; its path would exceed the maximum depth`);
196
165
  return "failed";
197
166
  }
167
+ /** A replay that threw: refused for depth, tampered, or left unsettled. */
168
+ async function failedMoveReplay(input) {
169
+ const { abandonAppliedMove, error, parentState, remoteMoveApplied, syncInput, } = input;
170
+ const { intent, state } = syncInput;
171
+ if (!syncInput.isCurrent()) {
172
+ return abandonAppliedMove();
173
+ }
174
+ if (error instanceof ContainerPathTooDeepError)
175
+ return abandonTooDeepMove(syncInput);
176
+ // Even a refused acknowledgement leaves the move on the server; its replay
177
+ // verifies the head again before settling.
178
+ if (remoteMoveApplied && error instanceof Error)
179
+ leaveAcceptedMoveQueued(syncInput, "Container contents: an accepted container move failed to settle", error);
180
+ await reportAndRethrowKeyingVerificationError(error, state.runtime.util.reportSecurityIncident, {
181
+ objectId: intent.containerId,
182
+ objectKind: "container",
183
+ operation: "container.move.replay",
184
+ organizationId: parentState.container.organizationId,
185
+ });
186
+ if (!syncInput.isCurrent()) {
187
+ return abandonAppliedMove();
188
+ }
189
+ await recordPendingMoveIntentError({
190
+ containerId: intent.containerId,
191
+ expectedIntentId: intent.id,
192
+ expectedUpdatedAt: intent.updatedAt,
193
+ isCurrent: syncInput.isCurrent,
194
+ message: `Failed to sync container move: ${errorMessage(error)}`,
195
+ state,
196
+ });
197
+ return currentMoveResult(syncInput.isCurrent, "failed");
198
+ }
198
199
  async function movePendingRemoteContainer(input) {
199
- const { parentState, syncInput } = input;
200
+ const { containerState, parentState, syncInput } = input;
200
201
  const { host, intent, state } = syncInput;
201
202
  let remoteMoveApplied = false;
202
203
  let remoteParentId = intent.parentContainerId;
203
204
  const abandonAppliedMove = () => {
204
205
  if (remoteMoveApplied) {
205
- reportStaleAcceptedMove(state, new Error("Stale pass dropped an accepted container move"));
206
+ leaveAcceptedMoveQueued(syncInput, STALE_ACCEPTED_MOVE, new Error("Stale pass dropped an accepted container move"));
206
207
  syncInput.requestRemoteReconciliation(remoteParentId);
207
208
  }
208
209
  return "abandoned";
209
210
  };
210
211
  try {
211
- const moved = await moveRemoteContainer({
212
+ const replayed = await moveRemoteContainer({
212
213
  containerId: intent.containerId,
213
- onStaleAcceptance: () => {
214
+ onAccepted: () => {
214
215
  remoteMoveApplied = true;
215
216
  },
216
217
  parentContainerId: intent.parentContainerId,
@@ -218,12 +219,14 @@ async function movePendingRemoteContainer(input) {
218
219
  runtime: state.runtime,
219
220
  stillCurrent: syncInput.isCurrent,
220
221
  });
221
- if (!moved) {
222
+ if (!replayed) {
222
223
  if (!syncInput.isCurrent()) {
223
224
  if (!remoteMoveApplied)
224
225
  syncInput.requestRemoteReconciliation(intent.parentContainerId);
225
226
  return abandonAppliedMove();
226
227
  }
228
+ if (remoteMoveApplied)
229
+ return unacknowledgedAcceptedMove(syncInput);
227
230
  await recordPendingMoveIntentError({
228
231
  containerId: intent.containerId,
229
232
  expectedIntentId: intent.id,
@@ -235,6 +238,9 @@ async function movePendingRemoteContainer(input) {
235
238
  return currentMoveResult(syncInput.isCurrent, "failed");
236
239
  }
237
240
  remoteMoveApplied = true;
241
+ const moved = "applied" in replayed
242
+ ? appliedRemoteContainer(containerState, replayed.applied)
243
+ : replayed.moved;
238
244
  remoteParentId = moved.parentId;
239
245
  if (!syncInput.isCurrent())
240
246
  return abandonAppliedMove();
@@ -247,36 +253,20 @@ async function movePendingRemoteContainer(input) {
247
253
  state,
248
254
  });
249
255
  if (!persisted)
250
- return unpersistedMoveResult(syncInput);
251
- state.runtime.util.log(`Container contents: synced queued container move ${intent.containerId}`);
256
+ return unpersistedAcceptedMove(syncInput);
257
+ state.runtime.util.log("applied" in replayed
258
+ ? `Container contents: settled queued container move ${intent.containerId} the server already held`
259
+ : `Container contents: synced queued container move ${intent.containerId}`);
252
260
  return "moved";
253
261
  }
254
262
  catch (error) {
255
- if (!syncInput.isCurrent()) {
256
- return abandonAppliedMove();
257
- }
258
- if (error instanceof ContainerPathTooDeepError)
259
- return abandonTooDeepMove(syncInput);
260
- await reportAndRethrowKeyingVerificationError(error, state.runtime.util.reportSecurityIncident, {
261
- objectId: intent.containerId,
262
- objectKind: "container",
263
- operation: "container.move.replay",
264
- organizationId: parentState.container.organizationId,
265
- });
266
- if (!syncInput.isCurrent()) {
267
- return abandonAppliedMove();
268
- }
269
- if (remoteMoveApplied)
270
- reportFailedAcceptedMove(state, error);
271
- await recordPendingMoveIntentError({
272
- containerId: intent.containerId,
273
- expectedIntentId: intent.id,
274
- expectedUpdatedAt: intent.updatedAt,
275
- isCurrent: syncInput.isCurrent,
276
- message: `Failed to sync container move: ${errorMessage(error)}`,
277
- state,
263
+ return failedMoveReplay({
264
+ abandonAppliedMove,
265
+ error,
266
+ parentState,
267
+ remoteMoveApplied,
268
+ syncInput,
278
269
  });
279
- return currentMoveResult(syncInput.isCurrent, "failed");
280
270
  }
281
271
  }
282
272
  async function trySyncPendingContainerMoveIntent(input) {
@@ -344,7 +334,11 @@ async function trySyncPendingContainerMoveIntent(input) {
344
334
  });
345
335
  return currentMoveResult(input.isCurrent, "blocked");
346
336
  }
347
- return movePendingRemoteContainer({ parentState, syncInput: input });
337
+ return movePendingRemoteContainer({
338
+ containerState,
339
+ parentState,
340
+ syncInput: input,
341
+ });
348
342
  }
349
343
  export async function syncPendingContainerMoveIntents(input) {
350
344
  if (!input.isCurrent()) {
@@ -369,6 +363,7 @@ export async function syncPendingContainerMoveIntents(input) {
369
363
  isRemoteSyncBlocked: input.isRemoteSyncBlocked,
370
364
  intent,
371
365
  requestRemoteReconciliation: input.requestRemoteReconciliation,
366
+ acceptedMoveRetry: input.acceptedMoveRetry,
372
367
  state,
373
368
  });
374
369
  if (result === "abandoned") {
@@ -377,6 +372,11 @@ export async function syncPendingContainerMoveIntents(input) {
377
372
  if (result === "moved") {
378
373
  movedCount += 1;
379
374
  }
375
+ else {
376
+ // A replay that failed before reaching the server keeps an earlier
377
+ // acceptance's retries going.
378
+ input.acceptedMoveRetry.resume(intent.id);
379
+ }
380
380
  }
381
381
  return movedCount;
382
382
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moveIntentSync.js","sourceRoot":"","sources":["moveIntentSync.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContainerPathTooDeepError } from \"../../../../data/containers/shared/containerPathLimits\";\nimport { reportAndRethrowKeyingVerificationError } from \"../../../../data/keyingProjectionVerification/error\";\nimport { errorMessage } from \"../../../../data/shared/errorMessage\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../../principals/runtimePolicyWarmer\";\nimport {\n createDetachedContainerMetadataState,\n installDetachedContainerMetadataState,\n} from \"../../metadata/metadataStateIsolation\";\nimport type { ContainerState, RemoteContainer } from \"../../remoteHydration\";\nimport { hasRemoteContainerMetadataState } from \"../../remoteHydration/reconciliation\";\nimport { moveRemoteContainer } from \"../remote\";\nimport type {\n AcceptedMoveRetry,\n ContainerMoveIntentSyncHost,\n ContainerMoveIntentSyncInput,\n ContainerMoveIntentSyncState,\n} from \"../types\";\nimport {\n appliedRemoteContainer,\n leaveAcceptedMoveQueued,\n STALE_ACCEPTED_MOVE,\n unacknowledgedAcceptedMove,\n unpersistedAcceptedMove,\n} from \"./acceptedMoveSettlement\";\n\ntype MoveIntentSyncResult = \"abandoned\" | \"blocked\" | \"failed\" | \"moved\";\n\nfunction currentMoveResult<\n Result extends Exclude<MoveIntentSyncResult, \"abandoned\">,\n>(isCurrent: () => boolean, result: Result): \"abandoned\" | Result {\n return isCurrent() ? result : \"abandoned\";\n}\n\nasync function recordPendingMoveIntentError(input: {\n blocked?: boolean | undefined;\n containerId: string;\n expectedIntentId: string;\n expectedUpdatedAt: string;\n isCurrent: () => boolean;\n message: string;\n state: ContainerMoveIntentSyncState;\n}): Promise<boolean> {\n if (!input.isCurrent()) {\n return false;\n }\n const execSql = input.state.runtime.infra.execSql;\n await input.state.persistence.recordMoveIntentError(execSql, {\n blocked: input.blocked,\n containerId: input.containerId,\n expectedIntentId: input.expectedIntentId,\n expectedUpdatedAt: input.expectedUpdatedAt,\n message: input.message,\n stillCurrent: input.isCurrent,\n });\n return input.isCurrent();\n}\n\nasync function resolveMoveIntentLocalUpdatedAt(input: {\n containerId: string;\n isCurrent: () => boolean;\n remoteUpdatedAt: string;\n state: ContainerMoveIntentSyncState;\n}): Promise<string | null> {\n if (!input.isCurrent()) {\n return null;\n }\n const previousLocalUpdatedAt =\n input.state.containersById.get(input.containerId)?.container\n .localUpdatedAt ?? null;\n if (\n !previousLocalUpdatedAt ||\n previousLocalUpdatedAt.localeCompare(input.remoteUpdatedAt) <= 0\n ) {\n return input.remoteUpdatedAt;\n }\n\n const execSql = input.state.runtime.infra.execSql;\n const containersWithPendingMetadataUpdates =\n await input.state.persistence.listContainerIdsWithPendingUpdates(execSql, [\n input.containerId,\n ]);\n if (!input.isCurrent()) {\n return null;\n }\n\n return containersWithPendingMetadataUpdates.includes(input.containerId)\n ? previousLocalUpdatedAt\n : input.remoteUpdatedAt;\n}\n\nexport async function persistAcceptedMoveIntent(input: {\n host: ContainerMoveIntentSyncHost;\n isCurrent: () => boolean;\n intent: ContainerMoveIntentSyncInput[\"intent\"];\n moved: RemoteContainer;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerMoveIntentSyncState;\n}): Promise<boolean> {\n const { host, intent, moved, state } = input;\n const abandon = () => {\n input.requestRemoteReconciliation(moved.parentId);\n return false;\n };\n if (!input.isCurrent()) return abandon();\n const containerState = state.containersById.get(intent.containerId);\n if (!containerState) {\n return false;\n }\n\n await createRuntimePrincipalPolicyWarmer(state.runtime)({\n organizationId: moved.organizationId,\n references: moved.metadataReferencedPrincipals,\n stillCurrent: input.isCurrent,\n });\n if (!input.isCurrent()) {\n return abandon();\n }\n const localUpdatedAt = await resolveMoveIntentLocalUpdatedAt({\n containerId: intent.containerId,\n isCurrent: input.isCurrent,\n remoteUpdatedAt: moved.updatedAt,\n state,\n });\n if (!input.isCurrent() || localUpdatedAt === null) {\n return abandon();\n }\n const persistenceCandidate =\n await createDetachedContainerMetadataState(containerState);\n if (!input.isCurrent()) {\n return abandon();\n }\n persistenceCandidate.container = {\n ...persistenceCandidate.container,\n createdAt: moved.createdAt,\n serverCreatedAt: moved.createdAt,\n serverUpdatedAt: moved.updatedAt,\n updatedAt: moved.updatedAt,\n };\n const persistenceResult = await host.persistContainerState(\n persistenceCandidate,\n {\n accessEpoch: moved.metadataAccessEpoch,\n accessStateHash: moved.metadataAccessStateHash,\n documentId: moved.metadataDocumentId,\n metadataDocumentId: moved.metadataDocumentId,\n organizationId: moved.organizationId,\n parentId: moved.parentId,\n systemSlot: moved.systemSlot ?? null,\n },\n false,\n {\n localUpdatedAt,\n serverTimestamps: {\n createdAt: moved.createdAt,\n updatedAt: moved.updatedAt,\n },\n },\n {\n expectedStateWhenMissing: containerState,\n isCurrent: input.isCurrent,\n moveIntentSettlement: {\n containerId: intent.containerId,\n expectedIntentId: intent.id,\n expectedUpdatedAt: intent.updatedAt,\n },\n },\n );\n if (!input.isCurrent() || persistenceResult.status !== \"persisted\") {\n return abandon();\n }\n if (persistenceResult.moveIntentSettled !== true) {\n throw new Error(\n \"Container move persistence must settle the intent atomically\",\n );\n }\n if (!input.isCurrent()) return abandon();\n const { record: nextRecord } = persistenceResult;\n installDetachedContainerMetadataState(containerState, persistenceCandidate, {\n candidateRecord: nextRecord,\n preserveConcurrentMetadataEdit: true,\n });\n containerState.container = {\n ...containerState.container,\n metadataDocumentId: moved.metadataDocumentId,\n organizationId: moved.organizationId,\n parentId: moved.parentId,\n };\n return true;\n}\n\n/**\n * A move refused for path length never succeeds; the queue-time check saw only\n * the descendants this device holds. Drop the queued move and restore the\n * previous parent together: the server row never changed, so no listing would\n * ever put the folder back.\n */\nasync function abandonTooDeepMove(\n syncInput: ContainerMoveIntentSyncInput,\n): Promise<MoveIntentSyncResult> {\n const { host, intent, state } = syncInput;\n const previousParentId = intent.previousParentContainerId;\n if (previousParentId === null) {\n throw new Error(\"A queued container move must name its previous parent\");\n }\n const abandoned = await state.persistence.abandonMoveIntentRevision(\n state.runtime.infra.execSql,\n {\n containerId: intent.containerId,\n expectedIntentId: intent.id,\n expectedUpdatedAt: intent.updatedAt,\n previousParentContainerId: previousParentId,\n stillCurrent: syncInput.isCurrent,\n },\n );\n if (!syncInput.isCurrent()) return \"abandoned\";\n // A newer local move superseded this revision; it replays on its own.\n if (!abandoned) return \"failed\";\n const containerState = state.containersById.get(intent.containerId);\n if (containerState) {\n containerState.container = {\n ...containerState.container,\n parentId: previousParentId,\n };\n }\n host.updateSnapshot();\n state.runtime.util.log(\n `Container contents: abandoned queued move of ${intent.containerId}; its path would exceed the maximum depth`,\n );\n return \"failed\";\n}\n\n/** A replay that threw: refused for depth, tampered, or left unsettled. */\nasync function failedMoveReplay(input: {\n abandonAppliedMove: () => \"abandoned\";\n error: unknown;\n parentState: ContainerState;\n remoteMoveApplied: boolean;\n syncInput: ContainerMoveIntentSyncInput;\n}): Promise<MoveIntentSyncResult> {\n const {\n abandonAppliedMove,\n error,\n parentState,\n remoteMoveApplied,\n syncInput,\n } = input;\n const { intent, state } = syncInput;\n if (!syncInput.isCurrent()) {\n return abandonAppliedMove();\n }\n if (error instanceof ContainerPathTooDeepError)\n return abandonTooDeepMove(syncInput);\n // Even a refused acknowledgement leaves the move on the server; its replay\n // verifies the head again before settling.\n if (remoteMoveApplied && error instanceof Error)\n leaveAcceptedMoveQueued(\n syncInput,\n \"Container contents: an accepted container move failed to settle\",\n error,\n );\n await reportAndRethrowKeyingVerificationError(\n error,\n state.runtime.util.reportSecurityIncident,\n {\n objectId: intent.containerId,\n objectKind: \"container\",\n operation: \"container.move.replay\",\n organizationId: parentState.container.organizationId,\n },\n );\n if (!syncInput.isCurrent()) {\n return abandonAppliedMove();\n }\n await recordPendingMoveIntentError({\n containerId: intent.containerId,\n expectedIntentId: intent.id,\n expectedUpdatedAt: intent.updatedAt,\n isCurrent: syncInput.isCurrent,\n message: `Failed to sync container move: ${errorMessage(error)}`,\n state,\n });\n return currentMoveResult(syncInput.isCurrent, \"failed\");\n}\n\nasync function movePendingRemoteContainer(input: {\n containerState: ContainerState;\n parentState: ContainerState;\n syncInput: ContainerMoveIntentSyncInput;\n}): Promise<MoveIntentSyncResult> {\n const { containerState, parentState, syncInput } = input;\n const { host, intent, state } = syncInput;\n let remoteMoveApplied = false;\n let remoteParentId: string | null = intent.parentContainerId;\n const abandonAppliedMove = () => {\n if (remoteMoveApplied) {\n leaveAcceptedMoveQueued(\n syncInput,\n STALE_ACCEPTED_MOVE,\n new Error(\"Stale pass dropped an accepted container move\"),\n );\n syncInput.requestRemoteReconciliation(remoteParentId);\n }\n return \"abandoned\" as const;\n };\n try {\n const replayed = await moveRemoteContainer({\n containerId: intent.containerId,\n onAccepted: () => {\n remoteMoveApplied = true;\n },\n parentContainerId: intent.parentContainerId,\n resolveProjectionUserKey: state.resolveProjectionUserKey,\n runtime: state.runtime,\n stillCurrent: syncInput.isCurrent,\n });\n if (!replayed) {\n if (!syncInput.isCurrent()) {\n if (!remoteMoveApplied)\n syncInput.requestRemoteReconciliation(intent.parentContainerId);\n return abandonAppliedMove();\n }\n if (remoteMoveApplied) return unacknowledgedAcceptedMove(syncInput);\n await recordPendingMoveIntentError({\n containerId: intent.containerId,\n expectedIntentId: intent.id,\n expectedUpdatedAt: intent.updatedAt,\n isCurrent: syncInput.isCurrent,\n message: \"Remote container move was rejected or unavailable\",\n state,\n });\n return currentMoveResult(syncInput.isCurrent, \"failed\");\n }\n remoteMoveApplied = true;\n const moved =\n \"applied\" in replayed\n ? appliedRemoteContainer(containerState, replayed.applied)\n : replayed.moved;\n remoteParentId = moved.parentId;\n if (!syncInput.isCurrent()) return abandonAppliedMove();\n\n const persisted = await persistAcceptedMoveIntent({\n host,\n isCurrent: syncInput.isCurrent,\n intent,\n moved,\n requestRemoteReconciliation: syncInput.requestRemoteReconciliation,\n state,\n });\n if (!persisted) return unpersistedAcceptedMove(syncInput);\n state.runtime.util.log(\n \"applied\" in replayed\n ? `Container contents: settled queued container move ${intent.containerId} the server already held`\n : `Container contents: synced queued container move ${intent.containerId}`,\n );\n return \"moved\";\n } catch (error: unknown) {\n return failedMoveReplay({\n abandonAppliedMove,\n error,\n parentState,\n remoteMoveApplied,\n syncInput,\n });\n }\n}\n\nasync function trySyncPendingContainerMoveIntent(\n input: ContainerMoveIntentSyncInput,\n): Promise<MoveIntentSyncResult> {\n const { intent, state } = input;\n if (!input.isCurrent()) {\n return \"abandoned\";\n }\n const containerState = state.containersById.get(intent.containerId);\n const parentState = state.containersById.get(intent.parentContainerId);\n\n if (!containerState) {\n await recordPendingMoveIntentError({\n blocked: true,\n containerId: intent.containerId,\n expectedIntentId: intent.id,\n expectedUpdatedAt: intent.updatedAt,\n isCurrent: input.isCurrent,\n message: \"Container move intent references a missing local container\",\n state,\n });\n return currentMoveResult(input.isCurrent, \"failed\");\n }\n if (!parentState) {\n await recordPendingMoveIntentError({\n blocked: true,\n containerId: intent.containerId,\n expectedIntentId: intent.id,\n expectedUpdatedAt: intent.updatedAt,\n isCurrent: input.isCurrent,\n message:\n \"Container move intent references a missing destination parent container\",\n state,\n });\n return currentMoveResult(input.isCurrent, \"failed\");\n }\n if (input.isRemoteSyncBlocked(containerState.container.organizationId)) {\n return \"blocked\";\n }\n if (\n !hasRemoteContainerMetadataState(containerState) ||\n input.isCreatePending(intent.containerId)\n ) {\n // Source not synced yet is TRANSIENT, exactly like the destination case\n // below: the source's own create intent syncs first (createIntentSync runs\n // before moveIntentSync in a pass), but if that create fails on this pass\n // the source still lacks remote metadata here. Keep the intent 'pending'\n // (not 'blocked') so the queue reports a retryable failure rather than a\n // missing-dependency block; either way listUnsyncedMoveIntents replays it\n // once the create lands.\n await recordPendingMoveIntentError({\n containerId: intent.containerId,\n expectedIntentId: intent.id,\n expectedUpdatedAt: intent.updatedAt,\n isCurrent: input.isCurrent,\n message: \"Container move source is not synced yet\",\n state,\n });\n return currentMoveResult(input.isCurrent, \"blocked\");\n }\n if (\n !hasRemoteContainerMetadataState(parentState) ||\n input.isCreatePending(intent.parentContainerId)\n ) {\n await recordPendingMoveIntentError({\n containerId: intent.containerId,\n expectedIntentId: intent.id,\n expectedUpdatedAt: intent.updatedAt,\n isCurrent: input.isCurrent,\n message: \"Container move destination parent is not synced yet\",\n state,\n });\n return currentMoveResult(input.isCurrent, \"blocked\");\n }\n\n return movePendingRemoteContainer({\n containerState,\n parentState,\n syncInput: input,\n });\n}\n\nexport async function syncPendingContainerMoveIntents(input: {\n host: ContainerMoveIntentSyncHost;\n isCreatePending: (containerId: string) => boolean;\n isCurrent: () => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n acceptedMoveRetry: AcceptedMoveRetry;\n state: ContainerMoveIntentSyncState;\n}): Promise<number> {\n if (!input.isCurrent()) {\n return 0;\n }\n const { host } = input;\n const state = { ...input.state };\n const execSql = state.runtime.infra.execSql;\n const pendingIntents =\n await state.persistence.listUnsyncedMoveIntents(execSql);\n if (!input.isCurrent()) {\n return 0;\n }\n let movedCount = 0;\n\n for (const intent of pendingIntents) {\n if (!input.isCurrent()) {\n return movedCount;\n }\n const result = await trySyncPendingContainerMoveIntent({\n host,\n isCurrent: input.isCurrent,\n isCreatePending: input.isCreatePending,\n isRemoteSyncBlocked: input.isRemoteSyncBlocked,\n intent,\n requestRemoteReconciliation: input.requestRemoteReconciliation,\n acceptedMoveRetry: input.acceptedMoveRetry,\n state,\n });\n if (result === \"abandoned\") {\n return movedCount;\n }\n\n if (result === \"moved\") {\n movedCount += 1;\n } else {\n // A replay that failed before reaching the server keeps an earlier\n // acceptance's retries going.\n input.acceptedMoveRetry.resume(intent.id);\n }\n }\n\n return movedCount;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { isContainerNotFoundFailure } from "../../../data/containers/shared/mutationFailures.js";
2
2
  import { ProjectionDependencyUnavailableError } from "../../../data/keyingProjectionVerification/dependencyUnavailable.js";
3
- import { moveRemoteContainer as moveRemoteContainerMutation, readContainerMutationMetadataDocumentId, referencedPrincipalHeadsFromContainerMutationResponse, shareRemoteContainer as shareRemoteContainerMutation, } from "../../containers/index.js";
3
+ import { readContainerMutationMetadataDocumentId, referencedPrincipalHeadsFromContainerMutationResponse, replayRemoteContainerMove, shareRemoteContainer as shareRemoteContainerMutation, } from "../../containers/index.js";
4
4
  import { createRuntimeCurrentGroupShare } from "../../containers/child/share/runtimeCurrentGroupShare.js";
5
5
  import { resolveDocumentCreateAuthor } from "../../documents/index.js";
6
6
  import { createRuntimeGroupMetadataAccess } from "../../organizations/groupMetadata/groupMetadataRuntime.js";
@@ -103,43 +103,46 @@ export async function shareRemoteContainerWithGroup(input) {
103
103
  }
104
104
  return sharedRemoteContainerStateFromMutation(input.previousProjection, shared.response);
105
105
  }
106
+ /**
107
+ * Replays a queued move. `applied` carries the verified head when the server
108
+ * already holds the container under the destination, so nothing is posted.
109
+ */
106
110
  export async function moveRemoteContainer(input) {
107
111
  const writer = resolveContainerWriterContext(input.runtime, "container move");
108
112
  if (!writer) {
109
113
  return null;
110
114
  }
111
115
  const { apiClient, author, execSql, secretKey: targetSecretKey } = writer;
112
- const moved = await moveRemoteContainerMutation({
116
+ const replayed = await replayRemoteContainerMove({
113
117
  reportSecurityIncident: input.runtime.util.reportSecurityIncident,
114
118
  apiClient,
115
119
  author,
116
120
  containerId: input.containerId,
117
121
  destinationParentContainerId: input.parentContainerId,
118
122
  execSql,
119
- onStaleAcceptance: input.onStaleAcceptance,
123
+ onAccepted: input.onAccepted,
120
124
  resolveProjectionUserKey: input.resolveProjectionUserKey,
121
125
  stillCurrent: input.stillCurrent,
122
126
  targetSecretKey,
123
127
  warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(input.runtime),
124
128
  });
125
- if (!moved) {
126
- return null;
129
+ if (!replayed || "applied" in replayed) {
130
+ return replayed;
127
131
  }
132
+ const { response } = replayed;
128
133
  return {
129
- createdAt: moved.response.createdAt,
130
- effectiveAccessLevel: "admin",
131
- id: moved.response.containerId,
132
- organizationId: moved.response.organizationId,
133
- parentId: moved.response.parentId,
134
- metadataDocumentId: readContainerMutationMetadataDocumentId({
135
- response: moved.response,
136
- }),
137
- metadataAccessEpoch: moved.response.manifestHead.epoch,
138
- metadataAccessStateHash: moved.response.manifestHead.manifestHash,
139
- metadataReferencedPrincipals: referencedPrincipalHeadsFromContainerMutationResponse({
140
- response: moved.response,
141
- }),
142
- updatedAt: moved.response.updatedAt,
134
+ moved: {
135
+ createdAt: response.createdAt,
136
+ effectiveAccessLevel: "admin",
137
+ id: response.containerId,
138
+ organizationId: response.organizationId,
139
+ parentId: response.parentId,
140
+ metadataDocumentId: readContainerMutationMetadataDocumentId({ response }),
141
+ metadataAccessEpoch: response.manifestHead.epoch,
142
+ metadataAccessStateHash: response.manifestHead.manifestHash,
143
+ metadataReferencedPrincipals: referencedPrincipalHeadsFromContainerMutationResponse({ response }),
144
+ updatedAt: response.updatedAt,
145
+ },
143
146
  };
144
147
  }
145
148
  export async function deleteRemoteContainer(input) {