@tearleads/client-sdk 0.1.67 → 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/workflows/container-contents/storedContainerState.js +5 -1
- package/workflows/container-contents/storedContainerState.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
|
@@ -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
|
@@ -52,7 +52,11 @@ export async function hydrateStoredContainerState(input) {
|
|
|
52
52
|
metadataUpdates: bytesToBase64(initialUpdate),
|
|
53
53
|
});
|
|
54
54
|
await persistence.saveContainer(execSql, nextContainer, nextRecord);
|
|
55
|
-
|
|
55
|
+
// A device-first child's first metadata waits for its create. A root's
|
|
56
|
+
// never does: only registration creates a root remotely, and provisioning
|
|
57
|
+
// commits the root's metadata itself, so a queued copy would only be
|
|
58
|
+
// replayed after login as duplicate ops.
|
|
59
|
+
if (!container.metadataDocumentId && container.parentId !== null) {
|
|
56
60
|
await enqueuePendingContainerUpdate(execSql, persistence, {
|
|
57
61
|
containerId: container.id,
|
|
58
62
|
update: initialUpdate,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storedContainerState.js","sourceRoot":"","sources":["storedContainerState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EACL,+BAA+B,EAC/B,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EAGL,6BAA6B,GAE9B,MAAM,wBAAwB,CAAC;AAOhC,SAAS,4BAA4B,CAAC,KAGrC;IACC,OAAO;QACL,WAAW,EAAE,CAAC;QACd,eAAe,EAAE,IAAI;QACrB,gBAAgB,EAAE,IAAI;QACtB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,kBAAkB;QAC9C,kBAAkB,EAAE,IAAI;QACxB,sBAAsB,EAAE,IAAI;QAC5B,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE;QACtB,aAAa,EAAE,IAAI;QACnB,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,kBAAkB,EAAE,EAAE;KACvB,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAChC,IAAuC,EACvC,KAAwC;IAExC,OAAO,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC;AAC9D,CAAC;AAED,SAAS,iBAAiB,CACxB,SAA4C;IAE5C,MAAM,cAAc,GAClB,SAAS,CAAC,cAAc,IAAI,SAAS,CAAC,SAAS,IAAI,IAAI,CAAC;IAC1D,OAAO,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,KAIjD;IACC,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,KAAK,CAAC;IACxD,MAAM,EAAE,SAAS,EAAE,GAAG,eAAe,CAAC;IACtC,MAAM,GAAG,GAAG,MAAM,+BAA+B,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAChE,IAAI,aAAa,GAAG,SAAS,CAAC;IAC9B,IAAI,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC;IAExC,IAAI,UAAU,EAAE,eAAe,EAAE,CAAC;QAChC,aAAa,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG,0BAA0B,CACzC,GAAG,EACH,uBAAuB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAC5C,CAAC;QACF,aAAa,GAAG;YACd,GAAG,SAAS;YACZ,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB,CAAC;QACF,IAAI,yBAAyB,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,CAAC;YACxD,MAAM,WAAW,CAAC,aAAa,CAC7B,OAAO,EACP,aAAa,EACb,UAAU,EACV,iBAAiB,CAAC,SAAS,CAAC,CAC7B,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,CAAC;QACN,2BAA2B,CAAC,GAAG,EAAE;YAC/B,IAAI,EAAE,SAAS,CAAC,IAAI;YACpB,IAAI,EAAE,SAAS,CAAC,IAAI;SACrB,CAAC,CAAC;QACH,MAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC5C,UAAU,GAAG,4BAA4B,CAAC;YACxC,SAAS;YACT,eAAe,EAAE,aAAa,CAAC,aAAa,CAAC;SAC9C,CAAC,CAAC;QACH,MAAM,WAAW,CAAC,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;QAEpE,IAAI,CAAC,SAAS,CAAC,kBAAkB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"storedContainerState.js","sourceRoot":"","sources":["storedContainerState.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes, bytesToBase64 } from \"@tearleads/encoding\";\nimport { exportAllUpdates, importUpdates } from \"@tearleads/loro\";\nimport {\n createContainerMetadataDocument,\n getDefaultContainerName,\n readContainerMetadataValue,\n writeContainerMetadataValue,\n} from \"../../data/containers/containerMetadataDocument\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport {\n type ContainerContentsPersistence,\n type ContainerMetadataRecord,\n enqueuePendingContainerUpdate,\n type StoredContainerState,\n} from \"./containerPersistence\";\nimport type { ContainerState } from \"./remoteHydration/types\";\n\ntype SaveContainerOptions = Parameters<\n ContainerContentsPersistence[\"saveContainer\"]\n>[3];\n\nfunction createInitialContainerRecord(input: {\n container: StoredContainerState[\"container\"];\n metadataUpdates: string;\n}): ContainerMetadataRecord {\n return {\n accessEpoch: 1,\n accessStateHash: null,\n contentKeyBundle: null,\n documentId: input.container.metadataDocumentId,\n documentKekTargets: null,\n documentManifestBundle: null,\n id: input.container.id,\n lastCommitLsn: null,\n metadataUpdates: input.metadataUpdates,\n snapshotEndVersion: \"\",\n };\n}\n\nfunction metadataProjectionChanged(\n left: StoredContainerState[\"container\"],\n right: StoredContainerState[\"container\"],\n): boolean {\n return left.icon !== right.icon || left.name !== right.name;\n}\n\nfunction replaySaveOptions(\n container: StoredContainerState[\"container\"],\n): SaveContainerOptions {\n const localUpdatedAt =\n container.localUpdatedAt ?? container.updatedAt ?? null;\n return localUpdatedAt ? { localUpdatedAt } : undefined;\n}\n\nexport async function hydrateStoredContainerState(input: {\n execSql: ExecSql;\n persistence: ContainerContentsPersistence;\n storedContainer: StoredContainerState;\n}): Promise<ContainerState> {\n const { execSql, persistence, storedContainer } = input;\n const { container } = storedContainer;\n const doc = await createContainerMetadataDocument(container.id);\n let nextContainer = container;\n let nextRecord = storedContainer.record;\n\n if (nextRecord?.metadataUpdates) {\n importUpdates(doc, [base64ToBytes(nextRecord.metadataUpdates)]);\n const metadata = readContainerMetadataValue(\n doc,\n getDefaultContainerName(container.parentId),\n );\n nextContainer = {\n ...container,\n icon: metadata.icon,\n name: metadata.name,\n };\n if (metadataProjectionChanged(container, nextContainer)) {\n await persistence.saveContainer(\n execSql,\n nextContainer,\n nextRecord,\n replaySaveOptions(container),\n );\n }\n } else {\n writeContainerMetadataValue(doc, {\n icon: container.icon,\n name: container.name,\n });\n const initialUpdate = exportAllUpdates(doc);\n nextRecord = createInitialContainerRecord({\n container,\n metadataUpdates: bytesToBase64(initialUpdate),\n });\n await persistence.saveContainer(execSql, nextContainer, nextRecord);\n\n // A device-first child's first metadata waits for its create. A root's\n // never does: only registration creates a root remotely, and provisioning\n // commits the root's metadata itself, so a queued copy would only be\n // replayed after login as duplicate ops.\n if (!container.metadataDocumentId && container.parentId !== null) {\n await enqueuePendingContainerUpdate(execSql, persistence, {\n containerId: container.id,\n update: initialUpdate,\n });\n }\n }\n\n return { container: nextContainer, doc, record: nextRecord };\n}\n\n/** Materialize a CAS winner without writing repair rows from a stale caller. */\nexport async function materializeStoredContainerStateReadOnly(input: {\n storedContainer: StoredContainerState;\n}): Promise<ContainerState | null> {\n const { container, record } = input.storedContainer;\n if (!record) return null;\n const doc = await createContainerMetadataDocument(container.id);\n let nextContainer = container;\n if (record.metadataUpdates) {\n importUpdates(doc, [base64ToBytes(record.metadataUpdates)]);\n const metadata = readContainerMetadataValue(\n doc,\n getDefaultContainerName(container.parentId),\n );\n nextContainer = { ...container, icon: metadata.icon, name: metadata.name };\n } else {\n writeContainerMetadataValue(doc, {\n icon: container.icon,\n name: container.name,\n });\n }\n return { container: nextContainer, doc, record };\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { assertProjectionVerificationCurrent } from "../../../data/keyingProjectionVerification/types.js";
|
|
2
|
+
import { isPrincipalHistoryStageChangedError } from "../../../data/persistence/principalHistory/principalHistoryStagePersistence.js";
|
|
3
|
+
/**
|
|
4
|
+
* An acknowledged publication may supersede the exact head a queued recovery
|
|
5
|
+
* is staging (`persistAcknowledgedPrincipalCurrents` writes outside the
|
|
6
|
+
* recovery queue), and the recovery then loses its progress CAS. Resume once,
|
|
7
|
+
* in the same queue turn, from the stage the publication left, instead of
|
|
8
|
+
* failing the operation that asked for the policy. A second loss propagates.
|
|
9
|
+
*/
|
|
10
|
+
export async function resumeAfterSupersededStage(attempt, stillCurrent) {
|
|
11
|
+
try {
|
|
12
|
+
return await attempt();
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (!isPrincipalHistoryStageChangedError(error))
|
|
16
|
+
throw error;
|
|
17
|
+
assertProjectionVerificationCurrent(stillCurrent);
|
|
18
|
+
return attempt();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=supersededStageResume.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supersededStageResume.js","sourceRoot":"","sources":["supersededStageResume.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,kDAAkD,CAAC;AACvG,OAAO,EAAE,mCAAmC,EAAE,MAAM,6EAA6E,CAAC;AAElI;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,OAAyB,EACzB,YAA2B;IAE3B,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,EAAE,CAAC;IACzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,mCAAmC,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;QAC7D,mCAAmC,CAAC,YAAY,CAAC,CAAC;QAClD,OAAO,OAAO,EAAE,CAAC;IACnB,CAAC;AACH,CAAC","sourcesContent":["import { assertProjectionVerificationCurrent } from \"../../../data/keyingProjectionVerification/types\";\nimport { isPrincipalHistoryStageChangedError } from \"../../../data/persistence/principalHistory/principalHistoryStagePersistence\";\n\n/**\n * An acknowledged publication may supersede the exact head a queued recovery\n * is staging (`persistAcknowledgedPrincipalCurrents` writes outside the\n * recovery queue), and the recovery then loses its progress CAS. Resume once,\n * in the same queue turn, from the stage the publication left, instead of\n * failing the operation that asked for the policy. A second loss propagates.\n */\nexport async function resumeAfterSupersededStage<T>(\n attempt: () => Promise<T>,\n stillCurrent: () => boolean,\n): Promise<T> {\n try {\n return await attempt();\n } catch (error) {\n if (!isPrincipalHistoryStageChangedError(error)) throw error;\n assertProjectionVerificationCurrent(stillCurrent);\n return attempt();\n }\n}\n"]}
|
|
@@ -8,6 +8,7 @@ import { PrincipalHistoryRecoveryRaceError, PrincipalPolicyHistoryReadError, } f
|
|
|
8
8
|
import { apiPrincipalPolicyPageReader, } from "./principalPolicyHistoryReader.js";
|
|
9
9
|
import { createPrincipalCurrentRecoveryBatch } from "./recoveryOperation/principalCurrentRecoveryBatch.js";
|
|
10
10
|
import { recoverWithPrincipalLocalPreference } from "./recoveryOperation/principalRecoveryLocalPreference.js";
|
|
11
|
+
import { resumeAfterSupersededStage } from "./recoveryOperation/supersededStageResume.js";
|
|
11
12
|
/** Keep the private recovery key inside its runtime lease while resolving cited evidence. */
|
|
12
13
|
export function createRuntimePrincipalPolicyResolver(runtime, options = {}) {
|
|
13
14
|
const resolve = createRuntimePrincipalPolicyCurrentResolver(runtime);
|
|
@@ -15,6 +16,16 @@ export function createRuntimePrincipalPolicyResolver(runtime, options = {}) {
|
|
|
15
16
|
? (input) => resolve({ ...input, preferLocalCurrent: options.preferLocalCurrent })
|
|
16
17
|
: undefined;
|
|
17
18
|
}
|
|
19
|
+
/** One recovery attempt for a resolver request through the operation's batch. */
|
|
20
|
+
function recoverPolicyCurrent(batch, options, input) {
|
|
21
|
+
return input.reference
|
|
22
|
+
? recoverWithPrincipalLocalPreference(batch.recover, {
|
|
23
|
+
...options,
|
|
24
|
+
reference: input.reference,
|
|
25
|
+
historyPage: input.historyPage,
|
|
26
|
+
}, input.preferLocalCurrent === true || input.preferLocalHistory === true, input.preferLocalHistory === true)
|
|
27
|
+
: batch.discover(options);
|
|
28
|
+
}
|
|
18
29
|
/** Keep current artifacts with their verified evidence and private runtime lifetime. */
|
|
19
30
|
export function createRuntimePrincipalPolicyCurrentResolver(runtime) {
|
|
20
31
|
const lease = readPrincipalHistoryProtection(runtime);
|
|
@@ -52,15 +63,7 @@ export function createRuntimePrincipalPolicyCurrentResolver(runtime) {
|
|
|
52
63
|
resolveTrustedUserIdentity: runtime.resolveTrustedUserIdentity,
|
|
53
64
|
stillCurrent,
|
|
54
65
|
};
|
|
55
|
-
const
|
|
56
|
-
const result = input.reference
|
|
57
|
-
? await recoverWithPrincipalLocalPreference(batch.recover, {
|
|
58
|
-
...options,
|
|
59
|
-
reference: input.reference,
|
|
60
|
-
historyPage: input.historyPage,
|
|
61
|
-
}, input.preferLocalCurrent === true ||
|
|
62
|
-
input.preferLocalHistory === true, input.preferLocalHistory === true)
|
|
63
|
-
: await batch.discover(options);
|
|
66
|
+
const result = await resumeAfterSupersededStage(() => recoverPolicyCurrent(recoverFor(input.recoveryBatch), options, input), stillCurrent);
|
|
64
67
|
// Reused evidence stays bound to every producer lifetime.
|
|
65
68
|
const resultCurrent = () => stillCurrent() && result.stillCurrent();
|
|
66
69
|
assertProjectionVerificationCurrent(resultCurrent);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtimePolicyRecovery.js","sourceRoot":"","sources":["runtimePolicyRecovery.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PrincipalPolicyPageCurrent } from \"@tearleads/api-client\";\nimport { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { ProjectionDependencyUnavailableError } from \"../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport { runWithSecurityIncidentReporting } from \"../../data/keyingProjectionVerification/error\";\nimport {\n assertProjectionVerificationCurrent,\n type PrincipalPolicyResolveRequest,\n type ReferencedPrincipalPolicyWarmer,\n type ResolvedPrincipalPolicyEvidence,\n} from \"../../data/keyingProjectionVerification/types\";\nimport type { PrincipalHistoryProtectionLease } from \"../../data/principals/principalHistoryProtection\";\nimport { readPrincipalHistoryProtection } from \"../../data/principals/principalHistoryRuntime\";\nimport { getOrCreatePrincipalPolicyCoordinator } from \"../../data/principals/principalPolicyCoordinator\";\nimport type { SecurityIncidentReporter } from \"../../data/shared/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport type { RecoveredPrincipalHistoryPage } from \"./historyRecovery/loadRecoveredPrincipalHistoryPage\";\nimport {\n PrincipalHistoryRecoveryRaceError,\n PrincipalPolicyHistoryReadError,\n} from \"./historyRecovery/principalHistoryRecoveryTypes\";\nimport type { ScopedPrincipalPolicyRecovery } from \"./historyRecovery/recoverScopedPrincipalPolicyHistory\";\nimport {\n apiPrincipalPolicyPageReader,\n type PrincipalPolicyHistoryReader,\n} from \"./principalPolicyHistoryReader\";\nimport { createPrincipalCurrentRecoveryBatch } from \"./recoveryOperation/principalCurrentRecoveryBatch\";\nimport { recoverWithPrincipalLocalPreference } from \"./recoveryOperation/principalRecoveryLocalPreference\";\n\nexport interface PrincipalPolicyRecoveryRuntime {\n readonly apiClient: Partial<PrincipalPolicyHistoryReader>;\n readonly infra: { readonly execSql: ExecSql };\n readonly state?: { readonly online: boolean } | undefined;\n readonly util: { readonly reportSecurityIncident: SecurityIncidentReporter };\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly withPrincipalHistoryProtection?:\n | PrincipalHistoryProtectionLease\n | undefined;\n}\n\n/** Keep the private recovery key inside its runtime lease while resolving cited evidence. */\nexport function createRuntimePrincipalPolicyResolver(\n runtime: PrincipalPolicyRecoveryRuntime,\n options: { readonly preferLocalCurrent?: boolean } = {},\n): ReferencedPrincipalPolicyWarmer[\"resolveReference\"] {\n const resolve = createRuntimePrincipalPolicyCurrentResolver(runtime);\n return resolve\n ? (input) =>\n resolve({ ...input, preferLocalCurrent: options.preferLocalCurrent })\n : undefined;\n}\n\nexport interface ResolvedPrincipalPolicyCurrent\n extends ResolvedPrincipalPolicyEvidence {\n readonly current: PrincipalPolicyPageCurrent;\n /** Rows end at the selected reference; current/policy may describe a newer recovered head. */\n readonly historyPage?: RecoveredPrincipalHistoryPage | undefined;\n}\n\ninterface PrincipalPolicyCurrentRequest\n extends Omit<PrincipalPolicyResolveRequest, \"reference\"> {\n readonly reference?: PrincipalPolicyResolveRequest[\"reference\"] | undefined;\n /** A read-model caller has selected this exact signed head; not a freshness read. */\n readonly preferLocalCurrent?: boolean | undefined;\n readonly historyPage?: { readonly beforeVersion?: number } | undefined;\n}\n\n/** Keep current artifacts with their verified evidence and private runtime lifetime. */\nexport function createRuntimePrincipalPolicyCurrentResolver(\n runtime: PrincipalPolicyRecoveryRuntime,\n):\n | ((\n input: PrincipalPolicyCurrentRequest,\n ) => Promise<ResolvedPrincipalPolicyCurrent>)\n | undefined {\n const lease = readPrincipalHistoryProtection(runtime);\n const historyReader = apiPrincipalPolicyPageReader(runtime.apiClient);\n if (!lease || !historyReader) return undefined;\n const batches = new WeakMap<\n object,\n ReturnType<typeof createPrincipalCurrentRecoveryBatch>\n >();\n const recoverFor = (batch: object | undefined) => {\n if (!batch) return createPrincipalCurrentRecoveryBatch();\n let recover = batches.get(batch);\n if (!recover) {\n recover = createPrincipalCurrentRecoveryBatch();\n batches.set(batch, recover);\n }\n return recover;\n };\n return (input) =>\n getOrCreatePrincipalPolicyCoordinator(runtime.infra.execSql).queueRecovery(\n input.organizationId,\n () =>\n runWithSecurityIncidentReporting(\n runtime.util.reportSecurityIncident,\n {\n objectId: input.reference?.principalId ?? input.organizationId,\n objectKind: \"principal\",\n operation: \"principal.policy.recover\",\n organizationId: input.organizationId,\n },\n () =>\n lease(async ({ protection, stillCurrent: leaseCurrent }) => {\n const stillCurrent = () =>\n leaseCurrent() && input.stillCurrent?.() !== false;\n assertProjectionVerificationCurrent(stillCurrent);\n assertHistoryPageRequest(input);\n const offline = runtime.state?.online === false;\n try {\n const options = {\n historyReader,\n execSql: runtime.infra.execSql,\n offline,\n organizationId: input.organizationId,\n protection,\n resolveTrustedUserIdentity:\n runtime.resolveTrustedUserIdentity,\n stillCurrent,\n };\n const batch = recoverFor(input.recoveryBatch);\n const result: ScopedPrincipalPolicyRecovery = input.reference\n ? await recoverWithPrincipalLocalPreference(\n batch.recover,\n {\n ...options,\n reference: input.reference,\n historyPage: input.historyPage,\n },\n input.preferLocalCurrent === true ||\n input.preferLocalHistory === true,\n input.preferLocalHistory === true,\n )\n : await batch.discover(options);\n // Reused evidence stays bound to every producer lifetime.\n const resultCurrent = () =>\n stillCurrent() && result.stillCurrent();\n assertProjectionVerificationCurrent(resultCurrent);\n return {\n current: result.current,\n organizationId: input.organizationId,\n policy: result.policy,\n dependencies: result.dependencies,\n historyPage: result.historyPage,\n stillCurrent: resultCurrent,\n };\n } catch (error) {\n assertProjectionVerificationCurrent(stillCurrent);\n if (\n error instanceof PrincipalPolicyHistoryReadError ||\n error instanceof PrincipalHistoryRecoveryRaceError ||\n (error instanceof KeyingVerificationError &&\n error.code === \"missing_dependency\")\n )\n throw new ProjectionDependencyUnavailableError(error.message);\n throw error;\n }\n }),\n ),\n );\n}\n\nfunction assertHistoryPageRequest(input: PrincipalPolicyCurrentRequest) {\n if (!input.historyPage) return;\n const before = input.historyPage.beforeVersion;\n if (\n !input.reference ||\n (before !== undefined &&\n (!Number.isSafeInteger(before) ||\n before <= 1 ||\n before > input.reference.version + 1))\n )\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"History display requires a selected head and an in-range cursor\",\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runtimePolicyRecovery.js","sourceRoot":"","sources":["runtimePolicyRecovery.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PrincipalPolicyPageCurrent } from \"@tearleads/api-client\";\nimport { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { ProjectionDependencyUnavailableError } from \"../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport { runWithSecurityIncidentReporting } from \"../../data/keyingProjectionVerification/error\";\nimport {\n assertProjectionVerificationCurrent,\n type PrincipalPolicyResolveRequest,\n type ReferencedPrincipalPolicyWarmer,\n type ResolvedPrincipalPolicyEvidence,\n} from \"../../data/keyingProjectionVerification/types\";\nimport type { PrincipalHistoryProtectionLease } from \"../../data/principals/principalHistoryProtection\";\nimport { readPrincipalHistoryProtection } from \"../../data/principals/principalHistoryRuntime\";\nimport { getOrCreatePrincipalPolicyCoordinator } from \"../../data/principals/principalPolicyCoordinator\";\nimport type { SecurityIncidentReporter } from \"../../data/shared/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport type { RecoveredPrincipalHistoryPage } from \"./historyRecovery/loadRecoveredPrincipalHistoryPage\";\nimport {\n PrincipalHistoryRecoveryRaceError,\n PrincipalPolicyHistoryReadError,\n} from \"./historyRecovery/principalHistoryRecoveryTypes\";\nimport type { ScopedPrincipalPolicyRecovery } from \"./historyRecovery/recoverScopedPrincipalPolicyHistory\";\nimport {\n apiPrincipalPolicyPageReader,\n type PrincipalPolicyHistoryReader,\n} from \"./principalPolicyHistoryReader\";\nimport { createPrincipalCurrentRecoveryBatch } from \"./recoveryOperation/principalCurrentRecoveryBatch\";\nimport { recoverWithPrincipalLocalPreference } from \"./recoveryOperation/principalRecoveryLocalPreference\";\nimport { resumeAfterSupersededStage } from \"./recoveryOperation/supersededStageResume\";\n\nexport interface PrincipalPolicyRecoveryRuntime {\n readonly apiClient: Partial<PrincipalPolicyHistoryReader>;\n readonly infra: { readonly execSql: ExecSql };\n readonly state?: { readonly online: boolean } | undefined;\n readonly util: { readonly reportSecurityIncident: SecurityIncidentReporter };\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly withPrincipalHistoryProtection?:\n | PrincipalHistoryProtectionLease\n | undefined;\n}\n\n/** Keep the private recovery key inside its runtime lease while resolving cited evidence. */\nexport function createRuntimePrincipalPolicyResolver(\n runtime: PrincipalPolicyRecoveryRuntime,\n options: { readonly preferLocalCurrent?: boolean } = {},\n): ReferencedPrincipalPolicyWarmer[\"resolveReference\"] {\n const resolve = createRuntimePrincipalPolicyCurrentResolver(runtime);\n return resolve\n ? (input) =>\n resolve({ ...input, preferLocalCurrent: options.preferLocalCurrent })\n : undefined;\n}\n\nexport interface ResolvedPrincipalPolicyCurrent\n extends ResolvedPrincipalPolicyEvidence {\n readonly current: PrincipalPolicyPageCurrent;\n /** Rows end at the selected reference; current/policy may describe a newer recovered head. */\n readonly historyPage?: RecoveredPrincipalHistoryPage | undefined;\n}\n\ninterface PrincipalPolicyCurrentRequest\n extends Omit<PrincipalPolicyResolveRequest, \"reference\"> {\n readonly reference?: PrincipalPolicyResolveRequest[\"reference\"] | undefined;\n /** A read-model caller has selected this exact signed head; not a freshness read. */\n readonly preferLocalCurrent?: boolean | undefined;\n readonly historyPage?: { readonly beforeVersion?: number } | undefined;\n}\n\n/** One recovery attempt for a resolver request through the operation's batch. */\nfunction recoverPolicyCurrent(\n batch: ReturnType<typeof createPrincipalCurrentRecoveryBatch>,\n options: Parameters<\n ReturnType<typeof createPrincipalCurrentRecoveryBatch>[\"discover\"]\n >[0],\n input: PrincipalPolicyCurrentRequest,\n): Promise<ScopedPrincipalPolicyRecovery> {\n return input.reference\n ? recoverWithPrincipalLocalPreference(\n batch.recover,\n {\n ...options,\n reference: input.reference,\n historyPage: input.historyPage,\n },\n input.preferLocalCurrent === true || input.preferLocalHistory === true,\n input.preferLocalHistory === true,\n )\n : batch.discover(options);\n}\n\n/** Keep current artifacts with their verified evidence and private runtime lifetime. */\nexport function createRuntimePrincipalPolicyCurrentResolver(\n runtime: PrincipalPolicyRecoveryRuntime,\n):\n | ((\n input: PrincipalPolicyCurrentRequest,\n ) => Promise<ResolvedPrincipalPolicyCurrent>)\n | undefined {\n const lease = readPrincipalHistoryProtection(runtime);\n const historyReader = apiPrincipalPolicyPageReader(runtime.apiClient);\n if (!lease || !historyReader) return undefined;\n const batches = new WeakMap<\n object,\n ReturnType<typeof createPrincipalCurrentRecoveryBatch>\n >();\n const recoverFor = (batch: object | undefined) => {\n if (!batch) return createPrincipalCurrentRecoveryBatch();\n let recover = batches.get(batch);\n if (!recover) {\n recover = createPrincipalCurrentRecoveryBatch();\n batches.set(batch, recover);\n }\n return recover;\n };\n return (input) =>\n getOrCreatePrincipalPolicyCoordinator(runtime.infra.execSql).queueRecovery(\n input.organizationId,\n () =>\n runWithSecurityIncidentReporting(\n runtime.util.reportSecurityIncident,\n {\n objectId: input.reference?.principalId ?? input.organizationId,\n objectKind: \"principal\",\n operation: \"principal.policy.recover\",\n organizationId: input.organizationId,\n },\n () =>\n lease(async ({ protection, stillCurrent: leaseCurrent }) => {\n const stillCurrent = () =>\n leaseCurrent() && input.stillCurrent?.() !== false;\n assertProjectionVerificationCurrent(stillCurrent);\n assertHistoryPageRequest(input);\n const offline = runtime.state?.online === false;\n try {\n const options = {\n historyReader,\n execSql: runtime.infra.execSql,\n offline,\n organizationId: input.organizationId,\n protection,\n resolveTrustedUserIdentity:\n runtime.resolveTrustedUserIdentity,\n stillCurrent,\n };\n const result = await resumeAfterSupersededStage(\n () =>\n recoverPolicyCurrent(\n recoverFor(input.recoveryBatch),\n options,\n input,\n ),\n stillCurrent,\n );\n // Reused evidence stays bound to every producer lifetime.\n const resultCurrent = () =>\n stillCurrent() && result.stillCurrent();\n assertProjectionVerificationCurrent(resultCurrent);\n return {\n current: result.current,\n organizationId: input.organizationId,\n policy: result.policy,\n dependencies: result.dependencies,\n historyPage: result.historyPage,\n stillCurrent: resultCurrent,\n };\n } catch (error) {\n assertProjectionVerificationCurrent(stillCurrent);\n if (\n error instanceof PrincipalPolicyHistoryReadError ||\n error instanceof PrincipalHistoryRecoveryRaceError ||\n (error instanceof KeyingVerificationError &&\n error.code === \"missing_dependency\")\n )\n throw new ProjectionDependencyUnavailableError(error.message);\n throw error;\n }\n }),\n ),\n );\n}\n\nfunction assertHistoryPageRequest(input: PrincipalPolicyCurrentRequest) {\n if (!input.historyPage) return;\n const before = input.historyPage.beforeVersion;\n if (\n !input.reference ||\n (before !== undefined &&\n (!Number.isSafeInteger(before) ||\n before <= 1 ||\n before > input.reference.version + 1))\n )\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"History display requires a selected head and an in-range cursor\",\n );\n}\n"]}
|