@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.
- package/package.json +1 -1
- package/stores/container-contents/syncAgent/acceptedMoveRetry.js +40 -0
- package/stores/container-contents/syncAgent/acceptedMoveRetry.js.map +1 -0
- package/stores/container-contents/syncAgent/syncLaneIteration.js +9 -2
- package/stores/container-contents/syncAgent/syncLaneIteration.js.map +1 -1
- package/workflows/container-contents/container-state/move/acceptedMoveSettlement.js +59 -0
- package/workflows/container-contents/container-state/move/acceptedMoveSettlement.js.map +1 -0
- package/workflows/container-contents/container-state/{moveIntentSync.js → move/moveIntentSync.js} +71 -71
- package/workflows/container-contents/container-state/move/moveIntentSync.js.map +1 -0
- package/workflows/container-contents/container-state/remote.js +22 -19
- package/workflows/container-contents/container-state/remote.js.map +1 -1
- package/workflows/container-contents/container-state/types.js.map +1 -1
- package/workflows/containers/child/move/index.d.ts +24 -3
- package/workflows/containers/child/move/index.js +62 -71
- package/workflows/containers/child/move/index.js.map +1 -1
- package/workflows/containers/child/move/moveRequest.js +47 -0
- package/workflows/containers/child/move/moveRequest.js.map +1 -0
- package/workflows/containers/child/mutationSubmit.js +2 -3
- package/workflows/containers/child/mutationSubmit.js.map +1 -1
- package/workflows/containers/index.d.ts +1 -1
- package/workflows/containers/index.js +1 -1
- package/workflows/containers/index.js.map +1 -1
- package/workflows/container-contents/container-state/moveIntentSync.js.map +0 -1
package/package.json
CHANGED
|
@@ -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
|
|
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"]}
|
package/workflows/container-contents/container-state/{moveIntentSync.js → move/moveIntentSync.js}
RENAMED
|
@@ -1,45 +1,14 @@
|
|
|
1
|
-
import { ContainerPathTooDeepError } from "
|
|
2
|
-
import { reportAndRethrowKeyingVerificationError } from "
|
|
3
|
-
import { errorMessage } from "
|
|
4
|
-
import { createRuntimePrincipalPolicyWarmer } from "
|
|
5
|
-
import { createDetachedContainerMetadataState, installDetachedContainerMetadataState, } from "
|
|
6
|
-
import { hasRemoteContainerMetadataState } from "
|
|
7
|
-
import { moveRemoteContainer } from "
|
|
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
|
-
|
|
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
|
|
212
|
+
const replayed = await moveRemoteContainer({
|
|
212
213
|
containerId: intent.containerId,
|
|
213
|
-
|
|
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 (!
|
|
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
|
|
251
|
-
state.runtime.util.log(
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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({
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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 (!
|
|
126
|
-
return
|
|
129
|
+
if (!replayed || "applied" in replayed) {
|
|
130
|
+
return replayed;
|
|
127
131
|
}
|
|
132
|
+
const { response } = replayed;
|
|
128
133
|
return {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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) {
|