@tearleads/client-sdk 0.1.75 → 0.1.77
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/containers/shared/mutationAcknowledgement.d.ts +38 -0
- package/data/{containers → persistence/container-contents}/containerBinding.js +1 -1
- package/data/persistence/container-contents/containerContentsPersistence.js +2 -2
- package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerContentsPersistenceTypes.d.ts +2 -2
- package/data/persistence/container-contents/containerContentsPersistenceTypes.js.map +1 -1
- package/data/persistence/container-contents/containerDeletionPersistence.js +2 -2
- package/data/persistence/container-contents/containerDeletionPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerHydrationPersistence.js +2 -2
- package/data/persistence/container-contents/containerHydrationPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js +2 -2
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerMetadataRows.js +1 -1
- package/data/persistence/container-contents/containerMetadataRows.js.map +1 -1
- package/data/persistence/container-contents/containerRebindPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerReconcilePersistence.js +2 -2
- package/data/persistence/container-contents/containerReconcilePersistence.js.map +1 -1
- package/data/persistence/container-contents/containerStructuralRepair.js +1 -1
- package/data/persistence/container-contents/containerStructuralRepair.js.map +1 -1
- package/data/persistence/container-contents/dormantMetadataSweep.js +1 -1
- package/data/persistence/container-contents/dormantMetadataSweep.js.map +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstoneHolds.js +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstoneHolds.js.map +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstones.js +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstones.js.map +1 -1
- package/data/persistence/documents/internal/discoveredDocumentPlacement.js +1 -1
- package/data/persistence/documents/internal/discoveredDocumentPlacement.js.map +1 -1
- package/data/principals/currentPolicyReferenceResolver.d.ts +3 -0
- package/index.d.ts +2 -2
- package/index.js +2 -2
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/stores/container-contents/systemContainers/systemContainerIcon.js +1 -1
- package/stores/container-contents/systemContainers/systemContainerIcon.js.map +1 -1
- package/workflows/container-contents/container-state/create/createChild.js +1 -1
- package/workflows/container-contents/container-state/create/createChild.js.map +1 -1
- package/workflows/container-contents/container-state/purge/purgeTree.js +1 -1
- package/workflows/container-contents/container-state/purge/purgeTree.js.map +1 -1
- package/workflows/container-contents/container-state/remote.js +1 -1
- package/workflows/container-contents/container-state/remote.js.map +1 -1
- package/workflows/container-contents/container-state/share/groupGrantVerification.js +1 -1
- package/workflows/container-contents/container-state/share/groupGrantVerification.js.map +1 -1
- package/workflows/container-contents/container-state/share/groupShareEpoch.js +1 -1
- package/workflows/container-contents/container-state/share/groupShareEpoch.js.map +1 -1
- package/workflows/container-contents/container-state/types.js.map +1 -1
- package/workflows/container-contents/containerPersistence.d.ts +3 -3
- package/workflows/container-contents/containerPersistence.js +1 -1
- package/workflows/container-contents/containerPersistence.js.map +1 -1
- package/workflows/container-contents/documentLinks/documentLinkIntent.js +1 -1
- package/workflows/container-contents/documentLinks/documentLinkIntent.js.map +1 -1
- package/workflows/container-contents/documentLinks/index.js +1 -1
- package/workflows/container-contents/documentLinks/index.js.map +1 -1
- package/workflows/container-contents/documentMove/documentMoveIntent.js +1 -1
- package/workflows/container-contents/documentMove/documentMoveIntent.js.map +1 -1
- package/workflows/container-contents/documentMove/documentMoveIntentSettlement.js +2 -2
- package/workflows/container-contents/documentMove/documentMoveIntentSettlement.js.map +1 -1
- package/workflows/container-contents/documentQueries/documentSubtreeQueries.js +1 -1
- package/workflows/container-contents/documentQueries/documentSubtreeQueries.js.map +1 -1
- package/workflows/container-contents/documentQueries/index.js +3 -3
- package/workflows/container-contents/documentQueries/index.js.map +1 -1
- package/workflows/container-contents/documentQueries/pendingDownloads.js +1 -1
- package/workflows/container-contents/documentQueries/pendingDownloads.js.map +1 -1
- package/workflows/container-contents/index.d.ts +1 -1
- package/workflows/container-contents/index.js +1 -1
- package/workflows/container-contents/index.js.map +1 -1
- package/workflows/container-contents/info/blobInfo.js +1 -1
- package/workflows/container-contents/info/blobInfo.js.map +1 -1
- package/workflows/container-contents/info/containerInfo.js +2 -2
- package/workflows/container-contents/info/containerInfo.js.map +1 -1
- package/{data/containers → workflows/container-contents/metadata}/containerMetadataDocument.d.ts +1 -1
- package/{data/containers → workflows/container-contents/metadata}/containerMetadataDocument.js +2 -2
- package/workflows/container-contents/metadata/containerMetadataDocument.js.map +1 -0
- package/workflows/container-contents/metadata/metadataMutationPreparation.js +1 -1
- package/workflows/container-contents/metadata/metadataMutationPreparation.js.map +1 -1
- package/workflows/container-contents/metadata/metadataPersistence.js +1 -1
- package/workflows/container-contents/metadata/metadataPersistence.js.map +1 -1
- package/workflows/container-contents/metadata/metadataRecord.js.map +1 -1
- package/workflows/container-contents/metadata/metadataStateIsolation.js +1 -1
- package/workflows/container-contents/metadata/metadataStateIsolation.js.map +1 -1
- package/workflows/container-contents/metadata/metadataSyncSettlement.js +1 -1
- package/workflows/container-contents/metadata/metadataSyncSettlement.js.map +1 -1
- package/workflows/container-contents/metadata/metadataTypes.d.ts +2 -2
- package/workflows/container-contents/metadata/metadataTypes.js.map +1 -1
- package/workflows/container-contents/recovery/folderRecovery.js +1 -1
- package/workflows/container-contents/recovery/folderRecovery.js.map +1 -1
- package/workflows/container-contents/remoteHydration/destination/replacementBinding.js +1 -1
- package/workflows/container-contents/remoteHydration/destination/replacementBinding.js.map +1 -1
- package/workflows/container-contents/remoteHydration/destination/verifiedDestination.js +1 -1
- package/workflows/container-contents/remoteHydration/destination/verifiedDestination.js.map +1 -1
- package/workflows/container-contents/remoteHydration/heldContainerRebind.js +1 -1
- package/workflows/container-contents/remoteHydration/heldContainerRebind.js.map +1 -1
- package/workflows/container-contents/remoteHydration/reattachMetadata.js +1 -1
- package/workflows/container-contents/remoteHydration/reattachMetadata.js.map +1 -1
- package/workflows/container-contents/remoteHydration/remoteContainers/insertRemoteContainer.js +2 -2
- package/workflows/container-contents/remoteHydration/remoteContainers/insertRemoteContainer.js.map +1 -1
- package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerState.js +1 -1
- package/workflows/container-contents/remoteHydration/remoteContainers/remoteContainerState.js.map +1 -1
- package/workflows/container-contents/remoteHydration/types.d.ts +1 -1
- package/workflows/container-contents/remoteHydration/types.js.map +1 -1
- package/workflows/container-contents/storedContainerState.js +1 -1
- package/workflows/container-contents/storedContainerState.js.map +1 -1
- package/workflows/containers/child/mutationSubmit.d.ts +76 -0
- package/workflows/containers/child/rekey/carriedDescendantRekeys.d.ts +70 -0
- package/workflows/containers/child/share/index.d.ts +1 -38
- package/workflows/containers/child/share/index.js +1 -205
- package/workflows/containers/child/share/index.js.map +1 -1
- package/workflows/containers/child/share/shareAncestorRepair.d.ts +37 -0
- package/workflows/containers/child/share/shareProjection.d.ts +7 -0
- package/workflows/containers/index.d.ts +3 -2
- package/workflows/containers/index.js +4 -2
- package/workflows/containers/index.js.map +1 -1
- package/workflows/organizations/currentOrganizationAuthority.d.ts +21 -0
- package/workflows/organizations/groupMetadata/groupMetadataAccess.js +1 -1
- package/workflows/organizations/groupMetadata/groupMetadataAccess.js.map +1 -1
- package/workflows/organizations/groupMetadata/groupMetadataRuntime.d.ts +30 -0
- package/workflows/{containers/child/share → organizations/groupShare}/currentShareGrantMutation.js +6 -6
- package/workflows/organizations/groupShare/currentShareGrantMutation.js.map +1 -0
- package/workflows/organizations/groupShare/currentSharePrincipalPolicy.d.ts +30 -0
- package/workflows/{containers/child/share → organizations/groupShare}/currentSharePrincipalPolicy.js +10 -10
- package/workflows/organizations/groupShare/currentSharePrincipalPolicy.js.map +1 -0
- package/workflows/organizations/groupShare/index.d.ts +4 -0
- package/workflows/organizations/groupShare/index.js +7 -0
- package/workflows/organizations/groupShare/index.js.map +1 -0
- package/workflows/organizations/groupShare/runtimeCurrentGroupShare.d.ts +4 -0
- package/workflows/{containers/child/share → organizations/groupShare}/runtimeCurrentGroupShare.js +6 -8
- package/workflows/organizations/groupShare/runtimeCurrentGroupShare.js.map +1 -0
- package/workflows/{containers/child/share → organizations/groupShare}/sharePrincipalPolicy.d.ts +9 -9
- package/workflows/{containers/child/share → organizations/groupShare}/sharePrincipalPolicy.js +13 -13
- package/workflows/organizations/groupShare/sharePrincipalPolicy.js.map +1 -0
- package/workflows/organizations/groupShare/shareWithGroup.d.ts +44 -0
- package/workflows/organizations/groupShare/shareWithGroup.js +210 -0
- package/workflows/organizations/groupShare/shareWithGroup.js.map +1 -0
- package/workflows/organizations/index.d.ts +1 -0
- package/workflows/organizations/index.js +1 -0
- package/workflows/organizations/index.js.map +1 -1
- package/workflows/organizations/principalMutations/currentPrincipalMutationLease.d.ts +16 -0
- package/workflows/organizations/readModel/localReadModelDetails.js +1 -1
- package/workflows/organizations/readModel/localReadModelDetails.js.map +1 -1
- package/workflows/principals/runtimePolicyRecovery.d.ts +42 -0
- package/workflows/registration/bootstrapRootContainer.js +1 -1
- package/workflows/registration/bootstrapRootContainer.js.map +1 -1
- package/workflows/registration/provisioningBootstrapBuilders.js +1 -1
- package/workflows/registration/provisioningBootstrapBuilders.js.map +1 -1
- package/workflows/registration/rootMetadataBootstrap.js +1 -1
- package/workflows/registration/rootMetadataBootstrap.js.map +1 -1
- package/workflows/sync/remoteReset.js +1 -1
- package/workflows/sync/remoteReset.js.map +1 -1
- package/data/containers/containerMetadataDocument.js.map +0 -1
- package/workflows/containers/child/share/currentShareGrantMutation.js.map +0 -1
- package/workflows/containers/child/share/currentSharePrincipalPolicy.js.map +0 -1
- package/workflows/containers/child/share/runtimeCurrentGroupShare.js.map +0 -1
- package/workflows/containers/child/share/sharePrincipalPolicy.js.map +0 -1
- /package/data/{containers → persistence/container-contents}/containerBinding.d.ts +0 -0
- /package/data/{containers → persistence/container-contents}/containerBinding.js.map +0 -0
- /package/data/persistence/{containers → container-contents}/containerPersistence.d.ts +0 -0
- /package/data/persistence/{containers → container-contents}/containerPersistence.js +0 -0
- /package/data/persistence/{containers → container-contents}/containerPersistence.js.map +0 -0
- /package/data/persistence/{containers → container-contents}/containerSyncWatermarkPersistence.d.ts +0 -0
- /package/data/persistence/{containers → container-contents}/containerSyncWatermarkPersistence.js +0 -0
- /package/data/persistence/{containers → container-contents}/containerSyncWatermarkPersistence.js.map +0 -0
- /package/data/persistence/{containers → container-contents}/documentContainerProjectionPersistence.js +0 -0
- /package/data/persistence/{containers → container-contents}/documentContainerProjectionPersistence.js.map +0 -0
- /package/data/persistence/{containers → container-contents}/documentPlacement.js +0 -0
- /package/data/persistence/{containers → container-contents}/documentPlacement.js.map +0 -0
|
@@ -5,9 +5,9 @@ import { serializeDocumentSyncPullContinuation } from "../../documents/shared/sy
|
|
|
5
5
|
import { documentRecordSelection, mapSelectedDocumentRecord, } from "../../sqlite/documentPersistence/index.js";
|
|
6
6
|
import { documentHistoryCheckpoints, documents } from "../../sqlite/schema/index.js";
|
|
7
7
|
import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
|
|
8
|
-
import { saveContainerRows, } from "../containers/containerPersistence.js";
|
|
9
8
|
import { saveContainerCreateIntent } from "./containerIntentPersistence.js";
|
|
10
9
|
import { saveContainerMoveIntent } from "./containerMoveIntentRows.js";
|
|
10
|
+
import { saveContainerRows, } from "./containerPersistence.js";
|
|
11
11
|
import { CONTAINER_METADATA_APP_KIND, clearDormantContainerMetadataInTransaction, } from "./dormantContainerMetadata.js";
|
|
12
12
|
export function getContainerMetadataScope(containerId) {
|
|
13
13
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerMetadataRows.js","sourceRoot":"","sources":["containerMetadataRows.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes } from \"@tearleads/encoding\";\nimport { getImportBlobMetadata } from \"@tearleads/loro\";\nimport { and, eq } from \"drizzle-orm\";\nimport { serializeDocumentSyncPullContinuation } from \"../../documents/shared/sync/pullContinuation\";\nimport {\n documentRecordSelection,\n mapSelectedDocumentRecord,\n} from \"../../sqlite/documentPersistence\";\nimport { documentHistoryCheckpoints, documents } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport {\n type ContainerRecord,\n saveContainerRows,\n} from \"../containers/containerPersistence\";\nimport type {\n ContainerCreateIntentInput,\n ContainerMetadataRecord,\n ContainerMoveIntentInput,\n} from \"./containerContentsPersistenceTypes\";\nimport { saveContainerCreateIntent } from \"./containerIntentPersistence\";\nimport { saveContainerMoveIntent } from \"./containerMoveIntentRows\";\nimport {\n CONTAINER_METADATA_APP_KIND,\n clearDormantContainerMetadataInTransaction,\n} from \"./dormantContainerMetadata\";\n\nexport function getContainerMetadataScope(containerId: string) {\n return {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n };\n}\n\n// The metadata document's content frontier, derived from its full updates\n// export. Empty or undecodable blobs read as \"never hydrated\".\nfunction deriveMetadataEndVersion(metadataUpdates: string): string {\n if (metadataUpdates.length === 0) {\n return \"\";\n }\n\n try {\n return getImportBlobMetadata(base64ToBytes(metadataUpdates))\n .partialEndVersionVector;\n } catch {\n return \"\";\n }\n}\n\nasync function saveContainerMetadataRecord(input: {\n containerId: string;\n record: ContainerMetadataRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}) {\n const { containerId, record, tx, updatedAt } = input;\n const snapshotEndVersion = deriveMetadataEndVersion(record.metadataUpdates);\n const nextRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n documentId: record.documentId,\n snapshotEndVersion,\n accessEpoch: record.accessEpoch,\n accessStateHash: record.accessStateHash ?? null,\n lastCommitLsn: record.lastCommitLsn ?? null,\n documentManifestBundle: record.documentManifestBundle ?? null,\n contentKeyBundle: record.contentKeyBundle ?? null,\n documentKekTargets: record.documentKekTargets ?? null,\n ...(record.pullContinuationRecoveryRequired === true &&\n record.pullContinuation === undefined\n ? {}\n : {\n pullContinuation: serializeDocumentSyncPullContinuation(\n record.pullContinuation ?? null,\n ),\n }),\n updatedAt,\n };\n\n await tx\n .insert(documents)\n .values(nextRow)\n .onConflictDoUpdate({\n target: [documents.appKind, documents.localId],\n set: nextRow,\n })\n .run();\n\n // The metadata document's content lives in the shared history-checkpoint\n // table, like every other document kind. The whole updates export IS the\n // checkpoint: metadata documents are tiny registries, so no tail/compaction\n // machinery is needed, and the row replaces atomically with the record.\n const checkpointRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n snapshot: record.metadataUpdates,\n endVersionVector: snapshotEndVersion,\n revision: updatedAt,\n updatedAt,\n };\n await tx\n .insert(documentHistoryCheckpoints)\n .values(checkpointRow)\n .onConflictDoUpdate({\n target: [\n documentHistoryCheckpoints.appKind,\n documentHistoryCheckpoints.localId,\n ],\n set: checkpointRow,\n })\n .run();\n}\n\nexport async function saveContainerContentsContainerRows(input: {\n container: ContainerRecord;\n createIntent?: ContainerCreateIntentInput | undefined;\n moveIntent?: ContainerMoveIntentInput | undefined;\n record: ContainerMetadataRecord | null;\n tx: ClientSQLiteTransactionScope;\n localUpdatedAt: string;\n serverTimestamps?:\n | {\n createdAt?: string | null;\n updatedAt?: string | null;\n }\n | undefined;\n}): Promise<ContainerRecord> {\n const {\n container,\n createIntent,\n moveIntent,\n localUpdatedAt,\n record,\n serverTimestamps,\n tx,\n } = input;\n const nextContainer = {\n ...container,\n ...(serverTimestamps\n ? {\n serverCreatedAt:\n serverTimestamps.createdAt ?? container.serverCreatedAt ?? null,\n serverUpdatedAt:\n serverTimestamps.updatedAt ?? container.serverUpdatedAt ?? null,\n }\n : {}),\n };\n\n const savedContainer = await saveContainerRows({\n record: nextContainer,\n tx,\n localUpdatedAt,\n });\n await clearDormantContainerMetadataInTransaction(tx, [container.id]);\n\n if (record) {\n await saveContainerMetadataRecord({\n containerId: container.id,\n record: {\n ...record,\n id: container.id,\n },\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (createIntent) {\n await saveContainerCreateIntent({\n containerId: container.id,\n createIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (moveIntent) {\n await saveContainerMoveIntent({\n containerId: container.id,\n moveIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n return savedContainer;\n}\n\n/**\n * ONE joined statement, so the record's key/access fields and the checkpoint\n * content it pairs with come from a single consistent read — two separate\n * queries could interleave with a concurrent metadata save (record +\n * checkpoint written in one transaction) and pair stale keying state with\n * newer content.\n */\nexport async function selectContainerMetadataRecord(\n execSql: ExecSql,\n containerId: string,\n): Promise<ContainerMetadataRecord | null> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n ...documentRecordSelection,\n metadataUpdates: documentHistoryCheckpoints.snapshot,\n })\n .from(documents)\n .leftJoin(\n documentHistoryCheckpoints,\n and(\n eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documentHistoryCheckpoints.localId, documents.localId),\n ),\n )\n .where(\n and(\n eq(documents.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documents.localId, containerId),\n ),\n )\n .limit(1);\n const row = rows[0];\n if (!row) {\n return null;\n }\n const { metadataUpdates, ...recordRow } = row;\n return {\n ...mapSelectedDocumentRecord(recordRow),\n metadataUpdates: metadataUpdates ?? \"\",\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"containerMetadataRows.js","sourceRoot":"","sources":["containerMetadataRows.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes } from \"@tearleads/encoding\";\nimport { getImportBlobMetadata } from \"@tearleads/loro\";\nimport { and, eq } from \"drizzle-orm\";\nimport { serializeDocumentSyncPullContinuation } from \"../../documents/shared/sync/pullContinuation\";\nimport {\n documentRecordSelection,\n mapSelectedDocumentRecord,\n} from \"../../sqlite/documentPersistence\";\nimport { documentHistoryCheckpoints, documents } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport type {\n ContainerCreateIntentInput,\n ContainerMetadataRecord,\n ContainerMoveIntentInput,\n} from \"./containerContentsPersistenceTypes\";\nimport { saveContainerCreateIntent } from \"./containerIntentPersistence\";\nimport { saveContainerMoveIntent } from \"./containerMoveIntentRows\";\nimport {\n type ContainerRecord,\n saveContainerRows,\n} from \"./containerPersistence\";\nimport {\n CONTAINER_METADATA_APP_KIND,\n clearDormantContainerMetadataInTransaction,\n} from \"./dormantContainerMetadata\";\n\nexport function getContainerMetadataScope(containerId: string) {\n return {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n };\n}\n\n// The metadata document's content frontier, derived from its full updates\n// export. Empty or undecodable blobs read as \"never hydrated\".\nfunction deriveMetadataEndVersion(metadataUpdates: string): string {\n if (metadataUpdates.length === 0) {\n return \"\";\n }\n\n try {\n return getImportBlobMetadata(base64ToBytes(metadataUpdates))\n .partialEndVersionVector;\n } catch {\n return \"\";\n }\n}\n\nasync function saveContainerMetadataRecord(input: {\n containerId: string;\n record: ContainerMetadataRecord;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}) {\n const { containerId, record, tx, updatedAt } = input;\n const snapshotEndVersion = deriveMetadataEndVersion(record.metadataUpdates);\n const nextRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n documentId: record.documentId,\n snapshotEndVersion,\n accessEpoch: record.accessEpoch,\n accessStateHash: record.accessStateHash ?? null,\n lastCommitLsn: record.lastCommitLsn ?? null,\n documentManifestBundle: record.documentManifestBundle ?? null,\n contentKeyBundle: record.contentKeyBundle ?? null,\n documentKekTargets: record.documentKekTargets ?? null,\n ...(record.pullContinuationRecoveryRequired === true &&\n record.pullContinuation === undefined\n ? {}\n : {\n pullContinuation: serializeDocumentSyncPullContinuation(\n record.pullContinuation ?? null,\n ),\n }),\n updatedAt,\n };\n\n await tx\n .insert(documents)\n .values(nextRow)\n .onConflictDoUpdate({\n target: [documents.appKind, documents.localId],\n set: nextRow,\n })\n .run();\n\n // The metadata document's content lives in the shared history-checkpoint\n // table, like every other document kind. The whole updates export IS the\n // checkpoint: metadata documents are tiny registries, so no tail/compaction\n // machinery is needed, and the row replaces atomically with the record.\n const checkpointRow = {\n appKind: CONTAINER_METADATA_APP_KIND,\n localId: containerId,\n snapshot: record.metadataUpdates,\n endVersionVector: snapshotEndVersion,\n revision: updatedAt,\n updatedAt,\n };\n await tx\n .insert(documentHistoryCheckpoints)\n .values(checkpointRow)\n .onConflictDoUpdate({\n target: [\n documentHistoryCheckpoints.appKind,\n documentHistoryCheckpoints.localId,\n ],\n set: checkpointRow,\n })\n .run();\n}\n\nexport async function saveContainerContentsContainerRows(input: {\n container: ContainerRecord;\n createIntent?: ContainerCreateIntentInput | undefined;\n moveIntent?: ContainerMoveIntentInput | undefined;\n record: ContainerMetadataRecord | null;\n tx: ClientSQLiteTransactionScope;\n localUpdatedAt: string;\n serverTimestamps?:\n | {\n createdAt?: string | null;\n updatedAt?: string | null;\n }\n | undefined;\n}): Promise<ContainerRecord> {\n const {\n container,\n createIntent,\n moveIntent,\n localUpdatedAt,\n record,\n serverTimestamps,\n tx,\n } = input;\n const nextContainer = {\n ...container,\n ...(serverTimestamps\n ? {\n serverCreatedAt:\n serverTimestamps.createdAt ?? container.serverCreatedAt ?? null,\n serverUpdatedAt:\n serverTimestamps.updatedAt ?? container.serverUpdatedAt ?? null,\n }\n : {}),\n };\n\n const savedContainer = await saveContainerRows({\n record: nextContainer,\n tx,\n localUpdatedAt,\n });\n await clearDormantContainerMetadataInTransaction(tx, [container.id]);\n\n if (record) {\n await saveContainerMetadataRecord({\n containerId: container.id,\n record: {\n ...record,\n id: container.id,\n },\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (createIntent) {\n await saveContainerCreateIntent({\n containerId: container.id,\n createIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n if (moveIntent) {\n await saveContainerMoveIntent({\n containerId: container.id,\n moveIntent,\n tx,\n updatedAt: localUpdatedAt,\n });\n }\n\n return savedContainer;\n}\n\n/**\n * ONE joined statement, so the record's key/access fields and the checkpoint\n * content it pairs with come from a single consistent read — two separate\n * queries could interleave with a concurrent metadata save (record +\n * checkpoint written in one transaction) and pair stale keying state with\n * newer content.\n */\nexport async function selectContainerMetadataRecord(\n execSql: ExecSql,\n containerId: string,\n): Promise<ContainerMetadataRecord | null> {\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n ...documentRecordSelection,\n metadataUpdates: documentHistoryCheckpoints.snapshot,\n })\n .from(documents)\n .leftJoin(\n documentHistoryCheckpoints,\n and(\n eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documentHistoryCheckpoints.localId, documents.localId),\n ),\n )\n .where(\n and(\n eq(documents.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documents.localId, containerId),\n ),\n )\n .limit(1);\n const row = rows[0];\n if (!row) {\n return null;\n }\n const { metadataUpdates, ...recordRow } = row;\n return {\n ...mapSelectedDocumentRecord(recordRow),\n metadataUpdates: metadataUpdates ?? \"\",\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerRebindPersistence.js","sourceRoot":"","sources":["containerRebindPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"containerRebindPersistence.js","sourceRoot":"","sources":["containerRebindPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport {\n containers,\n documents,\n dormantContainerMetadata,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport type { HeldContainerBinding } from \"./containerBinding\";\nimport { loadStoredHeldContainerBinding } from \"./containerHydrationPersistence\";\nimport { CONTAINER_METADATA_APP_KIND } from \"./dormantContainerMetadata\";\nimport {\n isSupersededContainerBinding,\n recordSupersededContainerBindingsInTransaction,\n} from \"./supersededContainerBindings\";\n\nfunction sameBinding(\n current: HeldContainerBinding | null,\n expected: HeldContainerBinding,\n): boolean {\n return (\n current?.organizationId === expected.organizationId &&\n current.metadataDocumentId === expected.metadataDocumentId\n );\n}\n\n/**\n * Move a held folder to the binding its own user re-created it under: purged\n * organization recovery re-homes folders under their existing ids. Local\n * content and queued edits follow, as they do on the recovering device, and\n * the metadata record's remote stream starts over at the new target. An\n * already-applied re-home succeeds; any other change to the held binding since\n * it was read returns false and changes nothing.\n */\nexport async function rebindStoredHeldContainer(\n execSql: ExecSql,\n input: {\n containerId: string;\n expected: HeldContainerBinding;\n next: { metadataDocumentId: string; organizationId: string };\n stillCurrent?: (() => boolean) | undefined;\n },\n): Promise<boolean> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const outcome = await getClientSQLitePersistenceRuntime(\n lockedExecSql,\n ).guardedTransaction(\n async (tx) => {\n const current = await loadStoredHeldContainerBinding(\n lockedExecSql,\n input.containerId,\n );\n const { metadataDocumentId, organizationId } = input.next;\n // Another store over this database may have applied the same re-home.\n if (sameBinding(current, { metadataDocumentId, organizationId }))\n return true;\n if (!sameBinding(current, input.expected)) return false;\n // A purged organization never returns; binding back is a replay.\n if (\n await isSupersededContainerBinding(lockedExecSql, {\n containerId: input.containerId,\n organizationId,\n })\n )\n return false;\n const supersededAt = new Date().toISOString();\n if (input.expected.organizationId !== organizationId) {\n await recordSupersededContainerBindingsInTransaction(tx, {\n containerIds: [input.containerId],\n organizationId: input.expected.organizationId,\n supersededAt,\n });\n }\n // The old stream's clock cannot order listings of the new one.\n await tx\n .update(containers)\n .set({ metadataDocumentId, organizationId, serverUpdatedAt: null })\n .where(eq(containers.id, input.containerId))\n .run();\n await tx\n .update(dormantContainerMetadata)\n .set({ organizationId })\n .where(eq(dormantContainerMetadata.containerId, input.containerId))\n .run();\n await tx\n .update(documents)\n .set({\n accessEpoch: 1,\n accessStateHash: null,\n contentKeyBundle: null,\n documentId: metadataDocumentId,\n documentKekTargets: null,\n documentManifestBundle: null,\n lastCommitLsn: null,\n pullContinuation: null,\n updatedAt: supersededAt,\n })\n .where(\n and(\n eq(documents.appKind, CONTAINER_METADATA_APP_KIND),\n eq(documents.localId, input.containerId),\n ),\n )\n .run();\n return true;\n },\n () => !input.stillCurrent || input.stillCurrent(),\n { behavior: \"immediate\" },\n );\n return outcome.committed && outcome.result === true;\n });\n}\n"]}
|
|
@@ -2,10 +2,10 @@ import { ensureDocumentTables } from "../../sqlite/documentPersistence/index.js"
|
|
|
2
2
|
import { documentContainerProjectionTables, documentPlacementIntentTables, documentProjectionTables, } from "../../sqlite/schema/index.js";
|
|
3
3
|
import { getClientSQLitePersistenceRuntime } from "../../sqlite/sqlitePersistenceRuntime.js";
|
|
4
4
|
import { ensureSqlTables, runSerializedSqlMutation, } from "../../sqlite/sqlSchema.js";
|
|
5
|
-
import { ensureContainerTables } from "../containers/containerPersistence.js";
|
|
6
|
-
import { sqlContainerSyncWatermarkPersistence } from "../containers/containerSyncWatermarkPersistence.js";
|
|
7
5
|
import { reassignContainerDocumentsInTransaction } from "./containerDocumentReassignment.js";
|
|
6
|
+
import { ensureContainerTables } from "./containerPersistence.js";
|
|
8
7
|
import { deleteLocalContainerRows, reparentLocalContainerChildren, updateReparentedDescendantContainers, } from "./containerStructuralRepair.js";
|
|
8
|
+
import { sqlContainerSyncWatermarkPersistence } from "./containerSyncWatermarkPersistence.js";
|
|
9
9
|
export const containerReconcilePersistence = {
|
|
10
10
|
async reassignContainerDocuments(execSql, input) {
|
|
11
11
|
if (input.fromContainerId === input.toContainerId) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerReconcilePersistence.js","sourceRoot":"","sources":["containerReconcilePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { ensureDocumentTables } from \"../../sqlite/documentPersistence\";\nimport {\n documentContainerProjectionTables,\n documentPlacementIntentTables,\n documentProjectionTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport { ensureContainerTables } from \"../containers/containerPersistence\";\nimport { sqlContainerSyncWatermarkPersistence } from \"../containers/containerSyncWatermarkPersistence\";\nimport type { ContainerContentsPersistence } from \"./containerContentsPersistenceTypes\";\nimport { reassignContainerDocumentsInTransaction } from \"./containerDocumentReassignment\";\nimport {\n deleteLocalContainerRows,\n reparentLocalContainerChildren,\n updateReparentedDescendantContainers,\n} from \"./containerStructuralRepair\";\n\ntype ContainerReconcilePersistence = Pick<\n ContainerContentsPersistence,\n | \"reassignContainerDocuments\"\n | \"reconcileLocalRootContainer\"\n | \"reconcileLocalSystemContainer\"\n>;\n\nexport const containerReconcilePersistence: ContainerReconcilePersistence = {\n async reassignContainerDocuments(execSql, input) {\n if (input.fromContainerId === input.toContainerId) {\n return;\n }\n if (input.stillCurrent?.() === false) {\n return;\n }\n\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const updatedAt = input.updatedAt ?? new Date().toISOString();\n await ensureSqlTables(lockedExecSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...documentProjectionTables,\n ]);\n if (input.stillCurrent?.() === false) {\n return;\n }\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const reassign = async (\n tx: Parameters<typeof reassignContainerDocumentsInTransaction>[0][\"tx\"],\n ) => {\n await reassignContainerDocumentsInTransaction({\n fromContainerId: input.fromContainerId,\n toContainerId: input.toContainerId,\n tx,\n updatedAt,\n });\n };\n if (input.stillCurrent) {\n await runtime.guardedTransaction(reassign, input.stillCurrent);\n } else {\n await runtime.transaction(reassign);\n }\n });\n },\n async reconcileLocalRootContainer(execSql, input) {\n if (input.localRootContainerId === input.remoteRootContainerId) {\n return;\n }\n if (input.stillCurrent?.() === false) {\n return;\n }\n\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const updatedAt = input.updatedAt ?? new Date().toISOString();\n await ensureSqlTables(lockedExecSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...documentProjectionTables,\n ]);\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n if (input.stillCurrent?.() === false) {\n return;\n }\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const reconcile = async (\n tx: Parameters<typeof updateReparentedDescendantContainers>[0][\"tx\"],\n ) => {\n await updateReparentedDescendantContainers({\n descendantReparents: input.descendantReparents,\n remoteOrganizationId: input.remoteOrganizationId,\n tx,\n updatedAt,\n });\n await reassignContainerDocumentsInTransaction({\n fromContainerId: input.localRootContainerId,\n toContainerId: input.remoteRootContainerId,\n tx,\n updatedAt,\n });\n await deleteLocalContainerRows({\n containerId: input.localRootContainerId,\n tx,\n });\n };\n if (input.stillCurrent) {\n await runtime.guardedTransaction(reconcile, input.stillCurrent);\n } else {\n await runtime.transaction(reconcile);\n }\n });\n },\n async reconcileLocalSystemContainer(execSql, input) {\n if (input.localContainerId === input.remoteContainerId) {\n return;\n }\n if (input.stillCurrent?.() === false) {\n return;\n }\n\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const updatedAt = input.updatedAt ?? new Date().toISOString();\n await ensureSqlTables(lockedExecSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...documentProjectionTables,\n ]);\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n if (input.stillCurrent?.() === false) {\n return;\n }\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const reconcile = async (\n tx: Parameters<typeof reparentLocalContainerChildren>[0][\"tx\"],\n ) => {\n await reparentLocalContainerChildren({\n fromContainerId: input.localContainerId,\n remoteOrganizationId: input.remoteOrganizationId,\n toContainerId: input.remoteContainerId,\n tx,\n updatedAt,\n });\n await reassignContainerDocumentsInTransaction({\n fromContainerId: input.localContainerId,\n toContainerId: input.remoteContainerId,\n tx,\n updatedAt,\n });\n await deleteLocalContainerRows({\n containerId: input.localContainerId,\n tx,\n });\n };\n if (input.stillCurrent) {\n await runtime.guardedTransaction(reconcile, input.stillCurrent);\n } else {\n await runtime.transaction(reconcile);\n }\n });\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"containerReconcilePersistence.js","sourceRoot":"","sources":["containerReconcilePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { ensureDocumentTables } from \"../../sqlite/documentPersistence\";\nimport {\n documentContainerProjectionTables,\n documentPlacementIntentTables,\n documentProjectionTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport type { ContainerContentsPersistence } from \"./containerContentsPersistenceTypes\";\nimport { reassignContainerDocumentsInTransaction } from \"./containerDocumentReassignment\";\nimport { ensureContainerTables } from \"./containerPersistence\";\nimport {\n deleteLocalContainerRows,\n reparentLocalContainerChildren,\n updateReparentedDescendantContainers,\n} from \"./containerStructuralRepair\";\nimport { sqlContainerSyncWatermarkPersistence } from \"./containerSyncWatermarkPersistence\";\n\ntype ContainerReconcilePersistence = Pick<\n ContainerContentsPersistence,\n | \"reassignContainerDocuments\"\n | \"reconcileLocalRootContainer\"\n | \"reconcileLocalSystemContainer\"\n>;\n\nexport const containerReconcilePersistence: ContainerReconcilePersistence = {\n async reassignContainerDocuments(execSql, input) {\n if (input.fromContainerId === input.toContainerId) {\n return;\n }\n if (input.stillCurrent?.() === false) {\n return;\n }\n\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const updatedAt = input.updatedAt ?? new Date().toISOString();\n await ensureSqlTables(lockedExecSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...documentProjectionTables,\n ]);\n if (input.stillCurrent?.() === false) {\n return;\n }\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const reassign = async (\n tx: Parameters<typeof reassignContainerDocumentsInTransaction>[0][\"tx\"],\n ) => {\n await reassignContainerDocumentsInTransaction({\n fromContainerId: input.fromContainerId,\n toContainerId: input.toContainerId,\n tx,\n updatedAt,\n });\n };\n if (input.stillCurrent) {\n await runtime.guardedTransaction(reassign, input.stillCurrent);\n } else {\n await runtime.transaction(reassign);\n }\n });\n },\n async reconcileLocalRootContainer(execSql, input) {\n if (input.localRootContainerId === input.remoteRootContainerId) {\n return;\n }\n if (input.stillCurrent?.() === false) {\n return;\n }\n\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const updatedAt = input.updatedAt ?? new Date().toISOString();\n await ensureSqlTables(lockedExecSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...documentProjectionTables,\n ]);\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n if (input.stillCurrent?.() === false) {\n return;\n }\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const reconcile = async (\n tx: Parameters<typeof updateReparentedDescendantContainers>[0][\"tx\"],\n ) => {\n await updateReparentedDescendantContainers({\n descendantReparents: input.descendantReparents,\n remoteOrganizationId: input.remoteOrganizationId,\n tx,\n updatedAt,\n });\n await reassignContainerDocumentsInTransaction({\n fromContainerId: input.localRootContainerId,\n toContainerId: input.remoteRootContainerId,\n tx,\n updatedAt,\n });\n await deleteLocalContainerRows({\n containerId: input.localRootContainerId,\n tx,\n });\n };\n if (input.stillCurrent) {\n await runtime.guardedTransaction(reconcile, input.stillCurrent);\n } else {\n await runtime.transaction(reconcile);\n }\n });\n },\n async reconcileLocalSystemContainer(execSql, input) {\n if (input.localContainerId === input.remoteContainerId) {\n return;\n }\n if (input.stillCurrent?.() === false) {\n return;\n }\n\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const updatedAt = input.updatedAt ?? new Date().toISOString();\n await ensureSqlTables(lockedExecSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...documentProjectionTables,\n ]);\n await ensureContainerTables(lockedExecSql);\n await ensureDocumentTables(lockedExecSql);\n await sqlContainerSyncWatermarkPersistence.ensureSchema(lockedExecSql);\n if (input.stillCurrent?.() === false) {\n return;\n }\n const runtime = getClientSQLitePersistenceRuntime(lockedExecSql);\n const reconcile = async (\n tx: Parameters<typeof reparentLocalContainerChildren>[0][\"tx\"],\n ) => {\n await reparentLocalContainerChildren({\n fromContainerId: input.localContainerId,\n remoteOrganizationId: input.remoteOrganizationId,\n toContainerId: input.remoteContainerId,\n tx,\n updatedAt,\n });\n await reassignContainerDocumentsInTransaction({\n fromContainerId: input.localContainerId,\n toContainerId: input.remoteContainerId,\n tx,\n updatedAt,\n });\n await deleteLocalContainerRows({\n containerId: input.localContainerId,\n tx,\n });\n };\n if (input.stillCurrent) {\n await runtime.guardedTransaction(reconcile, input.stillCurrent);\n } else {\n await runtime.transaction(reconcile);\n }\n });\n },\n};\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { and, asc, eq, inArray } from "drizzle-orm";
|
|
2
2
|
import { containers, documentContainerProjection, documentProjection, inContainerIntentLanes, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../sqlite/schema/index.js";
|
|
3
|
-
import { deleteContainerWatermarksInTransaction } from "../containers/containerSyncWatermarkPersistence.js";
|
|
4
3
|
import { deleteContainerDocumentTombstoneHoldsForContainers } from "../documents/containerDocumentTombstoneHoldsPersistence.js";
|
|
5
4
|
import { getLatestTimestamp } from "../latestTimestamp.js";
|
|
6
5
|
import { saveContainerCreateIntent } from "./containerIntentPersistence.js";
|
|
6
|
+
import { deleteContainerWatermarksInTransaction } from "./containerSyncWatermarkPersistence.js";
|
|
7
7
|
import { deleteContainerMetadataDocumentRowsInTransaction } from "./dormantContainerMetadata.js";
|
|
8
8
|
// The containers being removed still exist when repair runs (deletion follows
|
|
9
9
|
// it). Capture their org attribution so a detached document's pending writes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerStructuralRepair.js","sourceRoot":"","sources":["containerStructuralRepair.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray } from \"drizzle-orm\";\nimport {\n containers,\n documentContainerProjection,\n documentProjection,\n inContainerIntentLanes,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { deleteContainerWatermarksInTransaction } from \"../containers/containerSyncWatermarkPersistence\";\nimport { deleteContainerDocumentTombstoneHoldsForContainers } from \"../documents/containerDocumentTombstoneHoldsPersistence\";\nimport { getLatestTimestamp } from \"../latestTimestamp\";\nimport type {\n ContainerRemoval,\n LocalRootDescendantReparentInput,\n} from \"./containerContentsPersistenceTypes\";\nimport { saveContainerCreateIntent } from \"./containerIntentPersistence\";\nimport { deleteContainerMetadataDocumentRowsInTransaction } from \"./dormantContainerMetadata\";\n\n// The containers being removed still exist when repair runs (deletion follows\n// it). Capture their org attribution so a detached document's pending writes\n// keep a resolvable organization id after the join source is gone — e.g. when\n// access to a shared org is revoked mid-sync.\nasync function loadOrganizationIdsForContainers(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<Map<string, string>> {\n const rows = await tx\n .select({\n id: containers.id,\n organizationId: containers.organizationId,\n })\n .from(containers)\n .where(inArray(containers.id, containerIds));\n const organizationIdByContainerId = new Map<string, string>();\n for (const container of rows) {\n if (container.id && container.organizationId) {\n organizationIdByContainerId.set(container.id, container.organizationId);\n }\n }\n return organizationIdByContainerId;\n}\n\n// A document unlinked from a removed container may keep other live links; its\n// projection then re-homes to the first remaining link (deterministic order)\n// instead of dropping to null.\nasync function loadFirstRemainingContainerIdByDocumentId(\n tx: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<Map<string, string>> {\n const firstRemainingContainerIdByDocumentId = new Map<string, string>();\n if (documentIds.length === 0) {\n return firstRemainingContainerIdByDocumentId;\n }\n const remainingLinks = await tx\n .select({\n containerId: documentContainerProjection.containerId,\n documentId: documentContainerProjection.documentId,\n })\n .from(documentContainerProjection)\n .where(inArray(documentContainerProjection.documentId, documentIds))\n .orderBy(\n asc(documentContainerProjection.documentId),\n asc(documentContainerProjection.containerId),\n );\n for (const link of remainingLinks) {\n if (!firstRemainingContainerIdByDocumentId.has(link.documentId)) {\n firstRemainingContainerIdByDocumentId.set(\n link.documentId,\n link.containerId,\n );\n }\n }\n return firstRemainingContainerIdByDocumentId;\n}\n\nexport async function repairDocumentsForRemovedContainersInTransaction(input: {\n removals: ReadonlyArray<ContainerRemoval>;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { tx } = input;\n const updatedAtByContainerId = new Map(\n input.removals.map((removal) => [removal.containerId, removal.updatedAt]),\n );\n const containerIds = Array.from(updatedAtByContainerId.keys());\n if (containerIds.length === 0) {\n return;\n }\n\n {\n const selectedRows = await tx\n .select({\n containerId: documentProjection.containerId,\n documentId: documentProjection.documentId,\n localId: documentProjection.localId,\n updatedAt: documentProjection.updatedAt,\n })\n .from(documentProjection)\n .where(inArray(documentProjection.containerId, containerIds));\n\n const organizationIdByContainerId = await loadOrganizationIdsForContainers(\n tx,\n containerIds,\n );\n\n await tx\n .delete(documentContainerProjection)\n .where(inArray(documentContainerProjection.containerId, containerIds))\n .run();\n await deleteContainerDocumentTombstoneHoldsForContainers(tx, containerIds);\n\n const documentIds = Array.from(\n new Set(\n selectedRows.flatMap((row) => (row.documentId ? [row.documentId] : [])),\n ),\n );\n const firstRemainingContainerIdByDocumentId =\n await loadFirstRemainingContainerIdByDocumentId(tx, documentIds);\n\n for (const row of selectedRows) {\n if (!row.localId) {\n continue;\n }\n\n const removedOrganizationId = row.containerId\n ? organizationIdByContainerId.get(row.containerId)\n : undefined;\n await tx\n .update(documentProjection)\n .set({\n containerId: row.documentId\n ? (firstRemainingContainerIdByDocumentId.get(row.documentId) ??\n null)\n : null,\n ...(removedOrganizationId\n ? { organizationId: removedOrganizationId }\n : {}),\n updatedAt: getLatestTimestamp(\n row.updatedAt,\n row.containerId\n ? (updatedAtByContainerId.get(row.containerId) ?? row.updatedAt)\n : row.updatedAt,\n ),\n })\n .where(eq(documentProjection.localId, row.localId))\n .run();\n }\n }\n}\n\nexport async function updateReparentedDescendantContainers(input: {\n descendantReparents: ReadonlyArray<LocalRootDescendantReparentInput>;\n remoteOrganizationId: string;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}): Promise<void> {\n const { descendantReparents, remoteOrganizationId, tx, updatedAt } = input;\n const descendantIds = Array.from(\n new Set(descendantReparents.map((reparent) => reparent.containerId)),\n );\n if (descendantIds.length === 0) {\n return;\n }\n\n await tx\n .update(containers)\n .set({\n organizationId: remoteOrganizationId,\n localUpdatedAt: updatedAt,\n })\n .where(inArray(containers.id, descendantIds))\n .run();\n\n for (const reparent of descendantReparents) {\n await tx\n .update(containers)\n .set({ parentId: reparent.parentContainerId })\n .where(eq(containers.id, reparent.containerId))\n .run();\n\n if (reparent.updateCreateIntent && reparent.parentContainerId) {\n await saveContainerCreateIntent({\n containerId: reparent.containerId,\n createIntent: { parentContainerId: reparent.parentContainerId },\n tx,\n updatedAt,\n });\n }\n\n if (reparent.parentContainerId) {\n await tx\n .update(localIntents)\n .set({\n id: crypto.randomUUID(),\n targetContainerId: reparent.parentContainerId,\n syncStatus: \"pending\",\n updatedAt,\n })\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerMove),\n eq(localIntents.subjectId, reparent.containerId),\n ),\n )\n .run();\n }\n }\n}\n\nexport async function reparentLocalContainerChildren(input: {\n fromContainerId: string;\n remoteOrganizationId: string;\n toContainerId: string;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}): Promise<void> {\n const {\n fromContainerId,\n remoteOrganizationId,\n toContainerId,\n tx,\n updatedAt,\n } = input;\n await tx\n .update(containers)\n .set({\n organizationId: remoteOrganizationId,\n parentId: toContainerId,\n localUpdatedAt: updatedAt,\n })\n .where(eq(containers.parentId, fromContainerId))\n .run();\n const createLane = inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate);\n const createIntentRows = await tx\n .select({ containerId: localIntents.subjectId })\n .from(localIntents)\n .where(and(createLane, eq(localIntents.targetContainerId, fromContainerId)))\n .all();\n for (const intent of createIntentRows) {\n await tx\n .update(localIntents)\n .set({\n id: crypto.randomUUID(),\n targetContainerId: toContainerId,\n updatedAt,\n })\n .where(and(createLane, eq(localIntents.subjectId, intent.containerId)))\n .run();\n }\n const moveLane = inLocalIntentLane(LOCAL_INTENT_LANES.containerMove);\n const moveIntentRows = await tx\n .select({ containerId: localIntents.subjectId })\n .from(localIntents)\n .where(and(moveLane, eq(localIntents.targetContainerId, fromContainerId)))\n .all();\n for (const intent of moveIntentRows) {\n await tx\n .update(localIntents)\n .set({\n id: crypto.randomUUID(),\n targetContainerId: toContainerId,\n syncStatus: \"pending\",\n updatedAt,\n })\n .where(and(moveLane, eq(localIntents.subjectId, intent.containerId)))\n .run();\n }\n}\n\nexport async function deleteLocalContainerRows(input: {\n containerId: string;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { containerId, tx } = input;\n await tx\n .delete(localIntents)\n .where(\n and(inContainerIntentLanes(), eq(localIntents.subjectId, containerId)),\n )\n .run();\n await tx.delete(containers).where(eq(containers.id, containerId)).run();\n await deleteContainerMetadataDocumentRowsInTransaction(tx, [containerId]);\n await deleteContainerWatermarksInTransaction(tx, [containerId]);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"containerStructuralRepair.js","sourceRoot":"","sources":["containerStructuralRepair.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray } from \"drizzle-orm\";\nimport {\n containers,\n documentContainerProjection,\n documentProjection,\n inContainerIntentLanes,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { deleteContainerDocumentTombstoneHoldsForContainers } from \"../documents/containerDocumentTombstoneHoldsPersistence\";\nimport { getLatestTimestamp } from \"../latestTimestamp\";\nimport type {\n ContainerRemoval,\n LocalRootDescendantReparentInput,\n} from \"./containerContentsPersistenceTypes\";\nimport { saveContainerCreateIntent } from \"./containerIntentPersistence\";\nimport { deleteContainerWatermarksInTransaction } from \"./containerSyncWatermarkPersistence\";\nimport { deleteContainerMetadataDocumentRowsInTransaction } from \"./dormantContainerMetadata\";\n\n// The containers being removed still exist when repair runs (deletion follows\n// it). Capture their org attribution so a detached document's pending writes\n// keep a resolvable organization id after the join source is gone — e.g. when\n// access to a shared org is revoked mid-sync.\nasync function loadOrganizationIdsForContainers(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<Map<string, string>> {\n const rows = await tx\n .select({\n id: containers.id,\n organizationId: containers.organizationId,\n })\n .from(containers)\n .where(inArray(containers.id, containerIds));\n const organizationIdByContainerId = new Map<string, string>();\n for (const container of rows) {\n if (container.id && container.organizationId) {\n organizationIdByContainerId.set(container.id, container.organizationId);\n }\n }\n return organizationIdByContainerId;\n}\n\n// A document unlinked from a removed container may keep other live links; its\n// projection then re-homes to the first remaining link (deterministic order)\n// instead of dropping to null.\nasync function loadFirstRemainingContainerIdByDocumentId(\n tx: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<Map<string, string>> {\n const firstRemainingContainerIdByDocumentId = new Map<string, string>();\n if (documentIds.length === 0) {\n return firstRemainingContainerIdByDocumentId;\n }\n const remainingLinks = await tx\n .select({\n containerId: documentContainerProjection.containerId,\n documentId: documentContainerProjection.documentId,\n })\n .from(documentContainerProjection)\n .where(inArray(documentContainerProjection.documentId, documentIds))\n .orderBy(\n asc(documentContainerProjection.documentId),\n asc(documentContainerProjection.containerId),\n );\n for (const link of remainingLinks) {\n if (!firstRemainingContainerIdByDocumentId.has(link.documentId)) {\n firstRemainingContainerIdByDocumentId.set(\n link.documentId,\n link.containerId,\n );\n }\n }\n return firstRemainingContainerIdByDocumentId;\n}\n\nexport async function repairDocumentsForRemovedContainersInTransaction(input: {\n removals: ReadonlyArray<ContainerRemoval>;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { tx } = input;\n const updatedAtByContainerId = new Map(\n input.removals.map((removal) => [removal.containerId, removal.updatedAt]),\n );\n const containerIds = Array.from(updatedAtByContainerId.keys());\n if (containerIds.length === 0) {\n return;\n }\n\n {\n const selectedRows = await tx\n .select({\n containerId: documentProjection.containerId,\n documentId: documentProjection.documentId,\n localId: documentProjection.localId,\n updatedAt: documentProjection.updatedAt,\n })\n .from(documentProjection)\n .where(inArray(documentProjection.containerId, containerIds));\n\n const organizationIdByContainerId = await loadOrganizationIdsForContainers(\n tx,\n containerIds,\n );\n\n await tx\n .delete(documentContainerProjection)\n .where(inArray(documentContainerProjection.containerId, containerIds))\n .run();\n await deleteContainerDocumentTombstoneHoldsForContainers(tx, containerIds);\n\n const documentIds = Array.from(\n new Set(\n selectedRows.flatMap((row) => (row.documentId ? [row.documentId] : [])),\n ),\n );\n const firstRemainingContainerIdByDocumentId =\n await loadFirstRemainingContainerIdByDocumentId(tx, documentIds);\n\n for (const row of selectedRows) {\n if (!row.localId) {\n continue;\n }\n\n const removedOrganizationId = row.containerId\n ? organizationIdByContainerId.get(row.containerId)\n : undefined;\n await tx\n .update(documentProjection)\n .set({\n containerId: row.documentId\n ? (firstRemainingContainerIdByDocumentId.get(row.documentId) ??\n null)\n : null,\n ...(removedOrganizationId\n ? { organizationId: removedOrganizationId }\n : {}),\n updatedAt: getLatestTimestamp(\n row.updatedAt,\n row.containerId\n ? (updatedAtByContainerId.get(row.containerId) ?? row.updatedAt)\n : row.updatedAt,\n ),\n })\n .where(eq(documentProjection.localId, row.localId))\n .run();\n }\n }\n}\n\nexport async function updateReparentedDescendantContainers(input: {\n descendantReparents: ReadonlyArray<LocalRootDescendantReparentInput>;\n remoteOrganizationId: string;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}): Promise<void> {\n const { descendantReparents, remoteOrganizationId, tx, updatedAt } = input;\n const descendantIds = Array.from(\n new Set(descendantReparents.map((reparent) => reparent.containerId)),\n );\n if (descendantIds.length === 0) {\n return;\n }\n\n await tx\n .update(containers)\n .set({\n organizationId: remoteOrganizationId,\n localUpdatedAt: updatedAt,\n })\n .where(inArray(containers.id, descendantIds))\n .run();\n\n for (const reparent of descendantReparents) {\n await tx\n .update(containers)\n .set({ parentId: reparent.parentContainerId })\n .where(eq(containers.id, reparent.containerId))\n .run();\n\n if (reparent.updateCreateIntent && reparent.parentContainerId) {\n await saveContainerCreateIntent({\n containerId: reparent.containerId,\n createIntent: { parentContainerId: reparent.parentContainerId },\n tx,\n updatedAt,\n });\n }\n\n if (reparent.parentContainerId) {\n await tx\n .update(localIntents)\n .set({\n id: crypto.randomUUID(),\n targetContainerId: reparent.parentContainerId,\n syncStatus: \"pending\",\n updatedAt,\n })\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.containerMove),\n eq(localIntents.subjectId, reparent.containerId),\n ),\n )\n .run();\n }\n }\n}\n\nexport async function reparentLocalContainerChildren(input: {\n fromContainerId: string;\n remoteOrganizationId: string;\n toContainerId: string;\n tx: ClientSQLiteTransactionScope;\n updatedAt: string;\n}): Promise<void> {\n const {\n fromContainerId,\n remoteOrganizationId,\n toContainerId,\n tx,\n updatedAt,\n } = input;\n await tx\n .update(containers)\n .set({\n organizationId: remoteOrganizationId,\n parentId: toContainerId,\n localUpdatedAt: updatedAt,\n })\n .where(eq(containers.parentId, fromContainerId))\n .run();\n const createLane = inLocalIntentLane(LOCAL_INTENT_LANES.containerCreate);\n const createIntentRows = await tx\n .select({ containerId: localIntents.subjectId })\n .from(localIntents)\n .where(and(createLane, eq(localIntents.targetContainerId, fromContainerId)))\n .all();\n for (const intent of createIntentRows) {\n await tx\n .update(localIntents)\n .set({\n id: crypto.randomUUID(),\n targetContainerId: toContainerId,\n updatedAt,\n })\n .where(and(createLane, eq(localIntents.subjectId, intent.containerId)))\n .run();\n }\n const moveLane = inLocalIntentLane(LOCAL_INTENT_LANES.containerMove);\n const moveIntentRows = await tx\n .select({ containerId: localIntents.subjectId })\n .from(localIntents)\n .where(and(moveLane, eq(localIntents.targetContainerId, fromContainerId)))\n .all();\n for (const intent of moveIntentRows) {\n await tx\n .update(localIntents)\n .set({\n id: crypto.randomUUID(),\n targetContainerId: toContainerId,\n syncStatus: \"pending\",\n updatedAt,\n })\n .where(and(moveLane, eq(localIntents.subjectId, intent.containerId)))\n .run();\n }\n}\n\nexport async function deleteLocalContainerRows(input: {\n containerId: string;\n tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const { containerId, tx } = input;\n await tx\n .delete(localIntents)\n .where(\n and(inContainerIntentLanes(), eq(localIntents.subjectId, containerId)),\n )\n .run();\n await tx.delete(containers).where(eq(containers.id, containerId)).run();\n await deleteContainerMetadataDocumentRowsInTransaction(tx, [containerId]);\n await deleteContainerWatermarksInTransaction(tx, [containerId]);\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { and, eq, sql } from "drizzle-orm";
|
|
2
2
|
import { dormantContainerMetadata, dormantMetadataSweepRequests, } from "../../sqlite/schema/index.js";
|
|
3
3
|
import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
|
|
4
|
-
import { ensureContainerTables } from "
|
|
4
|
+
import { ensureContainerTables } from "./containerPersistence.js";
|
|
5
5
|
export async function requestDormantMetadataRestorationSweeps(execSql, input) {
|
|
6
6
|
await ensureContainerTables(execSql);
|
|
7
7
|
const requestedAt = new Date().toISOString();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dormantMetadataSweep.js","sourceRoot":"","sources":["dormantMetadataSweep.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, sql } from \"drizzle-orm\";\nimport {\n dormantContainerMetadata,\n dormantMetadataSweepRequests,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { ensureContainerTables } from \"../containers/containerPersistence\";\n\nexport interface DormantMetadataSweepRequest {\n readonly attemptCount: number;\n readonly generation: number;\n readonly lastAttemptedAt: string | null;\n readonly organizationId: string;\n readonly requestedAt: string;\n readonly requesterUserId: string;\n}\n\nexport interface DormantMetadataSweepPersistence {\n claimDormantMetadataSweepAttempt: (\n execSql: ExecSql,\n sweep: DormantMetadataSweepRequest,\n attemptedAt: string,\n stillCurrent?: (() => boolean) | undefined,\n ) => Promise<boolean>;\n completeDormantMetadataSweepRequest: (\n execSql: ExecSql,\n sweep: DormantMetadataSweepRequest,\n stillCurrent?: (() => boolean) | undefined,\n ) => Promise<void>;\n listDormantMetadataSweepRequests: (\n execSql: ExecSql,\n requesterUserId: string,\n ) => Promise<readonly DormantMetadataSweepRequest[]>;\n}\n\nexport async function requestDormantMetadataRestorationSweeps(\n execSql: ExecSql,\n input: { requesterUserId: string },\n): Promise<number> {\n await ensureContainerTables(execSql);\n const requestedAt = new Date().toISOString();\n return getClientSQLitePersistenceRuntime(execSql).runMutation(async (db) => {\n const organizations = await db\n .selectDistinct({\n organizationId: dormantContainerMetadata.organizationId,\n })\n .from(dormantContainerMetadata)\n .orderBy(dormantContainerMetadata.organizationId);\n if (organizations.length === 0) {\n return 0;\n }\n\n await db\n .insert(dormantMetadataSweepRequests)\n .values(\n organizations.map(({ organizationId }) => ({\n attemptCount: 0,\n generation: 1,\n lastAttemptedAt: null,\n organizationId,\n requestedAt,\n requesterUserId: input.requesterUserId,\n })),\n )\n .onConflictDoUpdate({\n target: [\n dormantMetadataSweepRequests.organizationId,\n dormantMetadataSweepRequests.requesterUserId,\n ],\n set: {\n attemptCount: 0,\n generation: sql`${dormantMetadataSweepRequests.generation} + 1`,\n lastAttemptedAt: null,\n requestedAt: sql`excluded.requested_at`,\n },\n })\n .run();\n return organizations.length;\n });\n}\n\nexport async function listDormantMetadataSweepRequests(\n execSql: ExecSql,\n requesterUserId: string,\n): Promise<readonly DormantMetadataSweepRequest[]> {\n await ensureContainerTables(execSql);\n return getClientSQLitePersistenceRuntime(execSql)\n .db.select({\n attemptCount: dormantMetadataSweepRequests.attemptCount,\n generation: dormantMetadataSweepRequests.generation,\n lastAttemptedAt: dormantMetadataSweepRequests.lastAttemptedAt,\n organizationId: dormantMetadataSweepRequests.organizationId,\n requestedAt: dormantMetadataSweepRequests.requestedAt,\n requesterUserId: dormantMetadataSweepRequests.requesterUserId,\n })\n .from(dormantMetadataSweepRequests)\n .where(eq(dormantMetadataSweepRequests.requesterUserId, requesterUserId))\n .orderBy(dormantMetadataSweepRequests.organizationId);\n}\n\nexport async function claimDormantMetadataSweepAttempt(\n execSql: ExecSql,\n sweep: DormantMetadataSweepRequest,\n attemptedAt: string,\n stillCurrent?: (() => boolean) | undefined,\n): Promise<boolean> {\n await ensureContainerTables(execSql);\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const claim = async (tx: ClientSQLiteTransactionScope) => {\n const [current] = await tx\n .select({ attemptCount: dormantMetadataSweepRequests.attemptCount })\n .from(dormantMetadataSweepRequests)\n .where(\n and(\n eq(dormantMetadataSweepRequests.organizationId, sweep.organizationId),\n eq(\n dormantMetadataSweepRequests.requesterUserId,\n sweep.requesterUserId,\n ),\n eq(dormantMetadataSweepRequests.generation, sweep.generation),\n ),\n )\n .limit(1);\n if (!current || current.attemptCount !== sweep.attemptCount) {\n return false;\n }\n await tx\n .update(dormantMetadataSweepRequests)\n .set({\n attemptCount: current.attemptCount + 1,\n lastAttemptedAt: attemptedAt,\n })\n .where(\n and(\n eq(dormantMetadataSweepRequests.organizationId, sweep.organizationId),\n eq(\n dormantMetadataSweepRequests.requesterUserId,\n sweep.requesterUserId,\n ),\n eq(dormantMetadataSweepRequests.generation, sweep.generation),\n eq(dormantMetadataSweepRequests.attemptCount, current.attemptCount),\n ),\n )\n .run();\n return true;\n };\n if (!stillCurrent) {\n return runtime.transaction(claim);\n }\n const outcome = await runtime.guardedTransaction(claim, stillCurrent);\n return outcome.committed && outcome.result === true;\n}\n\nexport async function completeDormantMetadataSweepRequest(\n execSql: ExecSql,\n sweep: DormantMetadataSweepRequest,\n stillCurrent?: (() => boolean) | undefined,\n): Promise<void> {\n await ensureContainerTables(execSql);\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const complete = async (tx: ClientSQLiteTransactionScope) => {\n await tx\n .delete(dormantMetadataSweepRequests)\n .where(\n and(\n eq(dormantMetadataSweepRequests.organizationId, sweep.organizationId),\n eq(\n dormantMetadataSweepRequests.requesterUserId,\n sweep.requesterUserId,\n ),\n eq(dormantMetadataSweepRequests.generation, sweep.generation),\n ),\n )\n .run();\n };\n if (stillCurrent) {\n await runtime.guardedTransaction(complete, stillCurrent);\n return;\n }\n await runtime.transaction(complete);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dormantMetadataSweep.js","sourceRoot":"","sources":["dormantMetadataSweep.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, sql } from \"drizzle-orm\";\nimport {\n dormantContainerMetadata,\n dormantMetadataSweepRequests,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { ensureContainerTables } from \"./containerPersistence\";\n\nexport interface DormantMetadataSweepRequest {\n readonly attemptCount: number;\n readonly generation: number;\n readonly lastAttemptedAt: string | null;\n readonly organizationId: string;\n readonly requestedAt: string;\n readonly requesterUserId: string;\n}\n\nexport interface DormantMetadataSweepPersistence {\n claimDormantMetadataSweepAttempt: (\n execSql: ExecSql,\n sweep: DormantMetadataSweepRequest,\n attemptedAt: string,\n stillCurrent?: (() => boolean) | undefined,\n ) => Promise<boolean>;\n completeDormantMetadataSweepRequest: (\n execSql: ExecSql,\n sweep: DormantMetadataSweepRequest,\n stillCurrent?: (() => boolean) | undefined,\n ) => Promise<void>;\n listDormantMetadataSweepRequests: (\n execSql: ExecSql,\n requesterUserId: string,\n ) => Promise<readonly DormantMetadataSweepRequest[]>;\n}\n\nexport async function requestDormantMetadataRestorationSweeps(\n execSql: ExecSql,\n input: { requesterUserId: string },\n): Promise<number> {\n await ensureContainerTables(execSql);\n const requestedAt = new Date().toISOString();\n return getClientSQLitePersistenceRuntime(execSql).runMutation(async (db) => {\n const organizations = await db\n .selectDistinct({\n organizationId: dormantContainerMetadata.organizationId,\n })\n .from(dormantContainerMetadata)\n .orderBy(dormantContainerMetadata.organizationId);\n if (organizations.length === 0) {\n return 0;\n }\n\n await db\n .insert(dormantMetadataSweepRequests)\n .values(\n organizations.map(({ organizationId }) => ({\n attemptCount: 0,\n generation: 1,\n lastAttemptedAt: null,\n organizationId,\n requestedAt,\n requesterUserId: input.requesterUserId,\n })),\n )\n .onConflictDoUpdate({\n target: [\n dormantMetadataSweepRequests.organizationId,\n dormantMetadataSweepRequests.requesterUserId,\n ],\n set: {\n attemptCount: 0,\n generation: sql`${dormantMetadataSweepRequests.generation} + 1`,\n lastAttemptedAt: null,\n requestedAt: sql`excluded.requested_at`,\n },\n })\n .run();\n return organizations.length;\n });\n}\n\nexport async function listDormantMetadataSweepRequests(\n execSql: ExecSql,\n requesterUserId: string,\n): Promise<readonly DormantMetadataSweepRequest[]> {\n await ensureContainerTables(execSql);\n return getClientSQLitePersistenceRuntime(execSql)\n .db.select({\n attemptCount: dormantMetadataSweepRequests.attemptCount,\n generation: dormantMetadataSweepRequests.generation,\n lastAttemptedAt: dormantMetadataSweepRequests.lastAttemptedAt,\n organizationId: dormantMetadataSweepRequests.organizationId,\n requestedAt: dormantMetadataSweepRequests.requestedAt,\n requesterUserId: dormantMetadataSweepRequests.requesterUserId,\n })\n .from(dormantMetadataSweepRequests)\n .where(eq(dormantMetadataSweepRequests.requesterUserId, requesterUserId))\n .orderBy(dormantMetadataSweepRequests.organizationId);\n}\n\nexport async function claimDormantMetadataSweepAttempt(\n execSql: ExecSql,\n sweep: DormantMetadataSweepRequest,\n attemptedAt: string,\n stillCurrent?: (() => boolean) | undefined,\n): Promise<boolean> {\n await ensureContainerTables(execSql);\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const claim = async (tx: ClientSQLiteTransactionScope) => {\n const [current] = await tx\n .select({ attemptCount: dormantMetadataSweepRequests.attemptCount })\n .from(dormantMetadataSweepRequests)\n .where(\n and(\n eq(dormantMetadataSweepRequests.organizationId, sweep.organizationId),\n eq(\n dormantMetadataSweepRequests.requesterUserId,\n sweep.requesterUserId,\n ),\n eq(dormantMetadataSweepRequests.generation, sweep.generation),\n ),\n )\n .limit(1);\n if (!current || current.attemptCount !== sweep.attemptCount) {\n return false;\n }\n await tx\n .update(dormantMetadataSweepRequests)\n .set({\n attemptCount: current.attemptCount + 1,\n lastAttemptedAt: attemptedAt,\n })\n .where(\n and(\n eq(dormantMetadataSweepRequests.organizationId, sweep.organizationId),\n eq(\n dormantMetadataSweepRequests.requesterUserId,\n sweep.requesterUserId,\n ),\n eq(dormantMetadataSweepRequests.generation, sweep.generation),\n eq(dormantMetadataSweepRequests.attemptCount, current.attemptCount),\n ),\n )\n .run();\n return true;\n };\n if (!stillCurrent) {\n return runtime.transaction(claim);\n }\n const outcome = await runtime.guardedTransaction(claim, stillCurrent);\n return outcome.committed && outcome.result === true;\n}\n\nexport async function completeDormantMetadataSweepRequest(\n execSql: ExecSql,\n sweep: DormantMetadataSweepRequest,\n stillCurrent?: (() => boolean) | undefined,\n): Promise<void> {\n await ensureContainerTables(execSql);\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const complete = async (tx: ClientSQLiteTransactionScope) => {\n await tx\n .delete(dormantMetadataSweepRequests)\n .where(\n and(\n eq(dormantMetadataSweepRequests.organizationId, sweep.organizationId),\n eq(\n dormantMetadataSweepRequests.requesterUserId,\n sweep.requesterUserId,\n ),\n eq(dormantMetadataSweepRequests.generation, sweep.generation),\n ),\n )\n .run();\n };\n if (stillCurrent) {\n await runtime.guardedTransaction(complete, stillCurrent);\n return;\n }\n await runtime.transaction(complete);\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { and, asc, eq, inArray, sql } from "drizzle-orm";
|
|
2
2
|
import { containerDocumentTombstoneHolds, documentContainerProjection, documentProjection, } from "../../../sqlite/schema/index.js";
|
|
3
|
-
import { filterWritableDocumentPlacements } from "../../
|
|
3
|
+
import { filterWritableDocumentPlacements } from "../../container-contents/documentPlacement.js";
|
|
4
4
|
/** Keep `IN (...)` lists well under SQLite's bound-parameter limit. */
|
|
5
5
|
const HOLD_SQL_ID_BATCH_SIZE = 400;
|
|
6
6
|
function batches(values) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerDocumentTombstoneHolds.js","sourceRoot":"","sources":["containerDocumentTombstoneHolds.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray, sql } from \"drizzle-orm\";\nimport {\n containerDocumentTombstoneHolds,\n documentContainerProjection,\n documentProjection,\n} from \"../../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport { filterWritableDocumentPlacements } from \"../../containers/documentPlacement\";\nimport type { ContainerDocumentTombstoneInput } from \"../types\";\n\nexport interface ContainerDocumentPlacementKey {\n readonly containerId: string;\n readonly documentId: string;\n}\n\nexport type HeldContainerDocumentTombstone = Omit<\n ContainerDocumentTombstoneInput,\n \"accessEpoch\" | \"linkedContainerIds\"\n> & {\n /** Held without a verification attempt; the backoff does not advance. */\n readonly deferred?: boolean | undefined;\n /** A verified head still links it; retry the tombstone while keeping it visible. */\n readonly refuted?: boolean | undefined;\n};\n\n/** Keep `IN (...)` lists well under SQLite's bound-parameter limit. */\nconst HOLD_SQL_ID_BATCH_SIZE = 400;\n\nfunction batches<T>(values: ReadonlyArray<T>): T[][] {\n const unique = Array.from(new Set(values));\n const result: T[][] = [];\n for (let index = 0; index < unique.length; index += HOLD_SQL_ID_BATCH_SIZE) {\n result.push(unique.slice(index, index + HOLD_SQL_ID_BATCH_SIZE));\n }\n return result;\n}\n\nexport function containerDocumentPlacementKey(\n placement: ContainerDocumentPlacementKey,\n): string {\n return `${placement.documentId}\\u0000${placement.containerId}`;\n}\nconst placementKey = containerDocumentPlacementKey;\n\nexport async function deleteContainerDocumentTombstoneHoldRows(\n tx: ClientSQLiteTransactionScope,\n placements: ReadonlyArray<ContainerDocumentPlacementKey>,\n): Promise<void> {\n for (const placement of placements) {\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(\n and(\n eq(containerDocumentTombstoneHolds.documentId, placement.documentId),\n eq(\n containerDocumentTombstoneHolds.containerId,\n placement.containerId,\n ),\n ),\n )\n .run();\n }\n}\n\n/**\n * A local move takes ownership of a document's placement: the intent, not a\n * server listing, now says where it lives, so every hold that was hiding one\n * of its placements is released with the link rewrite.\n */\nexport async function deleteContainerDocumentTombstoneHoldsForDocuments(\n tx: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<void> {\n for (const batch of batches(documentIds)) {\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.documentId, batch))\n .run();\n }\n}\n\n/** Holds on containers whose link rows are being removed locally. */\nexport async function deleteContainerDocumentTombstoneHoldsForContainers(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n for (const batch of batches(containerIds)) {\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.containerId, batch))\n .run();\n }\n}\n\n/**\n * The placements among `placements` that exist locally: a link row, or a\n * document projection row whose primary container is the named container.\n * A tombstone for any other placement has nothing to remove or hide, so it\n * is not worth fetching and verifying the document head for.\n */\nexport async function listKnownContainerDocumentPlacementsInTransaction(\n tx: ClientSQLiteTransactionScope,\n placements: ReadonlyArray<ContainerDocumentPlacementKey>,\n): Promise<ContainerDocumentPlacementKey[]> {\n const known = new Set<string>();\n for (const batch of batches(placements.map((entry) => entry.documentId))) {\n const [linkRows, projectionRows] = await Promise.all([\n tx\n .select({\n containerId: documentContainerProjection.containerId,\n documentId: documentContainerProjection.documentId,\n })\n .from(documentContainerProjection)\n .where(inArray(documentContainerProjection.documentId, batch)),\n tx\n .select({\n containerId: documentProjection.containerId,\n documentId: documentProjection.documentId,\n })\n .from(documentProjection)\n .where(inArray(documentProjection.documentId, batch)),\n ]);\n for (const row of [...linkRows, ...projectionRows]) {\n if (row.documentId !== null && row.containerId !== null) {\n known.add(\n placementKey({\n containerId: row.containerId,\n documentId: row.documentId,\n }),\n );\n }\n }\n }\n return placements.filter((placement) => known.has(placementKey(placement)));\n}\n\n/**\n * Hold listing tombstones whose signed evidence is unavailable. A placement a\n * pending move intent owns is never held: the intent, not the listing, decides\n * where the document lives until it settles (`ProtectPendingTombstones`).\n */\nexport async function holdContainerDocumentTombstonesInTransaction(\n tx: ClientSQLiteTransactionScope,\n tombstones: ReadonlyArray<HeldContainerDocumentTombstone>,\n now: string,\n): Promise<void> {\n const writable = new Set(\n (\n await filterWritableDocumentPlacements(\n tx,\n tombstones.map((tombstone) => ({\n documentId: tombstone.documentId,\n containerIds: [],\n })),\n )\n ).map((input) => input.documentId),\n );\n for (const tombstone of tombstones) {\n if (!writable.has(tombstone.documentId)) continue;\n const row = {\n containerId: tombstone.containerId,\n documentId: tombstone.documentId,\n tombstonedAt: tombstone.updatedAt,\n hidden: !tombstone.refuted,\n updatedAt: now,\n };\n // A deferred hold was not attempted: it keeps its attempt count and its\n // last-attempt time, so it stays due and is not starved by the cap.\n await tx\n .insert(containerDocumentTombstoneHolds)\n .values({ ...row, attempts: tombstone.deferred ? 0 : 1 })\n .onConflictDoUpdate({\n target: [\n containerDocumentTombstoneHolds.documentId,\n containerDocumentTombstoneHolds.containerId,\n ],\n set: tombstone.deferred\n ? { tombstonedAt: row.tombstonedAt }\n : {\n ...row,\n hidden: tombstone.refuted\n ? false\n : containerDocumentTombstoneHolds.hidden,\n attempts: sql`${containerDocumentTombstoneHolds.attempts} + 1`,\n },\n })\n .run();\n }\n}\n\n/** Release the holds on placements a listing has re-asserted as linked. */\nexport async function deleteContainerDocumentTombstoneHoldRowsForLinks(\n tx: ClientSQLiteTransactionScope,\n links: ReadonlyArray<{\n readonly containerIds: ReadonlyArray<string>;\n readonly documentId: string;\n }>,\n): Promise<void> {\n for (const link of links) {\n for (const batch of batches(link.containerIds)) {\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(\n and(\n eq(containerDocumentTombstoneHolds.documentId, link.documentId),\n inArray(containerDocumentTombstoneHolds.containerId, batch),\n ),\n )\n .run();\n }\n }\n}\n\nexport interface ContainerDocumentTombstoneHoldRow\n extends ContainerDocumentPlacementKey {\n readonly attempts: number;\n readonly hidden: boolean;\n readonly tombstonedAt: string;\n readonly updatedAt: string;\n}\n\nconst holdRowSelection = {\n attempts: containerDocumentTombstoneHolds.attempts,\n hidden: containerDocumentTombstoneHolds.hidden,\n containerId: containerDocumentTombstoneHolds.containerId,\n documentId: containerDocumentTombstoneHolds.documentId,\n tombstonedAt: containerDocumentTombstoneHolds.tombstonedAt,\n updatedAt: containerDocumentTombstoneHolds.updatedAt,\n};\n\n/** Every hold on the given containers, including visible refuted retries. */\nexport async function listContainerDocumentTombstoneHoldsInTransaction(\n handle: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<ContainerDocumentTombstoneHoldRow[]> {\n const holds: ContainerDocumentTombstoneHoldRow[] = [];\n for (const batch of batches(containerIds)) {\n holds.push(\n ...(await handle\n .select(holdRowSelection)\n .from(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.containerId, batch))\n .orderBy(\n asc(containerDocumentTombstoneHolds.containerId),\n asc(containerDocumentTombstoneHolds.documentId),\n )),\n );\n }\n return holds;\n}\n\n/** Every hidden hold on the given documents, in any container. */\nexport async function listContainerDocumentTombstoneHoldsForDocumentsInTransaction(\n handle: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<ContainerDocumentTombstoneHoldRow[]> {\n const holds: ContainerDocumentTombstoneHoldRow[] = [];\n for (const batch of batches(documentIds)) {\n holds.push(\n ...(await handle\n .select(holdRowSelection)\n .from(containerDocumentTombstoneHolds)\n .where(\n and(\n inArray(containerDocumentTombstoneHolds.documentId, batch),\n eq(containerDocumentTombstoneHolds.hidden, true),\n ),\n )\n .orderBy(\n asc(containerDocumentTombstoneHolds.documentId),\n asc(containerDocumentTombstoneHolds.containerId),\n )),\n );\n }\n return holds;\n}\n\n/**\n * Split the holds on the given containers into those whose placement is gone\n * (the document was purged, reset, or relinked locally: nothing left to hide,\n * so the hold is dropped) and those still hiding a placement.\n */\nexport async function partitionContainerDocumentTombstoneHolds(\n handle: ClientSQLiteTransactionScope,\n holds: ReadonlyArray<ContainerDocumentTombstoneHoldRow>,\n): Promise<{\n live: ContainerDocumentTombstoneHoldRow[];\n orphaned: ContainerDocumentTombstoneHoldRow[];\n}> {\n const known = new Set(\n (\n await listKnownContainerDocumentPlacementsInTransaction(handle, holds)\n ).map(placementKey),\n );\n return {\n live: holds.filter((hold) => known.has(placementKey(hold))),\n orphaned: holds.filter((hold) => !known.has(placementKey(hold))),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"containerDocumentTombstoneHolds.js","sourceRoot":"","sources":["containerDocumentTombstoneHolds.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray, sql } from \"drizzle-orm\";\nimport {\n containerDocumentTombstoneHolds,\n documentContainerProjection,\n documentProjection,\n} from \"../../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport { filterWritableDocumentPlacements } from \"../../container-contents/documentPlacement\";\nimport type { ContainerDocumentTombstoneInput } from \"../types\";\n\nexport interface ContainerDocumentPlacementKey {\n readonly containerId: string;\n readonly documentId: string;\n}\n\nexport type HeldContainerDocumentTombstone = Omit<\n ContainerDocumentTombstoneInput,\n \"accessEpoch\" | \"linkedContainerIds\"\n> & {\n /** Held without a verification attempt; the backoff does not advance. */\n readonly deferred?: boolean | undefined;\n /** A verified head still links it; retry the tombstone while keeping it visible. */\n readonly refuted?: boolean | undefined;\n};\n\n/** Keep `IN (...)` lists well under SQLite's bound-parameter limit. */\nconst HOLD_SQL_ID_BATCH_SIZE = 400;\n\nfunction batches<T>(values: ReadonlyArray<T>): T[][] {\n const unique = Array.from(new Set(values));\n const result: T[][] = [];\n for (let index = 0; index < unique.length; index += HOLD_SQL_ID_BATCH_SIZE) {\n result.push(unique.slice(index, index + HOLD_SQL_ID_BATCH_SIZE));\n }\n return result;\n}\n\nexport function containerDocumentPlacementKey(\n placement: ContainerDocumentPlacementKey,\n): string {\n return `${placement.documentId}\\u0000${placement.containerId}`;\n}\nconst placementKey = containerDocumentPlacementKey;\n\nexport async function deleteContainerDocumentTombstoneHoldRows(\n tx: ClientSQLiteTransactionScope,\n placements: ReadonlyArray<ContainerDocumentPlacementKey>,\n): Promise<void> {\n for (const placement of placements) {\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(\n and(\n eq(containerDocumentTombstoneHolds.documentId, placement.documentId),\n eq(\n containerDocumentTombstoneHolds.containerId,\n placement.containerId,\n ),\n ),\n )\n .run();\n }\n}\n\n/**\n * A local move takes ownership of a document's placement: the intent, not a\n * server listing, now says where it lives, so every hold that was hiding one\n * of its placements is released with the link rewrite.\n */\nexport async function deleteContainerDocumentTombstoneHoldsForDocuments(\n tx: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<void> {\n for (const batch of batches(documentIds)) {\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.documentId, batch))\n .run();\n }\n}\n\n/** Holds on containers whose link rows are being removed locally. */\nexport async function deleteContainerDocumentTombstoneHoldsForContainers(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n for (const batch of batches(containerIds)) {\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.containerId, batch))\n .run();\n }\n}\n\n/**\n * The placements among `placements` that exist locally: a link row, or a\n * document projection row whose primary container is the named container.\n * A tombstone for any other placement has nothing to remove or hide, so it\n * is not worth fetching and verifying the document head for.\n */\nexport async function listKnownContainerDocumentPlacementsInTransaction(\n tx: ClientSQLiteTransactionScope,\n placements: ReadonlyArray<ContainerDocumentPlacementKey>,\n): Promise<ContainerDocumentPlacementKey[]> {\n const known = new Set<string>();\n for (const batch of batches(placements.map((entry) => entry.documentId))) {\n const [linkRows, projectionRows] = await Promise.all([\n tx\n .select({\n containerId: documentContainerProjection.containerId,\n documentId: documentContainerProjection.documentId,\n })\n .from(documentContainerProjection)\n .where(inArray(documentContainerProjection.documentId, batch)),\n tx\n .select({\n containerId: documentProjection.containerId,\n documentId: documentProjection.documentId,\n })\n .from(documentProjection)\n .where(inArray(documentProjection.documentId, batch)),\n ]);\n for (const row of [...linkRows, ...projectionRows]) {\n if (row.documentId !== null && row.containerId !== null) {\n known.add(\n placementKey({\n containerId: row.containerId,\n documentId: row.documentId,\n }),\n );\n }\n }\n }\n return placements.filter((placement) => known.has(placementKey(placement)));\n}\n\n/**\n * Hold listing tombstones whose signed evidence is unavailable. A placement a\n * pending move intent owns is never held: the intent, not the listing, decides\n * where the document lives until it settles (`ProtectPendingTombstones`).\n */\nexport async function holdContainerDocumentTombstonesInTransaction(\n tx: ClientSQLiteTransactionScope,\n tombstones: ReadonlyArray<HeldContainerDocumentTombstone>,\n now: string,\n): Promise<void> {\n const writable = new Set(\n (\n await filterWritableDocumentPlacements(\n tx,\n tombstones.map((tombstone) => ({\n documentId: tombstone.documentId,\n containerIds: [],\n })),\n )\n ).map((input) => input.documentId),\n );\n for (const tombstone of tombstones) {\n if (!writable.has(tombstone.documentId)) continue;\n const row = {\n containerId: tombstone.containerId,\n documentId: tombstone.documentId,\n tombstonedAt: tombstone.updatedAt,\n hidden: !tombstone.refuted,\n updatedAt: now,\n };\n // A deferred hold was not attempted: it keeps its attempt count and its\n // last-attempt time, so it stays due and is not starved by the cap.\n await tx\n .insert(containerDocumentTombstoneHolds)\n .values({ ...row, attempts: tombstone.deferred ? 0 : 1 })\n .onConflictDoUpdate({\n target: [\n containerDocumentTombstoneHolds.documentId,\n containerDocumentTombstoneHolds.containerId,\n ],\n set: tombstone.deferred\n ? { tombstonedAt: row.tombstonedAt }\n : {\n ...row,\n hidden: tombstone.refuted\n ? false\n : containerDocumentTombstoneHolds.hidden,\n attempts: sql`${containerDocumentTombstoneHolds.attempts} + 1`,\n },\n })\n .run();\n }\n}\n\n/** Release the holds on placements a listing has re-asserted as linked. */\nexport async function deleteContainerDocumentTombstoneHoldRowsForLinks(\n tx: ClientSQLiteTransactionScope,\n links: ReadonlyArray<{\n readonly containerIds: ReadonlyArray<string>;\n readonly documentId: string;\n }>,\n): Promise<void> {\n for (const link of links) {\n for (const batch of batches(link.containerIds)) {\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(\n and(\n eq(containerDocumentTombstoneHolds.documentId, link.documentId),\n inArray(containerDocumentTombstoneHolds.containerId, batch),\n ),\n )\n .run();\n }\n }\n}\n\nexport interface ContainerDocumentTombstoneHoldRow\n extends ContainerDocumentPlacementKey {\n readonly attempts: number;\n readonly hidden: boolean;\n readonly tombstonedAt: string;\n readonly updatedAt: string;\n}\n\nconst holdRowSelection = {\n attempts: containerDocumentTombstoneHolds.attempts,\n hidden: containerDocumentTombstoneHolds.hidden,\n containerId: containerDocumentTombstoneHolds.containerId,\n documentId: containerDocumentTombstoneHolds.documentId,\n tombstonedAt: containerDocumentTombstoneHolds.tombstonedAt,\n updatedAt: containerDocumentTombstoneHolds.updatedAt,\n};\n\n/** Every hold on the given containers, including visible refuted retries. */\nexport async function listContainerDocumentTombstoneHoldsInTransaction(\n handle: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<ContainerDocumentTombstoneHoldRow[]> {\n const holds: ContainerDocumentTombstoneHoldRow[] = [];\n for (const batch of batches(containerIds)) {\n holds.push(\n ...(await handle\n .select(holdRowSelection)\n .from(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.containerId, batch))\n .orderBy(\n asc(containerDocumentTombstoneHolds.containerId),\n asc(containerDocumentTombstoneHolds.documentId),\n )),\n );\n }\n return holds;\n}\n\n/** Every hidden hold on the given documents, in any container. */\nexport async function listContainerDocumentTombstoneHoldsForDocumentsInTransaction(\n handle: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<ContainerDocumentTombstoneHoldRow[]> {\n const holds: ContainerDocumentTombstoneHoldRow[] = [];\n for (const batch of batches(documentIds)) {\n holds.push(\n ...(await handle\n .select(holdRowSelection)\n .from(containerDocumentTombstoneHolds)\n .where(\n and(\n inArray(containerDocumentTombstoneHolds.documentId, batch),\n eq(containerDocumentTombstoneHolds.hidden, true),\n ),\n )\n .orderBy(\n asc(containerDocumentTombstoneHolds.documentId),\n asc(containerDocumentTombstoneHolds.containerId),\n )),\n );\n }\n return holds;\n}\n\n/**\n * Split the holds on the given containers into those whose placement is gone\n * (the document was purged, reset, or relinked locally: nothing left to hide,\n * so the hold is dropped) and those still hiding a placement.\n */\nexport async function partitionContainerDocumentTombstoneHolds(\n handle: ClientSQLiteTransactionScope,\n holds: ReadonlyArray<ContainerDocumentTombstoneHoldRow>,\n): Promise<{\n live: ContainerDocumentTombstoneHoldRow[];\n orphaned: ContainerDocumentTombstoneHoldRow[];\n}> {\n const known = new Set(\n (\n await listKnownContainerDocumentPlacementsInTransaction(handle, holds)\n ).map(placementKey),\n );\n return {\n live: holds.filter((hold) => known.has(placementKey(hold))),\n orphaned: holds.filter((hold) => !known.has(placementKey(hold))),\n };\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { and, asc, desc, eq, inArray, notInArray } from "drizzle-orm";
|
|
2
2
|
import { documentContainerProjection, documentProjection, documents, } from "../../../sqlite/schema/index.js";
|
|
3
3
|
import { getClientSQLitePersistenceRuntime, } from "../../../sqlite/sqlitePersistenceRuntime.js";
|
|
4
|
-
import { filterWritableDocumentPlacements } from "../../
|
|
4
|
+
import { filterWritableDocumentPlacements } from "../../container-contents/documentPlacement.js";
|
|
5
5
|
import { getLatestTimestamp } from "../../latestTimestamp.js";
|
|
6
6
|
import { DOCUMENTS_APP_KIND } from "./constants.js";
|
|
7
7
|
import { deleteContainerDocumentTombstoneHoldRows } from "./containerDocumentTombstoneHolds.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerDocumentTombstones.js","sourceRoot":"","sources":["containerDocumentTombstones.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, desc, eq, inArray, notInArray } from \"drizzle-orm\";\nimport {\n documentContainerProjection,\n documentProjection,\n documents,\n} from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../../sqlite/sqlSchema\";\nimport { filterWritableDocumentPlacements } from \"../../containers/documentPlacement\";\nimport { getLatestTimestamp } from \"../../latestTimestamp\";\nimport type { ContainerDocumentTombstoneInput } from \"../types\";\nimport { DOCUMENTS_APP_KIND } from \"./constants\";\nimport { deleteContainerDocumentTombstoneHoldRows } from \"./containerDocumentTombstoneHolds\";\nimport {\n documentSummaryJoin,\n documentSummarySelection,\n mapDocumentSummary,\n} from \"./documentProjectionRows\";\n\nfunction dedupeContainerDocumentTombstones(\n tombstones: ReadonlyArray<ContainerDocumentTombstoneInput>,\n): ContainerDocumentTombstoneInput[] {\n return Array.from(\n new Map(\n tombstones.map((tombstone) => [\n `${tombstone.documentId}\\u0000${tombstone.containerId}`,\n tombstone,\n ]),\n ).values(),\n );\n}\n\nfunction buildContainerDocumentTombstoneState(\n uniqueTombstones: ReadonlyArray<ContainerDocumentTombstoneInput>,\n): {\n removedContainerIdsByDocumentId: Map<string, Set<string>>;\n tombstoneUpdatedAtByDocumentId: Map<string, string>;\n verifiedLinkedContainerIdsByDocumentId: Map<string, ReadonlySet<string>>;\n} {\n const removedContainerIdsByDocumentId = new Map<string, Set<string>>();\n const tombstoneUpdatedAtByDocumentId = new Map<string, string>();\n const verifiedLinkedContainerIdsByDocumentId = new Map<\n string,\n ReadonlySet<string>\n >();\n\n for (const tombstone of uniqueTombstones) {\n verifiedLinkedContainerIdsByDocumentId.set(\n tombstone.documentId,\n new Set(tombstone.linkedContainerIds),\n );\n const removedContainerIds =\n removedContainerIdsByDocumentId.get(tombstone.documentId) ?? new Set();\n removedContainerIds.add(tombstone.containerId);\n removedContainerIdsByDocumentId.set(\n tombstone.documentId,\n removedContainerIds,\n );\n tombstoneUpdatedAtByDocumentId.set(\n tombstone.documentId,\n getLatestTimestamp(\n tombstoneUpdatedAtByDocumentId.get(tombstone.documentId),\n tombstone.updatedAt,\n ),\n );\n }\n\n return {\n removedContainerIdsByDocumentId,\n tombstoneUpdatedAtByDocumentId,\n verifiedLinkedContainerIdsByDocumentId,\n };\n}\n\n/**\n * Remove the tombstoned placement and, with the head now verified, every\n * remaining local link row the head does not link: those are listing-seeded\n * rows, and leaving one would keep showing the document in a container the\n * signed link set never named. Rows the head links are kept as they are.\n */\nasync function deleteContainerDocumentTombstoneRows(\n tx: ClientSQLiteTransactionScope,\n uniqueTombstones: ReadonlyArray<ContainerDocumentTombstoneInput>,\n verifiedLinkedContainerIdsByDocumentId: ReadonlyMap<\n string,\n ReadonlySet<string>\n >,\n): Promise<void> {\n for (const tombstone of uniqueTombstones) {\n await tx\n .delete(documentContainerProjection)\n .where(\n and(\n eq(documentContainerProjection.documentId, tombstone.documentId),\n eq(documentContainerProjection.containerId, tombstone.containerId),\n ),\n )\n .run();\n }\n for (const [documentId, linked] of verifiedLinkedContainerIdsByDocumentId) {\n await tx\n .delete(documentContainerProjection)\n .where(\n linked.size === 0\n ? eq(documentContainerProjection.documentId, documentId)\n : and(\n eq(documentContainerProjection.documentId, documentId),\n notInArray(documentContainerProjection.containerId, [...linked]),\n ),\n )\n .run();\n }\n}\n\n/**\n * The primary container after a removal: the first remaining local row the\n * verified head links. A remaining row the head does not link is a\n * listing-seeded row and never becomes the primary, so a listing cannot\n * re-home the document. With no such row the document is unplaced (`null`)\n * and reachable through orphan recovery, rather than pointed at a head\n * container this device may not be able to read.\n */\nfunction selectNextContainerId(\n remainingContainerIds: ReadonlyArray<string>,\n verifiedLinkedContainerIds: ReadonlySet<string>,\n): string | null {\n return (\n remainingContainerIds.find((containerId) =>\n verifiedLinkedContainerIds.has(containerId),\n ) ?? null\n );\n}\n\nasync function updateSelectedContainersForDocumentTombstones(input: {\n documentId: string;\n removedContainerIds: ReadonlySet<string>;\n tombstoneUpdatedAt: string | undefined;\n tx: ClientSQLiteTransactionScope;\n verifiedLinkedContainerIds: ReadonlySet<string>;\n}): Promise<string[]> {\n const { documentId, removedContainerIds, tombstoneUpdatedAt, tx } = input;\n // A server document can own more than one local projection row: identity\n // recovery legitimately rematerializes a second projection for the same\n // documentId before semantic convergence completes. The link delete above is\n // keyed by (documentId, containerId) and the container item view surfaces a\n // document by its primary container_id, so EVERY projection row still pointing\n // at a removed container must be repaired here — resolving a single localId\n // left a duplicate row stranded at the source, keeping a moved document\n // visible in the container it was unlinked from.\n const localIds = (\n await tx\n .select({ localId: documents.localId })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n eq(documents.documentId, documentId),\n ),\n )\n ).map((row) => row.localId);\n if (localIds.length === 0) {\n return [];\n }\n\n const projectionRows = await tx\n .select({\n localId: documentProjection.localId,\n containerId: documentProjection.containerId,\n updatedAt: documentProjection.updatedAt,\n })\n .from(documentProjection)\n .where(inArray(documentProjection.localId, localIds));\n // Every projection row whose primary container is tombstoned OR outside the\n // verified link set is repointed: a primary at a listing-seeded row would\n // otherwise keep showing the document in a container the head never named.\n const rowsAtRemovedContainer = projectionRows.flatMap((row) =>\n row.localId !== null &&\n row.containerId !== null &&\n (removedContainerIds.has(row.containerId) ||\n !input.verifiedLinkedContainerIds.has(row.containerId))\n ? [{ localId: row.localId, updatedAt: row.updatedAt ?? undefined }]\n : [],\n );\n if (rowsAtRemovedContainer.length === 0) {\n return [];\n }\n\n const remainingLinkRows = await tx\n .select({ containerId: documentContainerProjection.containerId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.documentId, documentId))\n .orderBy(asc(documentContainerProjection.containerId));\n const nextContainerId = selectNextContainerId(\n remainingLinkRows.map((row) => row.containerId),\n input.verifiedLinkedContainerIds,\n );\n\n const changedLocalIds: string[] = [];\n for (const row of rowsAtRemovedContainer) {\n await tx\n .update(documentProjection)\n .set({\n containerId: nextContainerId,\n updatedAt: getLatestTimestamp(row.updatedAt, tombstoneUpdatedAt),\n })\n .where(eq(documentProjection.localId, row.localId))\n .run();\n changedLocalIds.push(row.localId);\n }\n\n return changedLocalIds;\n}\n\nexport async function applyContainerDocumentTombstonesWithExec(\n execSql: ExecSql,\n tombstones: ReadonlyArray<ContainerDocumentTombstoneInput>,\n) {\n const uniqueTombstones = dedupeContainerDocumentTombstones(tombstones);\n if (uniqueTombstones.length === 0) {\n return [];\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n\n return db.transaction(async (tx) => {\n // Besides the pending-intent guard, the verified head must not be older\n // than local document state: a lagging or replayed head must never\n // delete rows a newer listing or settled move wrote (`CheckEpoch`).\n const writable = await filterWritableDocumentPlacements(\n tx,\n uniqueTombstones.map((tombstone) => ({\n accessEpoch: tombstone.accessEpoch,\n documentId: tombstone.documentId,\n containerIds: [],\n })),\n );\n const writableIds = new Set(writable.map((input) => input.documentId));\n const applicable = uniqueTombstones.filter((tombstone) =>\n writableIds.has(tombstone.documentId),\n );\n const {\n removedContainerIdsByDocumentId,\n tombstoneUpdatedAtByDocumentId,\n verifiedLinkedContainerIdsByDocumentId,\n } = buildContainerDocumentTombstoneState(applicable);\n await deleteContainerDocumentTombstoneRows(\n tx,\n applicable,\n verifiedLinkedContainerIdsByDocumentId,\n );\n await deleteContainerDocumentTombstoneHoldRows(tx, applicable);\n\n const changedLocalIds: string[] = [];\n for (const [\n documentId,\n removedContainerIds,\n ] of removedContainerIdsByDocumentId) {\n changedLocalIds.push(\n ...(await updateSelectedContainersForDocumentTombstones({\n documentId,\n removedContainerIds,\n tombstoneUpdatedAt: tombstoneUpdatedAtByDocumentId.get(documentId),\n tx,\n verifiedLinkedContainerIds:\n verifiedLinkedContainerIdsByDocumentId.get(documentId) ?? new Set(),\n })),\n );\n }\n\n if (changedLocalIds.length === 0) {\n return [];\n }\n\n const rows = await tx\n .select(documentSummarySelection)\n .from(documentProjection)\n .leftJoin(documents, documentSummaryJoin)\n .where(inArray(documentProjection.localId, changedLocalIds))\n .orderBy(\n desc(documentProjection.updatedAt),\n desc(documentProjection.localId),\n );\n\n return rows.map(mapDocumentSummary);\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"containerDocumentTombstones.js","sourceRoot":"","sources":["containerDocumentTombstones.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,SAAS,GACV,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,iCAAiC,GAClC,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAAE,gCAAgC,EAAE,MAAM,4CAA4C,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,wCAAwC,EAAE,MAAM,mCAAmC,CAAC;AAC7F,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAElC,SAAS,iCAAiC,CACxC,UAA0D;IAE1D,OAAO,KAAK,CAAC,IAAI,CACf,IAAI,GAAG,CACL,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC;QAC5B,GAAG,SAAS,CAAC,UAAU,SAAS,SAAS,CAAC,WAAW,EAAE;QACvD,SAAS;KACV,CAAC,CACH,CAAC,MAAM,EAAE,CACX,CAAC;AACJ,CAAC;AAED,SAAS,oCAAoC,CAC3C,gBAAgE;IAMhE,MAAM,+BAA+B,GAAG,IAAI,GAAG,EAAuB,CAAC;IACvE,MAAM,8BAA8B,GAAG,IAAI,GAAG,EAAkB,CAAC;IACjE,MAAM,sCAAsC,GAAG,IAAI,GAAG,EAGnD,CAAC;IAEJ,KAAK,MAAM,SAAS,IAAI,gBAAgB,EAAE,CAAC;QACzC,sCAAsC,CAAC,GAAG,CACxC,SAAS,CAAC,UAAU,EACpB,IAAI,GAAG,CAAC,SAAS,CAAC,kBAAkB,CAAC,CACtC,CAAC;QACF,MAAM,mBAAmB,GACvB,+BAA+B,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;QACzE,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC/C,+BAA+B,CAAC,GAAG,CACjC,SAAS,CAAC,UAAU,EACpB,mBAAmB,CACpB,CAAC;QACF,8BAA8B,CAAC,GAAG,CAChC,SAAS,CAAC,UAAU,EACpB,kBAAkB,CAChB,8BAA8B,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,EACxD,SAAS,CAAC,SAAS,CACpB,CACF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,+BAA+B;QAC/B,8BAA8B;QAC9B,sCAAsC;KACvC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,oCAAoC,CACjD,EAAgC,EAChC,gBAAgE,EAChE,sCAGC;IAED,KAAK,MAAM,SAAS,IAAI,gBAAgB,EAAE,CAAC;QACzC,MAAM,EAAE;aACL,MAAM,CAAC,2BAA2B,CAAC;aACnC,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,2BAA2B,CAAC,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC,EAChE,EAAE,CAAC,2BAA2B,CAAC,WAAW,EAAE,SAAS,CAAC,WAAW,CAAC,CACnE,CACF;aACA,GAAG,EAAE,CAAC;IACX,CAAC;IACD,KAAK,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,sCAAsC,EAAE,CAAC;QAC1E,MAAM,EAAE;aACL,MAAM,CAAC,2BAA2B,CAAC;aACnC,KAAK,CACJ,MAAM,CAAC,IAAI,KAAK,CAAC;YACf,CAAC,CAAC,EAAE,CAAC,2BAA2B,CAAC,UAAU,EAAE,UAAU,CAAC;YACxD,CAAC,CAAC,GAAG,CACD,EAAE,CAAC,2BAA2B,CAAC,UAAU,EAAE,UAAU,CAAC,EACtD,UAAU,CAAC,2BAA2B,CAAC,WAAW,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CACjE,CACN;aACA,GAAG,EAAE,CAAC;IACX,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,qBAAqB,CAC5B,qBAA4C,EAC5C,0BAA+C;IAE/C,OAAO,CACL,qBAAqB,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CACzC,0BAA0B,CAAC,GAAG,CAAC,WAAW,CAAC,CAC5C,IAAI,IAAI,CACV,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,6CAA6C,CAAC,KAM5D;IACC,MAAM,EAAE,UAAU,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;IAC1E,yEAAyE;IACzE,wEAAwE;IACxE,6EAA6E;IAC7E,4EAA4E;IAC5E,+EAA+E;IAC/E,4EAA4E;IAC5E,wEAAwE;IACxE,iDAAiD;IACjD,MAAM,QAAQ,GAAG,CACf,MAAM,EAAE;SACL,MAAM,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;SACtC,IAAI,CAAC,SAAS,CAAC;SACf,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,kBAAkB,CAAC,EACzC,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CACrC,CACF,CACJ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,EAAE;SAC5B,MAAM,CAAC;QACN,OAAO,EAAE,kBAAkB,CAAC,OAAO;QACnC,WAAW,EAAE,kBAAkB,CAAC,WAAW;QAC3C,SAAS,EAAE,kBAAkB,CAAC,SAAS;KACxC,CAAC;SACD,IAAI,CAAC,kBAAkB,CAAC;SACxB,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IACxD,4EAA4E;IAC5E,0EAA0E;IAC1E,2EAA2E;IAC3E,MAAM,sBAAsB,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAC5D,GAAG,CAAC,OAAO,KAAK,IAAI;QACpB,GAAG,CAAC,WAAW,KAAK,IAAI;QACxB,CAAC,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC;YACvC,CAAC,KAAK,CAAC,0BAA0B,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,SAAS,EAAE,CAAC;QACnE,CAAC,CAAC,EAAE,CACP,CAAC;IACF,IAAI,sBAAsB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,iBAAiB,GAAG,MAAM,EAAE;SAC/B,MAAM,CAAC,EAAE,WAAW,EAAE,2BAA2B,CAAC,WAAW,EAAE,CAAC;SAChE,IAAI,CAAC,2BAA2B,CAAC;SACjC,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;SAC7D,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,WAAW,CAAC,CAAC,CAAC;IACzD,MAAM,eAAe,GAAG,qBAAqB,CAC3C,iBAAiB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,EAC/C,KAAK,CAAC,0BAA0B,CACjC,CAAC;IAEF,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,KAAK,MAAM,GAAG,IAAI,sBAAsB,EAAE,CAAC;QACzC,MAAM,EAAE;aACL,MAAM,CAAC,kBAAkB,CAAC;aAC1B,GAAG,CAAC;YACH,WAAW,EAAE,eAAe;YAC5B,SAAS,EAAE,kBAAkB,CAAC,GAAG,CAAC,SAAS,EAAE,kBAAkB,CAAC;SACjE,CAAC;aACD,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;aAClD,GAAG,EAAE,CAAC;QACT,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wCAAwC,CAC5D,OAAgB,EAChB,UAA0D;IAE1D,MAAM,gBAAgB,GAAG,iCAAiC,CAAC,UAAU,CAAC,CAAC;IACvE,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;IAE1D,OAAO,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACjC,wEAAwE;QACxE,mEAAmE;QACnE,oEAAoE;QACpE,MAAM,QAAQ,GAAG,MAAM,gCAAgC,CACrD,EAAE,EACF,gBAAgB,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACnC,WAAW,EAAE,SAAS,CAAC,WAAW;YAClC,UAAU,EAAE,SAAS,CAAC,UAAU;YAChC,YAAY,EAAE,EAAE;SACjB,CAAC,CAAC,CACJ,CAAC;QACF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;QACvE,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CACvD,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CACtC,CAAC;QACF,MAAM,EACJ,+BAA+B,EAC/B,8BAA8B,EAC9B,sCAAsC,GACvC,GAAG,oCAAoC,CAAC,UAAU,CAAC,CAAC;QACrD,MAAM,oCAAoC,CACxC,EAAE,EACF,UAAU,EACV,sCAAsC,CACvC,CAAC;QACF,MAAM,wCAAwC,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QAE/D,MAAM,eAAe,GAAa,EAAE,CAAC;QACrC,KAAK,MAAM,CACT,UAAU,EACV,mBAAmB,EACpB,IAAI,+BAA+B,EAAE,CAAC;YACrC,eAAe,CAAC,IAAI,CAClB,GAAG,CAAC,MAAM,6CAA6C,CAAC;gBACtD,UAAU;gBACV,mBAAmB;gBACnB,kBAAkB,EAAE,8BAA8B,CAAC,GAAG,CAAC,UAAU,CAAC;gBAClE,EAAE;gBACF,0BAA0B,EACxB,sCAAsC,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,GAAG,EAAE;aACtE,CAAC,CAAC,CACJ,CAAC;QACJ,CAAC;QAED,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,EAAE;aAClB,MAAM,CAAC,wBAAwB,CAAC;aAChC,IAAI,CAAC,kBAAkB,CAAC;aACxB,QAAQ,CAAC,SAAS,EAAE,mBAAmB,CAAC;aACxC,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;aAC3D,OAAO,CACN,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAClC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CACjC,CAAC;QAEJ,OAAO,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { and, asc, desc, eq, inArray, notInArray } from \"drizzle-orm\";\nimport {\n documentContainerProjection,\n documentProjection,\n documents,\n} from \"../../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../../sqlite/sqlSchema\";\nimport { filterWritableDocumentPlacements } from \"../../container-contents/documentPlacement\";\nimport { getLatestTimestamp } from \"../../latestTimestamp\";\nimport type { ContainerDocumentTombstoneInput } from \"../types\";\nimport { DOCUMENTS_APP_KIND } from \"./constants\";\nimport { deleteContainerDocumentTombstoneHoldRows } from \"./containerDocumentTombstoneHolds\";\nimport {\n documentSummaryJoin,\n documentSummarySelection,\n mapDocumentSummary,\n} from \"./documentProjectionRows\";\n\nfunction dedupeContainerDocumentTombstones(\n tombstones: ReadonlyArray<ContainerDocumentTombstoneInput>,\n): ContainerDocumentTombstoneInput[] {\n return Array.from(\n new Map(\n tombstones.map((tombstone) => [\n `${tombstone.documentId}\\u0000${tombstone.containerId}`,\n tombstone,\n ]),\n ).values(),\n );\n}\n\nfunction buildContainerDocumentTombstoneState(\n uniqueTombstones: ReadonlyArray<ContainerDocumentTombstoneInput>,\n): {\n removedContainerIdsByDocumentId: Map<string, Set<string>>;\n tombstoneUpdatedAtByDocumentId: Map<string, string>;\n verifiedLinkedContainerIdsByDocumentId: Map<string, ReadonlySet<string>>;\n} {\n const removedContainerIdsByDocumentId = new Map<string, Set<string>>();\n const tombstoneUpdatedAtByDocumentId = new Map<string, string>();\n const verifiedLinkedContainerIdsByDocumentId = new Map<\n string,\n ReadonlySet<string>\n >();\n\n for (const tombstone of uniqueTombstones) {\n verifiedLinkedContainerIdsByDocumentId.set(\n tombstone.documentId,\n new Set(tombstone.linkedContainerIds),\n );\n const removedContainerIds =\n removedContainerIdsByDocumentId.get(tombstone.documentId) ?? new Set();\n removedContainerIds.add(tombstone.containerId);\n removedContainerIdsByDocumentId.set(\n tombstone.documentId,\n removedContainerIds,\n );\n tombstoneUpdatedAtByDocumentId.set(\n tombstone.documentId,\n getLatestTimestamp(\n tombstoneUpdatedAtByDocumentId.get(tombstone.documentId),\n tombstone.updatedAt,\n ),\n );\n }\n\n return {\n removedContainerIdsByDocumentId,\n tombstoneUpdatedAtByDocumentId,\n verifiedLinkedContainerIdsByDocumentId,\n };\n}\n\n/**\n * Remove the tombstoned placement and, with the head now verified, every\n * remaining local link row the head does not link: those are listing-seeded\n * rows, and leaving one would keep showing the document in a container the\n * signed link set never named. Rows the head links are kept as they are.\n */\nasync function deleteContainerDocumentTombstoneRows(\n tx: ClientSQLiteTransactionScope,\n uniqueTombstones: ReadonlyArray<ContainerDocumentTombstoneInput>,\n verifiedLinkedContainerIdsByDocumentId: ReadonlyMap<\n string,\n ReadonlySet<string>\n >,\n): Promise<void> {\n for (const tombstone of uniqueTombstones) {\n await tx\n .delete(documentContainerProjection)\n .where(\n and(\n eq(documentContainerProjection.documentId, tombstone.documentId),\n eq(documentContainerProjection.containerId, tombstone.containerId),\n ),\n )\n .run();\n }\n for (const [documentId, linked] of verifiedLinkedContainerIdsByDocumentId) {\n await tx\n .delete(documentContainerProjection)\n .where(\n linked.size === 0\n ? eq(documentContainerProjection.documentId, documentId)\n : and(\n eq(documentContainerProjection.documentId, documentId),\n notInArray(documentContainerProjection.containerId, [...linked]),\n ),\n )\n .run();\n }\n}\n\n/**\n * The primary container after a removal: the first remaining local row the\n * verified head links. A remaining row the head does not link is a\n * listing-seeded row and never becomes the primary, so a listing cannot\n * re-home the document. With no such row the document is unplaced (`null`)\n * and reachable through orphan recovery, rather than pointed at a head\n * container this device may not be able to read.\n */\nfunction selectNextContainerId(\n remainingContainerIds: ReadonlyArray<string>,\n verifiedLinkedContainerIds: ReadonlySet<string>,\n): string | null {\n return (\n remainingContainerIds.find((containerId) =>\n verifiedLinkedContainerIds.has(containerId),\n ) ?? null\n );\n}\n\nasync function updateSelectedContainersForDocumentTombstones(input: {\n documentId: string;\n removedContainerIds: ReadonlySet<string>;\n tombstoneUpdatedAt: string | undefined;\n tx: ClientSQLiteTransactionScope;\n verifiedLinkedContainerIds: ReadonlySet<string>;\n}): Promise<string[]> {\n const { documentId, removedContainerIds, tombstoneUpdatedAt, tx } = input;\n // A server document can own more than one local projection row: identity\n // recovery legitimately rematerializes a second projection for the same\n // documentId before semantic convergence completes. The link delete above is\n // keyed by (documentId, containerId) and the container item view surfaces a\n // document by its primary container_id, so EVERY projection row still pointing\n // at a removed container must be repaired here — resolving a single localId\n // left a duplicate row stranded at the source, keeping a moved document\n // visible in the container it was unlinked from.\n const localIds = (\n await tx\n .select({ localId: documents.localId })\n .from(documents)\n .where(\n and(\n eq(documents.appKind, DOCUMENTS_APP_KIND),\n eq(documents.documentId, documentId),\n ),\n )\n ).map((row) => row.localId);\n if (localIds.length === 0) {\n return [];\n }\n\n const projectionRows = await tx\n .select({\n localId: documentProjection.localId,\n containerId: documentProjection.containerId,\n updatedAt: documentProjection.updatedAt,\n })\n .from(documentProjection)\n .where(inArray(documentProjection.localId, localIds));\n // Every projection row whose primary container is tombstoned OR outside the\n // verified link set is repointed: a primary at a listing-seeded row would\n // otherwise keep showing the document in a container the head never named.\n const rowsAtRemovedContainer = projectionRows.flatMap((row) =>\n row.localId !== null &&\n row.containerId !== null &&\n (removedContainerIds.has(row.containerId) ||\n !input.verifiedLinkedContainerIds.has(row.containerId))\n ? [{ localId: row.localId, updatedAt: row.updatedAt ?? undefined }]\n : [],\n );\n if (rowsAtRemovedContainer.length === 0) {\n return [];\n }\n\n const remainingLinkRows = await tx\n .select({ containerId: documentContainerProjection.containerId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.documentId, documentId))\n .orderBy(asc(documentContainerProjection.containerId));\n const nextContainerId = selectNextContainerId(\n remainingLinkRows.map((row) => row.containerId),\n input.verifiedLinkedContainerIds,\n );\n\n const changedLocalIds: string[] = [];\n for (const row of rowsAtRemovedContainer) {\n await tx\n .update(documentProjection)\n .set({\n containerId: nextContainerId,\n updatedAt: getLatestTimestamp(row.updatedAt, tombstoneUpdatedAt),\n })\n .where(eq(documentProjection.localId, row.localId))\n .run();\n changedLocalIds.push(row.localId);\n }\n\n return changedLocalIds;\n}\n\nexport async function applyContainerDocumentTombstonesWithExec(\n execSql: ExecSql,\n tombstones: ReadonlyArray<ContainerDocumentTombstoneInput>,\n) {\n const uniqueTombstones = dedupeContainerDocumentTombstones(tombstones);\n if (uniqueTombstones.length === 0) {\n return [];\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n\n return db.transaction(async (tx) => {\n // Besides the pending-intent guard, the verified head must not be older\n // than local document state: a lagging or replayed head must never\n // delete rows a newer listing or settled move wrote (`CheckEpoch`).\n const writable = await filterWritableDocumentPlacements(\n tx,\n uniqueTombstones.map((tombstone) => ({\n accessEpoch: tombstone.accessEpoch,\n documentId: tombstone.documentId,\n containerIds: [],\n })),\n );\n const writableIds = new Set(writable.map((input) => input.documentId));\n const applicable = uniqueTombstones.filter((tombstone) =>\n writableIds.has(tombstone.documentId),\n );\n const {\n removedContainerIdsByDocumentId,\n tombstoneUpdatedAtByDocumentId,\n verifiedLinkedContainerIdsByDocumentId,\n } = buildContainerDocumentTombstoneState(applicable);\n await deleteContainerDocumentTombstoneRows(\n tx,\n applicable,\n verifiedLinkedContainerIdsByDocumentId,\n );\n await deleteContainerDocumentTombstoneHoldRows(tx, applicable);\n\n const changedLocalIds: string[] = [];\n for (const [\n documentId,\n removedContainerIds,\n ] of removedContainerIdsByDocumentId) {\n changedLocalIds.push(\n ...(await updateSelectedContainersForDocumentTombstones({\n documentId,\n removedContainerIds,\n tombstoneUpdatedAt: tombstoneUpdatedAtByDocumentId.get(documentId),\n tx,\n verifiedLinkedContainerIds:\n verifiedLinkedContainerIdsByDocumentId.get(documentId) ?? new Set(),\n })),\n );\n }\n\n if (changedLocalIds.length === 0) {\n return [];\n }\n\n const rows = await tx\n .select(documentSummarySelection)\n .from(documentProjection)\n .leftJoin(documents, documentSummaryJoin)\n .where(inArray(documentProjection.localId, changedLocalIds))\n .orderBy(\n desc(documentProjection.updatedAt),\n desc(documentProjection.localId),\n );\n\n return rows.map(mapDocumentSummary);\n });\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_DOCUMENT_ACCESS_EPOCH } from "../../../documents/documentConstants.js";
|
|
2
|
-
import { loadDocumentMovePlacement } from "../../
|
|
2
|
+
import { loadDocumentMovePlacement } from "../../container-contents/documentPlacement.js";
|
|
3
3
|
import { resolvePersistedAccessStateHash } from "./documentRuntimeState.js";
|
|
4
4
|
export async function resolveDiscoveredDocumentPlacement(tx, existing, input) {
|
|
5
5
|
const intent = await loadDocumentMovePlacement(tx, input.documentId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discoveredDocumentPlacement.js","sourceRoot":"","sources":["discoveredDocumentPlacement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,sCAAsC,CAAC;AAGrF,OAAO,EAAE,yBAAyB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"discoveredDocumentPlacement.js","sourceRoot":"","sources":["discoveredDocumentPlacement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,sCAAsC,CAAC;AAGrF,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AAEvF,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC;AAEzE,MAAM,CAAC,KAAK,UAAU,kCAAkC,CACtD,EAAgC,EAChC,QAAqC,EACrC,KAA8B;IAE9B,MAAM,MAAM,GAAG,MAAM,yBAAyB,CAAC,EAAE,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACrE,MAAM,KAAK,GAAG,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;IAC5E,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAC1B,QAAQ,EAAE,WAAW,IAAI,6BAA6B,EACtD,KAAK,CAAC,WAAW,CAClB,CAAC;IACF,MAAM,kBAAkB,GACtB,MAAM,EAAE,UAAU,KAAK,eAAe;QACpC,CAAC,CAAC,CAAC,QAAQ,EAAE,WAAW,IAAI,MAAM,CAAC,iBAAiB,CAAC;QACrD,CAAC,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAChC,MAAM,WAAW,GACf,kBAAkB;QAClB,CAAC,KAAK;YACJ,CAAC,CAAC,QAAQ,CAAC,WAAW;YACtB,CAAC,CAAC,QAAQ,EAAE,WAAW;gBACnB,KAAK,CAAC,kBAAkB,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC;gBACzD,CAAC,CAAC,QAAQ,CAAC,WAAW;gBACtB,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,WAAW,CAAC;oBAChE,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC;oBAC3B,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;IAC5B,OAAO;QACL,WAAW;QACX,WAAW;QACX,eAAe,EAAE,+BAA+B,CAAC,QAAQ,EAAE;YACzD,WAAW;YACX,eAAe,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe;YACzE,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B,CAAC;KACH,CAAC;AACJ,CAAC","sourcesContent":["import { DEFAULT_DOCUMENT_ACCESS_EPOCH } from \"../../../documents/documentConstants\";\nimport type { DiscoveredDocumentInput } from \"../../../documents/documentSummary\";\nimport type { ClientSQLiteTransactionScope } from \"../../../sqlite/sqlitePersistenceRuntime\";\nimport { loadDocumentMovePlacement } from \"../../container-contents/documentPlacement\";\nimport type { StoredDocumentRecord } from \"../types\";\nimport { resolvePersistedAccessStateHash } from \"./documentRuntimeState\";\n\nexport async function resolveDiscoveredDocumentPlacement(\n tx: ClientSQLiteTransactionScope,\n existing: StoredDocumentRecord | null,\n input: DiscoveredDocumentInput,\n) {\n const intent = await loadDocumentMovePlacement(tx, input.documentId);\n const stale = existing !== null && existing.accessEpoch > input.accessEpoch;\n const accessEpoch = Math.max(\n existing?.accessEpoch ?? DEFAULT_DOCUMENT_ACCESS_EPOCH,\n input.accessEpoch,\n );\n const pendingContainerId =\n intent?.intentType === \"document.link\"\n ? (existing?.containerId ?? intent.targetContainerId)\n : intent?.targetContainerId;\n const containerId =\n pendingContainerId ??\n (stale\n ? existing.containerId\n : existing?.containerId &&\n input.linkedContainerIds.includes(existing.containerId)\n ? existing.containerId\n : (input.linkedContainerIds.find((id) => id === input.containerId) ??\n input.linkedContainerIds[0] ??\n input.containerId));\n return {\n accessEpoch,\n containerId,\n accessStateHash: resolvePersistedAccessStateHash(existing, {\n accessEpoch,\n accessStateHash: stale ? existing.accessStateHash : input.accessStateHash,\n documentId: input.documentId,\n }),\n };\n}\n"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ReferencedPrincipalHead, VerifiedPrincipalPolicyCurrent } from "../../internal/crypto/index.js";
|
|
2
|
+
/** Privately authenticated historical citations selected for one authored head. */
|
|
3
|
+
export type CurrentPolicyReferenceResolver = (current: VerifiedPrincipalPolicyCurrent, references: readonly ReferencedPrincipalHead[]) => Promise<VerifiedPrincipalPolicyCurrent>;
|
package/index.d.ts
CHANGED
|
@@ -16,11 +16,11 @@ export { blobByteSourceInputLength, createBlobByteSource, createBlobStore, creat
|
|
|
16
16
|
export type { ContainerContentsRootAdopter, ContainerContentsRootAdoptionInput, ContainerContentsStoreWorkflowRuntime, ContainerContentsWorkflowRuntime, ContainerDocumentLinkInput as ContainerDocumentQueriesLinkInput, ContainerSystemSlotDefinition, PendingDownloadQueueItem, RecoveryFolder, } from "./workflows/container-contents/index.js";
|
|
17
17
|
export { createContainerContentsDocumentsRuntime, createContainerContentsProjectionUserKeyResolver, createContainerContentsStoreWorkflowRuntime, createContainerContentsSyncLane, createContainerContentsWorkflowRuntime, createContainerParentSyncLane, createInitializedContainerMetadataDocument, defaultContainerContentsPersistence, deriveContainerSystemSlot, initializeDocumentLinksSchema, listBlobInfo, listDocumentLinkedContainerIds, loadContainerInfo, loadContainerSyncWatermark, loadDocumentAttributionRanges, loadDocumentInfo, replaceDocumentLinks, saveContainerSyncWatermark, syncedContainerDocumentObjectSyncState, } from "./workflows/container-contents/index.js";
|
|
18
18
|
export type { AggregatedContainerKekLog, ContainerMutationAuthor, ContainerReciteApi, ContainerWriteRefusal, KeyringRebuildResult, MaterializedContainerRekeyPlan, } from "./workflows/containers/index.js";
|
|
19
|
-
export { buildMaterializedContainerRekeyPlan, buildRootContainerCreatePlan, ContainerAuthorAccessError, ContainerKekRepairInaccessibleError, ContainerKekRepairRequiredError, ContainerKeyringOverrideStaleError, ContainerPathTooDeepError, classifyContainerWriteRefusal, containerWriterProjectionFromRekeyPlan, createRemoteContainer, fetchContainerKekLog,
|
|
19
|
+
export { buildMaterializedContainerRekeyPlan, buildRootContainerCreatePlan, ContainerAuthorAccessError, ContainerKekRepairInaccessibleError, ContainerKekRepairRequiredError, ContainerKeyringOverrideStaleError, ContainerPathTooDeepError, classifyContainerWriteRefusal, containerWriterProjectionFromRekeyPlan, createRemoteContainer, fetchContainerKekLog, HistoricalWrapUnavailableError, type HistoricalWrapUnavailableReason, isProjectionVerificationCancelledError, moveRemoteContainer, rebuildKeyringEntriesFromLog, recoverKeyringEntryFromWraps, rekeyRemoteContainer, revokeRemoteContainer, rootContainerWriterProjectionFromCreatePlan, shareRemoteContainer, } from "./workflows/containers/index.js";
|
|
20
20
|
export type { DocumentRecord, DocumentSyncContainerRekeyBuilder, DocumentSyncPullContinuation, DocumentSyncUpdateIsolationError, DocumentsPersistence, DocumentsWorkflowRuntime, DocumentsWorkflowRuntimeInput, IncomingDocumentSyncUpdateValidator, LocalAttachmentRecord, PendingAttachmentRecord, PendingUpdateInsert, PendingUpdateRecord, SyncRemoteDocumentInput, } from "./workflows/documents/index.js";
|
|
21
21
|
export { buildMaterializedDocumentCreatePlan, createDocumentProjectionUserKeyResolver, createDocumentsWorkflowRuntime, createRemoteDocument, DOCUMENT_SYNC_TRACE_FRAGMENT, DOCUMENT_SYNC_TRACE_PATTERN, DOCUMENTS_APP_KIND, DocumentRawHistoryUnavailableError, defaultDocumentsPersistence, deletePersistedDocument, isDatabaseUnavailableError, isDocumentSyncUpdateIsolationError, persistedDocumentCreateStateFromResponse, readPullContinuation, syncRemoteDocument, unwrapContainerKekPath, unwrapDocumentContentKeyTarget, validateDocumentSyncUpdateImports, } from "./workflows/documents/index.js";
|
|
22
22
|
export type { AcknowledgedPrincipalCurrentInput, AcknowledgedPrincipalCurrentRetirement, AuthoredPrincipalMutation, OrganizationGroupPolicyHistoryEntry, OrganizationNativePurchaseEligibility, OrganizationPrincipalMemberChange, OrganizationPrincipalMemberChangeType, OrganizationPrincipalPolicyHistory, OrganizationPrincipalPolicyHistoryEntry, PrincipalMutationJournalContext, PrincipalMutationJournalScope, PrincipalMutationResponse, ReconcileOrganizationDataUsageInput, } from "./workflows/organizations/index.js";
|
|
23
|
-
export { abandonJournaledPrincipalMutation, addOrganizationGroupUser, buildInitialGroupPolicyRequest, buildInitialMemberGroupPolicyRequest, buildOrganizationGroupPolicyHistory, buildOrganizationProfileDocumentPatch, buildRosterProfileDocumentPatch, checkNativePurchaseEligibility, createInitializedRosterProfileDocument, createOrganizationGroup, DEFAULT_PERSONAL_ORGANIZATION_PROFILE_NAME, deriveOrganizationMetadataContainerSystemSlot, deriveOrganizationRosterProfileContainerSystemSlot, discardUnreadableJournaledPrincipalMutation, GroupMembershipNameMismatchError, getOrganizationProfileDocumentLocalId, getRosterProfileDocumentLocalId, importOrganizationUser, loadLocalOrganizationDataUsage, loadLocalOrganizationDirectoryAndGroups, loadOrganizationBilling, loadOrganizationBillingHistory, loadOrganizationBillingManagementUrl, ORGANIZATION_METADATA_CONTAINER_NAME, ORGANIZATION_PROFILE_DOCUMENT_KIND, ORGANIZATION_ROSTER_PROFILE_CONTAINER_NAME, PendingPrincipalMutationError, PrincipalMutationOutcomeUnknownError, ROSTER_PROFILE_DOCUMENT_KIND, readJournaledPrincipalMutation, readOrganizationProfileName, reconcileOrganizationDataUsage, reconcileOrganizationDirectoryAndGroups, recoverJournaledPrincipalMutation, removeOrganizationGroupUser, resolveOrganizationBillingView, retainAcknowledgedPrincipalCurrents, revokeOrganizationContainerGrant, startOrganizationTrial, submitJournaledPrincipalMutation, subscribeOrganizationReadModelInvalidation, UnreadablePrincipalMutationError, updateOrganizationProfile, updateOrganizationRosterEntry, } from "./workflows/organizations/index.js";
|
|
23
|
+
export { abandonJournaledPrincipalMutation, addOrganizationGroupUser, buildInitialGroupPolicyRequest, buildInitialMemberGroupPolicyRequest, buildOrganizationGroupPolicyHistory, buildOrganizationProfileDocumentPatch, buildRosterProfileDocumentPatch, checkNativePurchaseEligibility, createInitializedRosterProfileDocument, createOrganizationGroup, DEFAULT_PERSONAL_ORGANIZATION_PROFILE_NAME, deriveOrganizationMetadataContainerSystemSlot, deriveOrganizationRosterProfileContainerSystemSlot, discardUnreadableJournaledPrincipalMutation, GroupMembershipNameMismatchError, GroupShareNameMismatchError, getOrganizationProfileDocumentLocalId, getRosterProfileDocumentLocalId, importOrganizationUser, loadLocalOrganizationDataUsage, loadLocalOrganizationDirectoryAndGroups, loadOrganizationBilling, loadOrganizationBillingHistory, loadOrganizationBillingManagementUrl, ORGANIZATION_METADATA_CONTAINER_NAME, ORGANIZATION_PROFILE_DOCUMENT_KIND, ORGANIZATION_ROSTER_PROFILE_CONTAINER_NAME, PendingPrincipalMutationError, PrincipalMutationOutcomeUnknownError, ROSTER_PROFILE_DOCUMENT_KIND, readJournaledPrincipalMutation, readOrganizationProfileName, reconcileOrganizationDataUsage, reconcileOrganizationDirectoryAndGroups, recoverJournaledPrincipalMutation, removeOrganizationGroupUser, resolveOrganizationBillingView, retainAcknowledgedPrincipalCurrents, revokeOrganizationContainerGrant, shareRemoteContainerWithGroup, startOrganizationTrial, submitJournaledPrincipalMutation, subscribeOrganizationReadModelInvalidation, UnreadablePrincipalMutationError, updateOrganizationProfile, updateOrganizationRosterEntry, } from "./workflows/organizations/index.js";
|
|
24
24
|
export type { AcknowledgedPrincipalPredecessor, CacheReferencedPrincipalPoliciesOptions, PrincipalHistoryProtectionLease, ProjectionPolicyHistoryRecoveryOptions, RecoveredPrincipalPolicyHistory, RecoveredScopedPrincipalPolicyHistory, RecoverPrincipalPolicyHistoryOptions, RecoverScopedPrincipalPolicyHistoryOptions, } from "./workflows/principals/index.js";
|
|
25
25
|
export { cacheReferencedPrincipalPolicies, captureAcknowledgedPrincipalPredecessor, PrincipalPolicyHistoryReadError, recoverPrincipalPolicyHistory, recoverProjectionPolicyHistory, recoverScopedPrincipalPolicyHistory, } from "./workflows/principals/index.js";
|
|
26
26
|
export type { InitialRootMetadataBootstrap, ProvisionedSystemContainerSpec, RegisterIdentityInput, RegistrationApi, } from "./workflows/registration/index.js";
|