@tearleads/client-sdk 0.1.84 → 0.1.86
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/persistence/container-contents/containerDocumentReassignment.js +7 -9
- package/data/persistence/container-contents/containerDocumentReassignment.js.map +1 -1
- package/data/persistence/container-contents/containerMetadataRows.js +3 -19
- package/data/persistence/container-contents/containerMetadataRows.js.map +1 -1
- package/data/persistence/container-contents/containerRebindPersistence.js +4 -17
- package/data/persistence/container-contents/containerRebindPersistence.js.map +1 -1
- package/data/persistence/container-contents/containerStructuralRepair.js +5 -11
- package/data/persistence/container-contents/containerStructuralRepair.js.map +1 -1
- package/data/persistence/container-contents/documentContainerProjectionPersistence.js +34 -9
- package/data/persistence/container-contents/documentContainerProjectionPersistence.js.map +1 -1
- package/data/persistence/container-contents/dormantContainerMetadata.js +4 -14
- package/data/persistence/container-contents/dormantContainerMetadata.js.map +1 -1
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.js +5 -9
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.js.map +1 -1
- package/data/persistence/documents/documentProjectionContainers.js +28 -0
- package/data/persistence/documents/documentProjectionContainers.js.map +1 -0
- package/data/persistence/documents/documentsPersistence.js +3 -7
- package/data/persistence/documents/documentsPersistence.js.map +1 -1
- package/data/persistence/documents/internal/containerDocumentTombstones.js +4 -11
- package/data/persistence/documents/internal/containerDocumentTombstones.js.map +1 -1
- package/data/persistence/documents/internal/documentDeletionPersistence.js +6 -20
- package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
- package/data/persistence/documents/records/documentRowsInTransaction.js +68 -0
- package/data/persistence/documents/records/documentRowsInTransaction.js.map +1 -0
- package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.d.ts +2 -0
- package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.js +9 -0
- package/data/persistence/keyingCheckpoints/documentPurgeCheckpointPersistence.js.map +1 -1
- package/data/persistence/keyingCheckpoints/keyingCheckpointPersistence.js +9 -0
- package/data/persistence/keyingCheckpoints/keyingCheckpointPersistence.js.map +1 -1
- package/data/persistence/principalPolicy/principalPolicyPersistence.js +3 -9
- package/data/persistence/principalPolicy/principalPolicyPersistence.js.map +1 -1
- package/data/persistence/principalPolicy/principalPolicyRemoteReset.js +10 -7
- package/data/persistence/principalPolicy/principalPolicyRemoteReset.js.map +1 -1
- package/package.json +3 -3
- package/sqlite/sqlite3.wasm +0 -0
- package/sqlite/worker.js +4 -4
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { and, eq, inArray, or, sql } from "drizzle-orm";
|
|
2
2
|
import { uniqueSortedStrings } from "../../documents/shared/readers.js";
|
|
3
|
-
import { documentContainerProjection,
|
|
3
|
+
import { documentContainerProjection, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../sqlite/schema/index.js";
|
|
4
4
|
import { documentIntentLinkTargets } from "../../sqlite/schema/documentPlacementIntentSchema.js";
|
|
5
5
|
import { deleteContainerDocumentTombstoneHoldRowsForLinks, deleteContainerDocumentTombstoneHoldsForContainers, } from "../documents/containerDocumentTombstoneHoldsPersistence.js";
|
|
6
|
+
import { reassignDocumentProjectionContainerInTransaction } from "../documents/documentProjectionContainers.js";
|
|
6
7
|
import { DOCUMENT_LINK_INTENT_TYPE, DOCUMENT_MOVE_INTENT_TYPE, } from "./documentMoveIntentPersistence.js";
|
|
7
8
|
async function reassignDocumentLinksForContainer(input) {
|
|
8
9
|
const { fromContainerId, toContainerId, tx, updatedAt } = input;
|
|
@@ -48,14 +49,11 @@ async function reassignDocumentLinksForContainer(input) {
|
|
|
48
49
|
}
|
|
49
50
|
async function reassignPrimaryDocumentsForContainer(input) {
|
|
50
51
|
const { fromContainerId, toContainerId, tx, updatedAt } = input;
|
|
51
|
-
await tx
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
})
|
|
57
|
-
.where(eq(documentProjection.containerId, fromContainerId))
|
|
58
|
-
.run();
|
|
52
|
+
await reassignDocumentProjectionContainerInTransaction(tx, {
|
|
53
|
+
fromContainerId,
|
|
54
|
+
toContainerId,
|
|
55
|
+
updatedAt,
|
|
56
|
+
});
|
|
59
57
|
}
|
|
60
58
|
async function reassignDocumentMoveIntentsForContainer(input) {
|
|
61
59
|
const { fromContainerId, toContainerId, tx, updatedAt } = input;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerDocumentReassignment.js","sourceRoot":"","sources":["containerDocumentReassignment.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, inArray, or, sql } from \"drizzle-orm\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport {\n documentContainerProjection,\n documentProjection,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport { documentIntentLinkTargets } from \"../../sqlite/schema/documentPlacementIntentSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n deleteContainerDocumentTombstoneHoldRowsForLinks,\n deleteContainerDocumentTombstoneHoldsForContainers,\n} from \"../documents/containerDocumentTombstoneHoldsPersistence\";\nimport {\n DOCUMENT_LINK_INTENT_TYPE,\n DOCUMENT_MOVE_INTENT_TYPE,\n} from \"./documentMoveIntentPersistence\";\n\ninterface ContainerDocumentReassignmentInput {\n readonly fromContainerId: string;\n readonly toContainerId: string;\n readonly tx: ClientSQLiteTransactionScope;\n readonly updatedAt: string;\n}\n\nasync function reassignDocumentLinksForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n await tx.run(sql`\n INSERT INTO ${documentContainerProjection} (\n ${sql.raw(\"document_id\")},\n ${sql.raw(\"container_id\")},\n ${sql.raw(\"updated_at\")}\n )\n SELECT\n ${documentContainerProjection.documentId},\n ${toContainerId},\n max(${documentContainerProjection.updatedAt}, ${updatedAt})\n FROM ${documentContainerProjection}\n WHERE ${documentContainerProjection.containerId} = ${fromContainerId}\n ON CONFLICT (\n ${sql.raw(\"document_id\")},\n ${sql.raw(\"container_id\")}\n ) DO UPDATE SET\n ${sql.raw(\"updated_at\")} = max(\n ${sql.raw(\"updated_at\")},\n excluded.${sql.raw(\"updated_at\")}\n )\n `);\n\n // The reassigned placements are asserted locally: a stale hold on the\n // destination would hide them, and holds on the vacated container have\n // nothing left to hide.\n const movedDocumentIds = (\n await tx\n .select({ documentId: documentContainerProjection.documentId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.containerId, fromContainerId))\n ).map((row) => row.documentId);\n await deleteContainerDocumentTombstoneHoldRowsForLinks(\n tx,\n movedDocumentIds.map((documentId) => ({\n containerIds: [toContainerId],\n documentId,\n })),\n );\n await deleteContainerDocumentTombstoneHoldsForContainers(tx, [\n fromContainerId,\n ]);\n await tx\n .delete(documentContainerProjection)\n .where(eq(documentContainerProjection.containerId, fromContainerId))\n .run();\n}\n\nasync function reassignPrimaryDocumentsForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n await tx\n .update(documentProjection)\n .set({\n containerId: toContainerId,\n updatedAt: sql`max(${documentProjection.updatedAt}, ${updatedAt})`,\n })\n .where(eq(documentProjection.containerId, fromContainerId))\n .run();\n}\n\nasync function reassignDocumentMoveIntentsForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n const affectedIntents = await tx\n .select({\n documentId: localIntents.subjectId,\n id: localIntents.id,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n inArray(localIntents.intentType, [\n DOCUMENT_MOVE_INTENT_TYPE,\n DOCUMENT_LINK_INTENT_TYPE,\n ]),\n or(\n eq(localIntents.sourceContainerId, fromContainerId),\n eq(localIntents.targetContainerId, fromContainerId),\n inArray(\n localIntents.id,\n tx\n .select({ id: documentIntentLinkTargets.intentId })\n .from(documentIntentLinkTargets)\n .where(\n and(\n eq(documentIntentLinkTargets.containerId, fromContainerId),\n eq(documentIntentLinkTargets.operation, \"link\"),\n ),\n ),\n ),\n ),\n ),\n );\n for (const intent of affectedIntents) {\n const id = crypto.randomUUID();\n const targets = await tx\n .select()\n .from(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, intent.id ?? \"\"));\n await tx\n .delete(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, intent.id ?? \"\"))\n .run();\n const retained = new Map(\n targets.map((target) => [target.containerId, target.operation]),\n );\n if (retained.get(fromContainerId) === \"link\") {\n retained.delete(fromContainerId);\n retained.set(toContainerId, \"link\");\n }\n if (retained.size)\n await tx\n .insert(documentIntentLinkTargets)\n .values(\n uniqueSortedStrings([...retained.keys()]).map((containerId) => ({\n intentId: id,\n containerId,\n operation: retained.get(containerId) ?? \"link\",\n })),\n )\n .run();\n await tx\n .update(localIntents)\n .set({\n id,\n lastAttemptedAt: null,\n lastError: null,\n sourceContainerId: sql`CASE\n WHEN ${localIntents.sourceContainerId} = ${fromContainerId}\n THEN ${toContainerId}\n ELSE ${localIntents.sourceContainerId}\n END`,\n syncStatus: \"pending\",\n targetContainerId: sql`CASE\n WHEN ${localIntents.targetContainerId} = ${fromContainerId}\n THEN ${toContainerId}\n ELSE ${localIntents.targetContainerId}\n END`,\n updatedAt: sql`max(${localIntents.updatedAt}, ${updatedAt})`,\n })\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.subjectId, intent.documentId),\n ),\n )\n .run();\n }\n}\n\nexport async function reassignContainerDocumentsInTransaction(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n await reassignDocumentLinksForContainer(input);\n await reassignPrimaryDocumentsForContainer(input);\n await reassignDocumentMoveIntentsForContainer(input);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"containerDocumentReassignment.js","sourceRoot":"","sources":["containerDocumentReassignment.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, inArray, or, sql } from \"drizzle-orm\";\nimport { uniqueSortedStrings } from \"../../documents/shared/readers\";\nimport {\n documentContainerProjection,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport { documentIntentLinkTargets } from \"../../sqlite/schema/documentPlacementIntentSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n deleteContainerDocumentTombstoneHoldRowsForLinks,\n deleteContainerDocumentTombstoneHoldsForContainers,\n} from \"../documents/containerDocumentTombstoneHoldsPersistence\";\nimport { reassignDocumentProjectionContainerInTransaction } from \"../documents/documentProjectionContainers\";\nimport {\n DOCUMENT_LINK_INTENT_TYPE,\n DOCUMENT_MOVE_INTENT_TYPE,\n} from \"./documentMoveIntentPersistence\";\n\ninterface ContainerDocumentReassignmentInput {\n readonly fromContainerId: string;\n readonly toContainerId: string;\n readonly tx: ClientSQLiteTransactionScope;\n readonly updatedAt: string;\n}\n\nasync function reassignDocumentLinksForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n await tx.run(sql`\n INSERT INTO ${documentContainerProjection} (\n ${sql.raw(\"document_id\")},\n ${sql.raw(\"container_id\")},\n ${sql.raw(\"updated_at\")}\n )\n SELECT\n ${documentContainerProjection.documentId},\n ${toContainerId},\n max(${documentContainerProjection.updatedAt}, ${updatedAt})\n FROM ${documentContainerProjection}\n WHERE ${documentContainerProjection.containerId} = ${fromContainerId}\n ON CONFLICT (\n ${sql.raw(\"document_id\")},\n ${sql.raw(\"container_id\")}\n ) DO UPDATE SET\n ${sql.raw(\"updated_at\")} = max(\n ${sql.raw(\"updated_at\")},\n excluded.${sql.raw(\"updated_at\")}\n )\n `);\n\n // The reassigned placements are asserted locally: a stale hold on the\n // destination would hide them, and holds on the vacated container have\n // nothing left to hide.\n const movedDocumentIds = (\n await tx\n .select({ documentId: documentContainerProjection.documentId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.containerId, fromContainerId))\n ).map((row) => row.documentId);\n await deleteContainerDocumentTombstoneHoldRowsForLinks(\n tx,\n movedDocumentIds.map((documentId) => ({\n containerIds: [toContainerId],\n documentId,\n })),\n );\n await deleteContainerDocumentTombstoneHoldsForContainers(tx, [\n fromContainerId,\n ]);\n await tx\n .delete(documentContainerProjection)\n .where(eq(documentContainerProjection.containerId, fromContainerId))\n .run();\n}\n\nasync function reassignPrimaryDocumentsForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n await reassignDocumentProjectionContainerInTransaction(tx, {\n fromContainerId,\n toContainerId,\n updatedAt,\n });\n}\n\nasync function reassignDocumentMoveIntentsForContainer(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n const { fromContainerId, toContainerId, tx, updatedAt } = input;\n const affectedIntents = await tx\n .select({\n documentId: localIntents.subjectId,\n id: localIntents.id,\n })\n .from(localIntents)\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n inArray(localIntents.intentType, [\n DOCUMENT_MOVE_INTENT_TYPE,\n DOCUMENT_LINK_INTENT_TYPE,\n ]),\n or(\n eq(localIntents.sourceContainerId, fromContainerId),\n eq(localIntents.targetContainerId, fromContainerId),\n inArray(\n localIntents.id,\n tx\n .select({ id: documentIntentLinkTargets.intentId })\n .from(documentIntentLinkTargets)\n .where(\n and(\n eq(documentIntentLinkTargets.containerId, fromContainerId),\n eq(documentIntentLinkTargets.operation, \"link\"),\n ),\n ),\n ),\n ),\n ),\n );\n for (const intent of affectedIntents) {\n const id = crypto.randomUUID();\n const targets = await tx\n .select()\n .from(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, intent.id ?? \"\"));\n await tx\n .delete(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, intent.id ?? \"\"))\n .run();\n const retained = new Map(\n targets.map((target) => [target.containerId, target.operation]),\n );\n if (retained.get(fromContainerId) === \"link\") {\n retained.delete(fromContainerId);\n retained.set(toContainerId, \"link\");\n }\n if (retained.size)\n await tx\n .insert(documentIntentLinkTargets)\n .values(\n uniqueSortedStrings([...retained.keys()]).map((containerId) => ({\n intentId: id,\n containerId,\n operation: retained.get(containerId) ?? \"link\",\n })),\n )\n .run();\n await tx\n .update(localIntents)\n .set({\n id,\n lastAttemptedAt: null,\n lastError: null,\n sourceContainerId: sql`CASE\n WHEN ${localIntents.sourceContainerId} = ${fromContainerId}\n THEN ${toContainerId}\n ELSE ${localIntents.sourceContainerId}\n END`,\n syncStatus: \"pending\",\n targetContainerId: sql`CASE\n WHEN ${localIntents.targetContainerId} = ${fromContainerId}\n THEN ${toContainerId}\n ELSE ${localIntents.targetContainerId}\n END`,\n updatedAt: sql`max(${localIntents.updatedAt}, ${updatedAt})`,\n })\n .where(\n and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.subjectId, intent.documentId),\n ),\n )\n .run();\n }\n}\n\nexport async function reassignContainerDocumentsInTransaction(\n input: ContainerDocumentReassignmentInput,\n): Promise<void> {\n await reassignDocumentLinksForContainer(input);\n await reassignPrimaryDocumentsForContainer(input);\n await reassignDocumentMoveIntentsForContainer(input);\n}\n"]}
|
|
@@ -5,6 +5,7 @@ import { serializeDocumentSyncPullContinuation } from "../../documents/shared/sy
|
|
|
5
5
|
import { documentHistoryCheckpoints, documents } from "../../sqlite/schema/index.js";
|
|
6
6
|
import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
|
|
7
7
|
import { documentRecordSelection, mapSelectedDocumentRecord, } from "../documents/records/index.js";
|
|
8
|
+
import { upsertDocumentHistoryCheckpointInTransaction, upsertDocumentRowInTransaction, } from "../documents/records/documentRowsInTransaction.js";
|
|
8
9
|
import { saveContainerCreateIntent } from "./containerIntentPersistence.js";
|
|
9
10
|
import { saveContainerMoveIntent } from "./containerMoveIntentRows.js";
|
|
10
11
|
import { saveContainerRows, } from "./containerPersistence.js";
|
|
@@ -51,14 +52,7 @@ async function saveContainerMetadataRecord(input) {
|
|
|
51
52
|
}),
|
|
52
53
|
updatedAt,
|
|
53
54
|
};
|
|
54
|
-
await tx
|
|
55
|
-
.insert(documents)
|
|
56
|
-
.values(nextRow)
|
|
57
|
-
.onConflictDoUpdate({
|
|
58
|
-
target: [documents.appKind, documents.localId],
|
|
59
|
-
set: nextRow,
|
|
60
|
-
})
|
|
61
|
-
.run();
|
|
55
|
+
await upsertDocumentRowInTransaction(tx, nextRow);
|
|
62
56
|
// The metadata document's content lives in the shared history-checkpoint
|
|
63
57
|
// table, like every other document kind. The whole updates export IS the
|
|
64
58
|
// checkpoint: metadata documents are tiny registries, so no tail/compaction
|
|
@@ -71,17 +65,7 @@ async function saveContainerMetadataRecord(input) {
|
|
|
71
65
|
revision: updatedAt,
|
|
72
66
|
updatedAt,
|
|
73
67
|
};
|
|
74
|
-
await tx
|
|
75
|
-
.insert(documentHistoryCheckpoints)
|
|
76
|
-
.values(checkpointRow)
|
|
77
|
-
.onConflictDoUpdate({
|
|
78
|
-
target: [
|
|
79
|
-
documentHistoryCheckpoints.appKind,
|
|
80
|
-
documentHistoryCheckpoints.localId,
|
|
81
|
-
],
|
|
82
|
-
set: checkpointRow,
|
|
83
|
-
})
|
|
84
|
-
.run();
|
|
68
|
+
await upsertDocumentHistoryCheckpointInTransaction(tx, checkpointRow);
|
|
85
69
|
}
|
|
86
70
|
export async function saveContainerContentsContainerRows(input) {
|
|
87
71
|
const { container, createIntent, moveIntent, localUpdatedAt, record, serverTimestamps, tx, } = input;
|
|
@@ -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 { documentHistoryCheckpoints, documents } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport {\n documentRecordSelection,\n mapSelectedDocumentRecord,\n} from \"../documents/records\";\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
|
+
{"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 { documentHistoryCheckpoints, documents } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport {\n documentRecordSelection,\n mapSelectedDocumentRecord,\n} from \"../documents/records\";\nimport {\n upsertDocumentHistoryCheckpointInTransaction,\n upsertDocumentRowInTransaction,\n} from \"../documents/records/documentRowsInTransaction\";\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 upsertDocumentRowInTransaction(tx, nextRow);\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 upsertDocumentHistoryCheckpointInTransaction(tx, checkpointRow);\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,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { containers,
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { containers, dormantContainerMetadata } from "../../sqlite/schema/index.js";
|
|
3
3
|
import { getClientSQLitePersistenceRuntime } from "../../sqlite/sqlitePersistenceRuntime.js";
|
|
4
4
|
import { runSerializedSqlMutation } from "../../sqlite/sqlSchema.js";
|
|
5
|
+
import { rebindDocumentRowInTransaction } from "../documents/records/documentRowsInTransaction.js";
|
|
5
6
|
import { loadStoredHeldContainerBinding } from "./containerHydrationPersistence.js";
|
|
6
7
|
import { CONTAINER_METADATA_APP_KIND } from "./dormantContainerMetadata.js";
|
|
7
8
|
import { isSupersededContainerBinding, recordSupersededContainerBindingsInTransaction, } from "./supersededContainerBindings.js";
|
|
@@ -52,21 +53,7 @@ export async function rebindStoredHeldContainer(execSql, input) {
|
|
|
52
53
|
.set({ organizationId })
|
|
53
54
|
.where(eq(dormantContainerMetadata.containerId, input.containerId))
|
|
54
55
|
.run();
|
|
55
|
-
await tx
|
|
56
|
-
.update(documents)
|
|
57
|
-
.set({
|
|
58
|
-
accessEpoch: 1,
|
|
59
|
-
accessStateHash: null,
|
|
60
|
-
contentKeyBundle: null,
|
|
61
|
-
documentId: metadataDocumentId,
|
|
62
|
-
documentKekTargets: null,
|
|
63
|
-
documentManifestBundle: null,
|
|
64
|
-
lastCommitLsn: null,
|
|
65
|
-
pullContinuation: null,
|
|
66
|
-
updatedAt: supersededAt,
|
|
67
|
-
})
|
|
68
|
-
.where(and(eq(documents.appKind, CONTAINER_METADATA_APP_KIND), eq(documents.localId, input.containerId)))
|
|
69
|
-
.run();
|
|
56
|
+
await rebindDocumentRowInTransaction(tx, { appKind: CONTAINER_METADATA_APP_KIND, localId: input.containerId }, { documentId: metadataDocumentId, updatedAt: supersededAt });
|
|
70
57
|
return true;
|
|
71
58
|
}, () => !input.stillCurrent || input.stillCurrent(), { behavior: "immediate" });
|
|
72
59
|
return outcome.committed && outcome.result === true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"containerRebindPersistence.js","sourceRoot":"","sources":["containerRebindPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"containerRebindPersistence.js","sourceRoot":"","sources":["containerRebindPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { eq } from \"drizzle-orm\";\nimport { containers, dormantContainerMetadata } from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, runSerializedSqlMutation } from \"../../sqlite/sqlSchema\";\nimport { rebindDocumentRowInTransaction } from \"../documents/records/documentRowsInTransaction\";\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 rebindDocumentRowInTransaction(\n tx,\n { appKind: CONTAINER_METADATA_APP_KIND, localId: input.containerId },\n { documentId: metadataDocumentId, updatedAt: supersededAt },\n );\n return true;\n },\n () => !input.stillCurrent || input.stillCurrent(),\n { behavior: \"immediate\" },\n );\n return outcome.committed && outcome.result === true;\n });\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
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
3
|
import { deleteContainerDocumentTombstoneHoldsForContainers } from "../documents/containerDocumentTombstoneHoldsPersistence.js";
|
|
4
|
+
import { setDocumentProjectionContainerInTransaction } from "../documents/documentProjectionContainers.js";
|
|
4
5
|
import { getLatestTimestamp } from "../latestTimestamp.js";
|
|
5
6
|
import { saveContainerCreateIntent } from "./containerIntentPersistence.js";
|
|
6
7
|
import { deleteContainerWatermarksInTransaction } from "./containerSyncWatermarkPersistence.js";
|
|
@@ -80,22 +81,15 @@ export async function repairDocumentsForRemovedContainersInTransaction(input) {
|
|
|
80
81
|
const removedOrganizationId = row.containerId
|
|
81
82
|
? organizationIdByContainerId.get(row.containerId)
|
|
82
83
|
: undefined;
|
|
83
|
-
await tx
|
|
84
|
-
.update(documentProjection)
|
|
85
|
-
.set({
|
|
84
|
+
await setDocumentProjectionContainerInTransaction(tx, row.localId, {
|
|
86
85
|
containerId: row.documentId
|
|
87
|
-
? (firstRemainingContainerIdByDocumentId.get(row.documentId) ??
|
|
88
|
-
null)
|
|
86
|
+
? (firstRemainingContainerIdByDocumentId.get(row.documentId) ?? null)
|
|
89
87
|
: null,
|
|
90
|
-
|
|
91
|
-
? { organizationId: removedOrganizationId }
|
|
92
|
-
: {}),
|
|
88
|
+
organizationId: removedOrganizationId,
|
|
93
89
|
updatedAt: getLatestTimestamp(row.updatedAt, row.containerId
|
|
94
90
|
? (updatedAtByContainerId.get(row.containerId) ?? row.updatedAt)
|
|
95
91
|
: row.updatedAt),
|
|
96
|
-
})
|
|
97
|
-
.where(eq(documentProjection.localId, row.localId))
|
|
98
|
-
.run();
|
|
92
|
+
});
|
|
99
93
|
}
|
|
100
94
|
}
|
|
101
95
|
}
|
|
@@ -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 { 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
|
+
{"version":3,"file":"containerStructuralRepair.js","sourceRoot":"","sources":["containerStructuralRepair.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,UAAU,EACV,2BAA2B,EAC3B,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,GACb,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,kDAAkD,EAAE,MAAM,yDAAyD,CAAC;AAC7H,OAAO,EAAE,2CAA2C,EAAE,MAAM,2CAA2C,CAAC;AACxG,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAKxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,sCAAsC,EAAE,MAAM,qCAAqC,CAAC;AAC7F,OAAO,EAAE,gDAAgD,EAAE,MAAM,4BAA4B,CAAC;AAE9F,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAC9E,8CAA8C;AAC9C,KAAK,UAAU,gCAAgC,CAC7C,EAAgC,EAChC,YAAmC;IAEnC,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,MAAM,CAAC;QACN,EAAE,EAAE,UAAU,CAAC,EAAE;QACjB,cAAc,EAAE,UAAU,CAAC,cAAc;KAC1C,CAAC;SACD,IAAI,CAAC,UAAU,CAAC;SAChB,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;IAC/C,MAAM,2BAA2B,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9D,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;QAC7B,IAAI,SAAS,CAAC,EAAE,IAAI,SAAS,CAAC,cAAc,EAAE,CAAC;YAC7C,2BAA2B,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IACD,OAAO,2BAA2B,CAAC;AACrC,CAAC;AAED,8EAA8E;AAC9E,6EAA6E;AAC7E,+BAA+B;AAC/B,KAAK,UAAU,yCAAyC,CACtD,EAAgC,EAChC,WAAkC;IAElC,MAAM,qCAAqC,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,qCAAqC,CAAC;IAC/C,CAAC;IACD,MAAM,cAAc,GAAG,MAAM,EAAE;SAC5B,MAAM,CAAC;QACN,WAAW,EAAE,2BAA2B,CAAC,WAAW;QACpD,UAAU,EAAE,2BAA2B,CAAC,UAAU;KACnD,CAAC;SACD,IAAI,CAAC,2BAA2B,CAAC;SACjC,KAAK,CAAC,OAAO,CAAC,2BAA2B,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;SACnE,OAAO,CACN,GAAG,CAAC,2BAA2B,CAAC,UAAU,CAAC,EAC3C,GAAG,CAAC,2BAA2B,CAAC,WAAW,CAAC,CAC7C,CAAC;IACJ,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QAClC,IAAI,CAAC,qCAAqC,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAChE,qCAAqC,CAAC,GAAG,CACvC,IAAI,CAAC,UAAU,EACf,IAAI,CAAC,WAAW,CACjB,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,qCAAqC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gDAAgD,CAAC,KAGtE;IACC,MAAM,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;IACrB,MAAM,sBAAsB,GAAG,IAAI,GAAG,CACpC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAC1E,CAAC;IACF,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/D,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,CAAC;QACC,MAAM,YAAY,GAAG,MAAM,EAAE;aAC1B,MAAM,CAAC;YACN,WAAW,EAAE,kBAAkB,CAAC,WAAW;YAC3C,UAAU,EAAE,kBAAkB,CAAC,UAAU;YACzC,OAAO,EAAE,kBAAkB,CAAC,OAAO;YACnC,SAAS,EAAE,kBAAkB,CAAC,SAAS;SACxC,CAAC;aACD,IAAI,CAAC,kBAAkB,CAAC;aACxB,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;QAEhE,MAAM,2BAA2B,GAAG,MAAM,gCAAgC,CACxE,EAAE,EACF,YAAY,CACb,CAAC;QAEF,MAAM,EAAE;aACL,MAAM,CAAC,2BAA2B,CAAC;aACnC,KAAK,CAAC,OAAO,CAAC,2BAA2B,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;aACrE,GAAG,EAAE,CAAC;QACT,MAAM,kDAAkD,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;QAE3E,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAC5B,IAAI,GAAG,CACL,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CACxE,CACF,CAAC;QACF,MAAM,qCAAqC,GACzC,MAAM,yCAAyC,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;QAEnE,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;gBACjB,SAAS;YACX,CAAC;YAED,MAAM,qBAAqB,GAAG,GAAG,CAAC,WAAW;gBAC3C,CAAC,CAAC,2BAA2B,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC;gBAClD,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,2CAA2C,CAAC,EAAE,EAAE,GAAG,CAAC,OAAO,EAAE;gBACjE,WAAW,EAAE,GAAG,CAAC,UAAU;oBACzB,CAAC,CAAC,CAAC,qCAAqC,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;oBACrE,CAAC,CAAC,IAAI;gBACR,cAAc,EAAE,qBAAqB;gBACrC,SAAS,EAAE,kBAAkB,CAC3B,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,WAAW;oBACb,CAAC,CAAC,CAAC,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC;oBAChE,CAAC,CAAC,GAAG,CAAC,SAAS,CAClB;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oCAAoC,CAAC,KAK1D;IACC,MAAM,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC3E,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAC9B,IAAI,GAAG,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CACrE,CAAC;IACF,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO;IACT,CAAC;IAED,MAAM,EAAE;SACL,MAAM,CAAC,UAAU,CAAC;SAClB,GAAG,CAAC;QACH,cAAc,EAAE,oBAAoB;QACpC,cAAc,EAAE,SAAS;KAC1B,CAAC;SACD,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;SAC5C,GAAG,EAAE,CAAC;IAET,KAAK,MAAM,QAAQ,IAAI,mBAAmB,EAAE,CAAC;QAC3C,MAAM,EAAE;aACL,MAAM,CAAC,UAAU,CAAC;aAClB,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,EAAE,CAAC;aAC7C,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;aAC9C,GAAG,EAAE,CAAC;QAET,IAAI,QAAQ,CAAC,kBAAkB,IAAI,QAAQ,CAAC,iBAAiB,EAAE,CAAC;YAC9D,MAAM,yBAAyB,CAAC;gBAC9B,WAAW,EAAE,QAAQ,CAAC,WAAW;gBACjC,YAAY,EAAE,EAAE,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB,EAAE;gBAC/D,EAAE;gBACF,SAAS;aACV,CAAC,CAAC;QACL,CAAC;QAED,IAAI,QAAQ,CAAC,iBAAiB,EAAE,CAAC;YAC/B,MAAM,EAAE;iBACL,MAAM,CAAC,YAAY,CAAC;iBACpB,GAAG,CAAC;gBACH,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;gBACvB,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;gBAC7C,UAAU,EAAE,SAAS;gBACrB,SAAS;aACV,CAAC;iBACD,KAAK,CACJ,GAAG,CACD,iBAAiB,CAAC,kBAAkB,CAAC,aAAa,CAAC,EACnD,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CACjD,CACF;iBACA,GAAG,EAAE,CAAC;QACX,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,KAMpD;IACC,MAAM,EACJ,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,EAAE,EACF,SAAS,GACV,GAAG,KAAK,CAAC;IACV,MAAM,EAAE;SACL,MAAM,CAAC,UAAU,CAAC;SAClB,GAAG,CAAC;QACH,cAAc,EAAE,oBAAoB;QACpC,QAAQ,EAAE,aAAa;QACvB,cAAc,EAAE,SAAS;KAC1B,CAAC;SACD,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;SAC/C,GAAG,EAAE,CAAC;IACT,MAAM,UAAU,GAAG,iBAAiB,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAC;IACzE,MAAM,gBAAgB,GAAG,MAAM,EAAE;SAC9B,MAAM,CAAC,EAAE,WAAW,EAAE,YAAY,CAAC,SAAS,EAAE,CAAC;SAC/C,IAAI,CAAC,YAAY,CAAC;SAClB,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,YAAY,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAAC,CAAC;SAC3E,GAAG,EAAE,CAAC;IACT,KAAK,MAAM,MAAM,IAAI,gBAAgB,EAAE,CAAC;QACtC,MAAM,EAAE;aACL,MAAM,CAAC,YAAY,CAAC;aACpB,GAAG,CAAC;YACH,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;YACvB,iBAAiB,EAAE,aAAa;YAChC,SAAS;SACV,CAAC;aACD,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;aACtE,GAAG,EAAE,CAAC;IACX,CAAC;IACD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAC;IACrE,MAAM,cAAc,GAAG,MAAM,EAAE;SAC5B,MAAM,CAAC,EAAE,WAAW,EAAE,YAAY,CAAC,SAAS,EAAE,CAAC;SAC/C,IAAI,CAAC,YAAY,CAAC;SAClB,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,YAAY,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAAC,CAAC;SACzE,GAAG,EAAE,CAAC;IACT,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;QACpC,MAAM,EAAE;aACL,MAAM,CAAC,YAAY,CAAC;aACpB,GAAG,CAAC;YACH,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;YACvB,iBAAiB,EAAE,aAAa;YAChC,UAAU,EAAE,SAAS;YACrB,SAAS;SACV,CAAC;aACD,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;aACpE,GAAG,EAAE,CAAC;IACX,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,KAG9C;IACC,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;IAClC,MAAM,EAAE;SACL,MAAM,CAAC,YAAY,CAAC;SACpB,KAAK,CACJ,GAAG,CAAC,sBAAsB,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CACvE;SACA,GAAG,EAAE,CAAC;IACT,MAAM,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;IACxE,MAAM,gDAAgD,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1E,MAAM,sCAAsC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;AAClE,CAAC","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 { setDocumentProjectionContainerInTransaction } from \"../documents/documentProjectionContainers\";\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 setDocumentProjectionContainerInTransaction(tx, row.localId, {\n containerId: row.documentId\n ? (firstRemainingContainerIdByDocumentId.get(row.documentId) ?? null)\n : null,\n organizationId: removedOrganizationId,\n updatedAt: getLatestTimestamp(\n row.updatedAt,\n row.containerId\n ? (updatedAtByContainerId.get(row.containerId) ?? row.updatedAt)\n : row.updatedAt,\n ),\n });\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,8 +1,9 @@
|
|
|
1
|
-
import { asc, eq, inArray } from "drizzle-orm";
|
|
2
|
-
import {
|
|
1
|
+
import { and, asc, eq, inArray, notInArray } from "drizzle-orm";
|
|
2
|
+
import { documentContainerProjection, documentContainerProjectionTables, documentPlacementIntentTables, documentTables, inLocalIntentLane, LOCAL_INTENT_LANES, localIntents, } from "../../sqlite/schema/index.js";
|
|
3
|
+
import { documentIntentLinkTargets } from "../../sqlite/schema/documentPlacementIntentSchema.js";
|
|
3
4
|
import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
|
|
4
5
|
import { ensureSqlTables } from "../../sqlite/sqlSchema.js";
|
|
5
|
-
import { deleteContainerDocumentTombstoneHoldRowsForLinks, deleteContainerDocumentTombstoneHoldsForDocuments, } from "../documents/containerDocumentTombstoneHoldsPersistence.js";
|
|
6
|
+
import { deleteContainerDocumentTombstoneHoldRowsForLinks, deleteContainerDocumentTombstoneHoldsForContainers, deleteContainerDocumentTombstoneHoldsForDocuments, } from "../documents/containerDocumentTombstoneHoldsPersistence.js";
|
|
6
7
|
import { filterWritableDocumentPlacements, } from "./documentPlacement.js";
|
|
7
8
|
/** Link rows and their tombstone holds leave together on a remote reset. */
|
|
8
9
|
export async function deleteDocumentPlacementRowsByContainerIds(tx, containerIds) {
|
|
@@ -10,20 +11,44 @@ export async function deleteDocumentPlacementRowsByContainerIds(tx, containerIds
|
|
|
10
11
|
.delete(documentContainerProjection)
|
|
11
12
|
.where(inArray(documentContainerProjection.containerId, containerIds))
|
|
12
13
|
.run();
|
|
13
|
-
await tx
|
|
14
|
-
.delete(containerDocumentTombstoneHolds)
|
|
15
|
-
.where(inArray(containerDocumentTombstoneHolds.containerId, containerIds))
|
|
16
|
-
.run();
|
|
14
|
+
await deleteContainerDocumentTombstoneHoldsForContainers(tx, containerIds);
|
|
17
15
|
}
|
|
18
16
|
export async function deleteDocumentPlacementRowsByDocumentIds(tx, documentIds) {
|
|
19
17
|
await tx
|
|
20
18
|
.delete(documentContainerProjection)
|
|
21
19
|
.where(inArray(documentContainerProjection.documentId, documentIds))
|
|
22
20
|
.run();
|
|
21
|
+
await deleteContainerDocumentTombstoneHoldsForDocuments(tx, documentIds);
|
|
22
|
+
}
|
|
23
|
+
/** Removes one placement a verified tombstone retired. */
|
|
24
|
+
export async function deleteDocumentPlacementRowInTransaction(tx, placement) {
|
|
25
|
+
await tx
|
|
26
|
+
.delete(documentContainerProjection)
|
|
27
|
+
.where(and(eq(documentContainerProjection.documentId, placement.documentId), eq(documentContainerProjection.containerId, placement.containerId)))
|
|
28
|
+
.run();
|
|
29
|
+
}
|
|
30
|
+
/** Removes a document's placements outside the containers its head links. */
|
|
31
|
+
export async function deleteUnlinkedDocumentPlacementRowsInTransaction(tx, documentId, linkedContainerIds) {
|
|
32
|
+
await tx
|
|
33
|
+
.delete(documentContainerProjection)
|
|
34
|
+
.where(linkedContainerIds.size === 0
|
|
35
|
+
? eq(documentContainerProjection.documentId, documentId)
|
|
36
|
+
: and(eq(documentContainerProjection.documentId, documentId), notInArray(documentContainerProjection.containerId, [
|
|
37
|
+
...linkedContainerIds,
|
|
38
|
+
])))
|
|
39
|
+
.run();
|
|
40
|
+
}
|
|
41
|
+
/** Removes a deleted document's queued placement intents and link targets. */
|
|
42
|
+
export async function deleteDocumentPlacementIntentsInTransaction(tx, documentId) {
|
|
43
|
+
const placementIntent = and(inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement), eq(localIntents.subjectId, documentId));
|
|
23
44
|
await tx
|
|
24
|
-
.delete(
|
|
25
|
-
.where(inArray(
|
|
45
|
+
.delete(documentIntentLinkTargets)
|
|
46
|
+
.where(inArray(documentIntentLinkTargets.intentId, tx
|
|
47
|
+
.select({ id: localIntents.id })
|
|
48
|
+
.from(localIntents)
|
|
49
|
+
.where(placementIntent)))
|
|
26
50
|
.run();
|
|
51
|
+
await tx.delete(localIntents).where(placementIntent).run();
|
|
27
52
|
}
|
|
28
53
|
export const sqlDocumentContainerProjectionPersistence = {
|
|
29
54
|
async ensureSchema(execSql) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documentContainerProjectionPersistence.js","sourceRoot":"","sources":["documentContainerProjectionPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { asc, eq, inArray } from \"drizzle-orm\";\nimport {\n containerDocumentTombstoneHolds,\n documentContainerProjection,\n documentContainerProjectionTables,\n documentPlacementIntentTables,\n documentTables,\n} from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport {\n deleteContainerDocumentTombstoneHoldRowsForLinks,\n deleteContainerDocumentTombstoneHoldsForDocuments,\n} from \"../documents/containerDocumentTombstoneHoldsPersistence\";\n\nimport {\n type DocumentPlacementInput,\n type DocumentPlacementWriteOptions,\n filterWritableDocumentPlacements,\n} from \"./documentPlacement\";\n\ninterface DocumentContainerProjectionPersistence {\n ensureSchema: (execSql: ExecSql) => Promise<void>;\n listLinkedContainerIds: (\n execSql: ExecSql,\n documentId: string,\n ) => Promise<ReadonlyArray<string>>;\n listLinkedContainerIdsByDocumentIds: (\n execSql: ExecSql,\n documentIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyMap<string, ReadonlyArray<string>>>;\n listDocumentIdsByContainerIds: (\n execSql: ExecSql,\n containerIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyArray<string>>;\n replaceDocumentLinks: (\n execSql: ExecSql,\n documentId: string,\n containerIds: ReadonlyArray<string>,\n options?: DocumentPlacementWriteOptions,\n ) => Promise<void>;\n replaceDocumentLinksBatch: (\n execSql: ExecSql,\n inputs: ReadonlyArray<DocumentPlacementInput>,\n options?: DocumentPlacementWriteOptions,\n ) => Promise<void>;\n}\n\n/** Link rows and their tombstone holds leave together on a remote reset. */\nexport async function deleteDocumentPlacementRowsByContainerIds(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n await tx\n .delete(documentContainerProjection)\n .where(inArray(documentContainerProjection.containerId, containerIds))\n .run();\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.containerId, containerIds))\n .run();\n}\n\nexport async function deleteDocumentPlacementRowsByDocumentIds(\n tx: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<void> {\n await tx\n .delete(documentContainerProjection)\n .where(inArray(documentContainerProjection.documentId, documentIds))\n .run();\n await tx\n .delete(containerDocumentTombstoneHolds)\n .where(inArray(containerDocumentTombstoneHolds.documentId, documentIds))\n .run();\n}\n\nexport const sqlDocumentContainerProjectionPersistence: DocumentContainerProjectionPersistence =\n {\n async ensureSchema(execSql) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n },\n async listLinkedContainerIds(execSql, documentId) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ containerId: documentContainerProjection.containerId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.documentId, documentId))\n .orderBy(asc(documentContainerProjection.containerId));\n\n return rows.map((row) => row.containerId);\n },\n async listLinkedContainerIdsByDocumentIds(execSql, documentIds) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const uniqueDocumentIds = Array.from(new Set(documentIds));\n if (uniqueDocumentIds.length === 0) {\n return new Map();\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n documentId: documentContainerProjection.documentId,\n containerId: documentContainerProjection.containerId,\n })\n .from(documentContainerProjection)\n .where(\n inArray(documentContainerProjection.documentId, uniqueDocumentIds),\n )\n .orderBy(\n asc(documentContainerProjection.documentId),\n asc(documentContainerProjection.containerId),\n );\n\n const linkedContainerIdsByDocumentId = new Map<string, string[]>();\n for (const documentId of uniqueDocumentIds) {\n linkedContainerIdsByDocumentId.set(documentId, []);\n }\n\n for (const row of rows) {\n const linkedContainerIds =\n linkedContainerIdsByDocumentId.get(row.documentId) ?? [];\n linkedContainerIds.push(row.containerId);\n linkedContainerIdsByDocumentId.set(row.documentId, linkedContainerIds);\n }\n\n return linkedContainerIdsByDocumentId;\n },\n async listDocumentIdsByContainerIds(execSql, containerIds) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const uniqueContainerIds = Array.from(new Set(containerIds));\n if (uniqueContainerIds.length === 0) {\n return [];\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .selectDistinct({ documentId: documentContainerProjection.documentId })\n .from(documentContainerProjection)\n .where(\n inArray(documentContainerProjection.containerId, uniqueContainerIds),\n )\n .orderBy(asc(documentContainerProjection.documentId));\n\n return rows.map((row) => row.documentId);\n },\n async replaceDocumentLinks(execSql, documentId, containerIds, options) {\n await sqlDocumentContainerProjectionPersistence.replaceDocumentLinksBatch(\n execSql,\n [{ documentId, containerIds }],\n options,\n );\n },\n async replaceDocumentLinksBatch(execSql, inputs, options) {\n await ensureSqlTables(execSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...documentTables,\n ]);\n const latest = new Map(inputs.map((input) => [input.documentId, input]));\n if (latest.size === 0) return;\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const replace = async (tx: ClientSQLiteTransactionScope) => {\n const writable = await filterWritableDocumentPlacements(\n tx,\n [...latest.values()],\n options,\n );\n if (writable.length === 0) return;\n // A placement the writer re-asserts is no longer tombstoned: a\n // listing that links it again, or a local move that owns it, releases\n // the hold so the document is not hidden until the next retry.\n if (options?.moveIntentId) {\n await deleteContainerDocumentTombstoneHoldsForDocuments(\n tx,\n writable.map((input) => input.documentId),\n );\n } else {\n await deleteContainerDocumentTombstoneHoldRowsForLinks(tx, writable);\n }\n await tx\n .delete(documentContainerProjection)\n .where(\n inArray(\n documentContainerProjection.documentId,\n writable.map((input) => input.documentId),\n ),\n )\n .run();\n const updatedAt = new Date().toISOString();\n const rows = writable.flatMap((input) =>\n Array.from(new Set(input.containerIds))\n .sort()\n .map((containerId) => ({\n documentId: input.documentId,\n containerId,\n updatedAt,\n })),\n );\n if (rows.length > 0)\n await tx.insert(documentContainerProjection).values(rows).run();\n };\n if (options?.stillCurrent) {\n await runtime.guardedTransaction(replace, options.stillCurrent);\n return;\n }\n await runtime.transaction(replace);\n },\n };\n"]}
|
|
1
|
+
{"version":3,"file":"documentContainerProjectionPersistence.js","sourceRoot":"","sources":["documentContainerProjectionPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray, notInArray } from \"drizzle-orm\";\nimport {\n documentContainerProjection,\n documentContainerProjectionTables,\n documentPlacementIntentTables,\n documentTables,\n inLocalIntentLane,\n LOCAL_INTENT_LANES,\n localIntents,\n} from \"../../sqlite/schema\";\nimport { documentIntentLinkTargets } from \"../../sqlite/schema/documentPlacementIntentSchema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport {\n deleteContainerDocumentTombstoneHoldRowsForLinks,\n deleteContainerDocumentTombstoneHoldsForContainers,\n deleteContainerDocumentTombstoneHoldsForDocuments,\n} from \"../documents/containerDocumentTombstoneHoldsPersistence\";\n\nimport {\n type DocumentPlacementInput,\n type DocumentPlacementWriteOptions,\n filterWritableDocumentPlacements,\n} from \"./documentPlacement\";\n\ninterface DocumentContainerProjectionPersistence {\n ensureSchema: (execSql: ExecSql) => Promise<void>;\n listLinkedContainerIds: (\n execSql: ExecSql,\n documentId: string,\n ) => Promise<ReadonlyArray<string>>;\n listLinkedContainerIdsByDocumentIds: (\n execSql: ExecSql,\n documentIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyMap<string, ReadonlyArray<string>>>;\n listDocumentIdsByContainerIds: (\n execSql: ExecSql,\n containerIds: ReadonlyArray<string>,\n ) => Promise<ReadonlyArray<string>>;\n replaceDocumentLinks: (\n execSql: ExecSql,\n documentId: string,\n containerIds: ReadonlyArray<string>,\n options?: DocumentPlacementWriteOptions,\n ) => Promise<void>;\n replaceDocumentLinksBatch: (\n execSql: ExecSql,\n inputs: ReadonlyArray<DocumentPlacementInput>,\n options?: DocumentPlacementWriteOptions,\n ) => Promise<void>;\n}\n\n/** Link rows and their tombstone holds leave together on a remote reset. */\nexport async function deleteDocumentPlacementRowsByContainerIds(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n await tx\n .delete(documentContainerProjection)\n .where(inArray(documentContainerProjection.containerId, containerIds))\n .run();\n await deleteContainerDocumentTombstoneHoldsForContainers(tx, containerIds);\n}\n\nexport async function deleteDocumentPlacementRowsByDocumentIds(\n tx: ClientSQLiteTransactionScope,\n documentIds: ReadonlyArray<string>,\n): Promise<void> {\n await tx\n .delete(documentContainerProjection)\n .where(inArray(documentContainerProjection.documentId, documentIds))\n .run();\n await deleteContainerDocumentTombstoneHoldsForDocuments(tx, documentIds);\n}\n\n/** Removes one placement a verified tombstone retired. */\nexport async function deleteDocumentPlacementRowInTransaction(\n tx: ClientSQLiteTransactionScope,\n placement: { readonly containerId: string; readonly documentId: string },\n): Promise<void> {\n await tx\n .delete(documentContainerProjection)\n .where(\n and(\n eq(documentContainerProjection.documentId, placement.documentId),\n eq(documentContainerProjection.containerId, placement.containerId),\n ),\n )\n .run();\n}\n\n/** Removes a document's placements outside the containers its head links. */\nexport async function deleteUnlinkedDocumentPlacementRowsInTransaction(\n tx: ClientSQLiteTransactionScope,\n documentId: string,\n linkedContainerIds: ReadonlySet<string>,\n): Promise<void> {\n await tx\n .delete(documentContainerProjection)\n .where(\n linkedContainerIds.size === 0\n ? eq(documentContainerProjection.documentId, documentId)\n : and(\n eq(documentContainerProjection.documentId, documentId),\n notInArray(documentContainerProjection.containerId, [\n ...linkedContainerIds,\n ]),\n ),\n )\n .run();\n}\n\n/** Removes a deleted document's queued placement intents and link targets. */\nexport async function deleteDocumentPlacementIntentsInTransaction(\n tx: ClientSQLiteTransactionScope,\n documentId: string,\n): Promise<void> {\n const placementIntent = and(\n inLocalIntentLane(LOCAL_INTENT_LANES.documentPlacement),\n eq(localIntents.subjectId, documentId),\n );\n await tx\n .delete(documentIntentLinkTargets)\n .where(\n inArray(\n documentIntentLinkTargets.intentId,\n tx\n .select({ id: localIntents.id })\n .from(localIntents)\n .where(placementIntent),\n ),\n )\n .run();\n await tx.delete(localIntents).where(placementIntent).run();\n}\n\nexport const sqlDocumentContainerProjectionPersistence: DocumentContainerProjectionPersistence =\n {\n async ensureSchema(execSql) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n },\n async listLinkedContainerIds(execSql, documentId) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({ containerId: documentContainerProjection.containerId })\n .from(documentContainerProjection)\n .where(eq(documentContainerProjection.documentId, documentId))\n .orderBy(asc(documentContainerProjection.containerId));\n\n return rows.map((row) => row.containerId);\n },\n async listLinkedContainerIdsByDocumentIds(execSql, documentIds) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const uniqueDocumentIds = Array.from(new Set(documentIds));\n if (uniqueDocumentIds.length === 0) {\n return new Map();\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .select({\n documentId: documentContainerProjection.documentId,\n containerId: documentContainerProjection.containerId,\n })\n .from(documentContainerProjection)\n .where(\n inArray(documentContainerProjection.documentId, uniqueDocumentIds),\n )\n .orderBy(\n asc(documentContainerProjection.documentId),\n asc(documentContainerProjection.containerId),\n );\n\n const linkedContainerIdsByDocumentId = new Map<string, string[]>();\n for (const documentId of uniqueDocumentIds) {\n linkedContainerIdsByDocumentId.set(documentId, []);\n }\n\n for (const row of rows) {\n const linkedContainerIds =\n linkedContainerIdsByDocumentId.get(row.documentId) ?? [];\n linkedContainerIds.push(row.containerId);\n linkedContainerIdsByDocumentId.set(row.documentId, linkedContainerIds);\n }\n\n return linkedContainerIdsByDocumentId;\n },\n async listDocumentIdsByContainerIds(execSql, containerIds) {\n await ensureSqlTables(execSql, documentContainerProjectionTables);\n const uniqueContainerIds = Array.from(new Set(containerIds));\n if (uniqueContainerIds.length === 0) {\n return [];\n }\n\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const rows = await db\n .selectDistinct({ documentId: documentContainerProjection.documentId })\n .from(documentContainerProjection)\n .where(\n inArray(documentContainerProjection.containerId, uniqueContainerIds),\n )\n .orderBy(asc(documentContainerProjection.documentId));\n\n return rows.map((row) => row.documentId);\n },\n async replaceDocumentLinks(execSql, documentId, containerIds, options) {\n await sqlDocumentContainerProjectionPersistence.replaceDocumentLinksBatch(\n execSql,\n [{ documentId, containerIds }],\n options,\n );\n },\n async replaceDocumentLinksBatch(execSql, inputs, options) {\n await ensureSqlTables(execSql, [\n ...documentContainerProjectionTables,\n ...documentPlacementIntentTables,\n ...documentTables,\n ]);\n const latest = new Map(inputs.map((input) => [input.documentId, input]));\n if (latest.size === 0) return;\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const replace = async (tx: ClientSQLiteTransactionScope) => {\n const writable = await filterWritableDocumentPlacements(\n tx,\n [...latest.values()],\n options,\n );\n if (writable.length === 0) return;\n // A placement the writer re-asserts is no longer tombstoned: a\n // listing that links it again, or a local move that owns it, releases\n // the hold so the document is not hidden until the next retry.\n if (options?.moveIntentId) {\n await deleteContainerDocumentTombstoneHoldsForDocuments(\n tx,\n writable.map((input) => input.documentId),\n );\n } else {\n await deleteContainerDocumentTombstoneHoldRowsForLinks(tx, writable);\n }\n await tx\n .delete(documentContainerProjection)\n .where(\n inArray(\n documentContainerProjection.documentId,\n writable.map((input) => input.documentId),\n ),\n )\n .run();\n const updatedAt = new Date().toISOString();\n const rows = writable.flatMap((input) =>\n Array.from(new Set(input.containerIds))\n .sort()\n .map((containerId) => ({\n documentId: input.documentId,\n containerId,\n updatedAt,\n })),\n );\n if (rows.length > 0)\n await tx.insert(documentContainerProjection).values(rows).run();\n };\n if (options?.stillCurrent) {\n await runtime.guardedTransaction(replace, options.stillCurrent);\n return;\n }\n await runtime.transaction(replace);\n },\n };\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { inArray, sql } from "drizzle-orm";
|
|
2
|
+
import { dormantContainerMetadata } from "../../sqlite/schema/index.js";
|
|
3
|
+
import { deleteDocumentScopeRowsInTransaction } from "../documents/records/documentRowsInTransaction.js";
|
|
3
4
|
export const CONTAINER_METADATA_APP_KIND = "container-metadata";
|
|
4
5
|
export async function retainDormantContainerMetadataInTransaction(tx, retained) {
|
|
5
6
|
if (retained.length === 0) {
|
|
@@ -32,18 +33,7 @@ export async function deleteContainerMetadataDocumentRowsInTransaction(tx, conta
|
|
|
32
33
|
if (ids.length === 0) {
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
|
-
await tx
|
|
36
|
-
.delete(documents)
|
|
37
|
-
.where(and(eq(documents.appKind, CONTAINER_METADATA_APP_KIND), inArray(documents.localId, ids)))
|
|
38
|
-
.run();
|
|
39
|
-
await tx
|
|
40
|
-
.delete(documentHistoryCheckpoints)
|
|
41
|
-
.where(and(eq(documentHistoryCheckpoints.appKind, CONTAINER_METADATA_APP_KIND), inArray(documentHistoryCheckpoints.localId, ids)))
|
|
42
|
-
.run();
|
|
43
|
-
await tx
|
|
44
|
-
.delete(documentHistoryUpdates)
|
|
45
|
-
.where(and(eq(documentHistoryUpdates.appKind, CONTAINER_METADATA_APP_KIND), inArray(documentHistoryUpdates.localId, ids)))
|
|
46
|
-
.run();
|
|
36
|
+
await deleteDocumentScopeRowsInTransaction(tx, CONTAINER_METADATA_APP_KIND, ids);
|
|
47
37
|
await clearDormantContainerMetadataInTransaction(tx, ids);
|
|
48
38
|
}
|
|
49
39
|
//# sourceMappingURL=dormantContainerMetadata.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dormantContainerMetadata.js","sourceRoot":"","sources":["dormantContainerMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"dormantContainerMetadata.js","sourceRoot":"","sources":["dormantContainerMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EAAE,oCAAoC,EAAE,MAAM,gDAAgD,CAAC;AAEtG,MAAM,CAAC,MAAM,2BAA2B,GAAG,oBAAoB,CAAC;AAQhE,MAAM,CAAC,KAAK,UAAU,2CAA2C,CAC/D,EAAgC,EAChC,QAA0D;IAE1D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,MAAM,EAAE;SACL,MAAM,CAAC,wBAAwB,CAAC;SAChC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;SACrB,kBAAkB,CAAC;QAClB,MAAM,EAAE,wBAAwB,CAAC,WAAW;QAC5C,GAAG,EAAE;YACH,cAAc,EAAE,GAAG,CAAA,0BAA0B;YAC7C,UAAU,EAAE,GAAG,CAAA,sBAAsB;SACtC;KACF,CAAC;SACD,GAAG,EAAE,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0CAA0C,CAC9D,EAAgC,EAChC,YAAmC;IAEnC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,MAAM,EAAE;SACL,MAAM,CAAC,wBAAwB,CAAC;SAChC,KAAK,CAAC,OAAO,CAAC,wBAAwB,CAAC,WAAW,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;SACvE,GAAG,EAAE,CAAC;AACX,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,gDAAgD,CACpE,EAAgC,EAChC,YAAmC;IAEnC,MAAM,GAAG,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC;IAC9B,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO;IACT,CAAC;IACD,MAAM,oCAAoC,CACxC,EAAE,EACF,2BAA2B,EAC3B,GAAG,CACJ,CAAC;IACF,MAAM,0CAA0C,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;AAC5D,CAAC","sourcesContent":["import { inArray, sql } from \"drizzle-orm\";\nimport { dormantContainerMetadata } from \"../../sqlite/schema\";\nimport type { ClientSQLiteTransactionScope } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { deleteDocumentScopeRowsInTransaction } from \"../documents/records/documentRowsInTransaction\";\n\nexport const CONTAINER_METADATA_APP_KIND = \"container-metadata\";\n\ninterface DormantContainerMetadataRetention {\n readonly containerId: string;\n readonly organizationId: string;\n readonly retainedAt: string;\n}\n\nexport async function retainDormantContainerMetadataInTransaction(\n tx: ClientSQLiteTransactionScope,\n retained: ReadonlyArray<DormantContainerMetadataRetention>,\n): Promise<void> {\n if (retained.length === 0) {\n return;\n }\n\n await tx\n .insert(dormantContainerMetadata)\n .values([...retained])\n .onConflictDoUpdate({\n target: dormantContainerMetadata.containerId,\n set: {\n organizationId: sql`excluded.organization_id`,\n retainedAt: sql`excluded.retained_at`,\n },\n })\n .run();\n}\n\nexport async function clearDormantContainerMetadataInTransaction(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n if (containerIds.length === 0) {\n return;\n }\n await tx\n .delete(dormantContainerMetadata)\n .where(inArray(dormantContainerMetadata.containerId, [...containerIds]))\n .run();\n}\n\n/** Delete every durable row owned by a container-metadata document scope. */\nexport async function deleteContainerMetadataDocumentRowsInTransaction(\n tx: ClientSQLiteTransactionScope,\n containerIds: ReadonlyArray<string>,\n): Promise<void> {\n const ids = [...containerIds];\n if (ids.length === 0) {\n return;\n }\n await deleteDocumentScopeRowsInTransaction(\n tx,\n CONTAINER_METADATA_APP_KIND,\n ids,\n );\n await clearDormantContainerMetadataInTransaction(tx, ids);\n}\n"]}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { and, asc, eq, inArray, lte, min, sql } from "drizzle-orm";
|
|
2
2
|
import { uniqueSortedStrings } from "../../documents/shared/readers.js";
|
|
3
3
|
import { readStringArray } from "../../shared/recordReaders.js";
|
|
4
|
-
import {
|
|
4
|
+
import { documentContainerProjectionTables } from "../../sqlite/schema/index.js";
|
|
5
5
|
import { documentDiscoveryHeads, documentDiscoverySequence, pendingDocumentDiscoveries, } from "../../sqlite/schema/documentDiscoveryEvidenceSchema.js";
|
|
6
6
|
import { getClientSQLitePersistenceRuntime } from "../../sqlite/sqlitePersistenceRuntime.js";
|
|
7
7
|
import { ensureSqlTables, runSerializedSqlMutation, } from "../../sqlite/sqlSchema.js";
|
|
8
8
|
import { loadDocumentPurgeCheckpoint } from "../keyingCheckpoints/documentPurgeCheckpointPersistence.js";
|
|
9
|
+
import { loadAccessManifestCheckpoint } from "../keyingCheckpoints/keyingCheckpointPersistence.js";
|
|
9
10
|
import { heldTombstoneRetryDelayMs } from "./containerDocumentTombstoneHoldsPersistence.js";
|
|
10
11
|
import { isDiscoveredDocumentCandidate, readStoredDiscoveryCandidate, } from "./documentDiscoveryCandidate.js";
|
|
11
12
|
import { isDocumentDiscoveryGenerationCurrent } from "./documentDiscoveryReset.js";
|
|
@@ -192,14 +193,9 @@ class SqlDocumentDiscoveryEvidenceStore {
|
|
|
192
193
|
// signed checkpoint without updating documents.accessEpoch or this cache.
|
|
193
194
|
// Old-organization pins survive recovery; only the cached signed head's
|
|
194
195
|
// organization can authorize reuse. Missing pins require full verification.
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
.
|
|
198
|
-
.where(and(eq(accessManifestCheckpoints.objectKind, "document"), eq(accessManifestCheckpoints.objectId, documentId), eq(accessManifestCheckpoints.organizationId, row.organizationId)))
|
|
199
|
-
.limit(1);
|
|
200
|
-
if (checkpoints.length !== 1 ||
|
|
201
|
-
checkpoints[0]?.manifestHash !== row.manifestHash ||
|
|
202
|
-
checkpoints[0]?.epoch !== row.accessEpoch)
|
|
196
|
+
const checkpoint = await loadAccessManifestCheckpoint(this.execSql, "document", row.organizationId, documentId);
|
|
197
|
+
if (checkpoint?.manifestHash !== row.manifestHash ||
|
|
198
|
+
checkpoint.epoch !== row.accessEpoch)
|
|
203
199
|
return null;
|
|
204
200
|
return {
|
|
205
201
|
organizationId: row.organizationId,
|