@tearleads/client-sdk 0.1.66 → 0.1.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/persistence/principalHistory/principalHistoryStagePersistence.d.ts +2 -0
- package/data/persistence/principalHistory/principalHistoryStagePersistence.js +4 -0
- package/data/persistence/principalHistory/principalHistoryStagePersistence.js.map +1 -1
- 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/container-contents/storedContainerState.js +5 -1
- package/workflows/container-contents/storedContainerState.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/principals/recoveryOperation/supersededStageResume.js +21 -0
- package/workflows/principals/recoveryOperation/supersededStageResume.js.map +1 -0
- package/workflows/principals/runtimePolicyRecovery.js +12 -9
- package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
- package/workflows/container-contents/container-state/moveIntentSync.js.map +0 -1
|
@@ -3,6 +3,8 @@ import { type ExecSql } from "../../sqlite/sqlSchema.js";
|
|
|
3
3
|
import { type PrincipalHistoryEvidencePage } from "./principalHistoryEvidencePersistence.js";
|
|
4
4
|
/** A stage's verification work; its retention hints are written by persistence. */
|
|
5
5
|
export type PrincipalHistoryStage = Omit<typeof principalHistoryStages.$inferSelect, "scopeId" | "touchedAt">;
|
|
6
|
+
/** Another writer replaced this stage's progress between load and save. */
|
|
7
|
+
export declare function isPrincipalHistoryStageChangedError(error: unknown): boolean;
|
|
6
8
|
export declare function loadPrincipalHistoryStage(execSql: ExecSql, id: string): Promise<PrincipalHistoryStage | null>;
|
|
7
9
|
/** Publish accepted entries and index nodes atomically with authenticated progress. */
|
|
8
10
|
export declare function savePrincipalHistoryStage(input: {
|
|
@@ -25,6 +25,10 @@ class PrincipalHistoryStageChangedError extends Error {
|
|
|
25
25
|
this.name = "PrincipalHistoryStageChangedError";
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
/** Another writer replaced this stage's progress between load and save. */
|
|
29
|
+
export function isPrincipalHistoryStageChangedError(error) {
|
|
30
|
+
return error instanceof PrincipalHistoryStageChangedError;
|
|
31
|
+
}
|
|
28
32
|
export async function loadPrincipalHistoryStage(execSql, id) {
|
|
29
33
|
await ensureSqlTables(execSql, principalHistoryStageTables);
|
|
30
34
|
const { db } = getClientSQLitePersistenceRuntime(execSql);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalHistoryStagePersistence.js","sourceRoot":"","sources":["principalHistoryStagePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { assertProjectionVerificationCurrent } from \"../../keyingProjectionVerification/types\";\nimport { principalHistoryEvidenceTables } from \"../../sqlite/schema/principalHistoryEvidenceSchema\";\nimport { principalHistoryNodeRetentionTables } from \"../../sqlite/schema/principalHistoryNodeRetentionSchema\";\nimport {\n principalHistoryStages,\n principalHistoryStageTables,\n} from \"../../sqlite/schema/principalHistoryStageSchema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport {\n type PrincipalHistoryEvidencePage,\n writePrincipalHistoryEvidencePage,\n} from \"./principalHistoryEvidencePersistence\";\nimport { reclaimIncompletePrincipalHistoryStages } from \"./principalHistoryIncompleteRetention\";\nimport { reclaimPrincipalHistoryNodes } from \"./principalHistoryNodeRetention\";\nimport {\n releasePrincipalHistoryRoot,\n retainPrincipalHistoryRoot,\n} from \"./principalHistoryRootOwnership\";\nimport { reclaimUnpublishedPrincipalHistoryStages } from \"./principalHistoryUnpublishedRetention\";\n\n/** A stage's verification work; its retention hints are written by persistence. */\nexport type PrincipalHistoryStage = Omit<\n typeof principalHistoryStages.$inferSelect,\n \"scopeId\" | \"touchedAt\"\n>;\n\nconst principalHistoryStageColumns = {\n id: principalHistoryStages.id,\n organizationId: principalHistoryStages.organizationId,\n currentJson: principalHistoryStages.currentJson,\n afterVersion: principalHistoryStages.afterVersion,\n complete: principalHistoryStages.complete,\n progress: principalHistoryStages.progress,\n};\n\nclass PrincipalHistoryStageChangedError extends Error {\n readonly code = \"principal_history_stage_changed\";\n constructor() {\n super(\n \"Another operation changed the saved principal history; resume from its current state\",\n );\n this.name = \"PrincipalHistoryStageChangedError\";\n }\n}\n\nexport async function loadPrincipalHistoryStage(\n execSql: ExecSql,\n id: string,\n): Promise<PrincipalHistoryStage | null> {\n await ensureSqlTables(execSql, principalHistoryStageTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [row] = await db\n .select(principalHistoryStageColumns)\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, id))\n .limit(1);\n return row ?? null;\n}\n\n/** Publish accepted entries and index nodes atomically with authenticated progress. */\nexport async function savePrincipalHistoryStage(input: {\n readonly execSql: ExecSql;\n readonly stage: PrincipalHistoryStage;\n readonly evidence: PrincipalHistoryEvidencePage;\n readonly previousProgress: string | null;\n readonly stillCurrent: () => boolean;\n}): Promise<void> {\n const evidence = structuredClone(input.evidence);\n const stage = { ...input.stage, scopeId: evidence.scopeId };\n await ensureSqlTables(input.execSql, [\n ...principalHistoryStageTables,\n ...principalHistoryEvidenceTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const saved = await runtime.guardedTransaction(\n async (tx) => {\n const [previous] = await tx\n .select({ progress: principalHistoryStages.progress })\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, stage.id))\n .limit(1);\n if ((previous?.progress ?? null) !== input.previousProgress)\n throw new PrincipalHistoryStageChangedError();\n await writePrincipalHistoryEvidencePage(tx, evidence);\n const row = { ...stage, touchedAt: Date.now() };\n await tx\n .insert(principalHistoryStages)\n .values(row)\n .onConflictDoUpdate({ target: principalHistoryStages.id, set: row })\n .run();\n await retainPrincipalHistoryRoot(tx, {\n id: `stage:${stage.id}`,\n scopeId: evidence.scopeId,\n organizationId: stage.organizationId,\n rootHash: evidence.indexRootHash,\n });\n await reclaimIncompletePrincipalHistoryStages(tx, stage);\n await reclaimUnpublishedPrincipalHistoryStages(tx, stage);\n await reclaimPrincipalHistoryNodes(tx, evidence);\n },\n input.stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => saved.committed);\n}\n\nexport async function discardPrincipalHistoryStage(\n execSql: ExecSql,\n stage: PrincipalHistoryStage,\n stillCurrent: () => boolean,\n) {\n await ensureSqlTables(execSql, [\n ...principalHistoryStageTables,\n ...principalHistoryNodeRetentionTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const discarded = await runtime.guardedTransaction(\n async (db) => {\n const [current] = await db\n .select({ progress: principalHistoryStages.progress })\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, stage.id))\n .limit(1);\n if (current?.progress !== stage.progress) return;\n await releasePrincipalHistoryRoot(db, `stage:${stage.id}`);\n await db\n .delete(principalHistoryStages)\n .where(\n and(\n eq(principalHistoryStages.id, stage.id),\n eq(principalHistoryStages.progress, stage.progress),\n ),\n )\n .run();\n },\n stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => discarded.committed);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"principalHistoryStagePersistence.js","sourceRoot":"","sources":["principalHistoryStagePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { assertProjectionVerificationCurrent } from \"../../keyingProjectionVerification/types\";\nimport { principalHistoryEvidenceTables } from \"../../sqlite/schema/principalHistoryEvidenceSchema\";\nimport { principalHistoryNodeRetentionTables } from \"../../sqlite/schema/principalHistoryNodeRetentionSchema\";\nimport {\n principalHistoryStages,\n principalHistoryStageTables,\n} from \"../../sqlite/schema/principalHistoryStageSchema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport {\n type PrincipalHistoryEvidencePage,\n writePrincipalHistoryEvidencePage,\n} from \"./principalHistoryEvidencePersistence\";\nimport { reclaimIncompletePrincipalHistoryStages } from \"./principalHistoryIncompleteRetention\";\nimport { reclaimPrincipalHistoryNodes } from \"./principalHistoryNodeRetention\";\nimport {\n releasePrincipalHistoryRoot,\n retainPrincipalHistoryRoot,\n} from \"./principalHistoryRootOwnership\";\nimport { reclaimUnpublishedPrincipalHistoryStages } from \"./principalHistoryUnpublishedRetention\";\n\n/** A stage's verification work; its retention hints are written by persistence. */\nexport type PrincipalHistoryStage = Omit<\n typeof principalHistoryStages.$inferSelect,\n \"scopeId\" | \"touchedAt\"\n>;\n\nconst principalHistoryStageColumns = {\n id: principalHistoryStages.id,\n organizationId: principalHistoryStages.organizationId,\n currentJson: principalHistoryStages.currentJson,\n afterVersion: principalHistoryStages.afterVersion,\n complete: principalHistoryStages.complete,\n progress: principalHistoryStages.progress,\n};\n\nclass PrincipalHistoryStageChangedError extends Error {\n readonly code = \"principal_history_stage_changed\";\n constructor() {\n super(\n \"Another operation changed the saved principal history; resume from its current state\",\n );\n this.name = \"PrincipalHistoryStageChangedError\";\n }\n}\n\n/** Another writer replaced this stage's progress between load and save. */\nexport function isPrincipalHistoryStageChangedError(error: unknown): boolean {\n return error instanceof PrincipalHistoryStageChangedError;\n}\n\nexport async function loadPrincipalHistoryStage(\n execSql: ExecSql,\n id: string,\n): Promise<PrincipalHistoryStage | null> {\n await ensureSqlTables(execSql, principalHistoryStageTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [row] = await db\n .select(principalHistoryStageColumns)\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, id))\n .limit(1);\n return row ?? null;\n}\n\n/** Publish accepted entries and index nodes atomically with authenticated progress. */\nexport async function savePrincipalHistoryStage(input: {\n readonly execSql: ExecSql;\n readonly stage: PrincipalHistoryStage;\n readonly evidence: PrincipalHistoryEvidencePage;\n readonly previousProgress: string | null;\n readonly stillCurrent: () => boolean;\n}): Promise<void> {\n const evidence = structuredClone(input.evidence);\n const stage = { ...input.stage, scopeId: evidence.scopeId };\n await ensureSqlTables(input.execSql, [\n ...principalHistoryStageTables,\n ...principalHistoryEvidenceTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const saved = await runtime.guardedTransaction(\n async (tx) => {\n const [previous] = await tx\n .select({ progress: principalHistoryStages.progress })\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, stage.id))\n .limit(1);\n if ((previous?.progress ?? null) !== input.previousProgress)\n throw new PrincipalHistoryStageChangedError();\n await writePrincipalHistoryEvidencePage(tx, evidence);\n const row = { ...stage, touchedAt: Date.now() };\n await tx\n .insert(principalHistoryStages)\n .values(row)\n .onConflictDoUpdate({ target: principalHistoryStages.id, set: row })\n .run();\n await retainPrincipalHistoryRoot(tx, {\n id: `stage:${stage.id}`,\n scopeId: evidence.scopeId,\n organizationId: stage.organizationId,\n rootHash: evidence.indexRootHash,\n });\n await reclaimIncompletePrincipalHistoryStages(tx, stage);\n await reclaimUnpublishedPrincipalHistoryStages(tx, stage);\n await reclaimPrincipalHistoryNodes(tx, evidence);\n },\n input.stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => saved.committed);\n}\n\nexport async function discardPrincipalHistoryStage(\n execSql: ExecSql,\n stage: PrincipalHistoryStage,\n stillCurrent: () => boolean,\n) {\n await ensureSqlTables(execSql, [\n ...principalHistoryStageTables,\n ...principalHistoryNodeRetentionTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const discarded = await runtime.guardedTransaction(\n async (db) => {\n const [current] = await db\n .select({ progress: principalHistoryStages.progress })\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, stage.id))\n .limit(1);\n if (current?.progress !== stage.progress) return;\n await releasePrincipalHistoryRoot(db, `stage:${stage.id}`);\n await db\n .delete(principalHistoryStages)\n .where(\n and(\n eq(principalHistoryStages.id, stage.id),\n eq(principalHistoryStages.progress, stage.progress),\n ),\n )\n .run();\n },\n stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => discarded.committed);\n}\n"]}
|
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
|
}
|