@tearleads/client-sdk 0.1.79 → 0.1.81
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/deviceFirst/index.d.ts +1 -1
- package/client/deviceFirst/index.js +1 -1
- package/client/deviceFirst/index.js.map +1 -1
- package/client/runtime/principalMutationApi.js +1 -1
- package/client/runtime/principalMutationApi.js.map +1 -1
- package/client/runtime/principalMutationDispatch.js +1 -1
- package/client/runtime/principalMutationDispatch.js.map +1 -1
- package/client/runtime/rootContainerAdoption.js.map +1 -1
- package/client/runtime/workflowRuntime.d.ts +1 -1
- package/client/runtime/workflowRuntime.js.map +1 -1
- package/client/session/sessionPurgeRecovery.js +1 -1
- package/client/session/sessionPurgeRecovery.js.map +1 -1
- package/data/blobs/attachments/projection.js +2 -3
- package/data/blobs/attachments/projection.js.map +1 -1
- package/data/documents/documentConstants.d.ts +1 -0
- package/data/documents/documentConstants.js +1 -0
- package/data/documents/documentConstants.js.map +1 -1
- package/data/documents/shared/containerKek/containerKekEntryVerification.d.ts +3 -0
- package/data/documents/shared/containerKek/index.d.ts +5 -0
- package/data/documents/shared/containerKek/index.js +9 -0
- package/data/documents/shared/containerKek/index.js.map +1 -0
- package/data/documents/shared/projection/index.d.ts +1 -1
- package/data/documents/shared/projection/index.js +1 -1
- package/data/documents/shared/projection/index.js.map +1 -1
- package/data/documents/shared/sync/syncResponseAuthorization.js +1 -1
- package/data/documents/shared/sync/syncResponseAuthorization.js.map +1 -1
- package/data/keyingProjectionVerification/container/containerCreateManifest.d.ts +12 -0
- package/data/keyingProjectionVerification/container/containerDestinationVerification.d.ts +16 -0
- package/data/keyingProjectionVerification/index.d.ts +5 -0
- package/data/keyingProjectionVerification/index.js +6 -1
- package/data/keyingProjectionVerification/index.js.map +1 -1
- package/data/keyingProjectionVerification/principalPolicy/mergeProjectionPolicyEvidence.d.ts +3 -0
- package/data/keyingProjectionVerification/principalPolicy/principalPolicyCache.js +1 -1
- package/data/keyingProjectionVerification/principalPolicy/principalPolicyCache.js.map +1 -1
- package/data/keyingProjectionVerification/principalPolicy/principalPolicyCheckpointSelection.d.ts +6 -0
- package/data/{persistence → keyingProjectionVerification}/principalPolicy/principalPolicyCheckpointSelection.js +1 -1
- package/data/keyingProjectionVerification/principalPolicy/principalPolicyCheckpointSelection.js.map +1 -0
- package/data/keyingProjectionVerification/principalPolicy/principalPolicyVerification.d.ts +24 -0
- package/data/keyingProjectionVerification/principalPolicy/principalPolicyVerification.js +1 -1
- package/data/keyingProjectionVerification/principalPolicy/principalPolicyVerification.js.map +1 -1
- package/data/keyingProjectionVerification/principalPolicy/resolvedPrincipalPolicy.js +1 -1
- package/data/keyingProjectionVerification/principalPolicy/resolvedPrincipalPolicy.js.map +1 -1
- package/data/persistence/container-contents/documentPlacement.js +1 -1
- package/data/persistence/container-contents/documentPlacement.js.map +1 -1
- package/data/persistence/documents/containerDocumentTombstoneHoldsPersistence.js +1 -1
- package/data/persistence/documents/containerDocumentTombstoneHoldsPersistence.js.map +1 -1
- package/data/persistence/documents/documentsPersistence.d.ts +1 -1
- package/data/persistence/documents/documentsPersistence.js +2 -3
- package/data/persistence/documents/documentsPersistence.js.map +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstones.js +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstones.js.map +1 -1
- package/data/persistence/documents/internal/createDocumentWithHistoryCheckpoint.js +1 -1
- package/data/persistence/documents/internal/createDocumentWithHistoryCheckpoint.js.map +1 -1
- package/data/persistence/documents/internal/documentDeletionPersistence.js +1 -1
- package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
- package/data/persistence/documents/internal/documentMutationPersistence.js +1 -1
- package/data/persistence/documents/internal/documentMutationPersistence.js.map +1 -1
- package/data/persistence/documents/internal/documentProjectionRows.js +1 -2
- package/data/persistence/documents/internal/documentProjectionRows.js.map +1 -1
- package/data/persistence/documents/internal/documentPullContinuationPersistence.js +1 -1
- package/data/persistence/documents/internal/documentPullContinuationPersistence.js.map +1 -1
- package/data/persistence/documents/internal/documentRows.js +1 -2
- package/data/persistence/documents/internal/documentRows.js.map +1 -1
- package/data/persistence/documents/internal/orphanSideRows.js +1 -1
- package/data/persistence/documents/internal/orphanSideRows.js.map +1 -1
- package/data/persistence/documents/internal/pendingCreateAdoption.js +1 -1
- package/data/persistence/documents/internal/pendingCreateAdoption.js.map +1 -1
- package/data/persistence/documents/internal/rowQueryPersistence.js +1 -1
- package/data/persistence/documents/internal/rowQueryPersistence.js.map +1 -1
- package/data/persistence/keyingCheckpoints/keyingCheckpointAdvancePersistence.js +11 -51
- package/data/persistence/keyingCheckpoints/keyingCheckpointAdvancePersistence.js.map +1 -1
- package/data/persistence/keyingCheckpoints/locallyAcknowledgedCheckpointPersistence.d.ts +1 -30
- package/data/persistence/keyingCheckpoints/locallyAcknowledgedCheckpointPersistence.js +4 -85
- package/data/persistence/keyingCheckpoints/locallyAcknowledgedCheckpointPersistence.js.map +1 -1
- package/data/persistence/principalPolicy/locallyAcknowledgedPrincipalPolicyPersistence.js +88 -0
- package/data/persistence/principalPolicy/locallyAcknowledgedPrincipalPolicyPersistence.js.map +1 -0
- package/data/persistence/principalPolicy/verifiedPrincipalPolicyBundlePersistence.js +53 -0
- package/data/persistence/principalPolicy/verifiedPrincipalPolicyBundlePersistence.js.map +1 -0
- package/package.json +1 -1
- package/stores/local-projection/localProjectionStore.d.ts +1 -1
- package/stores/local-projection/localProjectionStore.js +1 -1
- package/stores/local-projection/localProjectionStore.js.map +1 -1
- package/stores/local-projection/registry.d.ts +1 -1
- package/stores/local-projection/registry.js.map +1 -1
- package/stores/local-projection/types.d.ts +1 -1
- package/stores/local-projection/types.js.map +1 -1
- package/workflows/blobs/hydration/decrypt.js +1 -2
- package/workflows/blobs/hydration/decrypt.js.map +1 -1
- package/workflows/blobs/hydration/hydrate.d.ts +1 -1
- package/workflows/blobs/hydration/hydrate.js.map +1 -1
- package/workflows/container-contents/container-state/create/createAdoption.js +1 -2
- package/workflows/container-contents/container-state/create/createAdoption.js.map +1 -1
- package/workflows/container-contents/container-state/purge/purgeTreeScope.js +1 -1
- package/workflows/container-contents/container-state/purge/purgeTreeScope.js.map +1 -1
- package/workflows/container-contents/info/documentInfo.js +1 -1
- package/workflows/container-contents/info/documentInfo.js.map +1 -1
- package/workflows/container-contents/metadata/index.js +1 -2
- package/workflows/container-contents/metadata/index.js.map +1 -1
- package/workflows/container-contents/metadata/metadataMutationPreparation.js +1 -1
- package/workflows/container-contents/metadata/metadataMutationPreparation.js.map +1 -1
- package/workflows/container-contents/metadata/metadataPullContinuationInvalidation.js +1 -1
- package/workflows/container-contents/metadata/metadataPullContinuationInvalidation.js.map +1 -1
- package/workflows/container-contents/metadata/metadataSyncErrors.js +1 -1
- package/workflows/container-contents/metadata/metadataSyncErrors.js.map +1 -1
- package/workflows/container-contents/metadata/metadataSyncSettlement.d.ts +1 -1
- package/workflows/container-contents/metadata/metadataSyncSettlement.js.map +1 -1
- package/workflows/container-contents/metadata/metadataTypes.d.ts +1 -1
- package/workflows/container-contents/metadata/metadataTypes.js.map +1 -1
- package/workflows/container-contents/pendingWrites/index.js +1 -1
- package/workflows/container-contents/pendingWrites/index.js.map +1 -1
- package/workflows/container-contents/remoteHydration/destination/verifiedDestination.js +1 -3
- package/workflows/container-contents/remoteHydration/destination/verifiedDestination.js.map +1 -1
- package/workflows/containers/child/create/index.js +1 -1
- package/workflows/containers/child/create/index.js.map +1 -1
- package/workflows/containers/child/move/moveRotation.js +1 -1
- package/workflows/containers/child/move/moveRotation.js.map +1 -1
- package/workflows/containers/child/move/moveWraps.js +1 -1
- package/workflows/containers/child/move/moveWraps.js.map +1 -1
- package/workflows/containers/child/rekey/rekeyProjection.js +1 -1
- package/workflows/containers/child/rekey/rekeyProjection.js.map +1 -1
- package/workflows/containers/child/rotation/rotationContext.js +1 -2
- package/workflows/containers/child/rotation/rotationContext.js.map +1 -1
- package/workflows/containers/child/share/shareAncestorRepair.js +1 -2
- package/workflows/containers/child/share/shareAncestorRepair.js.map +1 -1
- package/workflows/containers/index.d.ts +2 -3
- package/workflows/containers/index.js +1 -2
- package/workflows/containers/index.js.map +1 -1
- package/workflows/containers/writeRefusal.js +1 -1
- package/workflows/containers/writeRefusal.js.map +1 -1
- package/workflows/documents/linkSet/linkSetProjectionSeed.js +1 -1
- package/workflows/documents/linkSet/linkSetProjectionSeed.js.map +1 -1
- package/workflows/documents/syncRekeys/syncAutomaticContainerRekeys.js +1 -1
- package/workflows/documents/syncRekeys/syncAutomaticContainerRekeys.js.map +1 -1
- package/workflows/organizations/groupMetadata/groupMetadataAccess.js +1 -1
- package/workflows/organizations/groupMetadata/groupMetadataAccess.js.map +1 -1
- package/workflows/organizations/groupPolicy/groupPolicyMutationContext.js +2 -2
- package/workflows/organizations/groupPolicy/groupPolicyMutationContext.js.map +1 -1
- package/workflows/organizations/groups/deleteOrganizationGroup.js +1 -1
- package/workflows/organizations/groups/deleteOrganizationGroup.js.map +1 -1
- package/workflows/organizations/groups/organizationGroupDirectory.js +1 -1
- package/workflows/organizations/groups/organizationGroupDirectory.js.map +1 -1
- package/workflows/organizations/provisioning/createOrganization.d.ts +1 -1
- package/workflows/organizations/provisioning/createOrganization.js +1 -1
- package/workflows/organizations/provisioning/createOrganization.js.map +1 -1
- package/workflows/organizations/provisioning/organizationProvisioningAttempt.d.ts +1 -1
- package/workflows/organizations/provisioning/organizationProvisioningAttempt.js.map +1 -1
- package/workflows/organizations/readModel/listLocalOrganizations.js +1 -1
- package/workflows/organizations/readModel/listLocalOrganizations.js.map +1 -1
- package/workflows/principals/externalAdminPolicy.js +1 -1
- package/workflows/principals/externalAdminPolicy.js.map +1 -1
- package/workflows/principals/policyCache/principalPolicyCacheAdvance.js +1 -1
- package/workflows/principals/policyCache/principalPolicyCacheAdvance.js.map +1 -1
- package/workflows/principals/policyCache/principalPolicyOrganizationScope.js +1 -1
- package/workflows/principals/policyCache/principalPolicyOrganizationScope.js.map +1 -1
- package/workflows/registration/index.d.ts +2 -1
- package/workflows/registration/index.js +2 -1
- package/workflows/registration/index.js.map +1 -1
- package/workflows/registration/organizationProvisioning.d.ts +39 -0
- package/workflows/registration/organizationProvisioning.js +211 -0
- package/workflows/registration/organizationProvisioning.js.map +1 -0
- package/workflows/registration/organizationProvisioningArtifacts.d.ts +2 -3
- package/workflows/registration/organizationProvisioningArtifacts.js.map +1 -1
- package/workflows/registration/provisioningBootstrapBuilders.d.ts +3 -3
- package/workflows/registration/provisioningBootstrapBuilders.js +3 -4
- package/workflows/registration/provisioningBootstrapBuilders.js.map +1 -1
- package/workflows/registration/registerIdentity.d.ts +2 -39
- package/workflows/registration/registerIdentity.js +2 -207
- package/workflows/registration/registerIdentity.js.map +1 -1
- package/workflows/registration/registrationPrincipalPolicyPersistence.js +1 -1
- package/workflows/registration/registrationPrincipalPolicyPersistence.js.map +1 -1
- package/workflows/sync/remoteResetPlans.js +2 -2
- package/workflows/sync/remoteResetPlans.js.map +1 -1
- package/data/persistence/documents/internal/constants.d.ts +0 -1
- package/data/persistence/documents/internal/constants.js +0 -2
- package/data/persistence/documents/internal/constants.js.map +0 -1
- package/data/persistence/principalKeys/principalGrantRetirementPersistence.d.ts +0 -9
- package/data/persistence/principalPolicy/principalPolicyCheckpointSelection.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decrypt.js","sourceRoot":"","sources":["decrypt.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n blobContentMetadataHash,\n bytesToHex,\n CONTENT_RECORD_ENCRYPTION_SUITE,\n computeBlobAccessManifestHash,\n computeBlobContentKeyTargetHash,\n computeContentRecordNonceDomainHash,\n makeVerifiedBlobKekTargets,\n verifyWriteHeader,\n} from \"@tearleads/crypto\";\nimport type { BlobKekTargetsResponse } from \"@tearleads/validators/response\";\nimport { deriveBlobContentRecordKey } from \"../../../data/blobs/attachments/crypto\";\nimport { unwrapBlobContentKey } from \"../../../data/blobs/attachments/projection\";\nimport {\n parseBlobEncryptedBytes,\n readBlobKekTarget,\n readDocumentManifestIdentity,\n sortBlobTargets,\n} from \"../../../data/blobs/attachments/readers\";\nimport { assertBlobContentKeyBundleTargetHash } from \"../../../data/blobs/attachments/responses\";\nimport type {\n BlobEncryptedBytesHeader,\n DecryptDocumentAttachmentBlobInput,\n} from \"../../../data/blobs/attachments/types\";\nimport type { BlobBytes } from \"../../../data/blobs/blobContracts\";\nimport { importContentKeyMaterial } from \"../../../data/documents/shared/contentRecordKeys\";\nimport { assertDocumentWriterProjectionConsistent } from \"../../../data/documents/shared/projection\";\nimport {\n asWebCryptoBytes,\n readWriteHeader,\n uniqueSortedStrings,\n} from \"../../../data/documents/shared/readers\";\nimport { projectionVerificationOptions } from \"../../../data/documents/shared/types\";\nimport { readCanonicalRecord } from \"../../../data/keying/canonicalJson\";\nimport type { DocumentWriterProjectionAuthorization } from \"../../../data/keyingProjectionVerification\";\nimport {\n readRecordString,\n requireProjectionUserKeyResolver,\n} from \"../../../data/keyingProjectionVerification\";\nimport { resolveEventContainerPaths } from \"../../../data/keyingProjectionVerification/document/documentDependencyPaths\";\n\nimport { assertAttachmentBindingVerified } from \"../verification/attachmentBindingVerification\";\nimport { assertBlobWrapScopeVerified } from \"../verification/verifiedWrapScope\";\nimport { decryptBlobChunks } from \"./blobChunkDecryption\";\n\nasync function assertBlobEncryptionMetadata(input: {\n readonly contentKeyBundle: DecryptDocumentAttachmentBlobInput[\"binding\"][\"contentKeyBundle\"];\n readonly encrypted: BlobEncryptedBytesHeader;\n readonly expectedBlobId: string;\n readonly organizationId: string;\n}): Promise<void> {\n const { contentKeyBundle, encrypted, expectedBlobId, organizationId } = input;\n if (\n encrypted.blobId !== expectedBlobId ||\n encrypted.contentRecordId !== expectedBlobId\n ) {\n throw new Error(\"Blob encrypted bytes blob id mismatch\");\n }\n if (\n encrypted.contentKeyEpoch !== contentKeyBundle.contentKeyEpoch ||\n contentKeyBundle.blobId !== expectedBlobId\n ) {\n throw new Error(\"Blob encrypted bytes content-key bundle mismatch\");\n }\n await assertBlobContentKeyBundleTargetHash(contentKeyBundle);\n\n const expectedNonceDomainHash = await computeContentRecordNonceDomainHash({\n version: 1,\n organizationId,\n objectKind: \"blob\",\n objectId: expectedBlobId,\n contentKeyEpoch: encrypted.contentKeyEpoch,\n encryptionSuite: CONTENT_RECORD_ENCRYPTION_SUITE,\n contentRecordId: encrypted.contentRecordId,\n });\n if (encrypted.nonceDomainHash !== expectedNonceDomainHash) {\n throw new Error(\"Blob encrypted bytes nonce domain mismatch\");\n }\n const expectedMetadataHash = await blobContentMetadataHash({\n blobId: expectedBlobId,\n byteLength: encrypted.byteLength,\n chunkCount: encrypted.chunkCount,\n chunkSize: encrypted.chunkSize,\n contentKeyEpoch: encrypted.contentKeyEpoch,\n });\n if (encrypted.metadataHash !== expectedMetadataHash) {\n throw new Error(\"Blob encrypted bytes metadata hash mismatch\");\n }\n}\n\nexport type AttachmentBlobDecryptionInput = Omit<\n DecryptDocumentAttachmentBlobInput,\n \"encryptedBytes\"\n> & {\n encrypted: BlobEncryptedBytesHeader;\n};\n\n/** Verifies metadata and authority; the caller must still authenticate ciphertext. */\nexport async function prepareDocumentAttachmentBlobDecryption({\n binding,\n encrypted,\n expectedDocumentId,\n expectedSlotId,\n execSql,\n resolveProjectionUserKey,\n targetSecretKey,\n stillCurrent,\n warmReferencedPrincipalPolicies,\n writerProjection,\n}: AttachmentBlobDecryptionInput): Promise<{\n contentKey: Uint8Array;\n recordKey: CryptoKey;\n organizationId: string;\n}> {\n const requiredResolveProjectionUserKey = requireProjectionUserKeyResolver(\n resolveProjectionUserKey,\n \"Document attachment blob decrypt\",\n );\n const verificationOptions = projectionVerificationOptions({\n execSql,\n stillCurrent,\n warmReferencedPrincipalPolicies,\n resolveProjectionUserKey: requiredResolveProjectionUserKey,\n });\n let documentAuthorization: DocumentWriterProjectionAuthorization | undefined;\n await assertDocumentWriterProjectionConsistent(writerProjection, {\n allowStaleContentKeyBundle: true,\n execSql,\n onVerifiedAuthorization: (authorization) => {\n documentAuthorization = authorization;\n },\n ...verificationOptions,\n });\n const { documentId, organizationId } =\n readDocumentManifestIdentity(writerProjection);\n await assertBlobEncryptionMetadata({\n contentKeyBundle: binding.contentKeyBundle,\n encrypted,\n expectedBlobId: binding.blobId,\n organizationId,\n });\n await assertAttachmentBindingVerified({\n authorization: documentAuthorization,\n binding,\n expectedDocumentId,\n expectedSlotId,\n resolveProjectionUserKey: requiredResolveProjectionUserKey,\n });\n await assertBlobWriteHeaderVerified({\n authorization: documentAuthorization,\n binding,\n encrypted,\n organizationId,\n resolveProjectionUserKey: requiredResolveProjectionUserKey,\n });\n\n if (!documentAuthorization)\n throw new Error(\"Blob document authorization is unavailable\");\n assertBlobWrapScopeVerified({\n authorization: documentAuthorization,\n binding,\n documentId,\n });\n const contentKey = await unwrapBlobContentKey({\n blobId: binding.blobId,\n contentKeyBundle: binding.contentKeyBundle,\n documentId,\n encrypted,\n execSql,\n expectedBindingId: binding.bindingId,\n secretKey: targetSecretKey,\n ...verificationOptions,\n writerProjection,\n });\n const contentKeyMaterial = await importContentKeyMaterial(contentKey);\n const recordKey = await deriveBlobContentRecordKey({\n blobId: binding.blobId,\n contentKeyEpoch: encrypted.contentKeyEpoch,\n contentKeyMaterial,\n contentRecordId: encrypted.contentRecordId,\n organizationId,\n usage: \"decrypt\",\n });\n return { contentKey, recordKey, organizationId };\n}\n\nasync function decryptDocumentAttachmentBlobWithKey(\n input: DecryptDocumentAttachmentBlobInput,\n): Promise<{ bytes: BlobBytes; contentKey: Uint8Array }> {\n const encrypted = parseBlobEncryptedBytes(input.encryptedBytes);\n const context = await prepareDocumentAttachmentBlobDecryption({\n ...input,\n encrypted,\n });\n const header = readWriteHeader(\n input.binding.writeHeader,\n \"Attachment blob write header\",\n );\n const ciphertextHash = bytesToHex(\n new Uint8Array(\n await crypto.subtle.digest(\n \"SHA-256\",\n asWebCryptoBytes(input.encryptedBytes),\n ),\n ),\n );\n if (header.ciphertextHash !== ciphertextHash)\n throw new Error(\"Attachment blob write header does not match ciphertext\");\n const bytes = await decryptBlobChunks({\n ...context,\n encrypted,\n expectedBlobId: input.binding.blobId,\n });\n return { bytes, contentKey: context.contentKey };\n}\n\nexport async function decryptDocumentAttachmentBlob(\n input: DecryptDocumentAttachmentBlobInput,\n): Promise<BlobBytes> {\n return (await decryptDocumentAttachmentBlobWithKey(input)).bytes;\n}\n\nfunction assertStringSetEquals(input: {\n readonly actual: readonly string[];\n readonly expected: readonly string[];\n readonly label: string;\n}): void {\n const actual = uniqueSortedStrings(input.actual);\n const expected = uniqueSortedStrings(input.expected);\n if (\n actual.length !== expected.length ||\n actual.some((value, index) => value !== expected[index])\n ) {\n throw new Error(`${input.label} is inconsistent`);\n }\n}\n\nasync function verifiedBlobKekTargetsForBinding(input: {\n readonly binding: DecryptDocumentAttachmentBlobInput[\"binding\"];\n readonly requireBindingMembership: boolean;\n readonly projection: BlobKekTargetsResponse | undefined;\n}) {\n const { binding, projection } = input;\n if (!projection) {\n throw new Error(\"Attachment binding lacks blob KEK verification material\");\n }\n const targets = sortBlobTargets(\n projection.targets.map((target, index) =>\n readBlobKekTarget(target, `Attachment blob KEK target[${index}]`),\n ),\n );\n const targetHash = await computeBlobContentKeyTargetHash(targets);\n if (\n targetHash !== projection.blobKeyTargetHash ||\n projection.blobId !== binding.blobId\n ) {\n throw new Error(\"Attachment blob KEK target hash is inconsistent\");\n }\n if (\n input.requireBindingMembership &&\n (!projection.activeBindingIds.includes(binding.bindingId) ||\n !targets.some(\n (target) =>\n target.bindingId === binding.bindingId &&\n target.documentId === attachmentBindingDocumentId(binding),\n ))\n ) {\n throw new Error(\"Attachment binding is absent from blob KEK targets\");\n }\n assertStringSetEquals({\n actual: projection.linkedContainerManifestHashes,\n expected: targets.map((target) => target.containerManifestHash),\n label: \"Attachment blob container manifests\",\n });\n assertStringSetEquals({\n actual: projection.linkedContainerKeyEpochIds,\n expected: targets.map((target) => target.containerKeyEpochId),\n label: \"Attachment blob container KEKs\",\n });\n const accessManifestHash = await computeBlobAccessManifestHash({\n version: 1,\n blobId: projection.blobId,\n organizationId: projection.organizationId,\n activeBindingIds: projection.activeBindingIds,\n documentManifestHashes: projection.documentManifestHashes,\n linkedContainerManifestHashes: projection.linkedContainerManifestHashes,\n linkedContainerKeyEpochIds: projection.linkedContainerKeyEpochIds,\n blobKeyTargetHash: projection.blobKeyTargetHash,\n });\n if (accessManifestHash !== projection.blobAccessManifestHash) {\n throw new Error(\"Attachment blob access manifest hash is inconsistent\");\n }\n return makeVerifiedBlobKekTargets({ ...projection, targets });\n}\n\nfunction attachmentBindingDocumentId(\n binding: DecryptDocumentAttachmentBlobInput[\"binding\"],\n): string {\n if (!binding.bindingEvent) {\n throw new Error(\"Attachment binding lacks a signed event\");\n }\n return readRecordString(\n readCanonicalRecord(\n binding.bindingEvent.body,\n \"Attachment binding event body\",\n ),\n \"documentId\",\n \"Attachment binding event body\",\n );\n}\n\nasync function assertBlobWriteHeaderVerified(input: {\n readonly authorization: DocumentWriterProjectionAuthorization | undefined;\n readonly binding: DecryptDocumentAttachmentBlobInput[\"binding\"];\n readonly encrypted: BlobEncryptedBytesHeader;\n readonly organizationId: string;\n readonly resolveProjectionUserKey: ReturnType<\n typeof requireProjectionUserKeyResolver\n >;\n}): Promise<void> {\n if (!input.authorization || !input.binding.writeHeader) {\n throw new Error(\"Attachment blob lacks verified write authority\");\n }\n const header = readWriteHeader(\n input.binding.writeHeader,\n \"Attachment blob write header\",\n );\n const writer = await input.resolveProjectionUserKey(header.writerUserId);\n if (!writer) {\n throw new Error(\"Attachment blob writer identity is unavailable\");\n }\n await verifiedBlobKekTargetsForBinding({\n binding: input.binding,\n projection: input.binding.blobKekTargets,\n requireBindingMembership: true,\n });\n const blobKekTargets = await verifiedBlobKekTargetsForBinding({\n binding: input.binding,\n projection: input.binding.writeAuthorization,\n // The signed binding proves which slot exposes the blob now. Writer\n // authorization describes the older target set committed when the bytes\n // were written, so a later valid binding need not appear in that set.\n requireBindingMembership: false,\n });\n if (\n header.metadataHash !== input.encrypted.metadataHash ||\n header.contentKeyEpoch !== input.encrypted.contentKeyEpoch ||\n header.contentRecordId !== input.encrypted.contentRecordId ||\n header.nonceDomainHash !== input.encrypted.nonceDomainHash\n ) {\n throw new Error(\"Attachment blob write header does not match ciphertext\");\n }\n const verified = await verifyWriteHeader({\n authorizationMembership: \"referenced\",\n blobAuthorization: {\n authorizingContainerPaths: resolveEventContainerPaths({\n containerPathByManifestHash:\n input.authorization.containerPathByManifestHash,\n dependencyManifestHashes: header.dependencyManifestHashes,\n }).dependencyContainerPaths,\n blobKekTargets,\n principalPolicies: input.authorization.principalPolicies,\n },\n expectedAccessManifestHash: blobKekTargets.blobAccessManifestHash,\n expectedObject: {\n objectKind: \"blob\",\n objectId: input.binding.blobId,\n organizationId: input.organizationId,\n },\n expectedTargetHash: blobKekTargets.blobKeyTargetHash,\n header,\n writerPublicKey: writer.signingPublicKey,\n });\n if (!verified.ok) {\n throw verified.error;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"decrypt.js","sourceRoot":"","sources":["decrypt.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n blobContentMetadataHash,\n bytesToHex,\n CONTENT_RECORD_ENCRYPTION_SUITE,\n computeBlobAccessManifestHash,\n computeBlobContentKeyTargetHash,\n computeContentRecordNonceDomainHash,\n makeVerifiedBlobKekTargets,\n verifyWriteHeader,\n} from \"@tearleads/crypto\";\nimport type { BlobKekTargetsResponse } from \"@tearleads/validators/response\";\nimport { deriveBlobContentRecordKey } from \"../../../data/blobs/attachments/crypto\";\nimport { unwrapBlobContentKey } from \"../../../data/blobs/attachments/projection\";\nimport {\n parseBlobEncryptedBytes,\n readBlobKekTarget,\n readDocumentManifestIdentity,\n sortBlobTargets,\n} from \"../../../data/blobs/attachments/readers\";\nimport { assertBlobContentKeyBundleTargetHash } from \"../../../data/blobs/attachments/responses\";\nimport type {\n BlobEncryptedBytesHeader,\n DecryptDocumentAttachmentBlobInput,\n} from \"../../../data/blobs/attachments/types\";\nimport type { BlobBytes } from \"../../../data/blobs/blobContracts\";\nimport { importContentKeyMaterial } from \"../../../data/documents/shared/contentRecordKeys\";\nimport { assertDocumentWriterProjectionConsistent } from \"../../../data/documents/shared/projection\";\nimport {\n asWebCryptoBytes,\n readWriteHeader,\n uniqueSortedStrings,\n} from \"../../../data/documents/shared/readers\";\nimport { projectionVerificationOptions } from \"../../../data/documents/shared/types\";\nimport { readCanonicalRecord } from \"../../../data/keying/canonicalJson\";\nimport type { DocumentWriterProjectionAuthorization } from \"../../../data/keyingProjectionVerification\";\nimport {\n readRecordString,\n requireProjectionUserKeyResolver,\n resolveEventContainerPaths,\n} from \"../../../data/keyingProjectionVerification\";\n\nimport { assertAttachmentBindingVerified } from \"../verification/attachmentBindingVerification\";\nimport { assertBlobWrapScopeVerified } from \"../verification/verifiedWrapScope\";\nimport { decryptBlobChunks } from \"./blobChunkDecryption\";\n\nasync function assertBlobEncryptionMetadata(input: {\n readonly contentKeyBundle: DecryptDocumentAttachmentBlobInput[\"binding\"][\"contentKeyBundle\"];\n readonly encrypted: BlobEncryptedBytesHeader;\n readonly expectedBlobId: string;\n readonly organizationId: string;\n}): Promise<void> {\n const { contentKeyBundle, encrypted, expectedBlobId, organizationId } = input;\n if (\n encrypted.blobId !== expectedBlobId ||\n encrypted.contentRecordId !== expectedBlobId\n ) {\n throw new Error(\"Blob encrypted bytes blob id mismatch\");\n }\n if (\n encrypted.contentKeyEpoch !== contentKeyBundle.contentKeyEpoch ||\n contentKeyBundle.blobId !== expectedBlobId\n ) {\n throw new Error(\"Blob encrypted bytes content-key bundle mismatch\");\n }\n await assertBlobContentKeyBundleTargetHash(contentKeyBundle);\n\n const expectedNonceDomainHash = await computeContentRecordNonceDomainHash({\n version: 1,\n organizationId,\n objectKind: \"blob\",\n objectId: expectedBlobId,\n contentKeyEpoch: encrypted.contentKeyEpoch,\n encryptionSuite: CONTENT_RECORD_ENCRYPTION_SUITE,\n contentRecordId: encrypted.contentRecordId,\n });\n if (encrypted.nonceDomainHash !== expectedNonceDomainHash) {\n throw new Error(\"Blob encrypted bytes nonce domain mismatch\");\n }\n const expectedMetadataHash = await blobContentMetadataHash({\n blobId: expectedBlobId,\n byteLength: encrypted.byteLength,\n chunkCount: encrypted.chunkCount,\n chunkSize: encrypted.chunkSize,\n contentKeyEpoch: encrypted.contentKeyEpoch,\n });\n if (encrypted.metadataHash !== expectedMetadataHash) {\n throw new Error(\"Blob encrypted bytes metadata hash mismatch\");\n }\n}\n\nexport type AttachmentBlobDecryptionInput = Omit<\n DecryptDocumentAttachmentBlobInput,\n \"encryptedBytes\"\n> & {\n encrypted: BlobEncryptedBytesHeader;\n};\n\n/** Verifies metadata and authority; the caller must still authenticate ciphertext. */\nexport async function prepareDocumentAttachmentBlobDecryption({\n binding,\n encrypted,\n expectedDocumentId,\n expectedSlotId,\n execSql,\n resolveProjectionUserKey,\n targetSecretKey,\n stillCurrent,\n warmReferencedPrincipalPolicies,\n writerProjection,\n}: AttachmentBlobDecryptionInput): Promise<{\n contentKey: Uint8Array;\n recordKey: CryptoKey;\n organizationId: string;\n}> {\n const requiredResolveProjectionUserKey = requireProjectionUserKeyResolver(\n resolveProjectionUserKey,\n \"Document attachment blob decrypt\",\n );\n const verificationOptions = projectionVerificationOptions({\n execSql,\n stillCurrent,\n warmReferencedPrincipalPolicies,\n resolveProjectionUserKey: requiredResolveProjectionUserKey,\n });\n let documentAuthorization: DocumentWriterProjectionAuthorization | undefined;\n await assertDocumentWriterProjectionConsistent(writerProjection, {\n allowStaleContentKeyBundle: true,\n execSql,\n onVerifiedAuthorization: (authorization) => {\n documentAuthorization = authorization;\n },\n ...verificationOptions,\n });\n const { documentId, organizationId } =\n readDocumentManifestIdentity(writerProjection);\n await assertBlobEncryptionMetadata({\n contentKeyBundle: binding.contentKeyBundle,\n encrypted,\n expectedBlobId: binding.blobId,\n organizationId,\n });\n await assertAttachmentBindingVerified({\n authorization: documentAuthorization,\n binding,\n expectedDocumentId,\n expectedSlotId,\n resolveProjectionUserKey: requiredResolveProjectionUserKey,\n });\n await assertBlobWriteHeaderVerified({\n authorization: documentAuthorization,\n binding,\n encrypted,\n organizationId,\n resolveProjectionUserKey: requiredResolveProjectionUserKey,\n });\n\n if (!documentAuthorization)\n throw new Error(\"Blob document authorization is unavailable\");\n assertBlobWrapScopeVerified({\n authorization: documentAuthorization,\n binding,\n documentId,\n });\n const contentKey = await unwrapBlobContentKey({\n blobId: binding.blobId,\n contentKeyBundle: binding.contentKeyBundle,\n documentId,\n encrypted,\n execSql,\n expectedBindingId: binding.bindingId,\n secretKey: targetSecretKey,\n ...verificationOptions,\n writerProjection,\n });\n const contentKeyMaterial = await importContentKeyMaterial(contentKey);\n const recordKey = await deriveBlobContentRecordKey({\n blobId: binding.blobId,\n contentKeyEpoch: encrypted.contentKeyEpoch,\n contentKeyMaterial,\n contentRecordId: encrypted.contentRecordId,\n organizationId,\n usage: \"decrypt\",\n });\n return { contentKey, recordKey, organizationId };\n}\n\nasync function decryptDocumentAttachmentBlobWithKey(\n input: DecryptDocumentAttachmentBlobInput,\n): Promise<{ bytes: BlobBytes; contentKey: Uint8Array }> {\n const encrypted = parseBlobEncryptedBytes(input.encryptedBytes);\n const context = await prepareDocumentAttachmentBlobDecryption({\n ...input,\n encrypted,\n });\n const header = readWriteHeader(\n input.binding.writeHeader,\n \"Attachment blob write header\",\n );\n const ciphertextHash = bytesToHex(\n new Uint8Array(\n await crypto.subtle.digest(\n \"SHA-256\",\n asWebCryptoBytes(input.encryptedBytes),\n ),\n ),\n );\n if (header.ciphertextHash !== ciphertextHash)\n throw new Error(\"Attachment blob write header does not match ciphertext\");\n const bytes = await decryptBlobChunks({\n ...context,\n encrypted,\n expectedBlobId: input.binding.blobId,\n });\n return { bytes, contentKey: context.contentKey };\n}\n\nexport async function decryptDocumentAttachmentBlob(\n input: DecryptDocumentAttachmentBlobInput,\n): Promise<BlobBytes> {\n return (await decryptDocumentAttachmentBlobWithKey(input)).bytes;\n}\n\nfunction assertStringSetEquals(input: {\n readonly actual: readonly string[];\n readonly expected: readonly string[];\n readonly label: string;\n}): void {\n const actual = uniqueSortedStrings(input.actual);\n const expected = uniqueSortedStrings(input.expected);\n if (\n actual.length !== expected.length ||\n actual.some((value, index) => value !== expected[index])\n ) {\n throw new Error(`${input.label} is inconsistent`);\n }\n}\n\nasync function verifiedBlobKekTargetsForBinding(input: {\n readonly binding: DecryptDocumentAttachmentBlobInput[\"binding\"];\n readonly requireBindingMembership: boolean;\n readonly projection: BlobKekTargetsResponse | undefined;\n}) {\n const { binding, projection } = input;\n if (!projection) {\n throw new Error(\"Attachment binding lacks blob KEK verification material\");\n }\n const targets = sortBlobTargets(\n projection.targets.map((target, index) =>\n readBlobKekTarget(target, `Attachment blob KEK target[${index}]`),\n ),\n );\n const targetHash = await computeBlobContentKeyTargetHash(targets);\n if (\n targetHash !== projection.blobKeyTargetHash ||\n projection.blobId !== binding.blobId\n ) {\n throw new Error(\"Attachment blob KEK target hash is inconsistent\");\n }\n if (\n input.requireBindingMembership &&\n (!projection.activeBindingIds.includes(binding.bindingId) ||\n !targets.some(\n (target) =>\n target.bindingId === binding.bindingId &&\n target.documentId === attachmentBindingDocumentId(binding),\n ))\n ) {\n throw new Error(\"Attachment binding is absent from blob KEK targets\");\n }\n assertStringSetEquals({\n actual: projection.linkedContainerManifestHashes,\n expected: targets.map((target) => target.containerManifestHash),\n label: \"Attachment blob container manifests\",\n });\n assertStringSetEquals({\n actual: projection.linkedContainerKeyEpochIds,\n expected: targets.map((target) => target.containerKeyEpochId),\n label: \"Attachment blob container KEKs\",\n });\n const accessManifestHash = await computeBlobAccessManifestHash({\n version: 1,\n blobId: projection.blobId,\n organizationId: projection.organizationId,\n activeBindingIds: projection.activeBindingIds,\n documentManifestHashes: projection.documentManifestHashes,\n linkedContainerManifestHashes: projection.linkedContainerManifestHashes,\n linkedContainerKeyEpochIds: projection.linkedContainerKeyEpochIds,\n blobKeyTargetHash: projection.blobKeyTargetHash,\n });\n if (accessManifestHash !== projection.blobAccessManifestHash) {\n throw new Error(\"Attachment blob access manifest hash is inconsistent\");\n }\n return makeVerifiedBlobKekTargets({ ...projection, targets });\n}\n\nfunction attachmentBindingDocumentId(\n binding: DecryptDocumentAttachmentBlobInput[\"binding\"],\n): string {\n if (!binding.bindingEvent) {\n throw new Error(\"Attachment binding lacks a signed event\");\n }\n return readRecordString(\n readCanonicalRecord(\n binding.bindingEvent.body,\n \"Attachment binding event body\",\n ),\n \"documentId\",\n \"Attachment binding event body\",\n );\n}\n\nasync function assertBlobWriteHeaderVerified(input: {\n readonly authorization: DocumentWriterProjectionAuthorization | undefined;\n readonly binding: DecryptDocumentAttachmentBlobInput[\"binding\"];\n readonly encrypted: BlobEncryptedBytesHeader;\n readonly organizationId: string;\n readonly resolveProjectionUserKey: ReturnType<\n typeof requireProjectionUserKeyResolver\n >;\n}): Promise<void> {\n if (!input.authorization || !input.binding.writeHeader) {\n throw new Error(\"Attachment blob lacks verified write authority\");\n }\n const header = readWriteHeader(\n input.binding.writeHeader,\n \"Attachment blob write header\",\n );\n const writer = await input.resolveProjectionUserKey(header.writerUserId);\n if (!writer) {\n throw new Error(\"Attachment blob writer identity is unavailable\");\n }\n await verifiedBlobKekTargetsForBinding({\n binding: input.binding,\n projection: input.binding.blobKekTargets,\n requireBindingMembership: true,\n });\n const blobKekTargets = await verifiedBlobKekTargetsForBinding({\n binding: input.binding,\n projection: input.binding.writeAuthorization,\n // The signed binding proves which slot exposes the blob now. Writer\n // authorization describes the older target set committed when the bytes\n // were written, so a later valid binding need not appear in that set.\n requireBindingMembership: false,\n });\n if (\n header.metadataHash !== input.encrypted.metadataHash ||\n header.contentKeyEpoch !== input.encrypted.contentKeyEpoch ||\n header.contentRecordId !== input.encrypted.contentRecordId ||\n header.nonceDomainHash !== input.encrypted.nonceDomainHash\n ) {\n throw new Error(\"Attachment blob write header does not match ciphertext\");\n }\n const verified = await verifyWriteHeader({\n authorizationMembership: \"referenced\",\n blobAuthorization: {\n authorizingContainerPaths: resolveEventContainerPaths({\n containerPathByManifestHash:\n input.authorization.containerPathByManifestHash,\n dependencyManifestHashes: header.dependencyManifestHashes,\n }).dependencyContainerPaths,\n blobKekTargets,\n principalPolicies: input.authorization.principalPolicies,\n },\n expectedAccessManifestHash: blobKekTargets.blobAccessManifestHash,\n expectedObject: {\n objectKind: \"blob\",\n objectId: input.binding.blobId,\n organizationId: input.organizationId,\n },\n expectedTargetHash: blobKekTargets.blobKeyTargetHash,\n header,\n writerPublicKey: writer.signingPublicKey,\n });\n if (!verified.ok) {\n throw verified.error;\n }\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BlobBytesResponse, RequestResultOptions } from "../../../internal/api-client/index.js";
|
|
2
2
|
import type { BlobAttachmentSummary, DocumentWriterProjectionResponse, ListDocumentAttachmentsResponse } from "../../../internal/validators/response/index.js";
|
|
3
3
|
import type { BlobBytes } from "../../../data/blobs/blobContracts.js";
|
|
4
|
-
import type { DocumentAttachment } from "../../../data/documents/
|
|
4
|
+
import type { DocumentAttachment } from "../../../data/documents/index.js";
|
|
5
5
|
import type { ProjectionUserKeyResolver, ReferencedPrincipalPolicyWarmer } from "../../../data/keyingProjectionVerification/index.js";
|
|
6
6
|
import type { SecurityIncidentReporter } from "../../../data/shared/securityIncidents.js";
|
|
7
7
|
import type { ExecSql } from "../../../data/sqlite/sqlSchema.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hydrate.js","sourceRoot":"","sources":["hydrate.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAO/C,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,mDAAmD,CAAC;AAM5F,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAGjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAyDlE,SAAS,yBAAyB,CAChC,UAA8B,EAC9B,OAA8B;IAE9B,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,UAAU,CAAC,MAAM;QACjB,OAAO,CAAC,SAAS;QACjB,UAAU,CAAC,aAAa;KACzB,CAAC,CAAC;AACL,CAAC;AAiBD,KAAK,UAAU,qBAAqB,CAClC,IAAuB,EACvB,cAAuC;IAEvC,MAAM,UAAU,GAAG,IAAI,UAAU,CAC/B,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CACtD,CAAC;IAEF,OAAO,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC;AAChD,CAAC;AAED,SAAS,uBAAuB,CAAC,KAUhC;IACC,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;IACvC,MAAM,eAAe,GAAG,KAAK,CAAC,uBAAuB,EAAE,CAAC,MAAM,CAAC,CAAC;IAChE,wEAAwE;IACxE,yEAAyE;IACzE,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAC/B,yBAAyB,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAC3D,EACD,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,mBAAmB,EAAE,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,WAAW,KAAK,IAAI,IAAI,WAAW,KAAK,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;AACtE,CAAC;AAED,KAAK,UAAU,0BAA0B,CACvC,KAIC;IAED,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;IAC1C,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,WAAW,CAAC;IACjD,IAAI,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAClD,KAAK,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE;QACxD,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,cAAuC,CAAC;IAC5C,IAAI,CAAC;QACH,cAAc,GAAG,MAAM,sBAAsB,CAC3C,IAAI,EACJ,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,QAAQ,CACf,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,EAAE,CACH,GAAG,SAAS,UAAU,OAAO,CAAC,MAAM,uCAAuC,YAAY,CACrF,KAAK,CACN,EAAE,CACJ,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,cAAc,CAAC,UAAU,KAAK,IAAI,CAAC,UAAU,EAAE,CAAC;QAClD,GAAG,EAAE,CACH,GAAG,SAAS,UAAU,OAAO,CAAC,MAAM,yCAAyC,CAC9E,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,CAAC,MAAM,qBAAqB,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QACzD,GAAG,EAAE,CACH,GAAG,SAAS,UAAU,OAAO,CAAC,MAAM,oCAAoC,CACzE,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IAC9B,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,cAAc;KACf,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,iCAAiC,CAC9C,KAEC;IAED,MAAM,eAAe,GAAG,IAAI,GAAG,EAA6C,CAAC;IAC7E,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAC5C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAChD,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,uBAAuB,CAAC;QAChD,GAAG,KAAK;QACR,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC3D,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM;YAC7B,GAAG,EAAE,GAAG,EAAE,CACR,eAAe,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,CACpD,0BAA0B,CAAC;gBACzB,GAAG,KAAK;gBACR,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,MAAM;gBACN,QAAQ;aACT,CAAC,CACH;SACJ,CAAC,CAAC;KACJ,CAAC,CAAC;IAEH,OAAO,IAAI,GAAG,CACZ,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAC7B,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CACjD,CACF,CAAC;AACJ,CAAC;AAED,SAAS,sCAAsC,CAC7C,gBAAkE,EAClE,mBAA2E;IAE3E,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;QACzC,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC9D,OAAO,MAAM;YACX,CAAC,CAAC;gBACE;oBACE,GAAG,MAAM;oBACT,cAAc,EAAE,MAAM,CAAC,cAAc;iBACtC;aACF;YACH,CAAC,CAAC,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,mCAAmC,CAChD,KAIC;IAED,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC;QAC1C,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO;QAC7B,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc;QAC3C,kBAAkB,EAAE,KAAK,CAAC,UAAU;QACpC,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM;QAC9C,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,wBAAwB,EAAE,KAAK,CAAC,wBAAwB;QACxD,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,+BAA+B,EAAE,KAAK,CAAC,+BAA+B;QACtE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;KACzC,CAAC,CAAC;IAEH,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC;IAC7C,MAAM,aAAa,GAAG,MAAM,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAC3D,MAAM,cAAc,GAAG,aAAa,KAAK,UAAU,CAAC,aAAa,CAAC;IAClE,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,WAAW,CAAC;IACjD,IAAI,cAAc,IAAI,KAAK,CAAC,uBAAuB,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACzE,qEAAqE;QACrE,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAC/B,yBAAyB,CAAC,UAAU,EAAE,OAAO,CAAC,CAC/C,CAAC;QACF,KAAK,CAAC,GAAG,EAAE,CACT,GAAG,SAAS,4FAA4F,CACzG,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,cAAc,EAAE,CAAC;QACnB,KAAK,CAAC,GAAG,EAAE,CACT,GAAG,SAAS,2FAA2F,CACxG,CAAC;IACJ,CAAC;IACD,OAAO;QACL,UAAU;QACV,OAAO;QACP,KAAK;QACL,aAAa;QACb,cAAc;QACd,UAAU,EAAE,QAAQ,OAAO,CAAC,MAAM,EAAE;KACrC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,KAIC;IAED,MAAM,kBAAkB,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,uBAAuB,CACtE,KAAK,CAAC,UAAU,CACjB,CAAC;IACF,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,eAAe,GAAG,IAAI,GAAG,CAC7B,kBAAkB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAC/D,CAAC;IACF,MAAM,gBAAgB,GACpB,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE;QACvC,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACvD,OAAO,OAAO;YACZ,uBAAuB,CAAC;gBACtB,UAAU;gBACV,OAAO;gBACP,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;gBAC9C,uBAAuB,EAAE,KAAK,CAAC,uBAAuB;gBACtD,sBAAsB,EAAE,KAAK,CAAC,sBAAsB;aACrD,CAAC;YACF,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;YAC3B,CAAC,CAAC,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;IACL,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,iBAAiB,EAAE,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,WAAW,GAAG,sCAAsC,CACxD,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;QACtC,GAAG,KAAK;QACR,gBAAgB;KACjB,CAAC,CACH,CAAC;IACF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,gBAAgB,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,2BAA2B,CACxE,KAAK,CAAC,UAAU,CACjB,CAAC;IACF,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,WAAW,CAAC;QACjD,KAAK,CAAC,GAAG,EAAE,CACT,GAAG,SAAS,6CAA6C,KAAK,CAAC,UAAU,+BAA+B,CACzG,CAAC;QACF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,iBAAiB,GAAG,yBAAyB,CACjD,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,UAAU,CACjB,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAAC;QAC7C,GAAG,KAAK;QACR,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAClC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM;YAC7B,GAAG,EAAE,GAAG,EAAE,CACR,mCAAmC,CAAC;gBAClC,GAAG,KAAK;gBACR,iBAAiB;gBACjB,MAAM;gBACN,gBAAgB;aACjB,CAAC;SACL,CAAC,CAAC;KACJ,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAC9B,CAAC,IAAI,EAA0C,EAAE,CAAC,IAAI,KAAK,IAAI,CAChE,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,KAAK,gBAAgB,CAAC,MAAM;QAAE,KAAK,CAAC,iBAAiB,EAAE,EAAE,CAAC;IAC7E,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["import type {\n BlobBytesResponse,\n RequestResultOptions,\n} from \"@tearleads/api-client\";\nimport { bytesToHex } from \"@tearleads/crypto\";\nimport type {\n BlobAttachmentSummary,\n DocumentWriterProjectionResponse,\n ListDocumentAttachmentsResponse,\n} from \"@tearleads/validators/response\";\nimport type { BlobBytes } from \"../../../data/blobs/blobContracts\";\nimport { runBlobTransfer } from \"../../../data/blobs/transferPool\";\nimport { attachmentContentSha256 } from \"../../../data/documents/attachmentContentIdentity\";\nimport type { DocumentAttachment } from \"../../../data/documents/documentContent\";\nimport type {\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../../../data/keyingProjectionVerification\";\nimport { errorMessage } from \"../../../data/shared/errorMessage\";\nimport type { SecurityIncidentReporter } from \"../../../data/shared/securityIncidents\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { createAttachmentDecryptor } from \"../verification/attachmentDecryptor\";\nimport { collectHydrationResults } from \"./hydrationResults\";\nimport { readAttachmentTransfer } from \"./readAttachmentTransfer\";\n\ninterface DocumentAttachmentHydrationApi {\n evictDocumentWriterProjection?(documentId: string): void;\n getBlobBytes(\n blobId: string,\n options?: RequestResultOptions,\n ): Promise<BlobBytesResponse | null>;\n getDocumentWriterProjection(\n documentId: string,\n ): Promise<DocumentWriterProjectionResponse | null>;\n listDocumentAttachments(\n documentId: string,\n ): Promise<ListDocumentAttachmentsResponse | null>;\n}\n\ninterface HydratedDocumentAttachmentBlob {\n attachment: DocumentAttachment;\n binding: BlobAttachmentSummary;\n bytes: BlobBytes;\n /** Plaintext digest of `bytes`. */\n contentSha256: string;\n /**\n * The served binding is validly signed but its plaintext digest differs\n * from the digest the document content records for the slot. Set only when\n * the slot held no local bytes: the bind and the content update carrying\n * its digest are two server writes, and an uploader lost between them must\n * not leave the attachment permanently invisible. Never a security incident;\n * an honest crash window is indistinguishable from a served rollback.\n */\n intentMismatch: boolean;\n storageKey: string;\n}\n\ninterface DocumentAttachmentHydrationContext {\n apiClient: DocumentAttachmentHydrationApi;\n documentId: string;\n execSql: ExecSql;\n localBlobIdBySlotId?:\n | Readonly<Record<string, string | null | undefined>>\n | undefined;\n localStorageKeyBySlotId?: Readonly<Record<string, string | undefined>>;\n log?: ((message: string) => void) | undefined;\n logPrefix?: string | undefined;\n /**\n * Served bindings already downloaded and refused for a held copy, keyed per\n * (slot, binding, intent). Populated here; a later pass skips the download\n * until the binding or the document intent changes.\n */\n rejectedServedBindings?: Set<string> | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n reportSecurityIncident?: SecurityIncidentReporter | undefined;\n targetSecretKey: Uint8Array;\n stillCurrent?: (() => boolean) | undefined;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n}\n\nfunction servedBindingRejectionKey(\n attachment: DocumentAttachment,\n binding: BlobAttachmentSummary,\n): string {\n return JSON.stringify([\n attachment.slotId,\n binding.bindingId,\n attachment.contentSha256,\n ]);\n}\n\ninterface DocumentAttachmentHydrationTarget {\n attachment: DocumentAttachment;\n binding: BlobAttachmentSummary;\n}\n\ninterface LoadedDocumentAttachmentBlob\n extends DocumentAttachmentHydrationTarget {\n encryptedBytes: Uint8Array<ArrayBuffer>;\n}\n\ninterface LoadedDocumentAttachmentBlobBytes {\n blobId: string;\n encryptedBytes: Uint8Array<ArrayBuffer>;\n}\n\nasync function hasExpectedBlobSha256(\n blob: BlobBytesResponse,\n encryptedBytes: Uint8Array<ArrayBuffer>,\n): Promise<boolean> {\n const blobDigest = new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", encryptedBytes),\n );\n\n return bytesToHex(blobDigest) === blob.sha256;\n}\n\nfunction shouldHydrateAttachment(input: {\n attachment: DocumentAttachment;\n binding: BlobAttachmentSummary;\n localBlobIdBySlotId:\n | Readonly<Record<string, string | null | undefined>>\n | undefined;\n localStorageKeyBySlotId:\n | Readonly<Record<string, string | undefined>>\n | undefined;\n rejectedServedBindings: ReadonlySet<string> | undefined;\n}): boolean {\n const slotId = input.attachment.slotId;\n const localStorageKey = input.localStorageKeyBySlotId?.[slotId];\n // An empty slot always hydrates: a rejection only ever protected a held\n // copy, so once that copy is gone the served binding is shown (flagged).\n if (!localStorageKey) {\n return true;\n }\n if (\n input.rejectedServedBindings?.has(\n servedBindingRejectionKey(input.attachment, input.binding),\n )\n ) {\n return false;\n }\n\n const localBlobId = input.localBlobIdBySlotId?.[slotId];\n return localBlobId !== null && localBlobId !== input.binding.blobId;\n}\n\nasync function loadDocumentAttachmentBlob(\n input: DocumentAttachmentHydrationContext & {\n binding: BlobAttachmentSummary;\n signal: AbortSignal;\n progress: () => void;\n },\n): Promise<LoadedDocumentAttachmentBlobBytes | null> {\n const { apiClient, binding, log } = input;\n const logPrefix = input.logPrefix ?? \"Documents\";\n if (input.stillCurrent?.() === false) return null;\n input.signal.throwIfAborted();\n const blob = await apiClient.getBlobBytes(binding.blobId, {\n signal: input.signal,\n });\n if (!blob) {\n return null;\n }\n\n let encryptedBytes: Uint8Array<ArrayBuffer>;\n try {\n encryptedBytes = await readAttachmentTransfer(\n blob,\n input.signal,\n input.progress,\n );\n } catch (error) {\n log?.(\n `${logPrefix}: blob ${binding.blobId} failed to stream during hydration: ${errorMessage(\n error,\n )}`,\n );\n return null;\n }\n\n if (encryptedBytes.byteLength !== blob.byteLength) {\n log?.(\n `${logPrefix}: blob ${binding.blobId} byte length mismatch during hydration.`,\n );\n return null;\n }\n\n if (!(await hasExpectedBlobSha256(blob, encryptedBytes))) {\n log?.(\n `${logPrefix}: blob ${binding.blobId} sha256 mismatch during hydration.`,\n );\n return null;\n }\n\n input.signal.throwIfAborted();\n return {\n blobId: binding.blobId,\n encryptedBytes,\n };\n}\n\nasync function loadUniqueDocumentAttachmentBlobs(\n input: DocumentAttachmentHydrationContext & {\n hydrationTargets: ReadonlyArray<DocumentAttachmentHydrationTarget>;\n },\n): Promise<ReadonlyMap<string, LoadedDocumentAttachmentBlobBytes>> {\n const targetsByBlobId = new Map<string, DocumentAttachmentHydrationTarget>();\n for (const target of input.hydrationTargets) {\n if (!targetsByBlobId.has(target.binding.blobId)) {\n targetsByBlobId.set(target.binding.blobId, target);\n }\n }\n\n const loadedBlobs = await collectHydrationResults({\n ...input,\n tasks: Array.from(targetsByBlobId.values()).map((target) => ({\n blobId: target.binding.blobId,\n run: () =>\n runBlobTransfer(input.apiClient, (signal, progress) =>\n loadDocumentAttachmentBlob({\n ...input,\n binding: target.binding,\n signal,\n progress,\n }),\n ),\n })),\n });\n\n return new Map(\n loadedBlobs.flatMap((loaded) =>\n loaded ? [[loaded.blobId, loaded] as const] : [],\n ),\n );\n}\n\nfunction applyLoadedBlobBytesToHydrationTargets(\n hydrationTargets: ReadonlyArray<DocumentAttachmentHydrationTarget>,\n loadedBlobsByBlobId: ReadonlyMap<string, LoadedDocumentAttachmentBlobBytes>,\n): LoadedDocumentAttachmentBlob[] {\n return hydrationTargets.flatMap((target) => {\n const loaded = loadedBlobsByBlobId.get(target.binding.blobId);\n return loaded\n ? [\n {\n ...target,\n encryptedBytes: loaded.encryptedBytes,\n },\n ]\n : [];\n });\n}\n\nasync function decryptLoadedDocumentAttachmentBlob(\n input: DocumentAttachmentHydrationContext & {\n loaded: LoadedDocumentAttachmentBlob;\n decryptAttachment: ReturnType<typeof createAttachmentDecryptor>;\n writerProjection: DocumentWriterProjectionResponse;\n },\n): Promise<HydratedDocumentAttachmentBlob | null> {\n const bytes = await input.decryptAttachment({\n binding: input.loaded.binding,\n encryptedBytes: input.loaded.encryptedBytes,\n expectedDocumentId: input.documentId,\n expectedSlotId: input.loaded.attachment.slotId,\n execSql: input.execSql,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n targetSecretKey: input.targetSecretKey,\n stillCurrent: input.stillCurrent,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n writerProjection: input.writerProjection,\n });\n\n const { attachment, binding } = input.loaded;\n const contentSha256 = await attachmentContentSha256(bytes);\n const intentMismatch = contentSha256 !== attachment.contentSha256;\n const logPrefix = input.logPrefix ?? \"Documents\";\n if (intentMismatch && input.localStorageKeyBySlotId?.[attachment.slotId]) {\n // Never replace a held copy with bytes the document does not record.\n input.rejectedServedBindings?.add(\n servedBindingRejectionKey(attachment, binding),\n );\n input.log?.(\n `${logPrefix}: served attachment bytes differ from the current document content; keeping the held copy.`,\n );\n return null;\n }\n if (intentMismatch) {\n input.log?.(\n `${logPrefix}: served attachment bytes differ from the current document content; showing them flagged.`,\n );\n }\n return {\n attachment,\n binding,\n bytes,\n contentSha256,\n intentMismatch,\n storageKey: `blob-${binding.blobId}`,\n };\n}\n\nexport async function hydrateDocumentAttachmentBlobs(\n input: DocumentAttachmentHydrationContext & {\n attachments: ReadonlyArray<DocumentAttachment>;\n /** No binding target remains unchecked or unavailable in this pass. */\n onBindingsChecked?: () => void;\n },\n): Promise<HydratedDocumentAttachmentBlob[] | null> {\n const attachmentBindings = await input.apiClient.listDocumentAttachments(\n input.documentId,\n );\n if (!attachmentBindings) {\n return null;\n }\n\n const bindingBySlotId = new Map(\n attachmentBindings.map((binding) => [binding.slotId, binding]),\n );\n const hydrationTargets: DocumentAttachmentHydrationTarget[] =\n input.attachments.flatMap((attachment) => {\n const binding = bindingBySlotId.get(attachment.slotId);\n return binding &&\n shouldHydrateAttachment({\n attachment,\n binding,\n localBlobIdBySlotId: input.localBlobIdBySlotId,\n localStorageKeyBySlotId: input.localStorageKeyBySlotId,\n rejectedServedBindings: input.rejectedServedBindings,\n })\n ? [{ attachment, binding }]\n : [];\n });\n if (hydrationTargets.length === 0) {\n input.onBindingsChecked?.();\n return [];\n }\n\n const loadedBlobs = applyLoadedBlobBytesToHydrationTargets(\n hydrationTargets,\n await loadUniqueDocumentAttachmentBlobs({\n ...input,\n hydrationTargets,\n }),\n );\n if (loadedBlobs.length === 0) {\n return [];\n }\n\n const writerProjection = await input.apiClient.getDocumentWriterProjection(\n input.documentId,\n );\n if (!writerProjection) {\n const logPrefix = input.logPrefix ?? \"Documents\";\n input.log?.(\n `${logPrefix}: cannot hydrate attachments for document ${input.documentId} without a writer projection.`,\n );\n return [];\n }\n\n const decryptAttachment = createAttachmentDecryptor(\n input.apiClient,\n input.documentId,\n );\n const hydrated = await collectHydrationResults({\n ...input,\n tasks: loadedBlobs.map((loaded) => ({\n blobId: loaded.binding.blobId,\n run: () =>\n decryptLoadedDocumentAttachmentBlob({\n ...input,\n decryptAttachment,\n loaded,\n writerProjection,\n }),\n })),\n });\n const complete = hydrated.filter(\n (item): item is HydratedDocumentAttachmentBlob => item !== null,\n );\n if (complete.length === hydrationTargets.length) input.onBindingsChecked?.();\n return complete;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"hydrate.js","sourceRoot":"","sources":["hydrate.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n BlobBytesResponse,\n RequestResultOptions,\n} from \"@tearleads/api-client\";\nimport { bytesToHex } from \"@tearleads/crypto\";\nimport type {\n BlobAttachmentSummary,\n DocumentWriterProjectionResponse,\n ListDocumentAttachmentsResponse,\n} from \"@tearleads/validators/response\";\nimport type { BlobBytes } from \"../../../data/blobs/blobContracts\";\nimport { runBlobTransfer } from \"../../../data/blobs/transferPool\";\nimport type { DocumentAttachment } from \"../../../data/documents\";\nimport { attachmentContentSha256 } from \"../../../data/documents/attachmentContentIdentity\";\nimport type {\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../../../data/keyingProjectionVerification\";\nimport { errorMessage } from \"../../../data/shared/errorMessage\";\nimport type { SecurityIncidentReporter } from \"../../../data/shared/securityIncidents\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { createAttachmentDecryptor } from \"../verification/attachmentDecryptor\";\nimport { collectHydrationResults } from \"./hydrationResults\";\nimport { readAttachmentTransfer } from \"./readAttachmentTransfer\";\n\ninterface DocumentAttachmentHydrationApi {\n evictDocumentWriterProjection?(documentId: string): void;\n getBlobBytes(\n blobId: string,\n options?: RequestResultOptions,\n ): Promise<BlobBytesResponse | null>;\n getDocumentWriterProjection(\n documentId: string,\n ): Promise<DocumentWriterProjectionResponse | null>;\n listDocumentAttachments(\n documentId: string,\n ): Promise<ListDocumentAttachmentsResponse | null>;\n}\n\ninterface HydratedDocumentAttachmentBlob {\n attachment: DocumentAttachment;\n binding: BlobAttachmentSummary;\n bytes: BlobBytes;\n /** Plaintext digest of `bytes`. */\n contentSha256: string;\n /**\n * The served binding is validly signed but its plaintext digest differs\n * from the digest the document content records for the slot. Set only when\n * the slot held no local bytes: the bind and the content update carrying\n * its digest are two server writes, and an uploader lost between them must\n * not leave the attachment permanently invisible. Never a security incident;\n * an honest crash window is indistinguishable from a served rollback.\n */\n intentMismatch: boolean;\n storageKey: string;\n}\n\ninterface DocumentAttachmentHydrationContext {\n apiClient: DocumentAttachmentHydrationApi;\n documentId: string;\n execSql: ExecSql;\n localBlobIdBySlotId?:\n | Readonly<Record<string, string | null | undefined>>\n | undefined;\n localStorageKeyBySlotId?: Readonly<Record<string, string | undefined>>;\n log?: ((message: string) => void) | undefined;\n logPrefix?: string | undefined;\n /**\n * Served bindings already downloaded and refused for a held copy, keyed per\n * (slot, binding, intent). Populated here; a later pass skips the download\n * until the binding or the document intent changes.\n */\n rejectedServedBindings?: Set<string> | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n reportSecurityIncident?: SecurityIncidentReporter | undefined;\n targetSecretKey: Uint8Array;\n stillCurrent?: (() => boolean) | undefined;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n}\n\nfunction servedBindingRejectionKey(\n attachment: DocumentAttachment,\n binding: BlobAttachmentSummary,\n): string {\n return JSON.stringify([\n attachment.slotId,\n binding.bindingId,\n attachment.contentSha256,\n ]);\n}\n\ninterface DocumentAttachmentHydrationTarget {\n attachment: DocumentAttachment;\n binding: BlobAttachmentSummary;\n}\n\ninterface LoadedDocumentAttachmentBlob\n extends DocumentAttachmentHydrationTarget {\n encryptedBytes: Uint8Array<ArrayBuffer>;\n}\n\ninterface LoadedDocumentAttachmentBlobBytes {\n blobId: string;\n encryptedBytes: Uint8Array<ArrayBuffer>;\n}\n\nasync function hasExpectedBlobSha256(\n blob: BlobBytesResponse,\n encryptedBytes: Uint8Array<ArrayBuffer>,\n): Promise<boolean> {\n const blobDigest = new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", encryptedBytes),\n );\n\n return bytesToHex(blobDigest) === blob.sha256;\n}\n\nfunction shouldHydrateAttachment(input: {\n attachment: DocumentAttachment;\n binding: BlobAttachmentSummary;\n localBlobIdBySlotId:\n | Readonly<Record<string, string | null | undefined>>\n | undefined;\n localStorageKeyBySlotId:\n | Readonly<Record<string, string | undefined>>\n | undefined;\n rejectedServedBindings: ReadonlySet<string> | undefined;\n}): boolean {\n const slotId = input.attachment.slotId;\n const localStorageKey = input.localStorageKeyBySlotId?.[slotId];\n // An empty slot always hydrates: a rejection only ever protected a held\n // copy, so once that copy is gone the served binding is shown (flagged).\n if (!localStorageKey) {\n return true;\n }\n if (\n input.rejectedServedBindings?.has(\n servedBindingRejectionKey(input.attachment, input.binding),\n )\n ) {\n return false;\n }\n\n const localBlobId = input.localBlobIdBySlotId?.[slotId];\n return localBlobId !== null && localBlobId !== input.binding.blobId;\n}\n\nasync function loadDocumentAttachmentBlob(\n input: DocumentAttachmentHydrationContext & {\n binding: BlobAttachmentSummary;\n signal: AbortSignal;\n progress: () => void;\n },\n): Promise<LoadedDocumentAttachmentBlobBytes | null> {\n const { apiClient, binding, log } = input;\n const logPrefix = input.logPrefix ?? \"Documents\";\n if (input.stillCurrent?.() === false) return null;\n input.signal.throwIfAborted();\n const blob = await apiClient.getBlobBytes(binding.blobId, {\n signal: input.signal,\n });\n if (!blob) {\n return null;\n }\n\n let encryptedBytes: Uint8Array<ArrayBuffer>;\n try {\n encryptedBytes = await readAttachmentTransfer(\n blob,\n input.signal,\n input.progress,\n );\n } catch (error) {\n log?.(\n `${logPrefix}: blob ${binding.blobId} failed to stream during hydration: ${errorMessage(\n error,\n )}`,\n );\n return null;\n }\n\n if (encryptedBytes.byteLength !== blob.byteLength) {\n log?.(\n `${logPrefix}: blob ${binding.blobId} byte length mismatch during hydration.`,\n );\n return null;\n }\n\n if (!(await hasExpectedBlobSha256(blob, encryptedBytes))) {\n log?.(\n `${logPrefix}: blob ${binding.blobId} sha256 mismatch during hydration.`,\n );\n return null;\n }\n\n input.signal.throwIfAborted();\n return {\n blobId: binding.blobId,\n encryptedBytes,\n };\n}\n\nasync function loadUniqueDocumentAttachmentBlobs(\n input: DocumentAttachmentHydrationContext & {\n hydrationTargets: ReadonlyArray<DocumentAttachmentHydrationTarget>;\n },\n): Promise<ReadonlyMap<string, LoadedDocumentAttachmentBlobBytes>> {\n const targetsByBlobId = new Map<string, DocumentAttachmentHydrationTarget>();\n for (const target of input.hydrationTargets) {\n if (!targetsByBlobId.has(target.binding.blobId)) {\n targetsByBlobId.set(target.binding.blobId, target);\n }\n }\n\n const loadedBlobs = await collectHydrationResults({\n ...input,\n tasks: Array.from(targetsByBlobId.values()).map((target) => ({\n blobId: target.binding.blobId,\n run: () =>\n runBlobTransfer(input.apiClient, (signal, progress) =>\n loadDocumentAttachmentBlob({\n ...input,\n binding: target.binding,\n signal,\n progress,\n }),\n ),\n })),\n });\n\n return new Map(\n loadedBlobs.flatMap((loaded) =>\n loaded ? [[loaded.blobId, loaded] as const] : [],\n ),\n );\n}\n\nfunction applyLoadedBlobBytesToHydrationTargets(\n hydrationTargets: ReadonlyArray<DocumentAttachmentHydrationTarget>,\n loadedBlobsByBlobId: ReadonlyMap<string, LoadedDocumentAttachmentBlobBytes>,\n): LoadedDocumentAttachmentBlob[] {\n return hydrationTargets.flatMap((target) => {\n const loaded = loadedBlobsByBlobId.get(target.binding.blobId);\n return loaded\n ? [\n {\n ...target,\n encryptedBytes: loaded.encryptedBytes,\n },\n ]\n : [];\n });\n}\n\nasync function decryptLoadedDocumentAttachmentBlob(\n input: DocumentAttachmentHydrationContext & {\n loaded: LoadedDocumentAttachmentBlob;\n decryptAttachment: ReturnType<typeof createAttachmentDecryptor>;\n writerProjection: DocumentWriterProjectionResponse;\n },\n): Promise<HydratedDocumentAttachmentBlob | null> {\n const bytes = await input.decryptAttachment({\n binding: input.loaded.binding,\n encryptedBytes: input.loaded.encryptedBytes,\n expectedDocumentId: input.documentId,\n expectedSlotId: input.loaded.attachment.slotId,\n execSql: input.execSql,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n targetSecretKey: input.targetSecretKey,\n stillCurrent: input.stillCurrent,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n writerProjection: input.writerProjection,\n });\n\n const { attachment, binding } = input.loaded;\n const contentSha256 = await attachmentContentSha256(bytes);\n const intentMismatch = contentSha256 !== attachment.contentSha256;\n const logPrefix = input.logPrefix ?? \"Documents\";\n if (intentMismatch && input.localStorageKeyBySlotId?.[attachment.slotId]) {\n // Never replace a held copy with bytes the document does not record.\n input.rejectedServedBindings?.add(\n servedBindingRejectionKey(attachment, binding),\n );\n input.log?.(\n `${logPrefix}: served attachment bytes differ from the current document content; keeping the held copy.`,\n );\n return null;\n }\n if (intentMismatch) {\n input.log?.(\n `${logPrefix}: served attachment bytes differ from the current document content; showing them flagged.`,\n );\n }\n return {\n attachment,\n binding,\n bytes,\n contentSha256,\n intentMismatch,\n storageKey: `blob-${binding.blobId}`,\n };\n}\n\nexport async function hydrateDocumentAttachmentBlobs(\n input: DocumentAttachmentHydrationContext & {\n attachments: ReadonlyArray<DocumentAttachment>;\n /** No binding target remains unchecked or unavailable in this pass. */\n onBindingsChecked?: () => void;\n },\n): Promise<HydratedDocumentAttachmentBlob[] | null> {\n const attachmentBindings = await input.apiClient.listDocumentAttachments(\n input.documentId,\n );\n if (!attachmentBindings) {\n return null;\n }\n\n const bindingBySlotId = new Map(\n attachmentBindings.map((binding) => [binding.slotId, binding]),\n );\n const hydrationTargets: DocumentAttachmentHydrationTarget[] =\n input.attachments.flatMap((attachment) => {\n const binding = bindingBySlotId.get(attachment.slotId);\n return binding &&\n shouldHydrateAttachment({\n attachment,\n binding,\n localBlobIdBySlotId: input.localBlobIdBySlotId,\n localStorageKeyBySlotId: input.localStorageKeyBySlotId,\n rejectedServedBindings: input.rejectedServedBindings,\n })\n ? [{ attachment, binding }]\n : [];\n });\n if (hydrationTargets.length === 0) {\n input.onBindingsChecked?.();\n return [];\n }\n\n const loadedBlobs = applyLoadedBlobBytesToHydrationTargets(\n hydrationTargets,\n await loadUniqueDocumentAttachmentBlobs({\n ...input,\n hydrationTargets,\n }),\n );\n if (loadedBlobs.length === 0) {\n return [];\n }\n\n const writerProjection = await input.apiClient.getDocumentWriterProjection(\n input.documentId,\n );\n if (!writerProjection) {\n const logPrefix = input.logPrefix ?? \"Documents\";\n input.log?.(\n `${logPrefix}: cannot hydrate attachments for document ${input.documentId} without a writer projection.`,\n );\n return [];\n }\n\n const decryptAttachment = createAttachmentDecryptor(\n input.apiClient,\n input.documentId,\n );\n const hydrated = await collectHydrationResults({\n ...input,\n tasks: loadedBlobs.map((loaded) => ({\n blobId: loaded.binding.blobId,\n run: () =>\n decryptLoadedDocumentAttachmentBlob({\n ...input,\n decryptAttachment,\n loaded,\n writerProjection,\n }),\n })),\n });\n const complete = hydrated.filter(\n (item): item is HydratedDocumentAttachmentBlob => item !== null,\n );\n if (complete.length === hydrationTargets.length) input.onBindingsChecked?.();\n return complete;\n}\n"]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { KeyingVerificationError, } from "../../../../internal/crypto/index.js";
|
|
2
|
-
import { verifiedContainerCreateManifest } from "../../../../data/keyingProjectionVerification/
|
|
3
|
-
import { verifyContainerDestinationProjection } from "../../../../data/keyingProjectionVerification/container/containerDestinationVerification.js";
|
|
2
|
+
import { verifiedContainerCreateManifest, verifyContainerDestinationProjection, } from "../../../../data/keyingProjectionVerification/index.js";
|
|
4
3
|
import { isKeyingVerificationError, reportKeyingVerificationErrorInCauseChain, } from "../../../../data/keyingProjectionVerification/error.js";
|
|
5
4
|
import { errorMessage } from "../../../../data/shared/errorMessage.js";
|
|
6
5
|
import { createRuntimePrincipalPolicyWarmer } from "../../../principals/runtimePolicyWarmer.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createAdoption.js","sourceRoot":"","sources":["createAdoption.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type KeyingVerificationCode,\n KeyingVerificationError,\n} from \"@tearleads/crypto\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport { verifiedContainerCreateManifest } from \"../../../../data/keyingProjectionVerification/container/containerCreateManifest\";\nimport { verifyContainerDestinationProjection } from \"../../../../data/keyingProjectionVerification/container/containerDestinationVerification\";\nimport {\n isKeyingVerificationError,\n reportKeyingVerificationErrorInCauseChain,\n} from \"../../../../data/keyingProjectionVerification/error\";\nimport { errorMessage } from \"../../../../data/shared/errorMessage\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../../principals/runtimePolicyWarmer\";\nimport type { ContainerState } from \"../../remoteHydration\";\nimport type {\n AdoptedContainerPlacement,\n ContainerCreateIntentSyncInput,\n ContainerCreateIntentSyncState,\n} from \"../types\";\nimport { settleContainerCreateIntent } from \"./createIntentSettlement\";\n\nconst LABEL = \"Container create conflict\";\n\n/** The recorded error that parks an intent whose listed create is foreign. */\nexport const CONTAINER_CREATE_ADOPTION_REFUSED =\n \"Container create adoption was refused\";\n\n/**\n * The listed create's signed identity is not this device's pending create: its\n * signed create names another signer, another organization, or a root. An honest server never lists that under the\n * id this device minted, and no later read can change those signed facts, so\n * the intent parks instead of re-verifying.\n */\nclass ForeignContainerCreateError extends KeyingVerificationError {}\n\nfunction foreignCreate(\n code: KeyingVerificationCode,\n message: string,\n): ForeignContainerCreateError {\n return new ForeignContainerCreateError(code, `${LABEL} ${message}`);\n}\n\nasync function verifyAdoptableProjection(input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n readonly projection: ContainerWriterProjectionResponse;\n readonly sessionUserId: string;\n readonly state: ContainerCreateIntentSyncState;\n}): Promise<AdoptedContainerPlacement> {\n const { projection } = input;\n if (\n projection.containerId !== input.containerId ||\n projection.organizationId !== input.expectedOrganizationId\n ) {\n // The envelope is unsigned: a corrupt read is retried, not parked.\n throw new KeyingVerificationError(\n \"object_mismatch\",\n `${LABEL} projection has the wrong identity`,\n );\n }\n const { path, verifiedByHash } = await verifyContainerDestinationProjection({\n execSql: input.state.runtime.infra.execSql,\n projection,\n resolveUserKey: input.state.resolveProjectionUserKey,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n input.state.runtime,\n ),\n });\n const head = path.at(-1);\n // One read naming another container says nothing signed about this id.\n if (!head || head.state.containerId !== input.containerId) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n `${LABEL} projection names another container`,\n );\n }\n const create = verifiedContainerCreateManifest({\n head,\n label: LABEL,\n verifiedByHash,\n });\n if (create.event.event.signerUserId !== input.sessionUserId) {\n throw foreignCreate(\"signer_mismatch\", \"was signed by another user\");\n }\n if (create.state.organizationId !== input.expectedOrganizationId) {\n throw foreignCreate(\"object_mismatch\", \"belongs to another organization\");\n }\n const createdParentId = create.state.parentContainerId;\n const currentParentId = head.state.parentContainerId;\n if (createdParentId === null || currentParentId === null) {\n throw foreignCreate(\"object_mismatch\", \"is an organization root\");\n }\n return { createdParentId, currentParentId };\n}\n\n/**\n * A pending create whose container the listing already carries is adopted\n * only when the container's signed epoch-1 `container.create` is this user's,\n * in the intended organization (#2365 finding 26). Listing metadata is\n * unsigned: a create by another writer that collides on the pending id, or one\n * a dishonest server invents, must not become this device's container.\n * Documents apply the same rule (`workflows/documents/createAdoption.ts`).\n *\n * Returns where the create committed and where the verified head sits now.\n * The user may have moved the pending container since, which rewrites its\n * intent's parent; that is a move still owed, not a mismatch.\n */\nasync function assertContainerCreateAdoptable(input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n readonly state: ContainerCreateIntentSyncState;\n}): Promise<AdoptedContainerPlacement> {\n const { runtime } = input.state;\n const sessionUserId = runtime.auth.userId;\n // Without a session user there is no signer to compare: not tampering.\n if (!sessionUserId) {\n throw new Error(`${LABEL} needs a signed-in session user`);\n }\n const projection = await runtime.apiClient.getContainerWriterProjection(\n input.containerId,\n );\n if (!projection) {\n throw new Error(`${LABEL} is unavailable to verify`);\n }\n try {\n return await verifyAdoptableProjection({\n ...input,\n projection,\n sessionUserId,\n });\n } catch (error) {\n // Never re-serve a cached projection that failed verification.\n if (isKeyingVerificationError(error)) {\n runtime.apiClient.evictContainerWriterProjection(input.containerId);\n }\n throw error;\n }\n}\n\nfunction verifyListedContainerCreate(input: {\n readonly intent: ContainerCreateIntentSyncInput[\"intent\"];\n readonly parentState: ContainerState;\n readonly state: ContainerCreateIntentSyncState;\n}): Promise<AdoptedContainerPlacement> {\n const adoption = {\n containerId: input.intent.containerId,\n expectedOrganizationId: input.parentState.container.organizationId,\n };\n return input.state.verifyCreateAdoption\n ? input.state.verifyCreateAdoption(adoption)\n : assertContainerCreateAdoptable({ ...adoption, state: input.state });\n}\n\n/** Settles a verified adoption against the row hydration installed. */\nasync function markContainerCreateIntentAdopted(input: {\n containerState: ContainerState;\n placement: AdoptedContainerPlacement;\n syncInput: ContainerCreateIntentSyncInput;\n}): Promise<boolean> {\n const { containerState, placement, syncInput } = input;\n const remoteMetadataDocumentId = containerState.record.documentId;\n const remoteMetadataAccessStateHash = containerState.record.accessStateHash;\n\n if (!remoteMetadataDocumentId || !remoteMetadataAccessStateHash) {\n return false;\n }\n return settleContainerCreateIntent({\n createdParentContainerId: placement.createdParentId,\n desiredParentContainerId: syncInput.intent.parentContainerId,\n intent: syncInput.intent,\n isCurrent: syncInput.isCurrent,\n remoteContainerId: containerState.container.id,\n remoteMetadataAccessStateHash,\n remoteMetadataDocumentId,\n state: syncInput.state,\n supersededMovePreviousParentId: placement.currentParentId,\n });\n}\n\n/**\n * Records a failed adoption and leaves the lane's other intents to proceed. A\n * foreign create parks the intent: its signed facts never change, so later\n * passes neither re-read it nor report it again. Any other failure retries.\n */\nasync function recordAdoptionFailure(input: {\n readonly error: unknown;\n readonly organizationId: string;\n readonly syncInput: ContainerCreateIntentSyncInput;\n}): Promise<\"abandoned\" | \"failed\"> {\n const { intent, isCurrent, state } = input.syncInput;\n if (!isCurrent()) return \"abandoned\";\n await reportKeyingVerificationErrorInCauseChain(\n input.error,\n state.runtime.util.reportSecurityIncident,\n {\n objectId: intent.containerId,\n objectKind: \"container\",\n operation: \"container.create.replay\",\n organizationId: input.organizationId,\n },\n );\n if (!isCurrent()) return \"abandoned\";\n const action =\n input.error instanceof ForeignContainerCreateError\n ? CONTAINER_CREATE_ADOPTION_REFUSED\n : \"Container create adoption verification failed\";\n await state.persistence.recordCreateIntentRevisionError(\n state.runtime.infra.execSql,\n {\n containerId: intent.containerId,\n expectedIntentId: intent.id,\n expectedUpdatedAt: intent.updatedAt,\n message: `${action}: ${errorMessage(input.error)}`,\n stillCurrent: isCurrent,\n },\n );\n return isCurrent() ? \"failed\" : \"abandoned\";\n}\n\n/**\n * Adopts a pending create the listing already carries, once its signed create\n * proves it is this device's. A parked refusal stays blocked without a read.\n */\nexport async function syncListedContainerCreate(input: {\n readonly containerState: ContainerState;\n readonly parentState: ContainerState;\n readonly syncInput: ContainerCreateIntentSyncInput;\n}): Promise<\"abandoned\" | \"blocked\" | \"created\" | \"failed\"> {\n const { containerState, parentState, syncInput } = input;\n const { intent, isCurrent, state } = syncInput;\n if (intent.lastError?.startsWith(CONTAINER_CREATE_ADOPTION_REFUSED)) {\n return \"blocked\";\n }\n let placement: AdoptedContainerPlacement;\n try {\n placement = await verifyListedContainerCreate({\n intent,\n parentState,\n state,\n });\n } catch (error) {\n return recordAdoptionFailure({\n error,\n organizationId: parentState.container.organizationId,\n syncInput,\n });\n }\n const marked = await markContainerCreateIntentAdopted({\n containerState,\n placement,\n syncInput,\n });\n if (!marked) return isCurrent() ? \"blocked\" : \"abandoned\";\n // The folder now sits elsewhere; list it there rather than waiting for the\n // next poll to correct the local parent.\n if (placement.currentParentId !== intent.parentContainerId) {\n syncInput.requestRemoteReconciliation(placement.currentParentId);\n }\n return \"created\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"createAdoption.js","sourceRoot":"","sources":["createAdoption.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type KeyingVerificationCode,\n KeyingVerificationError,\n} from \"@tearleads/crypto\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n verifiedContainerCreateManifest,\n verifyContainerDestinationProjection,\n} from \"../../../../data/keyingProjectionVerification\";\nimport {\n isKeyingVerificationError,\n reportKeyingVerificationErrorInCauseChain,\n} from \"../../../../data/keyingProjectionVerification/error\";\nimport { errorMessage } from \"../../../../data/shared/errorMessage\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../../principals/runtimePolicyWarmer\";\nimport type { ContainerState } from \"../../remoteHydration\";\nimport type {\n AdoptedContainerPlacement,\n ContainerCreateIntentSyncInput,\n ContainerCreateIntentSyncState,\n} from \"../types\";\nimport { settleContainerCreateIntent } from \"./createIntentSettlement\";\n\nconst LABEL = \"Container create conflict\";\n\n/** The recorded error that parks an intent whose listed create is foreign. */\nexport const CONTAINER_CREATE_ADOPTION_REFUSED =\n \"Container create adoption was refused\";\n\n/**\n * The listed create's signed identity is not this device's pending create: its\n * signed create names another signer, another organization, or a root. An honest server never lists that under the\n * id this device minted, and no later read can change those signed facts, so\n * the intent parks instead of re-verifying.\n */\nclass ForeignContainerCreateError extends KeyingVerificationError {}\n\nfunction foreignCreate(\n code: KeyingVerificationCode,\n message: string,\n): ForeignContainerCreateError {\n return new ForeignContainerCreateError(code, `${LABEL} ${message}`);\n}\n\nasync function verifyAdoptableProjection(input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n readonly projection: ContainerWriterProjectionResponse;\n readonly sessionUserId: string;\n readonly state: ContainerCreateIntentSyncState;\n}): Promise<AdoptedContainerPlacement> {\n const { projection } = input;\n if (\n projection.containerId !== input.containerId ||\n projection.organizationId !== input.expectedOrganizationId\n ) {\n // The envelope is unsigned: a corrupt read is retried, not parked.\n throw new KeyingVerificationError(\n \"object_mismatch\",\n `${LABEL} projection has the wrong identity`,\n );\n }\n const { path, verifiedByHash } = await verifyContainerDestinationProjection({\n execSql: input.state.runtime.infra.execSql,\n projection,\n resolveUserKey: input.state.resolveProjectionUserKey,\n warmReferencedPrincipalPolicies: createRuntimePrincipalPolicyWarmer(\n input.state.runtime,\n ),\n });\n const head = path.at(-1);\n // One read naming another container says nothing signed about this id.\n if (!head || head.state.containerId !== input.containerId) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n `${LABEL} projection names another container`,\n );\n }\n const create = verifiedContainerCreateManifest({\n head,\n label: LABEL,\n verifiedByHash,\n });\n if (create.event.event.signerUserId !== input.sessionUserId) {\n throw foreignCreate(\"signer_mismatch\", \"was signed by another user\");\n }\n if (create.state.organizationId !== input.expectedOrganizationId) {\n throw foreignCreate(\"object_mismatch\", \"belongs to another organization\");\n }\n const createdParentId = create.state.parentContainerId;\n const currentParentId = head.state.parentContainerId;\n if (createdParentId === null || currentParentId === null) {\n throw foreignCreate(\"object_mismatch\", \"is an organization root\");\n }\n return { createdParentId, currentParentId };\n}\n\n/**\n * A pending create whose container the listing already carries is adopted\n * only when the container's signed epoch-1 `container.create` is this user's,\n * in the intended organization (#2365 finding 26). Listing metadata is\n * unsigned: a create by another writer that collides on the pending id, or one\n * a dishonest server invents, must not become this device's container.\n * Documents apply the same rule (`workflows/documents/createAdoption.ts`).\n *\n * Returns where the create committed and where the verified head sits now.\n * The user may have moved the pending container since, which rewrites its\n * intent's parent; that is a move still owed, not a mismatch.\n */\nasync function assertContainerCreateAdoptable(input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n readonly state: ContainerCreateIntentSyncState;\n}): Promise<AdoptedContainerPlacement> {\n const { runtime } = input.state;\n const sessionUserId = runtime.auth.userId;\n // Without a session user there is no signer to compare: not tampering.\n if (!sessionUserId) {\n throw new Error(`${LABEL} needs a signed-in session user`);\n }\n const projection = await runtime.apiClient.getContainerWriterProjection(\n input.containerId,\n );\n if (!projection) {\n throw new Error(`${LABEL} is unavailable to verify`);\n }\n try {\n return await verifyAdoptableProjection({\n ...input,\n projection,\n sessionUserId,\n });\n } catch (error) {\n // Never re-serve a cached projection that failed verification.\n if (isKeyingVerificationError(error)) {\n runtime.apiClient.evictContainerWriterProjection(input.containerId);\n }\n throw error;\n }\n}\n\nfunction verifyListedContainerCreate(input: {\n readonly intent: ContainerCreateIntentSyncInput[\"intent\"];\n readonly parentState: ContainerState;\n readonly state: ContainerCreateIntentSyncState;\n}): Promise<AdoptedContainerPlacement> {\n const adoption = {\n containerId: input.intent.containerId,\n expectedOrganizationId: input.parentState.container.organizationId,\n };\n return input.state.verifyCreateAdoption\n ? input.state.verifyCreateAdoption(adoption)\n : assertContainerCreateAdoptable({ ...adoption, state: input.state });\n}\n\n/** Settles a verified adoption against the row hydration installed. */\nasync function markContainerCreateIntentAdopted(input: {\n containerState: ContainerState;\n placement: AdoptedContainerPlacement;\n syncInput: ContainerCreateIntentSyncInput;\n}): Promise<boolean> {\n const { containerState, placement, syncInput } = input;\n const remoteMetadataDocumentId = containerState.record.documentId;\n const remoteMetadataAccessStateHash = containerState.record.accessStateHash;\n\n if (!remoteMetadataDocumentId || !remoteMetadataAccessStateHash) {\n return false;\n }\n return settleContainerCreateIntent({\n createdParentContainerId: placement.createdParentId,\n desiredParentContainerId: syncInput.intent.parentContainerId,\n intent: syncInput.intent,\n isCurrent: syncInput.isCurrent,\n remoteContainerId: containerState.container.id,\n remoteMetadataAccessStateHash,\n remoteMetadataDocumentId,\n state: syncInput.state,\n supersededMovePreviousParentId: placement.currentParentId,\n });\n}\n\n/**\n * Records a failed adoption and leaves the lane's other intents to proceed. A\n * foreign create parks the intent: its signed facts never change, so later\n * passes neither re-read it nor report it again. Any other failure retries.\n */\nasync function recordAdoptionFailure(input: {\n readonly error: unknown;\n readonly organizationId: string;\n readonly syncInput: ContainerCreateIntentSyncInput;\n}): Promise<\"abandoned\" | \"failed\"> {\n const { intent, isCurrent, state } = input.syncInput;\n if (!isCurrent()) return \"abandoned\";\n await reportKeyingVerificationErrorInCauseChain(\n input.error,\n state.runtime.util.reportSecurityIncident,\n {\n objectId: intent.containerId,\n objectKind: \"container\",\n operation: \"container.create.replay\",\n organizationId: input.organizationId,\n },\n );\n if (!isCurrent()) return \"abandoned\";\n const action =\n input.error instanceof ForeignContainerCreateError\n ? CONTAINER_CREATE_ADOPTION_REFUSED\n : \"Container create adoption verification failed\";\n await state.persistence.recordCreateIntentRevisionError(\n state.runtime.infra.execSql,\n {\n containerId: intent.containerId,\n expectedIntentId: intent.id,\n expectedUpdatedAt: intent.updatedAt,\n message: `${action}: ${errorMessage(input.error)}`,\n stillCurrent: isCurrent,\n },\n );\n return isCurrent() ? \"failed\" : \"abandoned\";\n}\n\n/**\n * Adopts a pending create the listing already carries, once its signed create\n * proves it is this device's. A parked refusal stays blocked without a read.\n */\nexport async function syncListedContainerCreate(input: {\n readonly containerState: ContainerState;\n readonly parentState: ContainerState;\n readonly syncInput: ContainerCreateIntentSyncInput;\n}): Promise<\"abandoned\" | \"blocked\" | \"created\" | \"failed\"> {\n const { containerState, parentState, syncInput } = input;\n const { intent, isCurrent, state } = syncInput;\n if (intent.lastError?.startsWith(CONTAINER_CREATE_ADOPTION_REFUSED)) {\n return \"blocked\";\n }\n let placement: AdoptedContainerPlacement;\n try {\n placement = await verifyListedContainerCreate({\n intent,\n parentState,\n state,\n });\n } catch (error) {\n return recordAdoptionFailure({\n error,\n organizationId: parentState.container.organizationId,\n syncInput,\n });\n }\n const marked = await markContainerCreateIntentAdopted({\n containerState,\n placement,\n syncInput,\n });\n if (!marked) return isCurrent() ? \"blocked\" : \"abandoned\";\n // The folder now sits elsewhere; list it there rather than waiting for the\n // next poll to correct the local parent.\n if (placement.currentParentId !== intent.parentContainerId) {\n syncInput.requestRemoteReconciliation(placement.currentParentId);\n }\n return \"created\";\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { KeyingVerificationError } from "../../../../internal/crypto/index.js";
|
|
2
|
-
import { verifyContainerWriterProjection } from "../../../../data/keyingProjectionVerification/
|
|
2
|
+
import { verifyContainerWriterProjection } from "../../../../data/keyingProjectionVerification/index.js";
|
|
3
3
|
import { runWithSecurityIncidentReporting } from "../../../../data/keyingProjectionVerification/error.js";
|
|
4
4
|
import { rethrowProjectionVerificationCancelled } from "../../../../data/keyingProjectionVerification/types.js";
|
|
5
5
|
import { hasUnsettledDocumentPlacement } from "../../../../data/persistence/container-contents/documentPurgePlacement.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"purgeTreeScope.js","sourceRoot":"","sources":["purgeTreeScope.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport type { DocumentSummary } from \"../../../../data/documents/documentSummary\";\nimport type { ProjectionUserKeyResolver } from \"../../../../data/keyingProjectionVerification\";\nimport { verifyContainerWriterProjection } from \"../../../../data/keyingProjectionVerification/container/containerProjectionVerification\";\nimport { runWithSecurityIncidentReporting } from \"../../../../data/keyingProjectionVerification/error\";\nimport { rethrowProjectionVerificationCancelled } from \"../../../../data/keyingProjectionVerification/types\";\nimport { hasUnsettledDocumentPlacement } from \"../../../../data/persistence/container-contents/documentPurgePlacement\";\nimport { sqlDocumentsPersistence } from \"../../../../data/persistence/documents/documentsPersistence\";\nimport { errorMessage } from \"../../../../data/shared/errorMessage\";\nimport type { ExecSql } from \"../../../../data/sqlite/sqlSchema\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../../principals/runtimePolicyWarmer\";\nimport type { ContainerContentsPersistence } from \"../../containerPersistence\";\nimport type { ContainerState } from \"../../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../../runtime\";\nimport { assertLocalPurgeScope } from \"./localPurgeScope\";\nimport { snapshotContainerStates } from \"./purgeTreeCollection\";\nimport { readLocalPurgeScope } from \"./readLocalPurgeScope\";\n\ninterface SubtreePurgeScopeInput {\n containersById: ReadonlyMap<string, ContainerState>;\n persistence: ContainerContentsPersistence;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n rootContainerId: string;\n runtime: ContainerContentsWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}\n\nasync function verifiedRemoteScope(\n input: SubtreePurgeScopeInput,\n current: Pick<ContainerState, \"container\" | \"record\">,\n verifiedAncestorsById: Map<string, readonly string[]>,\n): Promise<boolean> {\n const { runtime, rootContainerId } = input;\n const targetId = current.container.id;\n return runWithSecurityIncidentReporting(\n runtime.util.reportSecurityIncident,\n {\n objectId: targetId,\n objectKind: \"container\",\n operation: \"container.purge.scope\",\n },\n async () => {\n runtime.apiClient.evictContainerWriterProjection(targetId);\n const projection =\n await runtime.apiClient.getContainerWriterProjection(targetId);\n if (!projection || input.stillCurrent?.() === false) return false;\n if (\n projection.containerId !== targetId ||\n projection.organizationId !== current.container.organizationId\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Subtree purge container identity mismatch\",\n );\n }\n const path = await verifyContainerWriterProjection({\n execSql: runtime.infra.execSql,\n projection,\n resolveUserKey: input.resolveProjectionUserKey,\n stillCurrent: input.stillCurrent,\n warmReferencedPrincipalPolicies:\n createRuntimePrincipalPolicyWarmer(runtime),\n });\n const rootIndex = path.findIndex(\n (head) => head.state.containerId === rootContainerId,\n );\n const latest = await input.persistence.loadContainerMetadataState(\n runtime.infra.execSql,\n targetId,\n );\n const pendingMoves = await readPendingMoves(input);\n const allowed =\n rootIndex >= 0 &&\n path[rootIndex]?.state.parentContainerId ===\n input.containersById.get(rootContainerId)?.container.parentId &&\n latest?.container.parentId === current.container.parentId &&\n latest.record?.documentId === current.record.documentId &&\n !path\n .slice(rootIndex)\n .some((head) => pendingMoves.has(head.state.containerId)) &&\n input.stillCurrent?.() !== false;\n if (allowed)\n verifiedAncestorsById.set(\n targetId,\n path.slice(rootIndex).map((head) => head.state.containerId),\n );\n return allowed;\n },\n );\n}\n\n/**\n * A local-only unit cites no signed request, so a remote ordinary root must\n * prove that it still sits under the parent it was selected from. A system\n * root's signed topology is fixed by its destination role.\n */\nasync function rootPlacementIsCurrent(\n input: SubtreePurgeScopeInput,\n verifiedAncestorsById: Map<string, readonly string[]>,\n): Promise<boolean> {\n const root = input.containersById.get(input.rootContainerId);\n if (!root?.record.documentId || (root.container.systemSlot ?? null) !== null)\n return true;\n return verifiedRemoteScope(input, root, verifiedAncestorsById);\n}\n\nasync function allowsContainer(\n input: SubtreePurgeScopeInput,\n containerId: string,\n verifiedAncestorsById: Map<string, readonly string[]>,\n localOnlyUnit: boolean,\n): Promise<boolean> {\n const { runtime, persistence, rootContainerId } = input;\n if (input.stillCurrent?.() === false) return false;\n const execSql = runtime.infra.execSql;\n const pendingMoves = await readPendingMoves(input);\n if (pendingMoves.has(rootContainerId)) return false;\n const seen = new Set<string>([rootContainerId]);\n let currentId: string | null = containerId;\n let verifiedRemoteAncestry = false;\n while (currentId !== rootContainerId) {\n if (!currentId || seen.has(currentId) || pendingMoves.has(currentId))\n return false;\n seen.add(currentId);\n const expected = input.containersById.get(currentId);\n const current = await persistence.loadContainerMetadataState(\n execSql,\n currentId,\n );\n if (\n !expected ||\n !current?.record ||\n current.container.parentId !== expected.container.parentId ||\n current.container.organizationId !== expected.container.organizationId ||\n current.record.documentId !== expected.record.documentId\n )\n return false;\n if (current.record.documentId) {\n if (\n !(await verifiedRemoteScope(\n input,\n {\n container: current.container,\n record: current.record,\n },\n verifiedAncestorsById,\n ))\n )\n return false;\n verifiedRemoteAncestry = true;\n break;\n }\n currentId = current.container.parentId;\n }\n if (\n localOnlyUnit &&\n !verifiedRemoteAncestry &&\n !(await rootPlacementIsCurrent(input, verifiedAncestorsById))\n )\n return false;\n return readLocalPurgeScope({\n ...input,\n containerId,\n execSql,\n verifiedAncestorsById,\n });\n}\n\nasync function readPendingMoves(input: SubtreePurgeScopeInput) {\n return new Set(\n (\n await input.persistence.listUnsyncedMoveIntents(\n input.runtime.infra.execSql,\n )\n ).map((intent) => intent.containerId),\n );\n}\n\nasync function documentPlacementIsCurrent(\n input: SubtreePurgeScopeInput,\n document: DocumentSummary,\n): Promise<boolean> {\n const execSql = input.runtime.infra.execSql;\n if (await hasUnsettledDocumentPlacement(execSql, document.id)) return false;\n const current = await sqlDocumentsPersistence.loadDocument(\n execSql,\n document.id,\n );\n return (\n current !== null &&\n current.documentId === document.documentId &&\n current.containerId === document.containerId &&\n input.stillCurrent?.() !== false\n );\n}\n\n/** Listing edges only select candidates; each destructive unit proves its scope. */\nexport function createSubtreePurgeScope(request: SubtreePurgeScopeInput) {\n const input = {\n ...request,\n containersById: snapshotContainerStates(request.containersById),\n };\n const verifiedAncestorsById = new Map<string, readonly string[]>();\n return {\n allowsLocalScopeInTransaction: (execSql: ExecSql, containerId: string) =>\n checkAvailableScope(input, async () => {\n await assertLocalPurgeScope({\n ...input,\n execSql,\n containerId,\n verifiedAncestorsById,\n });\n return true;\n }),\n assertLocalScopeInTransaction: (\n execSql: ExecSql,\n containerId: string | null | undefined,\n localId: string,\n ) =>\n assertLocalPurgeScope({\n ...input,\n execSql,\n containerId,\n localId,\n verifiedAncestorsById,\n }),\n allowsContainer: (containerId: string) =>\n checkAvailableScope(input, () =>\n allowsContainer(\n input,\n containerId,\n verifiedAncestorsById,\n !input.containersById.get(containerId)?.record.documentId,\n ),\n ),\n allowsDocument: (document: DocumentSummary) =>\n checkAvailableScope(input, async () => {\n if (\n !document.containerId ||\n !(await documentPlacementIsCurrent(input, document))\n )\n return false;\n if (\n !(await allowsContainer(\n input,\n document.containerId,\n verifiedAncestorsById,\n !document.documentId,\n ))\n )\n return false;\n // Freeze local placement checks together after all remote verification.\n return readLocalPurgeScope(\n {\n ...input,\n containerId: document.containerId,\n execSql: input.runtime.infra.execSql,\n localId: document.id,\n verifiedAncestorsById,\n },\n document,\n );\n }),\n };\n}\n\nasync function checkAvailableScope(\n input: SubtreePurgeScopeInput,\n check: () => Promise<boolean>,\n): Promise<boolean> {\n try {\n return await check();\n } catch (error) {\n rethrowProjectionVerificationCancelled(error);\n if (error instanceof KeyingVerificationError) throw error;\n input.runtime.util.log(\n `Container contents: purge scope unavailable: ${errorMessage(error)}`,\n );\n return false;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"purgeTreeScope.js","sourceRoot":"","sources":["purgeTreeScope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EAAE,+BAA+B,EAAE,MAAM,+CAA+C,CAAC;AAChG,OAAO,EAAE,gCAAgC,EAAE,MAAM,qDAAqD,CAAC;AACvG,OAAO,EAAE,sCAAsC,EAAE,MAAM,qDAAqD,CAAC;AAC7G,OAAO,EAAE,6BAA6B,EAAE,MAAM,wEAAwE,CAAC;AACvH,OAAO,EAAE,uBAAuB,EAAE,MAAM,6DAA6D,CAAC;AACtG,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAEpE,OAAO,EAAE,kCAAkC,EAAE,MAAM,yCAAyC,CAAC;AAI7F,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAW5D,KAAK,UAAU,mBAAmB,CAChC,KAA6B,EAC7B,OAAqD,EACrD,qBAAqD;IAErD,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,KAAK,CAAC;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;IACtC,OAAO,gCAAgC,CACrC,OAAO,CAAC,IAAI,CAAC,sBAAsB,EACnC;QACE,QAAQ,EAAE,QAAQ;QAClB,UAAU,EAAE,WAAW;QACvB,SAAS,EAAE,uBAAuB;KACnC,EACD,KAAK,IAAI,EAAE;QACT,OAAO,CAAC,SAAS,CAAC,8BAA8B,CAAC,QAAQ,CAAC,CAAC;QAC3D,MAAM,UAAU,GACd,MAAM,OAAO,CAAC,SAAS,CAAC,4BAA4B,CAAC,QAAQ,CAAC,CAAC;QACjE,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,KAAK;YAAE,OAAO,KAAK,CAAC;QAClE,IACE,UAAU,CAAC,WAAW,KAAK,QAAQ;YACnC,UAAU,CAAC,cAAc,KAAK,OAAO,CAAC,SAAS,CAAC,cAAc,EAC9D,CAAC;YACD,MAAM,IAAI,uBAAuB,CAC/B,iBAAiB,EACjB,2CAA2C,CAC5C,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,+BAA+B,CAAC;YACjD,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;YAC9B,UAAU;YACV,cAAc,EAAE,KAAK,CAAC,wBAAwB;YAC9C,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,+BAA+B,EAC7B,kCAAkC,CAAC,OAAO,CAAC;SAC9C,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAC9B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,KAAK,eAAe,CACrD,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,0BAA0B,CAC/D,OAAO,CAAC,KAAK,CAAC,OAAO,EACrB,QAAQ,CACT,CAAC;QACF,MAAM,YAAY,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACnD,MAAM,OAAO,GACX,SAAS,IAAI,CAAC;YACd,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,iBAAiB;gBACtC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,SAAS,CAAC,QAAQ;YAC/D,MAAM,EAAE,SAAS,CAAC,QAAQ,KAAK,OAAO,CAAC,SAAS,CAAC,QAAQ;YACzD,MAAM,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,UAAU;YACvD,CAAC,IAAI;iBACF,KAAK,CAAC,SAAS,CAAC;iBAChB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC3D,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,KAAK,CAAC;QACnC,IAAI,OAAO;YACT,qBAAqB,CAAC,GAAG,CACvB,QAAQ,EACR,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAC5D,CAAC;QACJ,OAAO,OAAO,CAAC;IACjB,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,sBAAsB,CACnC,KAA6B,EAC7B,qBAAqD;IAErD,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC7D,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,IAAI;QAC1E,OAAO,IAAI,CAAC;IACd,OAAO,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,qBAAqB,CAAC,CAAC;AACjE,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,KAA6B,EAC7B,WAAmB,EACnB,qBAAqD,EACrD,aAAsB;IAEtB,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,KAAK,CAAC;IACxD,IAAI,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;IACtC,MAAM,YAAY,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACnD,IAAI,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC;QAAE,OAAO,KAAK,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,CAAC,eAAe,CAAC,CAAC,CAAC;IAChD,IAAI,SAAS,GAAkB,WAAW,CAAC;IAC3C,IAAI,sBAAsB,GAAG,KAAK,CAAC;IACnC,OAAO,SAAS,KAAK,eAAe,EAAE,CAAC;QACrC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC;YAClE,OAAO,KAAK,CAAC;QACf,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACpB,MAAM,QAAQ,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,0BAA0B,CAC1D,OAAO,EACP,SAAS,CACV,CAAC;QACF,IACE,CAAC,QAAQ;YACT,CAAC,OAAO,EAAE,MAAM;YAChB,OAAO,CAAC,SAAS,CAAC,QAAQ,KAAK,QAAQ,CAAC,SAAS,CAAC,QAAQ;YAC1D,OAAO,CAAC,SAAS,CAAC,cAAc,KAAK,QAAQ,CAAC,SAAS,CAAC,cAAc;YACtE,OAAO,CAAC,MAAM,CAAC,UAAU,KAAK,QAAQ,CAAC,MAAM,CAAC,UAAU;YAExD,OAAO,KAAK,CAAC;QACf,IAAI,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAC9B,IACE,CAAC,CAAC,MAAM,mBAAmB,CACzB,KAAK,EACL;gBACE,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,EACD,qBAAqB,CACtB,CAAC;gBAEF,OAAO,KAAK,CAAC;YACf,sBAAsB,GAAG,IAAI,CAAC;YAC9B,MAAM;QACR,CAAC;QACD,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC;IACzC,CAAC;IACD,IACE,aAAa;QACb,CAAC,sBAAsB;QACvB,CAAC,CAAC,MAAM,sBAAsB,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;QAE7D,OAAO,KAAK,CAAC;IACf,OAAO,mBAAmB,CAAC;QACzB,GAAG,KAAK;QACR,WAAW;QACX,OAAO;QACP,qBAAqB;KACtB,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,KAA6B;IAC3D,OAAO,IAAI,GAAG,CACZ,CACE,MAAM,KAAK,CAAC,WAAW,CAAC,uBAAuB,CAC7C,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAC5B,CACF,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CACtC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,0BAA0B,CACvC,KAA6B,EAC7B,QAAyB;IAEzB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;IAC5C,IAAI,MAAM,6BAA6B,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5E,MAAM,OAAO,GAAG,MAAM,uBAAuB,CAAC,YAAY,CACxD,OAAO,EACP,QAAQ,CAAC,EAAE,CACZ,CAAC;IACF,OAAO,CACL,OAAO,KAAK,IAAI;QAChB,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,UAAU;QAC1C,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW;QAC5C,KAAK,CAAC,YAAY,EAAE,EAAE,KAAK,KAAK,CACjC,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,uBAAuB,CAAC,OAA+B;IACrE,MAAM,KAAK,GAAG;QACZ,GAAG,OAAO;QACV,cAAc,EAAE,uBAAuB,CAAC,OAAO,CAAC,cAAc,CAAC;KAChE,CAAC;IACF,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAA6B,CAAC;IACnE,OAAO;QACL,6BAA6B,EAAE,CAAC,OAAgB,EAAE,WAAmB,EAAE,EAAE,CACvE,mBAAmB,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;YACpC,MAAM,qBAAqB,CAAC;gBAC1B,GAAG,KAAK;gBACR,OAAO;gBACP,WAAW;gBACX,qBAAqB;aACtB,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QACJ,6BAA6B,EAAE,CAC7B,OAAgB,EAChB,WAAsC,EACtC,OAAe,EACf,EAAE,CACF,qBAAqB,CAAC;YACpB,GAAG,KAAK;YACR,OAAO;YACP,WAAW;YACX,OAAO;YACP,qBAAqB;SACtB,CAAC;QACJ,eAAe,EAAE,CAAC,WAAmB,EAAE,EAAE,CACvC,mBAAmB,CAAC,KAAK,EAAE,GAAG,EAAE,CAC9B,eAAe,CACb,KAAK,EACL,WAAW,EACX,qBAAqB,EACrB,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,UAAU,CAC1D,CACF;QACH,cAAc,EAAE,CAAC,QAAyB,EAAE,EAAE,CAC5C,mBAAmB,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;YACpC,IACE,CAAC,QAAQ,CAAC,WAAW;gBACrB,CAAC,CAAC,MAAM,0BAA0B,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;gBAEpD,OAAO,KAAK,CAAC;YACf,IACE,CAAC,CAAC,MAAM,eAAe,CACrB,KAAK,EACL,QAAQ,CAAC,WAAW,EACpB,qBAAqB,EACrB,CAAC,QAAQ,CAAC,UAAU,CACrB,CAAC;gBAEF,OAAO,KAAK,CAAC;YACf,wEAAwE;YACxE,OAAO,mBAAmB,CACxB;gBACE,GAAG,KAAK;gBACR,WAAW,EAAE,QAAQ,CAAC,WAAW;gBACjC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO;gBACpC,OAAO,EAAE,QAAQ,CAAC,EAAE;gBACpB,qBAAqB;aACtB,EACD,QAAQ,CACT,CAAC;QACJ,CAAC,CAAC;KACL,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,mBAAmB,CAChC,KAA6B,EAC7B,KAA6B;IAE7B,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,sCAAsC,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,KAAK,YAAY,uBAAuB;YAAE,MAAM,KAAK,CAAC;QAC1D,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CACpB,gDAAgD,YAAY,CAAC,KAAK,CAAC,EAAE,CACtE,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport type { DocumentSummary } from \"../../../../data/documents/documentSummary\";\nimport type { ProjectionUserKeyResolver } from \"../../../../data/keyingProjectionVerification\";\nimport { verifyContainerWriterProjection } from \"../../../../data/keyingProjectionVerification\";\nimport { runWithSecurityIncidentReporting } from \"../../../../data/keyingProjectionVerification/error\";\nimport { rethrowProjectionVerificationCancelled } from \"../../../../data/keyingProjectionVerification/types\";\nimport { hasUnsettledDocumentPlacement } from \"../../../../data/persistence/container-contents/documentPurgePlacement\";\nimport { sqlDocumentsPersistence } from \"../../../../data/persistence/documents/documentsPersistence\";\nimport { errorMessage } from \"../../../../data/shared/errorMessage\";\nimport type { ExecSql } from \"../../../../data/sqlite/sqlSchema\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../../../principals/runtimePolicyWarmer\";\nimport type { ContainerContentsPersistence } from \"../../containerPersistence\";\nimport type { ContainerState } from \"../../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../../runtime\";\nimport { assertLocalPurgeScope } from \"./localPurgeScope\";\nimport { snapshotContainerStates } from \"./purgeTreeCollection\";\nimport { readLocalPurgeScope } from \"./readLocalPurgeScope\";\n\ninterface SubtreePurgeScopeInput {\n containersById: ReadonlyMap<string, ContainerState>;\n persistence: ContainerContentsPersistence;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n rootContainerId: string;\n runtime: ContainerContentsWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}\n\nasync function verifiedRemoteScope(\n input: SubtreePurgeScopeInput,\n current: Pick<ContainerState, \"container\" | \"record\">,\n verifiedAncestorsById: Map<string, readonly string[]>,\n): Promise<boolean> {\n const { runtime, rootContainerId } = input;\n const targetId = current.container.id;\n return runWithSecurityIncidentReporting(\n runtime.util.reportSecurityIncident,\n {\n objectId: targetId,\n objectKind: \"container\",\n operation: \"container.purge.scope\",\n },\n async () => {\n runtime.apiClient.evictContainerWriterProjection(targetId);\n const projection =\n await runtime.apiClient.getContainerWriterProjection(targetId);\n if (!projection || input.stillCurrent?.() === false) return false;\n if (\n projection.containerId !== targetId ||\n projection.organizationId !== current.container.organizationId\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Subtree purge container identity mismatch\",\n );\n }\n const path = await verifyContainerWriterProjection({\n execSql: runtime.infra.execSql,\n projection,\n resolveUserKey: input.resolveProjectionUserKey,\n stillCurrent: input.stillCurrent,\n warmReferencedPrincipalPolicies:\n createRuntimePrincipalPolicyWarmer(runtime),\n });\n const rootIndex = path.findIndex(\n (head) => head.state.containerId === rootContainerId,\n );\n const latest = await input.persistence.loadContainerMetadataState(\n runtime.infra.execSql,\n targetId,\n );\n const pendingMoves = await readPendingMoves(input);\n const allowed =\n rootIndex >= 0 &&\n path[rootIndex]?.state.parentContainerId ===\n input.containersById.get(rootContainerId)?.container.parentId &&\n latest?.container.parentId === current.container.parentId &&\n latest.record?.documentId === current.record.documentId &&\n !path\n .slice(rootIndex)\n .some((head) => pendingMoves.has(head.state.containerId)) &&\n input.stillCurrent?.() !== false;\n if (allowed)\n verifiedAncestorsById.set(\n targetId,\n path.slice(rootIndex).map((head) => head.state.containerId),\n );\n return allowed;\n },\n );\n}\n\n/**\n * A local-only unit cites no signed request, so a remote ordinary root must\n * prove that it still sits under the parent it was selected from. A system\n * root's signed topology is fixed by its destination role.\n */\nasync function rootPlacementIsCurrent(\n input: SubtreePurgeScopeInput,\n verifiedAncestorsById: Map<string, readonly string[]>,\n): Promise<boolean> {\n const root = input.containersById.get(input.rootContainerId);\n if (!root?.record.documentId || (root.container.systemSlot ?? null) !== null)\n return true;\n return verifiedRemoteScope(input, root, verifiedAncestorsById);\n}\n\nasync function allowsContainer(\n input: SubtreePurgeScopeInput,\n containerId: string,\n verifiedAncestorsById: Map<string, readonly string[]>,\n localOnlyUnit: boolean,\n): Promise<boolean> {\n const { runtime, persistence, rootContainerId } = input;\n if (input.stillCurrent?.() === false) return false;\n const execSql = runtime.infra.execSql;\n const pendingMoves = await readPendingMoves(input);\n if (pendingMoves.has(rootContainerId)) return false;\n const seen = new Set<string>([rootContainerId]);\n let currentId: string | null = containerId;\n let verifiedRemoteAncestry = false;\n while (currentId !== rootContainerId) {\n if (!currentId || seen.has(currentId) || pendingMoves.has(currentId))\n return false;\n seen.add(currentId);\n const expected = input.containersById.get(currentId);\n const current = await persistence.loadContainerMetadataState(\n execSql,\n currentId,\n );\n if (\n !expected ||\n !current?.record ||\n current.container.parentId !== expected.container.parentId ||\n current.container.organizationId !== expected.container.organizationId ||\n current.record.documentId !== expected.record.documentId\n )\n return false;\n if (current.record.documentId) {\n if (\n !(await verifiedRemoteScope(\n input,\n {\n container: current.container,\n record: current.record,\n },\n verifiedAncestorsById,\n ))\n )\n return false;\n verifiedRemoteAncestry = true;\n break;\n }\n currentId = current.container.parentId;\n }\n if (\n localOnlyUnit &&\n !verifiedRemoteAncestry &&\n !(await rootPlacementIsCurrent(input, verifiedAncestorsById))\n )\n return false;\n return readLocalPurgeScope({\n ...input,\n containerId,\n execSql,\n verifiedAncestorsById,\n });\n}\n\nasync function readPendingMoves(input: SubtreePurgeScopeInput) {\n return new Set(\n (\n await input.persistence.listUnsyncedMoveIntents(\n input.runtime.infra.execSql,\n )\n ).map((intent) => intent.containerId),\n );\n}\n\nasync function documentPlacementIsCurrent(\n input: SubtreePurgeScopeInput,\n document: DocumentSummary,\n): Promise<boolean> {\n const execSql = input.runtime.infra.execSql;\n if (await hasUnsettledDocumentPlacement(execSql, document.id)) return false;\n const current = await sqlDocumentsPersistence.loadDocument(\n execSql,\n document.id,\n );\n return (\n current !== null &&\n current.documentId === document.documentId &&\n current.containerId === document.containerId &&\n input.stillCurrent?.() !== false\n );\n}\n\n/** Listing edges only select candidates; each destructive unit proves its scope. */\nexport function createSubtreePurgeScope(request: SubtreePurgeScopeInput) {\n const input = {\n ...request,\n containersById: snapshotContainerStates(request.containersById),\n };\n const verifiedAncestorsById = new Map<string, readonly string[]>();\n return {\n allowsLocalScopeInTransaction: (execSql: ExecSql, containerId: string) =>\n checkAvailableScope(input, async () => {\n await assertLocalPurgeScope({\n ...input,\n execSql,\n containerId,\n verifiedAncestorsById,\n });\n return true;\n }),\n assertLocalScopeInTransaction: (\n execSql: ExecSql,\n containerId: string | null | undefined,\n localId: string,\n ) =>\n assertLocalPurgeScope({\n ...input,\n execSql,\n containerId,\n localId,\n verifiedAncestorsById,\n }),\n allowsContainer: (containerId: string) =>\n checkAvailableScope(input, () =>\n allowsContainer(\n input,\n containerId,\n verifiedAncestorsById,\n !input.containersById.get(containerId)?.record.documentId,\n ),\n ),\n allowsDocument: (document: DocumentSummary) =>\n checkAvailableScope(input, async () => {\n if (\n !document.containerId ||\n !(await documentPlacementIsCurrent(input, document))\n )\n return false;\n if (\n !(await allowsContainer(\n input,\n document.containerId,\n verifiedAncestorsById,\n !document.documentId,\n ))\n )\n return false;\n // Freeze local placement checks together after all remote verification.\n return readLocalPurgeScope(\n {\n ...input,\n containerId: document.containerId,\n execSql: input.runtime.infra.execSql,\n localId: document.id,\n verifiedAncestorsById,\n },\n document,\n );\n }),\n };\n}\n\nasync function checkAvailableScope(\n input: SubtreePurgeScopeInput,\n check: () => Promise<boolean>,\n): Promise<boolean> {\n try {\n return await check();\n } catch (error) {\n rethrowProjectionVerificationCancelled(error);\n if (error instanceof KeyingVerificationError) throw error;\n input.runtime.util.log(\n `Container contents: purge scope unavailable: ${errorMessage(error)}`,\n );\n return false;\n }\n}\n"]}
|
|
@@ -7,7 +7,7 @@ import { reportAndRethrowKeyingVerificationError } from "../../../data/keyingPro
|
|
|
7
7
|
import { sqlDocumentsPersistence } from "../../../data/persistence/documents/documentsPersistence.js";
|
|
8
8
|
import { documentAttachmentBlobProjection, documentPendingAttachments, documentProjection, } from "../../../data/sqlite/schema/index.js";
|
|
9
9
|
import { getClientSQLitePersistenceRuntime } from "../../../data/sqlite/sqlitePersistenceRuntime.js";
|
|
10
|
-
import { loadPersistedDocumentContent } from "../../documents/
|
|
10
|
+
import { loadPersistedDocumentContent } from "../../documents/index.js";
|
|
11
11
|
import { computeDocumentBlame } from "../attribution/documentCharacterBlame.js";
|
|
12
12
|
import { computeFieldBlame } from "../attribution/documentFieldBlame.js";
|
|
13
13
|
function readRecordString(value, key) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documentInfo.js","sourceRoot":"","sources":["documentInfo.ts"],"names":[],"mappings":"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","sourcesContent":["import { bytesToBase64 } from \"@tearleads/encoding\";\nimport { exportFullHistorySnapshot } from \"@tearleads/loro\";\nimport type {\n BlobAttachmentSummary,\n ContainerKeyingPathResponse,\n DocumentEditAttributionResponse,\n DocumentWriterProjectionResponse,\n ListDocumentAttachmentsResponse,\n} from \"@tearleads/validators/response\";\nimport { and, eq, isNull } from \"drizzle-orm\";\nimport { DEFAULT_DOCUMENT_KIND } from \"../../../data/documents/documentConstants\";\nimport {\n type DocumentBlameRange,\n type DocumentCharacterBlameSummary,\n type DocumentContributor,\n type DocumentFieldBlame,\n summarizeDocumentContributors,\n} from \"../../../data/documents/editAttribution\";\nimport { reportAndRethrowKeyingVerificationError } from \"../../../data/keyingProjectionVerification/error\";\nimport { sqlDocumentsPersistence } from \"../../../data/persistence/documents/documentsPersistence\";\nimport type { SecurityIncidentReporter } from \"../../../data/shared/securityIncidents\";\nimport {\n documentAttachmentBlobProjection,\n documentPendingAttachments,\n documentProjection,\n} from \"../../../data/sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../../data/sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { loadPersistedDocumentContent } from \"../../documents/historyContent\";\nimport { computeDocumentBlame } from \"../attribution/documentCharacterBlame\";\nimport { computeFieldBlame } from \"../attribution/documentFieldBlame\";\nimport type {\n DocumentInfo,\n DocumentInfoAttachment,\n DocumentInfoAuthorizingContainerPath,\n DocumentInfoLocalDetails,\n DocumentInfoRemoteAttachmentBinding,\n DocumentInfoRemoteDetails,\n DocumentInfoRemoteMode,\n} from \"./documentInfoTypes\";\n\nexport type {\n DocumentInfo,\n DocumentInfoAttachment,\n DocumentInfoAuthorizingContainerPath,\n DocumentInfoLocalDetails,\n DocumentInfoRemoteAttachmentBinding,\n DocumentInfoRemoteDetails,\n DocumentInfoRemoteMode,\n} from \"./documentInfoTypes\";\n\ninterface DocumentInfoApi {\n getDocumentEditAttribution: (\n documentId: string,\n requestKey?: string,\n ) => Promise<DocumentEditAttributionResponse | null>;\n getDocumentWriterProjection: (\n documentId: string,\n ) => Promise<DocumentWriterProjectionResponse | null>;\n listDocumentAttachments: (\n documentId: string,\n ) => Promise<ListDocumentAttachmentsResponse | null>;\n}\n\ninterface PendingAttachmentInfoRow {\n byteLength: number;\n createdAt: string;\n localId: string;\n mimeType: string | null;\n name: string;\n slotId: string;\n storageKey: string;\n}\n\ninterface LocalAttachmentInfoRow {\n blobId: string | null;\n byteLength: number;\n localId: string;\n mimeType: string | null;\n slotId: string;\n storageKey: string;\n updatedAt: string;\n}\n\nfunction readRecordString(value: unknown, key: string): string | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n\n const nextValue = Reflect.get(value, key);\n return typeof nextValue === \"string\" && nextValue.length > 0\n ? nextValue\n : null;\n}\n\nfunction readRecordNumber(value: unknown, key: string): number | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n\n const nextValue = Reflect.get(value, key);\n return typeof nextValue === \"number\" && Number.isFinite(nextValue)\n ? nextValue\n : null;\n}\n\nfunction readRecordArrayLength(value: unknown, key: string): number | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n\n const nextValue = Reflect.get(value, key);\n return Array.isArray(nextValue) ? nextValue.length : null;\n}\n\nfunction readStoredManifestHash(\n value: string | null | undefined,\n): string | null {\n if (!value) {\n return null;\n }\n\n try {\n const parsed: unknown = JSON.parse(value);\n return readRecordString(parsed, \"manifestHash\");\n } catch {\n return null;\n }\n}\n\nfunction mapPendingAttachmentRow(\n row: PendingAttachmentInfoRow,\n): DocumentInfoAttachment {\n return {\n attachmentKind: \"pending\",\n blobId: null,\n byteLength: row.byteLength,\n createdAt: row.createdAt,\n localId: row.localId,\n mimeType: row.mimeType,\n name: row.name,\n slotId: row.slotId,\n storageKey: row.storageKey,\n updatedAt: null,\n };\n}\n\nfunction mapLocalAttachmentRow(\n row: LocalAttachmentInfoRow,\n): DocumentInfoAttachment {\n return {\n attachmentKind: \"local\",\n blobId: row.blobId,\n byteLength: row.byteLength,\n createdAt: null,\n localId: row.localId,\n mimeType: row.mimeType,\n name: null,\n slotId: row.slotId,\n storageKey: row.storageKey,\n updatedAt: row.updatedAt,\n };\n}\n\nasync function loadDocumentInfoAttachments(input: {\n execSql: ExecSql;\n localId: string;\n}): Promise<DocumentInfoAttachment[]> {\n const { db } = getClientSQLitePersistenceRuntime(input.execSql);\n const [pendingRows, localRows] = await Promise.all([\n db\n .select({\n byteLength: documentPendingAttachments.byteLength,\n createdAt: documentPendingAttachments.createdAt,\n localId: documentPendingAttachments.localId,\n mimeType: documentPendingAttachments.mimeType,\n name: documentPendingAttachments.name,\n slotId: documentPendingAttachments.slotId,\n storageKey: documentPendingAttachments.storageKey,\n })\n .from(documentPendingAttachments)\n .where(eq(documentPendingAttachments.localId, input.localId))\n .orderBy(\n documentPendingAttachments.createdAt,\n documentPendingAttachments.slotId,\n ),\n db\n .select({\n blobId: documentAttachmentBlobProjection.blobId,\n byteLength: documentAttachmentBlobProjection.byteLength,\n localId: documentAttachmentBlobProjection.localId,\n mimeType: documentAttachmentBlobProjection.mimeType,\n slotId: documentAttachmentBlobProjection.slotId,\n storageKey: documentAttachmentBlobProjection.storageKey,\n updatedAt: documentAttachmentBlobProjection.updatedAt,\n })\n .from(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, input.localId),\n // Unlinked slots keep their row as a pending-detach marker; the\n // document no longer has the attachment, so do not report it.\n isNull(documentAttachmentBlobProjection.detachedAt),\n ),\n )\n .orderBy(\n documentAttachmentBlobProjection.updatedAt,\n documentAttachmentBlobProjection.slotId,\n ),\n ]);\n\n return [\n ...pendingRows.map(mapPendingAttachmentRow),\n ...localRows.map(mapLocalAttachmentRow),\n ];\n}\n\nasync function loadLocalDocumentInfo(input: {\n execSql: ExecSql | null;\n localId: string;\n}): Promise<{\n attachments: DocumentInfoAttachment[];\n contentSnapshot: string | null;\n local: DocumentInfoLocalDetails;\n}> {\n const fallback = {\n attachments: [],\n contentSnapshot: null,\n local: {\n accessEpoch: null,\n accessStateHash: null,\n containerId: null,\n documentId: null,\n documentKind: null,\n hasContentKeyBundle: false,\n hasDocumentKekTargets: false,\n hasDocumentManifestBundle: false,\n lastCommitLsn: null,\n localDocumentManifestHash: null,\n localId: input.localId,\n pendingAttachmentByteLength: 0,\n pendingAttachmentCount: 0,\n pendingUpdateCount: 0,\n title: null,\n updatedAt: null,\n },\n };\n\n if (!input.execSql) {\n return fallback;\n }\n\n await sqlDocumentsPersistence.ensureSchema(input.execSql);\n const { db } = getClientSQLitePersistenceRuntime(input.execSql);\n const [document, pendingUpdates, attachments, projectionRows, contentDoc] =\n await Promise.all([\n sqlDocumentsPersistence.loadDocument(input.execSql, input.localId),\n sqlDocumentsPersistence.listPendingUpdates(input.execSql, input.localId),\n loadDocumentInfoAttachments({\n execSql: input.execSql,\n localId: input.localId,\n }),\n db\n .select({ updatedAt: documentProjection.updatedAt })\n .from(documentProjection)\n .where(eq(documentProjection.localId, input.localId))\n .limit(1),\n loadPersistedDocumentContent({\n execSql: input.execSql,\n localId: input.localId,\n persistence: sqlDocumentsPersistence,\n }).catch(() => null),\n ]);\n\n if (!document) {\n return fallback;\n }\n\n const pendingAttachments = attachments.filter(\n (attachment) => attachment.attachmentKind === \"pending\",\n );\n\n return {\n attachments,\n // Blame reads op-level provenance out of a serialized snapshot; export the\n // durable-history content for it. Degrades to null (blame unavailable)\n // rather than failing the Info panel.\n contentSnapshot: contentDoc\n ? bytesToBase64(exportFullHistorySnapshot(contentDoc))\n : null,\n local: {\n accessEpoch: document.accessEpoch,\n accessStateHash: document.accessStateHash ?? null,\n containerId: document.containerId,\n documentId: document.documentId,\n documentKind: document.documentKind ?? DEFAULT_DOCUMENT_KIND,\n hasContentKeyBundle: Boolean(document.contentKeyBundle),\n hasDocumentKekTargets: Boolean(document.documentKekTargets),\n hasDocumentManifestBundle: Boolean(document.documentManifestBundle),\n lastCommitLsn: document.lastCommitLsn ?? null,\n localDocumentManifestHash: readStoredManifestHash(\n document.documentManifestBundle,\n ),\n localId: document.id,\n pendingAttachmentByteLength: pendingAttachments.reduce(\n (total, attachment) => total + attachment.byteLength,\n 0,\n ),\n pendingAttachmentCount: pendingAttachments.length,\n pendingUpdateCount: pendingUpdates.length,\n title: document.title ?? null,\n updatedAt: projectionRows[0]?.updatedAt ?? null,\n },\n };\n}\n\nfunction shouldLoadRemoteDocumentInfo(input: {\n local: DocumentInfoLocalDetails;\n remoteInfoMode: DocumentInfoRemoteMode;\n}): boolean {\n return input.remoteInfoMode !== \"never\" && Boolean(input.local.documentId);\n}\n\nfunction mapAttachmentBinding(\n binding: BlobAttachmentSummary,\n): DocumentInfoRemoteAttachmentBinding {\n return {\n bindingId: binding.bindingId,\n blobId: binding.blobId,\n slotId: binding.slotId,\n };\n}\n\nfunction mapAuthorizingContainerPath(\n projection: ContainerKeyingPathResponse,\n): DocumentInfoAuthorizingContainerPath {\n const leafBundle = projection.path.at(-1);\n const leafKek = projection.containerKeks.at(-1);\n\n return {\n containerId: projection.containerId,\n containerKeyEpoch: leafKek?.containerKeyEpoch ?? null,\n containerKeyEpochId: leafKek?.containerKeyEpochId ?? null,\n leafManifestHash: leafBundle?.manifestHash ?? null,\n organizationId: projection.organizationId,\n pathLength: projection.path.length,\n };\n}\n\nfunction getDocumentInfoRemoteDetails(input: {\n attachmentBindings: ReadonlyArray<BlobAttachmentSummary>;\n attribution: DocumentEditAttributionResponse | null;\n blameRanges: DocumentBlameRange[] | null;\n characterBlame: DocumentCharacterBlameSummary | null;\n contributors: DocumentContributor[];\n fieldBlame: DocumentFieldBlame[] | null;\n projection: DocumentWriterProjectionResponse;\n}): DocumentInfoRemoteDetails {\n const {\n attachmentBindings,\n attribution,\n blameRanges,\n characterBlame,\n contributors,\n fieldBlame,\n projection,\n } = input;\n const manifestState = projection.documentManifest.state;\n const manifest = projection.documentManifest.manifest;\n\n return {\n activeAttachmentBindings: attachmentBindings.map(mapAttachmentBinding),\n attributionRevision: attribution?.attributionRevision ?? null,\n attributionStatus: attribution\n ? attribution.truncated\n ? \"truncated\"\n : \"available\"\n : \"unavailable\",\n attributionSegments: attribution?.segments ?? [],\n authorizingContainerPaths: projection.authorizingContainerPaths.map(\n mapAuthorizingContainerPath,\n ),\n blameRanges,\n characterBlame,\n fieldBlame,\n contentKeyEpoch: projection.contentKeyBundle.contentKeyEpoch,\n contentKeyTargetCount: projection.contentKeyBundle.targets.length,\n contentKeyTargetHash: projection.contentKeyBundle.targetHash,\n contributors,\n currentManifestHash: projection.documentManifest.manifestHash,\n documentContainerManifestHistoryCount:\n projection.documentContainerManifestHistory.length,\n documentKekTargetCount: projection.documentKekTargets.targets.length,\n documentKeyTargetHash: projection.documentKekTargets.documentKeyTargetHash,\n documentManifestContainerPathCount:\n projection.documentManifestContainerPaths.length,\n documentManifestHistoryCount: projection.documentManifestHistory.length,\n linkedContainerKeyEpochCount:\n projection.documentKekTargets.linkedContainerKeyEpochIds.length,\n linkedContainerManifestCount:\n projection.documentKekTargets.linkedContainerManifestHashes.length,\n linkSetManifestHash: projection.documentKekTargets.linkSetManifestHash,\n manifestEpoch: readRecordNumber(manifest, \"epoch\"),\n previousManifestHash: readRecordString(\n manifestState,\n \"previousManifestHash\",\n ),\n referencedPrincipalCount: readRecordArrayLength(\n manifest,\n \"referencedPrincipalHeads\",\n ),\n };\n}\n\nexport async function loadDocumentInfo(input: {\n apiClient: DocumentInfoApi;\n attributionRequestKey?: string | undefined;\n execSql?: ExecSql | null;\n localId: string;\n reportSecurityIncident: SecurityIncidentReporter;\n remoteInfoMode?: DocumentInfoRemoteMode | undefined;\n}): Promise<DocumentInfo> {\n const { attachments, contentSnapshot, local } = await loadLocalDocumentInfo({\n execSql: input.execSql ?? null,\n localId: input.localId,\n });\n\n if (\n !shouldLoadRemoteDocumentInfo({\n local,\n remoteInfoMode: input.remoteInfoMode ?? \"if-synced\",\n }) ||\n !local.documentId\n ) {\n return { attachments, local, remoteInfo: null };\n }\n\n const attributionPromise = input.apiClient\n .getDocumentEditAttribution(local.documentId, input.attributionRequestKey)\n .catch(async (error: unknown) => {\n await reportAndRethrowKeyingVerificationError(\n error,\n input.reportSecurityIncident,\n {\n objectId: local.documentId ?? input.localId,\n objectKind: \"document\",\n operation: \"document.info.attribution\",\n },\n );\n return null;\n });\n const [projection, attachmentBindings, attribution] = await Promise.all([\n input.apiClient.getDocumentWriterProjection(local.documentId),\n input.apiClient.listDocumentAttachments(local.documentId),\n // Attribution is supplementary: a rejection (network/server) must not block\n // the rest of the document-info load, so swallow it to null (no contributors).\n attributionPromise,\n ]);\n\n if (!projection) {\n throw new Error(\n `Document info projection was unavailable for local document ${input.localId} with remote document ${local.documentId}.`,\n );\n }\n\n const attributionComplete = attribution && !attribution.truncated;\n const attributionSegments = attribution?.segments ?? [];\n const blame = attributionComplete\n ? computeDocumentBlame(contentSnapshot, attributionSegments)\n : null;\n return {\n attachments,\n local,\n remoteInfo: getDocumentInfoRemoteDetails({\n attachmentBindings: attachmentBindings ?? [],\n attribution,\n blameRanges: blame?.blameRanges ?? null,\n characterBlame: blame?.characterBlame ?? null,\n contributors: attributionComplete\n ? summarizeDocumentContributors(attributionSegments)\n : [],\n fieldBlame: attributionComplete\n ? computeFieldBlame(contentSnapshot, attributionSegments)\n : null,\n projection,\n }),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"documentInfo.js","sourceRoot":"","sources":["documentInfo.ts"],"names":[],"mappings":"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","sourcesContent":["import { bytesToBase64 } from \"@tearleads/encoding\";\nimport { exportFullHistorySnapshot } from \"@tearleads/loro\";\nimport type {\n BlobAttachmentSummary,\n ContainerKeyingPathResponse,\n DocumentEditAttributionResponse,\n DocumentWriterProjectionResponse,\n ListDocumentAttachmentsResponse,\n} from \"@tearleads/validators/response\";\nimport { and, eq, isNull } from \"drizzle-orm\";\nimport { DEFAULT_DOCUMENT_KIND } from \"../../../data/documents/documentConstants\";\nimport {\n type DocumentBlameRange,\n type DocumentCharacterBlameSummary,\n type DocumentContributor,\n type DocumentFieldBlame,\n summarizeDocumentContributors,\n} from \"../../../data/documents/editAttribution\";\nimport { reportAndRethrowKeyingVerificationError } from \"../../../data/keyingProjectionVerification/error\";\nimport { sqlDocumentsPersistence } from \"../../../data/persistence/documents/documentsPersistence\";\nimport type { SecurityIncidentReporter } from \"../../../data/shared/securityIncidents\";\nimport {\n documentAttachmentBlobProjection,\n documentPendingAttachments,\n documentProjection,\n} from \"../../../data/sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../../data/sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { loadPersistedDocumentContent } from \"../../documents\";\nimport { computeDocumentBlame } from \"../attribution/documentCharacterBlame\";\nimport { computeFieldBlame } from \"../attribution/documentFieldBlame\";\nimport type {\n DocumentInfo,\n DocumentInfoAttachment,\n DocumentInfoAuthorizingContainerPath,\n DocumentInfoLocalDetails,\n DocumentInfoRemoteAttachmentBinding,\n DocumentInfoRemoteDetails,\n DocumentInfoRemoteMode,\n} from \"./documentInfoTypes\";\n\nexport type {\n DocumentInfo,\n DocumentInfoAttachment,\n DocumentInfoAuthorizingContainerPath,\n DocumentInfoLocalDetails,\n DocumentInfoRemoteAttachmentBinding,\n DocumentInfoRemoteDetails,\n DocumentInfoRemoteMode,\n} from \"./documentInfoTypes\";\n\ninterface DocumentInfoApi {\n getDocumentEditAttribution: (\n documentId: string,\n requestKey?: string,\n ) => Promise<DocumentEditAttributionResponse | null>;\n getDocumentWriterProjection: (\n documentId: string,\n ) => Promise<DocumentWriterProjectionResponse | null>;\n listDocumentAttachments: (\n documentId: string,\n ) => Promise<ListDocumentAttachmentsResponse | null>;\n}\n\ninterface PendingAttachmentInfoRow {\n byteLength: number;\n createdAt: string;\n localId: string;\n mimeType: string | null;\n name: string;\n slotId: string;\n storageKey: string;\n}\n\ninterface LocalAttachmentInfoRow {\n blobId: string | null;\n byteLength: number;\n localId: string;\n mimeType: string | null;\n slotId: string;\n storageKey: string;\n updatedAt: string;\n}\n\nfunction readRecordString(value: unknown, key: string): string | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n\n const nextValue = Reflect.get(value, key);\n return typeof nextValue === \"string\" && nextValue.length > 0\n ? nextValue\n : null;\n}\n\nfunction readRecordNumber(value: unknown, key: string): number | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n\n const nextValue = Reflect.get(value, key);\n return typeof nextValue === \"number\" && Number.isFinite(nextValue)\n ? nextValue\n : null;\n}\n\nfunction readRecordArrayLength(value: unknown, key: string): number | null {\n if (!value || typeof value !== \"object\") {\n return null;\n }\n\n const nextValue = Reflect.get(value, key);\n return Array.isArray(nextValue) ? nextValue.length : null;\n}\n\nfunction readStoredManifestHash(\n value: string | null | undefined,\n): string | null {\n if (!value) {\n return null;\n }\n\n try {\n const parsed: unknown = JSON.parse(value);\n return readRecordString(parsed, \"manifestHash\");\n } catch {\n return null;\n }\n}\n\nfunction mapPendingAttachmentRow(\n row: PendingAttachmentInfoRow,\n): DocumentInfoAttachment {\n return {\n attachmentKind: \"pending\",\n blobId: null,\n byteLength: row.byteLength,\n createdAt: row.createdAt,\n localId: row.localId,\n mimeType: row.mimeType,\n name: row.name,\n slotId: row.slotId,\n storageKey: row.storageKey,\n updatedAt: null,\n };\n}\n\nfunction mapLocalAttachmentRow(\n row: LocalAttachmentInfoRow,\n): DocumentInfoAttachment {\n return {\n attachmentKind: \"local\",\n blobId: row.blobId,\n byteLength: row.byteLength,\n createdAt: null,\n localId: row.localId,\n mimeType: row.mimeType,\n name: null,\n slotId: row.slotId,\n storageKey: row.storageKey,\n updatedAt: row.updatedAt,\n };\n}\n\nasync function loadDocumentInfoAttachments(input: {\n execSql: ExecSql;\n localId: string;\n}): Promise<DocumentInfoAttachment[]> {\n const { db } = getClientSQLitePersistenceRuntime(input.execSql);\n const [pendingRows, localRows] = await Promise.all([\n db\n .select({\n byteLength: documentPendingAttachments.byteLength,\n createdAt: documentPendingAttachments.createdAt,\n localId: documentPendingAttachments.localId,\n mimeType: documentPendingAttachments.mimeType,\n name: documentPendingAttachments.name,\n slotId: documentPendingAttachments.slotId,\n storageKey: documentPendingAttachments.storageKey,\n })\n .from(documentPendingAttachments)\n .where(eq(documentPendingAttachments.localId, input.localId))\n .orderBy(\n documentPendingAttachments.createdAt,\n documentPendingAttachments.slotId,\n ),\n db\n .select({\n blobId: documentAttachmentBlobProjection.blobId,\n byteLength: documentAttachmentBlobProjection.byteLength,\n localId: documentAttachmentBlobProjection.localId,\n mimeType: documentAttachmentBlobProjection.mimeType,\n slotId: documentAttachmentBlobProjection.slotId,\n storageKey: documentAttachmentBlobProjection.storageKey,\n updatedAt: documentAttachmentBlobProjection.updatedAt,\n })\n .from(documentAttachmentBlobProjection)\n .where(\n and(\n eq(documentAttachmentBlobProjection.localId, input.localId),\n // Unlinked slots keep their row as a pending-detach marker; the\n // document no longer has the attachment, so do not report it.\n isNull(documentAttachmentBlobProjection.detachedAt),\n ),\n )\n .orderBy(\n documentAttachmentBlobProjection.updatedAt,\n documentAttachmentBlobProjection.slotId,\n ),\n ]);\n\n return [\n ...pendingRows.map(mapPendingAttachmentRow),\n ...localRows.map(mapLocalAttachmentRow),\n ];\n}\n\nasync function loadLocalDocumentInfo(input: {\n execSql: ExecSql | null;\n localId: string;\n}): Promise<{\n attachments: DocumentInfoAttachment[];\n contentSnapshot: string | null;\n local: DocumentInfoLocalDetails;\n}> {\n const fallback = {\n attachments: [],\n contentSnapshot: null,\n local: {\n accessEpoch: null,\n accessStateHash: null,\n containerId: null,\n documentId: null,\n documentKind: null,\n hasContentKeyBundle: false,\n hasDocumentKekTargets: false,\n hasDocumentManifestBundle: false,\n lastCommitLsn: null,\n localDocumentManifestHash: null,\n localId: input.localId,\n pendingAttachmentByteLength: 0,\n pendingAttachmentCount: 0,\n pendingUpdateCount: 0,\n title: null,\n updatedAt: null,\n },\n };\n\n if (!input.execSql) {\n return fallback;\n }\n\n await sqlDocumentsPersistence.ensureSchema(input.execSql);\n const { db } = getClientSQLitePersistenceRuntime(input.execSql);\n const [document, pendingUpdates, attachments, projectionRows, contentDoc] =\n await Promise.all([\n sqlDocumentsPersistence.loadDocument(input.execSql, input.localId),\n sqlDocumentsPersistence.listPendingUpdates(input.execSql, input.localId),\n loadDocumentInfoAttachments({\n execSql: input.execSql,\n localId: input.localId,\n }),\n db\n .select({ updatedAt: documentProjection.updatedAt })\n .from(documentProjection)\n .where(eq(documentProjection.localId, input.localId))\n .limit(1),\n loadPersistedDocumentContent({\n execSql: input.execSql,\n localId: input.localId,\n persistence: sqlDocumentsPersistence,\n }).catch(() => null),\n ]);\n\n if (!document) {\n return fallback;\n }\n\n const pendingAttachments = attachments.filter(\n (attachment) => attachment.attachmentKind === \"pending\",\n );\n\n return {\n attachments,\n // Blame reads op-level provenance out of a serialized snapshot; export the\n // durable-history content for it. Degrades to null (blame unavailable)\n // rather than failing the Info panel.\n contentSnapshot: contentDoc\n ? bytesToBase64(exportFullHistorySnapshot(contentDoc))\n : null,\n local: {\n accessEpoch: document.accessEpoch,\n accessStateHash: document.accessStateHash ?? null,\n containerId: document.containerId,\n documentId: document.documentId,\n documentKind: document.documentKind ?? DEFAULT_DOCUMENT_KIND,\n hasContentKeyBundle: Boolean(document.contentKeyBundle),\n hasDocumentKekTargets: Boolean(document.documentKekTargets),\n hasDocumentManifestBundle: Boolean(document.documentManifestBundle),\n lastCommitLsn: document.lastCommitLsn ?? null,\n localDocumentManifestHash: readStoredManifestHash(\n document.documentManifestBundle,\n ),\n localId: document.id,\n pendingAttachmentByteLength: pendingAttachments.reduce(\n (total, attachment) => total + attachment.byteLength,\n 0,\n ),\n pendingAttachmentCount: pendingAttachments.length,\n pendingUpdateCount: pendingUpdates.length,\n title: document.title ?? null,\n updatedAt: projectionRows[0]?.updatedAt ?? null,\n },\n };\n}\n\nfunction shouldLoadRemoteDocumentInfo(input: {\n local: DocumentInfoLocalDetails;\n remoteInfoMode: DocumentInfoRemoteMode;\n}): boolean {\n return input.remoteInfoMode !== \"never\" && Boolean(input.local.documentId);\n}\n\nfunction mapAttachmentBinding(\n binding: BlobAttachmentSummary,\n): DocumentInfoRemoteAttachmentBinding {\n return {\n bindingId: binding.bindingId,\n blobId: binding.blobId,\n slotId: binding.slotId,\n };\n}\n\nfunction mapAuthorizingContainerPath(\n projection: ContainerKeyingPathResponse,\n): DocumentInfoAuthorizingContainerPath {\n const leafBundle = projection.path.at(-1);\n const leafKek = projection.containerKeks.at(-1);\n\n return {\n containerId: projection.containerId,\n containerKeyEpoch: leafKek?.containerKeyEpoch ?? null,\n containerKeyEpochId: leafKek?.containerKeyEpochId ?? null,\n leafManifestHash: leafBundle?.manifestHash ?? null,\n organizationId: projection.organizationId,\n pathLength: projection.path.length,\n };\n}\n\nfunction getDocumentInfoRemoteDetails(input: {\n attachmentBindings: ReadonlyArray<BlobAttachmentSummary>;\n attribution: DocumentEditAttributionResponse | null;\n blameRanges: DocumentBlameRange[] | null;\n characterBlame: DocumentCharacterBlameSummary | null;\n contributors: DocumentContributor[];\n fieldBlame: DocumentFieldBlame[] | null;\n projection: DocumentWriterProjectionResponse;\n}): DocumentInfoRemoteDetails {\n const {\n attachmentBindings,\n attribution,\n blameRanges,\n characterBlame,\n contributors,\n fieldBlame,\n projection,\n } = input;\n const manifestState = projection.documentManifest.state;\n const manifest = projection.documentManifest.manifest;\n\n return {\n activeAttachmentBindings: attachmentBindings.map(mapAttachmentBinding),\n attributionRevision: attribution?.attributionRevision ?? null,\n attributionStatus: attribution\n ? attribution.truncated\n ? \"truncated\"\n : \"available\"\n : \"unavailable\",\n attributionSegments: attribution?.segments ?? [],\n authorizingContainerPaths: projection.authorizingContainerPaths.map(\n mapAuthorizingContainerPath,\n ),\n blameRanges,\n characterBlame,\n fieldBlame,\n contentKeyEpoch: projection.contentKeyBundle.contentKeyEpoch,\n contentKeyTargetCount: projection.contentKeyBundle.targets.length,\n contentKeyTargetHash: projection.contentKeyBundle.targetHash,\n contributors,\n currentManifestHash: projection.documentManifest.manifestHash,\n documentContainerManifestHistoryCount:\n projection.documentContainerManifestHistory.length,\n documentKekTargetCount: projection.documentKekTargets.targets.length,\n documentKeyTargetHash: projection.documentKekTargets.documentKeyTargetHash,\n documentManifestContainerPathCount:\n projection.documentManifestContainerPaths.length,\n documentManifestHistoryCount: projection.documentManifestHistory.length,\n linkedContainerKeyEpochCount:\n projection.documentKekTargets.linkedContainerKeyEpochIds.length,\n linkedContainerManifestCount:\n projection.documentKekTargets.linkedContainerManifestHashes.length,\n linkSetManifestHash: projection.documentKekTargets.linkSetManifestHash,\n manifestEpoch: readRecordNumber(manifest, \"epoch\"),\n previousManifestHash: readRecordString(\n manifestState,\n \"previousManifestHash\",\n ),\n referencedPrincipalCount: readRecordArrayLength(\n manifest,\n \"referencedPrincipalHeads\",\n ),\n };\n}\n\nexport async function loadDocumentInfo(input: {\n apiClient: DocumentInfoApi;\n attributionRequestKey?: string | undefined;\n execSql?: ExecSql | null;\n localId: string;\n reportSecurityIncident: SecurityIncidentReporter;\n remoteInfoMode?: DocumentInfoRemoteMode | undefined;\n}): Promise<DocumentInfo> {\n const { attachments, contentSnapshot, local } = await loadLocalDocumentInfo({\n execSql: input.execSql ?? null,\n localId: input.localId,\n });\n\n if (\n !shouldLoadRemoteDocumentInfo({\n local,\n remoteInfoMode: input.remoteInfoMode ?? \"if-synced\",\n }) ||\n !local.documentId\n ) {\n return { attachments, local, remoteInfo: null };\n }\n\n const attributionPromise = input.apiClient\n .getDocumentEditAttribution(local.documentId, input.attributionRequestKey)\n .catch(async (error: unknown) => {\n await reportAndRethrowKeyingVerificationError(\n error,\n input.reportSecurityIncident,\n {\n objectId: local.documentId ?? input.localId,\n objectKind: \"document\",\n operation: \"document.info.attribution\",\n },\n );\n return null;\n });\n const [projection, attachmentBindings, attribution] = await Promise.all([\n input.apiClient.getDocumentWriterProjection(local.documentId),\n input.apiClient.listDocumentAttachments(local.documentId),\n // Attribution is supplementary: a rejection (network/server) must not block\n // the rest of the document-info load, so swallow it to null (no contributors).\n attributionPromise,\n ]);\n\n if (!projection) {\n throw new Error(\n `Document info projection was unavailable for local document ${input.localId} with remote document ${local.documentId}.`,\n );\n }\n\n const attributionComplete = attribution && !attribution.truncated;\n const attributionSegments = attribution?.segments ?? [];\n const blame = attributionComplete\n ? computeDocumentBlame(contentSnapshot, attributionSegments)\n : null;\n return {\n attachments,\n local,\n remoteInfo: getDocumentInfoRemoteDetails({\n attachmentBindings: attachmentBindings ?? [],\n attribution,\n blameRanges: blame?.blameRanges ?? null,\n characterBlame: blame?.characterBlame ?? null,\n contributors: attributionComplete\n ? summarizeDocumentContributors(attributionSegments)\n : [],\n fieldBlame: attributionComplete\n ? computeFieldBlame(contentSnapshot, attributionSegments)\n : null,\n projection,\n }),\n };\n}\n"]}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { encodeVersionVector, exportFullHistorySnapshot, } from "../../../internal/loro/index.js";
|
|
2
|
-
import { readPullContinuation } from "../../../data/documents/shared/sync/syncPagination.js";
|
|
3
2
|
import { settleOutgoingPassAndDecideReArm } from "../../../data/sync/outgoingUpdateSettlement.js";
|
|
4
|
-
import { shouldClearDocumentSyncFailureAfterPass } from "../../documents/index.js";
|
|
3
|
+
import { readPullContinuation, shouldClearDocumentSyncFailureAfterPass, } from "../../documents/index.js";
|
|
5
4
|
import { applyIncomingContainerMetadataUpdates } from "./metadataIncomingUpdateIsolation.js";
|
|
6
5
|
import { currentMetadataPullContinuation, hasCurrentContainerMetadataReadState, installContainerMetadataRecord, persistContainerMetadataStateFromRuntime, } from "./metadataPersistence.js";
|
|
7
6
|
import { syncRemoteContainerMetadata, } from "./metadataRemoteSync.js";
|