@tearleads/client-sdk 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/organizations/organizationGroupHistory.js +4 -7
- package/client/organizations/organizationGroupHistory.js.map +1 -1
- package/client/organizations/organizationReadModels.js +2 -86
- package/client/organizations/organizationReadModels.js.map +1 -1
- package/client/organizations/principalMutations.js +53 -133
- package/client/organizations/principalMutations.js.map +1 -1
- package/client/principalMutationDispatch.js +2 -2
- package/client/principalMutationDispatch.js.map +1 -1
- package/data/keyingProjectionVerification/principalPolicyVerification.js +5 -5
- package/data/keyingProjectionVerification/principalPolicyVerification.js.map +1 -1
- package/data/keyingProjectionVerification/resolvedPrincipalPolicy.js +21 -7
- package/data/keyingProjectionVerification/resolvedPrincipalPolicy.js.map +1 -1
- package/data/keyingProjectionVerification/types.d.ts +1 -8
- package/data/keyingProjectionVerification/types.js +0 -9
- package/data/keyingProjectionVerification/types.js.map +1 -1
- package/data/keyingProjectionVerification.d.ts +1 -1
- package/data/keyingProjectionVerification.js.map +1 -1
- package/data/persistence/documents/documentsPersistence.js +7 -0
- package/data/persistence/documents/documentsPersistence.js.map +1 -1
- package/data/persistence/documents/orphanBlobPersistence.d.ts +13 -0
- package/data/persistence/documents/orphanBlobPersistence.js +2 -0
- package/data/persistence/documents/orphanBlobPersistence.js.map +1 -0
- package/data/persistence/documents/types.d.ts +2 -0
- package/data/persistence/documents/types.js.map +1 -1
- package/data/persistence/principalHistoryPrefixPersistence.d.ts +5 -1
- package/data/persistence/principalHistoryPrefixPersistence.js +22 -5
- package/data/persistence/principalHistoryPrefixPersistence.js.map +1 -1
- package/data/principals/principalMutationJournalShape.d.ts +1 -1
- package/data/principals/principalMutationJournalShape.js +2 -2
- package/data/principals/principalMutationJournalShape.js.map +1 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/stores/documents/documentStore/attachmentHydration.js +2 -2
- package/stores/documents/documentStore/attachmentHydration.js.map +1 -1
- package/stores/documents/documentStore/attachments.js +7 -5
- package/stores/documents/documentStore/attachments.js.map +1 -1
- package/stores/documents/documentStore/discard.js +3 -2
- package/stores/documents/documentStore/discard.js.map +1 -1
- package/stores/documents/documentStore/orphanMaintenance.js +3 -5
- package/stores/documents/documentStore/orphanMaintenance.js.map +1 -1
- package/stores/documents/documentStore/persistence.js +3 -4
- package/stores/documents/documentStore/persistence.js.map +1 -1
- package/stores/documents/documentStore/syncDetachedAttachments.js +3 -2
- package/stores/documents/documentStore/syncDetachedAttachments.js.map +1 -1
- package/stores/documents/documentStore/syncRequest.js +3 -4
- package/stores/documents/documentStore/syncRequest.js.map +1 -1
- package/workflows/container-contents/container-state/groupGrantVerification.js +23 -74
- package/workflows/container-contents/container-state/groupGrantVerification.js.map +1 -1
- package/workflows/container-contents/container-state/groupShareEpoch.js +6 -23
- package/workflows/container-contents/container-state/groupShareEpoch.js.map +1 -1
- package/workflows/container-contents/container-state/remote.js +5 -3
- 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/documentLinks.d.ts +1 -1
- package/workflows/container-contents/documentLinks.js.map +1 -1
- package/workflows/container-contents/documentPurge.d.ts +1 -1
- package/workflows/container-contents/documentPurge.js +4 -4
- package/workflows/container-contents/documentPurge.js.map +1 -1
- package/workflows/container-contents/metadataRemoteSync.d.ts +1 -1
- package/workflows/container-contents/metadataRemoteSync.js.map +1 -1
- package/workflows/container-contents/remoteHydration/types.d.ts +2 -2
- package/workflows/container-contents/remoteHydration/types.js.map +1 -1
- package/workflows/container-contents/remoteHydration/verifiedDestination.js +6 -7
- package/workflows/container-contents/remoteHydration/verifiedDestination.js.map +1 -1
- package/workflows/containers/child/runtimeCurrentGroupShare.js +1 -1
- package/workflows/containers/child/runtimeCurrentGroupShare.js.map +1 -1
- package/workflows/documents/orphanBlobReclaimState.js +20 -0
- package/workflows/documents/orphanBlobReclaimState.js.map +1 -0
- package/workflows/documents/orphanBlobReclaims.d.ts +3 -2
- package/workflows/documents/orphanBlobReclaims.js +49 -47
- package/workflows/documents/orphanBlobReclaims.js.map +1 -1
- package/workflows/organizations/groupMetadataRuntime.js +11 -8
- package/workflows/organizations/groupMetadataRuntime.js.map +1 -1
- package/workflows/organizations/organizationGroupNames.js +8 -55
- package/workflows/organizations/organizationGroupNames.js.map +1 -1
- package/workflows/organizations/principalOperationReceipt.js +1 -1
- package/workflows/organizations/principalOperationReceipt.js.map +1 -1
- package/workflows/organizations/runtimeGroupMetadataAuthority.js +16 -0
- package/workflows/organizations/runtimeGroupMetadataAuthority.js.map +1 -0
- package/workflows/principals/runtimePolicyWarmer.js +29 -27
- package/workflows/principals/runtimePolicyWarmer.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groupShareEpoch.js","sourceRoot":"","sources":["groupShareEpoch.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"groupShareEpoch.js","sourceRoot":"","sources":["groupShareEpoch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oCAAoC,EAAE,MAAM,kEAAkE,CAAC;AACxH,OAAO,EAAE,wCAAwC,EAAE,MAAM,oDAAoD,CAAC;AAG9G,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,KAQjD;IACC,IAAI,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAClD,MAAM,WAAW,GAAG,wCAAwC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5E,IAAI,CAAC,WAAW;QACd,MAAM,IAAI,oCAAoC,CAC5C,+CAA+C,CAChD,CAAC;IACJ,OAAO,WAAW,CAChB;QACE,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;KACjD,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CACtC,CAAC;AACJ,CAAC","sourcesContent":["import { ProjectionDependencyUnavailableError } from \"../../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport { createRuntimeCurrentSharePrincipalPolicy } from \"../../containers/child/currentSharePrincipalPolicy\";\nimport type { ContainerWorkflowRuntime } from \"./types\";\n\nexport async function resolveCurrentGroupKeyEpoch(input: {\n // Bound here, in the one verified load a duplicate share performs, so a\n // duplicate never reports success for a group the user did not choose.\n expectedGroupName?: string | undefined;\n groupId: string;\n organizationId: string;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<number | null> {\n if (input.stillCurrent?.() === false) return null;\n const readCurrent = createRuntimeCurrentSharePrincipalPolicy(input.runtime);\n if (!readCurrent)\n throw new ProjectionDependencyUnavailableError(\n \"Group sharing requires private paged recovery\",\n );\n return readCurrent(\n {\n expectedGroupName: input.expectedGroupName,\n groupId: input.groupId,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent ?? (() => true),\n },\n async ({ policy }) => policy.keyEpoch,\n );\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isContainerNotFoundFailure } from "../../../data/containers/shared/mutationFailures.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ProjectionDependencyUnavailableError } from "../../../data/keyingProjectionVerification/dependencyUnavailable.js";
|
|
3
|
+
import { moveRemoteContainer as moveRemoteContainerMutation, readContainerMutationMetadataDocumentId, referencedPrincipalHeadsFromContainerMutationResponse, shareRemoteContainer as shareRemoteContainerMutation, } from "../../containers/index.js";
|
|
3
4
|
import { createRuntimeCurrentGroupShare } from "../../containers/child/runtimeCurrentGroupShare.js";
|
|
4
5
|
import { resolveDocumentCreateAuthor } from "../../documents/index.js";
|
|
5
6
|
import { createRuntimeGroupMetadataAccess } from "../../organizations/groupMetadataRuntime.js";
|
|
@@ -75,8 +76,9 @@ export async function shareRemoteContainerWithGroup(input) {
|
|
|
75
76
|
return null;
|
|
76
77
|
}
|
|
77
78
|
const { apiClient, author, execSql, secretKey: targetSecretKey } = writer;
|
|
78
|
-
const shareWithGroup = createRuntimeCurrentGroupShare(input.runtime)
|
|
79
|
-
|
|
79
|
+
const shareWithGroup = createRuntimeCurrentGroupShare(input.runtime);
|
|
80
|
+
if (!shareWithGroup)
|
|
81
|
+
throw new ProjectionDependencyUnavailableError("Group sharing requires private paged recovery");
|
|
80
82
|
const shared = await shareWithGroup({
|
|
81
83
|
reportSecurityIncident: input.runtime.util.reportSecurityIncident,
|
|
82
84
|
accessLevel: input.accessLevel,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.js","sourceRoot":"","sources":["remote.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type {\n ContainerMutationResponse,\n ContainerWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { isContainerNotFoundFailure } from \"../../../data/containers/shared/mutationFailures\";\nimport type { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport {\n moveRemoteContainer as moveRemoteContainerMutation,\n readContainerMutationMetadataDocumentId,\n referencedPrincipalHeadsFromContainerMutationResponse,\n shareRemoteContainer as shareRemoteContainerMutation,\n shareRemoteContainerWithGroup as shareRemoteContainerWithGroupMutation,\n} from \"../../containers\";\nimport { createRuntimeCurrentGroupShare } from \"../../containers/child/runtimeCurrentGroupShare\";\nimport { resolveDocumentCreateAuthor } from \"../../documents\";\nimport { createRuntimeGroupMetadataAccess } from \"../../organizations/groupMetadataRuntime\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../principals/runtimePolicyWarmer\";\nimport {\n type ContainerAlreadyCommitted,\n createRemoteContainerWithMetadataDocument,\n} from \"./createWithMetadata\";\nimport type {\n ContainerWorkflowRuntime,\n CreatedRemoteContainerState,\n RemoteContainer,\n SharedRemoteContainerState,\n} from \"./types\";\n\n/**\n * Resolve the author, api client, executor, and encapsulation secret a remote\n * container mutation needs, logging a skip for the given operation when the\n * writer context is unavailable.\n */\nfunction resolveContainerWriterContext(\n runtime: ContainerWorkflowRuntime,\n operation:\n | \"container create\"\n | \"container group share\"\n | \"container move\"\n | \"container share\",\n) {\n const author = resolveDocumentCreateAuthor(runtime);\n const secretKey = runtime.crypto.encapsulationKeyPair?.secretKey;\n if (!author || !secretKey) {\n runtime.util.log(\n `Container contents: skipped ${operation} because the writer context is unavailable.`,\n );\n return null;\n }\n\n return {\n apiClient: runtime.apiClient,\n author,\n execSql: runtime.infra.execSql,\n secretKey,\n };\n}\n\nexport async function createRemoteContainer(input: {\n systemSlot?: ContainerSystemSlot | null | undefined;\n containerId: string;\n parentContainerId: string;\n parentProjection?: ContainerWriterProjectionResponse | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<CreatedRemoteContainerState | ContainerAlreadyCommitted | null> {\n return createRemoteContainerWithMetadataDocument(input);\n}\n\nfunction sharedRemoteContainerStateFromMutation(\n previousProjection: ContainerWriterProjectionResponse,\n response: ContainerMutationResponse,\n): SharedRemoteContainerState {\n if (\n response.containerId !== previousProjection.containerId ||\n response.organizationId !== previousProjection.organizationId\n )\n throw new Error(\"Container mutation response projection mismatch\");\n return {\n accessManifestHash: response.manifestHead.manifestHash,\n accessEpoch: response.manifestHead.epoch,\n createdAt: response.createdAt,\n metadataDocumentId: readContainerMutationMetadataDocumentId({ response }),\n referencedPrincipalHeads:\n referencedPrincipalHeadsFromContainerMutationResponse({ response }),\n updatedAt: response.updatedAt,\n // The next read obtains the post-commit proof. A failed read cannot undo\n // acknowledgement of this successful mutation.\n writerProjection: null,\n };\n}\n\nexport async function shareRemoteContainer(input: {\n accessLevel: \"read\" | \"write\" | \"admin\";\n containerId: string;\n previousProjection: ContainerWriterProjectionResponse;\n recipientUserId: string;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<SharedRemoteContainerState | null> {\n const writer = resolveContainerWriterContext(\n input.runtime,\n \"container share\",\n );\n if (!writer) {\n return null;\n }\n const { apiClient, author, execSql, secretKey: targetSecretKey } = writer;\n\n const shared = await shareRemoteContainerMutation({\n reportSecurityIncident: input.runtime.util.reportSecurityIncident,\n accessLevel: input.accessLevel,\n apiClient,\n author,\n containerId: input.containerId,\n execSql,\n previousProjection: input.previousProjection,\n recipientUserId: input.recipientUserId,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n resolveTrustedUserIdentity: input.runtime.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n targetSecretKey,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n input.runtime,\n ),\n });\n if (!shared) {\n return null;\n }\n\n return sharedRemoteContainerStateFromMutation(\n input.previousProjection,\n shared.response,\n );\n}\n\nexport async function shareRemoteContainerWithGroup(input: {\n accessLevel: \"read\" | \"write\" | \"admin\";\n containerId: string;\n expectedGroupName?: string | undefined;\n knownContainerKeks?: ReadonlyMap<string, Uint8Array> | undefined;\n previousProjection: ContainerWriterProjectionResponse;\n recipientGroupId: string;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<SharedRemoteContainerState | null> {\n const writer = resolveContainerWriterContext(\n input.runtime,\n \"container group share\",\n );\n if (!writer) {\n return null;\n }\n const { apiClient, author, execSql, secretKey: targetSecretKey } = writer;\n\n const shareWithGroup =\n createRuntimeCurrentGroupShare(input.runtime) ??\n shareRemoteContainerWithGroupMutation;\n const shared = await shareWithGroup({\n reportSecurityIncident: input.runtime.util.reportSecurityIncident,\n accessLevel: input.accessLevel,\n apiClient,\n author,\n containerId: input.containerId,\n execSql,\n expectedGroupName: input.expectedGroupName ?? null,\n readEncryptedName: (bundle) =>\n createRuntimeGroupMetadataAccess(\n input.runtime,\n author.organizationId,\n input.stillCurrent,\n ).readName(bundle),\n knownContainerKeks: input.knownContainerKeks,\n previousProjection: input.previousProjection,\n recipientGroupId: input.recipientGroupId,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n resolveTrustedUserIdentity: input.runtime.resolveTrustedUserIdentity,\n signingKeyPair: input.runtime.crypto.signingKeyPair,\n stillCurrent: input.stillCurrent,\n targetSecretKey,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n input.runtime,\n ),\n });\n if (!shared) {\n return null;\n }\n\n return sharedRemoteContainerStateFromMutation(\n input.previousProjection,\n shared.response,\n );\n}\n\nexport async function moveRemoteContainer(input: {\n containerId: string;\n parentContainerId: string;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<RemoteContainer | null> {\n const writer = resolveContainerWriterContext(input.runtime, \"container move\");\n if (!writer) {\n return null;\n }\n const { apiClient, author, execSql, secretKey: targetSecretKey } = writer;\n\n const moved = await moveRemoteContainerMutation({\n reportSecurityIncident: input.runtime.util.reportSecurityIncident,\n apiClient,\n author,\n containerId: input.containerId,\n destinationParentContainerId: input.parentContainerId,\n execSql,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n stillCurrent: input.stillCurrent,\n targetSecretKey,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n input.runtime,\n ),\n });\n if (!moved) {\n return null;\n }\n\n return {\n createdAt: moved.response.createdAt,\n effectiveAccessLevel: \"admin\",\n id: moved.response.containerId,\n organizationId: moved.response.organizationId,\n parentId: moved.response.parentId,\n metadataDocumentId: readContainerMutationMetadataDocumentId({\n response: moved.response,\n }),\n metadataAccessEpoch: moved.response.manifestHead.epoch,\n metadataAccessStateHash: moved.response.manifestHead.manifestHash,\n metadataReferencedPrincipals:\n referencedPrincipalHeadsFromContainerMutationResponse({\n response: moved.response,\n }),\n updatedAt: moved.response.updatedAt,\n };\n}\n\nexport async function deleteRemoteContainer(input: {\n containerId: string;\n organizationId: string;\n runtime: ContainerWorkflowRuntime;\n}): Promise<{ deletedAt: string } | null> {\n const deleteResult = await input.runtime.apiClient.deleteContainerResult(\n input.containerId,\n {\n expectedPaymentRequiredOrganizationId: input.organizationId,\n reportErrors: false,\n },\n );\n if (!deleteResult.ok && !isContainerNotFoundFailure(deleteResult)) {\n deleteResult.report();\n return null;\n }\n\n if (!deleteResult.ok) {\n // A 404 proves this immutable id is permanently absent, but it does not\n // carry the server deletion time. A maximal fence prevents an older\n // in-flight listing page from resurrecting it.\n return { deletedAt: \"9999-12-31T23:59:59.999Z\" };\n }\n\n return { deletedAt: deleteResult.data.deletedAt };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remote.js","sourceRoot":"","sources":["remote.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type {\n ContainerMutationResponse,\n ContainerWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { isContainerNotFoundFailure } from \"../../../data/containers/shared/mutationFailures\";\nimport type { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport { ProjectionDependencyUnavailableError } from \"../../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport {\n moveRemoteContainer as moveRemoteContainerMutation,\n readContainerMutationMetadataDocumentId,\n referencedPrincipalHeadsFromContainerMutationResponse,\n shareRemoteContainer as shareRemoteContainerMutation,\n} from \"../../containers\";\nimport { createRuntimeCurrentGroupShare } from \"../../containers/child/runtimeCurrentGroupShare\";\nimport { resolveDocumentCreateAuthor } from \"../../documents\";\nimport { createRuntimeGroupMetadataAccess } from \"../../organizations/groupMetadataRuntime\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../principals/runtimePolicyWarmer\";\nimport {\n type ContainerAlreadyCommitted,\n createRemoteContainerWithMetadataDocument,\n} from \"./createWithMetadata\";\nimport type {\n ContainerWorkflowRuntime,\n CreatedRemoteContainerState,\n RemoteContainer,\n SharedRemoteContainerState,\n} from \"./types\";\n\n/**\n * Resolve the author, api client, executor, and encapsulation secret a remote\n * container mutation needs, logging a skip for the given operation when the\n * writer context is unavailable.\n */\nfunction resolveContainerWriterContext(\n runtime: ContainerWorkflowRuntime,\n operation:\n | \"container create\"\n | \"container group share\"\n | \"container move\"\n | \"container share\",\n) {\n const author = resolveDocumentCreateAuthor(runtime);\n const secretKey = runtime.crypto.encapsulationKeyPair?.secretKey;\n if (!author || !secretKey) {\n runtime.util.log(\n `Container contents: skipped ${operation} because the writer context is unavailable.`,\n );\n return null;\n }\n\n return {\n apiClient: runtime.apiClient,\n author,\n execSql: runtime.infra.execSql,\n secretKey,\n };\n}\n\nexport async function createRemoteContainer(input: {\n systemSlot?: ContainerSystemSlot | null | undefined;\n containerId: string;\n parentContainerId: string;\n parentProjection?: ContainerWriterProjectionResponse | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<CreatedRemoteContainerState | ContainerAlreadyCommitted | null> {\n return createRemoteContainerWithMetadataDocument(input);\n}\n\nfunction sharedRemoteContainerStateFromMutation(\n previousProjection: ContainerWriterProjectionResponse,\n response: ContainerMutationResponse,\n): SharedRemoteContainerState {\n if (\n response.containerId !== previousProjection.containerId ||\n response.organizationId !== previousProjection.organizationId\n )\n throw new Error(\"Container mutation response projection mismatch\");\n return {\n accessManifestHash: response.manifestHead.manifestHash,\n accessEpoch: response.manifestHead.epoch,\n createdAt: response.createdAt,\n metadataDocumentId: readContainerMutationMetadataDocumentId({ response }),\n referencedPrincipalHeads:\n referencedPrincipalHeadsFromContainerMutationResponse({ response }),\n updatedAt: response.updatedAt,\n // The next read obtains the post-commit proof. A failed read cannot undo\n // acknowledgement of this successful mutation.\n writerProjection: null,\n };\n}\n\nexport async function shareRemoteContainer(input: {\n accessLevel: \"read\" | \"write\" | \"admin\";\n containerId: string;\n previousProjection: ContainerWriterProjectionResponse;\n recipientUserId: string;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<SharedRemoteContainerState | null> {\n const writer = resolveContainerWriterContext(\n input.runtime,\n \"container share\",\n );\n if (!writer) {\n return null;\n }\n const { apiClient, author, execSql, secretKey: targetSecretKey } = writer;\n\n const shared = await shareRemoteContainerMutation({\n reportSecurityIncident: input.runtime.util.reportSecurityIncident,\n accessLevel: input.accessLevel,\n apiClient,\n author,\n containerId: input.containerId,\n execSql,\n previousProjection: input.previousProjection,\n recipientUserId: input.recipientUserId,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n resolveTrustedUserIdentity: input.runtime.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n targetSecretKey,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n input.runtime,\n ),\n });\n if (!shared) {\n return null;\n }\n\n return sharedRemoteContainerStateFromMutation(\n input.previousProjection,\n shared.response,\n );\n}\n\nexport async function shareRemoteContainerWithGroup(input: {\n accessLevel: \"read\" | \"write\" | \"admin\";\n containerId: string;\n expectedGroupName?: string | undefined;\n knownContainerKeks?: ReadonlyMap<string, Uint8Array> | undefined;\n previousProjection: ContainerWriterProjectionResponse;\n recipientGroupId: string;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<SharedRemoteContainerState | null> {\n const writer = resolveContainerWriterContext(\n input.runtime,\n \"container group share\",\n );\n if (!writer) {\n return null;\n }\n const { apiClient, author, execSql, secretKey: targetSecretKey } = writer;\n\n const shareWithGroup = createRuntimeCurrentGroupShare(input.runtime);\n if (!shareWithGroup)\n throw new ProjectionDependencyUnavailableError(\n \"Group sharing requires private paged recovery\",\n );\n const shared = await shareWithGroup({\n reportSecurityIncident: input.runtime.util.reportSecurityIncident,\n accessLevel: input.accessLevel,\n apiClient,\n author,\n containerId: input.containerId,\n execSql,\n expectedGroupName: input.expectedGroupName ?? null,\n readEncryptedName: (bundle) =>\n createRuntimeGroupMetadataAccess(\n input.runtime,\n author.organizationId,\n input.stillCurrent,\n ).readName(bundle),\n knownContainerKeks: input.knownContainerKeks,\n previousProjection: input.previousProjection,\n recipientGroupId: input.recipientGroupId,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n resolveTrustedUserIdentity: input.runtime.resolveTrustedUserIdentity,\n signingKeyPair: input.runtime.crypto.signingKeyPair,\n stillCurrent: input.stillCurrent,\n targetSecretKey,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n input.runtime,\n ),\n });\n if (!shared) {\n return null;\n }\n\n return sharedRemoteContainerStateFromMutation(\n input.previousProjection,\n shared.response,\n );\n}\n\nexport async function moveRemoteContainer(input: {\n containerId: string;\n parentContainerId: string;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<RemoteContainer | null> {\n const writer = resolveContainerWriterContext(input.runtime, \"container move\");\n if (!writer) {\n return null;\n }\n const { apiClient, author, execSql, secretKey: targetSecretKey } = writer;\n\n const moved = await moveRemoteContainerMutation({\n reportSecurityIncident: input.runtime.util.reportSecurityIncident,\n apiClient,\n author,\n containerId: input.containerId,\n destinationParentContainerId: input.parentContainerId,\n execSql,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n stillCurrent: input.stillCurrent,\n targetSecretKey,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n input.runtime,\n ),\n });\n if (!moved) {\n return null;\n }\n\n return {\n createdAt: moved.response.createdAt,\n effectiveAccessLevel: \"admin\",\n id: moved.response.containerId,\n organizationId: moved.response.organizationId,\n parentId: moved.response.parentId,\n metadataDocumentId: readContainerMutationMetadataDocumentId({\n response: moved.response,\n }),\n metadataAccessEpoch: moved.response.manifestHead.epoch,\n metadataAccessStateHash: moved.response.manifestHead.manifestHash,\n metadataReferencedPrincipals:\n referencedPrincipalHeadsFromContainerMutationResponse({\n response: moved.response,\n }),\n updatedAt: moved.response.updatedAt,\n };\n}\n\nexport async function deleteRemoteContainer(input: {\n containerId: string;\n organizationId: string;\n runtime: ContainerWorkflowRuntime;\n}): Promise<{ deletedAt: string } | null> {\n const deleteResult = await input.runtime.apiClient.deleteContainerResult(\n input.containerId,\n {\n expectedPaymentRequiredOrganizationId: input.organizationId,\n reportErrors: false,\n },\n );\n if (!deleteResult.ok && !isContainerNotFoundFailure(deleteResult)) {\n deleteResult.report();\n return null;\n }\n\n if (!deleteResult.ok) {\n // A 404 proves this immutable id is permanently absent, but it does not\n // carry the server deletion time. A maximal fence prevents an older\n // in-flight listing page from resurrecting it.\n return { deletedAt: \"9999-12-31T23:59:59.999Z\" };\n }\n\n return { deletedAt: deleteResult.data.deletedAt };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type {\n ContainerDeleteResponse,\n ContainerWriterProjectionResponse,\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createInitializedContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport type { DocumentRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type {\n createRemoteContainer,\n moveRemoteContainer,\n shareRemoteContainer,\n shareRemoteContainerWithGroup,\n} from \"../../containers\";\nimport type { createRemoteDocument } from \"../../documents\";\nimport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerMoveIntentRecord,\n} from \"../containerPersistence\";\nimport type {\n ContainerState,\n RemoteContainer as HydratedRemoteContainer,\n RemoteContainerHydrationHost,\n} from \"../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ContainerWorkflowApi = Parameters<\n typeof createRemoteContainer\n>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainerWithGroup>[0][\"apiClient\"] &\n Parameters<typeof moveRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof createRemoteDocument>[0][\"apiClient\"] & {\n deleteContainerResult(\n containerId: string,\n options?: {\n expectedPaymentRequiredOrganizationId?: string;\n reportErrors?: boolean;\n },\n ): Promise<\n | { data: ContainerDeleteResponse; ok: true }\n | {\n ok: false;\n report: () => void;\n status: number | null;\n }\n >;\n primeContainerWriterProjection(\n containerId: string,\n projection: ContainerWriterProjectionResponse,\n ): void;\n getCurrentPrincipalPolicy(\n principalType: \"group\" | \"organization\",\n principalId: string,\n ): Promise<PrincipalPolicyBundleResponse | null>;\n };\n\nexport interface ContainerWorkflowRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n readonly apiClient: ContainerWorkflowApi;\n}\n\nexport interface CreatedRemoteContainerState {\n accessManifestHash: string;\n /** The slot this client signed into the create; never the server's echo. */\n systemSlot: ContainerSystemSlot | null;\n containerId: string;\n createdAt: string;\n metadataDocumentId: string;\n organizationId: string;\n /** A created container is a child; the acknowledgement echoed this parent. */\n parentId: string;\n persistedMetadataState: Pick<\n DocumentRecord,\n | \"documentId\"\n | \"contentKeyBundle\"\n | \"documentKekTargets\"\n | \"documentManifestBundle\"\n >;\n updatedAt: string;\n}\n\nexport interface SharedRemoteContainerState {\n accessEpoch: number;\n accessManifestHash: string;\n createdAt: string;\n metadataDocumentId: string;\n referencedPrincipalHeads: ReferencedPrincipalStateResponse[];\n updatedAt: string;\n writerProjection: ContainerWriterProjectionResponse | null;\n}\n\nexport interface SharedContainerState {\n container: ContainerState[\"container\"];\n record: ContainerState[\"record\"];\n}\n\nexport type SharedContainerStateResult =\n | { status: \"missing\" }\n | { status: \"confirmed\" }\n | (SharedContainerState & {\n status: \"identity-superseded\" | \"persisted\";\n });\n\nexport type RemoteContainer = HydratedRemoteContainer;\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createInitializedContainerMetadataDocument>\n>[\"doc\"];\n\nexport interface CreatedChildContainerState {\n containerState: ContainerState;\n initialUpdate: Uint8Array;\n shouldRequestSync: boolean;\n}\n\nexport interface ContainerIntentSyncState {\n containersById: ReadonlyMap<string, ContainerState>;\n persistence: ContainerContentsPersistence;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n /**\n * Verifies that a listed container is this device's pending create before it\n * is adopted and returns where it sits; `assertContainerCreateAdoptable`\n * unless a test replaces it.\n */\n verifyCreateAdoption?: (input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n }) => Promise<AdoptedContainerPlacement>;\n}\n\n/** Where a verified listed create committed, and where its container is now. */\nexport interface AdoptedContainerPlacement {\n /** The parent its signed epoch-1 `container.create` named. */\n readonly createdParentId: string;\n /** The verified head's parent; another writer may have moved it since. */\n readonly currentParentId: string;\n}\n\nexport type ContainerCreateIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerCreateIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\"\n>;\n\nexport interface ContainerCreateIntentSyncInput {\n host: ContainerCreateIntentSyncHost;\n /** A parent whose own create has not settled takes no child create. */\n isCreatePending: (containerId: string) => boolean;\n isCurrent: () => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerCreateIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerCreateIntentSyncState;\n}\n\nexport type ContainerMoveIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerMoveIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\" | \"updateSnapshot\"\n>;\n\nexport interface ContainerMoveIntentSyncInput {\n host: ContainerMoveIntentSyncHost;\n isCurrent: () => boolean;\n /** A folder whose create has not settled takes no structural writes. */\n isCreatePending: (containerId: string) => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerMoveIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerMoveIntentSyncState;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type {\n ContainerDeleteResponse,\n ContainerWriterProjectionResponse,\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createInitializedContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport type { DocumentRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type {\n createRemoteContainer,\n moveRemoteContainer,\n shareRemoteContainer,\n shareRemoteContainerWithGroup,\n} from \"../../containers\";\nimport type { createRemoteDocument } from \"../../documents\";\nimport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerMoveIntentRecord,\n} from \"../containerPersistence\";\nimport type {\n ContainerState,\n RemoteContainer as HydratedRemoteContainer,\n RemoteContainerHydrationHost,\n} from \"../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ContainerWorkflowApi = Parameters<\n typeof createRemoteContainer\n>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainerWithGroup>[0][\"apiClient\"] &\n Parameters<typeof moveRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof createRemoteDocument>[0][\"apiClient\"] &\n Partial<\n Pick<\n ApiClient,\n \"getPrincipalPolicyPages\" | \"getProjectionPolicyHistoryPages\"\n >\n > & {\n deleteContainerResult(\n containerId: string,\n options?: {\n expectedPaymentRequiredOrganizationId?: string;\n reportErrors?: boolean;\n },\n ): Promise<\n | { data: ContainerDeleteResponse; ok: true }\n | {\n ok: false;\n report: () => void;\n status: number | null;\n }\n >;\n primeContainerWriterProjection(\n containerId: string,\n projection: ContainerWriterProjectionResponse,\n ): void;\n getCurrentPrincipalPolicy(\n principalType: \"group\" | \"organization\",\n principalId: string,\n ): Promise<PrincipalPolicyBundleResponse | null>;\n };\n\nexport interface ContainerWorkflowRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n readonly apiClient: ContainerWorkflowApi;\n}\n\nexport interface CreatedRemoteContainerState {\n accessManifestHash: string;\n /** The slot this client signed into the create; never the server's echo. */\n systemSlot: ContainerSystemSlot | null;\n containerId: string;\n createdAt: string;\n metadataDocumentId: string;\n organizationId: string;\n /** A created container is a child; the acknowledgement echoed this parent. */\n parentId: string;\n persistedMetadataState: Pick<\n DocumentRecord,\n | \"documentId\"\n | \"contentKeyBundle\"\n | \"documentKekTargets\"\n | \"documentManifestBundle\"\n >;\n updatedAt: string;\n}\n\nexport interface SharedRemoteContainerState {\n accessEpoch: number;\n accessManifestHash: string;\n createdAt: string;\n metadataDocumentId: string;\n referencedPrincipalHeads: ReferencedPrincipalStateResponse[];\n updatedAt: string;\n writerProjection: ContainerWriterProjectionResponse | null;\n}\n\nexport interface SharedContainerState {\n container: ContainerState[\"container\"];\n record: ContainerState[\"record\"];\n}\n\nexport type SharedContainerStateResult =\n | { status: \"missing\" }\n | { status: \"confirmed\" }\n | (SharedContainerState & {\n status: \"identity-superseded\" | \"persisted\";\n });\n\nexport type RemoteContainer = HydratedRemoteContainer;\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createInitializedContainerMetadataDocument>\n>[\"doc\"];\n\nexport interface CreatedChildContainerState {\n containerState: ContainerState;\n initialUpdate: Uint8Array;\n shouldRequestSync: boolean;\n}\n\nexport interface ContainerIntentSyncState {\n containersById: ReadonlyMap<string, ContainerState>;\n persistence: ContainerContentsPersistence;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n /**\n * Verifies that a listed container is this device's pending create before it\n * is adopted and returns where it sits; `assertContainerCreateAdoptable`\n * unless a test replaces it.\n */\n verifyCreateAdoption?: (input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n }) => Promise<AdoptedContainerPlacement>;\n}\n\n/** Where a verified listed create committed, and where its container is now. */\nexport interface AdoptedContainerPlacement {\n /** The parent its signed epoch-1 `container.create` named. */\n readonly createdParentId: string;\n /** The verified head's parent; another writer may have moved it since. */\n readonly currentParentId: string;\n}\n\nexport type ContainerCreateIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerCreateIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\"\n>;\n\nexport interface ContainerCreateIntentSyncInput {\n host: ContainerCreateIntentSyncHost;\n /** A parent whose own create has not settled takes no child create. */\n isCreatePending: (containerId: string) => boolean;\n isCurrent: () => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerCreateIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerCreateIntentSyncState;\n}\n\nexport type ContainerMoveIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerMoveIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\" | \"updateSnapshot\"\n>;\n\nexport interface ContainerMoveIntentSyncInput {\n host: ContainerMoveIntentSyncHost;\n isCurrent: () => boolean;\n /** A folder whose create has not settled takes no structural writes. */\n isCreatePending: (containerId: string) => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerMoveIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerMoveIntentSyncState;\n}\n"]}
|
|
@@ -6,7 +6,7 @@ type ContainerDocumentLinkApi = Parameters<typeof relinkRemoteDocument>[0]["apiC
|
|
|
6
6
|
type ContainerDocumentLinkOperation = Parameters<typeof relinkRemoteDocument>[0]["operation"];
|
|
7
7
|
type RemoteDocumentPersistedState = RelinkRemoteDocumentResult["persistedState"];
|
|
8
8
|
interface ContainerDocumentLinkRuntime extends Pick<ContainerContentsWorkflowRuntime, "auth" | "crypto" | "infra" | "resolveTrustedUserIdentity" | "state" | "util"> {
|
|
9
|
-
apiClient: ContainerDocumentLinkApi & Pick<ContainerContentsWorkflowRuntime["apiClient"], "
|
|
9
|
+
apiClient: ContainerDocumentLinkApi & Pick<ContainerContentsWorkflowRuntime["apiClient"], "getPrincipalPolicyPages" | "getProjectionPolicyHistoryPages">;
|
|
10
10
|
}
|
|
11
11
|
type MoveRemoteContainerDocumentStatus = "complete" | "partial";
|
|
12
12
|
interface MoveRemoteContainerDocumentResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documentLinks.js","sourceRoot":"","sources":["documentLinks.ts"],"names":[],"mappings":"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","sourcesContent":["import type { VerifiedDocumentLinkSetManifest } from \"@tearleads/crypto\";\nimport type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n assertDocumentWriterProjectionConsistent,\n readLinkedContainerIdsFromDocumentManifest,\n} from \"../../data/documents/shared/projection\";\nimport { persistedDocumentCreateStateFromWriterProjection } from \"../../data/documents/shared/responses\";\nimport { errorMessage } from \"../../data/errorMessage\";\nimport type { ProjectionUserKeyResolver } from \"../../data/keyingProjectionVerification\";\nimport { reportAndRethrowKeyingVerificationError } from \"../../data/keyingProjectionVerification/error\";\nimport { sqlDocumentContainerProjectionPersistence } from \"../../data/persistence/containers/documentContainerProjectionPersistence\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport { isDatabaseUnavailableError } from \"../../data/sync/databaseUnavailable\";\nimport {\n type DocumentLinkSetFailureHandler,\n type RelinkRemoteDocumentResult,\n relinkRemoteDocument,\n resolveDocumentCreateAuthor,\n} from \"../documents\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../principals/runtimePolicyWarmer\";\nimport { resolveContainerDocumentMoveUnlinkIds } from \"./documentMoveUnlinkIds\";\nimport type { ContainerContentsWorkflowRuntime } from \"./runtime\";\n\ntype ContainerDocumentLinkApi = Parameters<\n typeof relinkRemoteDocument\n>[0][\"apiClient\"];\ntype ContainerDocumentLinkOperation = Parameters<\n typeof relinkRemoteDocument\n>[0][\"operation\"];\ntype RemoteDocumentPersistedState =\n RelinkRemoteDocumentResult[\"persistedState\"];\n\n// A cold-cache denial must keep its HTTP status: collapsing a 403 to null\n// would leave an access-denied move routinely retriable instead of parking\n// it for the access-restored signal (row 7).\nasync function fetchMoveWriterProjection(input: {\n documentId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n runtime: ContainerDocumentLinkRuntime;\n}): Promise<Awaited<\n ReturnType<ContainerDocumentLinkApi[\"getDocumentWriterProjection\"]>\n> | null> {\n const { apiClient } = input.runtime;\n const result = await apiClient.getDocumentWriterProjectionResult(\n input.documentId,\n { reportErrors: false },\n );\n if (result.ok) {\n return result.data;\n }\n result.report();\n input.onFailure?.({ message: result.message, status: result.status });\n return null;\n}\n\ninterface ContainerDocumentLinkRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: ContainerDocumentLinkApi &\n Pick<\n ContainerContentsWorkflowRuntime[\"apiClient\"],\n \"getCurrentPrincipalPolicy\"\n >;\n}\n\ntype MoveRemoteContainerDocumentStatus = \"complete\" | \"partial\";\n\ninterface MoveRemoteContainerDocumentResult {\n accessEpoch: number | null;\n accessStateHash: string | null;\n linkedContainerIds: readonly string[];\n nextContainerId: string;\n remoteState: RemoteDocumentPersistedState | null;\n status: MoveRemoteContainerDocumentStatus;\n}\n\nexport type { RemoteDocumentPersistedState };\n\nexport async function initializeDocumentLinksSchema(\n execSql: ExecSql,\n): Promise<void> {\n await sqlDocumentContainerProjectionPersistence.ensureSchema(execSql);\n}\n\nexport async function listDocumentLinkedContainerIds(\n execSql: ExecSql,\n documentId: string,\n): Promise<ReadonlyArray<string>> {\n return sqlDocumentContainerProjectionPersistence.listLinkedContainerIds(\n execSql,\n documentId,\n );\n}\n\nexport async function replaceDocumentLinks(\n execSql: ExecSql,\n documentId: string,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n await sqlDocumentContainerProjectionPersistence.replaceDocumentLinks(\n execSql,\n documentId,\n containerIds,\n );\n}\n\nfunction resolveActiveDocumentContainerId(\n linkedContainerIds: ReadonlyArray<string>,\n preferredContainerId: string,\n): string | null {\n if (linkedContainerIds.includes(preferredContainerId)) {\n return preferredContainerId;\n }\n\n return linkedContainerIds[0] ?? null;\n}\n\nfunction containerDocumentMoveResult(input: {\n document: RelinkRemoteDocumentResult;\n nextContainerId: string;\n status: MoveRemoteContainerDocumentStatus;\n}): MoveRemoteContainerDocumentResult {\n return {\n accessEpoch: input.document.plan.state.epoch,\n accessStateHash: input.document.response.accessManifest.manifestHash,\n linkedContainerIds: input.document.linkedContainerIds,\n nextContainerId: input.nextContainerId,\n remoteState: input.document.persistedState,\n status: input.status,\n };\n}\n\nasync function containerDocumentAlreadyMovedResult(input: {\n writerProjection: DocumentWriterProjectionResponse;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerDocumentLinkRuntime;\n isCurrent?: (() => boolean) | undefined;\n linkedContainerIds: readonly string[];\n nextContainerId: string;\n status: MoveRemoteContainerDocumentStatus;\n}): Promise<MoveRemoteContainerDocumentResult> {\n const verified: { head?: VerifiedDocumentLinkSetManifest | undefined } = {};\n await assertDocumentWriterProjectionConsistent(input.writerProjection, {\n execSql: input.runtime.infra.execSql,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n stillCurrent: input.isCurrent,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n input.runtime,\n ),\n onVerifiedAuthorization: (authorization) => {\n verified.head = authorization.documentManifestByHash.get(\n input.writerProjection.documentManifest.manifestHash,\n );\n },\n });\n if (!verified.head)\n throw new Error(\"Move replay lacks a verified document head\");\n return {\n accessEpoch: verified.head.state.epoch,\n accessStateHash: verified.head.manifestHash,\n linkedContainerIds: input.linkedContainerIds,\n nextContainerId: input.nextContainerId,\n remoteState: persistedDocumentCreateStateFromWriterProjection(\n input.writerProjection,\n ),\n status: input.status,\n };\n}\n\n/**\n * Hand a link/unlink/move/purge failure to host diagnostics as a real `Error`.\n * The caller's string-only log line stays the fallback for a host without\n * `logError`, so this never logs a second copy of the same failure.\n */\nfunction reportDocumentLinkMutationFailure(\n util: ContainerDocumentLinkRuntime[\"util\"],\n error: unknown,\n): void {\n // `ApiClient` returns `RequestFailure` objects instead of throwing, so\n // offline and 403/404/409 already left through the `!result` branch: a status\n // filter here would be dead code. Only a database lost mid-flight stays quiet.\n if (!util.logError || isDatabaseUnavailableError(error)) return;\n try {\n // A host logger that throws or rejects must not replace the null result or\n // skip the failure handler below.\n void Promise.resolve(\n util.logError(\"Container contents: document link mutation failed\", error),\n ).catch(() => undefined);\n } catch {\n // Hosts may throw synchronously or return a rejected promise.\n }\n}\n\nexport async function relinkRemoteContainerDocument(input: {\n documentId: string;\n isCurrent?: (() => boolean) | undefined;\n noteId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n operation: ContainerDocumentLinkOperation;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n rotationSnapshot?: Uint8Array | undefined;\n runtime: ContainerDocumentLinkRuntime;\n targetContainerId: string;\n}): Promise<RelinkRemoteDocumentResult | null> {\n const {\n documentId,\n noteId,\n operation,\n resolveProjectionUserKey,\n runtime,\n targetContainerId,\n } = input;\n const author = resolveDocumentCreateAuthor(runtime);\n const targetSecretKey = runtime.crypto.encapsulationKeyPair?.secretKey;\n const execSql = runtime.infra.execSql;\n if (input.isCurrent?.() === false) return null;\n if (!author || !targetSecretKey) {\n runtime.util.log(\n \"Container contents: document mutation skipped because the local key context is unavailable\",\n );\n input.onFailure?.({\n message: \"the local key context is unavailable\",\n status: null,\n });\n return null;\n }\n\n try {\n const result = await relinkRemoteDocument({\n apiClient: runtime.apiClient,\n author,\n documentId,\n execSql,\n onFailure: input.onFailure,\n operation,\n resolveProjectionUserKey,\n rotationSnapshot: input.rotationSnapshot,\n stillCurrent: input.isCurrent,\n targetContainerId,\n targetSecretKey,\n warmReferencedPrincipalPolicies:\n createRuntimePrincipalPolicyWarmer(runtime),\n });\n if (!result) {\n runtime.util.log(\n `Container contents: failed to ${operation} note ${noteId} ${operation === \"link\" ? \"to\" : \"from\"} container ${targetContainerId}`,\n );\n return null;\n }\n if (input.isCurrent?.() === false) return null;\n\n await sqlDocumentContainerProjectionPersistence.replaceDocumentLinks(\n execSql,\n documentId,\n result.linkedContainerIds,\n { stillCurrent: input.isCurrent },\n );\n\n return result;\n } catch (error) {\n await reportAndRethrowKeyingVerificationError(\n error,\n runtime.util.reportSecurityIncident,\n {\n objectId: documentId,\n objectKind: \"document\",\n operation: `document.${operation}`,\n },\n );\n const message = errorMessage(error);\n runtime.util.log(\n `Container contents: failed to ${operation} note ${noteId} ${operation === \"link\" ? \"to\" : \"from\"} container ${targetContainerId}: ${message}`,\n );\n // After the keying rethrow above, so a verification failure keeps its\n // security incident instead of being reported twice, and before the failure\n // handler, so a throwing caller callback cannot swallow the report.\n reportDocumentLinkMutationFailure(runtime.util, error);\n input.onFailure?.({ message, status: null });\n return null;\n }\n}\n\nasync function linkRemoteContainerDocument(input: {\n documentId: string;\n isCurrent?: (() => boolean) | undefined;\n noteId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerDocumentLinkRuntime;\n targetContainerId: string;\n}): Promise<RelinkRemoteDocumentResult | null> {\n return relinkRemoteContainerDocument({\n ...input,\n operation: \"link\",\n });\n}\n\nexport async function unlinkRemoteContainerDocument(input: {\n documentId: string;\n isCurrent?: (() => boolean) | undefined;\n noteId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n rotationSnapshot: Uint8Array;\n runtime: ContainerDocumentLinkRuntime;\n targetContainerId: string;\n}): Promise<RelinkRemoteDocumentResult | null> {\n return relinkRemoteContainerDocument({\n ...input,\n operation: \"unlink\",\n });\n}\n\nexport async function moveRemoteContainerDocument(input: {\n linkOnly?: boolean | undefined;\n additionalLinkContainerIds?: readonly string[] | undefined;\n removedLinkContainerIds?: readonly string[] | undefined;\n currentContainerId: string;\n documentId: string;\n isCurrent?: (() => boolean) | undefined;\n noteId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n replaceLinkedContainers?: boolean | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n rotationSnapshot: Uint8Array;\n runtime: ContainerDocumentLinkRuntime;\n targetContainerId: string;\n}): Promise<MoveRemoteContainerDocumentResult | null> {\n const {\n documentId,\n noteId,\n resolveProjectionUserKey,\n runtime,\n targetContainerId,\n } = input;\n if (input.isCurrent?.() === false) return null;\n const writerProjection = await fetchMoveWriterProjection({\n documentId,\n onFailure: input.onFailure,\n runtime,\n });\n if (!writerProjection || input.isCurrent?.() === false) {\n return null;\n }\n if (writerProjection.documentId !== documentId)\n throw new Error(\"Move replay document id mismatch\");\n const initialLinkedContainerIds =\n readLinkedContainerIdsFromDocumentManifest(writerProjection);\n\n let latestLinkedContainerIds: readonly string[] = initialLinkedContainerIds;\n let latestDocument: RelinkRemoteDocumentResult | null = null;\n for (const linkTarget of new Set([\n ...(input.linkOnly ? [] : [targetContainerId]),\n ...(input.additionalLinkContainerIds ?? []),\n ])) {\n if (latestLinkedContainerIds.includes(linkTarget)) continue;\n const linkedDocument = await linkRemoteContainerDocument({\n documentId,\n isCurrent: input.isCurrent,\n noteId,\n onFailure: input.onFailure,\n resolveProjectionUserKey,\n runtime,\n targetContainerId: linkTarget,\n });\n if (!linkedDocument || input.isCurrent?.() === false) {\n return null;\n }\n\n latestDocument = linkedDocument;\n latestLinkedContainerIds = linkedDocument.linkedContainerIds;\n }\n\n const unlinkContainerIds = resolveContainerDocumentMoveUnlinkIds({\n ...input,\n linkedContainerIds: latestLinkedContainerIds,\n });\n\n const unlinked = await unlinkMovedDocumentSources({\n ...input,\n latestDocument,\n latestLinkedContainerIds,\n unlinkContainerIds,\n });\n if (!unlinked) return null;\n latestDocument = unlinked.latestDocument;\n latestLinkedContainerIds = unlinked.latestLinkedContainerIds;\n\n const nextContainerId = resolveActiveDocumentContainerId(\n latestLinkedContainerIds,\n targetContainerId,\n );\n if (!nextContainerId) {\n return null;\n }\n\n const status =\n unlinked.failedUnlinkContainerIds.length > 0 ? \"partial\" : \"complete\";\n return latestDocument\n ? containerDocumentMoveResult({\n document: latestDocument,\n nextContainerId,\n status,\n })\n : containerDocumentAlreadyMovedResult({\n writerProjection,\n resolveProjectionUserKey,\n runtime,\n isCurrent: input.isCurrent,\n linkedContainerIds: latestLinkedContainerIds,\n nextContainerId,\n status,\n });\n}\n\n// The unlink half of a move: every source is attempted even when an earlier\n// one fails, so one dead source cannot strand the others; a failed unlink\n// leaves the move \"partial\" for the queue to retry.\nasync function unlinkMovedDocumentSources(input: {\n documentId: string;\n isCurrent?: (() => boolean) | undefined;\n latestDocument: RelinkRemoteDocumentResult | null;\n latestLinkedContainerIds: readonly string[];\n noteId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n rotationSnapshot: Uint8Array;\n runtime: ContainerDocumentLinkRuntime;\n targetContainerId: string;\n unlinkContainerIds: readonly string[];\n}): Promise<{\n failedUnlinkContainerIds: readonly string[];\n latestDocument: RelinkRemoteDocumentResult | null;\n latestLinkedContainerIds: readonly string[];\n} | null> {\n const { noteId, runtime, targetContainerId } = input;\n let { latestDocument, latestLinkedContainerIds } = input;\n const failedUnlinkContainerIds: string[] = [];\n for (const unlinkContainerId of input.unlinkContainerIds) {\n if (input.isCurrent?.() === false) return null;\n const unlinkedDocument = await unlinkRemoteContainerDocument({\n documentId: input.documentId,\n isCurrent: input.isCurrent,\n noteId,\n onFailure: input.onFailure,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n rotationSnapshot: input.rotationSnapshot,\n runtime,\n targetContainerId: unlinkContainerId,\n });\n if (input.isCurrent?.() === false) return null;\n if (!unlinkedDocument) {\n failedUnlinkContainerIds.push(unlinkContainerId);\n runtime.util.log(\n `Container contents: note ${noteId} was linked to ${targetContainerId} but failed to unlink from ${unlinkContainerId}`,\n );\n continue;\n }\n\n latestDocument = unlinkedDocument;\n latestLinkedContainerIds = unlinkedDocument.linkedContainerIds;\n }\n return { failedUnlinkContainerIds, latestDocument, latestLinkedContainerIds };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"documentLinks.js","sourceRoot":"","sources":["documentLinks.ts"],"names":[],"mappings":"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","sourcesContent":["import type { VerifiedDocumentLinkSetManifest } from \"@tearleads/crypto\";\nimport type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n assertDocumentWriterProjectionConsistent,\n readLinkedContainerIdsFromDocumentManifest,\n} from \"../../data/documents/shared/projection\";\nimport { persistedDocumentCreateStateFromWriterProjection } from \"../../data/documents/shared/responses\";\nimport { errorMessage } from \"../../data/errorMessage\";\nimport type { ProjectionUserKeyResolver } from \"../../data/keyingProjectionVerification\";\nimport { reportAndRethrowKeyingVerificationError } from \"../../data/keyingProjectionVerification/error\";\nimport { sqlDocumentContainerProjectionPersistence } from \"../../data/persistence/containers/documentContainerProjectionPersistence\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport { isDatabaseUnavailableError } from \"../../data/sync/databaseUnavailable\";\nimport {\n type DocumentLinkSetFailureHandler,\n type RelinkRemoteDocumentResult,\n relinkRemoteDocument,\n resolveDocumentCreateAuthor,\n} from \"../documents\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../principals/runtimePolicyWarmer\";\nimport { resolveContainerDocumentMoveUnlinkIds } from \"./documentMoveUnlinkIds\";\nimport type { ContainerContentsWorkflowRuntime } from \"./runtime\";\n\ntype ContainerDocumentLinkApi = Parameters<\n typeof relinkRemoteDocument\n>[0][\"apiClient\"];\ntype ContainerDocumentLinkOperation = Parameters<\n typeof relinkRemoteDocument\n>[0][\"operation\"];\ntype RemoteDocumentPersistedState =\n RelinkRemoteDocumentResult[\"persistedState\"];\n\n// A cold-cache denial must keep its HTTP status: collapsing a 403 to null\n// would leave an access-denied move routinely retriable instead of parking\n// it for the access-restored signal (row 7).\nasync function fetchMoveWriterProjection(input: {\n documentId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n runtime: ContainerDocumentLinkRuntime;\n}): Promise<Awaited<\n ReturnType<ContainerDocumentLinkApi[\"getDocumentWriterProjection\"]>\n> | null> {\n const { apiClient } = input.runtime;\n const result = await apiClient.getDocumentWriterProjectionResult(\n input.documentId,\n { reportErrors: false },\n );\n if (result.ok) {\n return result.data;\n }\n result.report();\n input.onFailure?.({ message: result.message, status: result.status });\n return null;\n}\n\ninterface ContainerDocumentLinkRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: ContainerDocumentLinkApi &\n Pick<\n ContainerContentsWorkflowRuntime[\"apiClient\"],\n \"getPrincipalPolicyPages\" | \"getProjectionPolicyHistoryPages\"\n >;\n}\n\ntype MoveRemoteContainerDocumentStatus = \"complete\" | \"partial\";\n\ninterface MoveRemoteContainerDocumentResult {\n accessEpoch: number | null;\n accessStateHash: string | null;\n linkedContainerIds: readonly string[];\n nextContainerId: string;\n remoteState: RemoteDocumentPersistedState | null;\n status: MoveRemoteContainerDocumentStatus;\n}\n\nexport type { RemoteDocumentPersistedState };\n\nexport async function initializeDocumentLinksSchema(\n execSql: ExecSql,\n): Promise<void> {\n await sqlDocumentContainerProjectionPersistence.ensureSchema(execSql);\n}\n\nexport async function listDocumentLinkedContainerIds(\n execSql: ExecSql,\n documentId: string,\n): Promise<ReadonlyArray<string>> {\n return sqlDocumentContainerProjectionPersistence.listLinkedContainerIds(\n execSql,\n documentId,\n );\n}\n\nexport async function replaceDocumentLinks(\n execSql: ExecSql,\n documentId: string,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n await sqlDocumentContainerProjectionPersistence.replaceDocumentLinks(\n execSql,\n documentId,\n containerIds,\n );\n}\n\nfunction resolveActiveDocumentContainerId(\n linkedContainerIds: ReadonlyArray<string>,\n preferredContainerId: string,\n): string | null {\n if (linkedContainerIds.includes(preferredContainerId)) {\n return preferredContainerId;\n }\n\n return linkedContainerIds[0] ?? null;\n}\n\nfunction containerDocumentMoveResult(input: {\n document: RelinkRemoteDocumentResult;\n nextContainerId: string;\n status: MoveRemoteContainerDocumentStatus;\n}): MoveRemoteContainerDocumentResult {\n return {\n accessEpoch: input.document.plan.state.epoch,\n accessStateHash: input.document.response.accessManifest.manifestHash,\n linkedContainerIds: input.document.linkedContainerIds,\n nextContainerId: input.nextContainerId,\n remoteState: input.document.persistedState,\n status: input.status,\n };\n}\n\nasync function containerDocumentAlreadyMovedResult(input: {\n writerProjection: DocumentWriterProjectionResponse;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerDocumentLinkRuntime;\n isCurrent?: (() => boolean) | undefined;\n linkedContainerIds: readonly string[];\n nextContainerId: string;\n status: MoveRemoteContainerDocumentStatus;\n}): Promise<MoveRemoteContainerDocumentResult> {\n const verified: { head?: VerifiedDocumentLinkSetManifest | undefined } = {};\n await assertDocumentWriterProjectionConsistent(input.writerProjection, {\n execSql: input.runtime.infra.execSql,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n stillCurrent: input.isCurrent,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n input.runtime,\n ),\n onVerifiedAuthorization: (authorization) => {\n verified.head = authorization.documentManifestByHash.get(\n input.writerProjection.documentManifest.manifestHash,\n );\n },\n });\n if (!verified.head)\n throw new Error(\"Move replay lacks a verified document head\");\n return {\n accessEpoch: verified.head.state.epoch,\n accessStateHash: verified.head.manifestHash,\n linkedContainerIds: input.linkedContainerIds,\n nextContainerId: input.nextContainerId,\n remoteState: persistedDocumentCreateStateFromWriterProjection(\n input.writerProjection,\n ),\n status: input.status,\n };\n}\n\n/**\n * Hand a link/unlink/move/purge failure to host diagnostics as a real `Error`.\n * The caller's string-only log line stays the fallback for a host without\n * `logError`, so this never logs a second copy of the same failure.\n */\nfunction reportDocumentLinkMutationFailure(\n util: ContainerDocumentLinkRuntime[\"util\"],\n error: unknown,\n): void {\n // `ApiClient` returns `RequestFailure` objects instead of throwing, so\n // offline and 403/404/409 already left through the `!result` branch: a status\n // filter here would be dead code. Only a database lost mid-flight stays quiet.\n if (!util.logError || isDatabaseUnavailableError(error)) return;\n try {\n // A host logger that throws or rejects must not replace the null result or\n // skip the failure handler below.\n void Promise.resolve(\n util.logError(\"Container contents: document link mutation failed\", error),\n ).catch(() => undefined);\n } catch {\n // Hosts may throw synchronously or return a rejected promise.\n }\n}\n\nexport async function relinkRemoteContainerDocument(input: {\n documentId: string;\n isCurrent?: (() => boolean) | undefined;\n noteId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n operation: ContainerDocumentLinkOperation;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n rotationSnapshot?: Uint8Array | undefined;\n runtime: ContainerDocumentLinkRuntime;\n targetContainerId: string;\n}): Promise<RelinkRemoteDocumentResult | null> {\n const {\n documentId,\n noteId,\n operation,\n resolveProjectionUserKey,\n runtime,\n targetContainerId,\n } = input;\n const author = resolveDocumentCreateAuthor(runtime);\n const targetSecretKey = runtime.crypto.encapsulationKeyPair?.secretKey;\n const execSql = runtime.infra.execSql;\n if (input.isCurrent?.() === false) return null;\n if (!author || !targetSecretKey) {\n runtime.util.log(\n \"Container contents: document mutation skipped because the local key context is unavailable\",\n );\n input.onFailure?.({\n message: \"the local key context is unavailable\",\n status: null,\n });\n return null;\n }\n\n try {\n const result = await relinkRemoteDocument({\n apiClient: runtime.apiClient,\n author,\n documentId,\n execSql,\n onFailure: input.onFailure,\n operation,\n resolveProjectionUserKey,\n rotationSnapshot: input.rotationSnapshot,\n stillCurrent: input.isCurrent,\n targetContainerId,\n targetSecretKey,\n warmReferencedPrincipalPolicies:\n createRuntimePrincipalPolicyWarmer(runtime),\n });\n if (!result) {\n runtime.util.log(\n `Container contents: failed to ${operation} note ${noteId} ${operation === \"link\" ? \"to\" : \"from\"} container ${targetContainerId}`,\n );\n return null;\n }\n if (input.isCurrent?.() === false) return null;\n\n await sqlDocumentContainerProjectionPersistence.replaceDocumentLinks(\n execSql,\n documentId,\n result.linkedContainerIds,\n { stillCurrent: input.isCurrent },\n );\n\n return result;\n } catch (error) {\n await reportAndRethrowKeyingVerificationError(\n error,\n runtime.util.reportSecurityIncident,\n {\n objectId: documentId,\n objectKind: \"document\",\n operation: `document.${operation}`,\n },\n );\n const message = errorMessage(error);\n runtime.util.log(\n `Container contents: failed to ${operation} note ${noteId} ${operation === \"link\" ? \"to\" : \"from\"} container ${targetContainerId}: ${message}`,\n );\n // After the keying rethrow above, so a verification failure keeps its\n // security incident instead of being reported twice, and before the failure\n // handler, so a throwing caller callback cannot swallow the report.\n reportDocumentLinkMutationFailure(runtime.util, error);\n input.onFailure?.({ message, status: null });\n return null;\n }\n}\n\nasync function linkRemoteContainerDocument(input: {\n documentId: string;\n isCurrent?: (() => boolean) | undefined;\n noteId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerDocumentLinkRuntime;\n targetContainerId: string;\n}): Promise<RelinkRemoteDocumentResult | null> {\n return relinkRemoteContainerDocument({\n ...input,\n operation: \"link\",\n });\n}\n\nexport async function unlinkRemoteContainerDocument(input: {\n documentId: string;\n isCurrent?: (() => boolean) | undefined;\n noteId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n rotationSnapshot: Uint8Array;\n runtime: ContainerDocumentLinkRuntime;\n targetContainerId: string;\n}): Promise<RelinkRemoteDocumentResult | null> {\n return relinkRemoteContainerDocument({\n ...input,\n operation: \"unlink\",\n });\n}\n\nexport async function moveRemoteContainerDocument(input: {\n linkOnly?: boolean | undefined;\n additionalLinkContainerIds?: readonly string[] | undefined;\n removedLinkContainerIds?: readonly string[] | undefined;\n currentContainerId: string;\n documentId: string;\n isCurrent?: (() => boolean) | undefined;\n noteId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n replaceLinkedContainers?: boolean | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n rotationSnapshot: Uint8Array;\n runtime: ContainerDocumentLinkRuntime;\n targetContainerId: string;\n}): Promise<MoveRemoteContainerDocumentResult | null> {\n const {\n documentId,\n noteId,\n resolveProjectionUserKey,\n runtime,\n targetContainerId,\n } = input;\n if (input.isCurrent?.() === false) return null;\n const writerProjection = await fetchMoveWriterProjection({\n documentId,\n onFailure: input.onFailure,\n runtime,\n });\n if (!writerProjection || input.isCurrent?.() === false) {\n return null;\n }\n if (writerProjection.documentId !== documentId)\n throw new Error(\"Move replay document id mismatch\");\n const initialLinkedContainerIds =\n readLinkedContainerIdsFromDocumentManifest(writerProjection);\n\n let latestLinkedContainerIds: readonly string[] = initialLinkedContainerIds;\n let latestDocument: RelinkRemoteDocumentResult | null = null;\n for (const linkTarget of new Set([\n ...(input.linkOnly ? [] : [targetContainerId]),\n ...(input.additionalLinkContainerIds ?? []),\n ])) {\n if (latestLinkedContainerIds.includes(linkTarget)) continue;\n const linkedDocument = await linkRemoteContainerDocument({\n documentId,\n isCurrent: input.isCurrent,\n noteId,\n onFailure: input.onFailure,\n resolveProjectionUserKey,\n runtime,\n targetContainerId: linkTarget,\n });\n if (!linkedDocument || input.isCurrent?.() === false) {\n return null;\n }\n\n latestDocument = linkedDocument;\n latestLinkedContainerIds = linkedDocument.linkedContainerIds;\n }\n\n const unlinkContainerIds = resolveContainerDocumentMoveUnlinkIds({\n ...input,\n linkedContainerIds: latestLinkedContainerIds,\n });\n\n const unlinked = await unlinkMovedDocumentSources({\n ...input,\n latestDocument,\n latestLinkedContainerIds,\n unlinkContainerIds,\n });\n if (!unlinked) return null;\n latestDocument = unlinked.latestDocument;\n latestLinkedContainerIds = unlinked.latestLinkedContainerIds;\n\n const nextContainerId = resolveActiveDocumentContainerId(\n latestLinkedContainerIds,\n targetContainerId,\n );\n if (!nextContainerId) {\n return null;\n }\n\n const status =\n unlinked.failedUnlinkContainerIds.length > 0 ? \"partial\" : \"complete\";\n return latestDocument\n ? containerDocumentMoveResult({\n document: latestDocument,\n nextContainerId,\n status,\n })\n : containerDocumentAlreadyMovedResult({\n writerProjection,\n resolveProjectionUserKey,\n runtime,\n isCurrent: input.isCurrent,\n linkedContainerIds: latestLinkedContainerIds,\n nextContainerId,\n status,\n });\n}\n\n// The unlink half of a move: every source is attempted even when an earlier\n// one fails, so one dead source cannot strand the others; a failed unlink\n// leaves the move \"partial\" for the queue to retry.\nasync function unlinkMovedDocumentSources(input: {\n documentId: string;\n isCurrent?: (() => boolean) | undefined;\n latestDocument: RelinkRemoteDocumentResult | null;\n latestLinkedContainerIds: readonly string[];\n noteId: string;\n onFailure?: DocumentLinkSetFailureHandler | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n rotationSnapshot: Uint8Array;\n runtime: ContainerDocumentLinkRuntime;\n targetContainerId: string;\n unlinkContainerIds: readonly string[];\n}): Promise<{\n failedUnlinkContainerIds: readonly string[];\n latestDocument: RelinkRemoteDocumentResult | null;\n latestLinkedContainerIds: readonly string[];\n} | null> {\n const { noteId, runtime, targetContainerId } = input;\n let { latestDocument, latestLinkedContainerIds } = input;\n const failedUnlinkContainerIds: string[] = [];\n for (const unlinkContainerId of input.unlinkContainerIds) {\n if (input.isCurrent?.() === false) return null;\n const unlinkedDocument = await unlinkRemoteContainerDocument({\n documentId: input.documentId,\n isCurrent: input.isCurrent,\n noteId,\n onFailure: input.onFailure,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n rotationSnapshot: input.rotationSnapshot,\n runtime,\n targetContainerId: unlinkContainerId,\n });\n if (input.isCurrent?.() === false) return null;\n if (!unlinkedDocument) {\n failedUnlinkContainerIds.push(unlinkContainerId);\n runtime.util.log(\n `Container contents: note ${noteId} was linked to ${targetContainerId} but failed to unlink from ${unlinkContainerId}`,\n );\n continue;\n }\n\n latestDocument = unlinkedDocument;\n latestLinkedContainerIds = unlinkedDocument.linkedContainerIds;\n }\n return { failedUnlinkContainerIds, latestDocument, latestLinkedContainerIds };\n}\n"]}
|
|
@@ -3,7 +3,7 @@ import type { StoredDocumentKind } from "../../data/documents/documentKinds.js";
|
|
|
3
3
|
import type { ProjectionUserKeyResolver } from "../../data/keyingProjectionVerification.js";
|
|
4
4
|
import { type DocumentsPersistence } from "../documents/index.js";
|
|
5
5
|
import type { ContainerContentsWorkflowRuntime } from "./runtime.js";
|
|
6
|
-
type ContainerDocumentPurgeApi = Pick<ContainerContentsWorkflowRuntime["apiClient"], "
|
|
6
|
+
type ContainerDocumentPurgeApi = Pick<ContainerContentsWorkflowRuntime["apiClient"], "getPrincipalPolicyPages" | "getProjectionPolicyHistoryPages" | "getDocumentPurgeProof" | "getDocumentWriterProjectionResult" | "purgeDocument">;
|
|
7
7
|
type DocumentPurgeResult = DocumentPurgeResponse | {
|
|
8
8
|
readonly documentId: string;
|
|
9
9
|
readonly purgedAt: string;
|
|
@@ -71,8 +71,8 @@ export async function purgeRemoteContainerDocument(input) {
|
|
|
71
71
|
runtime.util.log(`Container contents: failed to purge note ${noteId} (document ${documentId})`);
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
74
|
-
if (deleted
|
|
75
|
-
void reclaimDocumentOrphanBlobs(runtime);
|
|
74
|
+
if (deleted) {
|
|
75
|
+
void reclaimDocumentOrphanBlobs(runtime, persistence);
|
|
76
76
|
}
|
|
77
77
|
runtime.util.log(`Container contents: purged note ${noteId} (document ${documentId})`);
|
|
78
78
|
return response;
|
|
@@ -111,8 +111,8 @@ export async function purgeLocalContainerDocument(input) {
|
|
|
111
111
|
runtime.util.log(`Container contents: local-only purge refused for note ${noteId} because its identity changed`);
|
|
112
112
|
return null;
|
|
113
113
|
}
|
|
114
|
-
if (deleted
|
|
115
|
-
void reclaimDocumentOrphanBlobs(runtime);
|
|
114
|
+
if (deleted) {
|
|
115
|
+
void reclaimDocumentOrphanBlobs(runtime, persistence);
|
|
116
116
|
}
|
|
117
117
|
runtime.util.log(`Container contents: purged local-only note ${noteId}`);
|
|
118
118
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documentPurge.js","sourceRoot":"","sources":["documentPurge.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentPurgeResponse } from \"@tearleads/validators/response\";\nimport type { StoredDocumentKind } from \"../../data/documents/documentKinds\";\nimport { errorMessage } from \"../../data/errorMessage\";\nimport type { ProjectionUserKeyResolver } from \"../../data/keyingProjectionVerification\";\nimport { reportAndRethrowKeyingVerificationError } from \"../../data/keyingProjectionVerification/error\";\nimport { rethrowProjectionVerificationCancelled } from \"../../data/keyingProjectionVerification/types\";\nimport {\n type DocumentsPersistence,\n defaultDocumentsPersistence,\n deletePersistedDocument,\n purgeRemoteDocument,\n reclaimDocumentOrphanBlobs,\n resolveDocumentCreateAuthor,\n} from \"../documents\";\nimport { ensureDocumentClientProjectionTables } from \"../documents/persistence\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../principals/runtimePolicyWarmer\";\nimport type { ContainerContentsWorkflowRuntime } from \"./runtime\";\n\ntype ContainerDocumentPurgeApi = Pick<\n ContainerContentsWorkflowRuntime[\"apiClient\"],\n | \"getCurrentPrincipalPolicy\"\n | \"getDocumentPurgeProof\"\n | \"getDocumentWriterProjectionResult\"\n | \"purgeDocument\"\n>;\ntype DocumentPurgeResult =\n | DocumentPurgeResponse\n | {\n readonly documentId: string;\n readonly purgedAt: string;\n readonly reclaimedBlobStorageKeys: readonly string[];\n }\n | null;\n\ninterface ContainerDocumentPurgeRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: ContainerDocumentPurgeApi;\n}\n\ninterface PurgeRemoteContainerDocumentInput {\n documentId: string;\n documentKind: StoredDocumentKind;\n noteId: string;\n persistence?: DocumentsPersistence | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerDocumentPurgeRuntime;\n}\n\nasync function deleteAbsentDocumentState(input: {\n readonly commitPurgeProof: (\n transactionExecSql: ContainerDocumentPurgeRuntime[\"infra\"][\"execSql\"],\n ) => Promise<void>;\n readonly documentId: string;\n readonly documentKind: StoredDocumentKind;\n readonly noteId: string;\n readonly persistence: DocumentsPersistence;\n readonly runtime: ContainerDocumentPurgeRuntime;\n}): Promise<boolean> {\n return input.persistence.deleteDocumentSideRowsIfAbsent(\n input.runtime.infra.execSql,\n input.noteId,\n input.documentId,\n async (transactionExecSql) => {\n await input.runtime.infra.documentProjectors.deleteStoredDocumentClientProjection(\n {\n documentKind: input.documentKind,\n execSql: transactionExecSql,\n localId: input.noteId,\n },\n );\n await input.commitPurgeProof(transactionExecSql);\n },\n );\n}\n\nasync function commitVerifiedDocumentPurge(input: {\n readonly commitPurgeProof: (\n transactionExecSql: ContainerDocumentPurgeRuntime[\"infra\"][\"execSql\"],\n ) => Promise<void>;\n readonly documentId: string;\n readonly documentKind: StoredDocumentKind;\n readonly noteId: string;\n readonly persistence: DocumentsPersistence;\n readonly runtime: ContainerDocumentPurgeRuntime;\n}): Promise<boolean> {\n await input.persistence.ensureSchema(input.runtime.infra.execSql);\n await ensureDocumentClientProjectionTables({\n documentProjectors: input.runtime.infra.documentProjectors,\n execSql: input.runtime.infra.execSql,\n });\n const expectedRecord = await input.persistence.loadDocument(\n input.runtime.infra.execSql,\n input.noteId,\n );\n if (expectedRecord && expectedRecord.documentId !== input.documentId) {\n throw new Error(\n \"Local document identity changed while its remote purge was committing\",\n );\n }\n const deleted = expectedRecord\n ? await deletePersistedDocument({\n beforeDeleteInTransaction: input.commitPurgeProof,\n documentProjectors: input.runtime.infra.documentProjectors,\n execSql: input.runtime.infra.execSql,\n expectedRecord,\n localId: input.noteId,\n persistence: input.persistence,\n })\n : await deleteAbsentDocumentState(input);\n if (!deleted) {\n throw new Error(\n \"Local document state changed while its remote purge was committing\",\n );\n }\n return true;\n}\n\nexport async function purgeRemoteContainerDocument(\n input: PurgeRemoteContainerDocumentInput,\n): Promise<DocumentPurgeResult> {\n const { documentId, noteId, runtime } = input;\n const persistence = input.persistence ?? defaultDocumentsPersistence;\n try {\n const author = resolveDocumentCreateAuthor(runtime);\n if (!author) {\n runtime.util.log(\n `Container contents: failed to purge note ${noteId} because the local signing context is unavailable`,\n );\n return null;\n }\n let deleted = false;\n const response = await purgeRemoteDocument({\n apiClient: runtime.apiClient,\n author,\n documentId,\n execSql: runtime.infra.execSql,\n onVerifiedPurge: async ({ commitPurgeProof }) => {\n deleted = await commitVerifiedDocumentPurge({\n commitPurgeProof,\n documentId,\n documentKind: input.documentKind,\n noteId,\n persistence,\n runtime,\n });\n },\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n warmReferencedPrincipalPolicies:\n createRuntimePrincipalPolicyWarmer(runtime),\n });\n if (!response) {\n runtime.util.log(\n `Container contents: failed to purge note ${noteId} (document ${documentId})`,\n );\n return null;\n }\n if (deleted && persistence === defaultDocumentsPersistence) {\n void reclaimDocumentOrphanBlobs(runtime);\n }\n runtime.util.log(\n `Container contents: purged note ${noteId} (document ${documentId})`,\n );\n return response;\n } catch (error) {\n // A cancelled verification is neither a failure nor a completed purge; the\n // caller that owns the generation decides what to do with it.\n rethrowProjectionVerificationCancelled(error);\n await reportAndRethrowKeyingVerificationError(\n error,\n runtime.util.reportSecurityIncident,\n {\n objectId: documentId,\n objectKind: \"document\",\n operation: \"document.purge\",\n },\n );\n runtime.util.log(\n `Container contents: failed to purge note ${noteId} (document ${documentId}): ${errorMessage(error)}`,\n );\n return null;\n }\n}\n\ninterface ContainerDocumentLocalPurgeRuntime\n extends Pick<ContainerContentsWorkflowRuntime, \"infra\" | \"util\"> {}\n\nexport async function purgeLocalContainerDocument(input: {\n noteId: string;\n persistence?: DocumentsPersistence | undefined;\n runtime: ContainerDocumentLocalPurgeRuntime;\n}): Promise<DocumentPurgeResult> {\n const { noteId, runtime } = input;\n const persistence = input.persistence ?? defaultDocumentsPersistence;\n try {\n await persistence.ensureSchema(runtime.infra.execSql);\n const expectedRecord = await persistence.loadDocument(\n runtime.infra.execSql,\n noteId,\n );\n if (!expectedRecord || expectedRecord.documentId !== null) {\n runtime.util.log(\n `Container contents: local-only purge refused for note ${noteId} because it is absent or remote-backed`,\n );\n return null;\n }\n const deleted = await deletePersistedDocument({\n documentProjectors: runtime.infra.documentProjectors,\n execSql: runtime.infra.execSql,\n expectedRecord,\n localId: noteId,\n persistence,\n });\n if (!deleted) {\n runtime.util.log(\n `Container contents: local-only purge refused for note ${noteId} because its identity changed`,\n );\n return null;\n }\n if (deleted && persistence === defaultDocumentsPersistence) {\n void reclaimDocumentOrphanBlobs(runtime);\n }\n runtime.util.log(`Container contents: purged local-only note ${noteId}`);\n return {\n documentId: noteId,\n purgedAt: new Date().toISOString(),\n reclaimedBlobStorageKeys: [],\n };\n } catch (error) {\n runtime.util.log(\n `Container contents: failed to purge local-only note ${noteId}: ${errorMessage(error)}`,\n );\n return null;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"documentPurge.js","sourceRoot":"","sources":["documentPurge.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentPurgeResponse } from \"@tearleads/validators/response\";\nimport type { StoredDocumentKind } from \"../../data/documents/documentKinds\";\nimport { errorMessage } from \"../../data/errorMessage\";\nimport type { ProjectionUserKeyResolver } from \"../../data/keyingProjectionVerification\";\nimport { reportAndRethrowKeyingVerificationError } from \"../../data/keyingProjectionVerification/error\";\nimport { rethrowProjectionVerificationCancelled } from \"../../data/keyingProjectionVerification/types\";\nimport {\n type DocumentsPersistence,\n defaultDocumentsPersistence,\n deletePersistedDocument,\n purgeRemoteDocument,\n reclaimDocumentOrphanBlobs,\n resolveDocumentCreateAuthor,\n} from \"../documents\";\nimport { ensureDocumentClientProjectionTables } from \"../documents/persistence\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../principals/runtimePolicyWarmer\";\nimport type { ContainerContentsWorkflowRuntime } from \"./runtime\";\n\ntype ContainerDocumentPurgeApi = Pick<\n ContainerContentsWorkflowRuntime[\"apiClient\"],\n | \"getPrincipalPolicyPages\"\n | \"getProjectionPolicyHistoryPages\"\n | \"getDocumentPurgeProof\"\n | \"getDocumentWriterProjectionResult\"\n | \"purgeDocument\"\n>;\ntype DocumentPurgeResult =\n | DocumentPurgeResponse\n | {\n readonly documentId: string;\n readonly purgedAt: string;\n readonly reclaimedBlobStorageKeys: readonly string[];\n }\n | null;\n\ninterface ContainerDocumentPurgeRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: ContainerDocumentPurgeApi;\n}\n\ninterface PurgeRemoteContainerDocumentInput {\n documentId: string;\n documentKind: StoredDocumentKind;\n noteId: string;\n persistence?: DocumentsPersistence | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerDocumentPurgeRuntime;\n}\n\nasync function deleteAbsentDocumentState(input: {\n readonly commitPurgeProof: (\n transactionExecSql: ContainerDocumentPurgeRuntime[\"infra\"][\"execSql\"],\n ) => Promise<void>;\n readonly documentId: string;\n readonly documentKind: StoredDocumentKind;\n readonly noteId: string;\n readonly persistence: DocumentsPersistence;\n readonly runtime: ContainerDocumentPurgeRuntime;\n}): Promise<boolean> {\n return input.persistence.deleteDocumentSideRowsIfAbsent(\n input.runtime.infra.execSql,\n input.noteId,\n input.documentId,\n async (transactionExecSql) => {\n await input.runtime.infra.documentProjectors.deleteStoredDocumentClientProjection(\n {\n documentKind: input.documentKind,\n execSql: transactionExecSql,\n localId: input.noteId,\n },\n );\n await input.commitPurgeProof(transactionExecSql);\n },\n );\n}\n\nasync function commitVerifiedDocumentPurge(input: {\n readonly commitPurgeProof: (\n transactionExecSql: ContainerDocumentPurgeRuntime[\"infra\"][\"execSql\"],\n ) => Promise<void>;\n readonly documentId: string;\n readonly documentKind: StoredDocumentKind;\n readonly noteId: string;\n readonly persistence: DocumentsPersistence;\n readonly runtime: ContainerDocumentPurgeRuntime;\n}): Promise<boolean> {\n await input.persistence.ensureSchema(input.runtime.infra.execSql);\n await ensureDocumentClientProjectionTables({\n documentProjectors: input.runtime.infra.documentProjectors,\n execSql: input.runtime.infra.execSql,\n });\n const expectedRecord = await input.persistence.loadDocument(\n input.runtime.infra.execSql,\n input.noteId,\n );\n if (expectedRecord && expectedRecord.documentId !== input.documentId) {\n throw new Error(\n \"Local document identity changed while its remote purge was committing\",\n );\n }\n const deleted = expectedRecord\n ? await deletePersistedDocument({\n beforeDeleteInTransaction: input.commitPurgeProof,\n documentProjectors: input.runtime.infra.documentProjectors,\n execSql: input.runtime.infra.execSql,\n expectedRecord,\n localId: input.noteId,\n persistence: input.persistence,\n })\n : await deleteAbsentDocumentState(input);\n if (!deleted) {\n throw new Error(\n \"Local document state changed while its remote purge was committing\",\n );\n }\n return true;\n}\n\nexport async function purgeRemoteContainerDocument(\n input: PurgeRemoteContainerDocumentInput,\n): Promise<DocumentPurgeResult> {\n const { documentId, noteId, runtime } = input;\n const persistence = input.persistence ?? defaultDocumentsPersistence;\n try {\n const author = resolveDocumentCreateAuthor(runtime);\n if (!author) {\n runtime.util.log(\n `Container contents: failed to purge note ${noteId} because the local signing context is unavailable`,\n );\n return null;\n }\n let deleted = false;\n const response = await purgeRemoteDocument({\n apiClient: runtime.apiClient,\n author,\n documentId,\n execSql: runtime.infra.execSql,\n onVerifiedPurge: async ({ commitPurgeProof }) => {\n deleted = await commitVerifiedDocumentPurge({\n commitPurgeProof,\n documentId,\n documentKind: input.documentKind,\n noteId,\n persistence,\n runtime,\n });\n },\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n warmReferencedPrincipalPolicies:\n createRuntimePrincipalPolicyWarmer(runtime),\n });\n if (!response) {\n runtime.util.log(\n `Container contents: failed to purge note ${noteId} (document ${documentId})`,\n );\n return null;\n }\n if (deleted) {\n void reclaimDocumentOrphanBlobs(runtime, persistence);\n }\n runtime.util.log(\n `Container contents: purged note ${noteId} (document ${documentId})`,\n );\n return response;\n } catch (error) {\n // A cancelled verification is neither a failure nor a completed purge; the\n // caller that owns the generation decides what to do with it.\n rethrowProjectionVerificationCancelled(error);\n await reportAndRethrowKeyingVerificationError(\n error,\n runtime.util.reportSecurityIncident,\n {\n objectId: documentId,\n objectKind: \"document\",\n operation: \"document.purge\",\n },\n );\n runtime.util.log(\n `Container contents: failed to purge note ${noteId} (document ${documentId}): ${errorMessage(error)}`,\n );\n return null;\n }\n}\n\ninterface ContainerDocumentLocalPurgeRuntime\n extends Pick<ContainerContentsWorkflowRuntime, \"infra\" | \"util\"> {}\n\nexport async function purgeLocalContainerDocument(input: {\n noteId: string;\n persistence?: DocumentsPersistence | undefined;\n runtime: ContainerDocumentLocalPurgeRuntime;\n}): Promise<DocumentPurgeResult> {\n const { noteId, runtime } = input;\n const persistence = input.persistence ?? defaultDocumentsPersistence;\n try {\n await persistence.ensureSchema(runtime.infra.execSql);\n const expectedRecord = await persistence.loadDocument(\n runtime.infra.execSql,\n noteId,\n );\n if (!expectedRecord || expectedRecord.documentId !== null) {\n runtime.util.log(\n `Container contents: local-only purge refused for note ${noteId} because it is absent or remote-backed`,\n );\n return null;\n }\n const deleted = await deletePersistedDocument({\n documentProjectors: runtime.infra.documentProjectors,\n execSql: runtime.infra.execSql,\n expectedRecord,\n localId: noteId,\n persistence,\n });\n if (!deleted) {\n runtime.util.log(\n `Container contents: local-only purge refused for note ${noteId} because its identity changed`,\n );\n return null;\n }\n if (deleted) {\n void reclaimDocumentOrphanBlobs(runtime, persistence);\n }\n runtime.util.log(`Container contents: purged local-only note ${noteId}`);\n return {\n documentId: noteId,\n purgedAt: new Date().toISOString(),\n reclaimedBlobStorageKeys: [],\n };\n } catch (error) {\n runtime.util.log(\n `Container contents: failed to purge local-only note ${noteId}: ${errorMessage(error)}`,\n );\n return null;\n }\n}\n"]}
|
|
@@ -4,7 +4,7 @@ import type { PendingUpdateRecord } from "../../data/sqlite/documentPersistence.
|
|
|
4
4
|
import { type RekeyPendingUpdate, syncRemoteDocument } from "../documents/index.js";
|
|
5
5
|
import type { ContainerMetadataState } from "./metadataTypes.js";
|
|
6
6
|
import type { ContainerContentsWorkflowRuntime } from "./runtime.js";
|
|
7
|
-
type ContainerMetadataSyncApi = Parameters<typeof syncRemoteDocument>[0]["apiClient"] & Pick<ContainerContentsWorkflowRuntime["apiClient"], "
|
|
7
|
+
type ContainerMetadataSyncApi = Parameters<typeof syncRemoteDocument>[0]["apiClient"] & Pick<ContainerContentsWorkflowRuntime["apiClient"], "getPrincipalPolicyPages" | "getProjectionPolicyHistoryPages">;
|
|
8
8
|
export interface ContainerMetadataSyncRuntime extends Pick<ContainerContentsWorkflowRuntime, "auth" | "crypto" | "infra" | "resolveTrustedUserIdentity" | "state" | "util"> {
|
|
9
9
|
apiClient: ContainerMetadataSyncApi;
|
|
10
10
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadataRemoteSync.js","sourceRoot":"","sources":["metadataRemoteSync.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport type { ProjectionUserKeyResolver } from \"../../data/keyingProjectionVerification\";\nimport { CONTAINER_METADATA_APP_KIND } from \"../../data/persistence/container-contents/containerContentsPersistence\";\nimport type { PendingUpdateRecord } from \"../../data/sqlite/documentPersistence\";\nimport {\n createDocumentWriterPublicKeyResolver,\n describeDocumentSyncSubmitFailure,\n type RekeyPendingUpdate,\n resolveDocumentCreateAuthor,\n syncRemoteDocument,\n} from \"../documents\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../principals/runtimePolicyWarmer\";\nimport {\n metadataIncomingUpdateIsolation,\n recordCurrentMetadataSyncFailure,\n} from \"./metadataIncomingUpdateIsolation\";\nimport { deferRecoverableMetadataSyncError } from \"./metadataSyncErrors\";\nimport type { ContainerMetadataState } from \"./metadataTypes\";\nimport type { ContainerContentsWorkflowRuntime } from \"./runtime\";\n\ntype ContainerMetadataSyncApi = Parameters<\n typeof syncRemoteDocument\n>[0][\"apiClient\"] &\n Pick<\n ContainerContentsWorkflowRuntime[\"apiClient\"],\n \"
|
|
1
|
+
{"version":3,"file":"metadataRemoteSync.js","sourceRoot":"","sources":["metadataRemoteSync.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport type { ProjectionUserKeyResolver } from \"../../data/keyingProjectionVerification\";\nimport { CONTAINER_METADATA_APP_KIND } from \"../../data/persistence/container-contents/containerContentsPersistence\";\nimport type { PendingUpdateRecord } from \"../../data/sqlite/documentPersistence\";\nimport {\n createDocumentWriterPublicKeyResolver,\n describeDocumentSyncSubmitFailure,\n type RekeyPendingUpdate,\n resolveDocumentCreateAuthor,\n syncRemoteDocument,\n} from \"../documents\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../principals/runtimePolicyWarmer\";\nimport {\n metadataIncomingUpdateIsolation,\n recordCurrentMetadataSyncFailure,\n} from \"./metadataIncomingUpdateIsolation\";\nimport { deferRecoverableMetadataSyncError } from \"./metadataSyncErrors\";\nimport type { ContainerMetadataState } from \"./metadataTypes\";\nimport type { ContainerContentsWorkflowRuntime } from \"./runtime\";\n\ntype ContainerMetadataSyncApi = Parameters<\n typeof syncRemoteDocument\n>[0][\"apiClient\"] &\n Pick<\n ContainerContentsWorkflowRuntime[\"apiClient\"],\n \"getPrincipalPolicyPages\" | \"getProjectionPolicyHistoryPages\"\n >;\n\nexport interface ContainerMetadataSyncRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: ContainerMetadataSyncApi;\n}\n\ntype ContainerMetadataSyncResult = NonNullable<\n Awaited<ReturnType<typeof syncRemoteDocument>>\n>;\n\nexport interface ContainerMetadataSyncAttempt {\n consumedPullContinuation: ContainerMetadataState[\"pullContinuation\"];\n outgoingUpdateCount: number;\n requestRecord: ContainerMetadataState[\"record\"];\n synced: ContainerMetadataSyncResult;\n}\n\ninterface SyncRemoteContainerMetadataInput {\n buildRotationSnapshot?: (() => Promise<Uint8Array | null>) | undefined;\n containerId: string;\n currentDocument: ContainerMetadataState[\"doc\"];\n documentId: string | null;\n isCurrent: () => boolean;\n lastCommitLsn?: string | null | undefined;\n localVersionVector: string | null;\n onOutgoingUpdatesMaterialized?:\n | ((updateIds: readonly string[]) => void)\n | undefined;\n onPullContinuationInvalidated?: Parameters<\n typeof syncRemoteDocument\n >[0][\"onPullContinuationInvalidated\"];\n pendingUpdates: readonly PendingUpdateRecord[];\n persistedState: ContainerMetadataState[\"record\"];\n pullContinuation?: ContainerMetadataState[\"pullContinuation\"];\n rekeyPendingUpdate: RekeyPendingUpdate;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerMetadataSyncRuntime;\n targetSecretKey: Uint8Array;\n writerProjection?: DocumentWriterProjectionResponse | undefined;\n}\n\nfunction createContainerMetadataSyncAttempt(input: {\n outgoingUpdateCount: number;\n requestedPullContinuation: ContainerMetadataState[\"pullContinuation\"];\n requestRecord: ContainerMetadataState[\"record\"];\n synced: ContainerMetadataSyncResult;\n}): ContainerMetadataSyncAttempt {\n return {\n consumedPullContinuation:\n input.synced.plan.request.pullCursor === undefined\n ? null\n : (input.requestedPullContinuation ?? null),\n outgoingUpdateCount: input.outgoingUpdateCount,\n requestRecord: input.requestRecord,\n synced: input.synced,\n };\n}\n\nexport async function syncRemoteContainerMetadata(\n input: SyncRemoteContainerMetadataInput,\n): Promise<ContainerMetadataSyncAttempt | null> {\n const { documentId, isCurrent, runtime } = input;\n if (!documentId) return null;\n const author = resolveDocumentCreateAuthor(runtime);\n if (!author) {\n runtime.util.log(\n \"Container contents: skipped metadata sync because the writer context is unavailable.\",\n );\n return null;\n }\n const execSql = runtime.infra.execSql;\n const metadataScope = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: input.containerId,\n };\n const synced = await syncRemoteDocument({\n apiClient: runtime.apiClient,\n author,\n buildRotationSnapshot: input.buildRotationSnapshot,\n documentId,\n execSql,\n isRemoteSyncBlocked: runtime.util.isRemoteSyncBlocked,\n localVersionVector: input.localVersionVector,\n minLsn: input.lastCommitLsn ?? undefined,\n ...metadataIncomingUpdateIsolation({\n currentDocument: input.currentDocument,\n execSql,\n isCurrent,\n logError: runtime.util.logError,\n metadataScope,\n }),\n onOutgoingUpdatesMaterialized: input.onOutgoingUpdatesMaterialized,\n onPullContinuationInvalidated: input.onPullContinuationInvalidated,\n onSyncTrace: (line) => runtime.util.log(`Container contents: ${line}`),\n onTerminalSubmitFailure: async (failure) => {\n await recordCurrentMetadataSyncFailure({\n execSql,\n failure: {\n attemptedAt: new Date().toISOString(),\n message: describeDocumentSyncSubmitFailure(failure),\n status: failure.status,\n },\n isCurrent,\n metadataScope,\n });\n },\n pendingUpdates: input.pendingUpdates,\n persistedState: input.persistedState,\n pullContinuation: input.pullContinuation ?? undefined,\n rekeyPendingUpdate: (rekeyExecSql, updateId) =>\n isCurrent()\n ? input.rekeyPendingUpdate(rekeyExecSql, updateId)\n : Promise.resolve(null),\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n resolveWriterPublicKey: createDocumentWriterPublicKeyResolver({\n logPrefix: \"Container contents\",\n runtime,\n writerKeyLabel: \"metadata writer key\",\n }),\n stillCurrent: isCurrent,\n targetSecretKey: input.targetSecretKey,\n warmReferencedPrincipalPolicies:\n createRuntimePrincipalPolicyWarmer(runtime),\n writerProjection: input.writerProjection,\n }).catch((error: unknown) =>\n deferRecoverableMetadataSyncError({\n containerId: input.containerId,\n error,\n runtime,\n }),\n );\n if (!synced) return null;\n return createContainerMetadataSyncAttempt({\n outgoingUpdateCount: input.pendingUpdates.length,\n requestedPullContinuation: input.pullContinuation,\n requestRecord: input.persistedState,\n synced,\n });\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RequestResult } from "../../../internal/api-client/index.js";
|
|
1
|
+
import type { ApiClient, RequestResult } from "../../../internal/api-client/index.js";
|
|
2
2
|
import type { ContainerReplacementAuthorizationsResponse } from "../../../internal/validators/operation/index.js";
|
|
3
3
|
import type { ListContainerParentLanesRequest } from "../../../internal/validators/request/index.js";
|
|
4
4
|
import type { ContainerSummary, ContainerWriterProjectionResponse, DocumentWriterProjectionResponse, ListContainerParentLanesResponse, ListContainersResponse, PrincipalPolicyBundleResponse, ReferencedPrincipalStateResponse } from "../../../internal/validators/response/index.js";
|
|
@@ -30,7 +30,7 @@ export interface ContainerState {
|
|
|
30
30
|
pullContinuation?: ContainerDocumentRecord["pullContinuation"] | undefined;
|
|
31
31
|
record: ContainerDocumentRecord;
|
|
32
32
|
}
|
|
33
|
-
interface RemoteContainerHydrationApi {
|
|
33
|
+
interface RemoteContainerHydrationApi extends Partial<Pick<ApiClient, "getPrincipalPolicyPages" | "getProjectionPolicyHistoryPages">> {
|
|
34
34
|
getContainerReplacementAuthorizationsResult(containerId: string, replacesOrganizationId: string): Promise<RequestResult<ContainerReplacementAuthorizationsResponse>>;
|
|
35
35
|
evictContainerWriterProjection(containerId: string): void;
|
|
36
36
|
getContainerWriterProjection(containerId: string): Promise<ContainerWriterProjectionResponse | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { RequestResult } from \"@tearleads/api-client\";\nimport type { ContainerReplacementAuthorizationsResponse } from \"@tearleads/validators/operation\";\nimport type { ListContainerParentLanesRequest } from \"@tearleads/validators/request\";\nimport type {\n ContainerSummary,\n ContainerWriterProjectionResponse,\n DocumentWriterProjectionResponse,\n ListContainerParentLanesResponse,\n ListContainersResponse,\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type {\n ContainerContentsPersistence,\n ContainerDocumentRecord,\n ContainerHydrationTombstone,\n ContainerRecord,\n createContainerParentSyncLane,\n} from \"../containerPersistence\";\nimport type { ContainerMetadataPatch } from \"../metadata\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ListedRemoteContainerPageItem =\n ListContainersResponse[\"items\"][number];\nexport type ContainerChildIndex = Map<string, Set<string>>;\nexport type QueueContainerParentLane = (parentId: string | null) => void;\nexport type RemoteContainerIngestQueue = Map<string, RemoteContainer>;\nexport type SaveContainerOptions = Parameters<\n ContainerContentsPersistence[\"saveContainer\"]\n>[3];\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createContainerMetadataDocument>\n>;\nexport type RemoteContainer = Pick<\n ContainerSummary,\n | \"createdAt\"\n | \"effectiveAccessLevel\"\n | \"systemSlot\"\n | \"id\"\n | \"metadataAccessEpoch\"\n | \"metadataAccessStateHash\"\n | \"metadataDocumentId\"\n | \"metadataReferencedPrincipals\"\n | \"organizationId\"\n | \"parentId\"\n | \"updatedAt\"\n>;\n\nexport interface ContainerState {\n containerWriterProjection?:\n | ContainerWriterProjectionResponse\n | null\n | undefined;\n /**\n * Bumped whenever a realtime hint drops this container's cached writer\n * projections (container and metadata document alike); an operation that\n * fetched or carried a projection installs it only if this is unchanged\n * since it started, so a pre-hint answer never lands after the hint.\n */\n writerProjectionGeneration?: number | undefined;\n metadataReferencedPrincipals?:\n | readonly ReferencedPrincipalStateResponse[]\n | undefined;\n container: ContainerRecord;\n doc: ContainerMetadataDocumentState;\n metadataWriterProjection?:\n | DocumentWriterProjectionResponse\n | null\n | undefined;\n /** Durable pull progress mirrored from the metadata document record. */\n pullContinuation?: ContainerDocumentRecord[\"pullContinuation\"] | undefined;\n record: ContainerDocumentRecord;\n}\n\ninterface RemoteContainerHydrationApi {\n getContainerReplacementAuthorizationsResult(\n containerId: string,\n replacesOrganizationId: string,\n ): Promise<RequestResult<ContainerReplacementAuthorizationsResponse>>;\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n getCurrentPrincipalPolicy(\n principalType: \"group\" | \"organization\",\n principalId: string,\n ): Promise<PrincipalPolicyBundleResponse | null>;\n listContainerParentLanes(\n input: ListContainerParentLanesRequest,\n ): Promise<ListContainerParentLanesResponse | null>;\n}\n\ninterface RemoteContainerHydrationRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"adoptRootContainer\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: RemoteContainerHydrationApi;\n}\n\nexport interface RemoteContainerHydrationState {\n containersById: Map<string, ContainerState>;\n lifecycleGeneration?: number | undefined;\n persistence: ContainerContentsPersistence;\n rootLaneHydrated?: boolean | undefined;\n runtime: RemoteContainerHydrationRuntime;\n structuralGeneration?: number | undefined;\n}\n\nexport type PersistContainerStateResult =\n | { status: \"identity-superseded\"; record: ContainerDocumentRecord }\n | { status: \"missing\" }\n | {\n status: \"persisted\";\n record: ContainerDocumentRecord;\n createIntentSettled?: true | undefined;\n moveIntentSettled?: true | undefined;\n }\n | { status: \"stale-generation\" };\n\nexport interface RemoteContainerHydrationHost {\n persistContainerState: (\n containerState: ContainerState,\n patch?: Partial<ContainerMetadataPatch>,\n updateView?: boolean,\n saveOptions?: SaveContainerOptions,\n mutationOptions?: {\n expectedStateWhenMissing?: ContainerState | undefined;\n isCurrent?: (() => boolean) | undefined;\n createIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"createIntentSettlement\"];\n moveIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"moveIntentSettlement\"];\n preserveDurableStructureWhenPending?: boolean | undefined;\n },\n ) => Promise<PersistContainerStateResult>;\n requestDocumentPriming?: (() => void) | undefined;\n updateSnapshot: () => void;\n}\n\nexport interface ContainerParentHydrationLane {\n parentId: string | null;\n watermark?: ListContainersResponse[\"nextWatermark\"];\n}\n\nexport interface ExpectedContainerState {\n container: ContainerRecord;\n fingerprint: string;\n}\n\nexport interface FetchedContainerParentLanePage {\n expectedContainerStates: ReadonlyMap<string, ExpectedContainerState>;\n expectedHydrationTombstones: ReadonlyMap<string, ContainerHydrationTombstone>;\n lane: ContainerParentHydrationLane;\n response: ListContainersResponse;\n syncLane: ReturnType<typeof createContainerParentSyncLane>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ApiClient, RequestResult } from \"@tearleads/api-client\";\nimport type { ContainerReplacementAuthorizationsResponse } from \"@tearleads/validators/operation\";\nimport type { ListContainerParentLanesRequest } from \"@tearleads/validators/request\";\nimport type {\n ContainerSummary,\n ContainerWriterProjectionResponse,\n DocumentWriterProjectionResponse,\n ListContainerParentLanesResponse,\n ListContainersResponse,\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type {\n ContainerContentsPersistence,\n ContainerDocumentRecord,\n ContainerHydrationTombstone,\n ContainerRecord,\n createContainerParentSyncLane,\n} from \"../containerPersistence\";\nimport type { ContainerMetadataPatch } from \"../metadata\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ListedRemoteContainerPageItem =\n ListContainersResponse[\"items\"][number];\nexport type ContainerChildIndex = Map<string, Set<string>>;\nexport type QueueContainerParentLane = (parentId: string | null) => void;\nexport type RemoteContainerIngestQueue = Map<string, RemoteContainer>;\nexport type SaveContainerOptions = Parameters<\n ContainerContentsPersistence[\"saveContainer\"]\n>[3];\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createContainerMetadataDocument>\n>;\nexport type RemoteContainer = Pick<\n ContainerSummary,\n | \"createdAt\"\n | \"effectiveAccessLevel\"\n | \"systemSlot\"\n | \"id\"\n | \"metadataAccessEpoch\"\n | \"metadataAccessStateHash\"\n | \"metadataDocumentId\"\n | \"metadataReferencedPrincipals\"\n | \"organizationId\"\n | \"parentId\"\n | \"updatedAt\"\n>;\n\nexport interface ContainerState {\n containerWriterProjection?:\n | ContainerWriterProjectionResponse\n | null\n | undefined;\n /**\n * Bumped whenever a realtime hint drops this container's cached writer\n * projections (container and metadata document alike); an operation that\n * fetched or carried a projection installs it only if this is unchanged\n * since it started, so a pre-hint answer never lands after the hint.\n */\n writerProjectionGeneration?: number | undefined;\n metadataReferencedPrincipals?:\n | readonly ReferencedPrincipalStateResponse[]\n | undefined;\n container: ContainerRecord;\n doc: ContainerMetadataDocumentState;\n metadataWriterProjection?:\n | DocumentWriterProjectionResponse\n | null\n | undefined;\n /** Durable pull progress mirrored from the metadata document record. */\n pullContinuation?: ContainerDocumentRecord[\"pullContinuation\"] | undefined;\n record: ContainerDocumentRecord;\n}\n\ninterface RemoteContainerHydrationApi\n extends Partial<\n Pick<\n ApiClient,\n \"getPrincipalPolicyPages\" | \"getProjectionPolicyHistoryPages\"\n >\n > {\n getContainerReplacementAuthorizationsResult(\n containerId: string,\n replacesOrganizationId: string,\n ): Promise<RequestResult<ContainerReplacementAuthorizationsResponse>>;\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n getCurrentPrincipalPolicy(\n principalType: \"group\" | \"organization\",\n principalId: string,\n ): Promise<PrincipalPolicyBundleResponse | null>;\n listContainerParentLanes(\n input: ListContainerParentLanesRequest,\n ): Promise<ListContainerParentLanesResponse | null>;\n}\n\ninterface RemoteContainerHydrationRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"adoptRootContainer\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: RemoteContainerHydrationApi;\n}\n\nexport interface RemoteContainerHydrationState {\n containersById: Map<string, ContainerState>;\n lifecycleGeneration?: number | undefined;\n persistence: ContainerContentsPersistence;\n rootLaneHydrated?: boolean | undefined;\n runtime: RemoteContainerHydrationRuntime;\n structuralGeneration?: number | undefined;\n}\n\nexport type PersistContainerStateResult =\n | { status: \"identity-superseded\"; record: ContainerDocumentRecord }\n | { status: \"missing\" }\n | {\n status: \"persisted\";\n record: ContainerDocumentRecord;\n createIntentSettled?: true | undefined;\n moveIntentSettled?: true | undefined;\n }\n | { status: \"stale-generation\" };\n\nexport interface RemoteContainerHydrationHost {\n persistContainerState: (\n containerState: ContainerState,\n patch?: Partial<ContainerMetadataPatch>,\n updateView?: boolean,\n saveOptions?: SaveContainerOptions,\n mutationOptions?: {\n expectedStateWhenMissing?: ContainerState | undefined;\n isCurrent?: (() => boolean) | undefined;\n createIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"createIntentSettlement\"];\n moveIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"moveIntentSettlement\"];\n preserveDurableStructureWhenPending?: boolean | undefined;\n },\n ) => Promise<PersistContainerStateResult>;\n requestDocumentPriming?: (() => void) | undefined;\n updateSnapshot: () => void;\n}\n\nexport interface ContainerParentHydrationLane {\n parentId: string | null;\n watermark?: ListContainersResponse[\"nextWatermark\"];\n}\n\nexport interface ExpectedContainerState {\n container: ContainerRecord;\n fingerprint: string;\n}\n\nexport interface FetchedContainerParentLanePage {\n expectedContainerStates: ReadonlyMap<string, ExpectedContainerState>;\n expectedHydrationTombstones: ReadonlyMap<string, ContainerHydrationTombstone>;\n lane: ContainerParentHydrationLane;\n response: ListContainersResponse;\n syncLane: ReturnType<typeof createContainerParentSyncLane>;\n}\n"]}
|
|
@@ -5,7 +5,7 @@ import { verifiedContainerCreateManifest } from "../../../data/keyingProjectionV
|
|
|
5
5
|
import { verifyContainerDestinationProjection } from "../../../data/keyingProjectionVerification/containerDestinationVerification.js";
|
|
6
6
|
import { verifyContainerWriterProjection } from "../../../data/keyingProjectionVerification/containerProjectionVerification.js";
|
|
7
7
|
import { isKeyingVerificationError, reportKeyingVerificationErrorInCauseChain, runWithSecurityIncidentReporting, } from "../../../data/keyingProjectionVerification/error.js";
|
|
8
|
-
import {
|
|
8
|
+
import { verifyRuntimeGroupMetadataAuthority } from "../../organizations/runtimeGroupMetadataAuthority.js";
|
|
9
9
|
import { createRuntimePrincipalPolicyWarmer } from "../../principals/runtimePolicyWarmer.js";
|
|
10
10
|
import { cachedDestinationRole, rememberDestinationRole, } from "./destinationRoleCache.js";
|
|
11
11
|
import { resolveWithMetadataRootReload } from "./destinationRootReload.js";
|
|
@@ -124,14 +124,13 @@ async function verifyDestinationRole(input) {
|
|
|
124
124
|
const head = path.at(-1);
|
|
125
125
|
if (!head)
|
|
126
126
|
throw new Error("Metadata root manifest is unavailable");
|
|
127
|
-
await
|
|
128
|
-
|
|
129
|
-
execSql: runtime.infra.execSql,
|
|
127
|
+
await verifyRuntimeGroupMetadataAuthority({
|
|
128
|
+
runtime,
|
|
130
129
|
organizationId: listed.organizationId,
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
organizationReference: projection.policyEvidence.organization?.head,
|
|
131
|
+
state: head.state,
|
|
133
132
|
stillCurrent: isCurrent ?? (() => true),
|
|
134
|
-
})
|
|
133
|
+
});
|
|
135
134
|
}
|
|
136
135
|
if (isCurrent?.() === false)
|
|
137
136
|
return null;
|