@tearleads/client-sdk 0.1.82 → 0.1.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/documents/documentSync.js.map +1 -1
- package/data/documents/shared/crypto.js.map +1 -1
- package/data/documents/shared/types.d.ts +1 -1
- package/data/documents/shared/types.js.map +1 -1
- package/data/persistence/container-contents/containerContentsPersistence.js +2 -2
- package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerContentsPersistenceTypes.d.ts +1 -1
- package/data/persistence/container-contents/containerContentsPersistenceTypes.js.map +1 -1
- package/data/persistence/container-contents/containerDeletionPersistence.js +1 -1
- package/data/persistence/container-contents/containerDeletionPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js +3 -3
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMetadataPullContinuationPersistence.js +1 -1
- package/data/persistence/container-contents/containerMetadataPullContinuationPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMetadataRows.js +1 -1
- package/data/persistence/container-contents/containerMetadataRows.js.map +1 -1
- package/data/persistence/container-contents/containerPendingUpdatePersistence.js +2 -2
- package/data/persistence/container-contents/containerPendingUpdatePersistence.js.map +1 -1
- package/data/persistence/container-contents/containerReconcilePersistence.js +1 -1
- package/data/persistence/container-contents/containerReconcilePersistence.js.map +1 -1
- package/data/persistence/container-contents/containerRemovalWork.js +1 -1
- package/data/persistence/container-contents/containerRemovalWork.js.map +1 -1
- package/data/persistence/documents/documentDiscoveryReset.d.ts +6 -0
- package/data/persistence/documents/documentsPersistence.js +2 -2
- package/data/persistence/documents/documentsPersistence.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/discardDocument.js +2 -2
- package/data/persistence/documents/internal/discardDocument.js.map +1 -1
- package/data/persistence/documents/internal/documentDeletionPersistence.js +2 -2
- package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
- package/data/persistence/documents/internal/documentMutationPersistence.js +3 -3
- package/data/persistence/documents/internal/documentMutationPersistence.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/documentRecordIdentity.js.map +1 -1
- package/data/persistence/documents/internal/documentRows.js.map +1 -1
- package/data/persistence/documents/internal/ensureDocumentsSchema.js +1 -1
- package/data/persistence/documents/internal/ensureDocumentsSchema.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/rowQueryPersistence.js +1 -1
- package/data/persistence/documents/internal/rowQueryPersistence.js.map +1 -1
- package/data/persistence/documents/internal/syncQueuePersistence.js +2 -2
- package/data/persistence/documents/internal/syncQueuePersistence.js.map +1 -1
- package/data/{sqlite/documentPersistence → persistence/documents/records}/documentHistoryPersistence.d.ts +1 -1
- package/data/{sqlite/documentPersistence → persistence/documents/records}/documentHistoryPersistence.js +5 -5
- package/data/persistence/documents/records/documentHistoryPersistence.js.map +1 -0
- package/data/{sqlite/documentPersistence → persistence/documents/records}/documentPendingUpdatePersistence.d.ts +2 -2
- package/data/{sqlite/documentPersistence → persistence/documents/records}/documentPendingUpdatePersistence.js +2 -2
- package/data/persistence/documents/records/documentPendingUpdatePersistence.js.map +1 -0
- package/data/{sqlite/documentPersistence → persistence/documents/records}/documentPersistenceTypes.d.ts +2 -2
- package/data/persistence/documents/records/documentPersistenceTypes.js.map +1 -0
- package/data/{sqlite/documentPersistence → persistence/documents/records}/documentRecordPersistence.d.ts +1 -1
- package/data/{sqlite/documentPersistence → persistence/documents/records}/documentRecordPersistence.js +4 -4
- package/data/persistence/documents/records/documentRecordPersistence.js.map +1 -0
- package/data/{sqlite/documentPersistence → persistence/documents/records}/documentSyncFailurePersistence.d.ts +2 -2
- package/data/{sqlite/documentPersistence → persistence/documents/records}/documentSyncFailurePersistence.js +3 -3
- package/data/persistence/documents/records/documentSyncFailurePersistence.js.map +1 -0
- package/data/{sqlite/documentPersistence → persistence/documents/records}/index.d.ts +2 -2
- package/data/{sqlite/documentPersistence → persistence/documents/records}/index.js +4 -4
- package/data/persistence/documents/records/index.js.map +1 -0
- package/data/persistence/documents/types.d.ts +2 -2
- package/data/persistence/documents/types.js.map +1 -1
- package/{workflows/sync/remoteResetPrincipalScope.d.ts → data/persistence/principalPolicy/principalPolicyRemoteReset.d.ts} +1 -1
- package/{workflows/sync/remoteResetPrincipalScope.js → data/persistence/principalPolicy/principalPolicyRemoteReset.js} +7 -7
- package/data/persistence/principalPolicy/principalPolicyRemoteReset.js.map +1 -0
- package/data/persistence/verifiedManifestBundlePersistence.d.ts +2 -0
- package/data/persistence/verifiedManifestBundlePersistence.js +7 -0
- package/data/persistence/verifiedManifestBundlePersistence.js.map +1 -1
- package/data/sqlite/{documentPersistence/documentOrphanBlobReclaims.js → schema/documentOrphanBlobReclaimSchema.js} +1 -1
- package/data/sqlite/schema/documentOrphanBlobReclaimSchema.js.map +1 -0
- package/data/sqlite/schema/index.js +1 -1
- package/data/sqlite/schema/index.js.map +1 -1
- package/data/sqlite/sqliteBindBudget.js +12 -0
- package/data/sqlite/sqliteBindBudget.js.map +1 -1
- package/package.json +1 -1
- package/workflows/container-contents/container-state/create/createIntentSeed.js.map +1 -1
- package/workflows/container-contents/container-state/create/createWithMetadataPlan.js.map +1 -1
- package/workflows/container-contents/container-state/types.js.map +1 -1
- package/workflows/container-contents/metadata/index.js.map +1 -1
- package/workflows/container-contents/metadata/metadataEvents.d.ts +1 -1
- package/workflows/container-contents/metadata/metadataEvents.js.map +1 -1
- package/workflows/container-contents/metadata/metadataIncomingUpdateIsolation.js +1 -1
- package/workflows/container-contents/metadata/metadataIncomingUpdateIsolation.js.map +1 -1
- package/workflows/container-contents/metadata/metadataRemoteSync.d.ts +1 -1
- package/workflows/container-contents/metadata/metadataRemoteSync.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/recovery/accessRestoration.js +1 -1
- package/workflows/container-contents/recovery/accessRestoration.js.map +1 -1
- package/workflows/container-contents/remoteReset/containerRemoteReset.d.ts +185 -0
- package/workflows/container-contents/remoteReset/containerRemoteReset.js +174 -0
- package/workflows/container-contents/remoteReset/containerRemoteReset.js.map +1 -0
- package/workflows/container-contents/remoteReset/remoteResetCursorScope.d.ts +14 -0
- package/workflows/{sync → container-contents/remoteReset}/remoteResetCursorScope.js +2 -2
- package/workflows/container-contents/remoteReset/remoteResetCursorScope.js.map +1 -0
- package/workflows/documents/create/documentCreationPersistence.js.map +1 -1
- package/workflows/documents/create/index.d.ts +1 -1
- package/workflows/documents/create/index.js.map +1 -1
- package/workflows/documents/documentMutationCommit.d.ts +1 -1
- package/workflows/documents/documentMutationCommit.js.map +1 -1
- package/workflows/documents/index.d.ts +2 -2
- package/workflows/documents/index.js +2 -2
- package/workflows/documents/index.js.map +1 -1
- package/workflows/documents/remoteReset/documentRemoteReset.d.ts +56 -0
- package/workflows/documents/remoteReset/documentRemoteReset.js +135 -0
- package/workflows/documents/remoteReset/documentRemoteReset.js.map +1 -0
- package/workflows/{sync → documents/remoteReset}/remoteResetAttachments.js +3 -3
- package/workflows/documents/remoteReset/remoteResetAttachments.js.map +1 -0
- package/workflows/{sync → documents/remoteReset}/remoteResetPlans.d.ts +1 -1
- package/workflows/{sync → documents/remoteReset}/remoteResetPlans.js +7 -7
- package/workflows/documents/remoteReset/remoteResetPlans.js.map +1 -0
- package/workflows/documents/sync/index.js.map +1 -1
- package/workflows/documents/sync/initialSync.d.ts +1 -1
- package/workflows/documents/sync/initialSync.js.map +1 -1
- package/workflows/documents/sync/readOnlySync.d.ts +1 -1
- package/workflows/documents/sync/readOnlySync.js.map +1 -1
- package/workflows/documents/sync/syncAttemptPlanning.js.map +1 -1
- package/workflows/documents/sync/syncAttemptState.d.ts +1 -1
- package/workflows/documents/sync/syncAttemptState.js.map +1 -1
- package/workflows/documents/sync/syncResponseResult.js.map +1 -1
- package/workflows/documents/sync/syncSubmittedResult.js.map +1 -1
- package/workflows/documents/syncFailures/index.js.map +1 -1
- package/workflows/documents/syncPlan/syncContentKeys.d.ts +1 -1
- package/workflows/documents/syncPlan/syncContentKeys.js.map +1 -1
- package/workflows/documents/syncPlan/syncPlanMaterial.js.map +1 -1
- package/workflows/documents/syncPlan/syncPlanRequestBounds.js.map +1 -1
- package/workflows/documents/syncRekeys/syncContainerRekeys.js.map +1 -1
- package/workflows/documents/syncRekeys/syncRecoveryRekey.d.ts +1 -1
- package/workflows/documents/syncRekeys/syncRecoveryRekey.js +1 -1
- package/workflows/documents/syncRekeys/syncRecoveryRekey.js.map +1 -1
- package/workflows/organizations/readModel/listLocalOrganizations.js +1 -1
- package/workflows/organizations/readModel/listLocalOrganizations.js.map +1 -1
- package/workflows/organizations/readModel/organizationRemoteReset.js +25 -0
- package/workflows/organizations/readModel/organizationRemoteReset.js.map +1 -0
- package/workflows/registration/persistRegistrationBootstrap.d.ts +1 -1
- package/workflows/registration/persistRegistrationBootstrap.js.map +1 -1
- package/workflows/sync/remoteReset.js +60 -229
- package/workflows/sync/remoteReset.js.map +1 -1
- package/workflows/sync/remoteResetScope.d.ts +7 -10
- package/workflows/sync/remoteResetScope.js +26 -108
- package/workflows/sync/remoteResetScope.js.map +1 -1
- package/data/sqlite/documentPersistence/documentHistoryPersistence.js.map +0 -1
- package/data/sqlite/documentPersistence/documentOrphanBlobReclaims.js.map +0 -1
- package/data/sqlite/documentPersistence/documentPendingUpdatePersistence.js.map +0 -1
- package/data/sqlite/documentPersistence/documentPersistenceTypes.js.map +0 -1
- package/data/sqlite/documentPersistence/documentRecordPersistence.js.map +0 -1
- package/data/sqlite/documentPersistence/documentSyncFailurePersistence.js.map +0 -1
- package/data/sqlite/documentPersistence/index.js.map +0 -1
- package/workflows/sync/remoteResetAttachments.js.map +0 -1
- package/workflows/sync/remoteResetBatches.js +0 -13
- package/workflows/sync/remoteResetBatches.js.map +0 -1
- package/workflows/sync/remoteResetCursorScope.js.map +0 -1
- package/workflows/sync/remoteResetPlans.js.map +0 -1
- package/workflows/sync/remoteResetPrincipalScope.js.map +0 -1
- /package/data/{sqlite/documentPersistence → persistence/documents/records}/documentPersistenceTypes.js +0 -0
|
@@ -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 { 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
|
+
{"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/persistence/documents/records\";\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 +1 @@
|
|
|
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/
|
|
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/persistence/documents/records\";\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"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncContainerRekeys.js","sourceRoot":"","sources":["syncContainerRekeys.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport type { MaterializedContainerRekeyPlan } from \"../../../data/containers/shared/types\";\nimport { projectionVerificationOptions } from \"../../../data/documents/shared/types\";\nimport type { PendingUpdateRecord } from \"../../../data/
|
|
1
|
+
{"version":3,"file":"syncContainerRekeys.js","sourceRoot":"","sources":["syncContainerRekeys.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DocumentWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport type { MaterializedContainerRekeyPlan } from \"../../../data/containers/shared/types\";\nimport { projectionVerificationOptions } from \"../../../data/documents/shared/types\";\nimport type { PendingUpdateRecord } from \"../../../data/persistence/documents/records\";\nimport type { SyncRemoteDocumentInput } from \"../sync/readOnlySync\";\nimport { DocumentSyncPreparationRequiredError } from \"../syncFailures/syncRepairAbandon\";\nimport { buildMaterializedDocumentSyncPlan } from \"../syncPlan/syncPlanMaterial\";\nimport { buildAutomaticContainerRekeys } from \"./syncAutomaticContainerRekeys\";\nimport { requiresStandaloneAncestorRepairs } from \"./syncContainerRekeyPreparation\";\nimport { applyDocumentSyncContainerRekeys } from \"./syncContainerRekeyProjection\";\nimport { computeInlineRekeyCommitId } from \"./syncRekeyCommit\";\n\n/** Plan only: standalone server repairs belong to the outer sync attempt. */\nexport async function buildRemoteDocumentSyncPlan(input: {\n preparedRekeys?: readonly MaterializedContainerRekeyPlan[] | undefined;\n minLsn?: string | undefined;\n pendingUpdates: readonly PendingUpdateRecord[];\n pullCursor?: string | undefined;\n projection: DocumentWriterProjectionResponse;\n regenerateQueuedCheckpoints: boolean;\n sync: SyncRemoteDocumentInput;\n}) {\n // Repairing this document's own container moves its key target hash, which\n // marks the content-key bundle stale. Only a pass that can build a rotation\n // snapshot is able to heal that; rotation settlement deliberately cannot\n // (`syncRequest.ts` omits the builder for `allowRecoveryBaseline: false`), so\n // repairing there would trade a classified \"ancestor repair required\" failure\n // for an unhealable bundle — after a standalone prefix may already have been\n // committed. Such a pass is left to fail as it did before automatic repair.\n const canHealStaleBundle = input.sync.buildRotationSnapshot !== undefined;\n let automaticPlans = input.preparedRekeys;\n if (\n input.pendingUpdates.length &&\n !input.sync.buildContainerRekeys &&\n canHealStaleBundle &&\n automaticPlans === undefined\n ) {\n const batch = await buildAutomaticContainerRekeys(\n input.sync,\n input.projection,\n );\n if (\n batch.hasMore ||\n (batch.plans.length > 0 && requiresStandaloneAncestorRepairs(input.sync))\n ) {\n throw new DocumentSyncPreparationRequiredError();\n }\n automaticPlans = batch.plans;\n }\n const rekeyPlans =\n input.pendingUpdates.length && input.sync.buildContainerRekeys\n ? await input.sync.buildContainerRekeys(input.projection, {\n persistVerificationCheckpoints: false,\n })\n : automaticPlans;\n const writerProjection = rekeyPlans?.length\n ? await applyDocumentSyncContainerRekeys({\n plans: rekeyPlans,\n writerProjection: input.projection,\n })\n : input.projection;\n let inlineRekeyCommitId: string | undefined;\n if (rekeyPlans?.length) {\n const headPendingUpdate = input.pendingUpdates[0];\n if (!headPendingUpdate) {\n throw new Error(\"Document inline rekey pending queue is empty\");\n }\n inlineRekeyCommitId = await computeInlineRekeyCommitId({\n headPendingUpdateId: headPendingUpdate.id,\n projection: input.projection,\n });\n }\n return buildMaterializedDocumentSyncPlan({\n author: input.sync.author,\n buildRotationSnapshot: input.sync.buildRotationSnapshot,\n containerRekeys: rekeyPlans?.map(({ plan }) => plan.request),\n execSql: input.sync.execSql,\n historyMode: input.sync.historyMode,\n inlineRekeyCommitId,\n localVersionVector: input.sync.localVersionVector,\n minLsn: input.minLsn,\n onSyncTrace: input.sync.onSyncTrace,\n pendingUpdates: input.pendingUpdates,\n persistVerificationCheckpoints: rekeyPlans?.length ? false : undefined,\n pullCursor: input.pullCursor,\n regenerateQueuedCheckpoints: input.regenerateQueuedCheckpoints,\n signedAt: input.sync.signedAt,\n targetSecretKey: input.sync.targetSecretKey,\n writerProjection,\n ...projectionVerificationOptions(input.sync),\n });\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DocumentSyncResponse } from "../../../internal/validators/response/index.js";
|
|
2
2
|
import type { DocumentSyncSubmitFailure } from "../../../data/documents/shared/types.js";
|
|
3
|
-
import { type PendingUpdateRecord } from "../../../data/
|
|
3
|
+
import { type PendingUpdateRecord } from "../../../data/persistence/documents/records/index.js";
|
|
4
4
|
import { type ExecSql } from "../../../data/sqlite/sqlSchema.js";
|
|
5
5
|
export type RekeyPendingUpdate = (execSql: ExecSql, id: string) => Promise<string | null>;
|
|
6
6
|
interface DecryptedSyncUpdate {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { base64ToBytes } from "../../../internal/encoding/index.js";
|
|
2
|
-
import { MAX_PENDING_UPDATE_REKEYS, rekeyDocumentPendingUpdate, } from "../../../data/
|
|
2
|
+
import { MAX_PENDING_UPDATE_REKEYS, rekeyDocumentPendingUpdate, } from "../../../data/persistence/documents/records/index.js";
|
|
3
3
|
import { getClientSQLitePersistenceRuntime } from "../../../data/sqlite/sqlitePersistenceRuntime.js";
|
|
4
4
|
import { runSerializedSqlMutation, } from "../../../data/sqlite/sqlSchema.js";
|
|
5
5
|
function bytesEqual(left, right) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncRecoveryRekey.js","sourceRoot":"","sources":["syncRecoveryRekey.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes } from \"@tearleads/encoding\";\nimport type { DocumentSyncResponse } from \"@tearleads/validators/response\";\nimport type { DocumentSyncSubmitFailure } from \"../../../data/documents/shared/types\";\nimport {\n MAX_PENDING_UPDATE_REKEYS,\n type PendingUpdateRecord,\n rekeyDocumentPendingUpdate,\n} from \"../../../data/sqlite/documentPersistence\";\nimport { getClientSQLitePersistenceRuntime } from \"../../../data/sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n runSerializedSqlMutation,\n} from \"../../../data/sqlite/sqlSchema\";\n\nexport type RekeyPendingUpdate = (\n execSql: ExecSql,\n id: string,\n) => Promise<string | null>;\n\ninterface DecryptedSyncUpdate {\n id: string;\n partialEndVersionVector: string;\n partialStartVersionVector: string;\n updateData: Uint8Array;\n}\n\nfunction bytesEqual(left: Uint8Array, right: Uint8Array): boolean {\n return (\n left.byteLength === right.byteLength &&\n left.every((value, index) => value === right[index])\n );\n}\n\nfunction pendingUpdateMatchesDecryptedUpdate(\n pendingUpdate: PendingUpdateRecord,\n decryptedUpdate: Omit<DecryptedSyncUpdate, \"id\">,\n): boolean {\n if (\n pendingUpdate.partialStartVersionVector !==\n decryptedUpdate.partialStartVersionVector ||\n pendingUpdate.partialEndVersionVector !==\n decryptedUpdate.partialEndVersionVector\n ) {\n return false;\n }\n\n return bytesEqual(\n base64ToBytes(pendingUpdate.updateData),\n decryptedUpdate.updateData,\n );\n}\n\nexport function settledPendingUpdateIdsFromSync(input: {\n decryptedUpdates: readonly DecryptedSyncUpdate[];\n recoveryPendingUpdatesById: ReadonlyMap<string, PendingUpdateRecord>;\n response: DocumentSyncResponse;\n}): string[] {\n const settled = new Set(input.response.acceptedOutgoingUpdateIds);\n\n for (const update of input.decryptedUpdates) {\n const pendingUpdate = input.recoveryPendingUpdatesById.get(update.id);\n if (\n pendingUpdate &&\n pendingUpdateMatchesDecryptedUpdate(pendingUpdate, update)\n ) {\n settled.add(update.id);\n }\n }\n\n return [...settled];\n}\n\n/**\n * A read-only recovery sync settles a conflicted pending update only when the\n * response carries it — but the server's frontier filter omits spans the\n * request's localVersionVector already covers, which a locally-applied\n * pending update always is. Left alone, the conflicted id is resubmitted (and\n * 409s the whole outgoing batch) forever: a lost ack leaves the server\n * holding the original ciphertext, and every rebuild encrypts with a fresh\n * IV, so a byte-identical retry can never be reproduced. Re-key what\n * recovery could not settle so the next sync pass submits the same ops under\n * a conflict-free id; the server-side copy stays harmless because CRDT\n * update import is idempotent.\n *\n * Re-keying goes through the caller's persistence implementation when given\n * (defaulting to the built-in pending-update table) and reports only ids\n * whose row actually changed, so a record living in some other queue never\n * counts as progress. Returns the fresh ids so callers can treat re-keying\n * as settlement-like progress and schedule the pass that submits them.\n *\n * A row whose persisted rekeyCount has reached the bound is not re-keyed\n * again: it is reported as exhausted so the caller can surface a terminal\n * failure instead of feeding an unbounded conflict loop that survives\n * restarts (each session's in-memory lane cap resets, this bound does not).\n */\nexport async function rekeyUnsettledRecoveryPendingUpdates(input: {\n execSql: ExecSql;\n recoveryPendingUpdatesById: ReadonlyMap<string, PendingUpdateRecord>;\n rekeyPendingUpdate?: RekeyPendingUpdate | undefined;\n settledPendingUpdateIds: readonly string[];\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<{\n exhaustedPendingUpdateIds: string[];\n rekeyedPendingUpdateIds: string[];\n}> {\n const rekeyPendingUpdate =\n input.rekeyPendingUpdate ?? rekeyDocumentPendingUpdate;\n const settled = new Set(input.settledPendingUpdateIds);\n const rekeyedPendingUpdateIds: string[] = [];\n const exhaustedPendingUpdateIds: string[] = [];\n for (const [pendingUpdateId, record] of input.recoveryPendingUpdatesById) {\n if (input.stillCurrent?.() === false) break;\n if (settled.has(pendingUpdateId)) {\n continue;\n }\n // Known multi-tab limitation: the count is the row as loaded at pass\n // start, so a sibling tab that settle-deleted the row can make this\n // record a failure for work that no longer exists. The stale failure is\n // transient — the document's next clean pass clears it.\n if ((record.rekeyCount ?? 0) >= MAX_PENDING_UPDATE_REKEYS) {\n exhaustedPendingUpdateIds.push(pendingUpdateId);\n continue;\n }\n const outcome = await runSerializedSqlMutation(\n input.execSql,\n (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).guardedTransaction(\n async () =>\n input.stillCurrent?.() === false\n ? null\n : rekeyPendingUpdate(lockedExecSql, pendingUpdateId),\n () => input.stillCurrent?.() !== false,\n ),\n );\n if (!outcome.committed) break;\n const nextId = outcome.result;\n if (nextId !== null) {\n rekeyedPendingUpdateIds.push(nextId);\n }\n }\n if (input.stillCurrent?.() === false) {\n return { exhaustedPendingUpdateIds: [], rekeyedPendingUpdateIds: [] };\n }\n return { exhaustedPendingUpdateIds, rekeyedPendingUpdateIds };\n}\n\n/**\n * Re-key what recovery could not settle and surface exhausted rows through\n * the caller's terminal-failure handler. The exhausted count is also\n * returned so the sync result can carry it: a pass that recorded this\n * failure must not read as clean to callers that clear recorded failures\n * on success.\n */\nexport async function rekeyAndReportUnsettledRecoveryPendingUpdates(\n input: Parameters<typeof rekeyUnsettledRecoveryPendingUpdates>[0] & {\n onTerminalSubmitFailure?:\n | ((failure: DocumentSyncSubmitFailure) => Promise<void> | void)\n | undefined;\n },\n): Promise<{\n exhaustedPendingUpdateCount: number;\n rekeyedPendingUpdateIds: string[];\n}> {\n const { exhaustedPendingUpdateIds, rekeyedPendingUpdateIds } =\n await rekeyUnsettledRecoveryPendingUpdates(input);\n if (input.stillCurrent?.() === false) {\n return { exhaustedPendingUpdateCount: 0, rekeyedPendingUpdateIds: [] };\n }\n if (exhaustedPendingUpdateIds.length > 0) {\n await input.onTerminalSubmitFailure?.(\n rekeyLimitSubmitFailure(exhaustedPendingUpdateIds.length),\n );\n }\n return {\n exhaustedPendingUpdateCount: exhaustedPendingUpdateIds.length,\n rekeyedPendingUpdateIds,\n };\n}\n\n/**\n * Terminal failure recorded when conflict recovery gives up on pending\n * updates whose persisted re-key bound is exhausted. Shaped like a submit\n * failure so the existing write-queue surface (recordDocumentSyncFailure via\n * onTerminalSubmitFailure) explains the stuck write without new plumbing.\n */\nexport function rekeyLimitSubmitFailure(\n exhaustedCount: number,\n): DocumentSyncSubmitFailure {\n return {\n message: `Update conflict recovery gave up after ${MAX_PENDING_UPDATE_REKEYS} re-key attempts (${exhaustedCount} update${exhaustedCount === 1 ? \"\" : \"s\"})`,\n ok: false,\n report: () => {},\n status: null,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"syncRecoveryRekey.js","sourceRoot":"","sources":["syncRecoveryRekey.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes } from \"@tearleads/encoding\";\nimport type { DocumentSyncResponse } from \"@tearleads/validators/response\";\nimport type { DocumentSyncSubmitFailure } from \"../../../data/documents/shared/types\";\nimport {\n MAX_PENDING_UPDATE_REKEYS,\n type PendingUpdateRecord,\n rekeyDocumentPendingUpdate,\n} from \"../../../data/persistence/documents/records\";\nimport { getClientSQLitePersistenceRuntime } from \"../../../data/sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n runSerializedSqlMutation,\n} from \"../../../data/sqlite/sqlSchema\";\n\nexport type RekeyPendingUpdate = (\n execSql: ExecSql,\n id: string,\n) => Promise<string | null>;\n\ninterface DecryptedSyncUpdate {\n id: string;\n partialEndVersionVector: string;\n partialStartVersionVector: string;\n updateData: Uint8Array;\n}\n\nfunction bytesEqual(left: Uint8Array, right: Uint8Array): boolean {\n return (\n left.byteLength === right.byteLength &&\n left.every((value, index) => value === right[index])\n );\n}\n\nfunction pendingUpdateMatchesDecryptedUpdate(\n pendingUpdate: PendingUpdateRecord,\n decryptedUpdate: Omit<DecryptedSyncUpdate, \"id\">,\n): boolean {\n if (\n pendingUpdate.partialStartVersionVector !==\n decryptedUpdate.partialStartVersionVector ||\n pendingUpdate.partialEndVersionVector !==\n decryptedUpdate.partialEndVersionVector\n ) {\n return false;\n }\n\n return bytesEqual(\n base64ToBytes(pendingUpdate.updateData),\n decryptedUpdate.updateData,\n );\n}\n\nexport function settledPendingUpdateIdsFromSync(input: {\n decryptedUpdates: readonly DecryptedSyncUpdate[];\n recoveryPendingUpdatesById: ReadonlyMap<string, PendingUpdateRecord>;\n response: DocumentSyncResponse;\n}): string[] {\n const settled = new Set(input.response.acceptedOutgoingUpdateIds);\n\n for (const update of input.decryptedUpdates) {\n const pendingUpdate = input.recoveryPendingUpdatesById.get(update.id);\n if (\n pendingUpdate &&\n pendingUpdateMatchesDecryptedUpdate(pendingUpdate, update)\n ) {\n settled.add(update.id);\n }\n }\n\n return [...settled];\n}\n\n/**\n * A read-only recovery sync settles a conflicted pending update only when the\n * response carries it — but the server's frontier filter omits spans the\n * request's localVersionVector already covers, which a locally-applied\n * pending update always is. Left alone, the conflicted id is resubmitted (and\n * 409s the whole outgoing batch) forever: a lost ack leaves the server\n * holding the original ciphertext, and every rebuild encrypts with a fresh\n * IV, so a byte-identical retry can never be reproduced. Re-key what\n * recovery could not settle so the next sync pass submits the same ops under\n * a conflict-free id; the server-side copy stays harmless because CRDT\n * update import is idempotent.\n *\n * Re-keying goes through the caller's persistence implementation when given\n * (defaulting to the built-in pending-update table) and reports only ids\n * whose row actually changed, so a record living in some other queue never\n * counts as progress. Returns the fresh ids so callers can treat re-keying\n * as settlement-like progress and schedule the pass that submits them.\n *\n * A row whose persisted rekeyCount has reached the bound is not re-keyed\n * again: it is reported as exhausted so the caller can surface a terminal\n * failure instead of feeding an unbounded conflict loop that survives\n * restarts (each session's in-memory lane cap resets, this bound does not).\n */\nexport async function rekeyUnsettledRecoveryPendingUpdates(input: {\n execSql: ExecSql;\n recoveryPendingUpdatesById: ReadonlyMap<string, PendingUpdateRecord>;\n rekeyPendingUpdate?: RekeyPendingUpdate | undefined;\n settledPendingUpdateIds: readonly string[];\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<{\n exhaustedPendingUpdateIds: string[];\n rekeyedPendingUpdateIds: string[];\n}> {\n const rekeyPendingUpdate =\n input.rekeyPendingUpdate ?? rekeyDocumentPendingUpdate;\n const settled = new Set(input.settledPendingUpdateIds);\n const rekeyedPendingUpdateIds: string[] = [];\n const exhaustedPendingUpdateIds: string[] = [];\n for (const [pendingUpdateId, record] of input.recoveryPendingUpdatesById) {\n if (input.stillCurrent?.() === false) break;\n if (settled.has(pendingUpdateId)) {\n continue;\n }\n // Known multi-tab limitation: the count is the row as loaded at pass\n // start, so a sibling tab that settle-deleted the row can make this\n // record a failure for work that no longer exists. The stale failure is\n // transient — the document's next clean pass clears it.\n if ((record.rekeyCount ?? 0) >= MAX_PENDING_UPDATE_REKEYS) {\n exhaustedPendingUpdateIds.push(pendingUpdateId);\n continue;\n }\n const outcome = await runSerializedSqlMutation(\n input.execSql,\n (lockedExecSql) =>\n getClientSQLitePersistenceRuntime(lockedExecSql).guardedTransaction(\n async () =>\n input.stillCurrent?.() === false\n ? null\n : rekeyPendingUpdate(lockedExecSql, pendingUpdateId),\n () => input.stillCurrent?.() !== false,\n ),\n );\n if (!outcome.committed) break;\n const nextId = outcome.result;\n if (nextId !== null) {\n rekeyedPendingUpdateIds.push(nextId);\n }\n }\n if (input.stillCurrent?.() === false) {\n return { exhaustedPendingUpdateIds: [], rekeyedPendingUpdateIds: [] };\n }\n return { exhaustedPendingUpdateIds, rekeyedPendingUpdateIds };\n}\n\n/**\n * Re-key what recovery could not settle and surface exhausted rows through\n * the caller's terminal-failure handler. The exhausted count is also\n * returned so the sync result can carry it: a pass that recorded this\n * failure must not read as clean to callers that clear recorded failures\n * on success.\n */\nexport async function rekeyAndReportUnsettledRecoveryPendingUpdates(\n input: Parameters<typeof rekeyUnsettledRecoveryPendingUpdates>[0] & {\n onTerminalSubmitFailure?:\n | ((failure: DocumentSyncSubmitFailure) => Promise<void> | void)\n | undefined;\n },\n): Promise<{\n exhaustedPendingUpdateCount: number;\n rekeyedPendingUpdateIds: string[];\n}> {\n const { exhaustedPendingUpdateIds, rekeyedPendingUpdateIds } =\n await rekeyUnsettledRecoveryPendingUpdates(input);\n if (input.stillCurrent?.() === false) {\n return { exhaustedPendingUpdateCount: 0, rekeyedPendingUpdateIds: [] };\n }\n if (exhaustedPendingUpdateIds.length > 0) {\n await input.onTerminalSubmitFailure?.(\n rekeyLimitSubmitFailure(exhaustedPendingUpdateIds.length),\n );\n }\n return {\n exhaustedPendingUpdateCount: exhaustedPendingUpdateIds.length,\n rekeyedPendingUpdateIds,\n };\n}\n\n/**\n * Terminal failure recorded when conflict recovery gives up on pending\n * updates whose persisted re-key bound is exhausted. Shaped like a submit\n * failure so the existing write-queue surface (recordDocumentSyncFailure via\n * onTerminalSubmitFailure) explains the stuck write without new plumbing.\n */\nexport function rekeyLimitSubmitFailure(\n exhaustedCount: number,\n): DocumentSyncSubmitFailure {\n return {\n message: `Update conflict recovery gave up after ${MAX_PENDING_UPDATE_REKEYS} re-key attempts (${exhaustedCount} update${exhaustedCount === 1 ? \"\" : \"s\"})`,\n ok: false,\n report: () => {},\n status: null,\n };\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readStoredDocumentState } from "../../../data/documents/documentKinds.js";
|
|
2
2
|
import { sqlContainerContentsPersistence } from "../../../data/persistence/container-contents/containerContentsPersistence.js";
|
|
3
3
|
import { DOCUMENTS_APP_KIND, sqlDocumentsPersistence, } from "../../../data/persistence/documents/documentsPersistence.js";
|
|
4
|
+
import { findLocalIdByDocumentId } from "../../../data/persistence/documents/records/index.js";
|
|
4
5
|
import { loadOrganizationProfileDocumentIds } from "../../../data/persistence/organizations/organizationProfileDocumentPointers.js";
|
|
5
|
-
import { findLocalIdByDocumentId } from "../../../data/sqlite/documentPersistence/index.js";
|
|
6
6
|
import { loadPersistedDocumentContent } from "../../documents/index.js";
|
|
7
7
|
import { getOrganizationProfileDocumentLocalId, readOrganizationProfileName, } from "../organizationProfile.js";
|
|
8
8
|
import { deriveOrganizationMetadataContainerSystemSlot } from "../rosterProfileContainer.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listLocalOrganizations.js","sourceRoot":"","sources":["listLocalOrganizations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,+BAA+B,EAAE,MAAM,2EAA2E,CAAC;AAC5H,OAAO,EACL,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,0DAA0D,CAAC;AAClE,OAAO,EAAE,kCAAkC,EAAE,MAAM,6EAA6E,CAAC;AACjI,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AAEnF,OAAO,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EACL,qCAAqC,EACrC,2BAA2B,GAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,6CAA6C,EAAE,MAAM,2BAA2B,CAAC;AAoB1F,2EAA2E;AAC3E,wEAAwE;AACxE,gFAAgF;AAChF,sDAAsD;AACtD,KAAK,UAAU,gCAAgC,CAC7C,OAAgB,EAChB,eAAuB;IAEvB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,4BAA4B,CAAC;YAC7C,OAAO;YACP,OAAO,EAAE,eAAe;YACxB,WAAW,EAAE,uBAAuB;SACrC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,2BAA2B,CAChC,uBAAuB,CAAC,GAAG,CAAC,CAAC,gBAAgB,CAC9C,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,KAAK,UAAU,yBAAyB,CACtC,OAAgB,EAChB,cAAsB,EACtB,sBAAkE,EAClE,iBAAgC;IAEhC,MAAM,SAAS,GAAG,MAAM,gCAAgC,CACtD,OAAO,EACP,qCAAqC,CAAC,EAAE,cAAc,EAAE,CAAC,CAC1D,CAAC;IACF,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,iBAAiB,EAAE,CAAC;QACtB,2EAA2E;QAC3E,mEAAmE;QACnE,MAAM,aAAa,GAAG,MAAM,uBAAuB,CACjD,OAAO,EACP,kBAAkB,EAClB,iBAAiB,CAClB,CAAC;QACF,MAAM,UAAU,GAAG,aAAa;YAC9B,CAAC,CAAC,MAAM,gCAAgC,CAAC,OAAO,EAAE,aAAa,CAAC;YAChE,CAAC,CAAC,IAAI,CAAC;QACT,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;IAED,MAAM,kBAAkB,GACtB,MAAM,6CAA6C,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;IAC1E,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,IAAI,CACnD,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,KAAK,kBAAkB,CAC3D,CAAC;IACF,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,gBAAgB,GACpB,MAAM,uBAAuB,CAAC,iCAAiC,CAC7D,OAAO,EACP,iBAAiB,CAAC,EAAE,CACrB,CAAC;IACJ,KAAK,MAAM,eAAe,IAAI,gBAAgB,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,MAAM,gCAAgC,CACjD,OAAO,EACP,eAAe,CAChB,CAAC;QACF,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,KAE5C;IACC,MAAM,UAAU,GAAG,MAAM,+BAA+B,CAAC,cAAc,CACrE,KAAK,CAAC,OAAO,CACd,CAAC;IACF,6EAA6E;IAC7E,qEAAqE;IACrE,MAAM,kCAAkC,GACtC,MAAM,kCAAkC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAC3D,GAAG,EAAE,CAAC,IAAI,GAAG,EAAkB,CAChC,CAAC;IAEJ,0EAA0E;IAC1E,4DAA4D;IAC5D,MAAM,0BAA0B,GAAG,IAAI,GAAG,EAGvC,CAAC;IACJ,KAAK,MAAM,EAAE,SAAS,EAAE,IAAI,UAAU,EAAE,CAAC;QACvC,MAAM,KAAK,GAAgC;YACzC,EAAE,EAAE,SAAS,CAAC,EAAE;YAChB,UAAU,EAAE,SAAS,CAAC,UAAU,IAAI,IAAI;SACzC,CAAC;QACF,MAAM,KAAK,GAAG,0BAA0B,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACvE,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,0BAA0B,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;QACzD,IACE,SAAS,CAAC,QAAQ,KAAK,IAAI;YAC3B,SAAS,CAAC,UAAU;YACpB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,EAClC,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IACH,cAAc,CAAC,IAAI,CACjB,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACd,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,aAAa,CACjD,KAAK,CAAC,SAAS,CAAC,cAAc,IAAI,EAAE,CACrC;QACD,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,aAAa,CACzC,KAAK,CAAC,SAAS,CAAC,cAAc,CAC/B;QACD,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CACtD,CAAC;IAEF,gFAAgF;IAChF,2EAA2E;IAC3E,2BAA2B;IAC3B,OAAO,OAAO,CAAC,GAAG,CAChB,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3C,IAAI,EAAE,MAAM,yBAAyB,CACnC,KAAK,CAAC,OAAO,EACb,SAAS,CAAC,cAAc,EACxB,0BAA0B,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,EAC9D,kCAAkC,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC;YAC9D,IAAI,CACP;QACD,cAAc,EAAE,SAAS,CAAC,cAAc;QACxC,eAAe,EAAE,SAAS,CAAC,EAAE;KAC9B,CAAC,CAAC,CACJ,CAAC;AACJ,CAAC","sourcesContent":["import { readStoredDocumentState } from \"../../../data/documents/documentKinds\";\nimport { sqlContainerContentsPersistence } from \"../../../data/persistence/container-contents/containerContentsPersistence\";\nimport {\n DOCUMENTS_APP_KIND,\n sqlDocumentsPersistence,\n} from \"../../../data/persistence/documents/documentsPersistence\";\nimport { loadOrganizationProfileDocumentIds } from \"../../../data/persistence/organizations/organizationProfileDocumentPointers\";\nimport { findLocalIdByDocumentId } from \"../../../data/sqlite/documentPersistence\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { loadPersistedDocumentContent } from \"../../documents\";\nimport {\n getOrganizationProfileDocumentLocalId,\n readOrganizationProfileName,\n} from \"../organizationProfile\";\nimport { deriveOrganizationMetadataContainerSystemSlot } from \"../rosterProfileContainer\";\n\n// Just the container fields this module needs to locate an org's metadata\n// container among the locally-persisted containers.\ninterface LocalContainerForNameLookup {\n id: string;\n systemSlot: string | null;\n}\n\n/**\n * A locally-known organization the user belongs to, derived from its persisted\n * root container. `name` is the decrypted organization-profile display name when\n * one is stored locally, or null when it has not synced yet.\n */\nexport interface LocalOrganizationSummary {\n name: string | null;\n organizationId: string;\n rootContainerId: string;\n}\n\n// Reads and decrypts the `name` field of the organization_profile document\n// persisted under `documentLocalId`, or null if it is absent, empty, or\n// unparseable. A single corrupt profile must not break the whole list, so every\n// failure collapses to a null (unnamed) organization.\nasync function readOrganizationNameFromDocument(\n execSql: ExecSql,\n documentLocalId: string,\n): Promise<string | null> {\n try {\n const doc = await loadPersistedDocumentContent({\n execSql,\n localId: documentLocalId,\n persistence: sqlDocumentsPersistence,\n });\n if (!doc) {\n return null;\n }\n\n return readOrganizationProfileName(\n readStoredDocumentState(doc).structuredFields,\n );\n } catch {\n return null;\n }\n}\n\n/**\n * Resolves an organization's decrypted display name from local state.\n *\n * The same organization_profile document is addressed by several different\n * local-id conventions depending on how it arrived, so one lookup by id is not\n * enough:\n *\n * - The org's own **provisioner** (and any admin who later edits the name)\n * writes the document under the deterministic alias\n * `org-profile:<organizationId>` — resolved by the fast path below.\n * - A **device that only synced it** — a member of another org, or this same\n * user's freshly re-hydrated device after an identity restore — never runs\n * that write. The sync ingest keys the document under the server documentId\n * (there is no local alias to reuse — see `upsertDiscoveredDocumentWithExec`),\n * so the alias lookup misses.\n *\n * Two fallbacks cover the synced case, in order of authority:\n *\n * 1. The **organization read model** records the org's `profileDocumentId`.\n * That pointer is container-independent, so it resolves the name wherever\n * the document happens to be linked — including a profile document created\n * by an admin through the org-manager editor rather than by provisioning.\n * 2. Failing that, locate the org's metadata container by its deterministic\n * system slot and read whichever profile document is linked there. Keying on\n * the system slot (rather than the document kind) keeps it correct even\n * before the projected document kind has been rebuilt from a freshly-synced\n * snapshot, and it still works before the read model has been projected.\n */\nasync function readLocalOrganizationName(\n execSql: ExecSql,\n organizationId: string,\n organizationContainers: ReadonlyArray<LocalContainerForNameLookup>,\n profileDocumentId: string | null,\n): Promise<string | null> {\n const aliasName = await readOrganizationNameFromDocument(\n execSql,\n getOrganizationProfileDocumentLocalId({ organizationId }),\n );\n if (aliasName !== null) {\n return aliasName;\n }\n\n if (profileDocumentId) {\n // Reach the row wherever it landed: a device that only synced the document\n // keys it under the server documentId rather than any local alias.\n const syncedLocalId = await findLocalIdByDocumentId(\n execSql,\n DOCUMENTS_APP_KIND,\n profileDocumentId,\n );\n const syncedName = syncedLocalId\n ? await readOrganizationNameFromDocument(execSql, syncedLocalId)\n : null;\n if (syncedName !== null) {\n return syncedName;\n }\n }\n\n const metadataSystemSlot =\n await deriveOrganizationMetadataContainerSystemSlot({ organizationId });\n const metadataContainer = organizationContainers.find(\n (container) => container.systemSlot === metadataSystemSlot,\n );\n if (!metadataContainer) {\n return null;\n }\n\n const documentLocalIds =\n await sqlDocumentsPersistence.findDocumentLocalIdsByContainerId(\n execSql,\n metadataContainer.id,\n );\n for (const documentLocalId of documentLocalIds) {\n const name = await readOrganizationNameFromDocument(\n execSql,\n documentLocalId,\n );\n if (name !== null) {\n return name;\n }\n }\n return null;\n}\n\n/**\n * Enumerates the organizations the user can manage from local state: one per\n * persisted root container. This is intentionally offline — the Explorer already\n * resolves files across organizations by effective permission, so listing the\n * organizations to manage needs no server round-trip.\n */\nexport async function listLocalOrganizations(input: {\n execSql: ExecSql;\n}): Promise<LocalOrganizationSummary[]> {\n const containers = await sqlContainerContentsPersistence.loadContainers(\n input.execSql,\n );\n // A projected read model is not a precondition for listing organizations, so\n // an unreadable one degrades to the container-derived lookups below.\n const profileDocumentIdsByOrganizationId =\n await loadOrganizationProfileDocumentIds(input.execSql).catch(\n () => new Map<string, string>(),\n );\n\n // Group every persisted container by org so name resolution can locate an\n // org's independent metadata root without a second DB scan.\n const containersByOrganizationId = new Map<\n string,\n LocalContainerForNameLookup[]\n >();\n for (const { container } of containers) {\n const entry: LocalContainerForNameLookup = {\n id: container.id,\n systemSlot: container.systemSlot ?? null,\n };\n const group = containersByOrganizationId.get(container.organizationId);\n if (group) {\n group.push(entry);\n } else {\n containersByOrganizationId.set(container.organizationId, [entry]);\n }\n }\n\n const seen = new Set<string>();\n const rootContainers = containers.filter(({ container }) => {\n if (\n container.parentId !== null ||\n container.systemSlot ||\n seen.has(container.organizationId)\n ) {\n return false;\n }\n seen.add(container.organizationId);\n return true;\n });\n rootContainers.sort(\n (left, right) =>\n (left.container.localCreatedAt ?? \"\").localeCompare(\n right.container.localCreatedAt ?? \"\",\n ) ||\n left.container.organizationId.localeCompare(\n right.container.organizationId,\n ) ||\n left.container.id.localeCompare(right.container.id),\n );\n\n // Read each organization's profile name concurrently; readLocalOrganizationName\n // already falls back to null per organization, so a single corrupt profile\n // cannot reject the batch.\n return Promise.all(\n rootContainers.map(async ({ container }) => ({\n name: await readLocalOrganizationName(\n input.execSql,\n container.organizationId,\n containersByOrganizationId.get(container.organizationId) ?? [],\n profileDocumentIdsByOrganizationId.get(container.organizationId) ??\n null,\n ),\n organizationId: container.organizationId,\n rootContainerId: container.id,\n })),\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"listLocalOrganizations.js","sourceRoot":"","sources":["listLocalOrganizations.ts"],"names":[],"mappings":"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","sourcesContent":["import { readStoredDocumentState } from \"../../../data/documents/documentKinds\";\nimport { sqlContainerContentsPersistence } from \"../../../data/persistence/container-contents/containerContentsPersistence\";\nimport {\n DOCUMENTS_APP_KIND,\n sqlDocumentsPersistence,\n} from \"../../../data/persistence/documents/documentsPersistence\";\nimport { findLocalIdByDocumentId } from \"../../../data/persistence/documents/records\";\nimport { loadOrganizationProfileDocumentIds } from \"../../../data/persistence/organizations/organizationProfileDocumentPointers\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { loadPersistedDocumentContent } from \"../../documents\";\nimport {\n getOrganizationProfileDocumentLocalId,\n readOrganizationProfileName,\n} from \"../organizationProfile\";\nimport { deriveOrganizationMetadataContainerSystemSlot } from \"../rosterProfileContainer\";\n\n// Just the container fields this module needs to locate an org's metadata\n// container among the locally-persisted containers.\ninterface LocalContainerForNameLookup {\n id: string;\n systemSlot: string | null;\n}\n\n/**\n * A locally-known organization the user belongs to, derived from its persisted\n * root container. `name` is the decrypted organization-profile display name when\n * one is stored locally, or null when it has not synced yet.\n */\nexport interface LocalOrganizationSummary {\n name: string | null;\n organizationId: string;\n rootContainerId: string;\n}\n\n// Reads and decrypts the `name` field of the organization_profile document\n// persisted under `documentLocalId`, or null if it is absent, empty, or\n// unparseable. A single corrupt profile must not break the whole list, so every\n// failure collapses to a null (unnamed) organization.\nasync function readOrganizationNameFromDocument(\n execSql: ExecSql,\n documentLocalId: string,\n): Promise<string | null> {\n try {\n const doc = await loadPersistedDocumentContent({\n execSql,\n localId: documentLocalId,\n persistence: sqlDocumentsPersistence,\n });\n if (!doc) {\n return null;\n }\n\n return readOrganizationProfileName(\n readStoredDocumentState(doc).structuredFields,\n );\n } catch {\n return null;\n }\n}\n\n/**\n * Resolves an organization's decrypted display name from local state.\n *\n * The same organization_profile document is addressed by several different\n * local-id conventions depending on how it arrived, so one lookup by id is not\n * enough:\n *\n * - The org's own **provisioner** (and any admin who later edits the name)\n * writes the document under the deterministic alias\n * `org-profile:<organizationId>` — resolved by the fast path below.\n * - A **device that only synced it** — a member of another org, or this same\n * user's freshly re-hydrated device after an identity restore — never runs\n * that write. The sync ingest keys the document under the server documentId\n * (there is no local alias to reuse — see `upsertDiscoveredDocumentWithExec`),\n * so the alias lookup misses.\n *\n * Two fallbacks cover the synced case, in order of authority:\n *\n * 1. The **organization read model** records the org's `profileDocumentId`.\n * That pointer is container-independent, so it resolves the name wherever\n * the document happens to be linked — including a profile document created\n * by an admin through the org-manager editor rather than by provisioning.\n * 2. Failing that, locate the org's metadata container by its deterministic\n * system slot and read whichever profile document is linked there. Keying on\n * the system slot (rather than the document kind) keeps it correct even\n * before the projected document kind has been rebuilt from a freshly-synced\n * snapshot, and it still works before the read model has been projected.\n */\nasync function readLocalOrganizationName(\n execSql: ExecSql,\n organizationId: string,\n organizationContainers: ReadonlyArray<LocalContainerForNameLookup>,\n profileDocumentId: string | null,\n): Promise<string | null> {\n const aliasName = await readOrganizationNameFromDocument(\n execSql,\n getOrganizationProfileDocumentLocalId({ organizationId }),\n );\n if (aliasName !== null) {\n return aliasName;\n }\n\n if (profileDocumentId) {\n // Reach the row wherever it landed: a device that only synced the document\n // keys it under the server documentId rather than any local alias.\n const syncedLocalId = await findLocalIdByDocumentId(\n execSql,\n DOCUMENTS_APP_KIND,\n profileDocumentId,\n );\n const syncedName = syncedLocalId\n ? await readOrganizationNameFromDocument(execSql, syncedLocalId)\n : null;\n if (syncedName !== null) {\n return syncedName;\n }\n }\n\n const metadataSystemSlot =\n await deriveOrganizationMetadataContainerSystemSlot({ organizationId });\n const metadataContainer = organizationContainers.find(\n (container) => container.systemSlot === metadataSystemSlot,\n );\n if (!metadataContainer) {\n return null;\n }\n\n const documentLocalIds =\n await sqlDocumentsPersistence.findDocumentLocalIdsByContainerId(\n execSql,\n metadataContainer.id,\n );\n for (const documentLocalId of documentLocalIds) {\n const name = await readOrganizationNameFromDocument(\n execSql,\n documentLocalId,\n );\n if (name !== null) {\n return name;\n }\n }\n return null;\n}\n\n/**\n * Enumerates the organizations the user can manage from local state: one per\n * persisted root container. This is intentionally offline — the Explorer already\n * resolves files across organizations by effective permission, so listing the\n * organizations to manage needs no server round-trip.\n */\nexport async function listLocalOrganizations(input: {\n execSql: ExecSql;\n}): Promise<LocalOrganizationSummary[]> {\n const containers = await sqlContainerContentsPersistence.loadContainers(\n input.execSql,\n );\n // A projected read model is not a precondition for listing organizations, so\n // an unreadable one degrades to the container-derived lookups below.\n const profileDocumentIdsByOrganizationId =\n await loadOrganizationProfileDocumentIds(input.execSql).catch(\n () => new Map<string, string>(),\n );\n\n // Group every persisted container by org so name resolution can locate an\n // org's independent metadata root without a second DB scan.\n const containersByOrganizationId = new Map<\n string,\n LocalContainerForNameLookup[]\n >();\n for (const { container } of containers) {\n const entry: LocalContainerForNameLookup = {\n id: container.id,\n systemSlot: container.systemSlot ?? null,\n };\n const group = containersByOrganizationId.get(container.organizationId);\n if (group) {\n group.push(entry);\n } else {\n containersByOrganizationId.set(container.organizationId, [entry]);\n }\n }\n\n const seen = new Set<string>();\n const rootContainers = containers.filter(({ container }) => {\n if (\n container.parentId !== null ||\n container.systemSlot ||\n seen.has(container.organizationId)\n ) {\n return false;\n }\n seen.add(container.organizationId);\n return true;\n });\n rootContainers.sort(\n (left, right) =>\n (left.container.localCreatedAt ?? \"\").localeCompare(\n right.container.localCreatedAt ?? \"\",\n ) ||\n left.container.organizationId.localeCompare(\n right.container.organizationId,\n ) ||\n left.container.id.localeCompare(right.container.id),\n );\n\n // Read each organization's profile name concurrently; readLocalOrganizationName\n // already falls back to null per organization, so a single corrupt profile\n // cannot reject the batch.\n return Promise.all(\n rootContainers.map(async ({ container }) => ({\n name: await readLocalOrganizationName(\n input.execSql,\n container.organizationId,\n containersByOrganizationId.get(container.organizationId) ?? [],\n profileDocumentIdsByOrganizationId.get(container.organizationId) ??\n null,\n ),\n organizationId: container.organizationId,\n rootContainerId: container.id,\n })),\n );\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { organizationDataUsageSnapshots } from "../../../data/sqlite/schema/organizationDataUsageSchema.js";
|
|
3
|
+
import { organizationPresentationDenials, organizationReadModelContainerGrants, organizationReadModelDirectoryUsers, organizationReadModelGroupMembers, organizationReadModelGroups, organizationReadModelRequesters, organizationReadModelState, } from "../../../data/sqlite/schema/organizationReadModelSchema.js";
|
|
4
|
+
// Organization Read Models' part of a remote reset: the sync remote reset runs
|
|
5
|
+
// inside `runOrganizationPresentationReset` and calls this in its transaction.
|
|
6
|
+
export { runOrganizationPresentationReset } from "./organizationPresentationAccessState.js";
|
|
7
|
+
/** Deletes every read model and presentation denial cached for the organization. */
|
|
8
|
+
export async function clearOrganizationReadModelsForRemoteReset(tx, organizationId) {
|
|
9
|
+
for (const table of [
|
|
10
|
+
organizationDataUsageSnapshots,
|
|
11
|
+
organizationReadModelContainerGrants,
|
|
12
|
+
organizationReadModelGroupMembers,
|
|
13
|
+
organizationReadModelDirectoryUsers,
|
|
14
|
+
organizationReadModelGroups,
|
|
15
|
+
organizationReadModelRequesters,
|
|
16
|
+
organizationReadModelState,
|
|
17
|
+
organizationPresentationDenials,
|
|
18
|
+
]) {
|
|
19
|
+
await tx
|
|
20
|
+
.delete(table)
|
|
21
|
+
.where(eq(table.organizationId, organizationId))
|
|
22
|
+
.run();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=organizationRemoteReset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"organizationRemoteReset.js","sourceRoot":"","sources":["organizationRemoteReset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,8BAA8B,EAAE,MAAM,yDAAyD,CAAC;AACzG,OAAO,EACL,+BAA+B,EAC/B,oCAAoC,EACpC,mCAAmC,EACnC,iCAAiC,EACjC,2BAA2B,EAC3B,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,yDAAyD,CAAC;AAGjE,+EAA+E;AAC/E,+EAA+E;AAC/E,OAAO,EAAE,gCAAgC,EAAE,MAAM,uCAAuC,CAAC;AAEzF,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,yCAAyC,CAC7D,EAAgC,EAChC,cAAsB;IAEtB,KAAK,MAAM,KAAK,IAAI;QAClB,8BAA8B;QAC9B,oCAAoC;QACpC,iCAAiC;QACjC,mCAAmC;QACnC,2BAA2B;QAC3B,+BAA+B;QAC/B,0BAA0B;QAC1B,+BAA+B;KACvB,EAAE,CAAC;QACX,MAAM,EAAE;aACL,MAAM,CAAC,KAAK,CAAC;aACb,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;aAC/C,GAAG,EAAE,CAAC;IACX,CAAC;AACH,CAAC","sourcesContent":["import { eq } from \"drizzle-orm\";\nimport { organizationDataUsageSnapshots } from \"../../../data/sqlite/schema/organizationDataUsageSchema\";\nimport {\n organizationPresentationDenials,\n organizationReadModelContainerGrants,\n organizationReadModelDirectoryUsers,\n organizationReadModelGroupMembers,\n organizationReadModelGroups,\n organizationReadModelRequesters,\n organizationReadModelState,\n} from \"../../../data/sqlite/schema/organizationReadModelSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../../../data/sqlite/sqlitePersistenceRuntime\";\n\n// Organization Read Models' part of a remote reset: the sync remote reset runs\n// inside `runOrganizationPresentationReset` and calls this in its transaction.\nexport { runOrganizationPresentationReset } from \"./organizationPresentationAccessState\";\n\n/** Deletes every read model and presentation denial cached for the organization. */\nexport async function clearOrganizationReadModelsForRemoteReset(\n tx: ClientSQLiteTransactionScope,\n organizationId: string,\n): Promise<void> {\n for (const table of [\n organizationDataUsageSnapshots,\n organizationReadModelContainerGrants,\n organizationReadModelGroupMembers,\n organizationReadModelDirectoryUsers,\n organizationReadModelGroups,\n organizationReadModelRequesters,\n organizationReadModelState,\n organizationPresentationDenials,\n ] as const) {\n await tx\n .delete(table)\n .where(eq(table.organizationId, organizationId))\n .run();\n }\n}\n"]}
|
|
@@ -2,8 +2,8 @@ import type { ContainerSystemSlot } from "../../internal/validators/containerSys
|
|
|
2
2
|
import type { PrincipalPolicyBundleResponse } from "../../internal/validators/response/index.js";
|
|
3
3
|
import type { DocumentProjectorRegistryInput } from "../../data/documents/documentKinds.js";
|
|
4
4
|
import { type AuthoredDiscoveryHead } from "../../data/persistence/documents/documentDiscoveryEvidencePersistence.js";
|
|
5
|
+
import type { DocumentRecord } from "../../data/persistence/documents/records/index.js";
|
|
5
6
|
import { type LocallyAcknowledgedAccessManifestHead } from "../../data/persistence/keyingCheckpoints/locallyAcknowledgedCheckpointPersistence.js";
|
|
6
|
-
import type { DocumentRecord } from "../../data/sqlite/documentPersistence/index.js";
|
|
7
7
|
import { type ExecSql, type ExecSqlClientLike } from "../../data/sqlite/sqlSchema.js";
|
|
8
8
|
interface ProvisionedContainerBootstrapInput {
|
|
9
9
|
accessEpoch: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistRegistrationBootstrap.js","sourceRoot":"","sources":["persistRegistrationBootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createDocument,\n encodeVersionVector,\n importUpdates,\n} from \"@tearleads/loro\";\nimport type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport type { DocumentProjectorRegistryInput } from \"../../data/documents/documentKinds\";\nimport { createPendingUpdateFields } from \"../../data/documents/documentSync\";\nimport {\n type ContainerMetadataRecord,\n sqlContainerContentsPersistence,\n} from \"../../data/persistence/container-contents/containerContentsPersistence\";\nimport {\n type AuthoredDiscoveryHead,\n primeAuthoredDocumentDiscoveryHeads,\n} from \"../../data/persistence/documents/documentDiscoveryEvidencePersistence\";\nimport {\n DOCUMENTS_APP_KIND,\n sqlDocumentsPersistence,\n} from \"../../data/persistence/documents/documentsPersistence\";\nimport {\n advanceLocallyAcknowledgedAccessManifestHeadsAtomically,\n type LocallyAcknowledgedAccessManifestHead,\n} from \"../../data/persistence/keyingCheckpoints/locallyAcknowledgedCheckpointPersistence\";\nimport type { DocumentRecord } from \"../../data/sqlite/documentPersistence\";\nimport { documentContainerProjectionTables } from \"../../data/sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../data/sqlite/sqlitePersistenceRuntime\";\nimport {\n createExecSql,\n type ExecSql,\n type ExecSqlClientLike,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../data/sqlite/sqlSchema\";\nimport { getScopedPeerSeed } from \"../../data/sync/crdtPeerSeed\";\nimport { persistDocumentState } from \"../documents/persistence\";\nimport { ORGANIZATION_PROFILE_DOCUMENT_KIND } from \"../organizations/organizationProfile\";\nimport {\n ORGANIZATION_METADATA_CONTAINER_NAME,\n ORGANIZATION_ROSTER_PROFILE_CONTAINER_NAME,\n ROSTER_PROFILE_DOCUMENT_KIND,\n} from \"../organizations/rosterProfileContainer\";\nimport { enqueueInitialContainerMetadataUpdate } from \"./registrationBootstrapPendingUpdates\";\nimport { persistRegistrationPrincipalPolicies } from \"./registrationPrincipalPolicyPersistence\";\n\ninterface ProvisionedContainerBootstrapInput {\n accessEpoch: number;\n accessStateHash: string;\n containerId: string;\n createdAt: string;\n metadataDocumentId: string;\n metadataSnapshot: string;\n metadataState: Pick<\n DocumentRecord,\n | \"documentId\"\n | \"contentKeyBundle\"\n | \"documentKekTargets\"\n | \"documentManifestBundle\"\n >;\n systemSlot: ContainerSystemSlot;\n updatedAt: string;\n}\n\ninterface ProvisionedProfileDocumentBootstrapInput {\n accessEpoch: number;\n accessStateHash: string;\n containerId: string;\n documentId: string;\n documentState: ProvisionedContainerBootstrapInput[\"metadataState\"];\n initialUpdate: Uint8Array;\n initialUpdateCommitted: boolean;\n localId: string;\n}\n\nexport interface RegistrationBootstrapInput {\n acknowledgedAccessHeads: readonly LocallyAcknowledgedAccessManifestHead[];\n /** Seeded documents' acknowledged signed heads; see the discovery cache. */\n authoredDiscoveryHeads: readonly AuthoredDiscoveryHead[];\n /** Checked inside the mutation queue claim; false skips every write. */\n canStartDurableMutation?: (() => boolean) | undefined;\n /**\n * @internal Test-only synchronization seam. Invoked synchronously,\n * immediately before this persist joins the serialized mutation queue —\n * no interleaving can separate the two. Lets identity-race tests\n * deterministically place an identity replacement inside the queue-wait\n * window instead of sleeping.\n */\n onPersistQueued?: (() => void) | undefined;\n containerId: string;\n initialAdminGroupPolicy: PrincipalPolicyBundleResponse;\n initialMemberGroupPolicy: PrincipalPolicyBundleResponse;\n initialOrganizationPolicy: PrincipalPolicyBundleResponse;\n rootMetadataAccessEpoch: number;\n rootMetadataAccessStateHash: string;\n rootContainerServerTimestamp: string;\n rootMetadataDocumentId: string;\n rootMetadataInitialUpdate: Uint8Array;\n rootMetadataInitialUpdateCommitted?: boolean | undefined;\n rootMetadataSnapshot: string;\n rootMetadataState: ProvisionedContainerBootstrapInput[\"metadataState\"];\n rosterProfileContainer?: ProvisionedContainerBootstrapInput & {\n metadataInitialUpdate: Uint8Array;\n metadataInitialUpdateCommitted?: boolean | undefined;\n };\n organizationMetadataContainer?: ProvisionedContainerBootstrapInput & {\n metadataInitialUpdate: Uint8Array;\n metadataInitialUpdateCommitted?: boolean | undefined;\n };\n organizationProfileDocument?: ProvisionedProfileDocumentBootstrapInput;\n rosterProfileDocument?: ProvisionedProfileDocumentBootstrapInput;\n // Additional app-owned system containers provisioned with the organization\n // (e.g. a trash bin). Each carries its own display name and icon (unlike the\n // roster/organization-metadata containers, whose names are fixed), since the\n // caller declares them.\n systemContainers?: Array<\n ProvisionedContainerBootstrapInput & {\n icon: string | null;\n name: string;\n }\n >;\n documentProjectors?: DocumentProjectorRegistryInput | undefined;\n organizationId: string;\n userId: string;\n}\nasync function persistProvisionedContainerBootstrap(\n execSql: ExecSql,\n input: {\n container: ProvisionedContainerBootstrapInput;\n icon: string | null;\n name: string;\n organizationId: string;\n parentId: string | null;\n },\n): Promise<void> {\n const { container } = input;\n const metadataState = container.metadataState;\n const record: ContainerMetadataRecord = {\n accessEpoch: container.accessEpoch,\n accessStateHash: container.accessStateHash,\n documentId: container.metadataDocumentId,\n id: container.containerId,\n lastCommitLsn: null,\n metadataUpdates: container.metadataSnapshot,\n snapshotEndVersion: \"\",\n contentKeyBundle: metadataState.contentKeyBundle ?? null,\n documentKekTargets: metadataState.documentKekTargets ?? null,\n documentManifestBundle: metadataState.documentManifestBundle ?? null,\n };\n await sqlContainerContentsPersistence.saveContainer(\n execSql,\n {\n id: container.containerId,\n effectiveAccessLevel: \"admin\",\n organizationId: input.organizationId,\n parentId: input.parentId,\n metadataDocumentId: container.metadataDocumentId,\n systemSlot: container.systemSlot,\n name: input.name,\n icon: input.icon,\n },\n record,\n {\n localUpdatedAt: container.updatedAt,\n serverTimestamps: {\n createdAt: container.createdAt,\n updatedAt: container.updatedAt,\n },\n },\n );\n}\n\nasync function persistRootContainerBootstrap(\n execSql: ExecSql,\n input: RegistrationBootstrapInput,\n): Promise<void> {\n const rootRecord: ContainerMetadataRecord = {\n accessEpoch: input.rootMetadataAccessEpoch,\n accessStateHash: input.rootMetadataAccessStateHash,\n documentId: input.rootMetadataDocumentId,\n id: input.containerId,\n lastCommitLsn: null,\n metadataUpdates: input.rootMetadataSnapshot,\n snapshotEndVersion: \"\",\n contentKeyBundle: input.rootMetadataState.contentKeyBundle ?? null,\n documentKekTargets: input.rootMetadataState.documentKekTargets ?? null,\n documentManifestBundle:\n input.rootMetadataState.documentManifestBundle ?? null,\n };\n await sqlContainerContentsPersistence.saveContainer(\n execSql,\n {\n id: input.containerId,\n effectiveAccessLevel: \"admin\",\n organizationId: input.organizationId,\n parentId: null,\n metadataDocumentId: input.rootMetadataDocumentId,\n name: \"/\",\n icon: null,\n },\n rootRecord,\n {\n localUpdatedAt: input.rootContainerServerTimestamp,\n serverTimestamps: {\n createdAt: input.rootContainerServerTimestamp,\n updatedAt: input.rootContainerServerTimestamp,\n },\n },\n );\n await enqueueInitialContainerMetadataUpdate(\n execSql,\n input.containerId,\n input.rootMetadataInitialUpdate,\n input.rootMetadataInitialUpdateCommitted,\n );\n}\n\n// Persist reserved profile containers using their fixed topology: roster profiles\n// inherit the personal root; shared organization metadata has an independent root.\nasync function persistNamedChildContainerBootstrap(\n execSql: ExecSql,\n input: RegistrationBootstrapInput,\n container: RegistrationBootstrapInput[\"rosterProfileContainer\"],\n name: string,\n): Promise<void> {\n if (!container) {\n return;\n }\n\n await persistProvisionedContainerBootstrap(execSql, {\n container,\n icon: null,\n name,\n organizationId: input.organizationId,\n parentId:\n container === input.organizationMetadataContainer\n ? null\n : input.containerId,\n });\n await enqueueInitialContainerMetadataUpdate(\n execSql,\n container.containerId,\n container.metadataInitialUpdate,\n container.metadataInitialUpdateCommitted,\n );\n}\n\nasync function persistSystemContainersBootstrap(\n execSql: ExecSql,\n input: RegistrationBootstrapInput,\n): Promise<void> {\n const systemContainers = input.systemContainers;\n if (!systemContainers) {\n return;\n }\n\n for (const systemContainer of systemContainers) {\n await persistProvisionedContainerBootstrap(execSql, {\n container: systemContainer,\n icon: systemContainer.icon,\n name: systemContainer.name,\n organizationId: input.organizationId,\n parentId: input.containerId,\n });\n // Provisioned system-container metadata is already committed remotely in\n // the organization transaction. Persist the initialized snapshot locally,\n // but do not queue a duplicate write with a second update id.\n }\n}\n\n// The ONE persist shape for the provisioned profile documents (organization\n// profile and roster profile): the input carries exactly the bootstrap\n// fields, so only the document kind and the host's projectors join it.\nasync function persistProfileDocumentBootstrap(\n execSql: ExecSql,\n input: RegistrationBootstrapInput,\n profileDocument: ProvisionedProfileDocumentBootstrapInput | undefined,\n documentKind: string,\n): Promise<void> {\n if (!profileDocument) {\n return;\n }\n\n await persistInitialDocumentBootstrap(execSql, {\n ...profileDocument,\n documentKind,\n documentProjectors: input.documentProjectors,\n });\n}\n\nexport async function persistInitialDocumentBootstrap(\n execSql: ExecSql,\n input: {\n accessEpoch: number;\n accessStateHash: string;\n containerId: string;\n documentId: string;\n documentKind: string;\n documentProjectors: DocumentProjectorRegistryInput | undefined;\n documentState: Pick<\n DocumentRecord,\n \"contentKeyBundle\" | \"documentKekTargets\" | \"documentManifestBundle\"\n >;\n initialUpdate: Uint8Array;\n initialUpdateCommitted: boolean;\n localId: string;\n },\n): Promise<void> {\n const doc = await createDocument(await getScopedPeerSeed(DOCUMENTS_APP_KIND));\n importUpdates(doc, [input.initialUpdate]);\n const pendingUpdate = input.initialUpdateCommitted\n ? undefined\n : (createPendingUpdateFields(input.initialUpdate) ?? undefined);\n\n await persistDocumentState({\n currentDoc: doc,\n currentRecord: null,\n documentProjectors: input.documentProjectors ?? [],\n execSql,\n localId: input.localId,\n patch: {\n accessEpoch: input.accessEpoch,\n accessStateHash: input.accessStateHash,\n containerId: input.containerId,\n contentKeyBundle: input.documentState.contentKeyBundle ?? null,\n documentId: input.documentId,\n // Successful provisioning makes this device's user the founding admin.\n // Stores can open before discovery returns the same access level.\n effectiveAccessLevel: \"admin\",\n documentKekTargets: input.documentState.documentKekTargets ?? null,\n documentKind: input.documentKind,\n documentManifestBundle:\n input.documentState.documentManifestBundle ?? null,\n // The create-only persistence primitive stores this frontier and the\n // full-history checkpoint in one transaction.\n snapshotEndVersion: encodeVersionVector(doc),\n },\n pendingUpdate,\n persistence: sqlDocumentsPersistence,\n });\n}\n\n/**\n * Persists the local root-container bootstrap created during successful\n * registration so container contents can initialize from SQLite on first\n * login — atomically: every write lands inside ONE transaction whose last\n * act re-checks identity currency, so an identity replaced anywhere between\n * the queue claim and the commit rolls the entire bootstrap back instead of\n * leaving a partial one. Resolves false when either currency check rejected\n * the write, so callers do not report a bootstrap that was never persisted.\n */\nexport async function persistRegistrationBootstrapFromExecSql(\n execSql: ExecSql,\n input: RegistrationBootstrapInput,\n): Promise<boolean> {\n input.onPersistQueued?.();\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n // In-mutex currency check, adjacent to the serialized-mutation claim\n // (see persistDocumentState): the identity can be replaced while this\n // persist waits for the queue, and its bootstrap must not be written\n // through a client the replacement closed or renewed.\n if (input.canStartDurableMutation && !input.canStartDurableMutation()) {\n return false;\n }\n // Idempotent DDL stays outside the atomic boundary.\n await sqlContainerContentsPersistence.ensureSchema(lockedExecSql);\n await sqlDocumentsPersistence.ensureSchema(lockedExecSql);\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n // Pre-commit currency guard: the write sequence awaits real work, so\n // the identity can be replaced mid-sequence. The guard runs\n // synchronously with the COMMIT dispatch, so a refusal rolls the whole\n // write set back and a pass cannot go stale before the commit is sent —\n // the partial-persist window a single entry check leaves open.\n const { committed } = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async () => {\n await advanceLocallyAcknowledgedAccessManifestHeadsAtomically({\n execSql: lockedExecSql,\n heads: input.acknowledgedAccessHeads,\n });\n await persistRegistrationPrincipalPolicies({\n adminGroup: input.initialAdminGroupPolicy,\n execSql: lockedExecSql,\n memberGroup: input.initialMemberGroupPolicy,\n organization: input.initialOrganizationPolicy,\n });\n await persistRootContainerBootstrap(lockedExecSql, input);\n await persistNamedChildContainerBootstrap(\n lockedExecSql,\n input,\n input.rosterProfileContainer,\n ORGANIZATION_ROSTER_PROFILE_CONTAINER_NAME,\n );\n await persistProfileDocumentBootstrap(\n lockedExecSql,\n input,\n input.rosterProfileDocument,\n ROSTER_PROFILE_DOCUMENT_KIND,\n );\n await persistNamedChildContainerBootstrap(\n lockedExecSql,\n input,\n input.organizationMetadataContainer,\n ORGANIZATION_METADATA_CONTAINER_NAME,\n );\n await persistProfileDocumentBootstrap(\n lockedExecSql,\n input,\n input.organizationProfileDocument,\n ORGANIZATION_PROFILE_DOCUMENT_KIND,\n );\n await persistSystemContainersBootstrap(lockedExecSql, input);\n // After the checkpoints above: discovery reuses a cached head only\n // while its access-manifest checkpoint still matches.\n await primeAuthoredDocumentDiscoveryHeads(\n lockedExecSql,\n input.authoredDiscoveryHeads,\n );\n },\n () => !input.canStartDurableMutation || input.canStartDurableMutation(),\n );\n return committed;\n });\n}\n\nexport async function persistRegistrationBootstrap(\n client: ExecSqlClientLike,\n input: RegistrationBootstrapInput,\n): Promise<void> {\n await persistRegistrationBootstrapFromExecSql(createExecSql(client), input);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"persistRegistrationBootstrap.js","sourceRoot":"","sources":["persistRegistrationBootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createDocument,\n encodeVersionVector,\n importUpdates,\n} from \"@tearleads/loro\";\nimport type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport type { DocumentProjectorRegistryInput } from \"../../data/documents/documentKinds\";\nimport { createPendingUpdateFields } from \"../../data/documents/documentSync\";\nimport {\n type ContainerMetadataRecord,\n sqlContainerContentsPersistence,\n} from \"../../data/persistence/container-contents/containerContentsPersistence\";\nimport {\n type AuthoredDiscoveryHead,\n primeAuthoredDocumentDiscoveryHeads,\n} from \"../../data/persistence/documents/documentDiscoveryEvidencePersistence\";\nimport {\n DOCUMENTS_APP_KIND,\n sqlDocumentsPersistence,\n} from \"../../data/persistence/documents/documentsPersistence\";\nimport type { DocumentRecord } from \"../../data/persistence/documents/records\";\nimport {\n advanceLocallyAcknowledgedAccessManifestHeadsAtomically,\n type LocallyAcknowledgedAccessManifestHead,\n} from \"../../data/persistence/keyingCheckpoints/locallyAcknowledgedCheckpointPersistence\";\nimport { documentContainerProjectionTables } from \"../../data/sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../data/sqlite/sqlitePersistenceRuntime\";\nimport {\n createExecSql,\n type ExecSql,\n type ExecSqlClientLike,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../data/sqlite/sqlSchema\";\nimport { getScopedPeerSeed } from \"../../data/sync/crdtPeerSeed\";\nimport { persistDocumentState } from \"../documents/persistence\";\nimport { ORGANIZATION_PROFILE_DOCUMENT_KIND } from \"../organizations/organizationProfile\";\nimport {\n ORGANIZATION_METADATA_CONTAINER_NAME,\n ORGANIZATION_ROSTER_PROFILE_CONTAINER_NAME,\n ROSTER_PROFILE_DOCUMENT_KIND,\n} from \"../organizations/rosterProfileContainer\";\nimport { enqueueInitialContainerMetadataUpdate } from \"./registrationBootstrapPendingUpdates\";\nimport { persistRegistrationPrincipalPolicies } from \"./registrationPrincipalPolicyPersistence\";\n\ninterface ProvisionedContainerBootstrapInput {\n accessEpoch: number;\n accessStateHash: string;\n containerId: string;\n createdAt: string;\n metadataDocumentId: string;\n metadataSnapshot: string;\n metadataState: Pick<\n DocumentRecord,\n | \"documentId\"\n | \"contentKeyBundle\"\n | \"documentKekTargets\"\n | \"documentManifestBundle\"\n >;\n systemSlot: ContainerSystemSlot;\n updatedAt: string;\n}\n\ninterface ProvisionedProfileDocumentBootstrapInput {\n accessEpoch: number;\n accessStateHash: string;\n containerId: string;\n documentId: string;\n documentState: ProvisionedContainerBootstrapInput[\"metadataState\"];\n initialUpdate: Uint8Array;\n initialUpdateCommitted: boolean;\n localId: string;\n}\n\nexport interface RegistrationBootstrapInput {\n acknowledgedAccessHeads: readonly LocallyAcknowledgedAccessManifestHead[];\n /** Seeded documents' acknowledged signed heads; see the discovery cache. */\n authoredDiscoveryHeads: readonly AuthoredDiscoveryHead[];\n /** Checked inside the mutation queue claim; false skips every write. */\n canStartDurableMutation?: (() => boolean) | undefined;\n /**\n * @internal Test-only synchronization seam. Invoked synchronously,\n * immediately before this persist joins the serialized mutation queue —\n * no interleaving can separate the two. Lets identity-race tests\n * deterministically place an identity replacement inside the queue-wait\n * window instead of sleeping.\n */\n onPersistQueued?: (() => void) | undefined;\n containerId: string;\n initialAdminGroupPolicy: PrincipalPolicyBundleResponse;\n initialMemberGroupPolicy: PrincipalPolicyBundleResponse;\n initialOrganizationPolicy: PrincipalPolicyBundleResponse;\n rootMetadataAccessEpoch: number;\n rootMetadataAccessStateHash: string;\n rootContainerServerTimestamp: string;\n rootMetadataDocumentId: string;\n rootMetadataInitialUpdate: Uint8Array;\n rootMetadataInitialUpdateCommitted?: boolean | undefined;\n rootMetadataSnapshot: string;\n rootMetadataState: ProvisionedContainerBootstrapInput[\"metadataState\"];\n rosterProfileContainer?: ProvisionedContainerBootstrapInput & {\n metadataInitialUpdate: Uint8Array;\n metadataInitialUpdateCommitted?: boolean | undefined;\n };\n organizationMetadataContainer?: ProvisionedContainerBootstrapInput & {\n metadataInitialUpdate: Uint8Array;\n metadataInitialUpdateCommitted?: boolean | undefined;\n };\n organizationProfileDocument?: ProvisionedProfileDocumentBootstrapInput;\n rosterProfileDocument?: ProvisionedProfileDocumentBootstrapInput;\n // Additional app-owned system containers provisioned with the organization\n // (e.g. a trash bin). Each carries its own display name and icon (unlike the\n // roster/organization-metadata containers, whose names are fixed), since the\n // caller declares them.\n systemContainers?: Array<\n ProvisionedContainerBootstrapInput & {\n icon: string | null;\n name: string;\n }\n >;\n documentProjectors?: DocumentProjectorRegistryInput | undefined;\n organizationId: string;\n userId: string;\n}\nasync function persistProvisionedContainerBootstrap(\n execSql: ExecSql,\n input: {\n container: ProvisionedContainerBootstrapInput;\n icon: string | null;\n name: string;\n organizationId: string;\n parentId: string | null;\n },\n): Promise<void> {\n const { container } = input;\n const metadataState = container.metadataState;\n const record: ContainerMetadataRecord = {\n accessEpoch: container.accessEpoch,\n accessStateHash: container.accessStateHash,\n documentId: container.metadataDocumentId,\n id: container.containerId,\n lastCommitLsn: null,\n metadataUpdates: container.metadataSnapshot,\n snapshotEndVersion: \"\",\n contentKeyBundle: metadataState.contentKeyBundle ?? null,\n documentKekTargets: metadataState.documentKekTargets ?? null,\n documentManifestBundle: metadataState.documentManifestBundle ?? null,\n };\n await sqlContainerContentsPersistence.saveContainer(\n execSql,\n {\n id: container.containerId,\n effectiveAccessLevel: \"admin\",\n organizationId: input.organizationId,\n parentId: input.parentId,\n metadataDocumentId: container.metadataDocumentId,\n systemSlot: container.systemSlot,\n name: input.name,\n icon: input.icon,\n },\n record,\n {\n localUpdatedAt: container.updatedAt,\n serverTimestamps: {\n createdAt: container.createdAt,\n updatedAt: container.updatedAt,\n },\n },\n );\n}\n\nasync function persistRootContainerBootstrap(\n execSql: ExecSql,\n input: RegistrationBootstrapInput,\n): Promise<void> {\n const rootRecord: ContainerMetadataRecord = {\n accessEpoch: input.rootMetadataAccessEpoch,\n accessStateHash: input.rootMetadataAccessStateHash,\n documentId: input.rootMetadataDocumentId,\n id: input.containerId,\n lastCommitLsn: null,\n metadataUpdates: input.rootMetadataSnapshot,\n snapshotEndVersion: \"\",\n contentKeyBundle: input.rootMetadataState.contentKeyBundle ?? null,\n documentKekTargets: input.rootMetadataState.documentKekTargets ?? null,\n documentManifestBundle:\n input.rootMetadataState.documentManifestBundle ?? null,\n };\n await sqlContainerContentsPersistence.saveContainer(\n execSql,\n {\n id: input.containerId,\n effectiveAccessLevel: \"admin\",\n organizationId: input.organizationId,\n parentId: null,\n metadataDocumentId: input.rootMetadataDocumentId,\n name: \"/\",\n icon: null,\n },\n rootRecord,\n {\n localUpdatedAt: input.rootContainerServerTimestamp,\n serverTimestamps: {\n createdAt: input.rootContainerServerTimestamp,\n updatedAt: input.rootContainerServerTimestamp,\n },\n },\n );\n await enqueueInitialContainerMetadataUpdate(\n execSql,\n input.containerId,\n input.rootMetadataInitialUpdate,\n input.rootMetadataInitialUpdateCommitted,\n );\n}\n\n// Persist reserved profile containers using their fixed topology: roster profiles\n// inherit the personal root; shared organization metadata has an independent root.\nasync function persistNamedChildContainerBootstrap(\n execSql: ExecSql,\n input: RegistrationBootstrapInput,\n container: RegistrationBootstrapInput[\"rosterProfileContainer\"],\n name: string,\n): Promise<void> {\n if (!container) {\n return;\n }\n\n await persistProvisionedContainerBootstrap(execSql, {\n container,\n icon: null,\n name,\n organizationId: input.organizationId,\n parentId:\n container === input.organizationMetadataContainer\n ? null\n : input.containerId,\n });\n await enqueueInitialContainerMetadataUpdate(\n execSql,\n container.containerId,\n container.metadataInitialUpdate,\n container.metadataInitialUpdateCommitted,\n );\n}\n\nasync function persistSystemContainersBootstrap(\n execSql: ExecSql,\n input: RegistrationBootstrapInput,\n): Promise<void> {\n const systemContainers = input.systemContainers;\n if (!systemContainers) {\n return;\n }\n\n for (const systemContainer of systemContainers) {\n await persistProvisionedContainerBootstrap(execSql, {\n container: systemContainer,\n icon: systemContainer.icon,\n name: systemContainer.name,\n organizationId: input.organizationId,\n parentId: input.containerId,\n });\n // Provisioned system-container metadata is already committed remotely in\n // the organization transaction. Persist the initialized snapshot locally,\n // but do not queue a duplicate write with a second update id.\n }\n}\n\n// The ONE persist shape for the provisioned profile documents (organization\n// profile and roster profile): the input carries exactly the bootstrap\n// fields, so only the document kind and the host's projectors join it.\nasync function persistProfileDocumentBootstrap(\n execSql: ExecSql,\n input: RegistrationBootstrapInput,\n profileDocument: ProvisionedProfileDocumentBootstrapInput | undefined,\n documentKind: string,\n): Promise<void> {\n if (!profileDocument) {\n return;\n }\n\n await persistInitialDocumentBootstrap(execSql, {\n ...profileDocument,\n documentKind,\n documentProjectors: input.documentProjectors,\n });\n}\n\nexport async function persistInitialDocumentBootstrap(\n execSql: ExecSql,\n input: {\n accessEpoch: number;\n accessStateHash: string;\n containerId: string;\n documentId: string;\n documentKind: string;\n documentProjectors: DocumentProjectorRegistryInput | undefined;\n documentState: Pick<\n DocumentRecord,\n \"contentKeyBundle\" | \"documentKekTargets\" | \"documentManifestBundle\"\n >;\n initialUpdate: Uint8Array;\n initialUpdateCommitted: boolean;\n localId: string;\n },\n): Promise<void> {\n const doc = await createDocument(await getScopedPeerSeed(DOCUMENTS_APP_KIND));\n importUpdates(doc, [input.initialUpdate]);\n const pendingUpdate = input.initialUpdateCommitted\n ? undefined\n : (createPendingUpdateFields(input.initialUpdate) ?? undefined);\n\n await persistDocumentState({\n currentDoc: doc,\n currentRecord: null,\n documentProjectors: input.documentProjectors ?? [],\n execSql,\n localId: input.localId,\n patch: {\n accessEpoch: input.accessEpoch,\n accessStateHash: input.accessStateHash,\n containerId: input.containerId,\n contentKeyBundle: input.documentState.contentKeyBundle ?? null,\n documentId: input.documentId,\n // Successful provisioning makes this device's user the founding admin.\n // Stores can open before discovery returns the same access level.\n effectiveAccessLevel: \"admin\",\n documentKekTargets: input.documentState.documentKekTargets ?? null,\n documentKind: input.documentKind,\n documentManifestBundle:\n input.documentState.documentManifestBundle ?? null,\n // The create-only persistence primitive stores this frontier and the\n // full-history checkpoint in one transaction.\n snapshotEndVersion: encodeVersionVector(doc),\n },\n pendingUpdate,\n persistence: sqlDocumentsPersistence,\n });\n}\n\n/**\n * Persists the local root-container bootstrap created during successful\n * registration so container contents can initialize from SQLite on first\n * login — atomically: every write lands inside ONE transaction whose last\n * act re-checks identity currency, so an identity replaced anywhere between\n * the queue claim and the commit rolls the entire bootstrap back instead of\n * leaving a partial one. Resolves false when either currency check rejected\n * the write, so callers do not report a bootstrap that was never persisted.\n */\nexport async function persistRegistrationBootstrapFromExecSql(\n execSql: ExecSql,\n input: RegistrationBootstrapInput,\n): Promise<boolean> {\n input.onPersistQueued?.();\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n // In-mutex currency check, adjacent to the serialized-mutation claim\n // (see persistDocumentState): the identity can be replaced while this\n // persist waits for the queue, and its bootstrap must not be written\n // through a client the replacement closed or renewed.\n if (input.canStartDurableMutation && !input.canStartDurableMutation()) {\n return false;\n }\n // Idempotent DDL stays outside the atomic boundary.\n await sqlContainerContentsPersistence.ensureSchema(lockedExecSql);\n await sqlDocumentsPersistence.ensureSchema(lockedExecSql);\n await ensureSqlTables(lockedExecSql, documentContainerProjectionTables);\n // Pre-commit currency guard: the write sequence awaits real work, so\n // the identity can be replaced mid-sequence. The guard runs\n // synchronously with the COMMIT dispatch, so a refusal rolls the whole\n // write set back and a pass cannot go stale before the commit is sent —\n // the partial-persist window a single entry check leaves open.\n const { committed } = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async () => {\n await advanceLocallyAcknowledgedAccessManifestHeadsAtomically({\n execSql: lockedExecSql,\n heads: input.acknowledgedAccessHeads,\n });\n await persistRegistrationPrincipalPolicies({\n adminGroup: input.initialAdminGroupPolicy,\n execSql: lockedExecSql,\n memberGroup: input.initialMemberGroupPolicy,\n organization: input.initialOrganizationPolicy,\n });\n await persistRootContainerBootstrap(lockedExecSql, input);\n await persistNamedChildContainerBootstrap(\n lockedExecSql,\n input,\n input.rosterProfileContainer,\n ORGANIZATION_ROSTER_PROFILE_CONTAINER_NAME,\n );\n await persistProfileDocumentBootstrap(\n lockedExecSql,\n input,\n input.rosterProfileDocument,\n ROSTER_PROFILE_DOCUMENT_KIND,\n );\n await persistNamedChildContainerBootstrap(\n lockedExecSql,\n input,\n input.organizationMetadataContainer,\n ORGANIZATION_METADATA_CONTAINER_NAME,\n );\n await persistProfileDocumentBootstrap(\n lockedExecSql,\n input,\n input.organizationProfileDocument,\n ORGANIZATION_PROFILE_DOCUMENT_KIND,\n );\n await persistSystemContainersBootstrap(lockedExecSql, input);\n // After the checkpoints above: discovery reuses a cached head only\n // while its access-manifest checkpoint still matches.\n await primeAuthoredDocumentDiscoveryHeads(\n lockedExecSql,\n input.authoredDiscoveryHeads,\n );\n },\n () => !input.canStartDurableMutation || input.canStartDurableMutation(),\n );\n return committed;\n });\n}\n\nexport async function persistRegistrationBootstrap(\n client: ExecSqlClientLike,\n input: RegistrationBootstrapInput,\n): Promise<void> {\n await persistRegistrationBootstrapFromExecSql(createExecSql(client), input);\n}\n"]}
|