@tearleads/client-sdk 0.1.76 → 0.1.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/{documents/blob/shared → blobs/attachments}/blobEnvelopeStream.js +1 -1
- package/data/{documents/blob/shared → blobs/attachments}/blobEnvelopeStream.js.map +1 -1
- package/data/blobs/attachments/blobRecordCrypto.d.ts +26 -0
- package/data/{documents/blob/shared → blobs/attachments}/blobRecordCrypto.js +2 -2
- package/data/{documents/blob/shared → blobs/attachments}/blobSourceSnapshot.d.ts +1 -1
- package/data/{documents/blob/shared → blobs/attachments}/blobSourceSnapshot.js +2 -2
- package/data/blobs/attachments/blobSourceSnapshot.js.map +1 -0
- package/data/blobs/attachments/chunkedBlobCrypto.d.ts +17 -0
- package/data/{documents/blob/shared → blobs/attachments}/chunkedBlobCrypto.js +3 -3
- package/data/blobs/attachments/chunkedBlobCrypto.js.map +1 -0
- package/data/blobs/attachments/crypto.d.ts +3 -0
- package/data/blobs/attachments/encryptedUploadParts.js.map +1 -0
- package/data/{documents/blob/shared → blobs/attachments}/events.js +3 -3
- package/data/blobs/attachments/events.js.map +1 -0
- package/data/{documents/blob/shared → blobs/attachments}/projection.js +8 -8
- package/data/blobs/attachments/projection.js.map +1 -0
- package/data/{documents/blob/shared → blobs/attachments}/readers.js +3 -3
- package/data/blobs/attachments/readers.js.map +1 -0
- package/data/{documents/blob/shared → blobs/attachments}/responses.js +2 -2
- package/data/blobs/attachments/responses.js.map +1 -0
- package/data/{documents/blob/shared → blobs/attachments}/types.d.ts +8 -8
- package/data/blobs/attachments/types.js.map +1 -0
- package/data/{containers → persistence/container-contents}/containerBinding.js +1 -1
- package/data/persistence/container-contents/containerContentsPersistence.js +2 -2
- package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerContentsPersistenceTypes.d.ts +2 -2
- package/data/persistence/container-contents/containerContentsPersistenceTypes.js.map +1 -1
- package/data/persistence/container-contents/containerDeletionPersistence.js +2 -2
- package/data/persistence/container-contents/containerDeletionPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerHydrationPersistence.js +2 -2
- package/data/persistence/container-contents/containerHydrationPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js +2 -2
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMetadataRows.js +1 -1
- package/data/persistence/container-contents/containerMetadataRows.js.map +1 -1
- package/data/persistence/container-contents/containerRebindPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerReconcilePersistence.js +2 -2
- package/data/persistence/container-contents/containerReconcilePersistence.js.map +1 -1
- package/data/persistence/container-contents/containerStructuralRepair.js +1 -1
- package/data/persistence/container-contents/containerStructuralRepair.js.map +1 -1
- package/data/persistence/container-contents/dormantMetadataSweep.js +1 -1
- package/data/persistence/container-contents/dormantMetadataSweep.js.map +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstoneHolds.js +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstoneHolds.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/discoveredDocumentPlacement.js +1 -1
- package/data/persistence/documents/internal/discoveredDocumentPlacement.js.map +1 -1
- package/package.json +1 -1
- package/stores/container-contents/systemContainers/systemContainerIcon.js +1 -1
- package/stores/container-contents/systemContainers/systemContainerIcon.js.map +1 -1
- package/stores/documents/documentStore/attachmentUpload/attachmentSourceUpload.js +1 -2
- package/stores/documents/documentStore/attachmentUpload/attachmentSourceUpload.js.map +1 -1
- package/stores/documents/documentStore/sync/syncAttachments.js.map +1 -1
- package/workflows/blobs/detach.js +2 -2
- package/workflows/blobs/detach.js.map +1 -1
- package/workflows/blobs/hydration/blobChunkDecryption.js +1 -1
- package/workflows/blobs/hydration/blobChunkDecryption.js.map +1 -1
- package/workflows/blobs/hydration/decrypt.d.ts +1 -1
- package/workflows/blobs/hydration/decrypt.js +4 -4
- package/workflows/blobs/hydration/decrypt.js.map +1 -1
- package/workflows/blobs/index.d.ts +3 -1
- package/workflows/blobs/index.js +3 -0
- package/workflows/blobs/index.js.map +1 -1
- package/workflows/blobs/upload/automaticMultipartUpload.js +1 -1
- package/workflows/blobs/upload/automaticMultipartUpload.js.map +1 -1
- package/workflows/blobs/upload/index.d.ts +2 -2
- package/workflows/blobs/upload/index.js +4 -4
- package/workflows/blobs/upload/index.js.map +1 -1
- package/workflows/blobs/upload/multipartFirstParts.js.map +1 -1
- package/workflows/blobs/upload/multipartStageFailures.js.map +1 -1
- package/workflows/blobs/upload/multipartStageResolution.js.map +1 -1
- package/workflows/blobs/upload/multipartUpload.js.map +1 -1
- package/workflows/blobs/verification/attachmentBindingVerification.js.map +1 -1
- package/workflows/blobs/verification/attachmentKeyAuthenticator.js +1 -1
- package/workflows/blobs/verification/attachmentKeyAuthenticator.js.map +1 -1
- package/workflows/blobs/verification/retainedAttachmentVerification.js +1 -1
- package/workflows/blobs/verification/retainedAttachmentVerification.js.map +1 -1
- package/workflows/blobs/verification/writerProjection.js +1 -1
- package/workflows/blobs/verification/writerProjection.js.map +1 -1
- package/workflows/container-contents/container-state/create/createChild.js +1 -1
- package/workflows/container-contents/container-state/create/createChild.js.map +1 -1
- package/workflows/container-contents/container-state/purge/purgeTree.js +1 -1
- package/workflows/container-contents/container-state/purge/purgeTree.js.map +1 -1
- package/workflows/container-contents/container-state/types.js.map +1 -1
- package/workflows/container-contents/containerPersistence.d.ts +3 -3
- package/workflows/container-contents/containerPersistence.js +1 -1
- package/workflows/container-contents/containerPersistence.js.map +1 -1
- package/workflows/container-contents/documentLinks/documentLinkIntent.js +1 -1
- package/workflows/container-contents/documentLinks/documentLinkIntent.js.map +1 -1
- package/workflows/container-contents/documentLinks/index.js +1 -1
- package/workflows/container-contents/documentLinks/index.js.map +1 -1
- package/workflows/container-contents/documentMove/documentMoveIntent.js +1 -1
- package/workflows/container-contents/documentMove/documentMoveIntent.js.map +1 -1
- package/workflows/container-contents/documentMove/documentMoveIntentSettlement.js +2 -2
- package/workflows/container-contents/documentMove/documentMoveIntentSettlement.js.map +1 -1
- package/workflows/container-contents/documentQueries/documentSubtreeQueries.js +1 -1
- package/workflows/container-contents/documentQueries/documentSubtreeQueries.js.map +1 -1
- package/workflows/container-contents/documentQueries/index.js +3 -3
- package/workflows/container-contents/documentQueries/index.js.map +1 -1
- package/workflows/container-contents/documentQueries/pendingDownloads.js +1 -1
- package/workflows/container-contents/documentQueries/pendingDownloads.js.map +1 -1
- package/workflows/container-contents/index.d.ts +1 -1
- package/workflows/container-contents/index.js +1 -1
- package/workflows/container-contents/index.js.map +1 -1
- package/workflows/container-contents/info/blobInfo.js +1 -1
- package/workflows/container-contents/info/blobInfo.js.map +1 -1
- package/workflows/container-contents/info/containerInfo.js +2 -2
- package/workflows/container-contents/info/containerInfo.js.map +1 -1
- package/{data/containers → workflows/container-contents/metadata}/containerMetadataDocument.d.ts +1 -1
- package/{data/containers → workflows/container-contents/metadata}/containerMetadataDocument.js +2 -2
- package/workflows/container-contents/metadata/containerMetadataDocument.js.map +1 -0
- package/workflows/container-contents/metadata/metadataMutationPreparation.js +1 -1
- package/workflows/container-contents/metadata/metadataMutationPreparation.js.map +1 -1
- package/workflows/container-contents/metadata/metadataPersistence.js +1 -1
- package/workflows/container-contents/metadata/metadataPersistence.js.map +1 -1
- package/workflows/container-contents/metadata/metadataRecord.js.map +1 -1
- package/workflows/container-contents/metadata/metadataStateIsolation.js +1 -1
- package/workflows/container-contents/metadata/metadataStateIsolation.js.map +1 -1
- package/workflows/container-contents/metadata/metadataSyncSettlement.js +1 -1
- package/workflows/container-contents/metadata/metadataSyncSettlement.js.map +1 -1
- package/workflows/container-contents/metadata/metadataTypes.d.ts +2 -2
- package/workflows/container-contents/metadata/metadataTypes.js.map +1 -1
- package/workflows/container-contents/recovery/folderRecovery.js +1 -1
- package/workflows/container-contents/recovery/folderRecovery.js.map +1 -1
- package/workflows/container-contents/remoteHydration/destination/replacementBinding.js +1 -1
- package/workflows/container-contents/remoteHydration/destination/replacementBinding.js.map +1 -1
- package/workflows/container-contents/remoteHydration/destination/verifiedDestination.js +1 -1
- package/workflows/container-contents/remoteHydration/destination/verifiedDestination.js.map +1 -1
- package/workflows/container-contents/remoteHydration/heldContainerRebind.js +1 -1
- package/workflows/container-contents/remoteHydration/heldContainerRebind.js.map +1 -1
- package/workflows/container-contents/remoteHydration/reattachMetadata.js +1 -1
- package/workflows/container-contents/remoteHydration/reattachMetadata.js.map +1 -1
- package/workflows/container-contents/remoteHydration/remoteContainers/insertRemoteContainer.js +2 -2
- package/workflows/container-contents/remoteHydration/remoteContainers/insertRemoteContainer.js.map +1 -1
- package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerState.js +1 -1
- package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerState.js.map +1 -1
- package/workflows/container-contents/remoteHydration/types.d.ts +1 -1
- package/workflows/container-contents/remoteHydration/types.js.map +1 -1
- package/workflows/container-contents/storedContainerState.js +1 -1
- package/workflows/container-contents/storedContainerState.js.map +1 -1
- package/workflows/organizations/groupMetadata/groupMetadataAccess.js +1 -1
- package/workflows/organizations/groupMetadata/groupMetadataAccess.js.map +1 -1
- package/workflows/organizations/readModel/localReadModelDetails.js +1 -1
- package/workflows/organizations/readModel/localReadModelDetails.js.map +1 -1
- package/workflows/registration/bootstrapRootContainer.js +1 -1
- package/workflows/registration/bootstrapRootContainer.js.map +1 -1
- package/workflows/registration/provisioningBootstrapBuilders.js +1 -1
- package/workflows/registration/provisioningBootstrapBuilders.js.map +1 -1
- package/workflows/registration/rootMetadataBootstrap.js +1 -1
- package/workflows/registration/rootMetadataBootstrap.js.map +1 -1
- package/workflows/sync/remoteReset.js +1 -1
- package/workflows/sync/remoteReset.js.map +1 -1
- package/data/containers/containerMetadataDocument.js.map +0 -1
- package/data/documents/blob/shared/blobSourceSnapshot.js.map +0 -1
- package/data/documents/blob/shared/chunkedBlobCrypto.js.map +0 -1
- package/data/documents/blob/shared/encryptedUploadParts.js.map +0 -1
- package/data/documents/blob/shared/events.js.map +0 -1
- package/data/documents/blob/shared/projection.js.map +0 -1
- package/data/documents/blob/shared/readers.js.map +0 -1
- package/data/documents/blob/shared/responses.js.map +0 -1
- package/data/documents/blob/shared/types.js.map +0 -1
- /package/data/{documents/blob/shared → blobs/attachments}/blobRecordCrypto.js.map +0 -0
- /package/data/{documents/blob/shared → blobs/attachments}/crypto.js +0 -0
- /package/data/{documents/blob/shared → blobs/attachments}/crypto.js.map +0 -0
- /package/data/{documents/blob/shared → blobs/attachments}/encryptedUploadParts.js +0 -0
- /package/data/{documents/blob/shared → blobs/attachments}/types.js +0 -0
- /package/data/{containers → persistence/container-contents}/containerBinding.d.ts +0 -0
- /package/data/{containers → persistence/container-contents}/containerBinding.js.map +0 -0
- /package/data/persistence/{containers → container-contents}/containerPersistence.d.ts +0 -0
- /package/data/persistence/{containers → container-contents}/containerPersistence.js +0 -0
- /package/data/persistence/{containers → container-contents}/containerPersistence.js.map +0 -0
- /package/data/persistence/{containers → container-contents}/containerSyncWatermarkPersistence.d.ts +0 -0
- /package/data/persistence/{containers → container-contents}/containerSyncWatermarkPersistence.js +0 -0
- /package/data/persistence/{containers → container-contents}/containerSyncWatermarkPersistence.js.map +0 -0
- /package/data/persistence/{containers → container-contents}/documentContainerProjectionPersistence.js +0 -0
- /package/data/persistence/{containers → container-contents}/documentContainerProjectionPersistence.js.map +0 -0
- /package/data/persistence/{containers → container-contents}/documentPlacement.js +0 -0
- /package/data/persistence/{containers → container-contents}/documentPlacement.js.map +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multipartStageResolution.js","sourceRoot":"","sources":["multipartStageResolution.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n InitiateMultipartBlobStageResponse,\n MultipartBlobStageStatusResponse,\n} from \"@tearleads/validators/response\";\nimport type { BlobEncryptionPlan } from \"../../../data/
|
|
1
|
+
{"version":3,"file":"multipartStageResolution.js","sourceRoot":"","sources":["multipartStageResolution.ts"],"names":[],"mappings":"AASA,OAAO,EAIL,0BAA0B,EAC1B,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,0BAA0B,CAAC;AAElC,SAAS,sBAAsB,CAC7B,KAA4E;IAE5E,OAAO,WAAW,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC;AACtE,CAAC;AAmBD,KAAK,UAAU,oBAAoB,CACjC,KAAiC,EACjC,aAAqB;IAErB,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,qBAAqB,CAAC,aAAa,CAAC,CAAC;IAC3E,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,+BAA+B,CAAC;YACpC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;YAC5C,OAAO,EAAE,aAAa;SACvB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,KAAK,aAAa,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,uDAAuD,aAAa,GAAG,CACxE,CAAC;IACJ,CAAC;IACD,sEAAsE;IACtE,wEAAwE;IACxE,yCAAyC;IACzC,IACE,OAAO,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;QAC/C,OAAO,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,CAAC,UAAU;QAClD,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,UAAU,CAAC,MAAM,EAC1C,CAAC;QACD,MAAM,IAAI,KAAK,CACb,+BAA+B,aAAa,sDAAsD,CACnG,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,KAAiC,EACjC,UAA+B;IAE/B,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC;IAChD,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,0BAA0B,CAAC;QAC7D,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,UAAU;QACV,UAAU,EAAE,CAAC,GAAG,UAAU,CAAC,WAAW,CAAC;QACvC,MAAM;KACP,CAAC,CAAC;IACH,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,0BAA0B,CAAC;YACzB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,QAAQ,EAAE,8CAA8C,UAAU,CAAC,cAAc,EAAE,SAAS;YAC5F,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,EAAE;SAC7D,CAAC,CACH,CAAC;IACJ,CAAC;IACD,IACE,KAAK,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;QAC7C,KAAK,CAAC,UAAU,KAAK,UAAU;QAC/B,KAAK,CAAC,MAAM,KAAK,MAAM,EACvB,CAAC;QACD,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;IACJ,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,OAAO,EAAE,qBAAqB,CAAC,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC;QAC7D,MAAM,EAAE,sBAAsB,CAAC,KAAK,CAAC;YACnC,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE;KACnC,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,4EAA4E;AAC5E,4EAA4E;AAC5E,KAAK,UAAU,sBAAsB,CACnC,KAAiC;IAEjC,MAAM,UAAU,GAAG,MAAM,0BAA0B,CACjD,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,WAAW,CAClB,CAAC;IACF,IAAI,CAAC;QACH,OAAO,MAAM,sBAAsB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACzD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,oBAAoB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAAiC;IAEjC,MAAM,aAAa,GAAG,KAAK,CAAC,SAAS,CAAC,aAAa,CAAC;IACpD,MAAM,OAAO,GAAG,aAAa;QAC3B,CAAC,CAAC,MAAM,oBAAoB,CAAC,KAAK,EAAE,aAAa,CAAC;QAClD,CAAC,CAAC,IAAI,CAAC;IACT,OAAO,OAAO;QACZ,CAAC,CAAC;YACE,QAAQ,EAAE,IAAI,GAAG,EAAE;YACnB,OAAO,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE;YACnD,MAAM,EAAE,OAAO;SAChB;QACH,CAAC,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;AACpC,CAAC","sourcesContent":["import type {\n InitiateMultipartBlobStageResponse,\n MultipartBlobStageStatusResponse,\n} from \"@tearleads/validators/response\";\nimport type { BlobEncryptionPlan } from \"../../../data/blobs/attachments/crypto\";\nimport type {\n BlobAttachmentApi,\n UploadDocumentAttachmentInput,\n} from \"../../../data/blobs/attachments/types\";\nimport {\n type MultipartFirstParts,\n type MultipartPartSources,\n type PreparedFirstPart,\n prepareFirstMultipartParts,\n readIssuedPartSources,\n releasePreparedParts,\n} from \"./multipartFirstParts\";\nimport {\n assertMultipartStageReplaceable,\n multipartApiFailureMessage,\n} from \"./multipartStageFailures\";\n\nfunction isMultipartStageStatus(\n stage: InitiateMultipartBlobStageResponse | MultipartBlobStageStatusResponse,\n): stage is MultipartBlobStageStatusResponse {\n return \"completed\" in stage && typeof stage.completed === \"boolean\";\n}\n\ninterface ResolveMultipartStageInput {\n readonly apiClient: BlobAttachmentApi;\n readonly concurrency: number;\n readonly encryption: BlobEncryptionPlan;\n readonly multipart: NonNullable<UploadDocumentAttachmentInput[\"multipart\"]>;\n readonly organizationId: string;\n readonly onStageUnavailable?:\n | ((stageId: string) => Promise<void>)\n | undefined;\n}\n\ninterface ResolvedMultipartStage {\n readonly prepared: Map<number, PreparedFirstPart>;\n readonly sources: MultipartPartSources;\n readonly status: MultipartBlobStageStatusResponse;\n}\n\nasync function resumeMultipartStage(\n input: ResolveMultipartStageInput,\n resumeStageId: string,\n): Promise<MultipartBlobStageStatusResponse | null> {\n const resumed = await input.apiClient.getMultipartBlobStage(resumeStageId);\n if (!resumed) {\n await assertMultipartStageReplaceable({\n apiClient: input.apiClient,\n onStageUnavailable: input.onStageUnavailable,\n stageId: resumeStageId,\n });\n return null;\n }\n if (resumed.stageId !== resumeStageId) {\n throw new Error(\n `Multipart blob resume response does not match stage ${resumeStageId}.`,\n );\n }\n // A successful lookup is authoritative: a metadata mismatch indicates\n // inconsistent persisted state or a bad server response, not proof that\n // the existing stage is safe to abandon.\n if (\n resumed.organizationId !== input.organizationId ||\n resumed.byteLength !== input.encryption.byteLength ||\n resumed.sha256 !== input.encryption.sha256\n ) {\n throw new Error(\n `Multipart blob resume stage ${resumeStageId} does not match the requested organization or bytes.`,\n );\n }\n return resumed;\n}\n\nasync function initiateWithFirstParts(\n input: ResolveMultipartStageInput,\n firstParts: MultipartFirstParts,\n): Promise<ResolvedMultipartStage> {\n const { byteLength, sha256 } = input.encryption;\n const stage = await input.apiClient.initiateMultipartBlobStage({\n organizationId: input.organizationId,\n byteLength,\n firstParts: [...firstParts.descriptors],\n sha256,\n });\n if (!stage) {\n throw new Error(\n multipartApiFailureMessage({\n apiClient: input.apiClient,\n fallback: `Multipart blob stage initiation failed for ${byteLength.toLocaleString()} bytes.`,\n request: { method: \"POST\", path: \"/blobs/stages/multipart\" },\n }),\n );\n }\n if (\n stage.organizationId !== input.organizationId ||\n stage.byteLength !== byteLength ||\n stage.sha256 !== sha256\n ) {\n throw new Error(\n \"Multipart blob stage initiation response does not match its request.\",\n );\n }\n return {\n prepared: firstParts.prepared,\n sources: readIssuedPartSources(firstParts.descriptors, stage),\n status: isMultipartStageStatus(stage)\n ? stage\n : { ...stage, completed: false },\n };\n}\n\n// A new stage carries the parts the upload starts with, so their upload\n// sources come back with the stage instead of one authorization each. Their\n// bytes hold transfer-pool slots until uploaded, or until initiation fails.\nasync function initiateMultipartStage(\n input: ResolveMultipartStageInput,\n): Promise<ResolvedMultipartStage> {\n const firstParts = await prepareFirstMultipartParts(\n input.apiClient,\n input.encryption,\n input.concurrency,\n );\n try {\n return await initiateWithFirstParts(input, firstParts);\n } catch (error) {\n releasePreparedParts(firstParts.prepared);\n throw error;\n }\n}\n\nexport async function resolveMultipartStage(\n input: ResolveMultipartStageInput,\n): Promise<ResolvedMultipartStage> {\n const resumeStageId = input.multipart.resumeStageId;\n const resumed = resumeStageId\n ? await resumeMultipartStage(input, resumeStageId)\n : null;\n return resumed\n ? {\n prepared: new Map(),\n sources: { apiTransport: false, issued: new Map() },\n status: resumed,\n }\n : initiateMultipartStage(input);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multipartUpload.js","sourceRoot":"","sources":["multipartUpload.ts"],"names":[],"mappings":"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","sourcesContent":["import { MAX_BLOB_CHUNK_COUNT } from \"@tearleads/crypto\";\nimport type { MultipartBlobStagePart } from \"@tearleads/validators/response\";\nimport { MAX_MULTIPART_BLOB_PART_BYTES } from \"@tearleads/validators/util\";\nimport {\n BLOB_TRANSFER_CONCURRENCY,\n runBlobTransfer,\n} from \"../../../data/blobs/transferPool\";\nimport type { BlobEncryptionPlan } from \"../../../data/documents/blob/shared/crypto\";\nimport type {\n BlobAttachmentApi,\n MultipartStageResolvedListener,\n MultipartUploadProgressListener,\n UploadDocumentAttachmentInput,\n} from \"../../../data/documents/blob/shared/types\";\nimport {\n type MultipartPartSources,\n type PreparedFirstPart,\n type PreparedMultipartPart,\n releasePreparedParts,\n sha256BytesHex,\n takePartSource,\n} from \"./multipartFirstParts\";\nimport {\n multipartApiFailureMessage,\n multipartStagePath,\n} from \"./multipartStageFailures\";\nimport { resolveMultipartStage } from \"./multipartStageResolution\";\n\nconst DEFAULT_MULTIPART_UPLOAD_CONCURRENCY = 4;\nconst MAX_MULTIPART_UPLOAD_CONCURRENCY = 4;\n// Fetch exposes no upload progress on every host. Allow a slow shared uplink\n// (8 KiB/s total, shared by every admitted part), plus setup/encryption time.\nconst MULTIPART_TRANSFER_BYTES_PER_SECOND = 8 * 1024;\n\ninterface MultipartPartCommit {\n readonly etag: string;\n readonly partNumber: number;\n}\n\ninterface MultipartPartUploadTask {\n readonly byteLength: number;\n readonly partIndex: number;\n readonly partNumber: number;\n}\n\nfunction pathSegment(value: number | string): string {\n return encodeURIComponent(String(value));\n}\n\nfunction multipartPartRequest(input: {\n readonly apiClient: BlobAttachmentApi;\n readonly partNumber: number;\n readonly stageId: string;\n}) {\n const basePath = `${multipartStagePath(input.stageId)}/parts/${pathSegment(input.partNumber)}`;\n const authorization = { method: \"POST\" as const, path: `${basePath}/upload` };\n // A successful authorization clears its previous failure. If a memory-store\n // byte request then fails, its separate transport diagnostic is authoritative.\n return input.apiClient.getRequestFailure?.(authorization)\n ? authorization\n : { method: \"PUT\" as const, path: `${basePath}/bytes` };\n}\n\nfunction normalizeMultipartUploadConcurrency(\n value: number | undefined,\n): number {\n if (value === undefined) {\n return DEFAULT_MULTIPART_UPLOAD_CONCURRENCY;\n }\n if (\n !Number.isInteger(value) ||\n value <= 0 ||\n value > MAX_MULTIPART_UPLOAD_CONCURRENCY\n ) {\n throw new Error(\n `Multipart blob upload concurrency must be between 1 and ${MAX_MULTIPART_UPLOAD_CONCURRENCY}`,\n );\n }\n\n return value;\n}\n\nfunction isMultipartPartCommit(\n part: MultipartPartCommit | undefined,\n): part is MultipartPartCommit {\n return part !== undefined;\n}\n\nasync function uploadMultipartPartTasks(input: {\n readonly apiClient: BlobAttachmentApi;\n readonly completeParts: (MultipartPartCommit | undefined)[];\n readonly concurrency: number;\n readonly encryption: BlobEncryptionPlan;\n readonly onPartUploaded?: (task: MultipartPartUploadTask) => void;\n readonly prepared: Map<number, PreparedFirstPart>;\n readonly sources: MultipartPartSources;\n readonly stageId: string;\n readonly tasks: readonly MultipartPartUploadTask[];\n readonly uploadId: string;\n}): Promise<void> {\n let failed = false;\n let firstFailure: { readonly error: unknown } | undefined;\n let nextTaskIndex = 0;\n const recordFailure = (error: unknown) => {\n failed = true;\n firstFailure ??= { error };\n };\n\n async function worker(): Promise<void> {\n while (!failed) {\n const task = input.tasks[nextTaskIndex];\n nextTaskIndex += 1;\n if (!task) {\n return;\n }\n\n try {\n // A first part already holds the pool slot its prepared bytes use.\n const heldPermit = input.prepared.get(task.partIndex)?.permit;\n await runBlobTransfer(\n input.apiClient,\n async (signal) => {\n if (failed) return;\n const aborted = () => recordFailure(signal.reason);\n signal.addEventListener(\"abort\", aborted, { once: true });\n try {\n const uploaded = await uploadMultipartPartBytes({\n apiClient: input.apiClient,\n encryption: input.encryption,\n prepared: input.prepared,\n sources: input.sources,\n stageId: input.stageId,\n task,\n uploadId: input.uploadId,\n signal,\n });\n signal.throwIfAborted();\n if (!uploaded) {\n failed = true;\n throw new Error(\n multipartApiFailureMessage({\n apiClient: input.apiClient,\n fallback: `Multipart blob part ${task.partNumber} upload failed for stage ${input.stageId}.`,\n request: multipartPartRequest({\n apiClient: input.apiClient,\n partNumber: task.partNumber,\n stageId: input.stageId,\n }),\n }),\n );\n }\n\n input.completeParts[task.partIndex] = {\n etag: uploaded.part.etag,\n partNumber: task.partNumber,\n };\n input.onPartUploaded?.(task);\n } catch (error) {\n // Publish failure before releasing the permit to a queued sibling.\n // Already-started parts still record success and drain through the join.\n recordFailure(error);\n } finally {\n signal.removeEventListener(\"abort\", aborted);\n }\n },\n 120_000 +\n Math.ceil(\n (task.byteLength * BLOB_TRANSFER_CONCURRENCY * 1000) /\n MULTIPART_TRANSFER_BYTES_PER_SECOND,\n ),\n heldPermit,\n );\n } catch (error) {\n recordFailure(error);\n }\n }\n }\n\n const workerCount = Math.min(input.concurrency, input.tasks.length);\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n if (firstFailure) {\n throw firstFailure.error;\n }\n}\n\n// A first part was encrypted once before the stage existed; hand its bytes\n// over (and release them) instead of encrypting it again.\nasync function encryptedPart(input: {\n readonly encryption: BlobEncryptionPlan;\n readonly prepared: Map<number, PreparedFirstPart>;\n readonly task: MultipartPartUploadTask;\n}): Promise<PreparedMultipartPart> {\n const prepared = input.prepared.get(input.task.partIndex);\n if (prepared) {\n input.prepared.delete(input.task.partIndex);\n return prepared;\n }\n const bytes = await input.encryption.encryptPart(input.task.partIndex);\n if (bytes.byteLength !== input.task.byteLength) {\n throw new Error(\n `Encrypted blob part ${input.task.partNumber} byte length mismatch: expected ${input.task.byteLength.toLocaleString()}, received ${bytes.byteLength.toLocaleString()}.`,\n );\n }\n return { bytes, sha256: await sha256BytesHex(bytes) };\n}\n\nasync function uploadMultipartPartBytes(input: {\n readonly apiClient: BlobAttachmentApi;\n readonly encryption: BlobEncryptionPlan;\n readonly prepared: Map<number, PreparedFirstPart>;\n readonly sources: MultipartPartSources;\n readonly stageId: string;\n readonly task: MultipartPartUploadTask;\n readonly uploadId: string;\n readonly signal: AbortSignal;\n}): ReturnType<BlobAttachmentApi[\"uploadMultipartBlobPartBytes\"]> {\n input.signal.throwIfAborted();\n const { bytes: encryptedPartBytes, sha256 } = await encryptedPart(input);\n input.signal.throwIfAborted();\n const source = takePartSource(input.sources, input.task.partNumber);\n const uploaded = await input.apiClient.uploadMultipartBlobPartBytes(\n input.stageId,\n input.task.partNumber,\n {\n byteLength: encryptedPartBytes.byteLength,\n encryptedBytes: encryptedPartBytes,\n sha256,\n ...(source === undefined ? {} : { source }),\n uploadId: input.uploadId,\n },\n { signal: input.signal },\n );\n input.signal.throwIfAborted();\n if (\n uploaded &&\n (uploaded.stageId !== input.stageId ||\n uploaded.uploadId !== input.uploadId ||\n uploaded.part.partNumber !== input.task.partNumber ||\n uploaded.part.byteLength !== input.task.byteLength)\n ) {\n throw new Error(\n `Multipart blob part ${input.task.partNumber} response does not match its request.`,\n );\n }\n return uploaded;\n}\n\nfunction uploadedPartsByPartNumber(\n parts: readonly MultipartBlobStagePart[],\n): ReadonlyMap<number, MultipartBlobStagePart> {\n return new Map(parts.map((part) => [part.partNumber, part]));\n}\n\ninterface MultipartUploadProgressState {\n bytesTotal: number;\n bytesUploaded: number;\n partsCompleted: number;\n partsTotal: number;\n}\n\ninterface MultipartPartPlan {\n completeParts: (MultipartPartCommit | undefined)[];\n progress: MultipartUploadProgressState;\n uploadTasks: MultipartPartUploadTask[];\n}\n\n/**\n * Plans encrypted part indices without materializing their bytes, reusing any\n * already-staged parts and seeding cumulative progress for resumed uploads.\n */\nfunction planMultipartParts(input: {\n encryption: BlobEncryptionPlan;\n uploadedParts: ReadonlyMap<number, MultipartBlobStagePart>;\n}): MultipartPartPlan {\n const { encryption, uploadedParts } = input;\n const completeParts: (MultipartPartCommit | undefined)[] = new Array(\n encryption.partCount,\n );\n const uploadTasks: MultipartPartUploadTask[] = [];\n const progress: MultipartUploadProgressState = {\n bytesTotal: 0,\n bytesUploaded: 0,\n partsCompleted: 0,\n partsTotal: encryption.partCount,\n };\n\n for (let partIndex = 0; partIndex < encryption.partCount; partIndex += 1) {\n const partNumber = partIndex + 1;\n const byteLength = encryption.getPartByteLength(partIndex);\n progress.bytesTotal += byteLength;\n const uploadedPart = uploadedParts.get(partNumber);\n if (uploadedPart?.byteLength === byteLength) {\n completeParts[partIndex] = {\n etag: uploadedPart.etag,\n partNumber,\n };\n progress.partsCompleted += 1;\n progress.bytesUploaded += byteLength;\n continue;\n }\n\n uploadTasks.push({\n byteLength,\n partIndex,\n partNumber,\n });\n }\n\n return { completeParts, progress, uploadTasks };\n}\n\nfunction assertMultipartPlanLimits(encryption: BlobEncryptionPlan): void {\n if (encryption.partCount > MAX_BLOB_CHUNK_COUNT) {\n throw new Error(\n `Multipart blob upload would require ${encryption.partCount.toLocaleString()} parts; the maximum is ${MAX_BLOB_CHUNK_COUNT.toLocaleString()} parts.`,\n );\n }\n for (let index = 0; index < encryption.partCount; index += 1) {\n if (encryption.getPartByteLength(index) > MAX_MULTIPART_BLOB_PART_BYTES) {\n throw new Error(\n `Encrypted blob part ${index + 1} exceeds the 32 MiB multipart part limit.`,\n );\n }\n }\n}\n\ntype StageMultipartBlobAttachmentInput = {\n readonly apiClient: BlobAttachmentApi;\n readonly encryption: BlobEncryptionPlan;\n readonly multipart: NonNullable<UploadDocumentAttachmentInput[\"multipart\"]>;\n readonly organizationId: string;\n readonly onMultipartProgress?: MultipartUploadProgressListener | undefined;\n readonly onStageResolved?: MultipartStageResolvedListener | undefined;\n readonly onStageUnavailable?:\n | ((stageId: string) => Promise<void>)\n | undefined;\n};\n\nexport async function stageMultipartBlobAttachment(\n input: StageMultipartBlobAttachmentInput,\n): Promise<string | null> {\n if (input.multipart.partSize !== input.encryption.chunkSize) {\n throw new Error(\n \"Multipart part size must match the encryption chunk size exactly.\",\n );\n }\n assertMultipartPlanLimits(input.encryption);\n const concurrency = normalizeMultipartUploadConcurrency(\n input.multipart.uploadConcurrency,\n );\n const resolved = await resolveMultipartStage({\n apiClient: input.apiClient,\n concurrency,\n encryption: input.encryption,\n multipart: input.multipart,\n organizationId: input.organizationId,\n onStageUnavailable: input.onStageUnavailable,\n });\n try {\n return await uploadResolvedMultipartStage(input, concurrency, resolved);\n } finally {\n // First parts no worker consumed (an early failure) return their slots.\n releasePreparedParts(resolved.prepared);\n }\n}\n\nasync function uploadResolvedMultipartStage(\n input: StageMultipartBlobAttachmentInput,\n concurrency: number,\n {\n prepared,\n sources,\n status,\n }: Awaited<ReturnType<typeof resolveMultipartStage>>,\n): Promise<string> {\n // Surface the stage id before any parts upload so the caller can persist it;\n // an upload interrupted mid-flight can then resume this same stage.\n await input.onStageResolved?.({\n partSize: input.encryption.chunkSize,\n stageId: status.stageId,\n });\n if (status.completed) {\n input.onMultipartProgress?.({\n bytesTotal: input.encryption.byteLength,\n bytesUploaded: input.encryption.byteLength,\n partsCompleted: input.encryption.partCount,\n partsTotal: input.encryption.partCount,\n });\n return status.stageId;\n }\n\n const { completeParts, progress, uploadTasks } = planMultipartParts({\n encryption: input.encryption,\n uploadedParts: uploadedPartsByPartNumber(status.uploadedParts),\n });\n\n const reportMultipartProgress = () => {\n input.onMultipartProgress?.({\n bytesTotal: progress.bytesTotal,\n bytesUploaded: progress.bytesUploaded,\n partsCompleted: progress.partsCompleted,\n partsTotal: progress.partsTotal,\n });\n };\n reportMultipartProgress();\n\n await uploadMultipartPartTasks({\n apiClient: input.apiClient,\n completeParts,\n concurrency,\n encryption: input.encryption,\n onPartUploaded: (task) => {\n progress.partsCompleted += 1;\n progress.bytesUploaded += task.byteLength;\n reportMultipartProgress();\n },\n prepared,\n sources,\n stageId: status.stageId,\n tasks: uploadTasks,\n uploadId: status.uploadId,\n });\n const committedParts = completeParts.filter(isMultipartPartCommit);\n if (committedParts.length !== input.encryption.partCount) {\n throw new Error(\n `Multipart blob upload incomplete for stage ${status.stageId}: committed ${committedParts.length.toLocaleString()} of ${input.encryption.partCount.toLocaleString()} parts.`,\n );\n }\n\n // Binding the attachment completes the stage: the API assembles these parts\n // and verifies the object's length and SHA-256 before promoting it.\n return status.stageId;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"multipartUpload.js","sourceRoot":"","sources":["multipartUpload.ts"],"names":[],"mappings":"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","sourcesContent":["import { MAX_BLOB_CHUNK_COUNT } from \"@tearleads/crypto\";\nimport type { MultipartBlobStagePart } from \"@tearleads/validators/response\";\nimport { MAX_MULTIPART_BLOB_PART_BYTES } from \"@tearleads/validators/util\";\nimport type { BlobEncryptionPlan } from \"../../../data/blobs/attachments/crypto\";\nimport type {\n BlobAttachmentApi,\n MultipartStageResolvedListener,\n MultipartUploadProgressListener,\n UploadDocumentAttachmentInput,\n} from \"../../../data/blobs/attachments/types\";\nimport {\n BLOB_TRANSFER_CONCURRENCY,\n runBlobTransfer,\n} from \"../../../data/blobs/transferPool\";\nimport {\n type MultipartPartSources,\n type PreparedFirstPart,\n type PreparedMultipartPart,\n releasePreparedParts,\n sha256BytesHex,\n takePartSource,\n} from \"./multipartFirstParts\";\nimport {\n multipartApiFailureMessage,\n multipartStagePath,\n} from \"./multipartStageFailures\";\nimport { resolveMultipartStage } from \"./multipartStageResolution\";\n\nconst DEFAULT_MULTIPART_UPLOAD_CONCURRENCY = 4;\nconst MAX_MULTIPART_UPLOAD_CONCURRENCY = 4;\n// Fetch exposes no upload progress on every host. Allow a slow shared uplink\n// (8 KiB/s total, shared by every admitted part), plus setup/encryption time.\nconst MULTIPART_TRANSFER_BYTES_PER_SECOND = 8 * 1024;\n\ninterface MultipartPartCommit {\n readonly etag: string;\n readonly partNumber: number;\n}\n\ninterface MultipartPartUploadTask {\n readonly byteLength: number;\n readonly partIndex: number;\n readonly partNumber: number;\n}\n\nfunction pathSegment(value: number | string): string {\n return encodeURIComponent(String(value));\n}\n\nfunction multipartPartRequest(input: {\n readonly apiClient: BlobAttachmentApi;\n readonly partNumber: number;\n readonly stageId: string;\n}) {\n const basePath = `${multipartStagePath(input.stageId)}/parts/${pathSegment(input.partNumber)}`;\n const authorization = { method: \"POST\" as const, path: `${basePath}/upload` };\n // A successful authorization clears its previous failure. If a memory-store\n // byte request then fails, its separate transport diagnostic is authoritative.\n return input.apiClient.getRequestFailure?.(authorization)\n ? authorization\n : { method: \"PUT\" as const, path: `${basePath}/bytes` };\n}\n\nfunction normalizeMultipartUploadConcurrency(\n value: number | undefined,\n): number {\n if (value === undefined) {\n return DEFAULT_MULTIPART_UPLOAD_CONCURRENCY;\n }\n if (\n !Number.isInteger(value) ||\n value <= 0 ||\n value > MAX_MULTIPART_UPLOAD_CONCURRENCY\n ) {\n throw new Error(\n `Multipart blob upload concurrency must be between 1 and ${MAX_MULTIPART_UPLOAD_CONCURRENCY}`,\n );\n }\n\n return value;\n}\n\nfunction isMultipartPartCommit(\n part: MultipartPartCommit | undefined,\n): part is MultipartPartCommit {\n return part !== undefined;\n}\n\nasync function uploadMultipartPartTasks(input: {\n readonly apiClient: BlobAttachmentApi;\n readonly completeParts: (MultipartPartCommit | undefined)[];\n readonly concurrency: number;\n readonly encryption: BlobEncryptionPlan;\n readonly onPartUploaded?: (task: MultipartPartUploadTask) => void;\n readonly prepared: Map<number, PreparedFirstPart>;\n readonly sources: MultipartPartSources;\n readonly stageId: string;\n readonly tasks: readonly MultipartPartUploadTask[];\n readonly uploadId: string;\n}): Promise<void> {\n let failed = false;\n let firstFailure: { readonly error: unknown } | undefined;\n let nextTaskIndex = 0;\n const recordFailure = (error: unknown) => {\n failed = true;\n firstFailure ??= { error };\n };\n\n async function worker(): Promise<void> {\n while (!failed) {\n const task = input.tasks[nextTaskIndex];\n nextTaskIndex += 1;\n if (!task) {\n return;\n }\n\n try {\n // A first part already holds the pool slot its prepared bytes use.\n const heldPermit = input.prepared.get(task.partIndex)?.permit;\n await runBlobTransfer(\n input.apiClient,\n async (signal) => {\n if (failed) return;\n const aborted = () => recordFailure(signal.reason);\n signal.addEventListener(\"abort\", aborted, { once: true });\n try {\n const uploaded = await uploadMultipartPartBytes({\n apiClient: input.apiClient,\n encryption: input.encryption,\n prepared: input.prepared,\n sources: input.sources,\n stageId: input.stageId,\n task,\n uploadId: input.uploadId,\n signal,\n });\n signal.throwIfAborted();\n if (!uploaded) {\n failed = true;\n throw new Error(\n multipartApiFailureMessage({\n apiClient: input.apiClient,\n fallback: `Multipart blob part ${task.partNumber} upload failed for stage ${input.stageId}.`,\n request: multipartPartRequest({\n apiClient: input.apiClient,\n partNumber: task.partNumber,\n stageId: input.stageId,\n }),\n }),\n );\n }\n\n input.completeParts[task.partIndex] = {\n etag: uploaded.part.etag,\n partNumber: task.partNumber,\n };\n input.onPartUploaded?.(task);\n } catch (error) {\n // Publish failure before releasing the permit to a queued sibling.\n // Already-started parts still record success and drain through the join.\n recordFailure(error);\n } finally {\n signal.removeEventListener(\"abort\", aborted);\n }\n },\n 120_000 +\n Math.ceil(\n (task.byteLength * BLOB_TRANSFER_CONCURRENCY * 1000) /\n MULTIPART_TRANSFER_BYTES_PER_SECOND,\n ),\n heldPermit,\n );\n } catch (error) {\n recordFailure(error);\n }\n }\n }\n\n const workerCount = Math.min(input.concurrency, input.tasks.length);\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n if (firstFailure) {\n throw firstFailure.error;\n }\n}\n\n// A first part was encrypted once before the stage existed; hand its bytes\n// over (and release them) instead of encrypting it again.\nasync function encryptedPart(input: {\n readonly encryption: BlobEncryptionPlan;\n readonly prepared: Map<number, PreparedFirstPart>;\n readonly task: MultipartPartUploadTask;\n}): Promise<PreparedMultipartPart> {\n const prepared = input.prepared.get(input.task.partIndex);\n if (prepared) {\n input.prepared.delete(input.task.partIndex);\n return prepared;\n }\n const bytes = await input.encryption.encryptPart(input.task.partIndex);\n if (bytes.byteLength !== input.task.byteLength) {\n throw new Error(\n `Encrypted blob part ${input.task.partNumber} byte length mismatch: expected ${input.task.byteLength.toLocaleString()}, received ${bytes.byteLength.toLocaleString()}.`,\n );\n }\n return { bytes, sha256: await sha256BytesHex(bytes) };\n}\n\nasync function uploadMultipartPartBytes(input: {\n readonly apiClient: BlobAttachmentApi;\n readonly encryption: BlobEncryptionPlan;\n readonly prepared: Map<number, PreparedFirstPart>;\n readonly sources: MultipartPartSources;\n readonly stageId: string;\n readonly task: MultipartPartUploadTask;\n readonly uploadId: string;\n readonly signal: AbortSignal;\n}): ReturnType<BlobAttachmentApi[\"uploadMultipartBlobPartBytes\"]> {\n input.signal.throwIfAborted();\n const { bytes: encryptedPartBytes, sha256 } = await encryptedPart(input);\n input.signal.throwIfAborted();\n const source = takePartSource(input.sources, input.task.partNumber);\n const uploaded = await input.apiClient.uploadMultipartBlobPartBytes(\n input.stageId,\n input.task.partNumber,\n {\n byteLength: encryptedPartBytes.byteLength,\n encryptedBytes: encryptedPartBytes,\n sha256,\n ...(source === undefined ? {} : { source }),\n uploadId: input.uploadId,\n },\n { signal: input.signal },\n );\n input.signal.throwIfAborted();\n if (\n uploaded &&\n (uploaded.stageId !== input.stageId ||\n uploaded.uploadId !== input.uploadId ||\n uploaded.part.partNumber !== input.task.partNumber ||\n uploaded.part.byteLength !== input.task.byteLength)\n ) {\n throw new Error(\n `Multipart blob part ${input.task.partNumber} response does not match its request.`,\n );\n }\n return uploaded;\n}\n\nfunction uploadedPartsByPartNumber(\n parts: readonly MultipartBlobStagePart[],\n): ReadonlyMap<number, MultipartBlobStagePart> {\n return new Map(parts.map((part) => [part.partNumber, part]));\n}\n\ninterface MultipartUploadProgressState {\n bytesTotal: number;\n bytesUploaded: number;\n partsCompleted: number;\n partsTotal: number;\n}\n\ninterface MultipartPartPlan {\n completeParts: (MultipartPartCommit | undefined)[];\n progress: MultipartUploadProgressState;\n uploadTasks: MultipartPartUploadTask[];\n}\n\n/**\n * Plans encrypted part indices without materializing their bytes, reusing any\n * already-staged parts and seeding cumulative progress for resumed uploads.\n */\nfunction planMultipartParts(input: {\n encryption: BlobEncryptionPlan;\n uploadedParts: ReadonlyMap<number, MultipartBlobStagePart>;\n}): MultipartPartPlan {\n const { encryption, uploadedParts } = input;\n const completeParts: (MultipartPartCommit | undefined)[] = new Array(\n encryption.partCount,\n );\n const uploadTasks: MultipartPartUploadTask[] = [];\n const progress: MultipartUploadProgressState = {\n bytesTotal: 0,\n bytesUploaded: 0,\n partsCompleted: 0,\n partsTotal: encryption.partCount,\n };\n\n for (let partIndex = 0; partIndex < encryption.partCount; partIndex += 1) {\n const partNumber = partIndex + 1;\n const byteLength = encryption.getPartByteLength(partIndex);\n progress.bytesTotal += byteLength;\n const uploadedPart = uploadedParts.get(partNumber);\n if (uploadedPart?.byteLength === byteLength) {\n completeParts[partIndex] = {\n etag: uploadedPart.etag,\n partNumber,\n };\n progress.partsCompleted += 1;\n progress.bytesUploaded += byteLength;\n continue;\n }\n\n uploadTasks.push({\n byteLength,\n partIndex,\n partNumber,\n });\n }\n\n return { completeParts, progress, uploadTasks };\n}\n\nfunction assertMultipartPlanLimits(encryption: BlobEncryptionPlan): void {\n if (encryption.partCount > MAX_BLOB_CHUNK_COUNT) {\n throw new Error(\n `Multipart blob upload would require ${encryption.partCount.toLocaleString()} parts; the maximum is ${MAX_BLOB_CHUNK_COUNT.toLocaleString()} parts.`,\n );\n }\n for (let index = 0; index < encryption.partCount; index += 1) {\n if (encryption.getPartByteLength(index) > MAX_MULTIPART_BLOB_PART_BYTES) {\n throw new Error(\n `Encrypted blob part ${index + 1} exceeds the 32 MiB multipart part limit.`,\n );\n }\n }\n}\n\ntype StageMultipartBlobAttachmentInput = {\n readonly apiClient: BlobAttachmentApi;\n readonly encryption: BlobEncryptionPlan;\n readonly multipart: NonNullable<UploadDocumentAttachmentInput[\"multipart\"]>;\n readonly organizationId: string;\n readonly onMultipartProgress?: MultipartUploadProgressListener | undefined;\n readonly onStageResolved?: MultipartStageResolvedListener | undefined;\n readonly onStageUnavailable?:\n | ((stageId: string) => Promise<void>)\n | undefined;\n};\n\nexport async function stageMultipartBlobAttachment(\n input: StageMultipartBlobAttachmentInput,\n): Promise<string | null> {\n if (input.multipart.partSize !== input.encryption.chunkSize) {\n throw new Error(\n \"Multipart part size must match the encryption chunk size exactly.\",\n );\n }\n assertMultipartPlanLimits(input.encryption);\n const concurrency = normalizeMultipartUploadConcurrency(\n input.multipart.uploadConcurrency,\n );\n const resolved = await resolveMultipartStage({\n apiClient: input.apiClient,\n concurrency,\n encryption: input.encryption,\n multipart: input.multipart,\n organizationId: input.organizationId,\n onStageUnavailable: input.onStageUnavailable,\n });\n try {\n return await uploadResolvedMultipartStage(input, concurrency, resolved);\n } finally {\n // First parts no worker consumed (an early failure) return their slots.\n releasePreparedParts(resolved.prepared);\n }\n}\n\nasync function uploadResolvedMultipartStage(\n input: StageMultipartBlobAttachmentInput,\n concurrency: number,\n {\n prepared,\n sources,\n status,\n }: Awaited<ReturnType<typeof resolveMultipartStage>>,\n): Promise<string> {\n // Surface the stage id before any parts upload so the caller can persist it;\n // an upload interrupted mid-flight can then resume this same stage.\n await input.onStageResolved?.({\n partSize: input.encryption.chunkSize,\n stageId: status.stageId,\n });\n if (status.completed) {\n input.onMultipartProgress?.({\n bytesTotal: input.encryption.byteLength,\n bytesUploaded: input.encryption.byteLength,\n partsCompleted: input.encryption.partCount,\n partsTotal: input.encryption.partCount,\n });\n return status.stageId;\n }\n\n const { completeParts, progress, uploadTasks } = planMultipartParts({\n encryption: input.encryption,\n uploadedParts: uploadedPartsByPartNumber(status.uploadedParts),\n });\n\n const reportMultipartProgress = () => {\n input.onMultipartProgress?.({\n bytesTotal: progress.bytesTotal,\n bytesUploaded: progress.bytesUploaded,\n partsCompleted: progress.partsCompleted,\n partsTotal: progress.partsTotal,\n });\n };\n reportMultipartProgress();\n\n await uploadMultipartPartTasks({\n apiClient: input.apiClient,\n completeParts,\n concurrency,\n encryption: input.encryption,\n onPartUploaded: (task) => {\n progress.partsCompleted += 1;\n progress.bytesUploaded += task.byteLength;\n reportMultipartProgress();\n },\n prepared,\n sources,\n stageId: status.stageId,\n tasks: uploadTasks,\n uploadId: status.uploadId,\n });\n const committedParts = completeParts.filter(isMultipartPartCommit);\n if (committedParts.length !== input.encryption.partCount) {\n throw new Error(\n `Multipart blob upload incomplete for stage ${status.stageId}: committed ${committedParts.length.toLocaleString()} of ${input.encryption.partCount.toLocaleString()} parts.`,\n );\n }\n\n // Binding the attachment completes the stage: the API assembles these parts\n // and verifies the object's length and SHA-256 before promoting it.\n return status.stageId;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachmentBindingVerification.js","sourceRoot":"","sources":["attachmentBindingVerification.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n verifyAttachmentBindingEvent,\n} from \"@tearleads/crypto\";\nimport type { DecryptDocumentAttachmentBlobInput } from \"../../../data/
|
|
1
|
+
{"version":3,"file":"attachmentBindingVerification.js","sourceRoot":"","sources":["attachmentBindingVerification.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n verifyAttachmentBindingEvent,\n} from \"@tearleads/crypto\";\nimport type { DecryptDocumentAttachmentBlobInput } from \"../../../data/blobs/attachments/types\";\nimport {\n readCanonicalJson,\n readCanonicalRecord,\n} from \"../../../data/keying/canonicalJson\";\nimport {\n type DocumentWriterProjectionAuthorization,\n type ProjectionUserKeyResolver,\n readAccessEvent,\n readRecordString,\n readRequiredRecordValue,\n resolveEventContainerPaths,\n} from \"../../../data/keyingProjectionVerification\";\n\nexport async function assertAttachmentBindingVerified(input: {\n readonly authorization: DocumentWriterProjectionAuthorization | undefined;\n readonly binding: Pick<\n DecryptDocumentAttachmentBlobInput[\"binding\"],\n | \"bindingEvent\"\n | \"documentManifestHash\"\n | \"previousBindingId\"\n | \"bindingId\"\n | \"blobId\"\n >;\n readonly expectedDocumentId: string;\n readonly expectedSlotId: string;\n readonly resolveProjectionUserKey: ProjectionUserKeyResolver;\n}): Promise<void> {\n if (!input.authorization) {\n throw new Error(\"Attachment binding lacks verified document authority\");\n }\n if (\n !input.binding.bindingEvent ||\n !input.binding.documentManifestHash ||\n input.binding.previousBindingId === undefined\n ) {\n throw new Error(\"Attachment binding lacks signed verification material\");\n }\n const eventBundle = readCanonicalRecord(\n input.binding.bindingEvent,\n \"Attachment binding event bundle\",\n );\n const event = readAccessEvent(\n readRequiredRecordValue(\n eventBundle,\n \"event\",\n \"Attachment binding event bundle\",\n ),\n \"Attachment binding event\",\n );\n const signer = await input.resolveProjectionUserKey(event.signerUserId);\n if (!signer) {\n throw new Error(\"Attachment binding signer identity is unavailable\");\n }\n const documentManifest = input.authorization.documentManifestByHash.get(\n input.binding.documentManifestHash,\n );\n if (!documentManifest) {\n throw new Error(\"Attachment binding document manifest is unverified\");\n }\n const { dependencyContainerPaths } = resolveEventContainerPaths({\n containerPathByManifestHash:\n input.authorization.containerPathByManifestHash,\n dependencyManifestHashes: event.dependencyManifestHashes.filter(\n (hash) => hash !== input.binding.documentManifestHash,\n ),\n });\n const verified = await verifyAttachmentBindingEvent({\n authorizationMembership: \"referenced\",\n authorizingContainerPaths: dependencyContainerPaths,\n body: readCanonicalJson(\n readRequiredRecordValue(\n eventBundle,\n \"body\",\n \"Attachment binding event bundle\",\n ),\n \"Attachment binding event body\",\n ),\n documentManifest,\n event,\n expectedBindingId: input.binding.bindingId,\n expectedBlobId: input.binding.blobId,\n expectedDocumentId: input.expectedDocumentId,\n expectedDocumentManifestHash: input.binding.documentManifestHash,\n expectedPreviousBindingId: input.binding.previousBindingId,\n principalPolicies: input.authorization.principalPolicies,\n signerPublicKey: signer.signingPublicKey,\n });\n if (!verified.ok) {\n throw verified.error;\n }\n if (\n verified.value.slotId !== input.expectedSlotId ||\n readRecordString(\n eventBundle,\n \"eventHash\",\n \"Attachment binding event bundle\",\n ) !== verified.value.event.eventHash\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Attachment binding slot or event hash is inconsistent\",\n );\n }\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AES_GCM_TAG_BYTES, blobChunkPlaintextByteLength, bytesToHex, } from "../../../internal/crypto/index.js";
|
|
2
|
-
import { createBlobEnvelopeStream } from "../../../data/
|
|
2
|
+
import { createBlobEnvelopeStream } from "../../../data/blobs/attachments/blobEnvelopeStream.js";
|
|
3
3
|
import { assertEqualBytes, readWriteHeader, } from "../../../data/documents/shared/readers.js";
|
|
4
4
|
import { decryptBlobChunk } from "../hydration/blobChunkDecryption.js";
|
|
5
5
|
import { prepareDocumentAttachmentBlobDecryption } from "../hydration/decrypt.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachmentKeyAuthenticator.js","sourceRoot":"","sources":["attachmentKeyAuthenticator.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,4BAA4B,EAC5B,UAAU,GACX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,wBAAwB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"attachmentKeyAuthenticator.js","sourceRoot":"","sources":["attachmentKeyAuthenticator.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AES_GCM_TAG_BYTES,\n blobChunkPlaintextByteLength,\n bytesToHex,\n} from \"@tearleads/crypto\";\nimport { createBlobEnvelopeStream } from \"../../../data/blobs/attachments/blobEnvelopeStream\";\nimport type {\n BlobEncryptedBytesHeader,\n DecryptDocumentAttachmentBlobInput,\n} from \"../../../data/blobs/attachments/types\";\nimport {\n assertEqualBytes,\n readWriteHeader,\n} from \"../../../data/documents/shared/readers\";\nimport type { DocumentLinkSetMutationApi } from \"../../../data/documents/shared/types\";\nimport { decryptBlobChunk } from \"../hydration/blobChunkDecryption\";\nimport { prepareDocumentAttachmentBlobDecryption } from \"../hydration/decrypt\";\nimport { createAttachmentProofReader } from \"./attachmentDecryptor\";\n\ntype AttachmentKeyInput = Omit<\n DecryptDocumentAttachmentBlobInput,\n \"encryptedBytes\"\n>;\ntype VerifiedKey = {\n ciphertextHash: string;\n encrypted: BlobEncryptedBytesHeader;\n contentKey: Uint8Array;\n};\ntype ProofReader = ReturnType<typeof createProofReader>;\n\nconst createProofReader = (\n api: DocumentLinkSetMutationApi,\n documentId: string,\n) =>\n createAttachmentProofReader(\n api,\n documentId,\n prepareDocumentAttachmentBlobDecryption,\n );\n\nasync function authenticateAttachmentKey(\n api: DocumentLinkSetMutationApi,\n input: AttachmentKeyInput,\n readProof: ProofReader,\n): Promise<VerifiedKey> {\n const blob = await api.getBlobBytes(input.binding.blobId);\n if (!blob)\n throw new Error(\"Attachment ciphertext is unavailable for relinking\");\n const body = new Response(blob.encryptedBytes).body;\n if (!body) throw new Error(\"Attachment ciphertext stream is unavailable\");\n const source = createBlobEnvelopeStream(body);\n try {\n const encrypted = await source.readHeader();\n // A missing citation can refresh the proof before consuming ciphertext.\n const context = await readProof({ ...input, encrypted });\n for (let index = 0; index < encrypted.chunkCount; index++) {\n const plaintextByteLength = blobChunkPlaintextByteLength({\n chunkCount: encrypted.chunkCount,\n chunkIndex: index,\n chunkSize: encrypted.chunkSize,\n plaintextByteLength: encrypted.byteLength,\n });\n const ciphertext = await source.read(\n plaintextByteLength + AES_GCM_TAG_BYTES,\n );\n await decryptBlobChunk({\n ...context,\n encrypted,\n expectedBlobId: input.binding.blobId,\n chunk: { ciphertext, index, plaintextByteLength },\n });\n }\n const header = readWriteHeader(\n input.binding.writeHeader,\n \"Attachment blob write header\",\n );\n if (bytesToHex(await source.finish()) !== header.ciphertextHash)\n throw new Error(\"Attachment blob write header does not match ciphertext\");\n return {\n encrypted,\n contentKey: context.contentKey,\n ciphertextHash: header.ciphertextHash,\n };\n } finally {\n await source.close();\n }\n}\n\n/** Shared blobs retain only authenticated keys and metadata, never their bytes. */\nexport function createAttachmentKeyAuthenticator(\n api: DocumentLinkSetMutationApi,\n documentId: string,\n) {\n const readProof = createProofReader(api, documentId);\n const authenticated = new Map<string, Promise<VerifiedKey>>();\n return async (input: AttachmentKeyInput): Promise<Uint8Array> => {\n const existing = authenticated.get(input.binding.blobId);\n if (!existing) {\n const pending = authenticateAttachmentKey(api, input, readProof);\n authenticated.set(input.binding.blobId, pending);\n return (await pending).contentKey;\n }\n const cached = await existing;\n const context = await readProof({ ...input, encrypted: cached.encrypted });\n if (\n readWriteHeader(input.binding.writeHeader, \"Attachment blob write header\")\n .ciphertextHash !== cached.ciphertextHash\n )\n throw new Error(\"Attachment blob write header does not match ciphertext\");\n assertEqualBytes(\n context.contentKey,\n cached.contentKey,\n \"Attachment bindings contain conflicting blob keys\",\n );\n return cached.contentKey;\n };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { KeyingVerificationError } from "../../../internal/crypto/index.js";
|
|
2
|
-
import { assertBlobContentKeyBundleTargetHash } from "../../../data/
|
|
2
|
+
import { assertBlobContentKeyBundleTargetHash } from "../../../data/blobs/attachments/responses.js";
|
|
3
3
|
import { assertDocumentWriterProjectionConsistent } from "../../../data/documents/shared/projection/index.js";
|
|
4
4
|
import { projectionVerificationOptions } from "../../../data/documents/shared/types.js";
|
|
5
5
|
import { requireProjectionUserKeyResolver, } from "../../../data/keyingProjectionVerification/index.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retainedAttachmentVerification.js","sourceRoot":"","sources":["retainedAttachmentVerification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,oCAAoC,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"retainedAttachmentVerification.js","sourceRoot":"","sources":["retainedAttachmentVerification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,oCAAoC,EAAE,MAAM,2CAA2C,CAAC;AAEjG,OAAO,EAAE,wCAAwC,EAAE,MAAM,2CAA2C,CAAC;AACrG,OAAO,EAAE,6BAA6B,EAAE,MAAM,sCAAsC,CAAC;AACrF,OAAO,EAEL,gCAAgC,GACjC,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAElE,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAGC;IAED,MAAM,wBAAwB,GAAG,gCAAgC,CAC/D,KAAK,CAAC,wBAAwB,EAC9B,2BAA2B,CAC5B,CAAC;IACF,IAAI,aAAgE,CAAC;IACrE,MAAM,wCAAwC,CAAC,KAAK,CAAC,gBAAgB,EAAE;QACrE,0BAA0B,EAAE,IAAI;QAChC,GAAG,6BAA6B,CAAC;YAC/B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,+BAA+B,EAAE,KAAK,CAAC,+BAA+B;YACtE,wBAAwB;SACzB,CAAC;QACF,uBAAuB,EAAE,CAAC,KAAK,EAAE,EAAE;YACjC,aAAa,GAAG,KAAK,CAAC;QACxB,CAAC;KACF,CAAC,CAAC;IACH,IAAI,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,MAAM,KAAK,KAAK,CAAC,OAAO,CAAC,MAAM;QAChE,MAAM,IAAI,uBAAuB,CAC/B,iBAAiB,EACjB,+CAA+C,CAChD,CAAC;IACJ,MAAM,oCAAoC,CAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC3E,MAAM,+BAA+B,CAAC;QACpC,GAAG,KAAK;QACR,aAAa;QACb,wBAAwB;KACzB,CAAC,CAAC;IACH,IAAI,CAAC,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAC3E,2BAA2B,CAAC;QAC1B,aAAa;QACb,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,UAAU,EAAE,KAAK,CAAC,kBAAkB;KACrC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { assertBlobContentKeyBundleTargetHash } from \"../../../data/blobs/attachments/responses\";\nimport type { DecryptDocumentAttachmentBlobInput } from \"../../../data/blobs/attachments/types\";\nimport { assertDocumentWriterProjectionConsistent } from \"../../../data/documents/shared/projection\";\nimport { projectionVerificationOptions } from \"../../../data/documents/shared/types\";\nimport {\n type DocumentWriterProjectionAuthorization,\n requireProjectionUserKeyResolver,\n} from \"../../../data/keyingProjectionVerification\";\nimport { assertAttachmentBindingVerified } from \"./attachmentBindingVerification\";\nimport { assertBlobWrapScopeVerified } from \"./verifiedWrapScope\";\n\n/** Retaining an existing envelope does not propagate a key to a new recipient. */\nexport async function verifyRetainedAttachment(\n input: Omit<\n DecryptDocumentAttachmentBlobInput,\n \"encryptedBytes\" | \"targetSecretKey\"\n >,\n): Promise<void> {\n const resolveProjectionUserKey = requireProjectionUserKeyResolver(\n input.resolveProjectionUserKey,\n \"Retained attachment scope\",\n );\n let authorization: DocumentWriterProjectionAuthorization | undefined;\n await assertDocumentWriterProjectionConsistent(input.writerProjection, {\n allowStaleContentKeyBundle: true,\n ...projectionVerificationOptions({\n execSql: input.execSql,\n stillCurrent: input.stillCurrent,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n resolveProjectionUserKey,\n }),\n onVerifiedAuthorization: (value) => {\n authorization = value;\n },\n });\n if (input.binding.contentKeyBundle.blobId !== input.binding.blobId)\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Retained attachment bundle names another blob\",\n );\n await assertBlobContentKeyBundleTargetHash(input.binding.contentKeyBundle);\n await assertAttachmentBindingVerified({\n ...input,\n authorization,\n resolveProjectionUserKey,\n });\n if (!authorization) throw new Error(\"Attachment authority is unavailable\");\n assertBlobWrapScopeVerified({\n authorization,\n binding: input.binding,\n documentId: input.expectedDocumentId,\n });\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readDocumentManifestIdentity } from "../../../data/
|
|
1
|
+
import { readDocumentManifestIdentity } from "../../../data/blobs/attachments/readers.js";
|
|
2
2
|
import { assertDocumentWriterProjectionConsistent } from "../../../data/documents/shared/projection/index.js";
|
|
3
3
|
import { projectionVerificationOptions, } from "../../../data/documents/shared/types.js";
|
|
4
4
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writerProjection.js","sourceRoot":"","sources":["writerProjection.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"writerProjection.js","sourceRoot":"","sources":["writerProjection.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,yCAAyC,CAAC;AAKvF,OAAO,EAAE,wCAAwC,EAAE,MAAM,2CAA2C,CAAC;AACrG,OAAO,EAEL,6BAA6B,GAC9B,MAAM,sCAAsC,CAAC;AAG9C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,KAQiC;IAKjC,MAAM,gBAAgB,GACpB,KAAK,CAAC,gBAAgB;QACtB,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,2BAA2B,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IACxE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,wCAAwC,CAAC,gBAAgB,EAAE;QAC/D,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,GAAG,6BAA6B,CAAC,KAAK,CAAC;KACxC,CAAC,CAAC;IACH,MAAM,gBAAgB,GAAG,4BAA4B,CAAC,gBAAgB,CAAC,CAAC;IACxE,IAAI,gBAAgB,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,GAAG,KAAK,CAAC,UAAU,2CAA2C,CAC/D,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,mBAAmB,EAAE,CAAC,gBAAgB,CAAC,cAAc,CAAC,EAAE,CAAC;QACjE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;AAChD,CAAC","sourcesContent":["import type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport { readDocumentManifestIdentity } from \"../../../data/blobs/attachments/readers\";\nimport type {\n BlobAttachmentApi,\n DocumentManifestIdentity,\n} from \"../../../data/blobs/attachments/types\";\nimport { assertDocumentWriterProjectionConsistent } from \"../../../data/documents/shared/projection\";\nimport {\n type ProjectionVerificationOptions,\n projectionVerificationOptions,\n} from \"../../../data/documents/shared/types\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\n\n/**\n * Shared prologue for blob attachment mutations (upload/detach): resolve the\n * document writer projection, verify its consistency, confirm it targets the\n * mutated document, and gate on a blocked organization. Returns null when the\n * projection is unavailable or the organization's remote sync is blocked.\n */\nexport async function resolveBlobMutationWriterProjection(\n input: {\n apiClient: Pick<BlobAttachmentApi, \"getDocumentWriterProjection\">;\n documentId: string;\n /** Prefix for the wrong-document error, e.g. \"Blob attachment detach\". */\n errorLabel: string;\n execSql?: ExecSql | undefined;\n isRemoteSyncBlocked?: ((organizationId: string) => boolean) | undefined;\n writerProjection?: DocumentWriterProjectionResponse | undefined;\n } & ProjectionVerificationOptions,\n): Promise<{\n manifestIdentity: DocumentManifestIdentity;\n writerProjection: DocumentWriterProjectionResponse;\n} | null> {\n const writerProjection =\n input.writerProjection ??\n (await input.apiClient.getDocumentWriterProjection(input.documentId));\n if (!writerProjection) {\n return null;\n }\n\n await assertDocumentWriterProjectionConsistent(writerProjection, {\n execSql: input.execSql,\n ...projectionVerificationOptions(input),\n });\n const manifestIdentity = readDocumentManifestIdentity(writerProjection);\n if (manifestIdentity.documentId !== input.documentId) {\n throw new Error(\n `${input.errorLabel} writer projection targets wrong document`,\n );\n }\n if (input.isRemoteSyncBlocked?.(manifestIdentity.organizationId)) {\n return null;\n }\n\n return { manifestIdentity, writerProjection };\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { bytesToBase64 } from "../../../../internal/encoding/index.js";
|
|
2
|
-
import { createInitializedContainerMetadataDocument } from "../../../../data/containers/containerMetadataDocument.js";
|
|
3
2
|
import { createPendingUpdateFields } from "../../../../data/documents/documentSync.js";
|
|
4
3
|
import { deriveStableUuidV4Shaped } from "../../../../data/keying/stableUuid.js";
|
|
5
4
|
import { defaultContainerContentsPersistence } from "../../containerPersistence.js";
|
|
5
|
+
import { createInitializedContainerMetadataDocument } from "../../metadata/containerMetadataDocument.js";
|
|
6
6
|
import { loadContainerWriterProjectionForState } from "../projectionCache.js";
|
|
7
7
|
import { createRemoteContainer } from "../remote.js";
|
|
8
8
|
import { CONTAINER_ALREADY_COMMITTED } from "./createWithMetadata.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createChild.js","sourceRoot":"","sources":["createChild.ts"],"names":[],"mappings":"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","sourcesContent":["import { bytesToBase64 } from \"@tearleads/encoding\";\nimport type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport { createInitializedContainerMetadataDocument } from \"../../../../data/containers/containerMetadataDocument\";\nimport { createPendingUpdateFields } from \"../../../../data/documents/documentSync\";\nimport { deriveStableUuidV4Shaped } from \"../../../../data/keying/stableUuid\";\nimport type { ProjectionUserKeyResolver } from \"../../../../data/keyingProjectionVerification\";\nimport type {\n ContainerContentsPersistence,\n ContainerDocumentRecord as DocumentRecord,\n} from \"../../containerPersistence\";\nimport { defaultContainerContentsPersistence } from \"../../containerPersistence\";\nimport type { ContainerState } from \"../../remoteHydration\";\nimport { loadContainerWriterProjectionForState } from \"../projectionCache\";\nimport { createRemoteContainer } from \"../remote\";\nimport type {\n ContainerMetadataDocumentState,\n ContainerWorkflowRuntime,\n CreatedChildContainerState,\n} from \"../types\";\nimport { CONTAINER_ALREADY_COMMITTED } from \"./createWithMetadata\";\n\nconst SYSTEM_CONTAINER_ID_NAMESPACE_BYTES = new Uint8Array([\n 0xc5, 0x5e, 0x8f, 0x2e, 0x3d, 0x82, 0x4b, 0x4d, 0x8d, 0xbd, 0x0d, 0xd2, 0xd3,\n 0x60, 0xb3, 0x6d,\n]);\n\n/**\n * Derives a stable, UUID-shaped container id for a built-in system slot.\n *\n * The namespace digest makes each slot idempotent across stale store snapshots.\n * The UUID version and variant bits are normalized to a v4 shape because remote\n * container APIs currently validate container ids with the same v4 UUID contract\n * they use for random user-created containers.\n */\nfunction deriveSystemContainerId(input: {\n parentContainerId: string;\n systemSlot: ContainerSystemSlot;\n}): Promise<string> {\n return deriveStableUuidV4Shaped(\n SYSTEM_CONTAINER_ID_NAMESPACE_BYTES,\n `${input.parentContainerId}\\u0000${input.systemSlot}`,\n );\n}\n\nasync function createChildContainerId(\n parentContainerId: string,\n systemSlot: ContainerSystemSlot | null | undefined,\n): Promise<string> {\n return systemSlot\n ? deriveSystemContainerId({ parentContainerId, systemSlot })\n : crypto.randomUUID();\n}\n\nasync function buildRemoteContainerContentsChildContainerState(input: {\n childId: string;\n icon: string | null;\n systemSlot?: ContainerSystemSlot | null | undefined;\n doc: ContainerMetadataDocumentState;\n initialRecord: DocumentRecord;\n parentState: ContainerState;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n trimmedName: string;\n}): Promise<ContainerState | null> {\n const {\n childId,\n icon,\n systemSlot,\n doc,\n initialRecord,\n parentState,\n resolveProjectionUserKey,\n runtime,\n stillCurrent,\n trimmedName,\n } = input;\n const parentProjection = await loadContainerWriterProjectionForState({\n containerState: parentState,\n runtime,\n });\n if (!parentProjection) {\n return null;\n }\n\n const created = await createRemoteContainer({\n systemSlot,\n containerId: childId,\n parentContainerId: parentState.container.id,\n parentProjection,\n resolveProjectionUserKey,\n runtime,\n stillCurrent,\n });\n\n if (!created || created === CONTAINER_ALREADY_COMMITTED) {\n // A benign manifest-exists conflict means the container is already committed\n // remotely (a lost create response). This eager path cannot rebuild its\n // committed metadata state, so leave the create intent to reconcile it via\n // hydration rather than fabricating a local record from a missing response.\n return null;\n }\n\n return {\n container: {\n id: created.containerId,\n // The creator owns a newly-created container until the server reconciles.\n effectiveAccessLevel: \"admin\",\n organizationId: created.organizationId,\n parentId: created.parentId,\n metadataDocumentId: created.metadataDocumentId,\n systemSlot: created.systemSlot,\n name: trimmedName,\n icon,\n createdAt: created.createdAt,\n serverCreatedAt: created.createdAt,\n serverUpdatedAt: created.updatedAt,\n updatedAt: created.updatedAt,\n },\n doc,\n record: {\n ...initialRecord,\n accessEpoch: 1,\n accessStateHash: created.accessManifestHash,\n ...created.persistedMetadataState,\n },\n };\n}\n\nfunction buildLocalContainerContentsChildContainerState(input: {\n childId: string;\n icon: string | null;\n systemSlot?: ContainerSystemSlot | null | undefined;\n doc: ContainerMetadataDocumentState;\n initialRecord: DocumentRecord;\n parentState: ContainerState;\n trimmedName: string;\n}): ContainerState {\n const {\n systemSlot,\n childId,\n doc,\n icon,\n initialRecord,\n parentState,\n trimmedName,\n } = input;\n\n return {\n container: {\n id: childId,\n effectiveAccessLevel: \"admin\",\n organizationId: parentState.container.organizationId,\n parentId: parentState.container.id,\n metadataDocumentId: null,\n systemSlot: systemSlot ?? null,\n name: trimmedName,\n icon,\n },\n doc,\n record: initialRecord,\n };\n}\n\nfunction createInitialChildContainerDocumentRecord(input: {\n childId: string;\n initialUpdate: Uint8Array;\n}): DocumentRecord {\n return {\n accessEpoch: 1,\n accessStateHash: null,\n contentKeyBundle: null,\n documentId: null,\n documentKekTargets: null,\n documentManifestBundle: null,\n id: input.childId,\n lastCommitLsn: null,\n metadataUpdates: bytesToBase64(input.initialUpdate),\n snapshotEndVersion: \"\",\n };\n}\n\nfunction resolveAtomicChildContainerSave(\n persistence: ContainerContentsPersistence,\n): NonNullable<\n ContainerContentsPersistence[\"saveContainerWithPendingUpdate\"]\n> | null {\n const saveContainerWithPendingUpdate =\n persistence.saveContainerWithPendingUpdate;\n if (!saveContainerWithPendingUpdate) return null;\n const inheritedSqlAtomicSave =\n saveContainerWithPendingUpdate ===\n defaultContainerContentsPersistence.saveContainerWithPendingUpdate &&\n (persistence.saveContainer !==\n defaultContainerContentsPersistence.saveContainer ||\n persistence.enqueuePendingUpdate !==\n defaultContainerContentsPersistence.enqueuePendingUpdate);\n return inheritedSqlAtomicSave ? null : saveContainerWithPendingUpdate;\n}\n\nasync function persistCreatedChildContainer(input: {\n containerState: ContainerState;\n initialUpdate: Uint8Array;\n persistence: ContainerContentsPersistence;\n queueRemoteSync: boolean;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<boolean | null> {\n const { containerState, persistence, queueRemoteSync, runtime } = input;\n const createIntent =\n queueRemoteSync &&\n !containerState.record.documentId &&\n containerState.container.parentId\n ? { parentContainerId: containerState.container.parentId }\n : undefined;\n const shouldRequestSync =\n queueRemoteSync &&\n (!containerState.record.documentId ||\n Boolean(containerState.record.contentKeyBundle));\n const saveOptions =\n createIntent || input.stillCurrent\n ? {\n ...(createIntent ? { createIntent } : {}),\n stillCurrent: input.stillCurrent,\n }\n : undefined;\n const pendingUpdate = shouldRequestSync\n ? createPendingUpdateFields(input.initialUpdate)\n : null;\n if (pendingUpdate) {\n const saveContainerWithPendingUpdate =\n resolveAtomicChildContainerSave(persistence);\n if (!saveContainerWithPendingUpdate) return null;\n containerState.container = await saveContainerWithPendingUpdate(\n runtime.infra.execSql,\n containerState.container,\n containerState.record,\n { ...saveOptions, pendingUpdate },\n );\n } else {\n containerState.container = await persistence.saveContainer(\n runtime.infra.execSql,\n containerState.container,\n containerState.record,\n saveOptions,\n );\n }\n return input.stillCurrent?.() === false ? null : shouldRequestSync;\n}\n\nexport async function createChildContainerState(input: {\n systemSlot?: ContainerSystemSlot | null | undefined;\n createRemote: boolean;\n icon?: string | null | undefined;\n name: string;\n parentState: ContainerState;\n persistence: ContainerContentsPersistence;\n queueRemoteSync?: boolean | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<CreatedChildContainerState | null> {\n const {\n createRemote,\n systemSlot,\n name,\n parentState,\n persistence,\n queueRemoteSync = true,\n resolveProjectionUserKey,\n runtime,\n } = input;\n const trimmedName = name.trim();\n if (!trimmedName) {\n return null;\n }\n\n const icon = input.icon?.trim() || null;\n const childId = await createChildContainerId(\n parentState.container.id,\n systemSlot,\n );\n const { doc, initialUpdate } =\n await createInitializedContainerMetadataDocument(childId, {\n icon,\n name: trimmedName,\n });\n const initialRecord = createInitialChildContainerDocumentRecord({\n childId,\n initialUpdate,\n });\n if (input.stillCurrent?.() === false) return null;\n // An initial metadata update and its container identity are one durable unit.\n // Refuse before any remote create when an adapter cannot provide\n // that atomic operation; a SQLite transaction cannot roll back adapter-owned\n // side effects.\n if (queueRemoteSync && !resolveAtomicChildContainerSave(persistence)) {\n return null;\n }\n\n const remoteChildState = createRemote\n ? await buildRemoteContainerContentsChildContainerState({\n childId,\n icon,\n systemSlot,\n doc,\n initialRecord,\n parentState,\n resolveProjectionUserKey,\n runtime,\n stillCurrent: input.stillCurrent,\n trimmedName,\n })\n : null;\n const containerState =\n remoteChildState ??\n buildLocalContainerContentsChildContainerState({\n childId,\n icon,\n systemSlot,\n doc,\n initialRecord,\n parentState,\n trimmedName,\n });\n const shouldRequestSync = await persistCreatedChildContainer({\n containerState,\n initialUpdate,\n persistence,\n queueRemoteSync,\n runtime,\n stillCurrent: input.stillCurrent,\n });\n if (shouldRequestSync === null) return null;\n\n return {\n containerState,\n initialUpdate,\n shouldRequestSync,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"createChild.js","sourceRoot":"","sources":["createChild.ts"],"names":[],"mappings":"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","sourcesContent":["import { bytesToBase64 } from \"@tearleads/encoding\";\nimport type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport { createPendingUpdateFields } from \"../../../../data/documents/documentSync\";\nimport { deriveStableUuidV4Shaped } from \"../../../../data/keying/stableUuid\";\nimport type { ProjectionUserKeyResolver } from \"../../../../data/keyingProjectionVerification\";\nimport type {\n ContainerContentsPersistence,\n ContainerDocumentRecord as DocumentRecord,\n} from \"../../containerPersistence\";\nimport { defaultContainerContentsPersistence } from \"../../containerPersistence\";\nimport { createInitializedContainerMetadataDocument } from \"../../metadata/containerMetadataDocument\";\nimport type { ContainerState } from \"../../remoteHydration\";\nimport { loadContainerWriterProjectionForState } from \"../projectionCache\";\nimport { createRemoteContainer } from \"../remote\";\nimport type {\n ContainerMetadataDocumentState,\n ContainerWorkflowRuntime,\n CreatedChildContainerState,\n} from \"../types\";\nimport { CONTAINER_ALREADY_COMMITTED } from \"./createWithMetadata\";\n\nconst SYSTEM_CONTAINER_ID_NAMESPACE_BYTES = new Uint8Array([\n 0xc5, 0x5e, 0x8f, 0x2e, 0x3d, 0x82, 0x4b, 0x4d, 0x8d, 0xbd, 0x0d, 0xd2, 0xd3,\n 0x60, 0xb3, 0x6d,\n]);\n\n/**\n * Derives a stable, UUID-shaped container id for a built-in system slot.\n *\n * The namespace digest makes each slot idempotent across stale store snapshots.\n * The UUID version and variant bits are normalized to a v4 shape because remote\n * container APIs currently validate container ids with the same v4 UUID contract\n * they use for random user-created containers.\n */\nfunction deriveSystemContainerId(input: {\n parentContainerId: string;\n systemSlot: ContainerSystemSlot;\n}): Promise<string> {\n return deriveStableUuidV4Shaped(\n SYSTEM_CONTAINER_ID_NAMESPACE_BYTES,\n `${input.parentContainerId}\\u0000${input.systemSlot}`,\n );\n}\n\nasync function createChildContainerId(\n parentContainerId: string,\n systemSlot: ContainerSystemSlot | null | undefined,\n): Promise<string> {\n return systemSlot\n ? deriveSystemContainerId({ parentContainerId, systemSlot })\n : crypto.randomUUID();\n}\n\nasync function buildRemoteContainerContentsChildContainerState(input: {\n childId: string;\n icon: string | null;\n systemSlot?: ContainerSystemSlot | null | undefined;\n doc: ContainerMetadataDocumentState;\n initialRecord: DocumentRecord;\n parentState: ContainerState;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n trimmedName: string;\n}): Promise<ContainerState | null> {\n const {\n childId,\n icon,\n systemSlot,\n doc,\n initialRecord,\n parentState,\n resolveProjectionUserKey,\n runtime,\n stillCurrent,\n trimmedName,\n } = input;\n const parentProjection = await loadContainerWriterProjectionForState({\n containerState: parentState,\n runtime,\n });\n if (!parentProjection) {\n return null;\n }\n\n const created = await createRemoteContainer({\n systemSlot,\n containerId: childId,\n parentContainerId: parentState.container.id,\n parentProjection,\n resolveProjectionUserKey,\n runtime,\n stillCurrent,\n });\n\n if (!created || created === CONTAINER_ALREADY_COMMITTED) {\n // A benign manifest-exists conflict means the container is already committed\n // remotely (a lost create response). This eager path cannot rebuild its\n // committed metadata state, so leave the create intent to reconcile it via\n // hydration rather than fabricating a local record from a missing response.\n return null;\n }\n\n return {\n container: {\n id: created.containerId,\n // The creator owns a newly-created container until the server reconciles.\n effectiveAccessLevel: \"admin\",\n organizationId: created.organizationId,\n parentId: created.parentId,\n metadataDocumentId: created.metadataDocumentId,\n systemSlot: created.systemSlot,\n name: trimmedName,\n icon,\n createdAt: created.createdAt,\n serverCreatedAt: created.createdAt,\n serverUpdatedAt: created.updatedAt,\n updatedAt: created.updatedAt,\n },\n doc,\n record: {\n ...initialRecord,\n accessEpoch: 1,\n accessStateHash: created.accessManifestHash,\n ...created.persistedMetadataState,\n },\n };\n}\n\nfunction buildLocalContainerContentsChildContainerState(input: {\n childId: string;\n icon: string | null;\n systemSlot?: ContainerSystemSlot | null | undefined;\n doc: ContainerMetadataDocumentState;\n initialRecord: DocumentRecord;\n parentState: ContainerState;\n trimmedName: string;\n}): ContainerState {\n const {\n systemSlot,\n childId,\n doc,\n icon,\n initialRecord,\n parentState,\n trimmedName,\n } = input;\n\n return {\n container: {\n id: childId,\n effectiveAccessLevel: \"admin\",\n organizationId: parentState.container.organizationId,\n parentId: parentState.container.id,\n metadataDocumentId: null,\n systemSlot: systemSlot ?? null,\n name: trimmedName,\n icon,\n },\n doc,\n record: initialRecord,\n };\n}\n\nfunction createInitialChildContainerDocumentRecord(input: {\n childId: string;\n initialUpdate: Uint8Array;\n}): DocumentRecord {\n return {\n accessEpoch: 1,\n accessStateHash: null,\n contentKeyBundle: null,\n documentId: null,\n documentKekTargets: null,\n documentManifestBundle: null,\n id: input.childId,\n lastCommitLsn: null,\n metadataUpdates: bytesToBase64(input.initialUpdate),\n snapshotEndVersion: \"\",\n };\n}\n\nfunction resolveAtomicChildContainerSave(\n persistence: ContainerContentsPersistence,\n): NonNullable<\n ContainerContentsPersistence[\"saveContainerWithPendingUpdate\"]\n> | null {\n const saveContainerWithPendingUpdate =\n persistence.saveContainerWithPendingUpdate;\n if (!saveContainerWithPendingUpdate) return null;\n const inheritedSqlAtomicSave =\n saveContainerWithPendingUpdate ===\n defaultContainerContentsPersistence.saveContainerWithPendingUpdate &&\n (persistence.saveContainer !==\n defaultContainerContentsPersistence.saveContainer ||\n persistence.enqueuePendingUpdate !==\n defaultContainerContentsPersistence.enqueuePendingUpdate);\n return inheritedSqlAtomicSave ? null : saveContainerWithPendingUpdate;\n}\n\nasync function persistCreatedChildContainer(input: {\n containerState: ContainerState;\n initialUpdate: Uint8Array;\n persistence: ContainerContentsPersistence;\n queueRemoteSync: boolean;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<boolean | null> {\n const { containerState, persistence, queueRemoteSync, runtime } = input;\n const createIntent =\n queueRemoteSync &&\n !containerState.record.documentId &&\n containerState.container.parentId\n ? { parentContainerId: containerState.container.parentId }\n : undefined;\n const shouldRequestSync =\n queueRemoteSync &&\n (!containerState.record.documentId ||\n Boolean(containerState.record.contentKeyBundle));\n const saveOptions =\n createIntent || input.stillCurrent\n ? {\n ...(createIntent ? { createIntent } : {}),\n stillCurrent: input.stillCurrent,\n }\n : undefined;\n const pendingUpdate = shouldRequestSync\n ? createPendingUpdateFields(input.initialUpdate)\n : null;\n if (pendingUpdate) {\n const saveContainerWithPendingUpdate =\n resolveAtomicChildContainerSave(persistence);\n if (!saveContainerWithPendingUpdate) return null;\n containerState.container = await saveContainerWithPendingUpdate(\n runtime.infra.execSql,\n containerState.container,\n containerState.record,\n { ...saveOptions, pendingUpdate },\n );\n } else {\n containerState.container = await persistence.saveContainer(\n runtime.infra.execSql,\n containerState.container,\n containerState.record,\n saveOptions,\n );\n }\n return input.stillCurrent?.() === false ? null : shouldRequestSync;\n}\n\nexport async function createChildContainerState(input: {\n systemSlot?: ContainerSystemSlot | null | undefined;\n createRemote: boolean;\n icon?: string | null | undefined;\n name: string;\n parentState: ContainerState;\n persistence: ContainerContentsPersistence;\n queueRemoteSync?: boolean | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<CreatedChildContainerState | null> {\n const {\n createRemote,\n systemSlot,\n name,\n parentState,\n persistence,\n queueRemoteSync = true,\n resolveProjectionUserKey,\n runtime,\n } = input;\n const trimmedName = name.trim();\n if (!trimmedName) {\n return null;\n }\n\n const icon = input.icon?.trim() || null;\n const childId = await createChildContainerId(\n parentState.container.id,\n systemSlot,\n );\n const { doc, initialUpdate } =\n await createInitializedContainerMetadataDocument(childId, {\n icon,\n name: trimmedName,\n });\n const initialRecord = createInitialChildContainerDocumentRecord({\n childId,\n initialUpdate,\n });\n if (input.stillCurrent?.() === false) return null;\n // An initial metadata update and its container identity are one durable unit.\n // Refuse before any remote create when an adapter cannot provide\n // that atomic operation; a SQLite transaction cannot roll back adapter-owned\n // side effects.\n if (queueRemoteSync && !resolveAtomicChildContainerSave(persistence)) {\n return null;\n }\n\n const remoteChildState = createRemote\n ? await buildRemoteContainerContentsChildContainerState({\n childId,\n icon,\n systemSlot,\n doc,\n initialRecord,\n parentState,\n resolveProjectionUserKey,\n runtime,\n stillCurrent: input.stillCurrent,\n trimmedName,\n })\n : null;\n const containerState =\n remoteChildState ??\n buildLocalContainerContentsChildContainerState({\n childId,\n icon,\n systemSlot,\n doc,\n initialRecord,\n parentState,\n trimmedName,\n });\n const shouldRequestSync = await persistCreatedChildContainer({\n containerState,\n initialUpdate,\n persistence,\n queueRemoteSync,\n runtime,\n stillCurrent: input.stillCurrent,\n });\n if (shouldRequestSync === null) return null;\n\n return {\n containerState,\n initialUpdate,\n shouldRequestSync,\n };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_DOCUMENT_KIND } from "../../../../data/documents/documentConstants.js";
|
|
2
|
-
import { sqlDocumentContainerProjectionPersistence } from "../../../../data/persistence/
|
|
2
|
+
import { sqlDocumentContainerProjectionPersistence } from "../../../../data/persistence/container-contents/documentContainerProjectionPersistence.js";
|
|
3
3
|
import { listDocumentsByContainerIdsOrDocumentIds } from "../../../../data/persistence/documents/documentsPersistence.js";
|
|
4
4
|
import { unlinkRemoteContainerDocument } from "../../documentLinks/index.js";
|
|
5
5
|
import { purgeLocalContainerDocument, purgeRemoteContainerDocument, } from "../../tombstones/documentPurge.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"purgeTree.js","sourceRoot":"","sources":["purgeTree.ts"],"names":[],"mappings":"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","sourcesContent":["import { DEFAULT_DOCUMENT_KIND } from \"../../../../data/documents/documentConstants\";\nimport type { DocumentSummary } from \"../../../../data/documents/documentSummary\";\nimport type { ProjectionUserKeyResolver } from \"../../../../data/keyingProjectionVerification\";\nimport { sqlDocumentContainerProjectionPersistence } from \"../../../../data/persistence/containers/documentContainerProjectionPersistence\";\nimport { listDocumentsByContainerIdsOrDocumentIds } from \"../../../../data/persistence/documents/documentsPersistence\";\nimport type { SubtreePurgeRoot } from \"../../../documents/purge/subtreePurgeScope\";\nimport type { ContainerContentsPersistence } from \"../../containerPersistence\";\nimport { unlinkRemoteContainerDocument } from \"../../documentLinks\";\nimport type { ContainerState } from \"../../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../../runtime\";\nimport {\n purgeLocalContainerDocument,\n purgeRemoteContainerDocument,\n} from \"../../tombstones/documentPurge\";\nimport type { PurgeProgress } from \"./purgeProgress\";\nimport {\n collectSubtreeLeafFirst,\n snapshotContainerStates,\n} from \"./purgeTreeCollection\";\nimport { deleteSubtreeContainers } from \"./purgeTreeContainerDeletion\";\nimport { createSubtreePurgeScope } from \"./purgeTreeScope\";\n\nexport { collectSubtreeLeafFirst } from \"./purgeTreeCollection\";\n\n// The store runtime satisfies the per-document purge/unlink workflows, so the\n// cascade reuses it verbatim.\ntype PurgeContainerTreeRuntime = ContainerContentsWorkflowRuntime;\n\ninterface PurgeContainerTreeInput {\n readonly containersById: ReadonlyMap<string, ContainerState>;\n readonly documentOperations?: SubtreeDocumentOperations | undefined;\n // When true, tear down everything UNDER the root (its documents plus every\n // descendant container) but never delete the root container itself. This is\n // how \"Empty Trash\" reuses the same engine: the Trash bin is a protected system\n // container that must survive while its whole contents are destroyed.\n readonly keepRootContainer?: boolean;\n readonly onProgress?: ((progress: PurgeProgress) => void) | undefined;\n readonly persistence: ContainerContentsPersistence;\n readonly prepareDocumentRotationSnapshot: (\n document: DocumentSummary,\n ) => Promise<Uint8Array | null>;\n readonly resolveProjectionUserKey: ProjectionUserKeyResolver;\n readonly rootContainerId: string;\n readonly runtime: PurgeContainerTreeRuntime;\n /** Stops a stale store generation between complete destructive units. */\n readonly stillCurrent?: (() => boolean) | undefined;\n // Checked at each unit boundary (between whole documents / whole containers),\n // never mid-remote-call, so a cancelled run stops on a consistent prefix.\n readonly signal?: AbortSignal | undefined;\n}\n\ninterface PurgeContainerTreeResult {\n readonly aborted: boolean;\n readonly completedCount: number;\n readonly failedCount: number;\n readonly purgedContainerIds: readonly string[];\n readonly remoteDeletedContainerIds: readonly string[];\n readonly totalCount: number;\n}\n\ninterface SubtreeDocumentPlan {\n // Synced documents whose every link falls inside the subtree: reduce them to\n // one retained link, as required by the purge endpoint, then destroy them.\n readonly purge: readonly {\n document: DocumentSummary;\n unlinkContainerIds: readonly string[];\n }[];\n // Synced documents also linked outside the subtree: keep them, but unlink\n // from each in-subtree container so the containers can be deleted.\n readonly unlinkByDocument: ReadonlyMap<\n string,\n { document: DocumentSummary; containerIds: readonly string[] }\n >;\n // Never-synced documents (no remote id): destroy locally only.\n readonly purgeLocal: readonly DocumentSummary[];\n}\n\ntype SubtreeDocumentDisposition =\n | { kind: \"purge\"; unlinkContainerIds: readonly string[] }\n | { kind: \"purge-local\" }\n | { kind: \"unlink\"; containerIds: readonly string[] };\n\n// Destroy local-only documents locally. Destroy synced documents only when all\n// links are in the subtree; preserve documents with an external link. The purge\n// plan reduces owned documents to the server-required sole link first.\nexport function classifySubtreeDocument(input: {\n readonly documentId: string | null;\n readonly linkedContainerIds: readonly string[];\n readonly preferredContainerId?: string | null | undefined;\n readonly subtreeContainerIds: ReadonlySet<string>;\n}): SubtreeDocumentDisposition {\n if (!input.documentId) {\n return { kind: \"purge-local\" };\n }\n const hasExternalLink = input.linkedContainerIds.some(\n (containerId) => !input.subtreeContainerIds.has(containerId),\n );\n if (!hasExternalLink) {\n const linkedContainerIds = Array.from(\n new Set(\n input.linkedContainerIds.filter((containerId) =>\n input.subtreeContainerIds.has(containerId),\n ),\n ),\n );\n const retainedContainerId =\n input.preferredContainerId &&\n linkedContainerIds.includes(input.preferredContainerId)\n ? input.preferredContainerId\n : linkedContainerIds[0];\n return {\n kind: \"purge\",\n unlinkContainerIds: linkedContainerIds.filter(\n (containerId) => containerId !== retainedContainerId,\n ),\n };\n }\n return {\n kind: \"unlink\",\n containerIds: input.linkedContainerIds.filter((containerId) =>\n input.subtreeContainerIds.has(containerId),\n ),\n };\n}\n\nasync function planSubtreeDocuments(input: {\n readonly execSql: PurgeContainerTreeRuntime[\"infra\"][\"execSql\"];\n readonly subtreeContainerIds: ReadonlySet<string>;\n}): Promise<SubtreeDocumentPlan> {\n const documents = await listDocumentsByContainerIdsOrDocumentIds(\n input.execSql,\n {\n containerIds: [...input.subtreeContainerIds],\n documentIds: [],\n },\n );\n const remoteDocumentIds = documents.flatMap((document) =>\n document.documentId ? [document.documentId] : [],\n );\n const linkedContainerIdsByDocumentId =\n await sqlDocumentContainerProjectionPersistence.listLinkedContainerIdsByDocumentIds(\n input.execSql,\n remoteDocumentIds,\n );\n\n const purge: Array<{\n document: DocumentSummary;\n unlinkContainerIds: readonly string[];\n }> = [];\n const purgeLocal: DocumentSummary[] = [];\n const unlinkByDocument = new Map<\n string,\n { document: DocumentSummary; containerIds: readonly string[] }\n >();\n for (const document of documents) {\n const disposition = classifySubtreeDocument({\n documentId: document.documentId ?? null,\n linkedContainerIds: document.documentId\n ? (linkedContainerIdsByDocumentId.get(document.documentId) ?? [])\n : [],\n preferredContainerId: document.containerId,\n subtreeContainerIds: input.subtreeContainerIds,\n });\n if (disposition.kind === \"purge-local\") {\n purgeLocal.push(document);\n } else if (disposition.kind === \"purge\") {\n purge.push({\n document,\n unlinkContainerIds: disposition.unlinkContainerIds,\n });\n } else if (document.documentId) {\n unlinkByDocument.set(document.documentId, {\n document,\n containerIds: disposition.containerIds,\n });\n }\n }\n\n return { purge, purgeLocal, unlinkByDocument };\n}\n\nasync function unlinkSubtreeDocument(input: {\n /** Rechecks the document and this exact unlink target before dispatch. */\n readonly beforeSubmit: (targetContainerId: string) => Promise<boolean>;\n readonly expectedSubtreeRoot: SubtreePurgeRoot;\n readonly stillCurrent?: (() => boolean) | undefined;\n readonly containerIds: readonly string[];\n readonly document: DocumentSummary;\n readonly prepareDocumentRotationSnapshot: PurgeContainerTreeInput[\"prepareDocumentRotationSnapshot\"];\n readonly resolveProjectionUserKey: ProjectionUserKeyResolver;\n readonly runtime: PurgeContainerTreeRuntime;\n}): Promise<boolean> {\n const documentId = input.document.documentId;\n if (!documentId) {\n return true;\n }\n let rotationSnapshot: Uint8Array | null;\n try {\n rotationSnapshot = await input.prepareDocumentRotationSnapshot(\n input.document,\n );\n } catch {\n return false;\n }\n if (!rotationSnapshot) {\n return false;\n }\n for (const containerId of input.containerIds) {\n const result = await unlinkRemoteContainerDocument({\n beforeSubmit: () => input.beforeSubmit(containerId),\n documentId,\n expectedSubtreeRoot: input.expectedSubtreeRoot,\n isCurrent: input.stillCurrent,\n noteId: input.document.id,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n rotationSnapshot,\n runtime: input.runtime,\n targetContainerId: containerId,\n });\n if (result === null) {\n return false;\n }\n }\n return true;\n}\n\ninterface SubtreeDocumentOperations {\n readonly purgeLocal: (document: DocumentSummary) => Promise<boolean>;\n readonly purgeRemote: (document: DocumentSummary) => Promise<boolean>;\n readonly unlink: (\n document: DocumentSummary,\n containerIds: readonly string[],\n ) => Promise<boolean>;\n}\n\nfunction resolveSubtreeDocumentOperations(\n input: PurgeContainerTreeInput,\n scope: ReturnType<typeof createSubtreePurgeScope>,\n): SubtreeDocumentOperations {\n const expectedSubtreeRoot = selectedSubtreeRoot(input);\n const operations: SubtreeDocumentOperations = input.documentOperations ?? {\n purgeLocal: async (document) =>\n (await purgeLocalContainerDocument({\n beforeDeleteInTransaction: (execSql) =>\n scope.assertLocalScopeInTransaction(\n execSql,\n document.containerId,\n document.id,\n ),\n noteId: document.id,\n expectedContainerId: document.containerId,\n runtime: input.runtime,\n })) !== null,\n purgeRemote: async (document) =>\n Boolean(\n document.documentId &&\n (await purgeRemoteContainerDocument({\n beforeSubmit: () => scope.allowsDocument(document),\n documentId: document.documentId,\n expectedSubtreeRoot,\n stillCurrent: input.stillCurrent,\n documentKind: document.documentKind ?? DEFAULT_DOCUMENT_KIND,\n noteId: document.id,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n runtime: input.runtime,\n })),\n ),\n unlink: (document, containerIds) =>\n unlinkSubtreeDocument({\n // Target preparation and signing can outlast the upfront checks.\n beforeSubmit: async (containerId) =>\n (await scope.allowsDocument(document)) &&\n (await scope.allowsContainer(containerId)),\n containerIds,\n document,\n expectedSubtreeRoot,\n stillCurrent: input.stillCurrent,\n prepareDocumentRotationSnapshot: input.prepareDocumentRotationSnapshot,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n runtime: input.runtime,\n }),\n };\n return {\n purgeLocal: async (document) =>\n (await scope.allowsDocument(document)) &&\n (await operations.purgeLocal(document)),\n purgeRemote: async (document) =>\n (await scope.allowsDocument(document)) &&\n (await operations.purgeRemote(document)),\n unlink: async (document, containerIds) => {\n if (!(await scope.allowsDocument(document))) return false;\n for (const containerId of containerIds) {\n if (!(await scope.allowsContainer(containerId))) return false;\n }\n return operations.unlink(document, containerIds);\n },\n };\n}\n\ninterface SubtreeTeardownResult {\n readonly aborted: boolean;\n}\n\n// Signed request paths must keep the root under the parent it was selected\n// from; a settled restore elsewhere preserves the root's own documents too.\nfunction selectedSubtreeRoot(\n input: Pick<PurgeContainerTreeInput, \"containersById\" | \"rootContainerId\">,\n): SubtreePurgeRoot {\n return {\n id: input.rootContainerId,\n parentId:\n input.containersById.get(input.rootContainerId)?.container.parentId ??\n null,\n };\n}\n\nfunction purgeWasCancelled(input: {\n readonly signal?: AbortSignal | undefined;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): boolean {\n return input.signal?.aborted === true || input.stillCurrent?.() === false;\n}\n\n// Tear down one whole document per progress step. A failed document is skipped,\n// leaving its container undeletable while the remaining subtree work continues.\n// Cancellation is honored only between complete document operations.\nasync function teardownSubtreeDocuments(input: {\n readonly documentOperations: SubtreeDocumentOperations;\n readonly plan: SubtreeDocumentPlan;\n readonly reportStep: (ok: boolean) => void;\n readonly signal?: AbortSignal | undefined;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<SubtreeTeardownResult> {\n for (const {\n document,\n containerIds,\n } of input.plan.unlinkByDocument.values()) {\n if (purgeWasCancelled(input)) {\n return { aborted: true };\n }\n const unlinked = await input.documentOperations.unlink(\n document,\n containerIds,\n );\n input.reportStep(unlinked);\n }\n for (const { document, unlinkContainerIds } of input.plan.purge) {\n if (purgeWasCancelled(input)) {\n return { aborted: true };\n }\n if (\n unlinkContainerIds.length > 0 &&\n !(await input.documentOperations.unlink(document, unlinkContainerIds))\n ) {\n input.reportStep(false);\n continue;\n }\n input.reportStep(await input.documentOperations.purgeRemote(document));\n }\n for (const document of input.plan.purgeLocal) {\n if (purgeWasCancelled(input)) {\n return { aborted: true };\n }\n input.reportStep(await input.documentOperations.purgeLocal(document));\n }\n return { aborted: false };\n}\n\n// Permanently destroy a folder that lives inside Trash, including everything\n// under it (or, with keepRootContainer, everything under the Trash bin while\n// leaving the bin in place — this is \"Empty Trash\"). Documents are destroyed only\n// when the subtree holds their last link; multi-folder documents are unlinked\n// from the subtree and preserved. Containers are deleted leaf-first so each\n// delete obeys the leaf-only constraint the store and server enforce. Every step\n// is remote-first then local (the per-document and per-container workflows handle\n// that internally), so a cancel between steps leaves local state reflecting\n// exactly the completed remote work.\n//\n// Progress is determinate: the plan (a pure read) completes before any\n// destructive step, so onProgress reports completed/failed against a total known\n// up front. A failed item is skipped rather than aborting the run, so the result\n// reports whatever prefix was actually destroyed alongside completed/failed\n// counts and whether a cancellation cut it short. Returns null only when the\n// target container is absent from the snapshot.\nexport async function purgeContainerTree(\n request: PurgeContainerTreeInput,\n): Promise<PurgeContainerTreeResult | null> {\n if (purgeWasCancelled(request)) {\n return null;\n }\n const input = {\n ...request,\n containersById: snapshotContainerStates(request.containersById),\n };\n const subtreeStates = collectSubtreeLeafFirst(\n input.containersById,\n input.rootContainerId,\n );\n if (subtreeStates.length === 0) {\n return null;\n }\n const subtreeContainerIds = new Set(\n subtreeStates.map((containerState) => containerState.container.id),\n );\n\n const plan = await planSubtreeDocuments({\n execSql: input.runtime.infra.execSql,\n subtreeContainerIds,\n });\n if (purgeWasCancelled(input)) {\n return null;\n }\n\n // Empty Trash keeps the Trash bin itself: tear down everything it holds, but\n // exclude the root from the container deletions (deleting a system container\n // would be rejected anyway).\n const containerStatesToDelete = input.keepRootContainer\n ? subtreeStates.filter(\n (containerState) =>\n containerState.container.id !== input.rootContainerId,\n )\n : subtreeStates;\n\n // The total is fully known here — planning is a pure read that completes before\n // any destructive step. Unlink work is counted per document (not per container\n // link) so completed can never exceed total even when a document has several\n // in-subtree links.\n const totalCount =\n plan.unlinkByDocument.size +\n plan.purge.length +\n plan.purgeLocal.length +\n containerStatesToDelete.length;\n\n let completedCount = 0;\n let failedCount = 0;\n const emitProgress = () => {\n input.onProgress?.({ completedCount, failedCount, totalCount });\n };\n const reportStep = (ok: boolean) => {\n if (ok) {\n completedCount += 1;\n } else {\n failedCount += 1;\n }\n emitProgress();\n };\n // Emit the initial 0/total so a determinate bar renders immediately, before the\n // first (potentially slow) remote call.\n emitProgress();\n\n const scope = createSubtreePurgeScope(input);\n const teardown = await teardownSubtreeDocuments({\n documentOperations: resolveSubtreeDocumentOperations(input, scope),\n plan,\n reportStep,\n signal: input.signal,\n stillCurrent: input.stillCurrent,\n });\n\n let purgedContainerIds: readonly string[] = [];\n let remoteDeletedContainerIds: readonly string[] = [];\n let aborted = teardown.aborted;\n if (!teardown.aborted) {\n const deletion = await deleteSubtreeContainers({\n allowsContainer: scope.allowsContainer,\n allowsLocalScopeInTransaction: scope.allowsLocalScopeInTransaction,\n root: selectedSubtreeRoot(input),\n persistence: input.persistence,\n reportStep,\n runtime: input.runtime,\n signal: input.signal,\n stillCurrent: input.stillCurrent,\n subtreeStates: containerStatesToDelete,\n });\n purgedContainerIds = deletion.purgedContainerIds;\n remoteDeletedContainerIds = deletion.remoteDeletedContainerIds;\n aborted = deletion.aborted;\n }\n\n return {\n aborted,\n completedCount,\n failedCount,\n purgedContainerIds,\n remoteDeletedContainerIds,\n totalCount,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"purgeTree.js","sourceRoot":"","sources":["purgeTree.ts"],"names":[],"mappings":"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","sourcesContent":["import { DEFAULT_DOCUMENT_KIND } from \"../../../../data/documents/documentConstants\";\nimport type { DocumentSummary } from \"../../../../data/documents/documentSummary\";\nimport type { ProjectionUserKeyResolver } from \"../../../../data/keyingProjectionVerification\";\nimport { sqlDocumentContainerProjectionPersistence } from \"../../../../data/persistence/container-contents/documentContainerProjectionPersistence\";\nimport { listDocumentsByContainerIdsOrDocumentIds } from \"../../../../data/persistence/documents/documentsPersistence\";\nimport type { SubtreePurgeRoot } from \"../../../documents/purge/subtreePurgeScope\";\nimport type { ContainerContentsPersistence } from \"../../containerPersistence\";\nimport { unlinkRemoteContainerDocument } from \"../../documentLinks\";\nimport type { ContainerState } from \"../../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../../runtime\";\nimport {\n purgeLocalContainerDocument,\n purgeRemoteContainerDocument,\n} from \"../../tombstones/documentPurge\";\nimport type { PurgeProgress } from \"./purgeProgress\";\nimport {\n collectSubtreeLeafFirst,\n snapshotContainerStates,\n} from \"./purgeTreeCollection\";\nimport { deleteSubtreeContainers } from \"./purgeTreeContainerDeletion\";\nimport { createSubtreePurgeScope } from \"./purgeTreeScope\";\n\nexport { collectSubtreeLeafFirst } from \"./purgeTreeCollection\";\n\n// The store runtime satisfies the per-document purge/unlink workflows, so the\n// cascade reuses it verbatim.\ntype PurgeContainerTreeRuntime = ContainerContentsWorkflowRuntime;\n\ninterface PurgeContainerTreeInput {\n readonly containersById: ReadonlyMap<string, ContainerState>;\n readonly documentOperations?: SubtreeDocumentOperations | undefined;\n // When true, tear down everything UNDER the root (its documents plus every\n // descendant container) but never delete the root container itself. This is\n // how \"Empty Trash\" reuses the same engine: the Trash bin is a protected system\n // container that must survive while its whole contents are destroyed.\n readonly keepRootContainer?: boolean;\n readonly onProgress?: ((progress: PurgeProgress) => void) | undefined;\n readonly persistence: ContainerContentsPersistence;\n readonly prepareDocumentRotationSnapshot: (\n document: DocumentSummary,\n ) => Promise<Uint8Array | null>;\n readonly resolveProjectionUserKey: ProjectionUserKeyResolver;\n readonly rootContainerId: string;\n readonly runtime: PurgeContainerTreeRuntime;\n /** Stops a stale store generation between complete destructive units. */\n readonly stillCurrent?: (() => boolean) | undefined;\n // Checked at each unit boundary (between whole documents / whole containers),\n // never mid-remote-call, so a cancelled run stops on a consistent prefix.\n readonly signal?: AbortSignal | undefined;\n}\n\ninterface PurgeContainerTreeResult {\n readonly aborted: boolean;\n readonly completedCount: number;\n readonly failedCount: number;\n readonly purgedContainerIds: readonly string[];\n readonly remoteDeletedContainerIds: readonly string[];\n readonly totalCount: number;\n}\n\ninterface SubtreeDocumentPlan {\n // Synced documents whose every link falls inside the subtree: reduce them to\n // one retained link, as required by the purge endpoint, then destroy them.\n readonly purge: readonly {\n document: DocumentSummary;\n unlinkContainerIds: readonly string[];\n }[];\n // Synced documents also linked outside the subtree: keep them, but unlink\n // from each in-subtree container so the containers can be deleted.\n readonly unlinkByDocument: ReadonlyMap<\n string,\n { document: DocumentSummary; containerIds: readonly string[] }\n >;\n // Never-synced documents (no remote id): destroy locally only.\n readonly purgeLocal: readonly DocumentSummary[];\n}\n\ntype SubtreeDocumentDisposition =\n | { kind: \"purge\"; unlinkContainerIds: readonly string[] }\n | { kind: \"purge-local\" }\n | { kind: \"unlink\"; containerIds: readonly string[] };\n\n// Destroy local-only documents locally. Destroy synced documents only when all\n// links are in the subtree; preserve documents with an external link. The purge\n// plan reduces owned documents to the server-required sole link first.\nexport function classifySubtreeDocument(input: {\n readonly documentId: string | null;\n readonly linkedContainerIds: readonly string[];\n readonly preferredContainerId?: string | null | undefined;\n readonly subtreeContainerIds: ReadonlySet<string>;\n}): SubtreeDocumentDisposition {\n if (!input.documentId) {\n return { kind: \"purge-local\" };\n }\n const hasExternalLink = input.linkedContainerIds.some(\n (containerId) => !input.subtreeContainerIds.has(containerId),\n );\n if (!hasExternalLink) {\n const linkedContainerIds = Array.from(\n new Set(\n input.linkedContainerIds.filter((containerId) =>\n input.subtreeContainerIds.has(containerId),\n ),\n ),\n );\n const retainedContainerId =\n input.preferredContainerId &&\n linkedContainerIds.includes(input.preferredContainerId)\n ? input.preferredContainerId\n : linkedContainerIds[0];\n return {\n kind: \"purge\",\n unlinkContainerIds: linkedContainerIds.filter(\n (containerId) => containerId !== retainedContainerId,\n ),\n };\n }\n return {\n kind: \"unlink\",\n containerIds: input.linkedContainerIds.filter((containerId) =>\n input.subtreeContainerIds.has(containerId),\n ),\n };\n}\n\nasync function planSubtreeDocuments(input: {\n readonly execSql: PurgeContainerTreeRuntime[\"infra\"][\"execSql\"];\n readonly subtreeContainerIds: ReadonlySet<string>;\n}): Promise<SubtreeDocumentPlan> {\n const documents = await listDocumentsByContainerIdsOrDocumentIds(\n input.execSql,\n {\n containerIds: [...input.subtreeContainerIds],\n documentIds: [],\n },\n );\n const remoteDocumentIds = documents.flatMap((document) =>\n document.documentId ? [document.documentId] : [],\n );\n const linkedContainerIdsByDocumentId =\n await sqlDocumentContainerProjectionPersistence.listLinkedContainerIdsByDocumentIds(\n input.execSql,\n remoteDocumentIds,\n );\n\n const purge: Array<{\n document: DocumentSummary;\n unlinkContainerIds: readonly string[];\n }> = [];\n const purgeLocal: DocumentSummary[] = [];\n const unlinkByDocument = new Map<\n string,\n { document: DocumentSummary; containerIds: readonly string[] }\n >();\n for (const document of documents) {\n const disposition = classifySubtreeDocument({\n documentId: document.documentId ?? null,\n linkedContainerIds: document.documentId\n ? (linkedContainerIdsByDocumentId.get(document.documentId) ?? [])\n : [],\n preferredContainerId: document.containerId,\n subtreeContainerIds: input.subtreeContainerIds,\n });\n if (disposition.kind === \"purge-local\") {\n purgeLocal.push(document);\n } else if (disposition.kind === \"purge\") {\n purge.push({\n document,\n unlinkContainerIds: disposition.unlinkContainerIds,\n });\n } else if (document.documentId) {\n unlinkByDocument.set(document.documentId, {\n document,\n containerIds: disposition.containerIds,\n });\n }\n }\n\n return { purge, purgeLocal, unlinkByDocument };\n}\n\nasync function unlinkSubtreeDocument(input: {\n /** Rechecks the document and this exact unlink target before dispatch. */\n readonly beforeSubmit: (targetContainerId: string) => Promise<boolean>;\n readonly expectedSubtreeRoot: SubtreePurgeRoot;\n readonly stillCurrent?: (() => boolean) | undefined;\n readonly containerIds: readonly string[];\n readonly document: DocumentSummary;\n readonly prepareDocumentRotationSnapshot: PurgeContainerTreeInput[\"prepareDocumentRotationSnapshot\"];\n readonly resolveProjectionUserKey: ProjectionUserKeyResolver;\n readonly runtime: PurgeContainerTreeRuntime;\n}): Promise<boolean> {\n const documentId = input.document.documentId;\n if (!documentId) {\n return true;\n }\n let rotationSnapshot: Uint8Array | null;\n try {\n rotationSnapshot = await input.prepareDocumentRotationSnapshot(\n input.document,\n );\n } catch {\n return false;\n }\n if (!rotationSnapshot) {\n return false;\n }\n for (const containerId of input.containerIds) {\n const result = await unlinkRemoteContainerDocument({\n beforeSubmit: () => input.beforeSubmit(containerId),\n documentId,\n expectedSubtreeRoot: input.expectedSubtreeRoot,\n isCurrent: input.stillCurrent,\n noteId: input.document.id,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n rotationSnapshot,\n runtime: input.runtime,\n targetContainerId: containerId,\n });\n if (result === null) {\n return false;\n }\n }\n return true;\n}\n\ninterface SubtreeDocumentOperations {\n readonly purgeLocal: (document: DocumentSummary) => Promise<boolean>;\n readonly purgeRemote: (document: DocumentSummary) => Promise<boolean>;\n readonly unlink: (\n document: DocumentSummary,\n containerIds: readonly string[],\n ) => Promise<boolean>;\n}\n\nfunction resolveSubtreeDocumentOperations(\n input: PurgeContainerTreeInput,\n scope: ReturnType<typeof createSubtreePurgeScope>,\n): SubtreeDocumentOperations {\n const expectedSubtreeRoot = selectedSubtreeRoot(input);\n const operations: SubtreeDocumentOperations = input.documentOperations ?? {\n purgeLocal: async (document) =>\n (await purgeLocalContainerDocument({\n beforeDeleteInTransaction: (execSql) =>\n scope.assertLocalScopeInTransaction(\n execSql,\n document.containerId,\n document.id,\n ),\n noteId: document.id,\n expectedContainerId: document.containerId,\n runtime: input.runtime,\n })) !== null,\n purgeRemote: async (document) =>\n Boolean(\n document.documentId &&\n (await purgeRemoteContainerDocument({\n beforeSubmit: () => scope.allowsDocument(document),\n documentId: document.documentId,\n expectedSubtreeRoot,\n stillCurrent: input.stillCurrent,\n documentKind: document.documentKind ?? DEFAULT_DOCUMENT_KIND,\n noteId: document.id,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n runtime: input.runtime,\n })),\n ),\n unlink: (document, containerIds) =>\n unlinkSubtreeDocument({\n // Target preparation and signing can outlast the upfront checks.\n beforeSubmit: async (containerId) =>\n (await scope.allowsDocument(document)) &&\n (await scope.allowsContainer(containerId)),\n containerIds,\n document,\n expectedSubtreeRoot,\n stillCurrent: input.stillCurrent,\n prepareDocumentRotationSnapshot: input.prepareDocumentRotationSnapshot,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n runtime: input.runtime,\n }),\n };\n return {\n purgeLocal: async (document) =>\n (await scope.allowsDocument(document)) &&\n (await operations.purgeLocal(document)),\n purgeRemote: async (document) =>\n (await scope.allowsDocument(document)) &&\n (await operations.purgeRemote(document)),\n unlink: async (document, containerIds) => {\n if (!(await scope.allowsDocument(document))) return false;\n for (const containerId of containerIds) {\n if (!(await scope.allowsContainer(containerId))) return false;\n }\n return operations.unlink(document, containerIds);\n },\n };\n}\n\ninterface SubtreeTeardownResult {\n readonly aborted: boolean;\n}\n\n// Signed request paths must keep the root under the parent it was selected\n// from; a settled restore elsewhere preserves the root's own documents too.\nfunction selectedSubtreeRoot(\n input: Pick<PurgeContainerTreeInput, \"containersById\" | \"rootContainerId\">,\n): SubtreePurgeRoot {\n return {\n id: input.rootContainerId,\n parentId:\n input.containersById.get(input.rootContainerId)?.container.parentId ??\n null,\n };\n}\n\nfunction purgeWasCancelled(input: {\n readonly signal?: AbortSignal | undefined;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): boolean {\n return input.signal?.aborted === true || input.stillCurrent?.() === false;\n}\n\n// Tear down one whole document per progress step. A failed document is skipped,\n// leaving its container undeletable while the remaining subtree work continues.\n// Cancellation is honored only between complete document operations.\nasync function teardownSubtreeDocuments(input: {\n readonly documentOperations: SubtreeDocumentOperations;\n readonly plan: SubtreeDocumentPlan;\n readonly reportStep: (ok: boolean) => void;\n readonly signal?: AbortSignal | undefined;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<SubtreeTeardownResult> {\n for (const {\n document,\n containerIds,\n } of input.plan.unlinkByDocument.values()) {\n if (purgeWasCancelled(input)) {\n return { aborted: true };\n }\n const unlinked = await input.documentOperations.unlink(\n document,\n containerIds,\n );\n input.reportStep(unlinked);\n }\n for (const { document, unlinkContainerIds } of input.plan.purge) {\n if (purgeWasCancelled(input)) {\n return { aborted: true };\n }\n if (\n unlinkContainerIds.length > 0 &&\n !(await input.documentOperations.unlink(document, unlinkContainerIds))\n ) {\n input.reportStep(false);\n continue;\n }\n input.reportStep(await input.documentOperations.purgeRemote(document));\n }\n for (const document of input.plan.purgeLocal) {\n if (purgeWasCancelled(input)) {\n return { aborted: true };\n }\n input.reportStep(await input.documentOperations.purgeLocal(document));\n }\n return { aborted: false };\n}\n\n// Permanently destroy a folder that lives inside Trash, including everything\n// under it (or, with keepRootContainer, everything under the Trash bin while\n// leaving the bin in place — this is \"Empty Trash\"). Documents are destroyed only\n// when the subtree holds their last link; multi-folder documents are unlinked\n// from the subtree and preserved. Containers are deleted leaf-first so each\n// delete obeys the leaf-only constraint the store and server enforce. Every step\n// is remote-first then local (the per-document and per-container workflows handle\n// that internally), so a cancel between steps leaves local state reflecting\n// exactly the completed remote work.\n//\n// Progress is determinate: the plan (a pure read) completes before any\n// destructive step, so onProgress reports completed/failed against a total known\n// up front. A failed item is skipped rather than aborting the run, so the result\n// reports whatever prefix was actually destroyed alongside completed/failed\n// counts and whether a cancellation cut it short. Returns null only when the\n// target container is absent from the snapshot.\nexport async function purgeContainerTree(\n request: PurgeContainerTreeInput,\n): Promise<PurgeContainerTreeResult | null> {\n if (purgeWasCancelled(request)) {\n return null;\n }\n const input = {\n ...request,\n containersById: snapshotContainerStates(request.containersById),\n };\n const subtreeStates = collectSubtreeLeafFirst(\n input.containersById,\n input.rootContainerId,\n );\n if (subtreeStates.length === 0) {\n return null;\n }\n const subtreeContainerIds = new Set(\n subtreeStates.map((containerState) => containerState.container.id),\n );\n\n const plan = await planSubtreeDocuments({\n execSql: input.runtime.infra.execSql,\n subtreeContainerIds,\n });\n if (purgeWasCancelled(input)) {\n return null;\n }\n\n // Empty Trash keeps the Trash bin itself: tear down everything it holds, but\n // exclude the root from the container deletions (deleting a system container\n // would be rejected anyway).\n const containerStatesToDelete = input.keepRootContainer\n ? subtreeStates.filter(\n (containerState) =>\n containerState.container.id !== input.rootContainerId,\n )\n : subtreeStates;\n\n // The total is fully known here — planning is a pure read that completes before\n // any destructive step. Unlink work is counted per document (not per container\n // link) so completed can never exceed total even when a document has several\n // in-subtree links.\n const totalCount =\n plan.unlinkByDocument.size +\n plan.purge.length +\n plan.purgeLocal.length +\n containerStatesToDelete.length;\n\n let completedCount = 0;\n let failedCount = 0;\n const emitProgress = () => {\n input.onProgress?.({ completedCount, failedCount, totalCount });\n };\n const reportStep = (ok: boolean) => {\n if (ok) {\n completedCount += 1;\n } else {\n failedCount += 1;\n }\n emitProgress();\n };\n // Emit the initial 0/total so a determinate bar renders immediately, before the\n // first (potentially slow) remote call.\n emitProgress();\n\n const scope = createSubtreePurgeScope(input);\n const teardown = await teardownSubtreeDocuments({\n documentOperations: resolveSubtreeDocumentOperations(input, scope),\n plan,\n reportStep,\n signal: input.signal,\n stillCurrent: input.stillCurrent,\n });\n\n let purgedContainerIds: readonly string[] = [];\n let remoteDeletedContainerIds: readonly string[] = [];\n let aborted = teardown.aborted;\n if (!teardown.aborted) {\n const deletion = await deleteSubtreeContainers({\n allowsContainer: scope.allowsContainer,\n allowsLocalScopeInTransaction: scope.allowsLocalScopeInTransaction,\n root: selectedSubtreeRoot(input),\n persistence: input.persistence,\n reportStep,\n runtime: input.runtime,\n signal: input.signal,\n stillCurrent: input.stillCurrent,\n subtreeStates: containerStatesToDelete,\n });\n purgedContainerIds = deletion.purgedContainerIds;\n remoteDeletedContainerIds = deletion.remoteDeletedContainerIds;\n aborted = deletion.aborted;\n }\n\n return {\n aborted,\n completedCount,\n failedCount,\n purgedContainerIds,\n remoteDeletedContainerIds,\n totalCount,\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type {\n ContainerDeleteResponse,\n ContainerWriterProjectionResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type {
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type {\n ContainerDeleteResponse,\n ContainerWriterProjectionResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport type { DocumentRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type {\n createRemoteContainer,\n moveRemoteContainer,\n shareRemoteContainer,\n} from \"../../containers\";\nimport type { createRemoteDocument } from \"../../documents\";\nimport type { shareRemoteContainerWithGroup } from \"../../organizations/groupShare\";\nimport type { CurrentPrincipalPolicyReader } from \"../../principals/currentPrincipalPolicyReader\";\nimport type { PrincipalPolicyHistoryReader } from \"../../principals/principalPolicyHistoryReader\";\nimport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerMoveIntentRecord,\n} from \"../containerPersistence\";\nimport type { createInitializedContainerMetadataDocument } from \"../metadata/containerMetadataDocument\";\nimport type {\n ContainerState,\n RemoteContainer as HydratedRemoteContainer,\n RemoteContainerHydrationHost,\n} from \"../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ContainerWorkflowApi = Parameters<\n typeof createRemoteContainer\n>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainerWithGroup>[0][\"apiClient\"] &\n Parameters<typeof moveRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof createRemoteDocument>[0][\"apiClient\"] &\n Partial<PrincipalPolicyHistoryReader> &\n CurrentPrincipalPolicyReader & {\n deleteContainerResult(\n containerId: string,\n options?: {\n expectedPaymentRequiredOrganizationId?: string;\n reportErrors?: boolean;\n requiredAncestorId?: string | undefined;\n },\n ): Promise<\n | { data: ContainerDeleteResponse; ok: true }\n | {\n ok: false;\n report: () => void;\n status: number | null;\n }\n >;\n primeContainerWriterProjection(\n containerId: string,\n projection: ContainerWriterProjectionResponse,\n ): void;\n };\n\nexport interface ContainerWorkflowRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n readonly apiClient: ContainerWorkflowApi;\n}\n\nexport interface CreatedRemoteContainerState {\n accessManifestHash: string;\n /**\n * The metadata seed the create committed, settled by the caller instead of\n * a first metadata sync. Absent when the create carried no seed or fell back\n * to a plain create.\n */\n metadataSeed?:\n | { readonly commitLsn: string | null; readonly updateId: string }\n | undefined;\n /** The slot this client signed into the create; never the server's echo. */\n systemSlot: ContainerSystemSlot | null;\n containerId: string;\n createdAt: string;\n metadataDocumentId: string;\n organizationId: string;\n /** A created container is a child; the acknowledgement echoed this parent. */\n parentId: string;\n persistedMetadataState: Pick<\n DocumentRecord,\n | \"documentId\"\n | \"contentKeyBundle\"\n | \"documentKekTargets\"\n | \"documentManifestBundle\"\n >;\n updatedAt: string;\n}\n\nexport interface SharedRemoteContainerState {\n accessEpoch: number;\n accessManifestHash: string;\n createdAt: string;\n metadataDocumentId: string;\n referencedPrincipalHeads: ReferencedPrincipalStateResponse[];\n updatedAt: string;\n writerProjection: ContainerWriterProjectionResponse | null;\n}\n\nexport interface SharedContainerState {\n container: ContainerState[\"container\"];\n record: ContainerState[\"record\"];\n}\n\nexport type SharedContainerStateResult =\n | { status: \"missing\" }\n | { status: \"confirmed\" }\n | (SharedContainerState & {\n status: \"identity-superseded\" | \"persisted\";\n });\n\nexport type RemoteContainer = HydratedRemoteContainer;\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createInitializedContainerMetadataDocument>\n>[\"doc\"];\n\nexport interface CreatedChildContainerState {\n containerState: ContainerState;\n initialUpdate: Uint8Array;\n shouldRequestSync: boolean;\n}\n\nexport interface ContainerIntentSyncState {\n containersById: ReadonlyMap<string, ContainerState>;\n /**\n * Metadata update ids this device sent, so their own update echoes are\n * classified as self-authored (shared with the metadata sync).\n */\n locallyAcceptedMetadataUpdateIds?: Set<string> | undefined;\n /** Metadata document ids of folder creates in flight (shared with hints). */\n plannedMetadataDocumentIds?: Set<string> | undefined;\n persistence: ContainerContentsPersistence;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n /**\n * Verifies that a listed container is this device's pending create before it\n * is adopted and returns where it sits; `assertContainerCreateAdoptable`\n * unless a test replaces it.\n */\n verifyCreateAdoption?: (input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n }) => Promise<AdoptedContainerPlacement>;\n}\n\n/** Where a verified listed create committed, and where its container is now. */\nexport interface AdoptedContainerPlacement {\n /** The parent its signed epoch-1 `container.create` named. */\n readonly createdParentId: string;\n /** The verified head's parent; another writer may have moved it since. */\n readonly currentParentId: string;\n}\n\nexport type ContainerCreateIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerCreateIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\"\n>;\n\nexport interface ContainerCreateIntentSyncInput {\n host: ContainerCreateIntentSyncHost;\n /** A parent whose own create has not settled takes no child create. */\n isCreatePending: (containerId: string) => boolean;\n isCurrent: () => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerCreateIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerCreateIntentSyncState;\n}\n\nexport type ContainerMoveIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerMoveIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\" | \"updateSnapshot\"\n>;\n\n/** Later passes for moves the server accepted but no pass settled. */\nexport interface AcceptedMoveRetry {\n /** The server holds the move: schedule a replay to settle it. */\n arm: (intentId: string) => void;\n /** A replay failed; continue an accepted move's retries, if it has some. */\n resume: (intentId: string) => void;\n}\n\nexport interface ContainerMoveIntentSyncInput {\n host: ContainerMoveIntentSyncHost;\n isCurrent: () => boolean;\n /** A folder whose create has not settled takes no structural writes. */\n isCreatePending: (containerId: string) => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerMoveIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n acceptedMoveRetry: AcceptedMoveRetry;\n state: ContainerMoveIntentSyncState;\n}\n"]}
|