@tearleads/client-sdk 0.1.78 → 0.1.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/deviceFirst/index.d.ts +1 -1
- package/client/deviceFirst/index.js +1 -1
- package/client/deviceFirst/index.js.map +1 -1
- package/client/runtime/principalMutationApi.js +1 -1
- package/client/runtime/principalMutationApi.js.map +1 -1
- package/client/runtime/principalMutationDispatch.js +1 -1
- package/client/runtime/principalMutationDispatch.js.map +1 -1
- package/client/runtime/rootContainerAdoption.js.map +1 -1
- package/client/runtime/workflowRuntime.d.ts +1 -1
- package/client/runtime/workflowRuntime.js.map +1 -1
- package/client/session/sessionPurgeRecovery.js +1 -1
- package/client/session/sessionPurgeRecovery.js.map +1 -1
- package/data/blobs/attachments/projection.js +2 -3
- package/data/blobs/attachments/projection.js.map +1 -1
- package/data/documents/documentConstants.d.ts +1 -0
- package/data/documents/documentConstants.js +1 -0
- package/data/documents/documentConstants.js.map +1 -1
- package/data/documents/shared/containerKek/containerKekEntryVerification.d.ts +3 -0
- package/data/documents/shared/containerKek/index.d.ts +5 -0
- package/data/documents/shared/containerKek/index.js +9 -0
- package/data/documents/shared/containerKek/index.js.map +1 -0
- package/data/documents/shared/projection/index.d.ts +1 -1
- package/data/documents/shared/projection/index.js +1 -1
- package/data/documents/shared/projection/index.js.map +1 -1
- package/data/{sync → documents/shared/sync}/documentSyncOutgoingBatch.d.ts +1 -1
- package/data/{sync → documents/shared/sync}/documentSyncOutgoingBatch.js +1 -1
- package/data/documents/shared/sync/syncResponseAuthorization.js +1 -1
- package/data/documents/shared/sync/syncResponseAuthorization.js.map +1 -1
- package/data/keyingProjectionVerification/container/containerCreateManifest.d.ts +12 -0
- package/data/keyingProjectionVerification/container/containerDestinationVerification.d.ts +16 -0
- package/data/keyingProjectionVerification/index.d.ts +4 -0
- package/data/keyingProjectionVerification/index.js +5 -1
- package/data/keyingProjectionVerification/index.js.map +1 -1
- package/data/keyingProjectionVerification/principalPolicy/mergeProjectionPolicyEvidence.d.ts +3 -0
- package/data/keyingProjectionVerification/principalPolicy/principalPolicyVerification.d.ts +24 -0
- package/data/persistence/container-contents/documentPlacement.js +1 -1
- package/data/persistence/container-contents/documentPlacement.js.map +1 -1
- package/data/persistence/documents/containerDocumentTombstoneHoldsPersistence.js +1 -1
- package/data/persistence/documents/containerDocumentTombstoneHoldsPersistence.js.map +1 -1
- package/data/persistence/documents/documentsPersistence.d.ts +1 -1
- package/data/persistence/documents/documentsPersistence.js +2 -3
- package/data/persistence/documents/documentsPersistence.js.map +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstones.js +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstones.js.map +1 -1
- package/data/persistence/documents/internal/createDocumentWithHistoryCheckpoint.js +1 -1
- package/data/persistence/documents/internal/createDocumentWithHistoryCheckpoint.js.map +1 -1
- package/data/persistence/documents/internal/documentDeletionPersistence.js +1 -1
- package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
- package/data/persistence/documents/internal/documentMutationPersistence.js +1 -1
- package/data/persistence/documents/internal/documentMutationPersistence.js.map +1 -1
- package/data/persistence/documents/internal/documentProjectionRows.js +1 -2
- package/data/persistence/documents/internal/documentProjectionRows.js.map +1 -1
- package/data/persistence/documents/internal/documentPullContinuationPersistence.js +1 -1
- package/data/persistence/documents/internal/documentPullContinuationPersistence.js.map +1 -1
- package/data/persistence/documents/internal/documentRows.js +1 -2
- package/data/persistence/documents/internal/documentRows.js.map +1 -1
- package/data/persistence/documents/internal/orphanSideRows.js +1 -1
- package/data/persistence/documents/internal/orphanSideRows.js.map +1 -1
- package/data/persistence/documents/internal/pendingCreateAdoption.js +1 -1
- package/data/persistence/documents/internal/pendingCreateAdoption.js.map +1 -1
- package/data/persistence/documents/internal/rowQueryPersistence.js +1 -1
- package/data/persistence/documents/internal/rowQueryPersistence.js.map +1 -1
- package/package.json +1 -1
- package/stores/local-projection/localProjectionStore.d.ts +1 -1
- package/stores/local-projection/localProjectionStore.js +1 -1
- package/stores/local-projection/localProjectionStore.js.map +1 -1
- package/stores/local-projection/registry.d.ts +1 -1
- package/stores/local-projection/registry.js.map +1 -1
- package/stores/local-projection/types.d.ts +1 -1
- package/stores/local-projection/types.js.map +1 -1
- package/workflows/blobs/hydration/decrypt.js +1 -2
- package/workflows/blobs/hydration/decrypt.js.map +1 -1
- package/workflows/blobs/hydration/hydrate.d.ts +1 -1
- package/workflows/blobs/hydration/hydrate.js.map +1 -1
- package/workflows/container-contents/container-state/create/createAdoption.js +1 -2
- package/workflows/container-contents/container-state/create/createAdoption.js.map +1 -1
- package/workflows/container-contents/container-state/purge/purgeTreeScope.js +1 -1
- package/workflows/container-contents/container-state/purge/purgeTreeScope.js.map +1 -1
- package/workflows/container-contents/info/documentInfo.js +1 -1
- package/workflows/container-contents/info/documentInfo.js.map +1 -1
- package/workflows/container-contents/metadata/index.js +1 -2
- package/workflows/container-contents/metadata/index.js.map +1 -1
- package/workflows/container-contents/metadata/metadataMutationPreparation.js +1 -1
- package/workflows/container-contents/metadata/metadataMutationPreparation.js.map +1 -1
- package/workflows/container-contents/metadata/metadataPullContinuationInvalidation.js +1 -1
- package/workflows/container-contents/metadata/metadataPullContinuationInvalidation.js.map +1 -1
- package/workflows/container-contents/metadata/metadataSyncErrors.js +1 -1
- package/workflows/container-contents/metadata/metadataSyncErrors.js.map +1 -1
- package/workflows/container-contents/metadata/metadataSyncSettlement.d.ts +1 -1
- package/workflows/container-contents/metadata/metadataSyncSettlement.js.map +1 -1
- package/workflows/container-contents/metadata/metadataTypes.d.ts +1 -1
- package/workflows/container-contents/metadata/metadataTypes.js.map +1 -1
- package/workflows/container-contents/pendingWrites/index.js +1 -1
- package/workflows/container-contents/pendingWrites/index.js.map +1 -1
- package/workflows/container-contents/remoteHydration/destination/verifiedDestination.js +1 -3
- package/workflows/container-contents/remoteHydration/destination/verifiedDestination.js.map +1 -1
- package/workflows/containers/child/create/index.js +1 -1
- package/workflows/containers/child/create/index.js.map +1 -1
- package/workflows/containers/child/move/moveRotation.js +1 -1
- package/workflows/containers/child/move/moveRotation.js.map +1 -1
- package/workflows/containers/child/move/moveWraps.js +1 -1
- package/workflows/containers/child/move/moveWraps.js.map +1 -1
- package/workflows/containers/child/rekey/rekeyProjection.js +1 -1
- package/workflows/containers/child/rekey/rekeyProjection.js.map +1 -1
- package/workflows/containers/child/rotation/rotationContext.js +1 -2
- package/workflows/containers/child/rotation/rotationContext.js.map +1 -1
- package/workflows/containers/child/share/shareAncestorRepair.js +1 -2
- package/workflows/containers/child/share/shareAncestorRepair.js.map +1 -1
- package/workflows/containers/index.d.ts +2 -3
- package/workflows/containers/index.js +1 -2
- package/workflows/containers/index.js.map +1 -1
- package/workflows/containers/writeRefusal.js +1 -1
- package/workflows/containers/writeRefusal.js.map +1 -1
- package/workflows/documents/index.d.ts +1 -1
- package/workflows/documents/index.js +1 -1
- package/workflows/documents/index.js.map +1 -1
- package/workflows/documents/linkSet/linkSetProjectionSeed.js +1 -1
- package/workflows/documents/linkSet/linkSetProjectionSeed.js.map +1 -1
- package/workflows/documents/sync/readOnlySync.js +1 -1
- package/workflows/documents/sync/readOnlySync.js.map +1 -1
- package/workflows/documents/sync/syncAttemptPlanning.js +1 -1
- package/workflows/documents/sync/syncAttemptPlanning.js.map +1 -1
- package/workflows/documents/syncPlan/syncPlanMaterial.js +1 -1
- package/workflows/documents/syncPlan/syncPlanMaterial.js.map +1 -1
- package/workflows/documents/syncPlan/syncPlanRequestBounds.js +1 -1
- package/workflows/documents/syncPlan/syncPlanRequestBounds.js.map +1 -1
- package/workflows/documents/syncRekeys/syncAutomaticContainerRekeys.js +1 -1
- package/workflows/documents/syncRekeys/syncAutomaticContainerRekeys.js.map +1 -1
- package/workflows/organizations/groupMetadata/groupMetadataAccess.js +1 -1
- package/workflows/organizations/groupMetadata/groupMetadataAccess.js.map +1 -1
- package/workflows/organizations/provisioning/createOrganization.d.ts +1 -1
- package/workflows/organizations/provisioning/createOrganization.js +1 -1
- package/workflows/organizations/provisioning/createOrganization.js.map +1 -1
- package/workflows/organizations/provisioning/organizationProvisioningAttempt.d.ts +1 -1
- package/workflows/organizations/provisioning/organizationProvisioningAttempt.js.map +1 -1
- package/workflows/organizations/readModel/listLocalOrganizations.js +1 -1
- package/workflows/organizations/readModel/listLocalOrganizations.js.map +1 -1
- package/workflows/registration/index.d.ts +2 -1
- package/workflows/registration/index.js +2 -1
- package/workflows/registration/index.js.map +1 -1
- package/workflows/registration/organizationProvisioning.d.ts +39 -0
- package/workflows/registration/organizationProvisioning.js +211 -0
- package/workflows/registration/organizationProvisioning.js.map +1 -0
- package/workflows/registration/organizationProvisioningArtifacts.d.ts +2 -3
- package/workflows/registration/organizationProvisioningArtifacts.js.map +1 -1
- package/workflows/registration/provisioningBootstrapBuilders.d.ts +3 -3
- package/workflows/registration/provisioningBootstrapBuilders.js +3 -4
- package/workflows/registration/provisioningBootstrapBuilders.js.map +1 -1
- package/workflows/registration/registerIdentity.d.ts +2 -39
- package/workflows/registration/registerIdentity.js +2 -207
- package/workflows/registration/registerIdentity.js.map +1 -1
- package/workflows/sync/remoteResetPlans.js +2 -2
- package/workflows/sync/remoteResetPlans.js.map +1 -1
- package/data/persistence/documents/internal/constants.d.ts +0 -1
- package/data/persistence/documents/internal/constants.js +0 -2
- package/data/persistence/documents/internal/constants.js.map +0 -1
- /package/data/{sync → documents/shared/sync}/documentSyncOutgoingBatch.js.map +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readOnlySync.js","sourceRoot":"","sources":["readOnlySync.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DocumentCreateResponse,\n DocumentSyncResponse,\n DocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport {\n isAccessManifestBundleWireResponse,\n isDocumentContentKeyBundleResponse,\n isDocumentKekTargetsResponse,\n} from \"@tearleads/validators/response\";\nimport {\n isRetryableDocumentSyncConflict,\n persistedDocumentSyncStateFromResponse,\n submitDocumentSync,\n} from \"../../../data/documents/shared/responses\";\nimport type {\n DocumentSyncUpdateIsolationError,\n IncomingDocumentSyncUpdateValidator,\n} from \"../../../data/documents/shared/sync/documentSyncUpdateIsolation\";\nimport {\n type DocumentSyncPullContinuation,\n resolvePullContinuationMinLsn,\n} from \"../../../data/documents/shared/sync/syncPagination\";\nimport type {\n DocumentCreateAuthor,\n DocumentSyncApi,\n DocumentSyncPlan,\n DocumentWriterPublicKeyResolver,\n PersistedDocumentSyncState,\n SyncRemoteDocumentResult,\n} from \"../../../data/documents/shared/types\";\nimport { projectionVerificationOptions } from \"../../../data/documents/shared/types\";\nimport { readCanonicalRecord } from \"../../../data/keying/canonicalJson\";\nimport type {\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../../../data/keyingProjectionVerification\";\nimport type { PendingUpdateRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { limitDocumentSyncRequestBytes } from \"../../../data/sync/documentSyncOutgoingBatch\";\nimport {\n assertRawContinuationCanRetry,\n refreshSyncAttemptWriterProjection,\n resolveSyncAttemptWriterProjection,\n} from \"../syncFailures\";\nimport {\n handleUpstreamDeletedDocumentSyncFailure,\n projectionIntegrityErrorCode,\n type RemoteDocumentDeletionHandler,\n type TerminalSubmitFailureHandler,\n} from \"../syncFailures/syncFailureClassification\";\nimport { DocumentRawHistoryUnavailableError } from \"../syncPlan/syncContentKeys\";\nimport { buildDocumentSyncPlan } from \"../syncPlan/syncPlanIdentity\";\nimport { buildMaterializedDocumentSyncPlan } from \"../syncPlan/syncPlanMaterial\";\nimport type { RekeyPendingUpdate } from \"../syncRekeys/syncRecoveryRekey\";\nimport {\n handleReadOnlyProjectionCompletionError,\n REFRESH_CACHED_PROJECTION,\n} from \"./readOnlyProjectionFailure\";\nimport type { DocumentSyncContainerRekeyBuilder } from \"./syncAttemptState\";\nimport { syncRemoteDocumentResultFromResponse } from \"./syncResponseResult\";\nimport { type DocumentSyncTraceEmitter, traceSubmitFailed } from \"./syncTrace\";\n\nasync function completeReadOnlyRemoteDocumentSyncWithProjection(\n input: Omit<ReadOnlyDocumentSyncCompletionInput, \"writerProjection\"> & {\n writerProjection: DocumentWriterProjectionResponse;\n },\n): Promise<SyncRemoteDocumentResult> {\n const materializedPlan = await buildMaterializedDocumentSyncPlan({\n author: input.author,\n execSql: input.execSql,\n historyMode: input.historyMode,\n localVersionVector: input.localVersionVector,\n minLsn: resolvePullContinuationMinLsn(input.pullContinuation, input.minLsn),\n pullCursor: input.pullContinuation?.cursor,\n onSyncTrace: input.onSyncTrace,\n pendingUpdates: [],\n signedAt: input.signedAt,\n targetSecretKey: input.targetSecretKey,\n writerProjection: input.writerProjection,\n ...projectionVerificationOptions(input),\n });\n\n const result = await syncRemoteDocumentResultFromResponse({\n ...projectionVerificationOptions(input),\n execSql: input.execSql,\n materializedPlan,\n recoveryPendingUpdatesById: new Map(),\n resolveWriterPublicKey: input.resolveWriterPublicKey,\n response: input.response,\n targetSecretKey: input.targetSecretKey,\n validateIncomingUpdates: input.validateIncomingUpdates,\n writerProjection: input.writerProjection,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n });\n return {\n ...result,\n hasIncompletePull: !input.pullComplete,\n };\n}\n\nfunction parsePersistedDocumentSyncRecord<T>(\n value: string | null | undefined,\n label: string,\n isRecord: (value: unknown) => value is T,\n): T | null {\n if (!value) {\n return null;\n }\n\n try {\n const record = readCanonicalRecord(JSON.parse(value), label);\n return isRecord(record) ? record : null;\n } catch {\n return null;\n }\n}\n\nfunction parsePersistedDocumentSyncState(\n persistedState: PersistedDocumentSyncState | null | undefined,\n documentId: string,\n): {\n contentKeyBundle: DocumentCreateResponse[\"contentKeyBundle\"];\n documentKekTargets: DocumentSyncResponse[\"documentKekTargets\"];\n documentManifest: DocumentCreateResponse[\"accessManifest\"];\n} | null {\n if (persistedState?.documentId !== documentId) {\n return null;\n }\n\n const contentKeyBundle = parsePersistedDocumentSyncRecord(\n persistedState.contentKeyBundle,\n \"Persisted document sync content-key bundle\",\n isDocumentContentKeyBundleResponse,\n );\n const documentKekTargets = parsePersistedDocumentSyncRecord(\n persistedState.documentKekTargets,\n \"Persisted document sync KEK targets\",\n isDocumentKekTargetsResponse,\n );\n const documentManifest = parsePersistedDocumentSyncRecord(\n persistedState.documentManifestBundle,\n \"Persisted document sync manifest\",\n isAccessManifestBundleWireResponse,\n );\n\n if (!contentKeyBundle || !documentKekTargets || !documentManifest) {\n return null;\n }\n\n return {\n contentKeyBundle,\n documentKekTargets,\n documentManifest,\n };\n}\n\nasync function buildReadOnlyDocumentSyncPlanFromPersistedState(input: {\n author: DocumentCreateAuthor;\n documentId: string;\n historyMode?: \"raw\" | undefined;\n localVersionVector: string | null;\n minLsn?: string | undefined;\n pullContinuation?: DocumentSyncPullContinuation | undefined;\n persistedState?: PersistedDocumentSyncState | null | undefined;\n signedAt?: string | undefined;\n}): Promise<DocumentSyncPlan | null> {\n const persisted = parsePersistedDocumentSyncState(\n input.persistedState,\n input.documentId,\n );\n if (!persisted) {\n return null;\n }\n\n const plan = await buildDocumentSyncPlan({\n author: input.author,\n contentKeyBundle: persisted.contentKeyBundle,\n documentId: input.documentId,\n documentKekTargets: persisted.documentKekTargets,\n documentManifest: persisted.documentManifest,\n historyMode: input.historyMode,\n localVersionVector: input.localVersionVector,\n minLsn: resolvePullContinuationMinLsn(input.pullContinuation, input.minLsn),\n outgoingUpdates: [],\n pullCursor: input.pullContinuation?.cursor,\n signedAt: input.signedAt,\n });\n const request = limitDocumentSyncRequestBytes(plan.request);\n return request === plan.request ? plan : { ...plan, request };\n}\n\ntype PersistedReadOnlyDocumentSyncResult =\n | {\n kind: \"completed\";\n result: SyncRemoteDocumentResult | null;\n }\n | {\n kind: \"not_completed\";\n };\n\ninterface ReadOnlyDocumentSyncCompletionInput {\n apiClient: DocumentSyncApi;\n author: DocumentCreateAuthor;\n documentId: string;\n execSql: ExecSql;\n historyMode?: \"raw\" | undefined;\n localVersionVector: string | null;\n minLsn?: string | undefined;\n pullContinuation?: DocumentSyncPullContinuation | undefined;\n onReadOnlyProjectionFailure?: TerminalSubmitFailureHandler | undefined;\n onRemoteDocumentDeleted?: RemoteDocumentDeletionHandler | undefined;\n onSyncTrace?: DocumentSyncTraceEmitter | undefined;\n pullComplete: boolean;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n resolveWriterPublicKey: DocumentWriterPublicKeyResolver;\n response: DocumentSyncResponse;\n signedAt?: string | undefined;\n stillCurrent?: (() => boolean) | undefined;\n targetSecretKey: Uint8Array;\n validateIncomingUpdates: IncomingDocumentSyncUpdateValidator;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n writerProjection?: DocumentWriterProjectionResponse | undefined;\n}\n\nasync function tryCompleteReadOnlyRemoteDocumentSyncWithProjection(input: {\n allowCachedProjectionRefresh: boolean;\n completion: ReadOnlyDocumentSyncCompletionInput;\n writerProjection: DocumentWriterProjectionResponse;\n}): Promise<\n | DocumentRawHistoryUnavailableError\n | SyncRemoteDocumentResult\n | null\n | typeof REFRESH_CACHED_PROJECTION\n> {\n try {\n return await completeReadOnlyRemoteDocumentSyncWithProjection({\n ...input.completion,\n writerProjection: input.writerProjection,\n });\n } catch (error) {\n return handleReadOnlyProjectionCompletionError(error, {\n allowCachedProjectionRefresh: input.allowCachedProjectionRefresh,\n historyMode: input.completion.historyMode,\n });\n }\n}\nasync function completeReadOnlyRemoteDocumentSyncWithUpdates(\n input: ReadOnlyDocumentSyncCompletionInput,\n): Promise<PersistedReadOnlyDocumentSyncResult> {\n const reusableWriterProjection =\n input.writerProjection?.documentId === input.documentId\n ? input.writerProjection\n : null;\n const writerProjection =\n reusableWriterProjection ??\n (await resolveSyncAttemptWriterProjection({\n apiClient: input.apiClient,\n documentId: input.documentId,\n onRemoteDocumentDeleted: input.onRemoteDocumentDeleted,\n onSyncTrace: input.onSyncTrace,\n onTerminalFailure: input.onReadOnlyProjectionFailure,\n reusableWriterProjection: null,\n stillCurrent: input.stillCurrent,\n }));\n if (!writerProjection) {\n return { kind: \"completed\", result: null };\n }\n const canRefreshProjection =\n input.historyMode === \"raw\" || Boolean(reusableWriterProjection);\n let result:\n | DocumentRawHistoryUnavailableError\n | SyncRemoteDocumentResult\n | null\n | typeof REFRESH_CACHED_PROJECTION = null;\n let retryAfterRollback = false;\n try {\n result = await tryCompleteReadOnlyRemoteDocumentSyncWithProjection({\n allowCachedProjectionRefresh: canRefreshProjection,\n completion: input,\n writerProjection,\n });\n } catch (error) {\n if (projectionIntegrityErrorCode(error) !== \"rollback\") {\n throw error;\n }\n retryAfterRollback = true;\n }\n if (\n !retryAfterRollback &&\n result !== REFRESH_CACHED_PROJECTION &&\n !(result instanceof DocumentRawHistoryUnavailableError) &&\n (result || !reusableWriterProjection)\n ) {\n return { kind: \"completed\", result };\n }\n const freshWriterProjection = await refreshSyncAttemptWriterProjection({\n apiClient: input.apiClient,\n documentId: input.documentId,\n onRemoteDocumentDeleted: input.onRemoteDocumentDeleted,\n onSyncTrace: input.onSyncTrace,\n onTerminalFailure: input.onReadOnlyProjectionFailure,\n stillCurrent: input.stillCurrent,\n unavailableError:\n result instanceof DocumentRawHistoryUnavailableError ? result : undefined,\n });\n if (!freshWriterProjection) {\n return { kind: \"completed\", result: null };\n }\n const freshResult = await tryCompleteReadOnlyRemoteDocumentSyncWithProjection(\n {\n allowCachedProjectionRefresh: false,\n completion: input,\n writerProjection: freshWriterProjection,\n },\n );\n if (\n freshResult === REFRESH_CACHED_PROJECTION ||\n freshResult instanceof DocumentRawHistoryUnavailableError\n ) {\n throw new Error(\"Fresh document projection unexpectedly requested refresh\");\n }\n return freshResult\n ? { kind: \"completed\", result: freshResult }\n : { kind: \"not_completed\" };\n}\nasync function syncReadOnlyRemoteDocumentFromPersistedState(\n input: Omit<\n ReadOnlyDocumentSyncCompletionInput,\n \"pullComplete\" | \"response\"\n > & {\n persistedState?: PersistedDocumentSyncState | null | undefined;\n },\n): Promise<PersistedReadOnlyDocumentSyncResult> {\n let plan: DocumentSyncPlan | null;\n try {\n plan = await buildReadOnlyDocumentSyncPlanFromPersistedState(input);\n } catch {\n plan = null;\n }\n if (!plan) {\n return { kind: \"not_completed\" };\n }\n if (input.stillCurrent?.() === false) {\n return { kind: \"completed\", result: null };\n }\n const submitted = await submitDocumentSync({\n apiClient: input.apiClient,\n expectedCommitLsnMode: input.pullContinuation?.commitLsnMode,\n plan,\n });\n if (input.stillCurrent?.() === false) {\n return { kind: \"completed\", result: null };\n }\n if (!submitted) {\n return { kind: \"completed\", result: null };\n }\n if (!submitted.ok) {\n if (\n await handleUpstreamDeletedDocumentSyncFailure({\n documentId: input.documentId,\n failure: submitted,\n onRemoteDocumentDeleted: input.onRemoteDocumentDeleted,\n })\n ) {\n return { kind: \"completed\", result: null };\n }\n\n if (isRetryableDocumentSyncConflict(submitted)) {\n traceSubmitFailed(input.onSyncTrace, {\n action: \"retry\",\n code: submitted.code,\n documentId: input.documentId,\n status: submitted.status,\n });\n assertRawContinuationCanRetry(input.historyMode, input.pullContinuation);\n return { kind: \"not_completed\" };\n }\n\n traceSubmitFailed(input.onSyncTrace, {\n action: \"stop\",\n code: submitted.code,\n documentId: input.documentId,\n status: submitted.status,\n });\n submitted.report();\n return { kind: \"completed\", result: null };\n }\n\n if (submitted.response.updates.length > 0) {\n return completeReadOnlyRemoteDocumentSyncWithUpdates({\n ...input,\n pullComplete: submitted.pullComplete,\n response: submitted.response,\n });\n }\n\n try {\n const persistedState = await persistedDocumentSyncStateFromResponse(\n plan,\n submitted.response,\n { resolveWriterPublicKey: input.resolveWriterPublicKey },\n );\n\n return {\n kind: \"completed\",\n result: {\n contentKey: new Uint8Array(),\n decryptedUpdates: [],\n exhaustedPendingUpdateCount: 0,\n hasDeferredPendingUpdates: false,\n hasIncompletePull: !submitted.pullComplete,\n persistedState,\n plan,\n rekeyedPendingUpdateIds: [],\n response: submitted.response,\n settledPendingUpdateIds: [],\n acceptedRecoveryBaseline: false,\n },\n };\n } catch (error) {\n if (input.historyMode === \"raw\") {\n throw error;\n }\n return { kind: \"not_completed\" };\n }\n}\n\nexport interface SyncRemoteDocumentInput {\n apiClient: DocumentSyncApi;\n author: DocumentCreateAuthor;\n /**\n * Supplies a full-history Loro snapshot of the local document so a\n * write-bearing pass can heal a stale content-key bundle by rotating to a\n * fresh content key anchored by a rotation baseline.\n */\n buildRotationSnapshot?: (() => Promise<Uint8Array | null>) | undefined;\n /** Overrides automatic ancestor repair for each submission's current projection. */\n buildContainerRekeys?: DocumentSyncContainerRekeyBuilder;\n documentId: string;\n execSql: ExecSql;\n /** Explicit read-only recovery that bypasses rotation-baseline redirect. */\n historyMode?: \"raw\" | undefined;\n isRemoteSyncBlocked?: ((organizationId: string) => boolean) | undefined;\n localVersionVector: string | null;\n minLsn?: string | undefined;\n pullContinuation?: DocumentSyncPullContinuation | undefined;\n /** Atomically commits the verified purge proof with matching local teardown. */\n onRemoteDocumentDeleted?: RemoteDocumentDeletionHandler | undefined;\n onSyncAbandoned?: ((reason: string) => void) | undefined;\n /** Clipboard-safe trace sink (see syncTrace.ts); never receives content. */\n onSyncTrace?: DocumentSyncTraceEmitter | undefined;\n /**\n * Fires when a pass WITHOUT queued writes cannot resolve its writer\n * projection (e.g. a coded 409 from the projection route). Read-only\n * revalidation otherwise fails silently — one burned request and a trace\n * line — leaving the document permanently stale with nothing durable to\n * explain why (edge-case row 13).\n */\n onReadOnlyProjectionFailure?: TerminalSubmitFailureHandler | undefined;\n /** Records a verified response update that could not be decrypted/imported. */\n onIncomingUpdateIsolationFailure?:\n | ((failure: DocumentSyncUpdateIsolationError) => void | Promise<void>)\n | undefined;\n /** Fires immediately before a materialized outgoing batch is submitted. */\n onOutgoingUpdatesMaterialized?:\n | ((updateIds: readonly string[]) => void)\n | undefined;\n /** Invalidates continuation state after the server rejects its snapshot. */\n onPullContinuationInvalidated?:\n | ((continuation: DocumentSyncPullContinuation) => void | Promise<void>)\n | undefined;\n onTerminalSubmitFailure?: TerminalSubmitFailureHandler | undefined;\n pendingUpdates?: readonly PendingUpdateRecord[] | undefined;\n persistedState?: PersistedDocumentSyncState | null | undefined;\n rekeyPendingUpdate?: RekeyPendingUpdate | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n resolveWriterPublicKey: DocumentWriterPublicKeyResolver;\n signedAt?: string | undefined;\n stillCurrent?: (() => boolean) | undefined;\n targetSecretKey: Uint8Array;\n /** Validates decrypted updates against scratch state before callers persist. */\n validateIncomingUpdates: IncomingDocumentSyncUpdateValidator;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n writerProjection?: DocumentWriterProjectionResponse | undefined;\n}\n\nexport async function tryPersistedReadOnlyDocumentSync(\n input: SyncRemoteDocumentInput,\n resolveProjectionUserKey: ProjectionUserKeyResolver,\n): Promise<PersistedReadOnlyDocumentSyncResult | null> {\n if (input.stillCurrent?.() === false) return null;\n if ((input.pendingUpdates ?? []).length > 0) return null;\n\n return syncReadOnlyRemoteDocumentFromPersistedState({\n ...input,\n resolveProjectionUserKey,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"readOnlySync.js","sourceRoot":"","sources":["readOnlySync.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DocumentCreateResponse,\n DocumentSyncResponse,\n DocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport {\n isAccessManifestBundleWireResponse,\n isDocumentContentKeyBundleResponse,\n isDocumentKekTargetsResponse,\n} from \"@tearleads/validators/response\";\nimport {\n isRetryableDocumentSyncConflict,\n persistedDocumentSyncStateFromResponse,\n submitDocumentSync,\n} from \"../../../data/documents/shared/responses\";\nimport { limitDocumentSyncRequestBytes } from \"../../../data/documents/shared/sync/documentSyncOutgoingBatch\";\nimport type {\n DocumentSyncUpdateIsolationError,\n IncomingDocumentSyncUpdateValidator,\n} from \"../../../data/documents/shared/sync/documentSyncUpdateIsolation\";\nimport {\n type DocumentSyncPullContinuation,\n resolvePullContinuationMinLsn,\n} from \"../../../data/documents/shared/sync/syncPagination\";\nimport type {\n DocumentCreateAuthor,\n DocumentSyncApi,\n DocumentSyncPlan,\n DocumentWriterPublicKeyResolver,\n PersistedDocumentSyncState,\n SyncRemoteDocumentResult,\n} from \"../../../data/documents/shared/types\";\nimport { projectionVerificationOptions } from \"../../../data/documents/shared/types\";\nimport { readCanonicalRecord } from \"../../../data/keying/canonicalJson\";\nimport type {\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../../../data/keyingProjectionVerification\";\nimport type { PendingUpdateRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport {\n assertRawContinuationCanRetry,\n refreshSyncAttemptWriterProjection,\n resolveSyncAttemptWriterProjection,\n} from \"../syncFailures\";\nimport {\n handleUpstreamDeletedDocumentSyncFailure,\n projectionIntegrityErrorCode,\n type RemoteDocumentDeletionHandler,\n type TerminalSubmitFailureHandler,\n} from \"../syncFailures/syncFailureClassification\";\nimport { DocumentRawHistoryUnavailableError } from \"../syncPlan/syncContentKeys\";\nimport { buildDocumentSyncPlan } from \"../syncPlan/syncPlanIdentity\";\nimport { buildMaterializedDocumentSyncPlan } from \"../syncPlan/syncPlanMaterial\";\nimport type { RekeyPendingUpdate } from \"../syncRekeys/syncRecoveryRekey\";\nimport {\n handleReadOnlyProjectionCompletionError,\n REFRESH_CACHED_PROJECTION,\n} from \"./readOnlyProjectionFailure\";\nimport type { DocumentSyncContainerRekeyBuilder } from \"./syncAttemptState\";\nimport { syncRemoteDocumentResultFromResponse } from \"./syncResponseResult\";\nimport { type DocumentSyncTraceEmitter, traceSubmitFailed } from \"./syncTrace\";\n\nasync function completeReadOnlyRemoteDocumentSyncWithProjection(\n input: Omit<ReadOnlyDocumentSyncCompletionInput, \"writerProjection\"> & {\n writerProjection: DocumentWriterProjectionResponse;\n },\n): Promise<SyncRemoteDocumentResult> {\n const materializedPlan = await buildMaterializedDocumentSyncPlan({\n author: input.author,\n execSql: input.execSql,\n historyMode: input.historyMode,\n localVersionVector: input.localVersionVector,\n minLsn: resolvePullContinuationMinLsn(input.pullContinuation, input.minLsn),\n pullCursor: input.pullContinuation?.cursor,\n onSyncTrace: input.onSyncTrace,\n pendingUpdates: [],\n signedAt: input.signedAt,\n targetSecretKey: input.targetSecretKey,\n writerProjection: input.writerProjection,\n ...projectionVerificationOptions(input),\n });\n\n const result = await syncRemoteDocumentResultFromResponse({\n ...projectionVerificationOptions(input),\n execSql: input.execSql,\n materializedPlan,\n recoveryPendingUpdatesById: new Map(),\n resolveWriterPublicKey: input.resolveWriterPublicKey,\n response: input.response,\n targetSecretKey: input.targetSecretKey,\n validateIncomingUpdates: input.validateIncomingUpdates,\n writerProjection: input.writerProjection,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n });\n return {\n ...result,\n hasIncompletePull: !input.pullComplete,\n };\n}\n\nfunction parsePersistedDocumentSyncRecord<T>(\n value: string | null | undefined,\n label: string,\n isRecord: (value: unknown) => value is T,\n): T | null {\n if (!value) {\n return null;\n }\n\n try {\n const record = readCanonicalRecord(JSON.parse(value), label);\n return isRecord(record) ? record : null;\n } catch {\n return null;\n }\n}\n\nfunction parsePersistedDocumentSyncState(\n persistedState: PersistedDocumentSyncState | null | undefined,\n documentId: string,\n): {\n contentKeyBundle: DocumentCreateResponse[\"contentKeyBundle\"];\n documentKekTargets: DocumentSyncResponse[\"documentKekTargets\"];\n documentManifest: DocumentCreateResponse[\"accessManifest\"];\n} | null {\n if (persistedState?.documentId !== documentId) {\n return null;\n }\n\n const contentKeyBundle = parsePersistedDocumentSyncRecord(\n persistedState.contentKeyBundle,\n \"Persisted document sync content-key bundle\",\n isDocumentContentKeyBundleResponse,\n );\n const documentKekTargets = parsePersistedDocumentSyncRecord(\n persistedState.documentKekTargets,\n \"Persisted document sync KEK targets\",\n isDocumentKekTargetsResponse,\n );\n const documentManifest = parsePersistedDocumentSyncRecord(\n persistedState.documentManifestBundle,\n \"Persisted document sync manifest\",\n isAccessManifestBundleWireResponse,\n );\n\n if (!contentKeyBundle || !documentKekTargets || !documentManifest) {\n return null;\n }\n\n return {\n contentKeyBundle,\n documentKekTargets,\n documentManifest,\n };\n}\n\nasync function buildReadOnlyDocumentSyncPlanFromPersistedState(input: {\n author: DocumentCreateAuthor;\n documentId: string;\n historyMode?: \"raw\" | undefined;\n localVersionVector: string | null;\n minLsn?: string | undefined;\n pullContinuation?: DocumentSyncPullContinuation | undefined;\n persistedState?: PersistedDocumentSyncState | null | undefined;\n signedAt?: string | undefined;\n}): Promise<DocumentSyncPlan | null> {\n const persisted = parsePersistedDocumentSyncState(\n input.persistedState,\n input.documentId,\n );\n if (!persisted) {\n return null;\n }\n\n const plan = await buildDocumentSyncPlan({\n author: input.author,\n contentKeyBundle: persisted.contentKeyBundle,\n documentId: input.documentId,\n documentKekTargets: persisted.documentKekTargets,\n documentManifest: persisted.documentManifest,\n historyMode: input.historyMode,\n localVersionVector: input.localVersionVector,\n minLsn: resolvePullContinuationMinLsn(input.pullContinuation, input.minLsn),\n outgoingUpdates: [],\n pullCursor: input.pullContinuation?.cursor,\n signedAt: input.signedAt,\n });\n const request = limitDocumentSyncRequestBytes(plan.request);\n return request === plan.request ? plan : { ...plan, request };\n}\n\ntype PersistedReadOnlyDocumentSyncResult =\n | {\n kind: \"completed\";\n result: SyncRemoteDocumentResult | null;\n }\n | {\n kind: \"not_completed\";\n };\n\ninterface ReadOnlyDocumentSyncCompletionInput {\n apiClient: DocumentSyncApi;\n author: DocumentCreateAuthor;\n documentId: string;\n execSql: ExecSql;\n historyMode?: \"raw\" | undefined;\n localVersionVector: string | null;\n minLsn?: string | undefined;\n pullContinuation?: DocumentSyncPullContinuation | undefined;\n onReadOnlyProjectionFailure?: TerminalSubmitFailureHandler | undefined;\n onRemoteDocumentDeleted?: RemoteDocumentDeletionHandler | undefined;\n onSyncTrace?: DocumentSyncTraceEmitter | undefined;\n pullComplete: boolean;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n resolveWriterPublicKey: DocumentWriterPublicKeyResolver;\n response: DocumentSyncResponse;\n signedAt?: string | undefined;\n stillCurrent?: (() => boolean) | undefined;\n targetSecretKey: Uint8Array;\n validateIncomingUpdates: IncomingDocumentSyncUpdateValidator;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n writerProjection?: DocumentWriterProjectionResponse | undefined;\n}\n\nasync function tryCompleteReadOnlyRemoteDocumentSyncWithProjection(input: {\n allowCachedProjectionRefresh: boolean;\n completion: ReadOnlyDocumentSyncCompletionInput;\n writerProjection: DocumentWriterProjectionResponse;\n}): Promise<\n | DocumentRawHistoryUnavailableError\n | SyncRemoteDocumentResult\n | null\n | typeof REFRESH_CACHED_PROJECTION\n> {\n try {\n return await completeReadOnlyRemoteDocumentSyncWithProjection({\n ...input.completion,\n writerProjection: input.writerProjection,\n });\n } catch (error) {\n return handleReadOnlyProjectionCompletionError(error, {\n allowCachedProjectionRefresh: input.allowCachedProjectionRefresh,\n historyMode: input.completion.historyMode,\n });\n }\n}\nasync function completeReadOnlyRemoteDocumentSyncWithUpdates(\n input: ReadOnlyDocumentSyncCompletionInput,\n): Promise<PersistedReadOnlyDocumentSyncResult> {\n const reusableWriterProjection =\n input.writerProjection?.documentId === input.documentId\n ? input.writerProjection\n : null;\n const writerProjection =\n reusableWriterProjection ??\n (await resolveSyncAttemptWriterProjection({\n apiClient: input.apiClient,\n documentId: input.documentId,\n onRemoteDocumentDeleted: input.onRemoteDocumentDeleted,\n onSyncTrace: input.onSyncTrace,\n onTerminalFailure: input.onReadOnlyProjectionFailure,\n reusableWriterProjection: null,\n stillCurrent: input.stillCurrent,\n }));\n if (!writerProjection) {\n return { kind: \"completed\", result: null };\n }\n const canRefreshProjection =\n input.historyMode === \"raw\" || Boolean(reusableWriterProjection);\n let result:\n | DocumentRawHistoryUnavailableError\n | SyncRemoteDocumentResult\n | null\n | typeof REFRESH_CACHED_PROJECTION = null;\n let retryAfterRollback = false;\n try {\n result = await tryCompleteReadOnlyRemoteDocumentSyncWithProjection({\n allowCachedProjectionRefresh: canRefreshProjection,\n completion: input,\n writerProjection,\n });\n } catch (error) {\n if (projectionIntegrityErrorCode(error) !== \"rollback\") {\n throw error;\n }\n retryAfterRollback = true;\n }\n if (\n !retryAfterRollback &&\n result !== REFRESH_CACHED_PROJECTION &&\n !(result instanceof DocumentRawHistoryUnavailableError) &&\n (result || !reusableWriterProjection)\n ) {\n return { kind: \"completed\", result };\n }\n const freshWriterProjection = await refreshSyncAttemptWriterProjection({\n apiClient: input.apiClient,\n documentId: input.documentId,\n onRemoteDocumentDeleted: input.onRemoteDocumentDeleted,\n onSyncTrace: input.onSyncTrace,\n onTerminalFailure: input.onReadOnlyProjectionFailure,\n stillCurrent: input.stillCurrent,\n unavailableError:\n result instanceof DocumentRawHistoryUnavailableError ? result : undefined,\n });\n if (!freshWriterProjection) {\n return { kind: \"completed\", result: null };\n }\n const freshResult = await tryCompleteReadOnlyRemoteDocumentSyncWithProjection(\n {\n allowCachedProjectionRefresh: false,\n completion: input,\n writerProjection: freshWriterProjection,\n },\n );\n if (\n freshResult === REFRESH_CACHED_PROJECTION ||\n freshResult instanceof DocumentRawHistoryUnavailableError\n ) {\n throw new Error(\"Fresh document projection unexpectedly requested refresh\");\n }\n return freshResult\n ? { kind: \"completed\", result: freshResult }\n : { kind: \"not_completed\" };\n}\nasync function syncReadOnlyRemoteDocumentFromPersistedState(\n input: Omit<\n ReadOnlyDocumentSyncCompletionInput,\n \"pullComplete\" | \"response\"\n > & {\n persistedState?: PersistedDocumentSyncState | null | undefined;\n },\n): Promise<PersistedReadOnlyDocumentSyncResult> {\n let plan: DocumentSyncPlan | null;\n try {\n plan = await buildReadOnlyDocumentSyncPlanFromPersistedState(input);\n } catch {\n plan = null;\n }\n if (!plan) {\n return { kind: \"not_completed\" };\n }\n if (input.stillCurrent?.() === false) {\n return { kind: \"completed\", result: null };\n }\n const submitted = await submitDocumentSync({\n apiClient: input.apiClient,\n expectedCommitLsnMode: input.pullContinuation?.commitLsnMode,\n plan,\n });\n if (input.stillCurrent?.() === false) {\n return { kind: \"completed\", result: null };\n }\n if (!submitted) {\n return { kind: \"completed\", result: null };\n }\n if (!submitted.ok) {\n if (\n await handleUpstreamDeletedDocumentSyncFailure({\n documentId: input.documentId,\n failure: submitted,\n onRemoteDocumentDeleted: input.onRemoteDocumentDeleted,\n })\n ) {\n return { kind: \"completed\", result: null };\n }\n\n if (isRetryableDocumentSyncConflict(submitted)) {\n traceSubmitFailed(input.onSyncTrace, {\n action: \"retry\",\n code: submitted.code,\n documentId: input.documentId,\n status: submitted.status,\n });\n assertRawContinuationCanRetry(input.historyMode, input.pullContinuation);\n return { kind: \"not_completed\" };\n }\n\n traceSubmitFailed(input.onSyncTrace, {\n action: \"stop\",\n code: submitted.code,\n documentId: input.documentId,\n status: submitted.status,\n });\n submitted.report();\n return { kind: \"completed\", result: null };\n }\n\n if (submitted.response.updates.length > 0) {\n return completeReadOnlyRemoteDocumentSyncWithUpdates({\n ...input,\n pullComplete: submitted.pullComplete,\n response: submitted.response,\n });\n }\n\n try {\n const persistedState = await persistedDocumentSyncStateFromResponse(\n plan,\n submitted.response,\n { resolveWriterPublicKey: input.resolveWriterPublicKey },\n );\n\n return {\n kind: \"completed\",\n result: {\n contentKey: new Uint8Array(),\n decryptedUpdates: [],\n exhaustedPendingUpdateCount: 0,\n hasDeferredPendingUpdates: false,\n hasIncompletePull: !submitted.pullComplete,\n persistedState,\n plan,\n rekeyedPendingUpdateIds: [],\n response: submitted.response,\n settledPendingUpdateIds: [],\n acceptedRecoveryBaseline: false,\n },\n };\n } catch (error) {\n if (input.historyMode === \"raw\") {\n throw error;\n }\n return { kind: \"not_completed\" };\n }\n}\n\nexport interface SyncRemoteDocumentInput {\n apiClient: DocumentSyncApi;\n author: DocumentCreateAuthor;\n /**\n * Supplies a full-history Loro snapshot of the local document so a\n * write-bearing pass can heal a stale content-key bundle by rotating to a\n * fresh content key anchored by a rotation baseline.\n */\n buildRotationSnapshot?: (() => Promise<Uint8Array | null>) | undefined;\n /** Overrides automatic ancestor repair for each submission's current projection. */\n buildContainerRekeys?: DocumentSyncContainerRekeyBuilder;\n documentId: string;\n execSql: ExecSql;\n /** Explicit read-only recovery that bypasses rotation-baseline redirect. */\n historyMode?: \"raw\" | undefined;\n isRemoteSyncBlocked?: ((organizationId: string) => boolean) | undefined;\n localVersionVector: string | null;\n minLsn?: string | undefined;\n pullContinuation?: DocumentSyncPullContinuation | undefined;\n /** Atomically commits the verified purge proof with matching local teardown. */\n onRemoteDocumentDeleted?: RemoteDocumentDeletionHandler | undefined;\n onSyncAbandoned?: ((reason: string) => void) | undefined;\n /** Clipboard-safe trace sink (see syncTrace.ts); never receives content. */\n onSyncTrace?: DocumentSyncTraceEmitter | undefined;\n /**\n * Fires when a pass WITHOUT queued writes cannot resolve its writer\n * projection (e.g. a coded 409 from the projection route). Read-only\n * revalidation otherwise fails silently — one burned request and a trace\n * line — leaving the document permanently stale with nothing durable to\n * explain why (edge-case row 13).\n */\n onReadOnlyProjectionFailure?: TerminalSubmitFailureHandler | undefined;\n /** Records a verified response update that could not be decrypted/imported. */\n onIncomingUpdateIsolationFailure?:\n | ((failure: DocumentSyncUpdateIsolationError) => void | Promise<void>)\n | undefined;\n /** Fires immediately before a materialized outgoing batch is submitted. */\n onOutgoingUpdatesMaterialized?:\n | ((updateIds: readonly string[]) => void)\n | undefined;\n /** Invalidates continuation state after the server rejects its snapshot. */\n onPullContinuationInvalidated?:\n | ((continuation: DocumentSyncPullContinuation) => void | Promise<void>)\n | undefined;\n onTerminalSubmitFailure?: TerminalSubmitFailureHandler | undefined;\n pendingUpdates?: readonly PendingUpdateRecord[] | undefined;\n persistedState?: PersistedDocumentSyncState | null | undefined;\n rekeyPendingUpdate?: RekeyPendingUpdate | undefined;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n resolveWriterPublicKey: DocumentWriterPublicKeyResolver;\n signedAt?: string | undefined;\n stillCurrent?: (() => boolean) | undefined;\n targetSecretKey: Uint8Array;\n /** Validates decrypted updates against scratch state before callers persist. */\n validateIncomingUpdates: IncomingDocumentSyncUpdateValidator;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n writerProjection?: DocumentWriterProjectionResponse | undefined;\n}\n\nexport async function tryPersistedReadOnlyDocumentSync(\n input: SyncRemoteDocumentInput,\n resolveProjectionUserKey: ProjectionUserKeyResolver,\n): Promise<PersistedReadOnlyDocumentSyncResult | null> {\n if (input.stillCurrent?.() === false) return null;\n if ((input.pendingUpdates ?? []).length > 0) return null;\n\n return syncReadOnlyRemoteDocumentFromPersistedState({\n ...input,\n resolveProjectionUserKey,\n });\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ContainerKekRepairInaccessibleError } from "../../../data/documents/shared/containerKek/containerKekCurrency.js";
|
|
2
2
|
import { assertDocumentManifestBundleConsistent } from "../../../data/documents/shared/readers.js";
|
|
3
|
+
import { isDocumentSyncRequestLimitError } from "../../../data/documents/shared/sync/documentSyncOutgoingBatch.js";
|
|
3
4
|
import { resolvePullContinuationMinLsn, } from "../../../data/documents/shared/sync/syncPagination.js";
|
|
4
|
-
import { isDocumentSyncRequestLimitError } from "../../../data/sync/documentSyncOutgoingBatch.js";
|
|
5
5
|
import { retrySyncPlanOrAbandon } from "../syncFailures/index.js";
|
|
6
6
|
import { abandonAncestorRepair, abandonBlockedSync, abandonInaccessibleAncestorRepair, abandonOversizedSyncPlan, } from "../syncFailures/syncAbandon.js";
|
|
7
7
|
import { DocumentAncestorRepairAbandonedError, DocumentSyncPreparationRequiredError, } from "../syncFailures/syncRepairAbandon.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncAttemptPlanning.js","sourceRoot":"","sources":["syncAttemptPlanning.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mCAAmC,EAAE,MAAM,kEAAkE,CAAC;AACvH,OAAO,EAAE,sCAAsC,EAAE,MAAM,wCAAwC,CAAC;AAChG,OAAO,
|
|
1
|
+
{"version":3,"file":"syncAttemptPlanning.js","sourceRoot":"","sources":["syncAttemptPlanning.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport { ContainerKekRepairInaccessibleError } from \"../../../data/documents/shared/containerKek/containerKekCurrency\";\nimport { assertDocumentManifestBundleConsistent } from \"../../../data/documents/shared/readers\";\nimport { isDocumentSyncRequestLimitError } from \"../../../data/documents/shared/sync/documentSyncOutgoingBatch\";\nimport {\n type DocumentSyncPullContinuation,\n resolvePullContinuationMinLsn,\n} from \"../../../data/documents/shared/sync/syncPagination\";\nimport type { PendingUpdateRecord } from \"../../../data/sqlite/documentPersistence\";\nimport { retrySyncPlanOrAbandon } from \"../syncFailures\";\nimport {\n abandonAncestorRepair,\n abandonBlockedSync,\n abandonInaccessibleAncestorRepair,\n abandonOversizedSyncPlan,\n} from \"../syncFailures/syncAbandon\";\nimport type { TerminalSubmitFailureHandler } from \"../syncFailures/syncFailureClassification\";\nimport {\n DocumentAncestorRepairAbandonedError,\n DocumentSyncPreparationRequiredError,\n} from \"../syncFailures/syncRepairAbandon\";\nimport { prepareSyncAttemptContainerRekeys } from \"../syncRekeys/syncContainerRekeyPreparation\";\nimport { buildRemoteDocumentSyncPlan } from \"../syncRekeys/syncContainerRekeys\";\nimport type { SyncRemoteDocumentInput } from \"./readOnlySync\";\n\n/**\n * A write-bearing pass records through the submit handler (its queued writes\n * are what the failure blocks). A read-only pass records through the\n * revalidation handler so the refusal still leaves a durable trail instead\n * of silently never revalidating (edge-case row 13). A cursor continuation is\n * wire-level read-only, but its failure still blocks the queued writes it\n * deliberately deferred. Update-id recovery likewise empties the in-flight\n * batch while durable rows remain. Both keep the submit classification and\n * its 403 handling.\n */\nexport function projectionFailureHandler(\n input: SyncRemoteDocumentInput,\n failureBlocksQueuedWrites: boolean,\n): TerminalSubmitFailureHandler | undefined {\n return failureBlocksQueuedWrites\n ? input.onTerminalSubmitFailure\n : input.onReadOnlyProjectionFailure;\n}\nexport async function planDocumentSyncAttempt(input: {\n pendingUpdates: readonly PendingUpdateRecord[];\n pullContinuation?: DocumentSyncPullContinuation | undefined;\n regenerateQueuedCheckpoints: boolean;\n sync: SyncRemoteDocumentInput;\n failureBlocksQueuedWrites: boolean;\n writerProjection: DocumentWriterProjectionResponse;\n}) {\n try {\n const pendingUpdates =\n input.pullContinuation === undefined ? input.pendingUpdates : [];\n if (\n pendingUpdates.length &&\n input.sync.isRemoteSyncBlocked?.(\n (\n await assertDocumentManifestBundleConsistent({\n bundle: input.writerProjection.documentManifest,\n label: \"Document sync manifest\",\n })\n ).organizationId,\n )\n ) {\n return abandonBlockedSync(input.sync);\n }\n // Durable repair happens once at this explicit attempt boundary. The\n // retryable builder below may only sign inline plans against its projection.\n const prepared =\n pendingUpdates.length &&\n !input.sync.buildContainerRekeys &&\n input.sync.buildRotationSnapshot\n ? await prepareSyncAttemptContainerRekeys(\n input.sync,\n input.writerProjection,\n )\n : { projection: input.writerProjection, plans: undefined };\n if (!prepared) return null;\n return await retrySyncPlanOrAbandon({\n apiClient: input.sync.apiClient,\n buildWithProjection: (projection) =>\n buildRemoteDocumentSyncPlan({\n pendingUpdates,\n preparedRekeys:\n projection === prepared.projection ? prepared.plans : undefined,\n minLsn: resolvePullContinuationMinLsn(\n input.pullContinuation,\n input.sync.minLsn,\n ),\n pullCursor: input.pullContinuation?.cursor,\n projection,\n regenerateQueuedCheckpoints: input.regenerateQueuedCheckpoints,\n sync: input.sync,\n }),\n documentId: input.sync.documentId,\n onRemoteDocumentDeleted: input.sync.onRemoteDocumentDeleted,\n onSyncAbandoned: input.sync.onSyncAbandoned,\n onSyncTrace: input.sync.onSyncTrace,\n onTerminalFailure: projectionFailureHandler(\n input.sync,\n input.failureBlocksQueuedWrites,\n ),\n stillCurrent: input.sync.stillCurrent,\n writerProjection: prepared.projection,\n });\n } catch (error) {\n if (error instanceof DocumentSyncPreparationRequiredError)\n return \"retry\" as const;\n if (error instanceof DocumentAncestorRepairAbandonedError) {\n return abandonAncestorRepair(input.sync, error);\n }\n if (error instanceof ContainerKekRepairInaccessibleError) {\n return abandonInaccessibleAncestorRepair(input.sync, error);\n }\n if (!isDocumentSyncRequestLimitError(error)) {\n throw error;\n }\n return abandonOversizedSyncPlan(input.sync, error);\n }\n}\n"]}
|
|
@@ -4,8 +4,8 @@ import { createPendingUpdateFields } from "../../../data/documents/documentSync.
|
|
|
4
4
|
import { assertDocumentKekPathsCurrent } from "../../../data/documents/shared/containerKek/containerKekCurrency.js";
|
|
5
5
|
import { assertDocumentWriterProjectionConsistent, authorizingContainerPathRefs, buildRotatedDocumentContentKeyBundle, ContainerKekHistoryUnavailableError, collectContainerKeksForDocumentSync, DocumentHistoryUnavailableError, unwrapDocumentContentKeyFromBundle, } from "../../../data/documents/shared/projection/index.js";
|
|
6
6
|
import { assertDocumentManifestBundleConsistent } from "../../../data/documents/shared/readers.js";
|
|
7
|
+
import { selectDocumentSyncOutgoingBatch } from "../../../data/documents/shared/sync/documentSyncOutgoingBatch.js";
|
|
7
8
|
import { projectionVerificationOptions } from "../../../data/documents/shared/types.js";
|
|
8
|
-
import { selectDocumentSyncOutgoingBatch } from "../../../data/sync/documentSyncOutgoingBatch.js";
|
|
9
9
|
import { traceCheckpointRegeneration, traceHealBlocked, traceHealPlanned, traceStaleBundle, traceStaleRead, } from "../sync/syncTrace.js";
|
|
10
10
|
import { prepareDocumentOutgoingUpdates } from "./syncContentKeys.js";
|
|
11
11
|
import { buildDocumentSyncPlan } from "./syncPlanIdentity.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncPlanMaterial.js","sourceRoot":"","sources":["syncPlanMaterial.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n VerifiedContainerAccessManifest,\n VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport {\n emptyVersionVector,\n getImportBlobMetadata,\n satisfiesVersionVector,\n versionVectorsEqual,\n} from \"@tearleads/loro\";\nimport type {\n DocumentCreateResponse,\n DocumentSyncResponse,\n DocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { documentKekTargetsFromContentKeyBundle } from \"@tearleads/validators/response\";\nimport { createPendingUpdateFields } from \"../../../data/documents/documentSync\";\nimport { assertDocumentKekPathsCurrent } from \"../../../data/documents/shared/containerKek/containerKekCurrency\";\nimport {\n assertDocumentWriterProjectionConsistent,\n authorizingContainerPathRefs,\n buildRotatedDocumentContentKeyBundle,\n ContainerKekHistoryUnavailableError,\n collectContainerKeksForDocumentSync,\n DocumentHistoryUnavailableError,\n unwrapDocumentContentKeyFromBundle,\n} from \"../../../data/documents/shared/projection\";\nimport { assertDocumentManifestBundleConsistent } from \"../../../data/documents/shared/readers\";\nimport type {\n BuildDocumentSyncPlanInput,\n DocumentCreateAuthor,\n MaterializedDocumentSyncPlan,\n ProjectionVerificationOptions,\n} from \"../../../data/documents/shared/types\";\nimport { projectionVerificationOptions } from \"../../../data/documents/shared/types\";\nimport type { DocumentWriterProjectionAuthorization } from \"../../../data/keyingProjectionVerification\";\nimport type { PendingUpdateRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { selectDocumentSyncOutgoingBatch } from \"../../../data/sync/documentSyncOutgoingBatch\";\nimport {\n type DocumentSyncTraceEmitter,\n traceCheckpointRegeneration,\n traceHealBlocked,\n traceHealPlanned,\n traceStaleBundle,\n traceStaleRead,\n} from \"../sync/syncTrace\";\nimport { prepareDocumentOutgoingUpdates } from \"./syncContentKeys\";\nimport { buildDocumentSyncPlan } from \"./syncPlanIdentity\";\nimport { finalizeMaterializedDocumentSyncPlan } from \"./syncPlanProjection\";\n\n/**\n * Builds the rotation baseline that anchors a stale-bundle heal: a full\n * history snapshot of the local document, re-encrypted under the fresh\n * content key so every current member (including post-rotation newcomers)\n * has an efficient current-epoch redirect. Predecessor KEKs remain the\n * correctness path when no covering baseline exists.\n */\nasync function buildStaleRecoveryBaselinePendingUpdate(\n buildRotationSnapshot: (() => Promise<Uint8Array | null>) | undefined,\n): Promise<PendingUpdateRecord> {\n const snapshot = buildRotationSnapshot ? await buildRotationSnapshot() : null;\n if (!snapshot) {\n throw new Error(\n \"Document content-key bundle is stale and no rotation snapshot is available to heal it\",\n );\n }\n const metadata = getImportBlobMetadata(snapshot);\n if (\n metadata.mode !== \"snapshot\" ||\n !versionVectorsEqual(\n metadata.partialStartVersionVector,\n emptyVersionVector(),\n )\n ) {\n throw new Error(\n \"Document stale-bundle recovery requires a full-history rotation snapshot\",\n );\n }\n const pendingFields = createPendingUpdateFields(\n snapshot,\n metadata.partialEndVersionVector,\n );\n if (!pendingFields) {\n throw new Error(\"Document stale-bundle recovery snapshot is empty\");\n }\n\n return {\n id: crypto.randomUUID(),\n ...pendingFields,\n };\n}\n\n/**\n * Resolves the content material a sync plan encrypts and carries. A stale\n * bundle names predecessor KEK targets while the document's current targets\n * name successor epochs, so it splits by intent: a write-bearing pass heals\n * the document with a FRESH content key at the next epoch, anchored by a\n * covering rotation baseline, while a read-only pass keeps the stale pair and\n * unwraps it through predecessor KEKs so the server can settle a complete pull.\n */\n/**\n * The manifest bundle a stale read-only plan must pair with: the one the\n * stale content-key bundle actually references. A stale bundle normally\n * still carries the current link-set manifest hash (KEK rotations do not\n * advance the document manifest), but the projection consistency check\n * defensively admits a bundle lagging the head as long as it appears in the\n * manifest history — pair with that historical bundle so the plan's own\n * identity checks hold.\n */\nfunction staleBundleDocumentManifest(\n writerProjection: DocumentWriterProjectionResponse,\n): DocumentCreateResponse[\"accessManifest\"] {\n const { contentKeyBundle, documentManifest } = writerProjection;\n if (contentKeyBundle.linkSetManifestHash === documentManifest.manifestHash) {\n return documentManifest;\n }\n const historicalManifest = writerProjection.documentManifestHistory.find(\n (bundle) => bundle.manifestHash === contentKeyBundle.linkSetManifestHash,\n );\n if (!historicalManifest) {\n throw new Error(\n \"Document stale bundle manifest is missing from the projection history\",\n );\n }\n return historicalManifest;\n}\n\n/**\n * Settling a superseded checkpoint deletes its queue row, so the fresh\n * baseline must PROVABLY subsume it — assumed coverage would silently drop\n * any ops the checkpoint alone carried.\n */\nfunction assertRecoveryBaselineCoversCheckpoints(\n recoveryBaseline: PendingUpdateRecord,\n checkpoints: readonly PendingUpdateRecord[],\n): void {\n for (const checkpoint of checkpoints) {\n if (\n !satisfiesVersionVector(\n recoveryBaseline.partialEndVersionVector,\n checkpoint.partialEndVersionVector,\n )\n ) {\n throw new Error(\n \"Document stale-bundle recovery snapshot does not cover a queued rotation checkpoint\",\n );\n }\n }\n}\n\ninterface ResolvedSyncPlanContentMaterial {\n contentKey: Uint8Array;\n contentKeyBundle: DocumentCreateResponse[\"contentKeyBundle\"];\n documentKekTargets: DocumentSyncResponse[\"documentKekTargets\"];\n documentManifest: DocumentCreateResponse[\"accessManifest\"];\n healedStaleContentKeyBundle: boolean;\n heldBackPendingUpdateIds: readonly string[];\n pendingUpdates: readonly PendingUpdateRecord[];\n staleRecoveryBaselineUpdateId?: string;\n}\n\nasync function resolveStaleHealMaterial(\n input: Parameters<typeof buildMaterializedDocumentSyncPlan>[0],\n containerKeksByEpochId: ReadonlyMap<string, Uint8Array>,\n pendingUpdates: readonly PendingUpdateRecord[],\n): Promise<ResolvedSyncPlanContentMaterial> {\n const contentKey = crypto.getRandomValues(new Uint8Array(32));\n // A rotation checkpoint left in the queue by an interrupted earlier\n // recovery is superseded by the fresh covering baseline built below:\n // submitting it alongside would trip the server's covering-baseline gate,\n // and resubmitting it after the heal could become the latest baseline at\n // the healed epoch and mask the covering one. Hold it out of the request;\n // on heal success its id is reported settled (the committed baseline\n // subsumes its full-history content) so the queue row is removed.\n //\n // A heal whose OWN baseline does not cover the committed frontier is\n // rejected by that same gate and surfaces as a terminal queue failure.\n // This write-bearing pass does not pull first. A device missing committed\n // history can independently rematerialize it through predecessor KEKs on a\n // read-only pass, then retry with a covering snapshot. The coverage proof\n // prevents a partial local view from becoming the redirect baseline.\n const ordinaryPendingUpdates = pendingUpdates.filter(\n (update) => update.sourceVersionVector == null,\n );\n const heldBackCheckpoints = pendingUpdates.filter(\n (update) => update.sourceVersionVector != null,\n );\n const recoveryBaseline = await buildStaleRecoveryBaselinePendingUpdate(\n input.buildRotationSnapshot,\n );\n const selectedOrdinaryPendingUpdates = selectDocumentSyncOutgoingBatch(\n ordinaryPendingUpdates,\n {\n reservedDataCharacters: recoveryBaseline.updateData.length,\n reservedUpdateCount: 1,\n },\n );\n assertRecoveryBaselineCoversCheckpoints(\n recoveryBaseline,\n heldBackCheckpoints,\n );\n const staleEpoch = input.writerProjection.contentKeyBundle.contentKeyEpoch;\n const contentKeyBundle = await buildRotatedDocumentContentKeyBundle({\n containerKeksByEpochId,\n contentKey,\n writerProjection: input.writerProjection,\n });\n traceHealPlanned(input.onSyncTrace, {\n documentId: input.writerProjection.documentId,\n fromEpoch: staleEpoch,\n heldBack: heldBackCheckpoints.length,\n toEpoch: contentKeyBundle.contentKeyEpoch,\n updates: selectedOrdinaryPendingUpdates.length,\n });\n return {\n contentKey,\n contentKeyBundle,\n documentKekTargets: input.writerProjection.documentKekTargets,\n documentManifest: input.writerProjection.documentManifest,\n healedStaleContentKeyBundle: true,\n heldBackPendingUpdateIds: heldBackCheckpoints.map((update) => update.id),\n pendingUpdates: [recoveryBaseline, ...selectedOrdinaryPendingUpdates],\n staleRecoveryBaselineUpdateId: recoveryBaseline.id,\n };\n}\n\n/**\n * Reactive repair for a healthy-projection pass whose queued rotation\n * checkpoint the server rejected via the covering-baseline gate (a leftover\n * from an interrupted recovery, a heal whose ack was lost, or a lost heal\n * race). Queued checkpoints normally pass through untouched — reset and\n * rotation flows legitimately submit them — but a rejected one would strand\n * the whole queue, and committing it could shrink redirect coverage. Replace\n * the stale checkpoints with one freshly regenerated covering baseline and\n * settle them on success (coverage proven above).\n */\nasync function resolveCheckpointRegenerationMaterial(\n input: Parameters<typeof buildMaterializedDocumentSyncPlan>[0],\n base: ResolvedSyncPlanContentMaterial,\n pendingUpdates: readonly PendingUpdateRecord[],\n): Promise<ResolvedSyncPlanContentMaterial> {\n const ordinaryPendingUpdates = pendingUpdates.filter(\n (update) => update.sourceVersionVector == null,\n );\n const queuedCheckpoints = pendingUpdates.filter(\n (update) => update.sourceVersionVector != null,\n );\n const recoveryBaseline = await buildStaleRecoveryBaselinePendingUpdate(\n input.buildRotationSnapshot,\n );\n const selectedOrdinaryPendingUpdates = selectDocumentSyncOutgoingBatch(\n ordinaryPendingUpdates,\n {\n reservedDataCharacters: recoveryBaseline.updateData.length,\n reservedUpdateCount: 1,\n },\n );\n assertRecoveryBaselineCoversCheckpoints(recoveryBaseline, queuedCheckpoints);\n traceCheckpointRegeneration(input.onSyncTrace, {\n checkpoints: queuedCheckpoints.length,\n documentId: input.writerProjection.documentId,\n updates: selectedOrdinaryPendingUpdates.length,\n });\n return {\n ...base,\n heldBackPendingUpdateIds: queuedCheckpoints.map((update) => update.id),\n pendingUpdates: [recoveryBaseline, ...selectedOrdinaryPendingUpdates],\n staleRecoveryBaselineUpdateId: recoveryBaseline.id,\n };\n}\n\nasync function resolveSyncPlanContentMaterial(\n input: Parameters<typeof buildMaterializedDocumentSyncPlan>[0],\n collectedKeks: Awaited<\n ReturnType<typeof collectContainerKeksForDocumentSync>\n >,\n): Promise<ResolvedSyncPlanContentMaterial> {\n const allPendingUpdates = input.pendingUpdates ?? [];\n if (allPendingUpdates.length > 0 || input.regenerateQueuedCheckpoints) {\n assertDocumentKekPathsCurrent(input.writerProjection);\n }\n const staleContentKeyBundle =\n input.writerProjection.contentKeyBundleStale === true;\n\n if (staleContentKeyBundle && allPendingUpdates.length > 0) {\n traceStaleBundle(input.onSyncTrace, {\n documentId: input.writerProjection.documentId,\n epoch: input.writerProjection.contentKeyBundle.contentKeyEpoch,\n pending: allPendingUpdates.length,\n });\n return resolveStaleHealMaterial(\n input,\n collectedKeks.keksByEpochId,\n allPendingUpdates,\n );\n }\n\n const pendingUpdates = selectDocumentSyncOutgoingBatch(allPendingUpdates);\n\n if (staleContentKeyBundle) {\n traceStaleRead(input.onSyncTrace, {\n documentId: input.writerProjection.documentId,\n epoch: input.writerProjection.contentKeyBundle.contentKeyEpoch,\n });\n // Current access includes every predecessor KEK. If damaged history makes\n // this old bundle unreachable, preserve the bridge-integrity failure: this\n // read actually needs that historical epoch and cannot safely continue.\n let staleContentKey: Uint8Array;\n try {\n staleContentKey = await unwrapDocumentContentKeyFromBundle(\n input.writerProjection.contentKeyBundle,\n input.writerProjection.documentId,\n collectedKeks.keksByEpochId,\n collectedKeks.predecessorFailuresByEpochId,\n collectedKeks.unattributedPredecessorFailuresByContainerId,\n );\n } catch (error) {\n if (error instanceof DocumentHistoryUnavailableError) {\n if (\n input.historyMode !== \"raw\" ||\n !(error.historyCause instanceof ContainerKekHistoryUnavailableError)\n ) {\n throw error;\n }\n }\n // A stale bundle can predate the requester's current authorizing links.\n // Keep the read-only pass alive; served updates that need this absent key\n // remain undecryptable until a spanning member heals the bundle.\n staleContentKey = new Uint8Array();\n }\n return {\n contentKey: staleContentKey,\n contentKeyBundle: input.writerProjection.contentKeyBundle,\n documentKekTargets: documentKekTargetsFromContentKeyBundle(\n input.writerProjection.contentKeyBundle,\n ),\n documentManifest: staleBundleDocumentManifest(input.writerProjection),\n healedStaleContentKeyBundle: false,\n heldBackPendingUpdateIds: [],\n pendingUpdates,\n };\n }\n\n const normalMaterial: ResolvedSyncPlanContentMaterial = {\n contentKey: await unwrapDocumentContentKeyFromBundle(\n input.writerProjection.contentKeyBundle,\n input.writerProjection.documentId,\n collectedKeks.keksByEpochId,\n collectedKeks.predecessorFailuresByEpochId,\n collectedKeks.unattributedPredecessorFailuresByContainerId,\n ),\n contentKeyBundle: input.writerProjection.contentKeyBundle,\n documentKekTargets: input.writerProjection.documentKekTargets,\n documentManifest: input.writerProjection.documentManifest,\n healedStaleContentKeyBundle: false,\n heldBackPendingUpdateIds: [],\n pendingUpdates,\n };\n if (\n input.regenerateQueuedCheckpoints === true &&\n allPendingUpdates.some((update) => update.sourceVersionVector != null)\n ) {\n return resolveCheckpointRegenerationMaterial(\n input,\n normalMaterial,\n allPendingUpdates,\n );\n }\n\n return normalMaterial;\n}\n\nexport async function buildMaterializedDocumentSyncPlan(\n input: {\n author: DocumentCreateAuthor;\n /**\n * Supplies a full-history Loro snapshot of the local document when a\n * stale content-key bundle must be healed, or when a leftover queued\n * rotation checkpoint must be regenerated as a covering baseline.\n * Without it those passes fail with a descriptive error instead.\n */\n buildRotationSnapshot?: (() => Promise<Uint8Array | null>) | undefined;\n containerRekeys?: BuildDocumentSyncPlanInput[\"containerRekeys\"];\n execSql?: ExecSql | undefined;\n historyMode?: \"raw\" | undefined;\n inlineRekeyCommitId?: string | undefined;\n localVersionVector: string | null;\n minLsn?: string | undefined;\n pullCursor?: string | undefined;\n /** Clipboard-safe trace sink (see syncTrace.ts); never receives content. */\n onSyncTrace?: DocumentSyncTraceEmitter | undefined;\n pendingUpdates?: readonly PendingUpdateRecord[] | undefined;\n persistVerificationCheckpoints?: boolean | undefined;\n /**\n * Replace queued rotation checkpoints with a freshly regenerated covering\n * baseline instead of passing them through. Set by the sync loop after\n * the server rejected a pass via the covering-baseline gate.\n */\n regenerateQueuedCheckpoints?: boolean | undefined;\n signedAt?: string | undefined;\n targetSecretKey: Uint8Array;\n writerProjection: DocumentWriterProjectionResponse;\n } & ProjectionVerificationOptions,\n): Promise<MaterializedDocumentSyncPlan> {\n // Reuse verification across consistency and unwrap passes; unwrap still\n // binds the key material independently.\n const verifiedByHash = new Map<string, VerifiedContainerAccessManifest>();\n const principalPolicyCache = new Map<string, VerifiedPrincipalPolicy>();\n let writerAuthorization: DocumentWriterProjectionAuthorization | undefined;\n await assertDocumentWriterProjectionConsistent(input.writerProjection, {\n allowStaleContentKeyBundle: true,\n execSql: input.execSql,\n persistVerificationCheckpoints: input.persistVerificationCheckpoints,\n onVerifiedAuthorization: (authorization) => {\n writerAuthorization = authorization;\n },\n principalPolicyCache,\n verifiedByHash,\n ...projectionVerificationOptions(input),\n });\n const collectedKeks = await collectContainerKeksForDocumentSync({\n execSql: input.execSql,\n persistVerificationCheckpoints: input.persistVerificationCheckpoints,\n principalPolicyCache,\n secretKey: input.targetSecretKey,\n verifiedByHash,\n writerProjection: input.writerProjection,\n ...projectionVerificationOptions(input),\n });\n const documentId = input.writerProjection.documentId;\n const manifestIdentity = await assertDocumentManifestBundleConsistent({\n bundle: input.writerProjection.documentManifest,\n label: \"Document sync manifest\",\n });\n let material: ResolvedSyncPlanContentMaterial;\n try {\n material = await resolveSyncPlanContentMaterial(input, collectedKeks);\n } catch (error) {\n // Only a stale-bundle HEAL (write-bearing) or a checkpoint regeneration\n // counts as a blocked recovery; an ordinary pass or a stale READ failing\n // here must not read as one in a support report. Enumerated reason only;\n // unknown errors emit nothing (fail closed).\n if (\n (input.writerProjection.contentKeyBundleStale === true &&\n (input.pendingUpdates ?? []).length > 0) ||\n input.regenerateQueuedCheckpoints === true\n ) {\n traceHealBlocked(input.onSyncTrace, {\n documentId: input.writerProjection.documentId,\n error,\n });\n }\n throw error;\n }\n const {\n contentKey,\n contentKeyBundle,\n documentKekTargets,\n documentManifest,\n pendingUpdates,\n } = material;\n const outgoingUpdates = await prepareDocumentOutgoingUpdates({\n contentKey,\n contentKeyEpoch: contentKeyBundle.contentKeyEpoch,\n documentId,\n organizationId: manifestIdentity.organizationId,\n pendingUpdates,\n });\n const basePlan = await buildDocumentSyncPlan({\n author: {\n ...input.author,\n organizationId: manifestIdentity.organizationId,\n },\n authorizingContainerPathRefs: authorizingContainerPathRefs(\n input.writerProjection,\n ),\n containerRekeys: input.containerRekeys,\n contentKeyBundle,\n documentId,\n documentKekTargets,\n documentManifest,\n historyMode: input.historyMode,\n inlineRekeyCommitId: input.inlineRekeyCommitId,\n localVersionVector: input.localVersionVector,\n minLsn: input.minLsn,\n outgoingUpdates,\n pullCursor: input.pullCursor,\n signedAt: input.signedAt,\n });\n return finalizeMaterializedDocumentSyncPlan({\n basePlan,\n material,\n writerAuthorization,\n writerProjection: input.writerProjection,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"syncPlanMaterial.js","sourceRoot":"","sources":["syncPlanMaterial.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n VerifiedContainerAccessManifest,\n VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport {\n emptyVersionVector,\n getImportBlobMetadata,\n satisfiesVersionVector,\n versionVectorsEqual,\n} from \"@tearleads/loro\";\nimport type {\n DocumentCreateResponse,\n DocumentSyncResponse,\n DocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { documentKekTargetsFromContentKeyBundle } from \"@tearleads/validators/response\";\nimport { createPendingUpdateFields } from \"../../../data/documents/documentSync\";\nimport { assertDocumentKekPathsCurrent } from \"../../../data/documents/shared/containerKek/containerKekCurrency\";\nimport {\n assertDocumentWriterProjectionConsistent,\n authorizingContainerPathRefs,\n buildRotatedDocumentContentKeyBundle,\n ContainerKekHistoryUnavailableError,\n collectContainerKeksForDocumentSync,\n DocumentHistoryUnavailableError,\n unwrapDocumentContentKeyFromBundle,\n} from \"../../../data/documents/shared/projection\";\nimport { assertDocumentManifestBundleConsistent } from \"../../../data/documents/shared/readers\";\nimport { selectDocumentSyncOutgoingBatch } from \"../../../data/documents/shared/sync/documentSyncOutgoingBatch\";\nimport type {\n BuildDocumentSyncPlanInput,\n DocumentCreateAuthor,\n MaterializedDocumentSyncPlan,\n ProjectionVerificationOptions,\n} from \"../../../data/documents/shared/types\";\nimport { projectionVerificationOptions } from \"../../../data/documents/shared/types\";\nimport type { DocumentWriterProjectionAuthorization } from \"../../../data/keyingProjectionVerification\";\nimport type { PendingUpdateRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport {\n type DocumentSyncTraceEmitter,\n traceCheckpointRegeneration,\n traceHealBlocked,\n traceHealPlanned,\n traceStaleBundle,\n traceStaleRead,\n} from \"../sync/syncTrace\";\nimport { prepareDocumentOutgoingUpdates } from \"./syncContentKeys\";\nimport { buildDocumentSyncPlan } from \"./syncPlanIdentity\";\nimport { finalizeMaterializedDocumentSyncPlan } from \"./syncPlanProjection\";\n\n/**\n * Builds the rotation baseline that anchors a stale-bundle heal: a full\n * history snapshot of the local document, re-encrypted under the fresh\n * content key so every current member (including post-rotation newcomers)\n * has an efficient current-epoch redirect. Predecessor KEKs remain the\n * correctness path when no covering baseline exists.\n */\nasync function buildStaleRecoveryBaselinePendingUpdate(\n buildRotationSnapshot: (() => Promise<Uint8Array | null>) | undefined,\n): Promise<PendingUpdateRecord> {\n const snapshot = buildRotationSnapshot ? await buildRotationSnapshot() : null;\n if (!snapshot) {\n throw new Error(\n \"Document content-key bundle is stale and no rotation snapshot is available to heal it\",\n );\n }\n const metadata = getImportBlobMetadata(snapshot);\n if (\n metadata.mode !== \"snapshot\" ||\n !versionVectorsEqual(\n metadata.partialStartVersionVector,\n emptyVersionVector(),\n )\n ) {\n throw new Error(\n \"Document stale-bundle recovery requires a full-history rotation snapshot\",\n );\n }\n const pendingFields = createPendingUpdateFields(\n snapshot,\n metadata.partialEndVersionVector,\n );\n if (!pendingFields) {\n throw new Error(\"Document stale-bundle recovery snapshot is empty\");\n }\n\n return {\n id: crypto.randomUUID(),\n ...pendingFields,\n };\n}\n\n/**\n * Resolves the content material a sync plan encrypts and carries. A stale\n * bundle names predecessor KEK targets while the document's current targets\n * name successor epochs, so it splits by intent: a write-bearing pass heals\n * the document with a FRESH content key at the next epoch, anchored by a\n * covering rotation baseline, while a read-only pass keeps the stale pair and\n * unwraps it through predecessor KEKs so the server can settle a complete pull.\n */\n/**\n * The manifest bundle a stale read-only plan must pair with: the one the\n * stale content-key bundle actually references. A stale bundle normally\n * still carries the current link-set manifest hash (KEK rotations do not\n * advance the document manifest), but the projection consistency check\n * defensively admits a bundle lagging the head as long as it appears in the\n * manifest history — pair with that historical bundle so the plan's own\n * identity checks hold.\n */\nfunction staleBundleDocumentManifest(\n writerProjection: DocumentWriterProjectionResponse,\n): DocumentCreateResponse[\"accessManifest\"] {\n const { contentKeyBundle, documentManifest } = writerProjection;\n if (contentKeyBundle.linkSetManifestHash === documentManifest.manifestHash) {\n return documentManifest;\n }\n const historicalManifest = writerProjection.documentManifestHistory.find(\n (bundle) => bundle.manifestHash === contentKeyBundle.linkSetManifestHash,\n );\n if (!historicalManifest) {\n throw new Error(\n \"Document stale bundle manifest is missing from the projection history\",\n );\n }\n return historicalManifest;\n}\n\n/**\n * Settling a superseded checkpoint deletes its queue row, so the fresh\n * baseline must PROVABLY subsume it — assumed coverage would silently drop\n * any ops the checkpoint alone carried.\n */\nfunction assertRecoveryBaselineCoversCheckpoints(\n recoveryBaseline: PendingUpdateRecord,\n checkpoints: readonly PendingUpdateRecord[],\n): void {\n for (const checkpoint of checkpoints) {\n if (\n !satisfiesVersionVector(\n recoveryBaseline.partialEndVersionVector,\n checkpoint.partialEndVersionVector,\n )\n ) {\n throw new Error(\n \"Document stale-bundle recovery snapshot does not cover a queued rotation checkpoint\",\n );\n }\n }\n}\n\ninterface ResolvedSyncPlanContentMaterial {\n contentKey: Uint8Array;\n contentKeyBundle: DocumentCreateResponse[\"contentKeyBundle\"];\n documentKekTargets: DocumentSyncResponse[\"documentKekTargets\"];\n documentManifest: DocumentCreateResponse[\"accessManifest\"];\n healedStaleContentKeyBundle: boolean;\n heldBackPendingUpdateIds: readonly string[];\n pendingUpdates: readonly PendingUpdateRecord[];\n staleRecoveryBaselineUpdateId?: string;\n}\n\nasync function resolveStaleHealMaterial(\n input: Parameters<typeof buildMaterializedDocumentSyncPlan>[0],\n containerKeksByEpochId: ReadonlyMap<string, Uint8Array>,\n pendingUpdates: readonly PendingUpdateRecord[],\n): Promise<ResolvedSyncPlanContentMaterial> {\n const contentKey = crypto.getRandomValues(new Uint8Array(32));\n // A rotation checkpoint left in the queue by an interrupted earlier\n // recovery is superseded by the fresh covering baseline built below:\n // submitting it alongside would trip the server's covering-baseline gate,\n // and resubmitting it after the heal could become the latest baseline at\n // the healed epoch and mask the covering one. Hold it out of the request;\n // on heal success its id is reported settled (the committed baseline\n // subsumes its full-history content) so the queue row is removed.\n //\n // A heal whose OWN baseline does not cover the committed frontier is\n // rejected by that same gate and surfaces as a terminal queue failure.\n // This write-bearing pass does not pull first. A device missing committed\n // history can independently rematerialize it through predecessor KEKs on a\n // read-only pass, then retry with a covering snapshot. The coverage proof\n // prevents a partial local view from becoming the redirect baseline.\n const ordinaryPendingUpdates = pendingUpdates.filter(\n (update) => update.sourceVersionVector == null,\n );\n const heldBackCheckpoints = pendingUpdates.filter(\n (update) => update.sourceVersionVector != null,\n );\n const recoveryBaseline = await buildStaleRecoveryBaselinePendingUpdate(\n input.buildRotationSnapshot,\n );\n const selectedOrdinaryPendingUpdates = selectDocumentSyncOutgoingBatch(\n ordinaryPendingUpdates,\n {\n reservedDataCharacters: recoveryBaseline.updateData.length,\n reservedUpdateCount: 1,\n },\n );\n assertRecoveryBaselineCoversCheckpoints(\n recoveryBaseline,\n heldBackCheckpoints,\n );\n const staleEpoch = input.writerProjection.contentKeyBundle.contentKeyEpoch;\n const contentKeyBundle = await buildRotatedDocumentContentKeyBundle({\n containerKeksByEpochId,\n contentKey,\n writerProjection: input.writerProjection,\n });\n traceHealPlanned(input.onSyncTrace, {\n documentId: input.writerProjection.documentId,\n fromEpoch: staleEpoch,\n heldBack: heldBackCheckpoints.length,\n toEpoch: contentKeyBundle.contentKeyEpoch,\n updates: selectedOrdinaryPendingUpdates.length,\n });\n return {\n contentKey,\n contentKeyBundle,\n documentKekTargets: input.writerProjection.documentKekTargets,\n documentManifest: input.writerProjection.documentManifest,\n healedStaleContentKeyBundle: true,\n heldBackPendingUpdateIds: heldBackCheckpoints.map((update) => update.id),\n pendingUpdates: [recoveryBaseline, ...selectedOrdinaryPendingUpdates],\n staleRecoveryBaselineUpdateId: recoveryBaseline.id,\n };\n}\n\n/**\n * Reactive repair for a healthy-projection pass whose queued rotation\n * checkpoint the server rejected via the covering-baseline gate (a leftover\n * from an interrupted recovery, a heal whose ack was lost, or a lost heal\n * race). Queued checkpoints normally pass through untouched — reset and\n * rotation flows legitimately submit them — but a rejected one would strand\n * the whole queue, and committing it could shrink redirect coverage. Replace\n * the stale checkpoints with one freshly regenerated covering baseline and\n * settle them on success (coverage proven above).\n */\nasync function resolveCheckpointRegenerationMaterial(\n input: Parameters<typeof buildMaterializedDocumentSyncPlan>[0],\n base: ResolvedSyncPlanContentMaterial,\n pendingUpdates: readonly PendingUpdateRecord[],\n): Promise<ResolvedSyncPlanContentMaterial> {\n const ordinaryPendingUpdates = pendingUpdates.filter(\n (update) => update.sourceVersionVector == null,\n );\n const queuedCheckpoints = pendingUpdates.filter(\n (update) => update.sourceVersionVector != null,\n );\n const recoveryBaseline = await buildStaleRecoveryBaselinePendingUpdate(\n input.buildRotationSnapshot,\n );\n const selectedOrdinaryPendingUpdates = selectDocumentSyncOutgoingBatch(\n ordinaryPendingUpdates,\n {\n reservedDataCharacters: recoveryBaseline.updateData.length,\n reservedUpdateCount: 1,\n },\n );\n assertRecoveryBaselineCoversCheckpoints(recoveryBaseline, queuedCheckpoints);\n traceCheckpointRegeneration(input.onSyncTrace, {\n checkpoints: queuedCheckpoints.length,\n documentId: input.writerProjection.documentId,\n updates: selectedOrdinaryPendingUpdates.length,\n });\n return {\n ...base,\n heldBackPendingUpdateIds: queuedCheckpoints.map((update) => update.id),\n pendingUpdates: [recoveryBaseline, ...selectedOrdinaryPendingUpdates],\n staleRecoveryBaselineUpdateId: recoveryBaseline.id,\n };\n}\n\nasync function resolveSyncPlanContentMaterial(\n input: Parameters<typeof buildMaterializedDocumentSyncPlan>[0],\n collectedKeks: Awaited<\n ReturnType<typeof collectContainerKeksForDocumentSync>\n >,\n): Promise<ResolvedSyncPlanContentMaterial> {\n const allPendingUpdates = input.pendingUpdates ?? [];\n if (allPendingUpdates.length > 0 || input.regenerateQueuedCheckpoints) {\n assertDocumentKekPathsCurrent(input.writerProjection);\n }\n const staleContentKeyBundle =\n input.writerProjection.contentKeyBundleStale === true;\n\n if (staleContentKeyBundle && allPendingUpdates.length > 0) {\n traceStaleBundle(input.onSyncTrace, {\n documentId: input.writerProjection.documentId,\n epoch: input.writerProjection.contentKeyBundle.contentKeyEpoch,\n pending: allPendingUpdates.length,\n });\n return resolveStaleHealMaterial(\n input,\n collectedKeks.keksByEpochId,\n allPendingUpdates,\n );\n }\n\n const pendingUpdates = selectDocumentSyncOutgoingBatch(allPendingUpdates);\n\n if (staleContentKeyBundle) {\n traceStaleRead(input.onSyncTrace, {\n documentId: input.writerProjection.documentId,\n epoch: input.writerProjection.contentKeyBundle.contentKeyEpoch,\n });\n // Current access includes every predecessor KEK. If damaged history makes\n // this old bundle unreachable, preserve the bridge-integrity failure: this\n // read actually needs that historical epoch and cannot safely continue.\n let staleContentKey: Uint8Array;\n try {\n staleContentKey = await unwrapDocumentContentKeyFromBundle(\n input.writerProjection.contentKeyBundle,\n input.writerProjection.documentId,\n collectedKeks.keksByEpochId,\n collectedKeks.predecessorFailuresByEpochId,\n collectedKeks.unattributedPredecessorFailuresByContainerId,\n );\n } catch (error) {\n if (error instanceof DocumentHistoryUnavailableError) {\n if (\n input.historyMode !== \"raw\" ||\n !(error.historyCause instanceof ContainerKekHistoryUnavailableError)\n ) {\n throw error;\n }\n }\n // A stale bundle can predate the requester's current authorizing links.\n // Keep the read-only pass alive; served updates that need this absent key\n // remain undecryptable until a spanning member heals the bundle.\n staleContentKey = new Uint8Array();\n }\n return {\n contentKey: staleContentKey,\n contentKeyBundle: input.writerProjection.contentKeyBundle,\n documentKekTargets: documentKekTargetsFromContentKeyBundle(\n input.writerProjection.contentKeyBundle,\n ),\n documentManifest: staleBundleDocumentManifest(input.writerProjection),\n healedStaleContentKeyBundle: false,\n heldBackPendingUpdateIds: [],\n pendingUpdates,\n };\n }\n\n const normalMaterial: ResolvedSyncPlanContentMaterial = {\n contentKey: await unwrapDocumentContentKeyFromBundle(\n input.writerProjection.contentKeyBundle,\n input.writerProjection.documentId,\n collectedKeks.keksByEpochId,\n collectedKeks.predecessorFailuresByEpochId,\n collectedKeks.unattributedPredecessorFailuresByContainerId,\n ),\n contentKeyBundle: input.writerProjection.contentKeyBundle,\n documentKekTargets: input.writerProjection.documentKekTargets,\n documentManifest: input.writerProjection.documentManifest,\n healedStaleContentKeyBundle: false,\n heldBackPendingUpdateIds: [],\n pendingUpdates,\n };\n if (\n input.regenerateQueuedCheckpoints === true &&\n allPendingUpdates.some((update) => update.sourceVersionVector != null)\n ) {\n return resolveCheckpointRegenerationMaterial(\n input,\n normalMaterial,\n allPendingUpdates,\n );\n }\n\n return normalMaterial;\n}\n\nexport async function buildMaterializedDocumentSyncPlan(\n input: {\n author: DocumentCreateAuthor;\n /**\n * Supplies a full-history Loro snapshot of the local document when a\n * stale content-key bundle must be healed, or when a leftover queued\n * rotation checkpoint must be regenerated as a covering baseline.\n * Without it those passes fail with a descriptive error instead.\n */\n buildRotationSnapshot?: (() => Promise<Uint8Array | null>) | undefined;\n containerRekeys?: BuildDocumentSyncPlanInput[\"containerRekeys\"];\n execSql?: ExecSql | undefined;\n historyMode?: \"raw\" | undefined;\n inlineRekeyCommitId?: string | undefined;\n localVersionVector: string | null;\n minLsn?: string | undefined;\n pullCursor?: string | undefined;\n /** Clipboard-safe trace sink (see syncTrace.ts); never receives content. */\n onSyncTrace?: DocumentSyncTraceEmitter | undefined;\n pendingUpdates?: readonly PendingUpdateRecord[] | undefined;\n persistVerificationCheckpoints?: boolean | undefined;\n /**\n * Replace queued rotation checkpoints with a freshly regenerated covering\n * baseline instead of passing them through. Set by the sync loop after\n * the server rejected a pass via the covering-baseline gate.\n */\n regenerateQueuedCheckpoints?: boolean | undefined;\n signedAt?: string | undefined;\n targetSecretKey: Uint8Array;\n writerProjection: DocumentWriterProjectionResponse;\n } & ProjectionVerificationOptions,\n): Promise<MaterializedDocumentSyncPlan> {\n // Reuse verification across consistency and unwrap passes; unwrap still\n // binds the key material independently.\n const verifiedByHash = new Map<string, VerifiedContainerAccessManifest>();\n const principalPolicyCache = new Map<string, VerifiedPrincipalPolicy>();\n let writerAuthorization: DocumentWriterProjectionAuthorization | undefined;\n await assertDocumentWriterProjectionConsistent(input.writerProjection, {\n allowStaleContentKeyBundle: true,\n execSql: input.execSql,\n persistVerificationCheckpoints: input.persistVerificationCheckpoints,\n onVerifiedAuthorization: (authorization) => {\n writerAuthorization = authorization;\n },\n principalPolicyCache,\n verifiedByHash,\n ...projectionVerificationOptions(input),\n });\n const collectedKeks = await collectContainerKeksForDocumentSync({\n execSql: input.execSql,\n persistVerificationCheckpoints: input.persistVerificationCheckpoints,\n principalPolicyCache,\n secretKey: input.targetSecretKey,\n verifiedByHash,\n writerProjection: input.writerProjection,\n ...projectionVerificationOptions(input),\n });\n const documentId = input.writerProjection.documentId;\n const manifestIdentity = await assertDocumentManifestBundleConsistent({\n bundle: input.writerProjection.documentManifest,\n label: \"Document sync manifest\",\n });\n let material: ResolvedSyncPlanContentMaterial;\n try {\n material = await resolveSyncPlanContentMaterial(input, collectedKeks);\n } catch (error) {\n // Only a stale-bundle HEAL (write-bearing) or a checkpoint regeneration\n // counts as a blocked recovery; an ordinary pass or a stale READ failing\n // here must not read as one in a support report. Enumerated reason only;\n // unknown errors emit nothing (fail closed).\n if (\n (input.writerProjection.contentKeyBundleStale === true &&\n (input.pendingUpdates ?? []).length > 0) ||\n input.regenerateQueuedCheckpoints === true\n ) {\n traceHealBlocked(input.onSyncTrace, {\n documentId: input.writerProjection.documentId,\n error,\n });\n }\n throw error;\n }\n const {\n contentKey,\n contentKeyBundle,\n documentKekTargets,\n documentManifest,\n pendingUpdates,\n } = material;\n const outgoingUpdates = await prepareDocumentOutgoingUpdates({\n contentKey,\n contentKeyEpoch: contentKeyBundle.contentKeyEpoch,\n documentId,\n organizationId: manifestIdentity.organizationId,\n pendingUpdates,\n });\n const basePlan = await buildDocumentSyncPlan({\n author: {\n ...input.author,\n organizationId: manifestIdentity.organizationId,\n },\n authorizingContainerPathRefs: authorizingContainerPathRefs(\n input.writerProjection,\n ),\n containerRekeys: input.containerRekeys,\n contentKeyBundle,\n documentId,\n documentKekTargets,\n documentManifest,\n historyMode: input.historyMode,\n inlineRekeyCommitId: input.inlineRekeyCommitId,\n localVersionVector: input.localVersionVector,\n minLsn: input.minLsn,\n outgoingUpdates,\n pullCursor: input.pullCursor,\n signedAt: input.signedAt,\n });\n return finalizeMaterializedDocumentSyncPlan({\n basePlan,\n material,\n writerAuthorization,\n writerProjection: input.writerProjection,\n });\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { limitDocumentSyncRequestBytes } from "../../../data/sync/documentSyncOutgoingBatch.js";
|
|
1
|
+
import { limitDocumentSyncRequestBytes } from "../../../data/documents/shared/sync/documentSyncOutgoingBatch.js";
|
|
2
2
|
export function boundDocumentSyncPlanRequest(plan, requiredOutgoingUpdateId) {
|
|
3
3
|
const request = limitDocumentSyncRequestBytes(plan.request, {
|
|
4
4
|
requiredOutgoingUpdateId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncPlanRequestBounds.js","sourceRoot":"","sources":["syncPlanRequestBounds.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"syncPlanRequestBounds.js","sourceRoot":"","sources":["syncPlanRequestBounds.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,+DAA+D,CAAC;AAW9G,MAAM,UAAU,4BAA4B,CAC1C,IAA0C,EAC1C,wBAA4C;IAE5C,MAAM,OAAO,GAAG,6BAA6B,CAAC,IAAI,CAAC,OAAO,EAAE;QAC1D,wBAAwB;KACzB,CAAC,CAAC;IACH,OAAO,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,QAA0B,EAC1B,IAA0C,EAC1C,gBAAkE;IAElE,OAAO;QACL,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,2BAA2B,EAAE,QAAQ,CAAC,2BAA2B;QACjE,wBAAwB,EAAE,QAAQ,CAAC,wBAAwB;QAC3D,IAAI;QACJ,gBAAgB;QAChB,GAAG,CAAC,QAAQ,CAAC,6BAA6B,KAAK,SAAS;YACtD,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,6BAA6B,EAAE,QAAQ,CAAC,6BAA6B;aACtE,CAAC;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,cAA8C,EAC9C,gBAA8C;IAE9C,MAAM,YAAY,GAAG,IAAI,GAAG,CAC1B,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CACzE,CAAC;IACF,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;QAC7B,GAAG,YAAY;QACf,GAAG,gBAAgB,CAAC,wBAAwB;KAC7C,CAAC,CAAC;IACH,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,oCAAoC,CAAC,KAMpD;IACC,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,mBAAmB,GACvB,KAAK,CAAC,wBAAwB;QAC9B,KAAK,CAAC,uBAAuB,CAAC,MAAM,GAAG,CAAC;QACxC,KAAK,CAAC,uBAAuB,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3C,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACvE,OAAO,CACL,KAAK,CAAC,uBAAuB,CAAC,MAAM,GAAG,CAAC;QACxC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CACzB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,uBAAuB,CAAC,GAAG,CAAC,QAAQ,CAAC,CACrD,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gCAAgC,CAC9C,gBAA8C,EAC9C,QAA8B;IAE9B,MAAM,kBAAkB,GAAG,gBAAgB,CAAC,6BAA6B,CAAC;IAC1E,OAAO,CACL,kBAAkB,KAAK,SAAS;QAChC,QAAQ,CAAC,yBAAyB,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAChE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gCAAgC,CAC9C,gBAA8C,EAC9C,QAA8B;IAE9B,OAAO,gCAAgC,CAAC,gBAAgB,EAAE,QAAQ,CAAC;QACjE,CAAC,CAAC,gBAAgB,CAAC,wBAAwB;QAC3C,CAAC,CAAC,EAAE,CAAC;AACT,CAAC","sourcesContent":["import type { DocumentSyncResponse } from \"@tearleads/validators/response\";\nimport { limitDocumentSyncRequestBytes } from \"../../../data/documents/shared/sync/documentSyncOutgoingBatch\";\nimport type { MaterializedDocumentSyncPlan } from \"../../../data/documents/shared/types\";\nimport type { PendingUpdateRecord } from \"../../../data/sqlite/documentPersistence\";\n\ninterface SyncPlanMaterial {\n readonly contentKey: Uint8Array;\n readonly healedStaleContentKeyBundle: boolean;\n readonly heldBackPendingUpdateIds: readonly string[];\n readonly staleRecoveryBaselineUpdateId?: string | undefined;\n}\n\nexport function boundDocumentSyncPlanRequest(\n plan: MaterializedDocumentSyncPlan[\"plan\"],\n requiredOutgoingUpdateId: string | undefined,\n): MaterializedDocumentSyncPlan[\"plan\"] {\n const request = limitDocumentSyncRequestBytes(plan.request, {\n requiredOutgoingUpdateId,\n });\n return request === plan.request ? plan : { ...plan, request };\n}\n\nexport function materializedDocumentSyncPlan(\n material: SyncPlanMaterial,\n plan: MaterializedDocumentSyncPlan[\"plan\"],\n writerProjection: MaterializedDocumentSyncPlan[\"writerProjection\"],\n): MaterializedDocumentSyncPlan {\n return {\n contentKey: material.contentKey,\n healedStaleContentKeyBundle: material.healedStaleContentKeyBundle,\n heldBackPendingUpdateIds: material.heldBackPendingUpdateIds,\n plan,\n writerProjection,\n ...(material.staleRecoveryBaselineUpdateId === undefined\n ? {}\n : {\n staleRecoveryBaselineUpdateId: material.staleRecoveryBaselineUpdateId,\n }),\n };\n}\n\nexport function recoverablePendingUpdates(\n pendingUpdates: readonly PendingUpdateRecord[],\n materializedPlan: MaterializedDocumentSyncPlan,\n): PendingUpdateRecord[] {\n const submittedIds = new Set(\n materializedPlan.plan.request.outgoingUpdates.map((update) => update.id),\n );\n const recoverableIds = new Set([\n ...submittedIds,\n ...materializedPlan.heldBackPendingUpdateIds,\n ]);\n return pendingUpdates.filter((update) => recoverableIds.has(update.id));\n}\n\nexport function hasDeferredPendingUpdatesAfterSubmit(input: {\n acceptedRecoveryBaseline: boolean;\n exhaustedPendingUpdateCount: number;\n pendingUpdateIds: readonly string[];\n rekeyedPendingUpdateIds: readonly string[];\n settledPendingUpdateIds: readonly string[];\n}): boolean {\n if (input.exhaustedPendingUpdateCount > 0) {\n return false;\n }\n\n const madeDurableProgress =\n input.acceptedRecoveryBaseline ||\n input.settledPendingUpdateIds.length > 0 ||\n input.rekeyedPendingUpdateIds.length > 0;\n if (!madeDurableProgress) {\n return false;\n }\n\n const settledPendingUpdateIds = new Set(input.settledPendingUpdateIds);\n return (\n input.rekeyedPendingUpdateIds.length > 0 ||\n input.pendingUpdateIds.some(\n (updateId) => !settledPendingUpdateIds.has(updateId),\n )\n );\n}\n\nexport function responseAcceptedRecoveryBaseline(\n materializedPlan: MaterializedDocumentSyncPlan,\n response: DocumentSyncResponse,\n): boolean {\n const recoveryBaselineId = materializedPlan.staleRecoveryBaselineUpdateId;\n return (\n recoveryBaselineId !== undefined &&\n response.acceptedOutgoingUpdateIds.includes(recoveryBaselineId)\n );\n}\n\nexport function acceptedHeldBackPendingUpdateIds(\n materializedPlan: MaterializedDocumentSyncPlan,\n response: DocumentSyncResponse,\n): readonly string[] {\n return responseAcceptedRecoveryBaseline(materializedPlan, response)\n ? materializedPlan.heldBackPendingUpdateIds\n : [];\n}\n"]}
|
|
@@ -5,7 +5,7 @@ import { ContainerKekRepairInaccessibleError } from "../../../data/documents/sha
|
|
|
5
5
|
import { ContainerKekTargetUnreachableError } from "../../../data/documents/shared/containerKek/containerKekPath.js";
|
|
6
6
|
import { documentContainerProjections } from "../../../data/keyingProjectionVerification/document/documentContainerProjections.js";
|
|
7
7
|
import { assertProjectionVerificationCurrent } from "../../../data/keyingProjectionVerification/types.js";
|
|
8
|
-
import { buildMaterializedContainerRekeyPlan } from "../../containers/
|
|
8
|
+
import { buildMaterializedContainerRekeyPlan } from "../../containers/index.js";
|
|
9
9
|
import { DocumentAncestorRepairAbandonedError } from "../syncFailures/syncRepairAbandon.js";
|
|
10
10
|
import { hasOtherLinkedWriteAccess } from "../syncFailures/syncRepairAccess.js";
|
|
11
11
|
import { applyContainerRekeyPlan } from "./syncContainerRekeyProjection.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncAutomaticContainerRekeys.js","sourceRoot":"","sources":["syncAutomaticContainerRekeys.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport type {\n ContainerWriterProjectionResponse,\n DocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { MAX_INLINE_CONTAINER_REKEYS } from \"@tearleads/validators/util\";\nimport { ContainerAuthorAccessError } from \"../../../data/containers/shared/authorAccess\";\nimport type { MaterializedContainerRekeyPlan } from \"../../../data/containers/shared/types\";\nimport { ContainerKekRepairInaccessibleError } from \"../../../data/documents/shared/containerKek/containerKekCurrency\";\nimport { ContainerKekTargetUnreachableError } from \"../../../data/documents/shared/containerKek/containerKekPath\";\nimport { documentContainerProjections } from \"../../../data/keyingProjectionVerification/document/documentContainerProjections\";\nimport { assertProjectionVerificationCurrent } from \"../../../data/keyingProjectionVerification/types\";\nimport { buildMaterializedContainerRekeyPlan } from \"../../containers/child/rekey\";\nimport type { SyncRemoteDocumentInput } from \"../sync/readOnlySync\";\nimport { DocumentAncestorRepairAbandonedError } from \"../syncFailures/syncRepairAbandon\";\nimport { hasOtherLinkedWriteAccess } from \"../syncFailures/syncRepairAccess\";\nimport { applyContainerRekeyPlan } from \"./syncContainerRekeyProjection\";\n\ninterface StaleContainer {\n /** The stale container is the one the document itself is linked into. */\n readonly isPathTarget: boolean;\n readonly projection: ContainerWriterProjectionResponse;\n}\n\nfunction firstStaleContainer(\n projection: DocumentWriterProjectionResponse,\n): StaleContainer | null {\n for (const path of documentContainerProjections(projection)) {\n for (let index = 1; index < path.containerKeks.length; index += 1) {\n const parent = path.containerKeks[index - 1];\n const child = path.containerKeks[index];\n if (!parent || !child)\n throw new Error(\"Container KEK path is incomplete\");\n if (child.parentContainerKeyEpochId === parent.containerKeyEpochId) {\n continue;\n }\n return {\n isPathTarget: index === path.containerKeks.length - 1,\n projection: {\n ...path,\n containerId: child.containerId,\n containerKeks: path.containerKeks.slice(0, index + 1),\n path: path.path.slice(0, index + 1),\n },\n };\n }\n }\n return null;\n}\n\n/**\n * The stale container is not this writer's to re-key: no wrap on the verified\n * path opens it, or the signed path grants the signer no write access there.\n * Both are judged against the path sliced at the stale container, so a grant\n * held only further down never counts — exactly the API's `container.rekey`\n * rule. Anything else (keyring damage, a forged path) stays an error.\n *\n * A target's write refusal is handled separately: another linked target may\n * still authorize the document write. Key unwrap failures on a target stay\n * errors, because this path's readable grant should open its own key.\n */\nfunction isRepairInaccessible(error: unknown, stale: StaleContainer): boolean {\n if (stale.isPathTarget) return false;\n return (\n error instanceof ContainerAuthorAccessError ||\n (error instanceof ContainerKekTargetUnreachableError &&\n error.containerId === stale.projection.containerId)\n );\n}\n\n/** Prepare parent-first repairs; the sync transaction commits them with the write. */\nexport async function buildAutomaticContainerRekeys(\n sync: SyncRemoteDocumentInput,\n initialProjection: DocumentWriterProjectionResponse,\n): Promise<{ plans: MaterializedContainerRekeyPlan[]; hasMore: boolean }> {\n const plans: MaterializedContainerRekeyPlan[] = [];\n const plannedIds = new Set<string>();\n let projection = initialProjection;\n for (;;) {\n const stale = firstStaleContainer(projection);\n if (!stale) return { plans, hasMore: false };\n const previousProjection = stale.projection;\n if (plans.length >= MAX_INLINE_CONTAINER_REKEYS) {\n return { plans, hasMore: true };\n }\n // Use the shared cancellation sentinel: syncRemoteDocument converts only\n // that to a graceful null, so a plain Error would report a routine\n // generation flip mid-repair as a real sync failure.\n assertProjectionVerificationCurrent(sync.stillCurrent);\n if (plannedIds.has(previousProjection.containerId)) {\n // Abandon like every other routine repair failure: two linked paths\n // disagreeing about a container is a concurrent-rotation race, not a\n // defect, and a raw Error would crash the sync lane instead.\n throw new DocumentAncestorRepairAbandonedError(\"peer-rotation\");\n }\n // Assert rather than adopt: resolveRotationContext compares the verified\n // manifest's organization against the author's, so substituting the path's\n // own organization here would make that the only cross-org check and leave\n // it comparing a server-supplied value with itself.\n if (previousProjection.organizationId !== sync.author.organizationId) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Document ancestor repair crosses the author's organization\",\n );\n }\n const planned = await buildMaterializedContainerRekeyPlan({\n author: sync.author,\n persistVerificationCheckpoints: false,\n previousProjection,\n signedAt: sync.signedAt,\n targetSecretKey: sync.targetSecretKey,\n resolveProjectionUserKey: sync.resolveProjectionUserKey,\n stillCurrent: sync.stillCurrent,\n warmReferencedPrincipalPolicies: sync.warmReferencedPrincipalPolicies,\n execSql: sync.execSql,\n }).catch(async (error: unknown) => {\n if (error instanceof ContainerAuthorAccessError && stale.isPathTarget) {\n if (\n await hasOtherLinkedWriteAccess(\n sync,\n initialProjection,\n previousProjection.containerId,\n )\n ) {\n throw new ContainerKekRepairInaccessibleError(\n previousProjection.containerId,\n );\n }\n await sync.onTerminalSubmitFailure?.({\n code: error.code,\n message: error.message,\n ok: false,\n report: () => undefined,\n status: error.status,\n });\n throw new DocumentAncestorRepairAbandonedError(\"refused\");\n }\n // Repairs run parent-first and access inherits downward, so only the\n // first stale container can be out of reach. Retry-classified like any\n // stale path: one refetch sees a repair a capable member already made.\n throw isRepairInaccessible(error, stale)\n ? new ContainerKekRepairInaccessibleError(\n previousProjection.containerId,\n )\n : error;\n });\n plannedIds.add(previousProjection.containerId);\n plans.push(planned);\n projection = await applyContainerRekeyPlan(projection, planned);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"syncAutomaticContainerRekeys.js","sourceRoot":"","sources":["syncAutomaticContainerRekeys.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport type {\n ContainerWriterProjectionResponse,\n DocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { MAX_INLINE_CONTAINER_REKEYS } from \"@tearleads/validators/util\";\nimport { ContainerAuthorAccessError } from \"../../../data/containers/shared/authorAccess\";\nimport type { MaterializedContainerRekeyPlan } from \"../../../data/containers/shared/types\";\nimport { ContainerKekRepairInaccessibleError } from \"../../../data/documents/shared/containerKek/containerKekCurrency\";\nimport { ContainerKekTargetUnreachableError } from \"../../../data/documents/shared/containerKek/containerKekPath\";\nimport { documentContainerProjections } from \"../../../data/keyingProjectionVerification/document/documentContainerProjections\";\nimport { assertProjectionVerificationCurrent } from \"../../../data/keyingProjectionVerification/types\";\nimport { buildMaterializedContainerRekeyPlan } from \"../../containers\";\nimport type { SyncRemoteDocumentInput } from \"../sync/readOnlySync\";\nimport { DocumentAncestorRepairAbandonedError } from \"../syncFailures/syncRepairAbandon\";\nimport { hasOtherLinkedWriteAccess } from \"../syncFailures/syncRepairAccess\";\nimport { applyContainerRekeyPlan } from \"./syncContainerRekeyProjection\";\n\ninterface StaleContainer {\n /** The stale container is the one the document itself is linked into. */\n readonly isPathTarget: boolean;\n readonly projection: ContainerWriterProjectionResponse;\n}\n\nfunction firstStaleContainer(\n projection: DocumentWriterProjectionResponse,\n): StaleContainer | null {\n for (const path of documentContainerProjections(projection)) {\n for (let index = 1; index < path.containerKeks.length; index += 1) {\n const parent = path.containerKeks[index - 1];\n const child = path.containerKeks[index];\n if (!parent || !child)\n throw new Error(\"Container KEK path is incomplete\");\n if (child.parentContainerKeyEpochId === parent.containerKeyEpochId) {\n continue;\n }\n return {\n isPathTarget: index === path.containerKeks.length - 1,\n projection: {\n ...path,\n containerId: child.containerId,\n containerKeks: path.containerKeks.slice(0, index + 1),\n path: path.path.slice(0, index + 1),\n },\n };\n }\n }\n return null;\n}\n\n/**\n * The stale container is not this writer's to re-key: no wrap on the verified\n * path opens it, or the signed path grants the signer no write access there.\n * Both are judged against the path sliced at the stale container, so a grant\n * held only further down never counts — exactly the API's `container.rekey`\n * rule. Anything else (keyring damage, a forged path) stays an error.\n *\n * A target's write refusal is handled separately: another linked target may\n * still authorize the document write. Key unwrap failures on a target stay\n * errors, because this path's readable grant should open its own key.\n */\nfunction isRepairInaccessible(error: unknown, stale: StaleContainer): boolean {\n if (stale.isPathTarget) return false;\n return (\n error instanceof ContainerAuthorAccessError ||\n (error instanceof ContainerKekTargetUnreachableError &&\n error.containerId === stale.projection.containerId)\n );\n}\n\n/** Prepare parent-first repairs; the sync transaction commits them with the write. */\nexport async function buildAutomaticContainerRekeys(\n sync: SyncRemoteDocumentInput,\n initialProjection: DocumentWriterProjectionResponse,\n): Promise<{ plans: MaterializedContainerRekeyPlan[]; hasMore: boolean }> {\n const plans: MaterializedContainerRekeyPlan[] = [];\n const plannedIds = new Set<string>();\n let projection = initialProjection;\n for (;;) {\n const stale = firstStaleContainer(projection);\n if (!stale) return { plans, hasMore: false };\n const previousProjection = stale.projection;\n if (plans.length >= MAX_INLINE_CONTAINER_REKEYS) {\n return { plans, hasMore: true };\n }\n // Use the shared cancellation sentinel: syncRemoteDocument converts only\n // that to a graceful null, so a plain Error would report a routine\n // generation flip mid-repair as a real sync failure.\n assertProjectionVerificationCurrent(sync.stillCurrent);\n if (plannedIds.has(previousProjection.containerId)) {\n // Abandon like every other routine repair failure: two linked paths\n // disagreeing about a container is a concurrent-rotation race, not a\n // defect, and a raw Error would crash the sync lane instead.\n throw new DocumentAncestorRepairAbandonedError(\"peer-rotation\");\n }\n // Assert rather than adopt: resolveRotationContext compares the verified\n // manifest's organization against the author's, so substituting the path's\n // own organization here would make that the only cross-org check and leave\n // it comparing a server-supplied value with itself.\n if (previousProjection.organizationId !== sync.author.organizationId) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Document ancestor repair crosses the author's organization\",\n );\n }\n const planned = await buildMaterializedContainerRekeyPlan({\n author: sync.author,\n persistVerificationCheckpoints: false,\n previousProjection,\n signedAt: sync.signedAt,\n targetSecretKey: sync.targetSecretKey,\n resolveProjectionUserKey: sync.resolveProjectionUserKey,\n stillCurrent: sync.stillCurrent,\n warmReferencedPrincipalPolicies: sync.warmReferencedPrincipalPolicies,\n execSql: sync.execSql,\n }).catch(async (error: unknown) => {\n if (error instanceof ContainerAuthorAccessError && stale.isPathTarget) {\n if (\n await hasOtherLinkedWriteAccess(\n sync,\n initialProjection,\n previousProjection.containerId,\n )\n ) {\n throw new ContainerKekRepairInaccessibleError(\n previousProjection.containerId,\n );\n }\n await sync.onTerminalSubmitFailure?.({\n code: error.code,\n message: error.message,\n ok: false,\n report: () => undefined,\n status: error.status,\n });\n throw new DocumentAncestorRepairAbandonedError(\"refused\");\n }\n // Repairs run parent-first and access inherits downward, so only the\n // first stale container can be out of reach. Retry-classified like any\n // stale path: one refetch sees a repair a capable member already made.\n throw isRepairInaccessible(error, stale)\n ? new ContainerKekRepairInaccessibleError(\n previousProjection.containerId,\n )\n : error;\n });\n plannedIds.add(previousProjection.containerId);\n plans.push(planned);\n projection = await applyContainerRekeyPlan(projection, planned);\n }\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { decryptGroupMetadata, readGroupMetadata, } from "../../../internal/crypto/index.js";
|
|
2
2
|
import { deriveOrganizationMetadataContainerSystemSlot } from "../../../internal/validators/containerSystemSlot.js";
|
|
3
3
|
import { getTargetContainerContext, readContainerState, } from "../../../data/containers/shared/projection.js";
|
|
4
|
-
import { unwrapContainerKekPath } from "../../../data/documents/shared/containerKek/
|
|
4
|
+
import { unwrapContainerKekPath } from "../../../data/documents/shared/containerKek/index.js";
|
|
5
5
|
import { assertProjectionVerificationCurrent } from "../../../data/keyingProjectionVerification/types.js";
|
|
6
6
|
import { loadContainers } from "../../../data/persistence/container-contents/containerPersistence.js";
|
|
7
7
|
import { withMetadataRootReload } from "./groupMetadataErrors.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groupMetadataAccess.js","sourceRoot":"","sources":["groupMetadataAccess.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,EAGpB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,6CAA6C,EAAE,MAAM,2CAA2C,CAAC;AAE1G,OAAO,EACL,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAC;AAKtG,OAAO,EAAE,mCAAmC,EAAE,MAAM,kDAAkD,CAAC;AACvG,OAAO,EAAE,cAAc,EAAE,MAAM,mEAAmE,CAAC;AAGnG,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAuB/D;;;;;;GAMG;AACH,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IACrD,YAAY,OAAe,EAAE,KAAc;QACzC,KAAK,CAAC,SAAS,OAAO,+BAA+B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;IAC7C,CAAC;CACF;AAED,SAAS,mBAAmB,CAC1B,KAA+B,EAC/B,WAAmB;IAEnB,OAAO,sBAAsB,CAC3B,GAAG,EAAE,CAAC,uBAAuB,CAAC,KAAK,EAAE,WAAW,CAAC,EACjD,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,8BAA8B,CAAC,WAAW,CAAC,CAClE,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,uBAAuB,CACpC,KAA+B,EAC/B,WAAmB;IAEnB,MAAM,UAAU,GACd,MAAM,KAAK,CAAC,SAAS,CAAC,4BAA4B,CAAC,WAAW,CAAC,CAAC;IAClE,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,IACE,UAAU,CAAC,WAAW,KAAK,WAAW;QACtC,UAAU,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;QAElD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,yBAAyB,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,MAAM,6CAA6C,CAAC;QAC/D,cAAc,EAAE,KAAK,CAAC,cAAc;KACrC,CAAC,CAAC;IACH,IACE,KAAK,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;QAC7C,KAAK,CAAC,UAAU,KAAK,IAAI;QACzB,KAAK,CAAC,iBAAiB,KAAK,IAAI;QAEhC,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;IACJ,MAAM,KAAK,CAAC,uBAAuB,CACjC,KAAK,EACL,UAAU,CAAC,cAAc,CAAC,YAAY,EAAE,IAAI,CAC7C,CAAC;IACF,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,MAAM,sBAAsB,CAAC;QACxC,GAAG,KAAK;QACR,UAAU;QACV,SAAS,EAAE,KAAK,CAAC,eAAe;KACjC,CAAC,CAAC;IACH,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAC7D,IAAI,CAAC,WAAW;QACd,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,OAAO;QACL,OAAO,EAAE;YACP,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,WAAW;YACX,mBAAmB,EAAE,MAAM,CAAC,GAAG,CAAC,mBAAmB;YACnD,WAAW;SACZ;QACD,IAAI;KACL,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,yBAAyB,CAAC,KAA+B;IACvE,MAAM,KAAK,GAAG,IAAI,GAAG,EAMlB,CAAC;IACJ,SAAS,MAAM,CAAC,WAAmB;QACjC,IAAI,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,mBAAmB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;YAClD,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,QAAQ,GAA0B,KAAK,EAAE,MAAM,EAAE,EAAE;QACvD,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACrE,IAAI,MAAM,IAAI,QAAQ;YACpB,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3D,IACE,QAAQ,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;YAChD,QAAQ,CAAC,OAAO,KAAK,MAAM,CAAC,YAAY,CAAC,WAAW;YACpD,MAAM,CAAC,YAAY,CAAC,aAAa,KAAK,OAAO;YAE7C,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnD,MAAM,WAAW,GAAG,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;QACpE,IAAI,CAAC,WAAW;YACd,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAClE,IAAI,CAAC;YACH,OAAO,MAAM,oBAAoB,CAAC;gBAChC,GAAG,EAAE,EAAE,GAAG,QAAQ,EAAE,WAAW,EAAE;gBACjC,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,WAAW;gBACxC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,UAAU;aAC1C,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,4BAA4B,CACpC,MAAM,CAAC,YAAY,CAAC,WAAW,EAC/B,KAAK,CACN,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IACF,OAAO;QACL,QAAQ;QACR,KAAK,CAAC,iBAAiB;YACrB,MAAM,IAAI,GAAG,MAAM,6CAA6C,CAAC;gBAC/D,cAAc,EAAE,KAAK,CAAC,cAAc;aACrC,CAAC,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACvD,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAC/B,CAAC,GAAG,EAAE,EAAE,CACN,GAAG,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;gBAC3C,GAAG,CAAC,UAAU,KAAK,IAAI,CAC1B,CAAC;YACF,IAAI,CAAC,SAAS;gBACZ,MAAM,IAAI,KAAK,CACb,yDAAyD,CAC1D,CAAC;YACJ,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YACzE,OAAO,OAAO,CAAC,OAAO,CAAC;QACzB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import {\n type ContainerAccessManifestState,\n decryptGroupMetadata,\n type GroupMetadataKey,\n type ReferencedPrincipalHead,\n readGroupMetadata,\n} from \"@tearleads/crypto\";\nimport { deriveOrganizationMetadataContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n getTargetContainerContext,\n readContainerState,\n} from \"../../../data/containers/shared/projection\";\nimport { unwrapContainerKekPath } from \"../../../data/documents/shared/containerKek/containerKekPath\";\nimport type {\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../../../data/keyingProjectionVerification\";\nimport { assertProjectionVerificationCurrent } from \"../../../data/keyingProjectionVerification/types\";\nimport { loadContainers } from \"../../../data/persistence/container-contents/containerPersistence\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport type { GroupPolicyNameReader } from \"../principalPolicy/principalPolicyRequest\";\nimport { withMetadataRootReload } from \"./groupMetadataErrors\";\n\nexport interface GroupMetadataAccessInput {\n readonly apiClient: {\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n };\n readonly verifyMetadataContainer: (\n state: ContainerAccessManifestState,\n organizationReference?: ReferencedPrincipalHead | undefined,\n ) => Promise<void>;\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly resolveProjectionUserKey: ProjectionUserKeyResolver;\n readonly targetSecretKey: Uint8Array;\n readonly stillCurrent?: (() => boolean) | undefined;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}\n\n/**\n * A group's signed name does not open under the metadata key it cites, or opens\n * to an invalid name. Every reader holding that key gets the same result. The\n * API checks the citation but can never check the ciphertext, so a dishonest\n * admin can sign such a name; directory walks isolate the group instead of\n * failing every name in the organization.\n */\nexport class GroupMetadataUnreadableError extends Error {\n constructor(groupId: string, cause: unknown) {\n super(`Group ${groupId} metadata cannot be decrypted`, { cause });\n this.name = \"GroupMetadataUnreadableError\";\n }\n}\n\nfunction loadMetadataKeyring(\n input: GroupMetadataAccessInput,\n containerId: string,\n) {\n return withMetadataRootReload(\n () => loadMetadataKeyringOnce(input, containerId),\n () => input.apiClient.evictContainerWriterProjection(containerId),\n );\n}\n\nasync function loadMetadataKeyringOnce(\n input: GroupMetadataAccessInput,\n containerId: string,\n) {\n const projection =\n await input.apiClient.getContainerWriterProjection(containerId);\n if (!projection) return null;\n if (\n projection.containerId !== containerId ||\n projection.organizationId !== input.organizationId\n )\n throw new Error(\"Group metadata container is unavailable\");\n const target = getTargetContainerContext(projection);\n const state = readContainerState(target.manifest);\n const slot = await deriveOrganizationMetadataContainerSystemSlot({\n organizationId: input.organizationId,\n });\n if (\n state.organizationId !== input.organizationId ||\n state.systemSlot !== slot ||\n state.parentContainerId !== null\n )\n throw new Error(\n \"Group metadata requires the signed organization metadata container\",\n );\n await input.verifyMetadataContainer(\n state,\n projection.policyEvidence.organization?.head,\n );\n assertProjectionVerificationCurrent(input.stillCurrent);\n const keys = await unwrapContainerKekPath({\n ...input,\n projection,\n secretKey: input.targetSecretKey,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n const keyMaterial = keys.get(target.kek.containerKeyEpochId);\n if (!keyMaterial)\n throw new Error(\"Group metadata container key is unavailable\");\n return {\n current: {\n organizationId: input.organizationId,\n containerId,\n containerKeyEpochId: target.kek.containerKeyEpochId,\n keyMaterial,\n },\n keys,\n };\n}\n\n/** Uses the shared metadata container's authenticated keyring, including old epochs. */\nexport function createGroupMetadataAccess(input: GroupMetadataAccessInput) {\n const loads = new Map<\n string,\n Promise<{\n current: GroupMetadataKey;\n keys: ReadonlyMap<string, Uint8Array>;\n } | null>\n >();\n function loaded(containerId: string) {\n let pending = loads.get(containerId);\n if (!pending) {\n pending = loadMetadataKeyring(input, containerId);\n loads.set(containerId, pending);\n }\n return pending;\n }\n const readName: GroupPolicyNameReader = async (bundle) => {\n assertProjectionVerificationCurrent(input.stillCurrent);\n const metadata = readGroupMetadata(bundle.currentPayload.ciphertext);\n if (\"role\" in metadata)\n return metadata.role === \"admins\" ? \"Admins\" : \"Members\";\n if (\n metadata.organizationId !== input.organizationId ||\n metadata.groupId !== bundle.currentState.principalId ||\n bundle.currentState.principalType !== \"group\"\n )\n throw new Error(\"Group metadata scope does not match its signed group\");\n const keyring = await loaded(metadata.containerId);\n const keyMaterial = keyring?.keys.get(metadata.containerKeyEpochId);\n if (!keyMaterial)\n throw new Error(\"Group metadata historical key is unavailable\");\n try {\n return await decryptGroupMetadata({\n key: { ...metadata, keyMaterial },\n groupId: bundle.currentState.principalId,\n payload: bundle.currentPayload.ciphertext,\n });\n } catch (cause) {\n throw new GroupMetadataUnreadableError(\n bundle.currentState.principalId,\n cause,\n );\n }\n };\n return {\n readName,\n async loadEncryptionKey(): Promise<GroupMetadataKey> {\n const slot = await deriveOrganizationMetadataContainerSystemSlot({\n organizationId: input.organizationId,\n });\n const containers = await loadContainers(input.execSql);\n const container = containers.find(\n (row) =>\n row.organizationId === input.organizationId &&\n row.systemSlot === slot,\n );\n if (!container)\n throw new Error(\n \"Organization metadata container has not been discovered\",\n );\n const keyring = await loaded(container.id);\n if (!keyring) throw new Error(\"Group metadata container is unavailable\");\n return keyring.current;\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"groupMetadataAccess.js","sourceRoot":"","sources":["groupMetadataAccess.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type ContainerAccessManifestState,\n decryptGroupMetadata,\n type GroupMetadataKey,\n type ReferencedPrincipalHead,\n readGroupMetadata,\n} from \"@tearleads/crypto\";\nimport { deriveOrganizationMetadataContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n getTargetContainerContext,\n readContainerState,\n} from \"../../../data/containers/shared/projection\";\nimport { unwrapContainerKekPath } from \"../../../data/documents/shared/containerKek\";\nimport type {\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../../../data/keyingProjectionVerification\";\nimport { assertProjectionVerificationCurrent } from \"../../../data/keyingProjectionVerification/types\";\nimport { loadContainers } from \"../../../data/persistence/container-contents/containerPersistence\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport type { GroupPolicyNameReader } from \"../principalPolicy/principalPolicyRequest\";\nimport { withMetadataRootReload } from \"./groupMetadataErrors\";\n\nexport interface GroupMetadataAccessInput {\n readonly apiClient: {\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n };\n readonly verifyMetadataContainer: (\n state: ContainerAccessManifestState,\n organizationReference?: ReferencedPrincipalHead | undefined,\n ) => Promise<void>;\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly resolveProjectionUserKey: ProjectionUserKeyResolver;\n readonly targetSecretKey: Uint8Array;\n readonly stillCurrent?: (() => boolean) | undefined;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}\n\n/**\n * A group's signed name does not open under the metadata key it cites, or opens\n * to an invalid name. Every reader holding that key gets the same result. The\n * API checks the citation but can never check the ciphertext, so a dishonest\n * admin can sign such a name; directory walks isolate the group instead of\n * failing every name in the organization.\n */\nexport class GroupMetadataUnreadableError extends Error {\n constructor(groupId: string, cause: unknown) {\n super(`Group ${groupId} metadata cannot be decrypted`, { cause });\n this.name = \"GroupMetadataUnreadableError\";\n }\n}\n\nfunction loadMetadataKeyring(\n input: GroupMetadataAccessInput,\n containerId: string,\n) {\n return withMetadataRootReload(\n () => loadMetadataKeyringOnce(input, containerId),\n () => input.apiClient.evictContainerWriterProjection(containerId),\n );\n}\n\nasync function loadMetadataKeyringOnce(\n input: GroupMetadataAccessInput,\n containerId: string,\n) {\n const projection =\n await input.apiClient.getContainerWriterProjection(containerId);\n if (!projection) return null;\n if (\n projection.containerId !== containerId ||\n projection.organizationId !== input.organizationId\n )\n throw new Error(\"Group metadata container is unavailable\");\n const target = getTargetContainerContext(projection);\n const state = readContainerState(target.manifest);\n const slot = await deriveOrganizationMetadataContainerSystemSlot({\n organizationId: input.organizationId,\n });\n if (\n state.organizationId !== input.organizationId ||\n state.systemSlot !== slot ||\n state.parentContainerId !== null\n )\n throw new Error(\n \"Group metadata requires the signed organization metadata container\",\n );\n await input.verifyMetadataContainer(\n state,\n projection.policyEvidence.organization?.head,\n );\n assertProjectionVerificationCurrent(input.stillCurrent);\n const keys = await unwrapContainerKekPath({\n ...input,\n projection,\n secretKey: input.targetSecretKey,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n const keyMaterial = keys.get(target.kek.containerKeyEpochId);\n if (!keyMaterial)\n throw new Error(\"Group metadata container key is unavailable\");\n return {\n current: {\n organizationId: input.organizationId,\n containerId,\n containerKeyEpochId: target.kek.containerKeyEpochId,\n keyMaterial,\n },\n keys,\n };\n}\n\n/** Uses the shared metadata container's authenticated keyring, including old epochs. */\nexport function createGroupMetadataAccess(input: GroupMetadataAccessInput) {\n const loads = new Map<\n string,\n Promise<{\n current: GroupMetadataKey;\n keys: ReadonlyMap<string, Uint8Array>;\n } | null>\n >();\n function loaded(containerId: string) {\n let pending = loads.get(containerId);\n if (!pending) {\n pending = loadMetadataKeyring(input, containerId);\n loads.set(containerId, pending);\n }\n return pending;\n }\n const readName: GroupPolicyNameReader = async (bundle) => {\n assertProjectionVerificationCurrent(input.stillCurrent);\n const metadata = readGroupMetadata(bundle.currentPayload.ciphertext);\n if (\"role\" in metadata)\n return metadata.role === \"admins\" ? \"Admins\" : \"Members\";\n if (\n metadata.organizationId !== input.organizationId ||\n metadata.groupId !== bundle.currentState.principalId ||\n bundle.currentState.principalType !== \"group\"\n )\n throw new Error(\"Group metadata scope does not match its signed group\");\n const keyring = await loaded(metadata.containerId);\n const keyMaterial = keyring?.keys.get(metadata.containerKeyEpochId);\n if (!keyMaterial)\n throw new Error(\"Group metadata historical key is unavailable\");\n try {\n return await decryptGroupMetadata({\n key: { ...metadata, keyMaterial },\n groupId: bundle.currentState.principalId,\n payload: bundle.currentPayload.ciphertext,\n });\n } catch (cause) {\n throw new GroupMetadataUnreadableError(\n bundle.currentState.principalId,\n cause,\n );\n }\n };\n return {\n readName,\n async loadEncryptionKey(): Promise<GroupMetadataKey> {\n const slot = await deriveOrganizationMetadataContainerSystemSlot({\n organizationId: input.organizationId,\n });\n const containers = await loadContainers(input.execSql);\n const container = containers.find(\n (row) =>\n row.organizationId === input.organizationId &&\n row.systemSlot === slot,\n );\n if (!container)\n throw new Error(\n \"Organization metadata container has not been discovered\",\n );\n const keyring = await loaded(container.id);\n if (!keyring) throw new Error(\"Group metadata container is unavailable\");\n return keyring.current;\n },\n };\n}\n"]}
|
|
@@ -3,8 +3,8 @@ import type { CreateOrganizationRequest } from "../../../internal/validators/req
|
|
|
3
3
|
import type { CreateOrganizationResponse } from "../../../internal/validators/response/index.js";
|
|
4
4
|
import type { DocumentProjectorRegistryInput } from "../../../data/documents/documentKinds.js";
|
|
5
5
|
import type { ExecSqlClientLike } from "../../../data/sqlite/sqlSchema.js";
|
|
6
|
+
import { type ProvisionedSystemContainerSpec } from "../../registration/organizationProvisioning.js";
|
|
6
7
|
import type { ProvisionedPrincipalHistoryPrimer } from "../../registration/provisionedPrincipalHistory.js";
|
|
7
|
-
import { type ProvisionedSystemContainerSpec } from "../../registration/registerIdentity.js";
|
|
8
8
|
import { removeNativeSubscriptionRestoreProvisioningAttempt } from "./organizationProvisioningAttempt.js";
|
|
9
9
|
export { removeNativeSubscriptionRestoreProvisioningAttempt };
|
|
10
10
|
export interface CreateOrganizationApi {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { signOrganizationReplacementAuthorization, } from "../../../internal/crypto/index.js";
|
|
2
2
|
import { createExecSql } from "../../../data/sqlite/sqlSchema.js";
|
|
3
|
-
import { buildOrganizationProvisioningArtifacts, persistOrganizationProvisioningState, } from "../../registration/
|
|
3
|
+
import { buildOrganizationProvisioningArtifacts, persistOrganizationProvisioningState, } from "../../registration/organizationProvisioning.js";
|
|
4
4
|
import { makeOrganizationProvisioningAttemptDurable, removeNativeSubscriptionRestoreProvisioningAttempt, } from "./organizationProvisioningAttempt.js";
|
|
5
5
|
import { pinOrganizationReplacementResponse } from "./organizationReplacementVerification.js";
|
|
6
6
|
export { removeNativeSubscriptionRestoreProvisioningAttempt };
|