@tearleads/client-sdk 0.1.43 → 0.1.45

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.
Files changed (189) hide show
  1. package/client/containerDocumentDiscovery.js +71 -29
  2. package/client/containerDocumentDiscovery.js.map +1 -1
  3. package/client/deviceFirst.js +4 -5
  4. package/client/deviceFirst.js.map +1 -1
  5. package/data/persistence/container-contents/containerContentsPersistence.js +2 -3
  6. package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
  7. package/data/persistence/container-contents/containerCreateIntentSettlement.js +16 -16
  8. package/data/persistence/container-contents/containerCreateIntentSettlement.js.map +1 -1
  9. package/data/persistence/container-contents/containerDeletionPersistence.js +5 -11
  10. package/data/persistence/container-contents/containerDeletionPersistence.js.map +1 -1
  11. package/data/persistence/container-contents/containerDocumentReassignment.js +13 -13
  12. package/data/persistence/container-contents/containerDocumentReassignment.js.map +1 -1
  13. package/data/persistence/container-contents/containerHydrationPersistence.js +7 -4
  14. package/data/persistence/container-contents/containerHydrationPersistence.js.map +1 -1
  15. package/data/persistence/container-contents/containerIntentPersistence.js +43 -42
  16. package/data/persistence/container-contents/containerIntentPersistence.js.map +1 -1
  17. package/data/persistence/container-contents/containerMetadataMutationPersistence.js +15 -20
  18. package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
  19. package/data/persistence/container-contents/containerMoveAbandonPersistence.js +1 -6
  20. package/data/persistence/container-contents/containerMoveAbandonPersistence.js.map +1 -1
  21. package/data/persistence/container-contents/containerMoveIntentRows.js +9 -8
  22. package/data/persistence/container-contents/containerMoveIntentRows.js.map +1 -1
  23. package/data/persistence/container-contents/containerReconcilePersistence.js +4 -8
  24. package/data/persistence/container-contents/containerReconcilePersistence.js.map +1 -1
  25. package/data/persistence/container-contents/containerRecoveryPersistence.js +20 -19
  26. package/data/persistence/container-contents/containerRecoveryPersistence.js.map +1 -1
  27. package/data/persistence/container-contents/containerRemovalWork.js +10 -10
  28. package/data/persistence/container-contents/containerRemovalWork.js.map +1 -1
  29. package/data/persistence/container-contents/containerStructuralRepair.js +21 -44
  30. package/data/persistence/container-contents/containerStructuralRepair.js.map +1 -1
  31. package/data/persistence/container-contents/documentLinkRemovalRepair.js +12 -11
  32. package/data/persistence/container-contents/documentLinkRemovalRepair.js.map +1 -1
  33. package/data/persistence/container-contents/documentMoveIntentPersistence.js +53 -28
  34. package/data/persistence/container-contents/documentMoveIntentPersistence.js.map +1 -1
  35. package/data/persistence/container-contents/documentPlacementIntentEnqueue.js +9 -8
  36. package/data/persistence/container-contents/documentPlacementIntentEnqueue.js.map +1 -1
  37. package/data/persistence/container-contents/documentPurgePlacement.js +6 -6
  38. package/data/persistence/container-contents/documentPurgePlacement.js.map +1 -1
  39. package/data/persistence/container-contents/dormantContainerMetadata.js +1 -5
  40. package/data/persistence/container-contents/dormantContainerMetadata.js.map +1 -1
  41. package/data/persistence/container-contents/pendingContainerCreate.js +5 -5
  42. package/data/persistence/container-contents/pendingContainerCreate.js.map +1 -1
  43. package/data/persistence/containers/containerPersistence.js +10 -27
  44. package/data/persistence/containers/containerPersistence.js.map +1 -1
  45. package/data/persistence/containers/containerSyncWatermarkPersistence.d.ts +11 -3
  46. package/data/persistence/containers/containerSyncWatermarkPersistence.js +46 -58
  47. package/data/persistence/containers/containerSyncWatermarkPersistence.js.map +1 -1
  48. package/data/persistence/containers/documentContainerProjectionPersistence.js +2 -2
  49. package/data/persistence/containers/documentContainerProjectionPersistence.js.map +1 -1
  50. package/data/persistence/containers/documentPlacement.js +10 -10
  51. package/data/persistence/containers/documentPlacement.js.map +1 -1
  52. package/data/persistence/documents/internal/discardDocument.js +5 -5
  53. package/data/persistence/documents/internal/discardDocument.js.map +1 -1
  54. package/data/persistence/documents/internal/documentDeletionPersistence.js +7 -9
  55. package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
  56. package/data/persistence/documents/internal/documentMutationPersistence.js +6 -8
  57. package/data/persistence/documents/internal/documentMutationPersistence.js.map +1 -1
  58. package/data/persistence/documents/internal/ensureDocumentsSchema.js +2 -2
  59. package/data/persistence/documents/internal/ensureDocumentsSchema.js.map +1 -1
  60. package/data/persistence/documents/internal/orphanSideRows.js +3 -22
  61. package/data/persistence/documents/internal/orphanSideRows.js.map +1 -1
  62. package/data/persistence/organizations/organizationDataUsagePersistence.js +31 -64
  63. package/data/persistence/organizations/organizationDataUsagePersistence.js.map +1 -1
  64. package/data/persistence/organizations/organizationReadModelPurge.js +1 -5
  65. package/data/persistence/organizations/organizationReadModelPurge.js.map +1 -1
  66. package/data/persistence/principalCurrentAcknowledgementPersistence.js +24 -17
  67. package/data/persistence/principalCurrentAcknowledgementPersistence.js.map +1 -1
  68. package/data/persistence/principalHistoryIncompleteRetention.js +3 -9
  69. package/data/persistence/principalHistoryIncompleteRetention.js.map +1 -1
  70. package/data/persistence/principalHistoryStagePersistence.d.ts +3 -9
  71. package/data/persistence/principalHistoryStagePersistence.js +15 -25
  72. package/data/persistence/principalHistoryStagePersistence.js.map +1 -1
  73. package/data/persistence/principalHistoryStageRetention.js +16 -15
  74. package/data/persistence/principalHistoryStageRetention.js.map +1 -1
  75. package/data/persistence/principalHistoryUnpublishedRetention.js +6 -13
  76. package/data/persistence/principalHistoryUnpublishedRetention.js.map +1 -1
  77. package/data/sqlite/containerSchema.d.ts +84 -180
  78. package/data/sqlite/containerSchema.js +14 -23
  79. package/data/sqlite/containerSchema.js.map +1 -1
  80. package/data/sqlite/documentPlacementIntentSchema.js +3 -50
  81. package/data/sqlite/documentPlacementIntentSchema.js.map +1 -1
  82. package/data/sqlite/documentSyncFailurePersistence.d.ts +16 -6
  83. package/data/sqlite/documentSyncFailurePersistence.js +44 -54
  84. package/data/sqlite/documentSyncFailurePersistence.js.map +1 -1
  85. package/data/sqlite/{documentPlacementIntentSchema.d.ts → localIntentSchema.d.ts} +162 -88
  86. package/data/sqlite/localIntentSchema.js +90 -0
  87. package/data/sqlite/localIntentSchema.js.map +1 -0
  88. package/data/sqlite/organizationDataUsageSchema.js +11 -17
  89. package/data/sqlite/organizationDataUsageSchema.js.map +1 -1
  90. package/data/sqlite/principalHistoryRetentionSchema.js +0 -15
  91. package/data/sqlite/principalHistoryRetentionSchema.js.map +1 -1
  92. package/data/sqlite/principalHistoryStageSchema.d.ts +44 -2
  93. package/data/sqlite/principalHistoryStageSchema.js +14 -2
  94. package/data/sqlite/principalHistoryStageSchema.js.map +1 -1
  95. package/data/sqlite/schema.d.ts +1019 -2284
  96. package/data/sqlite/schema.js +48 -169
  97. package/data/sqlite/schema.js.map +1 -1
  98. package/internal/api-client/ApiClient.d.ts +6 -34
  99. package/internal/api-client/ApiClient.js +8 -10
  100. package/internal/api-client/ApiClient.js.map +1 -1
  101. package/internal/api-client/apiRequestRuntime.d.ts +2 -0
  102. package/internal/api-client/apiRequestRuntime.js +4 -0
  103. package/internal/api-client/apiRequestRuntime.js.map +1 -1
  104. package/internal/api-client/requestInternals.js +7 -7
  105. package/internal/api-client/requestInternals.js.map +1 -1
  106. package/internal/api-client/routes/containers/reads.d.ts +13 -7
  107. package/internal/api-client/routes/containers/reads.js +36 -15
  108. package/internal/api-client/routes/containers/reads.js.map +1 -1
  109. package/internal/api-client/types.d.ts +1 -5
  110. package/internal/api-client/types.js.map +1 -1
  111. package/internal/validators/containerReadRefinements.js +15 -5
  112. package/internal/validators/containerReadRefinements.js.map +1 -1
  113. package/internal/validators/containerReadSchemas.d.ts +36 -0
  114. package/internal/validators/containerReadSchemas.js +64 -1
  115. package/internal/validators/containerReadSchemas.js.map +1 -1
  116. package/internal/validators/operation/containerReads.d.ts +118 -182
  117. package/internal/validators/operation/containerReads.js +20 -54
  118. package/internal/validators/operation/containerReads.js.map +1 -1
  119. package/internal/validators/operation/index.d.ts +1 -1
  120. package/internal/validators/operation/index.js +1 -1
  121. package/internal/validators/operation/index.js.map +1 -1
  122. package/internal/validators/operation/registry.d.ts +2 -2
  123. package/internal/validators/operation/registry.js +2 -2
  124. package/internal/validators/operation/registry.js.map +1 -1
  125. package/internal/validators/request/containerDocumentLanes.d.ts +23 -0
  126. package/internal/validators/request/containerDocumentLanes.js +23 -0
  127. package/internal/validators/request/containerDocumentLanes.js.map +1 -0
  128. package/internal/validators/request/containerParentLanes.js +5 -35
  129. package/internal/validators/request/containerParentLanes.js.map +1 -1
  130. package/internal/validators/request/index.d.ts +1 -0
  131. package/internal/validators/request/index.js +1 -0
  132. package/internal/validators/request/index.js.map +1 -1
  133. package/internal/validators/response/containerDocumentLanes.d.ts +82 -0
  134. package/internal/validators/response/containerDocumentLanes.js +35 -0
  135. package/internal/validators/response/containerDocumentLanes.js.map +1 -0
  136. package/internal/validators/response/containerParentLanes.js +3 -15
  137. package/internal/validators/response/containerParentLanes.js.map +1 -1
  138. package/internal/validators/response/index.d.ts +2 -1
  139. package/internal/validators/response/index.js +1 -1
  140. package/internal/validators/response/index.js.map +1 -1
  141. package/package.json +1 -1
  142. package/sync/reconciliation/knownContainerSweep.js +57 -54
  143. package/sync/reconciliation/knownContainerSweep.js.map +1 -1
  144. package/sync/reconciliation/reconcileContainer.js +49 -17
  145. package/sync/reconciliation/reconcileContainer.js.map +1 -1
  146. package/sync/reconciliation/service.js +44 -16
  147. package/sync/reconciliation/service.js.map +1 -1
  148. package/sync/reconciliation/serviceTypes.d.ts +23 -2
  149. package/sync/reconciliation/serviceTypes.js.map +1 -1
  150. package/workflows/container-contents/containerDocumentListing.js +92 -38
  151. package/workflows/container-contents/containerDocumentListing.js.map +1 -1
  152. package/workflows/container-contents/containerPersistence.d.ts +8 -2
  153. package/workflows/container-contents/containerPersistence.js +6 -7
  154. package/workflows/container-contents/containerPersistence.js.map +1 -1
  155. package/workflows/container-contents/documentDiscovery.d.ts +3 -3
  156. package/workflows/container-contents/documentDiscovery.js +24 -96
  157. package/workflows/container-contents/documentDiscovery.js.map +1 -1
  158. package/workflows/container-contents/documentDiscoveryApply.js +65 -0
  159. package/workflows/container-contents/documentDiscoveryApply.js.map +1 -0
  160. package/workflows/container-contents/documentDiscoveryLanes.js +53 -0
  161. package/workflows/container-contents/documentDiscoveryLanes.js.map +1 -0
  162. package/workflows/container-contents/documentDiscoveryTypes.d.ts +25 -19
  163. package/workflows/container-contents/documentDiscoveryTypes.js.map +1 -1
  164. package/workflows/container-contents/documentPriming.js +6 -10
  165. package/workflows/container-contents/documentPriming.js.map +1 -1
  166. package/workflows/container-contents/documentQueries/sql.js +11 -10
  167. package/workflows/container-contents/documentQueries/sql.js.map +1 -1
  168. package/workflows/container-contents/documentQueries.js +2 -2
  169. package/workflows/container-contents/documentQueries.js.map +1 -1
  170. package/workflows/container-contents/pendingDownloads.js +2 -5
  171. package/workflows/container-contents/pendingDownloads.js.map +1 -1
  172. package/workflows/container-contents/pendingWrites/deferredTails.js +3 -11
  173. package/workflows/container-contents/pendingWrites/deferredTails.js.map +1 -1
  174. package/workflows/container-contents/pendingWrites/sources.js +56 -80
  175. package/workflows/container-contents/pendingWrites/sources.js.map +1 -1
  176. package/workflows/container-contents/pendingWrites.js +6 -4
  177. package/workflows/container-contents/pendingWrites.js.map +1 -1
  178. package/workflows/container-contents/remoteContainerState.js +2 -1
  179. package/workflows/container-contents/remoteContainerState.js.map +1 -1
  180. package/workflows/container-contents/remoteHydration/laneFetchMarkers.js +11 -17
  181. package/workflows/container-contents/remoteHydration/laneFetchMarkers.js.map +1 -1
  182. package/workflows/sync/remoteReset.js +16 -23
  183. package/workflows/sync/remoteReset.js.map +1 -1
  184. package/workflows/sync/remoteResetCursorScope.js +10 -18
  185. package/workflows/sync/remoteResetCursorScope.js.map +1 -1
  186. package/workflows/sync/remoteResetPrincipalScope.js +1 -2
  187. package/workflows/sync/remoteResetPrincipalScope.js.map +1 -1
  188. package/workflows/sync/remoteResetScope.js +18 -18
  189. package/workflows/sync/remoteResetScope.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"organizationDataUsagePersistence.js","sourceRoot":"","sources":["organizationDataUsagePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ORGANIZATION_DOCUMENT_USAGE_CATEGORIES,\n type OrganizationDataUsageResponse,\n OrganizationDataUsageResponseSchema,\n type OrganizationDocumentUsageCategory,\n type OrganizationDocumentUsageCategoryBreakdown,\n} from \"@tearleads/validators/response\";\nimport { and, eq } from \"drizzle-orm\";\nimport {\n ORGANIZATION_DATA_USAGE_PROJECTION_VERSION,\n organizationDataUsageCategories,\n organizationDataUsageSnapshots,\n organizationDataUsageTables,\n} from \"../../sqlite/organizationDataUsageSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/organizationReadModelTableRegistry\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { ensureSqlTables } from \"../../sqlite/sqlTableSchema\";\nimport { clearOrganizationPresentationDenialInTransaction } from \"./organizationPresentationDenialPersistence\";\n\ntype StoredCategory = typeof organizationDataUsageCategories.$inferSelect;\ntype StoredSnapshot = typeof organizationDataUsageSnapshots.$inferSelect;\n\nfunction isNonNegativeSafeInteger(value: number): boolean {\n return Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction isDocumentUsageCategory(\n value: string,\n): value is OrganizationDocumentUsageCategory {\n return ORGANIZATION_DOCUMENT_USAGE_CATEGORIES.some(\n (category) => category === value,\n );\n}\n\nfunction isIsoTimestamp(value: string): boolean {\n try {\n return value.length > 0 && new Date(value).toISOString() === value;\n } catch {\n return false;\n }\n}\n\nfunction requireScope(organizationId: string, requesterUserId?: string): void {\n if (\n organizationId.length === 0 ||\n (requesterUserId !== undefined && requesterUserId.length === 0)\n ) {\n throw new Error(\"Organization data usage scope is invalid\");\n }\n}\n\nfunction parseResponse(\n response: OrganizationDataUsageResponse,\n): OrganizationDataUsageResponse {\n const parsed = OrganizationDataUsageResponseSchema.safeParse(response);\n if (!parsed.success) {\n throw new Error(\"Organization data usage response is invalid\");\n }\n return parsed.data;\n}\n\nexport async function purgeOrganizationDataUsageProjectionInTransaction(input: {\n readonly organizationId: string;\n readonly requesterUserId?: string | undefined;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n requireScope(input.organizationId, input.requesterUserId);\n const condition = input.requesterUserId\n ? and(\n eq(\n organizationDataUsageCategories.organizationId,\n input.organizationId,\n ),\n eq(\n organizationDataUsageCategories.requesterUserId,\n input.requesterUserId,\n ),\n )\n : eq(organizationDataUsageCategories.organizationId, input.organizationId);\n await input.tx.delete(organizationDataUsageCategories).where(condition).run();\n\n const snapshotCondition = input.requesterUserId\n ? and(\n eq(organizationDataUsageSnapshots.organizationId, input.organizationId),\n eq(\n organizationDataUsageSnapshots.requesterUserId,\n input.requesterUserId,\n ),\n )\n : eq(organizationDataUsageSnapshots.organizationId, input.organizationId);\n await input.tx\n .delete(organizationDataUsageSnapshots)\n .where(snapshotCondition)\n .run();\n}\n\nfunction isStoredSnapshotValid(\n snapshot: StoredSnapshot,\n organizationId: string,\n requesterUserId: string,\n): boolean {\n return (\n snapshot.organizationId === organizationId &&\n snapshot.requesterUserId === requesterUserId &&\n snapshot.projectionVersion === ORGANIZATION_DATA_USAGE_PROJECTION_VERSION &&\n isNonNegativeSafeInteger(snapshot.blobCount) &&\n isNonNegativeSafeInteger(snapshot.blobByteLength) &&\n isNonNegativeSafeInteger(snapshot.documentByteLength) &&\n isNonNegativeSafeInteger(snapshot.documentCount) &&\n isNonNegativeSafeInteger(snapshot.documentUpdateCount) &&\n isNonNegativeSafeInteger(snapshot.totalByteLength) &&\n isIsoTimestamp(snapshot.refreshedAt)\n );\n}\n\nfunction storedResponse(\n snapshot: StoredSnapshot,\n categories: readonly StoredCategory[],\n): OrganizationDataUsageResponse | null {\n if (categories.length !== ORGANIZATION_DOCUMENT_USAGE_CATEGORIES.length) {\n return null;\n }\n const byCategory = new Map<\n OrganizationDocumentUsageCategory,\n OrganizationDocumentUsageCategoryBreakdown\n >();\n for (const row of categories) {\n const category = row.category;\n if (\n row.organizationId !== snapshot.organizationId ||\n row.requesterUserId !== snapshot.requesterUserId ||\n !isDocumentUsageCategory(category) ||\n byCategory.has(category) ||\n !isNonNegativeSafeInteger(row.byteLength) ||\n !isNonNegativeSafeInteger(row.documentCount) ||\n !isNonNegativeSafeInteger(row.updateCount)\n ) {\n return null;\n }\n byCategory.set(category, {\n byteLength: row.byteLength,\n category,\n documentCount: row.documentCount,\n updateCount: row.updateCount,\n });\n }\n const breakdown = ORGANIZATION_DOCUMENT_USAGE_CATEGORIES.flatMap(\n (category) => {\n const entry = byCategory.get(category);\n return entry ? [entry] : [];\n },\n );\n if (breakdown.length !== ORGANIZATION_DOCUMENT_USAGE_CATEGORIES.length) {\n return null;\n }\n\n const response: OrganizationDataUsageResponse = {\n organizationId: snapshot.organizationId,\n blobs: {\n blobCount: snapshot.blobCount,\n byteLength: snapshot.blobByteLength,\n },\n documents: {\n breakdown,\n byteLength: snapshot.documentByteLength,\n documentCount: snapshot.documentCount,\n updateCount: snapshot.documentUpdateCount,\n },\n totalByteLength: snapshot.totalByteLength,\n };\n const parsed = OrganizationDataUsageResponseSchema.safeParse(response);\n return parsed.success ? parsed.data : null;\n}\n\nexport async function loadOrganizationDataUsageProjection(\n execSql: ExecSql,\n organizationId: string,\n requesterUserId: string,\n): Promise<OrganizationDataUsageResponse | null> {\n requireScope(organizationId, requesterUserId);\n await ensureSqlTables(execSql, organizationDataUsageTables);\n return getClientSQLitePersistenceRuntime(execSql).transaction(async (tx) => {\n const scope = and(\n eq(organizationDataUsageSnapshots.organizationId, organizationId),\n eq(organizationDataUsageSnapshots.requesterUserId, requesterUserId),\n );\n const [snapshot] = await tx\n .select()\n .from(organizationDataUsageSnapshots)\n .where(scope)\n .limit(1);\n const categories = await tx\n .select()\n .from(organizationDataUsageCategories)\n .where(\n and(\n eq(organizationDataUsageCategories.organizationId, organizationId),\n eq(organizationDataUsageCategories.requesterUserId, requesterUserId),\n ),\n );\n if (!snapshot) {\n if (categories.length > 0) {\n await purgeOrganizationDataUsageProjectionInTransaction({\n organizationId,\n requesterUserId,\n tx,\n });\n }\n return null;\n }\n const response = isStoredSnapshotValid(\n snapshot,\n organizationId,\n requesterUserId,\n )\n ? storedResponse(snapshot, categories)\n : null;\n if (!response) {\n await purgeOrganizationDataUsageProjectionInTransaction({\n organizationId,\n requesterUserId,\n tx,\n });\n }\n return response;\n });\n}\n\nexport async function saveOrganizationDataUsageProjection(input: {\n readonly execSql: ExecSql;\n readonly requesterUserId: string;\n readonly response: OrganizationDataUsageResponse;\n}): Promise<void> {\n const response = parseResponse(input.response);\n requireScope(response.organizationId, input.requesterUserId);\n await ensureSqlTables(input.execSql, [\n ...organizationDataUsageTables,\n ...organizationReadModelTables,\n ]);\n await getClientSQLitePersistenceRuntime(input.execSql).transaction(\n async (tx) => {\n // Storing an authoritative usage response proves access: retire any\n // durable denial marker for this scope in the same transaction.\n await clearOrganizationPresentationDenialInTransaction({\n organizationId: response.organizationId,\n projection: \"usage\",\n requesterUserId: input.requesterUserId,\n tx,\n });\n const scope = and(\n eq(\n organizationDataUsageCategories.organizationId,\n response.organizationId,\n ),\n eq(\n organizationDataUsageCategories.requesterUserId,\n input.requesterUserId,\n ),\n );\n await tx.delete(organizationDataUsageCategories).where(scope).run();\n\n const snapshot = {\n organizationId: response.organizationId,\n requesterUserId: input.requesterUserId,\n projectionVersion: ORGANIZATION_DATA_USAGE_PROJECTION_VERSION,\n blobCount: response.blobs.blobCount,\n blobByteLength: response.blobs.byteLength,\n documentByteLength: response.documents.byteLength,\n documentCount: response.documents.documentCount,\n documentUpdateCount: response.documents.updateCount,\n totalByteLength: response.totalByteLength,\n refreshedAt: new Date().toISOString(),\n };\n await tx\n .insert(organizationDataUsageSnapshots)\n .values(snapshot)\n .onConflictDoUpdate({\n target: [\n organizationDataUsageSnapshots.organizationId,\n organizationDataUsageSnapshots.requesterUserId,\n ],\n set: {\n projectionVersion: snapshot.projectionVersion,\n blobCount: snapshot.blobCount,\n blobByteLength: snapshot.blobByteLength,\n documentByteLength: snapshot.documentByteLength,\n documentCount: snapshot.documentCount,\n documentUpdateCount: snapshot.documentUpdateCount,\n totalByteLength: snapshot.totalByteLength,\n refreshedAt: snapshot.refreshedAt,\n },\n })\n .run();\n await tx\n .insert(organizationDataUsageCategories)\n .values(\n response.documents.breakdown.map((entry) => ({\n organizationId: response.organizationId,\n requesterUserId: input.requesterUserId,\n category: entry.category,\n byteLength: entry.byteLength,\n documentCount: entry.documentCount,\n updateCount: entry.updateCount,\n })),\n )\n .run();\n },\n );\n}\n\nexport async function purgeOrganizationDataUsageProjection(\n execSql: ExecSql,\n organizationId: string,\n requesterUserId?: string,\n): Promise<void> {\n requireScope(organizationId, requesterUserId);\n await ensureSqlTables(execSql, organizationDataUsageTables);\n await getClientSQLitePersistenceRuntime(execSql).transaction(async (tx) => {\n await purgeOrganizationDataUsageProjectionInTransaction({\n organizationId,\n requesterUserId,\n tx,\n });\n });\n}\n"]}
1
+ {"version":3,"file":"organizationDataUsagePersistence.js","sourceRoot":"","sources":["organizationDataUsagePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ORGANIZATION_DOCUMENT_USAGE_CATEGORIES,\n type OrganizationDataUsageResponse,\n OrganizationDataUsageResponseSchema,\n} from \"@tearleads/validators/response\";\nimport { and, eq } from \"drizzle-orm\";\nimport {\n ORGANIZATION_DATA_USAGE_PROJECTION_VERSION,\n organizationDataUsageSnapshots,\n organizationDataUsageTables,\n} from \"../../sqlite/organizationDataUsageSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/organizationReadModelTableRegistry\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { ensureSqlTables } from \"../../sqlite/sqlTableSchema\";\nimport { clearOrganizationPresentationDenialInTransaction } from \"./organizationPresentationDenialPersistence\";\n\ntype StoredSnapshot = typeof organizationDataUsageSnapshots.$inferSelect;\n\nfunction isNonNegativeSafeInteger(value: number): boolean {\n return Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction isIsoTimestamp(value: string): boolean {\n try {\n return value.length > 0 && new Date(value).toISOString() === value;\n } catch {\n return false;\n }\n}\n\nfunction requireScope(organizationId: string, requesterUserId?: string): void {\n if (\n organizationId.length === 0 ||\n (requesterUserId !== undefined && requesterUserId.length === 0)\n ) {\n throw new Error(\"Organization data usage scope is invalid\");\n }\n}\n\nfunction parseResponse(\n response: OrganizationDataUsageResponse,\n): OrganizationDataUsageResponse {\n const parsed = OrganizationDataUsageResponseSchema.safeParse(response);\n if (!parsed.success) {\n throw new Error(\"Organization data usage response is invalid\");\n }\n return parsed.data;\n}\n\nexport async function purgeOrganizationDataUsageProjectionInTransaction(input: {\n readonly organizationId: string;\n readonly requesterUserId?: string | undefined;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n requireScope(input.organizationId, input.requesterUserId);\n const snapshotCondition = input.requesterUserId\n ? and(\n eq(organizationDataUsageSnapshots.organizationId, input.organizationId),\n eq(\n organizationDataUsageSnapshots.requesterUserId,\n input.requesterUserId,\n ),\n )\n : eq(organizationDataUsageSnapshots.organizationId, input.organizationId);\n await input.tx\n .delete(organizationDataUsageSnapshots)\n .where(snapshotCondition)\n .run();\n}\n\nfunction isStoredSnapshotValid(\n snapshot: StoredSnapshot,\n organizationId: string,\n requesterUserId: string,\n): boolean {\n return (\n snapshot.organizationId === organizationId &&\n snapshot.requesterUserId === requesterUserId &&\n snapshot.projectionVersion === ORGANIZATION_DATA_USAGE_PROJECTION_VERSION &&\n isNonNegativeSafeInteger(snapshot.blobCount) &&\n isNonNegativeSafeInteger(snapshot.blobByteLength) &&\n isNonNegativeSafeInteger(snapshot.documentByteLength) &&\n isNonNegativeSafeInteger(snapshot.documentCount) &&\n isNonNegativeSafeInteger(snapshot.documentUpdateCount) &&\n isNonNegativeSafeInteger(snapshot.totalByteLength) &&\n isIsoTimestamp(snapshot.refreshedAt)\n );\n}\n\nfunction parseStoredBreakdown(json: string): unknown {\n try {\n return JSON.parse(json);\n } catch {\n return null;\n }\n}\n\n// The stored breakdown is untrusted local JSON: the strict response schema\n// re-checks every entry (known category, each exactly once, safe non-negative\n// counts that sum to the stored totals) before it is served.\nfunction storedResponse(\n snapshot: StoredSnapshot,\n): OrganizationDataUsageResponse | null {\n const parsed = OrganizationDataUsageResponseSchema.safeParse({\n organizationId: snapshot.organizationId,\n blobs: {\n blobCount: snapshot.blobCount,\n byteLength: snapshot.blobByteLength,\n },\n documents: {\n breakdown: parseStoredBreakdown(snapshot.categoryBreakdownJson),\n byteLength: snapshot.documentByteLength,\n documentCount: snapshot.documentCount,\n updateCount: snapshot.documentUpdateCount,\n },\n totalByteLength: snapshot.totalByteLength,\n });\n return parsed.success ? parsed.data : null;\n}\n\nfunction canonicalBreakdownJson(\n response: OrganizationDataUsageResponse,\n): string {\n const order = new Map<string, number>(\n ORGANIZATION_DOCUMENT_USAGE_CATEGORIES.map((category, index) => [\n category,\n index,\n ]),\n );\n const breakdown = [...response.documents.breakdown]\n .sort(\n (left, right) =>\n (order.get(left.category) ?? 0) - (order.get(right.category) ?? 0),\n )\n .map((entry) => ({\n byteLength: entry.byteLength,\n category: entry.category,\n documentCount: entry.documentCount,\n updateCount: entry.updateCount,\n }));\n return JSON.stringify(breakdown);\n}\n\nexport async function loadOrganizationDataUsageProjection(\n execSql: ExecSql,\n organizationId: string,\n requesterUserId: string,\n): Promise<OrganizationDataUsageResponse | null> {\n requireScope(organizationId, requesterUserId);\n await ensureSqlTables(execSql, organizationDataUsageTables);\n return getClientSQLitePersistenceRuntime(execSql).transaction(async (tx) => {\n const scope = and(\n eq(organizationDataUsageSnapshots.organizationId, organizationId),\n eq(organizationDataUsageSnapshots.requesterUserId, requesterUserId),\n );\n const [snapshot] = await tx\n .select()\n .from(organizationDataUsageSnapshots)\n .where(scope)\n .limit(1);\n if (!snapshot) {\n return null;\n }\n const response = isStoredSnapshotValid(\n snapshot,\n organizationId,\n requesterUserId,\n )\n ? storedResponse(snapshot)\n : null;\n if (!response) {\n await purgeOrganizationDataUsageProjectionInTransaction({\n organizationId,\n requesterUserId,\n tx,\n });\n }\n return response;\n });\n}\n\nexport async function saveOrganizationDataUsageProjection(input: {\n readonly execSql: ExecSql;\n readonly requesterUserId: string;\n readonly response: OrganizationDataUsageResponse;\n}): Promise<void> {\n const response = parseResponse(input.response);\n requireScope(response.organizationId, input.requesterUserId);\n await ensureSqlTables(input.execSql, [\n ...organizationDataUsageTables,\n ...organizationReadModelTables,\n ]);\n await getClientSQLitePersistenceRuntime(input.execSql).transaction(\n async (tx) => {\n // Storing an authoritative usage response proves access: retire any\n // durable denial marker for this scope in the same transaction.\n await clearOrganizationPresentationDenialInTransaction({\n organizationId: response.organizationId,\n projection: \"usage\",\n requesterUserId: input.requesterUserId,\n tx,\n });\n const snapshot = {\n organizationId: response.organizationId,\n requesterUserId: input.requesterUserId,\n projectionVersion: ORGANIZATION_DATA_USAGE_PROJECTION_VERSION,\n blobCount: response.blobs.blobCount,\n blobByteLength: response.blobs.byteLength,\n documentByteLength: response.documents.byteLength,\n documentCount: response.documents.documentCount,\n documentUpdateCount: response.documents.updateCount,\n totalByteLength: response.totalByteLength,\n categoryBreakdownJson: canonicalBreakdownJson(response),\n refreshedAt: new Date().toISOString(),\n };\n await tx\n .insert(organizationDataUsageSnapshots)\n .values(snapshot)\n .onConflictDoUpdate({\n target: [\n organizationDataUsageSnapshots.organizationId,\n organizationDataUsageSnapshots.requesterUserId,\n ],\n set: {\n projectionVersion: snapshot.projectionVersion,\n blobCount: snapshot.blobCount,\n blobByteLength: snapshot.blobByteLength,\n documentByteLength: snapshot.documentByteLength,\n documentCount: snapshot.documentCount,\n documentUpdateCount: snapshot.documentUpdateCount,\n totalByteLength: snapshot.totalByteLength,\n categoryBreakdownJson: snapshot.categoryBreakdownJson,\n refreshedAt: snapshot.refreshedAt,\n },\n })\n .run();\n },\n );\n}\n\nexport async function purgeOrganizationDataUsageProjection(\n execSql: ExecSql,\n organizationId: string,\n requesterUserId?: string,\n): Promise<void> {\n requireScope(organizationId, requesterUserId);\n await ensureSqlTables(execSql, organizationDataUsageTables);\n await getClientSQLitePersistenceRuntime(execSql).transaction(async (tx) => {\n await purgeOrganizationDataUsageProjectionInTransaction({\n organizationId,\n requesterUserId,\n tx,\n });\n });\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { and, eq, ne, notExists } from "drizzle-orm";
2
- import { organizationDataUsageCategories, organizationDataUsageSnapshots, } from "../../sqlite/organizationDataUsageSchema.js";
2
+ import { organizationDataUsageSnapshots } from "../../sqlite/organizationDataUsageSchema.js";
3
3
  import { organizationReadModelContainerGrants, organizationReadModelDirectoryUsers, organizationReadModelGroupMembers, organizationReadModelGroups, organizationReadModelRequesters, organizationReadModelState, } from "../../sqlite/organizationReadModelSchema.js";
4
4
  import { organizationReadModelTables } from "../../sqlite/schema.js";
5
5
  import { getClientSQLitePersistenceRuntime, } from "../../sqlite/sqlitePersistenceRuntime.js";
@@ -36,10 +36,6 @@ export async function pruneInactiveOrganizationRequestersInTransaction(input) {
36
36
  .delete(organizationDataUsageSnapshots)
37
37
  .where(inactiveUserScope(input, organizationDataUsageSnapshots.organizationId, organizationDataUsageSnapshots.requesterUserId))
38
38
  .run();
39
- await input.tx
40
- .delete(organizationDataUsageCategories)
41
- .where(inactiveUserScope(input, organizationDataUsageCategories.organizationId, organizationDataUsageCategories.requesterUserId))
42
- .run();
43
39
  }
44
40
  /**
45
41
  * Delete the shared projection rows for one organization. By default every
@@ -1 +1 @@
1
- {"version":3,"file":"organizationReadModelPurge.js","sourceRoot":"","sources":["organizationReadModelPurge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAe,EAAE,EAAE,EAAE,EAAE,SAAS,EAAY,MAAM,aAAa,CAAC;AAC5E,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,oCAAoC,EACpC,mCAAmC,EACnC,iCAAiC,EACjC,2BAA2B,EAC3B,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAEL,iCAAiC,GAClC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAgB,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,sCAAsC,EAAE,MAAM,qCAAqC,CAAC;AAC7F,OAAO,EAAE,mCAAmC,EAAE,MAAM,iCAAiC,CAAC;AAEtF;;;;GAIG;AACH,SAAS,iBAAiB,CACxB,KAIC,EACD,oBAA4B,EAC5B,YAAoB;IAEpB,MAAM,SAAS,GAAG,mCAAmC,CAAC;IACtD,OAAO,GAAG,CACR,EAAE,CAAC,oBAAoB,EAAE,KAAK,CAAC,cAAc,CAAC,EAC9C,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC,EACpC,SAAS,CACP,KAAK,CAAC,EAAE;SACL,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;SACpC,IAAI,CAAC,SAAS,CAAC;SACf,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,SAAS,CAAC,cAAc,EAAE,KAAK,CAAC,cAAc,CAAC,EAClD,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC,EAClC,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAC/B,CACF,CACJ,CACF,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gDAAgD,CAAC,KAItE;IACC,MAAM,KAAK,CAAC,EAAE;SACX,MAAM,CAAC,+BAA+B,CAAC;SACvC,KAAK,CACJ,iBAAiB,CACf,KAAK,EACL,+BAA+B,CAAC,cAAc,EAC9C,+BAA+B,CAAC,MAAM,CACvC,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,KAAK,CAAC,EAAE;SACX,MAAM,CAAC,8BAA8B,CAAC;SACtC,KAAK,CACJ,iBAAiB,CACf,KAAK,EACL,8BAA8B,CAAC,cAAc,EAC7C,8BAA8B,CAAC,eAAe,CAC/C,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,KAAK,CAAC,EAAE;SACX,MAAM,CAAC,+BAA+B,CAAC;SACvC,KAAK,CACJ,iBAAiB,CACf,KAAK,EACL,+BAA+B,CAAC,cAAc,EAC9C,+BAA+B,CAAC,eAAe,CAChD,CACF;SACA,GAAG,EAAE,CAAC;AACX,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iDAAiD,CAAC,KAIvE;IACC,MAAM,KAAK,CAAC,EAAE;SACX,MAAM,CAAC,oCAAoC,CAAC;SAC5C,KAAK,CACJ,EAAE,CACA,oCAAoC,CAAC,cAAc,EACnD,KAAK,CAAC,cAAc,CACrB,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,KAAK,CAAC,EAAE;SACX,MAAM,CAAC,iCAAiC,CAAC;SACzC,KAAK,CACJ,EAAE,CACA,iCAAiC,CAAC,cAAc,EAChD,KAAK,CAAC,cAAc,CACrB,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,KAAK,CAAC,EAAE;SACX,MAAM,CAAC,mCAAmC,CAAC;SAC3C,KAAK,CACJ,EAAE,CACA,mCAAmC,CAAC,cAAc,EAClD,KAAK,CAAC,cAAc,CACrB,CACF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,KAAK,CAAC,EAAE;SACX,MAAM,CAAC,2BAA2B,CAAC;SACnC,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAAC,cAAc,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;SAC3E,GAAG,EAAE,CAAC;IACT,MAAM,KAAK,CAAC,EAAE;SACX,MAAM,CAAC,+BAA+B,CAAC;SACvC,KAAK,CACJ,KAAK,CAAC,mBAAmB,KAAK,SAAS;QACrC,CAAC,CAAC,EAAE,CACA,+BAA+B,CAAC,cAAc,EAC9C,KAAK,CAAC,cAAc,CACrB;QACH,CAAC,CAAC,GAAG,CACD,EAAE,CACA,+BAA+B,CAAC,cAAc,EAC9C,KAAK,CAAC,cAAc,CACrB,EACD,EAAE,CACA,+BAA+B,CAAC,MAAM,EACtC,KAAK,CAAC,mBAAmB,CAC1B,CACF,CACN;SACA,GAAG,EAAE,CAAC;IACT,MAAM,KAAK,CAAC,EAAE;SACX,MAAM,CAAC,0BAA0B,CAAC;SAClC,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAAC,cAAc,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;SAC1E,GAAG,EAAE,CAAC;AACX,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,2CAA2C,CAAI,KAIpE;IACC,MAAM,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,2BAA2B,CAAC,CAAC;IAClE,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAC9B,MAAM,MAAM,GAAG,MAAM,iCAAiC,CACpD,KAAK,CAAC,OAAO,CACd,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACzB,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,mCAAmC,CAAC,EAAE,CAAC;gBAC5D,MAAM,KAAK,CAAC;YACd,CAAC;YACD,MAAM,iDAAiD,CAAC;gBACtD,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,EAAE;aACH,CAAC,CAAC;YACH,iBAAiB,GAAG,IAAI,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;IACH,IAAI,iBAAiB,EAAE,CAAC;QACtB,yEAAyE;QACzE,yEAAyE;QACzE,sCAAsC,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { and, type Column, eq, ne, notExists, type SQL } from \"drizzle-orm\";\nimport {\n organizationDataUsageCategories,\n organizationDataUsageSnapshots,\n} from \"../../sqlite/organizationDataUsageSchema\";\nimport {\n organizationReadModelContainerGrants,\n organizationReadModelDirectoryUsers,\n organizationReadModelGroupMembers,\n organizationReadModelGroups,\n organizationReadModelRequesters,\n organizationReadModelState,\n} from \"../../sqlite/organizationReadModelSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport { notifyOrganizationReadModelInvalidated } from \"./organizationReadModelInvalidation\";\nimport { OrganizationReadModelIntegrityError } from \"./organizationReadModelProtocol\";\n\n/**\n * Rows for users the persisted directory does not list as active. The\n * exclusion is one anti-join against the directory rows rather than a bound\n * list, so an organization of any size fits SQLite's bound-variable budget.\n */\nfunction inactiveUserScope(\n input: {\n readonly organizationId: string;\n readonly retainUserId: string;\n readonly tx: ClientSQLiteTransactionScope;\n },\n organizationIdColumn: Column,\n userIdColumn: Column,\n): SQL | undefined {\n const directory = organizationReadModelDirectoryUsers;\n return and(\n eq(organizationIdColumn, input.organizationId),\n ne(userIdColumn, input.retainUserId),\n notExists(\n input.tx\n .select({ userId: directory.userId })\n .from(directory)\n .where(\n and(\n eq(directory.organizationId, input.organizationId),\n eq(directory.userId, userIdColumn),\n eq(directory.status, \"active\"),\n ),\n ),\n ),\n );\n}\n\n/**\n * Retire requester rows — and the requester-scoped usage projections beside\n * them — for users the just-applied authoritative directory no longer lists as\n * active. Without this, a removed member whose identity shares the database\n * keeps presentation access until it personally sees a 403. The applying\n * requester's own rows are never pruned here: the server response that\n * carried this directory already proved that user's access. The caller must\n * have replaced the organization's directory rows earlier in `tx`.\n */\nexport async function pruneInactiveOrganizationRequestersInTransaction(input: {\n readonly organizationId: string;\n readonly retainUserId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await input.tx\n .delete(organizationReadModelRequesters)\n .where(\n inactiveUserScope(\n input,\n organizationReadModelRequesters.organizationId,\n organizationReadModelRequesters.userId,\n ),\n )\n .run();\n await input.tx\n .delete(organizationDataUsageSnapshots)\n .where(\n inactiveUserScope(\n input,\n organizationDataUsageSnapshots.organizationId,\n organizationDataUsageSnapshots.requesterUserId,\n ),\n )\n .run();\n await input.tx\n .delete(organizationDataUsageCategories)\n .where(\n inactiveUserScope(\n input,\n organizationDataUsageCategories.organizationId,\n organizationDataUsageCategories.requesterUserId,\n ),\n )\n .run();\n}\n\n/**\n * Delete the shared projection rows for one organization. By default every\n * requester row falls with them; `onlyRequesterUserId` scopes the requester\n * delete to that user for rebuilds where the shared rows are about to be\n * replaced authoritatively — other local identities keep their rows and read\n * the replacement instead of losing access until their own next reconcile.\n */\nexport async function purgeOrganizationReadModelProjectionInTransaction(input: {\n readonly onlyRequesterUserId?: string | undefined;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await input.tx\n .delete(organizationReadModelContainerGrants)\n .where(\n eq(\n organizationReadModelContainerGrants.organizationId,\n input.organizationId,\n ),\n )\n .run();\n await input.tx\n .delete(organizationReadModelGroupMembers)\n .where(\n eq(\n organizationReadModelGroupMembers.organizationId,\n input.organizationId,\n ),\n )\n .run();\n await input.tx\n .delete(organizationReadModelDirectoryUsers)\n .where(\n eq(\n organizationReadModelDirectoryUsers.organizationId,\n input.organizationId,\n ),\n )\n .run();\n await input.tx\n .delete(organizationReadModelGroups)\n .where(eq(organizationReadModelGroups.organizationId, input.organizationId))\n .run();\n await input.tx\n .delete(organizationReadModelRequesters)\n .where(\n input.onlyRequesterUserId === undefined\n ? eq(\n organizationReadModelRequesters.organizationId,\n input.organizationId,\n )\n : and(\n eq(\n organizationReadModelRequesters.organizationId,\n input.organizationId,\n ),\n eq(\n organizationReadModelRequesters.userId,\n input.onlyRequesterUserId,\n ),\n ),\n )\n .run();\n await input.tx\n .delete(organizationReadModelState)\n .where(eq(organizationReadModelState.organizationId, input.organizationId))\n .run();\n}\n\n/**\n * The ONE load shell for the durable read-model projection: run `load` in a\n * transaction and, if a stored row fails integrity validation, purge the\n * organization's projection and report null. The projection is a\n * server-refetchable cache, so the next reconcile requests a cursorless\n * snapshot instead of every read and repair pass failing on the same row.\n */\nexport async function loadWithOrganizationReadModelIntegrityPurge<T>(input: {\n readonly execSql: ExecSql;\n readonly load: (tx: ClientSQLiteTransactionScope) => Promise<T | null>;\n readonly organizationId: string;\n}): Promise<T | null> {\n await ensureSqlTables(input.execSql, organizationReadModelTables);\n let purgedInvalidRows = false;\n const result = await getClientSQLitePersistenceRuntime(\n input.execSql,\n ).transaction(async (tx) => {\n try {\n return await input.load(tx);\n } catch (error) {\n if (!(error instanceof OrganizationReadModelIntegrityError)) {\n throw error;\n }\n await purgeOrganizationReadModelProjectionInTransaction({\n organizationId: input.organizationId,\n tx,\n });\n purgedInvalidRows = true;\n return null;\n }\n });\n if (purgedInvalidRows) {\n // Realtime consumers may hold a caught-up lease over rows that no longer\n // exist; the notification lets them drop it and refetch authoritatively.\n notifyOrganizationReadModelInvalidated(input.execSql, input.organizationId);\n }\n return result;\n}\n"]}
1
+ {"version":3,"file":"organizationReadModelPurge.js","sourceRoot":"","sources":["organizationReadModelPurge.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, type Column, eq, ne, notExists, type SQL } from \"drizzle-orm\";\nimport { organizationDataUsageSnapshots } from \"../../sqlite/organizationDataUsageSchema\";\nimport {\n organizationReadModelContainerGrants,\n organizationReadModelDirectoryUsers,\n organizationReadModelGroupMembers,\n organizationReadModelGroups,\n organizationReadModelRequesters,\n organizationReadModelState,\n} from \"../../sqlite/organizationReadModelSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport { notifyOrganizationReadModelInvalidated } from \"./organizationReadModelInvalidation\";\nimport { OrganizationReadModelIntegrityError } from \"./organizationReadModelProtocol\";\n\n/**\n * Rows for users the persisted directory does not list as active. The\n * exclusion is one anti-join against the directory rows rather than a bound\n * list, so an organization of any size fits SQLite's bound-variable budget.\n */\nfunction inactiveUserScope(\n input: {\n readonly organizationId: string;\n readonly retainUserId: string;\n readonly tx: ClientSQLiteTransactionScope;\n },\n organizationIdColumn: Column,\n userIdColumn: Column,\n): SQL | undefined {\n const directory = organizationReadModelDirectoryUsers;\n return and(\n eq(organizationIdColumn, input.organizationId),\n ne(userIdColumn, input.retainUserId),\n notExists(\n input.tx\n .select({ userId: directory.userId })\n .from(directory)\n .where(\n and(\n eq(directory.organizationId, input.organizationId),\n eq(directory.userId, userIdColumn),\n eq(directory.status, \"active\"),\n ),\n ),\n ),\n );\n}\n\n/**\n * Retire requester rows — and the requester-scoped usage projections beside\n * them — for users the just-applied authoritative directory no longer lists as\n * active. Without this, a removed member whose identity shares the database\n * keeps presentation access until it personally sees a 403. The applying\n * requester's own rows are never pruned here: the server response that\n * carried this directory already proved that user's access. The caller must\n * have replaced the organization's directory rows earlier in `tx`.\n */\nexport async function pruneInactiveOrganizationRequestersInTransaction(input: {\n readonly organizationId: string;\n readonly retainUserId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await input.tx\n .delete(organizationReadModelRequesters)\n .where(\n inactiveUserScope(\n input,\n organizationReadModelRequesters.organizationId,\n organizationReadModelRequesters.userId,\n ),\n )\n .run();\n await input.tx\n .delete(organizationDataUsageSnapshots)\n .where(\n inactiveUserScope(\n input,\n organizationDataUsageSnapshots.organizationId,\n organizationDataUsageSnapshots.requesterUserId,\n ),\n )\n .run();\n}\n\n/**\n * Delete the shared projection rows for one organization. By default every\n * requester row falls with them; `onlyRequesterUserId` scopes the requester\n * delete to that user for rebuilds where the shared rows are about to be\n * replaced authoritatively — other local identities keep their rows and read\n * the replacement instead of losing access until their own next reconcile.\n */\nexport async function purgeOrganizationReadModelProjectionInTransaction(input: {\n readonly onlyRequesterUserId?: string | undefined;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await input.tx\n .delete(organizationReadModelContainerGrants)\n .where(\n eq(\n organizationReadModelContainerGrants.organizationId,\n input.organizationId,\n ),\n )\n .run();\n await input.tx\n .delete(organizationReadModelGroupMembers)\n .where(\n eq(\n organizationReadModelGroupMembers.organizationId,\n input.organizationId,\n ),\n )\n .run();\n await input.tx\n .delete(organizationReadModelDirectoryUsers)\n .where(\n eq(\n organizationReadModelDirectoryUsers.organizationId,\n input.organizationId,\n ),\n )\n .run();\n await input.tx\n .delete(organizationReadModelGroups)\n .where(eq(organizationReadModelGroups.organizationId, input.organizationId))\n .run();\n await input.tx\n .delete(organizationReadModelRequesters)\n .where(\n input.onlyRequesterUserId === undefined\n ? eq(\n organizationReadModelRequesters.organizationId,\n input.organizationId,\n )\n : and(\n eq(\n organizationReadModelRequesters.organizationId,\n input.organizationId,\n ),\n eq(\n organizationReadModelRequesters.userId,\n input.onlyRequesterUserId,\n ),\n ),\n )\n .run();\n await input.tx\n .delete(organizationReadModelState)\n .where(eq(organizationReadModelState.organizationId, input.organizationId))\n .run();\n}\n\n/**\n * The ONE load shell for the durable read-model projection: run `load` in a\n * transaction and, if a stored row fails integrity validation, purge the\n * organization's projection and report null. The projection is a\n * server-refetchable cache, so the next reconcile requests a cursorless\n * snapshot instead of every read and repair pass failing on the same row.\n */\nexport async function loadWithOrganizationReadModelIntegrityPurge<T>(input: {\n readonly execSql: ExecSql;\n readonly load: (tx: ClientSQLiteTransactionScope) => Promise<T | null>;\n readonly organizationId: string;\n}): Promise<T | null> {\n await ensureSqlTables(input.execSql, organizationReadModelTables);\n let purgedInvalidRows = false;\n const result = await getClientSQLitePersistenceRuntime(\n input.execSql,\n ).transaction(async (tx) => {\n try {\n return await input.load(tx);\n } catch (error) {\n if (!(error instanceof OrganizationReadModelIntegrityError)) {\n throw error;\n }\n await purgeOrganizationReadModelProjectionInTransaction({\n organizationId: input.organizationId,\n tx,\n });\n purgedInvalidRows = true;\n return null;\n }\n });\n if (purgedInvalidRows) {\n // Realtime consumers may hold a caught-up lease over rows that no longer\n // exist; the notification lets them drop it and refetch authoritatively.\n notifyOrganizationReadModelInvalidated(input.execSql, input.organizationId);\n }\n return result;\n}\n"]}
@@ -11,7 +11,7 @@ import { storePrincipalGrantRetirements } from "./principalGrantRetirementPersis
11
11
  import { writePrincipalHistoryEvidencePage, } from "./principalHistoryEvidencePersistence.js";
12
12
  import { reclaimPrincipalHistoryNodes } from "./principalHistoryNodeRetention.js";
13
13
  import { retainPrincipalHistoryRoot } from "./principalHistoryRootOwnership.js";
14
- import { reclaimCompletedPrincipalHistoryStages, recordPrincipalHistoryStageScope, } from "./principalHistoryStageRetention.js";
14
+ import { reclaimCompletedPrincipalHistoryStages, touchPrincipalHistoryStage, } from "./principalHistoryStageRetention.js";
15
15
  import { archivePrincipalHistoryKeyEnvelopes } from "./principalKeyEnvelopeArchivePersistence.js";
16
16
  /** A local writer won the CAS; retry the same authenticated receipt without HTTP. */
17
17
  export class PrincipalAcknowledgementChangedError extends Error {
@@ -73,11 +73,8 @@ async function archiveAcknowledgedStageKeys(tx, entry) {
73
73
  for (const stage of [entry.predecessorStage, entry.stage]) {
74
74
  if (!stage)
75
75
  continue;
76
- await recordPrincipalHistoryStageScope(tx, {
76
+ await touchPrincipalHistoryStage(tx, {
77
77
  id: stage.id,
78
- afterVersion: stage.afterVersion,
79
- complete: stage.complete,
80
- organizationId: entry.prefix.organizationId,
81
78
  scopeId: entry.prefix.scopeId,
82
79
  });
83
80
  await archivePrincipalHistoryKeyEnvelopes(tx, {
@@ -90,12 +87,17 @@ async function retainPredecessorStage(tx, entry) {
90
87
  // Preserve a prefix-only predecessor without replacing an existing completion.
91
88
  if (!entry.predecessorStage || !entry.predecessorIndexRootHash)
92
89
  return;
90
+ const row = {
91
+ ...entry.predecessorStage,
92
+ scopeId: entry.prefix.scopeId,
93
+ touchedAt: Date.now(),
94
+ };
93
95
  const inserted = await tx
94
96
  .insert(principalHistoryStages)
95
- .values(entry.predecessorStage)
97
+ .values(row)
96
98
  .onConflictDoUpdate({
97
99
  target: principalHistoryStages.id,
98
- set: entry.predecessorStage,
100
+ set: row,
99
101
  setWhere: eq(principalHistoryStages.complete, false),
100
102
  })
101
103
  .returning({ id: principalHistoryStages.id });
@@ -107,6 +109,20 @@ async function retainPredecessorStage(tx, entry) {
107
109
  rootHash: entry.predecessorIndexRootHash,
108
110
  });
109
111
  }
112
+ // A fully authenticated publication supersedes an in-flight stage for this
113
+ // exact head. Its other writer fails its progress CAS and resumes.
114
+ async function saveAcknowledgedStage(tx, entry) {
115
+ const row = {
116
+ ...entry.stage,
117
+ scopeId: entry.prefix.scopeId,
118
+ touchedAt: Date.now(),
119
+ };
120
+ await tx
121
+ .insert(principalHistoryStages)
122
+ .values(row)
123
+ .onConflictDoUpdate({ target: principalHistoryStages.id, set: row })
124
+ .run();
125
+ }
110
126
  /** Store current artifacts and resumable progress in the same transaction as their pins. */
111
127
  export async function persistAcknowledgedPrincipalCurrents(input) {
112
128
  const entries = structuredClone(input.entries);
@@ -133,16 +149,7 @@ export async function persistAcknowledgedPrincipalCurrents(input) {
133
149
  for (const entry of entries) {
134
150
  await writePrincipalHistoryEvidencePage(tx, entry.evidence);
135
151
  await retainPredecessorStage(tx, entry);
136
- // A fully authenticated publication supersedes an in-flight stage for
137
- // this exact head. Its other writer fails its progress CAS and resumes.
138
- await tx
139
- .insert(principalHistoryStages)
140
- .values(entry.stage)
141
- .onConflictDoUpdate({
142
- target: principalHistoryStages.id,
143
- set: entry.stage,
144
- })
145
- .run();
152
+ await saveAcknowledgedStage(tx, entry);
146
153
  await archiveAcknowledgedStageKeys(tx, entry);
147
154
  if (!entry.preservePrefix)
148
155
  await tx
@@ -1 +1 @@
1
- {"version":3,"file":"principalCurrentAcknowledgementPersistence.js","sourceRoot":"","sources":["principalCurrentAcknowledgementPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EAGvB,+BAA+B,GAChC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,mCAAmC,EAAE,MAAM,uCAAuC,CAAC;AAC5F,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AACpF,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEL,iCAAiC,GAClC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAgB,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EACL,mCAAmC,EACnC,kBAAkB,EAClB,4CAA4C,GAC7C,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,8BAA8B,EAAE,MAAM,uCAAuC,CAAC;AACvF,OAAO,EAEL,iCAAiC,GAClC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAE/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAE7E,OAAO,EACL,sCAAsC,EACtC,gCAAgC,GACjC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,mCAAmC,EAAE,MAAM,0CAA0C,CAAC;AAsB/F,qFAAqF;AACrF,MAAM,OAAO,oCAAqC,SAAQ,KAAK;IAC7D;QACE,KAAK,CACH,kFAAkF,CACnF,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,sCAAsC,CAAC;IACrD,CAAC;CACF;AAQD,SAAS,sBAAsB,CAC7B,OAA2D,EAC3D,cAAsB;IAEtB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YACf,MAAM,IAAI,uBAAuB,CAC/B,iBAAiB,EACjB,gDAAgD,CACjD,CAAC;QACJ,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,IACE,KAAK,CAAC,MAAM,CAAC,cAAc,KAAK,cAAc;YAC9C,CAAC,KAAK,CAAC,gBAAgB,KAAK,IAAI,CAAC;gBAC/B,CAAC,KAAK,CAAC,wBAAwB,KAAK,IAAI,CAAC;YAC3C,KAAK,CAAC,KAAK,CAAC,cAAc,KAAK,cAAc;YAC7C,CAAC,KAAK,CAAC,gBAAgB;gBACrB,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,cAAc,KAAK,cAAc;oBACxD,CAAC,KAAK,CAAC,gBAAgB,CAAC,QAAQ;oBAChC,KAAK,CAAC,gBAAgB,CAAC,YAAY,KAAK,KAAK,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC;gBAClE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,KAAK,OAAO;oBACtC,KAAK,CAAC,MAAM,CAAC,OAAO,KAAK,CAAC;oBAC1B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,KAAK,IAAI;oBACzC,KAAK,CAAC,sBAAsB,KAAK,IAAI,CAAC;YAC1C,KAAK,CAAC,QAAQ,CAAC,cAAc,KAAK,cAAc;YAChD,KAAK,CAAC,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC,QAAQ,CAAC,OAAO;YAC/C,KAAK,CAAC,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC,MAAM,CAAC,OAAO;YAC7C,KAAK,CAAC,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC;YACrD,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ;YACrB,KAAK,CAAC,KAAK,CAAC,WAAW,KAAK,KAAK,CAAC,MAAM,CAAC,WAAW;YAEpD,MAAM,IAAI,uBAAuB,CAC/B,iBAAiB,EACjB,uDAAuD,CACxD,CAAC;IACN,CAAC;AACH,CAAC;AAED,KAAK,UAAU,mBAAmB,CAChC,EAAgC,EAChC,KAA4C;IAE5C,MAAM,UAAU,GAAG,MAAM,mCAAmC,CAC1D,EAAE,EACF,KAAK,CAAC,MAAM,CACb,CAAC;IACF,IACE,UAAU,EAAE,OAAO,KAAK,KAAK,CAAC,kBAAkB,EAAE,OAAO;QACzD,UAAU,EAAE,SAAS,KAAK,KAAK,CAAC,kBAAkB,EAAE,SAAS;QAE7D,MAAM,IAAI,oCAAoC,EAAE,CAAC;IACnD,+BAA+B,CAAC;QAC9B,KAAK,EAAE,KAAK,CAAC,gBAAgB,CAAC,eAAe;QAC7C,YAAY,EAAE,KAAK,CAAC,gBAAgB,CAAC,KAAK;QAC1C,eAAe,EAAE,UAAU;KAC5B,CAAC,CAAC;IACH,IACE,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC;QAC3C,CAAC,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,CAAC;QAE7D,MAAM,IAAI,uBAAuB,CAC/B,mBAAmB,EACnB,yEAAyE,CAC1E,CAAC;IACJ,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE;SACtB,MAAM,EAAE;SACR,IAAI,CAAC,wBAAwB,CAAC;SAC9B,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;SACjE,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,IAAI,CAAC,MAAM,EAAE,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,sBAAsB;QAC7D,MAAM,IAAI,oCAAoC,EAAE,CAAC;AACrD,CAAC;AAED,KAAK,UAAU,4BAA4B,CACzC,EAAgC,EAChC,KAA8C;IAE9C,KAAK,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,gCAAgC,CAAC,EAAE,EAAE;YACzC,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc;YAC3C,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO;SAC9B,CAAC,CAAC;QACH,MAAM,mCAAmC,CAAC,EAAE,EAAE;YAC5C,GAAG,KAAK;YACR,OAAO,EAAE,KAAK,CAAC,YAAY,GAAG,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,EAAgC,EAChC,KAA8C;IAE9C,+EAA+E;IAC/E,IAAI,CAAC,KAAK,CAAC,gBAAgB,IAAI,CAAC,KAAK,CAAC,wBAAwB;QAAE,OAAO;IACvE,MAAM,QAAQ,GAAG,MAAM,EAAE;SACtB,MAAM,CAAC,sBAAsB,CAAC;SAC9B,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC;SAC9B,kBAAkB,CAAC;QAClB,MAAM,EAAE,sBAAsB,CAAC,EAAE;QACjC,GAAG,EAAE,KAAK,CAAC,gBAAgB;QAC3B,QAAQ,EAAE,EAAE,CAAC,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC;KACrD,CAAC;SACD,SAAS,CAAC,EAAE,EAAE,EAAE,sBAAsB,CAAC,EAAE,EAAE,CAAC,CAAC;IAChD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QACrB,MAAM,0BAA0B,CAAC,EAAE,EAAE;YACnC,EAAE,EAAE,SAAS,KAAK,CAAC,gBAAgB,CAAC,EAAE,EAAE;YACxC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO;YAC7B,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc;YAC3C,QAAQ,EAAE,KAAK,CAAC,wBAAwB;SACzC,CAAC,CAAC;AACP,CAAC;AAED,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,oCAAoC,CAAC,KAQ1D;IACC,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAC7D,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;IAC5C,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;IACxC,sBAAsB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAChD,mCAAmC,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE;QACnC,GAAG,qBAAqB;QACxB,GAAG,sBAAsB;KAC1B,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,iCAAiC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,kBAAkB,CAC7C,KAAK,EAAE,EAAE,EAAE,EAAE;QACX,KAAK,MAAM,KAAK,IAAI,OAAO;YAAE,MAAM,mBAAmB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAClE,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CACzB,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,MAAM,CAAC,WAAW,KAAK,UAAU,CAAC,WAAW;gBACnD,KAAK,CAAC,MAAM,CAAC,aAAa,KAAK,UAAU,CAAC,aAAa,CAC1D,EAAE,MAAM,CAAC;YACV,IAAI,CAAC,MAAM;gBACT,MAAM,IAAI,uBAAuB,CAC/B,iBAAiB,EACjB,sDAAsD,CACvD,CAAC;YACJ,MAAM,8BAA8B,CAClC,EAAE,EACF,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,EACzC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EACpC,cAAc,CACf,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,iCAAiC,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC5D,MAAM,sBAAsB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YACxC,sEAAsE;YACtE,wEAAwE;YACxE,MAAM,EAAE;iBACL,MAAM,CAAC,sBAAsB,CAAC;iBAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;iBACnB,kBAAkB,CAAC;gBAClB,MAAM,EAAE,sBAAsB,CAAC,EAAE;gBACjC,GAAG,EAAE,KAAK,CAAC,KAAK;aACjB,CAAC;iBACD,GAAG,EAAE,CAAC;YACT,MAAM,4BAA4B,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC9C,IAAI,CAAC,KAAK,CAAC,cAAc;gBACvB,MAAM,EAAE;qBACL,MAAM,CAAC,wBAAwB,CAAC;qBAChC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;qBACpB,kBAAkB,CAAC;oBAClB,MAAM,EAAE,wBAAwB,CAAC,OAAO;oBACxC,GAAG,EAAE,KAAK,CAAC,MAAM;iBAClB,CAAC;qBACD,GAAG,EAAE,CAAC;YACX,MAAM,0BAA0B,CAAC,EAAE,EAAE;gBACnC,EAAE,EAAE,SAAS,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE;gBAC7B,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO;gBAC7B,cAAc;gBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,aAAa;aACvC,CAAC,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,cAAc;gBACvB,MAAM,0BAA0B,CAAC,EAAE,EAAE;oBACnC,EAAE,EAAE,UAAU,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE;oBACpC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO;oBAC7B,cAAc;oBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,aAAa;iBACvC,CAAC,CAAC;YACL,MAAM,sCAAsC,CAAC,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;YACvE,MAAM,4BAA4B,CAAC,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;YAC7D,IACE,CAAC,KAAK,CAAC,kBAAkB;gBACzB,KAAK,CAAC,kBAAkB,CAAC,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO;gBAEvD,MAAM,4CAA4C,CAChD,EAAE,EACF,KAAK,CAAC,MAAM,CAAC,UAAU,EACvB,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EACxB,cAAc,CACf,CAAC;QACN,CAAC;IACH,CAAC,EACD,YAAY,EACZ,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CAAC;IACF,mCAAmC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import {\n KeyingVerificationError,\n type PrincipalPolicyCheckpoint,\n type VerifiedPrincipalPolicyCurrent,\n verifyPrincipalPolicyCheckpoint,\n} from \"@tearleads/crypto\";\nimport { eq } from \"drizzle-orm\";\nimport { assertProjectionVerificationCurrent } from \"../keyingProjectionVerification/types\";\nimport { principalHistoryPrefixes } from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { principalHistoryStages } from \"../sqlite/principalHistoryStageSchema\";\nimport {\n keyingCheckpointTables,\n principalPolicyTables,\n} from \"../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\nimport {\n loadStoredPrincipalPolicyCheckpoint,\n principalPolicyKey,\n upsertPrincipalPolicyCheckpointInTransaction,\n} from \"./keyingCheckpointPersistence\";\nimport { storePrincipalGrantRetirements } from \"./principalGrantRetirementPersistence\";\nimport {\n type PrincipalHistoryEvidencePage,\n writePrincipalHistoryEvidencePage,\n} from \"./principalHistoryEvidencePersistence\";\nimport { reclaimPrincipalHistoryNodes } from \"./principalHistoryNodeRetention\";\nimport type { PrincipalHistoryPrefix } from \"./principalHistoryPrefixPersistence\";\nimport { retainPrincipalHistoryRoot } from \"./principalHistoryRootOwnership\";\nimport type { PrincipalHistoryStage } from \"./principalHistoryStagePersistence\";\nimport {\n reclaimCompletedPrincipalHistoryStages,\n recordPrincipalHistoryStageScope,\n} from \"./principalHistoryStageRetention\";\nimport { archivePrincipalHistoryKeyEnvelopes } from \"./principalKeyEnvelopeArchivePersistence\";\n\n/** Owned output of authenticated prefix extension and exact receipt verification. */\nexport interface AcknowledgedPrincipalCurrentPublication {\n readonly policy: VerifiedPrincipalPolicyCurrent;\n readonly prefix: PrincipalHistoryPrefix;\n readonly previousPrefixProgress: string | null;\n readonly stage: PrincipalHistoryStage;\n readonly predecessorStage: PrincipalHistoryStage | null;\n readonly predecessorIndexRootHash: string | null;\n readonly evidence: PrincipalHistoryEvidencePage;\n}\n\nexport interface ReconciledPrincipalCurrentPublication\n extends AcknowledgedPrincipalCurrentPublication {\n readonly preservePrefix: boolean;\n readonly observedPrefixProgress: string | null;\n readonly observedCheckpoint: PrincipalPolicyCheckpoint | null;\n readonly retainedPrefix: PrincipalHistoryPrefix;\n readonly checkpointPolicy: VerifiedPrincipalPolicyCurrent;\n}\n\n/** A local writer won the CAS; retry the same authenticated receipt without HTTP. */\nexport class PrincipalAcknowledgementChangedError extends Error {\n constructor() {\n super(\n \"Authenticated principal progress changed during acknowledgement; retry retention\",\n );\n this.name = \"PrincipalAcknowledgementChangedError\";\n }\n}\n\nexport interface AcknowledgedPrincipalCurrentRetirement {\n readonly principalId: string;\n readonly principalType: VerifiedPrincipalPolicyCurrent[\"principalType\"];\n readonly containerIds: readonly string[];\n}\n\nfunction assertPublicationScope(\n entries: readonly AcknowledgedPrincipalCurrentPublication[],\n organizationId: string,\n) {\n const seen = new Set<string>();\n for (const entry of entries) {\n const key = principalPolicyKey(entry.policy);\n if (seen.has(key))\n throw new KeyingVerificationError(\n \"duplicate_entry\",\n \"Acknowledged current batch repeats a principal\",\n );\n seen.add(key);\n if (\n entry.prefix.organizationId !== organizationId ||\n (entry.predecessorStage === null) !==\n (entry.predecessorIndexRootHash === null) ||\n entry.stage.organizationId !== organizationId ||\n (entry.predecessorStage\n ? entry.predecessorStage.organizationId !== organizationId ||\n !entry.predecessorStage.complete ||\n entry.predecessorStage.afterVersion !== entry.policy.version - 2\n : entry.policy.principalType !== \"group\" ||\n entry.policy.version !== 1 ||\n entry.policy.state.prevStateHash !== null ||\n entry.previousPrefixProgress !== null) ||\n entry.evidence.organizationId !== organizationId ||\n entry.prefix.scopeId !== entry.evidence.scopeId ||\n entry.prefix.version !== entry.policy.version ||\n entry.stage.afterVersion !== entry.policy.version - 1 ||\n !entry.stage.complete ||\n entry.stage.currentJson !== entry.prefix.currentJson\n )\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Acknowledged current publication is outside its scope\",\n );\n }\n}\n\nasync function validatePublication(\n tx: ClientSQLiteTransactionScope,\n entry: ReconciledPrincipalCurrentPublication,\n) {\n const checkpoint = await loadStoredPrincipalPolicyCheckpoint(\n tx,\n entry.policy,\n );\n if (\n checkpoint?.version !== entry.observedCheckpoint?.version ||\n checkpoint?.stateHash !== entry.observedCheckpoint?.stateHash\n )\n throw new PrincipalAcknowledgementChangedError();\n verifyPrincipalPolicyCheckpoint({\n chain: entry.checkpointPolicy.retainedHistory,\n currentState: entry.checkpointPolicy.state,\n localCheckpoint: checkpoint,\n });\n if (\n (!checkpoint && entry.policy.version !== 1) ||\n (checkpoint && entry.policy.version > checkpoint.version + 1)\n )\n throw new KeyingVerificationError(\n \"stale_predecessor\",\n \"Acknowledged current policy must directly extend its durable checkpoint\",\n );\n const [prefix] = await tx\n .select()\n .from(principalHistoryPrefixes)\n .where(eq(principalHistoryPrefixes.scopeId, entry.prefix.scopeId))\n .limit(1);\n if ((prefix?.progress ?? null) !== entry.observedPrefixProgress)\n throw new PrincipalAcknowledgementChangedError();\n}\n\nasync function archiveAcknowledgedStageKeys(\n tx: ClientSQLiteTransactionScope,\n entry: AcknowledgedPrincipalCurrentPublication,\n) {\n for (const stage of [entry.predecessorStage, entry.stage]) {\n if (!stage) continue;\n await recordPrincipalHistoryStageScope(tx, {\n id: stage.id,\n afterVersion: stage.afterVersion,\n complete: stage.complete,\n organizationId: entry.prefix.organizationId,\n scopeId: entry.prefix.scopeId,\n });\n await archivePrincipalHistoryKeyEnvelopes(tx, {\n ...stage,\n version: stage.afterVersion + 1,\n });\n }\n}\n\nasync function retainPredecessorStage(\n tx: ClientSQLiteTransactionScope,\n entry: AcknowledgedPrincipalCurrentPublication,\n) {\n // Preserve a prefix-only predecessor without replacing an existing completion.\n if (!entry.predecessorStage || !entry.predecessorIndexRootHash) return;\n const inserted = await tx\n .insert(principalHistoryStages)\n .values(entry.predecessorStage)\n .onConflictDoUpdate({\n target: principalHistoryStages.id,\n set: entry.predecessorStage,\n setWhere: eq(principalHistoryStages.complete, false),\n })\n .returning({ id: principalHistoryStages.id });\n if (inserted.length > 0)\n await retainPrincipalHistoryRoot(tx, {\n id: `stage:${entry.predecessorStage.id}`,\n scopeId: entry.prefix.scopeId,\n organizationId: entry.prefix.organizationId,\n rootHash: entry.predecessorIndexRootHash,\n });\n}\n\n/** Store current artifacts and resumable progress in the same transaction as their pins. */\nexport async function persistAcknowledgedPrincipalCurrents(input: {\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly entries: readonly ReconciledPrincipalCurrentPublication[];\n readonly retirements?:\n | readonly AcknowledgedPrincipalCurrentRetirement[]\n | undefined;\n readonly stillCurrent: () => boolean;\n}): Promise<void> {\n const entries = structuredClone(input.entries);\n const retirements = structuredClone(input.retirements ?? []);\n const organizationId = input.organizationId;\n const stillCurrent = input.stillCurrent;\n assertPublicationScope(entries, organizationId);\n assertProjectionVerificationCurrent(stillCurrent);\n await ensureSqlTables(input.execSql, [\n ...principalPolicyTables,\n ...keyingCheckpointTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const stored = await runtime.guardedTransaction(\n async (tx) => {\n for (const entry of entries) await validatePublication(tx, entry);\n for (const retirement of retirements) {\n const policy = entries.find(\n (entry) =>\n entry.policy.principalId === retirement.principalId &&\n entry.policy.principalType === retirement.principalType,\n )?.policy;\n if (!policy)\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Retirement must accompany its acknowledged principal\",\n );\n await storePrincipalGrantRetirements(\n tx,\n { ...retirement, policy, organizationId },\n entries.map((entry) => entry.policy),\n organizationId,\n );\n }\n for (const entry of entries) {\n await writePrincipalHistoryEvidencePage(tx, entry.evidence);\n await retainPredecessorStage(tx, entry);\n // A fully authenticated publication supersedes an in-flight stage for\n // this exact head. Its other writer fails its progress CAS and resumes.\n await tx\n .insert(principalHistoryStages)\n .values(entry.stage)\n .onConflictDoUpdate({\n target: principalHistoryStages.id,\n set: entry.stage,\n })\n .run();\n await archiveAcknowledgedStageKeys(tx, entry);\n if (!entry.preservePrefix)\n await tx\n .insert(principalHistoryPrefixes)\n .values(entry.prefix)\n .onConflictDoUpdate({\n target: principalHistoryPrefixes.scopeId,\n set: entry.prefix,\n })\n .run();\n await retainPrincipalHistoryRoot(tx, {\n id: `stage:${entry.stage.id}`,\n scopeId: entry.prefix.scopeId,\n organizationId,\n rootHash: entry.evidence.indexRootHash,\n });\n if (!entry.preservePrefix)\n await retainPrincipalHistoryRoot(tx, {\n id: `prefix:${entry.prefix.scopeId}`,\n scopeId: entry.prefix.scopeId,\n organizationId,\n rootHash: entry.evidence.indexRootHash,\n });\n await reclaimCompletedPrincipalHistoryStages(tx, entry.retainedPrefix);\n await reclaimPrincipalHistoryNodes(tx, entry.retainedPrefix);\n if (\n !entry.observedCheckpoint ||\n entry.observedCheckpoint.version < entry.policy.version\n )\n await upsertPrincipalPolicyCheckpointInTransaction(\n tx,\n entry.policy.checkpoint,\n new Date().toISOString(),\n organizationId,\n );\n }\n },\n stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => stored.committed);\n}\n"]}
1
+ {"version":3,"file":"principalCurrentAcknowledgementPersistence.js","sourceRoot":"","sources":["principalCurrentAcknowledgementPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type PrincipalPolicyCheckpoint,\n type VerifiedPrincipalPolicyCurrent,\n verifyPrincipalPolicyCheckpoint,\n} from \"@tearleads/crypto\";\nimport { eq } from \"drizzle-orm\";\nimport { assertProjectionVerificationCurrent } from \"../keyingProjectionVerification/types\";\nimport { principalHistoryPrefixes } from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { principalHistoryStages } from \"../sqlite/principalHistoryStageSchema\";\nimport {\n keyingCheckpointTables,\n principalPolicyTables,\n} from \"../sqlite/schema\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\nimport {\n loadStoredPrincipalPolicyCheckpoint,\n principalPolicyKey,\n upsertPrincipalPolicyCheckpointInTransaction,\n} from \"./keyingCheckpointPersistence\";\nimport { storePrincipalGrantRetirements } from \"./principalGrantRetirementPersistence\";\nimport {\n type PrincipalHistoryEvidencePage,\n writePrincipalHistoryEvidencePage,\n} from \"./principalHistoryEvidencePersistence\";\nimport { reclaimPrincipalHistoryNodes } from \"./principalHistoryNodeRetention\";\nimport type { PrincipalHistoryPrefix } from \"./principalHistoryPrefixPersistence\";\nimport { retainPrincipalHistoryRoot } from \"./principalHistoryRootOwnership\";\nimport type { PrincipalHistoryStage } from \"./principalHistoryStagePersistence\";\nimport {\n reclaimCompletedPrincipalHistoryStages,\n touchPrincipalHistoryStage,\n} from \"./principalHistoryStageRetention\";\nimport { archivePrincipalHistoryKeyEnvelopes } from \"./principalKeyEnvelopeArchivePersistence\";\n\n/** Owned output of authenticated prefix extension and exact receipt verification. */\nexport interface AcknowledgedPrincipalCurrentPublication {\n readonly policy: VerifiedPrincipalPolicyCurrent;\n readonly prefix: PrincipalHistoryPrefix;\n readonly previousPrefixProgress: string | null;\n readonly stage: PrincipalHistoryStage;\n readonly predecessorStage: PrincipalHistoryStage | null;\n readonly predecessorIndexRootHash: string | null;\n readonly evidence: PrincipalHistoryEvidencePage;\n}\n\nexport interface ReconciledPrincipalCurrentPublication\n extends AcknowledgedPrincipalCurrentPublication {\n readonly preservePrefix: boolean;\n readonly observedPrefixProgress: string | null;\n readonly observedCheckpoint: PrincipalPolicyCheckpoint | null;\n readonly retainedPrefix: PrincipalHistoryPrefix;\n readonly checkpointPolicy: VerifiedPrincipalPolicyCurrent;\n}\n\n/** A local writer won the CAS; retry the same authenticated receipt without HTTP. */\nexport class PrincipalAcknowledgementChangedError extends Error {\n constructor() {\n super(\n \"Authenticated principal progress changed during acknowledgement; retry retention\",\n );\n this.name = \"PrincipalAcknowledgementChangedError\";\n }\n}\n\nexport interface AcknowledgedPrincipalCurrentRetirement {\n readonly principalId: string;\n readonly principalType: VerifiedPrincipalPolicyCurrent[\"principalType\"];\n readonly containerIds: readonly string[];\n}\n\nfunction assertPublicationScope(\n entries: readonly AcknowledgedPrincipalCurrentPublication[],\n organizationId: string,\n) {\n const seen = new Set<string>();\n for (const entry of entries) {\n const key = principalPolicyKey(entry.policy);\n if (seen.has(key))\n throw new KeyingVerificationError(\n \"duplicate_entry\",\n \"Acknowledged current batch repeats a principal\",\n );\n seen.add(key);\n if (\n entry.prefix.organizationId !== organizationId ||\n (entry.predecessorStage === null) !==\n (entry.predecessorIndexRootHash === null) ||\n entry.stage.organizationId !== organizationId ||\n (entry.predecessorStage\n ? entry.predecessorStage.organizationId !== organizationId ||\n !entry.predecessorStage.complete ||\n entry.predecessorStage.afterVersion !== entry.policy.version - 2\n : entry.policy.principalType !== \"group\" ||\n entry.policy.version !== 1 ||\n entry.policy.state.prevStateHash !== null ||\n entry.previousPrefixProgress !== null) ||\n entry.evidence.organizationId !== organizationId ||\n entry.prefix.scopeId !== entry.evidence.scopeId ||\n entry.prefix.version !== entry.policy.version ||\n entry.stage.afterVersion !== entry.policy.version - 1 ||\n !entry.stage.complete ||\n entry.stage.currentJson !== entry.prefix.currentJson\n )\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Acknowledged current publication is outside its scope\",\n );\n }\n}\n\nasync function validatePublication(\n tx: ClientSQLiteTransactionScope,\n entry: ReconciledPrincipalCurrentPublication,\n) {\n const checkpoint = await loadStoredPrincipalPolicyCheckpoint(\n tx,\n entry.policy,\n );\n if (\n checkpoint?.version !== entry.observedCheckpoint?.version ||\n checkpoint?.stateHash !== entry.observedCheckpoint?.stateHash\n )\n throw new PrincipalAcknowledgementChangedError();\n verifyPrincipalPolicyCheckpoint({\n chain: entry.checkpointPolicy.retainedHistory,\n currentState: entry.checkpointPolicy.state,\n localCheckpoint: checkpoint,\n });\n if (\n (!checkpoint && entry.policy.version !== 1) ||\n (checkpoint && entry.policy.version > checkpoint.version + 1)\n )\n throw new KeyingVerificationError(\n \"stale_predecessor\",\n \"Acknowledged current policy must directly extend its durable checkpoint\",\n );\n const [prefix] = await tx\n .select()\n .from(principalHistoryPrefixes)\n .where(eq(principalHistoryPrefixes.scopeId, entry.prefix.scopeId))\n .limit(1);\n if ((prefix?.progress ?? null) !== entry.observedPrefixProgress)\n throw new PrincipalAcknowledgementChangedError();\n}\n\nasync function archiveAcknowledgedStageKeys(\n tx: ClientSQLiteTransactionScope,\n entry: AcknowledgedPrincipalCurrentPublication,\n) {\n for (const stage of [entry.predecessorStage, entry.stage]) {\n if (!stage) continue;\n await touchPrincipalHistoryStage(tx, {\n id: stage.id,\n scopeId: entry.prefix.scopeId,\n });\n await archivePrincipalHistoryKeyEnvelopes(tx, {\n ...stage,\n version: stage.afterVersion + 1,\n });\n }\n}\n\nasync function retainPredecessorStage(\n tx: ClientSQLiteTransactionScope,\n entry: AcknowledgedPrincipalCurrentPublication,\n) {\n // Preserve a prefix-only predecessor without replacing an existing completion.\n if (!entry.predecessorStage || !entry.predecessorIndexRootHash) return;\n const row = {\n ...entry.predecessorStage,\n scopeId: entry.prefix.scopeId,\n touchedAt: Date.now(),\n };\n const inserted = await tx\n .insert(principalHistoryStages)\n .values(row)\n .onConflictDoUpdate({\n target: principalHistoryStages.id,\n set: row,\n setWhere: eq(principalHistoryStages.complete, false),\n })\n .returning({ id: principalHistoryStages.id });\n if (inserted.length > 0)\n await retainPrincipalHistoryRoot(tx, {\n id: `stage:${entry.predecessorStage.id}`,\n scopeId: entry.prefix.scopeId,\n organizationId: entry.prefix.organizationId,\n rootHash: entry.predecessorIndexRootHash,\n });\n}\n\n// A fully authenticated publication supersedes an in-flight stage for this\n// exact head. Its other writer fails its progress CAS and resumes.\nasync function saveAcknowledgedStage(\n tx: ClientSQLiteTransactionScope,\n entry: AcknowledgedPrincipalCurrentPublication,\n) {\n const row = {\n ...entry.stage,\n scopeId: entry.prefix.scopeId,\n touchedAt: Date.now(),\n };\n await tx\n .insert(principalHistoryStages)\n .values(row)\n .onConflictDoUpdate({ target: principalHistoryStages.id, set: row })\n .run();\n}\n\n/** Store current artifacts and resumable progress in the same transaction as their pins. */\nexport async function persistAcknowledgedPrincipalCurrents(input: {\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly entries: readonly ReconciledPrincipalCurrentPublication[];\n readonly retirements?:\n | readonly AcknowledgedPrincipalCurrentRetirement[]\n | undefined;\n readonly stillCurrent: () => boolean;\n}): Promise<void> {\n const entries = structuredClone(input.entries);\n const retirements = structuredClone(input.retirements ?? []);\n const organizationId = input.organizationId;\n const stillCurrent = input.stillCurrent;\n assertPublicationScope(entries, organizationId);\n assertProjectionVerificationCurrent(stillCurrent);\n await ensureSqlTables(input.execSql, [\n ...principalPolicyTables,\n ...keyingCheckpointTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const stored = await runtime.guardedTransaction(\n async (tx) => {\n for (const entry of entries) await validatePublication(tx, entry);\n for (const retirement of retirements) {\n const policy = entries.find(\n (entry) =>\n entry.policy.principalId === retirement.principalId &&\n entry.policy.principalType === retirement.principalType,\n )?.policy;\n if (!policy)\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Retirement must accompany its acknowledged principal\",\n );\n await storePrincipalGrantRetirements(\n tx,\n { ...retirement, policy, organizationId },\n entries.map((entry) => entry.policy),\n organizationId,\n );\n }\n for (const entry of entries) {\n await writePrincipalHistoryEvidencePage(tx, entry.evidence);\n await retainPredecessorStage(tx, entry);\n await saveAcknowledgedStage(tx, entry);\n await archiveAcknowledgedStageKeys(tx, entry);\n if (!entry.preservePrefix)\n await tx\n .insert(principalHistoryPrefixes)\n .values(entry.prefix)\n .onConflictDoUpdate({\n target: principalHistoryPrefixes.scopeId,\n set: entry.prefix,\n })\n .run();\n await retainPrincipalHistoryRoot(tx, {\n id: `stage:${entry.stage.id}`,\n scopeId: entry.prefix.scopeId,\n organizationId,\n rootHash: entry.evidence.indexRootHash,\n });\n if (!entry.preservePrefix)\n await retainPrincipalHistoryRoot(tx, {\n id: `prefix:${entry.prefix.scopeId}`,\n scopeId: entry.prefix.scopeId,\n organizationId,\n rootHash: entry.evidence.indexRootHash,\n });\n await reclaimCompletedPrincipalHistoryStages(tx, entry.retainedPrefix);\n await reclaimPrincipalHistoryNodes(tx, entry.retainedPrefix);\n if (\n !entry.observedCheckpoint ||\n entry.observedCheckpoint.version < entry.policy.version\n )\n await upsertPrincipalPolicyCheckpointInTransaction(\n tx,\n entry.policy.checkpoint,\n new Date().toISOString(),\n organizationId,\n );\n }\n },\n stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => stored.committed);\n}\n"]}
@@ -1,5 +1,4 @@
1
1
  import { and, asc, desc, eq, inArray, ne } from "drizzle-orm";
2
- import { principalHistoryStageScopes } from "../sqlite/principalHistoryRetentionSchema.js";
3
2
  import { principalHistoryStages } from "../sqlite/principalHistoryStageSchema.js";
4
3
  import { releasePrincipalHistoryRoot } from "./principalHistoryRootOwnership.js";
5
4
  const INCOMPLETE_STAGE_LIMIT = 8;
@@ -8,10 +7,9 @@ const RECLAIM_BATCH_LIMIT = 16;
8
7
  export async function reclaimIncompletePrincipalHistoryStages(tx, current) {
9
8
  const obsolete = await tx
10
9
  .select({ id: principalHistoryStages.id })
11
- .from(principalHistoryStageScopes)
12
- .innerJoin(principalHistoryStages, eq(principalHistoryStageScopes.id, principalHistoryStages.id))
13
- .where(and(eq(principalHistoryStageScopes.scopeId, current.scopeId), eq(principalHistoryStageScopes.organizationId, current.organizationId), eq(principalHistoryStageScopes.complete, false), eq(principalHistoryStages.organizationId, current.organizationId), eq(principalHistoryStages.complete, false), ne(principalHistoryStageScopes.id, current.id)))
14
- .orderBy(desc(principalHistoryStageScopes.touchedAt), asc(principalHistoryStageScopes.id))
10
+ .from(principalHistoryStages)
11
+ .where(and(eq(principalHistoryStages.scopeId, current.scopeId), eq(principalHistoryStages.organizationId, current.organizationId), eq(principalHistoryStages.complete, false), ne(principalHistoryStages.id, current.id)))
12
+ .orderBy(desc(principalHistoryStages.touchedAt), asc(principalHistoryStages.id))
15
13
  // The writer always keeps its own stage, even when recency hints tie.
16
14
  .offset(INCOMPLETE_STAGE_LIMIT - (current.complete ? 0 : 1))
17
15
  .limit(RECLAIM_BATCH_LIMIT);
@@ -24,9 +22,5 @@ export async function reclaimIncompletePrincipalHistoryStages(tx, current) {
24
22
  .delete(principalHistoryStages)
25
23
  .where(inArray(principalHistoryStages.id, ids))
26
24
  .run();
27
- await tx
28
- .delete(principalHistoryStageScopes)
29
- .where(inArray(principalHistoryStageScopes.id, ids))
30
- .run();
31
25
  }
32
26
  //# sourceMappingURL=principalHistoryIncompleteRetention.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"principalHistoryIncompleteRetention.js","sourceRoot":"","sources":["principalHistoryIncompleteRetention.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAE/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAE9E,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACjC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,uCAAuC,CAC3D,EAAgC,EAChC,OAKC;IAED,MAAM,QAAQ,GAAG,MAAM,EAAE;SACtB,MAAM,CAAC,EAAE,EAAE,EAAE,sBAAsB,CAAC,EAAE,EAAE,CAAC;SACzC,IAAI,CAAC,2BAA2B,CAAC;SACjC,SAAS,CACR,sBAAsB,EACtB,EAAE,CAAC,2BAA2B,CAAC,EAAE,EAAE,sBAAsB,CAAC,EAAE,CAAC,CAC9D;SACA,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,2BAA2B,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EACxD,EAAE,CAAC,2BAA2B,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,EACtE,EAAE,CAAC,2BAA2B,CAAC,QAAQ,EAAE,KAAK,CAAC,EAC/C,EAAE,CAAC,sBAAsB,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,EACjE,EAAE,CAAC,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC,EAC1C,EAAE,CAAC,2BAA2B,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,CAC/C,CACF;SACA,OAAO,CACN,IAAI,CAAC,2BAA2B,CAAC,SAAS,CAAC,EAC3C,GAAG,CAAC,2BAA2B,CAAC,EAAE,CAAC,CACpC;QACD,sEAAsE;SACrE,MAAM,CAAC,sBAAsB,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAC3D,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACzC,KAAK,MAAM,EAAE,IAAI,GAAG;QAAE,MAAM,2BAA2B,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;IAC3E,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC7B,MAAM,EAAE;SACL,MAAM,CAAC,sBAAsB,CAAC;SAC9B,KAAK,CAAC,OAAO,CAAC,sBAAsB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;SAC9C,GAAG,EAAE,CAAC;IACT,MAAM,EAAE;SACL,MAAM,CAAC,2BAA2B,CAAC;SACnC,KAAK,CAAC,OAAO,CAAC,2BAA2B,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;SACnD,GAAG,EAAE,CAAC;AACX,CAAC","sourcesContent":["import { and, asc, desc, eq, inArray, ne } from \"drizzle-orm\";\nimport { principalHistoryStageScopes } from \"../sqlite/principalHistoryRetentionSchema\";\nimport { principalHistoryStages } from \"../sqlite/principalHistoryStageSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../sqlite/sqlitePersistenceRuntime\";\nimport { releasePrincipalHistoryRoot } from \"./principalHistoryRootOwnership\";\n\nconst INCOMPLETE_STAGE_LIMIT = 8;\nconst RECLAIM_BATCH_LIMIT = 16;\n\n/** Disposable work only: completed artifacts and trust pins are unaffected. */\nexport async function reclaimIncompletePrincipalHistoryStages(\n tx: ClientSQLiteTransactionScope,\n current: {\n readonly id: string;\n readonly scopeId: string;\n readonly organizationId: string;\n readonly complete: boolean;\n },\n) {\n const obsolete = await tx\n .select({ id: principalHistoryStages.id })\n .from(principalHistoryStageScopes)\n .innerJoin(\n principalHistoryStages,\n eq(principalHistoryStageScopes.id, principalHistoryStages.id),\n )\n .where(\n and(\n eq(principalHistoryStageScopes.scopeId, current.scopeId),\n eq(principalHistoryStageScopes.organizationId, current.organizationId),\n eq(principalHistoryStageScopes.complete, false),\n eq(principalHistoryStages.organizationId, current.organizationId),\n eq(principalHistoryStages.complete, false),\n ne(principalHistoryStageScopes.id, current.id),\n ),\n )\n .orderBy(\n desc(principalHistoryStageScopes.touchedAt),\n asc(principalHistoryStageScopes.id),\n )\n // The writer always keeps its own stage, even when recency hints tie.\n .offset(INCOMPLETE_STAGE_LIMIT - (current.complete ? 0 : 1))\n .limit(RECLAIM_BATCH_LIMIT);\n const ids = obsolete.map(({ id }) => id);\n for (const id of ids) await releasePrincipalHistoryRoot(tx, `stage:${id}`);\n if (ids.length === 0) return;\n await tx\n .delete(principalHistoryStages)\n .where(inArray(principalHistoryStages.id, ids))\n .run();\n await tx\n .delete(principalHistoryStageScopes)\n .where(inArray(principalHistoryStageScopes.id, ids))\n .run();\n}\n"]}
1
+ {"version":3,"file":"principalHistoryIncompleteRetention.js","sourceRoot":"","sources":["principalHistoryIncompleteRetention.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAE/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAE9E,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACjC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,uCAAuC,CAC3D,EAAgC,EAChC,OAKC;IAED,MAAM,QAAQ,GAAG,MAAM,EAAE;SACtB,MAAM,CAAC,EAAE,EAAE,EAAE,sBAAsB,CAAC,EAAE,EAAE,CAAC;SACzC,IAAI,CAAC,sBAAsB,CAAC;SAC5B,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,sBAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EACnD,EAAE,CAAC,sBAAsB,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,EACjE,EAAE,CAAC,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC,EAC1C,EAAE,CAAC,sBAAsB,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,CAC1C,CACF;SACA,OAAO,CACN,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,EACtC,GAAG,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAC/B;QACD,sEAAsE;SACrE,MAAM,CAAC,sBAAsB,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAC3D,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACzC,KAAK,MAAM,EAAE,IAAI,GAAG;QAAE,MAAM,2BAA2B,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;IAC3E,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC7B,MAAM,EAAE;SACL,MAAM,CAAC,sBAAsB,CAAC;SAC9B,KAAK,CAAC,OAAO,CAAC,sBAAsB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;SAC9C,GAAG,EAAE,CAAC;AACX,CAAC","sourcesContent":["import { and, asc, desc, eq, inArray, ne } from \"drizzle-orm\";\nimport { principalHistoryStages } from \"../sqlite/principalHistoryStageSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../sqlite/sqlitePersistenceRuntime\";\nimport { releasePrincipalHistoryRoot } from \"./principalHistoryRootOwnership\";\n\nconst INCOMPLETE_STAGE_LIMIT = 8;\nconst RECLAIM_BATCH_LIMIT = 16;\n\n/** Disposable work only: completed artifacts and trust pins are unaffected. */\nexport async function reclaimIncompletePrincipalHistoryStages(\n tx: ClientSQLiteTransactionScope,\n current: {\n readonly id: string;\n readonly scopeId: string;\n readonly organizationId: string;\n readonly complete: boolean;\n },\n) {\n const obsolete = await tx\n .select({ id: principalHistoryStages.id })\n .from(principalHistoryStages)\n .where(\n and(\n eq(principalHistoryStages.scopeId, current.scopeId),\n eq(principalHistoryStages.organizationId, current.organizationId),\n eq(principalHistoryStages.complete, false),\n ne(principalHistoryStages.id, current.id),\n ),\n )\n .orderBy(\n desc(principalHistoryStages.touchedAt),\n asc(principalHistoryStages.id),\n )\n // The writer always keeps its own stage, even when recency hints tie.\n .offset(INCOMPLETE_STAGE_LIMIT - (current.complete ? 0 : 1))\n .limit(RECLAIM_BATCH_LIMIT);\n const ids = obsolete.map(({ id }) => id);\n for (const id of ids) await releasePrincipalHistoryRoot(tx, `stage:${id}`);\n if (ids.length === 0) return;\n await tx\n .delete(principalHistoryStages)\n .where(inArray(principalHistoryStages.id, ids))\n .run();\n}\n"]}
@@ -1,15 +1,9 @@
1
1
  import { principalHistoryStages } from "../sqlite/principalHistoryStageSchema.js";
2
2
  import { type ExecSql } from "../sqlite/sqlSchema.js";
3
3
  import { type PrincipalHistoryEvidencePage } from "./principalHistoryEvidencePersistence.js";
4
- export type PrincipalHistoryStage = typeof principalHistoryStages.$inferSelect;
5
- export declare function loadPrincipalHistoryStage(execSql: ExecSql, id: string): Promise<{
6
- id: string;
7
- organizationId: string;
8
- currentJson: string;
9
- afterVersion: number;
10
- complete: boolean;
11
- progress: string;
12
- } | null>;
4
+ /** A stage's verification work; its retention hints are written by persistence. */
5
+ export type PrincipalHistoryStage = Omit<typeof principalHistoryStages.$inferSelect, "scopeId" | "touchedAt">;
6
+ export declare function loadPrincipalHistoryStage(execSql: ExecSql, id: string): Promise<PrincipalHistoryStage | null>;
13
7
  /** Publish accepted entries and index nodes atomically with authenticated progress. */
14
8
  export declare function savePrincipalHistoryStage(input: {
15
9
  readonly execSql: ExecSql;
@@ -2,7 +2,6 @@ import { and, eq } from "drizzle-orm";
2
2
  import { assertProjectionVerificationCurrent } from "../keyingProjectionVerification/types.js";
3
3
  import { principalHistoryEvidenceTables } from "../sqlite/principalHistoryEvidenceSchema.js";
4
4
  import { principalHistoryNodeRetentionTables } from "../sqlite/principalHistoryNodeRetentionSchema.js";
5
- import { principalHistoryStageScopes } from "../sqlite/principalHistoryRetentionSchema.js";
6
5
  import { principalHistoryStages, principalHistoryStageTables, } from "../sqlite/principalHistoryStageSchema.js";
7
6
  import { getClientSQLitePersistenceRuntime } from "../sqlite/sqlitePersistenceRuntime.js";
8
7
  import { ensureSqlTables } from "../sqlite/sqlSchema.js";
@@ -10,8 +9,15 @@ import { writePrincipalHistoryEvidencePage, } from "./principalHistoryEvidencePe
10
9
  import { reclaimIncompletePrincipalHistoryStages } from "./principalHistoryIncompleteRetention.js";
11
10
  import { reclaimPrincipalHistoryNodes } from "./principalHistoryNodeRetention.js";
12
11
  import { releasePrincipalHistoryRoot, retainPrincipalHistoryRoot, } from "./principalHistoryRootOwnership.js";
13
- import { recordPrincipalHistoryStageScope } from "./principalHistoryStageRetention.js";
14
12
  import { reclaimUnpublishedPrincipalHistoryStages } from "./principalHistoryUnpublishedRetention.js";
13
+ const principalHistoryStageColumns = {
14
+ id: principalHistoryStages.id,
15
+ organizationId: principalHistoryStages.organizationId,
16
+ currentJson: principalHistoryStages.currentJson,
17
+ afterVersion: principalHistoryStages.afterVersion,
18
+ complete: principalHistoryStages.complete,
19
+ progress: principalHistoryStages.progress,
20
+ };
15
21
  class PrincipalHistoryStageChangedError extends Error {
16
22
  code = "principal_history_stage_changed";
17
23
  constructor() {
@@ -23,7 +29,7 @@ export async function loadPrincipalHistoryStage(execSql, id) {
23
29
  await ensureSqlTables(execSql, principalHistoryStageTables);
24
30
  const { db } = getClientSQLitePersistenceRuntime(execSql);
25
31
  const [row] = await db
26
- .select()
32
+ .select(principalHistoryStageColumns)
27
33
  .from(principalHistoryStages)
28
34
  .where(eq(principalHistoryStages.id, id))
29
35
  .limit(1);
@@ -31,8 +37,8 @@ export async function loadPrincipalHistoryStage(execSql, id) {
31
37
  }
32
38
  /** Publish accepted entries and index nodes atomically with authenticated progress. */
33
39
  export async function savePrincipalHistoryStage(input) {
34
- const stage = { ...input.stage };
35
40
  const evidence = structuredClone(input.evidence);
41
+ const stage = { ...input.stage, scopeId: evidence.scopeId };
36
42
  await ensureSqlTables(input.execSql, [
37
43
  ...principalHistoryStageTables,
38
44
  ...principalHistoryEvidenceTables,
@@ -47,32 +53,20 @@ export async function savePrincipalHistoryStage(input) {
47
53
  if ((previous?.progress ?? null) !== input.previousProgress)
48
54
  throw new PrincipalHistoryStageChangedError();
49
55
  await writePrincipalHistoryEvidencePage(tx, evidence);
56
+ const row = { ...stage, touchedAt: Date.now() };
50
57
  await tx
51
58
  .insert(principalHistoryStages)
52
- .values(stage)
53
- .onConflictDoUpdate({ target: principalHistoryStages.id, set: stage })
59
+ .values(row)
60
+ .onConflictDoUpdate({ target: principalHistoryStages.id, set: row })
54
61
  .run();
55
- await recordPrincipalHistoryStageScope(tx, {
56
- id: stage.id,
57
- afterVersion: stage.afterVersion,
58
- complete: stage.complete,
59
- organizationId: stage.organizationId,
60
- scopeId: evidence.scopeId,
61
- });
62
62
  await retainPrincipalHistoryRoot(tx, {
63
63
  id: `stage:${stage.id}`,
64
64
  scopeId: evidence.scopeId,
65
65
  organizationId: stage.organizationId,
66
66
  rootHash: evidence.indexRootHash,
67
67
  });
68
- await reclaimIncompletePrincipalHistoryStages(tx, {
69
- ...stage,
70
- scopeId: evidence.scopeId,
71
- });
72
- await reclaimUnpublishedPrincipalHistoryStages(tx, {
73
- ...stage,
74
- scopeId: evidence.scopeId,
75
- });
68
+ await reclaimIncompletePrincipalHistoryStages(tx, stage);
69
+ await reclaimUnpublishedPrincipalHistoryStages(tx, stage);
76
70
  await reclaimPrincipalHistoryNodes(tx, evidence);
77
71
  }, input.stillCurrent, { behavior: "immediate" });
78
72
  assertProjectionVerificationCurrent(() => saved.committed);
@@ -96,10 +90,6 @@ export async function discardPrincipalHistoryStage(execSql, stage, stillCurrent)
96
90
  .delete(principalHistoryStages)
97
91
  .where(and(eq(principalHistoryStages.id, stage.id), eq(principalHistoryStages.progress, stage.progress)))
98
92
  .run();
99
- await db
100
- .delete(principalHistoryStageScopes)
101
- .where(eq(principalHistoryStageScopes.id, stage.id))
102
- .run();
103
93
  }, stillCurrent, { behavior: "immediate" });
104
94
  assertProjectionVerificationCurrent(() => discarded.committed);
105
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"principalHistoryStagePersistence.js","sourceRoot":"","sources":["principalHistoryStagePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { assertProjectionVerificationCurrent } from \"../keyingProjectionVerification/types\";\nimport { principalHistoryEvidenceTables } from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { principalHistoryNodeRetentionTables } from \"../sqlite/principalHistoryNodeRetentionSchema\";\nimport { principalHistoryStageScopes } from \"../sqlite/principalHistoryRetentionSchema\";\nimport {\n principalHistoryStages,\n principalHistoryStageTables,\n} from \"../sqlite/principalHistoryStageSchema\";\nimport { getClientSQLitePersistenceRuntime } from \"../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\nimport {\n type PrincipalHistoryEvidencePage,\n writePrincipalHistoryEvidencePage,\n} from \"./principalHistoryEvidencePersistence\";\nimport { reclaimIncompletePrincipalHistoryStages } from \"./principalHistoryIncompleteRetention\";\nimport { reclaimPrincipalHistoryNodes } from \"./principalHistoryNodeRetention\";\nimport {\n releasePrincipalHistoryRoot,\n retainPrincipalHistoryRoot,\n} from \"./principalHistoryRootOwnership\";\nimport { recordPrincipalHistoryStageScope } from \"./principalHistoryStageRetention\";\nimport { reclaimUnpublishedPrincipalHistoryStages } from \"./principalHistoryUnpublishedRetention\";\n\nexport type PrincipalHistoryStage = typeof principalHistoryStages.$inferSelect;\n\nclass PrincipalHistoryStageChangedError extends Error {\n readonly code = \"principal_history_stage_changed\";\n constructor() {\n super(\n \"Another operation changed the saved principal history; resume from its current state\",\n );\n this.name = \"PrincipalHistoryStageChangedError\";\n }\n}\n\nexport async function loadPrincipalHistoryStage(execSql: ExecSql, id: string) {\n await ensureSqlTables(execSql, principalHistoryStageTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [row] = await db\n .select()\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, id))\n .limit(1);\n return row ?? null;\n}\n\n/** Publish accepted entries and index nodes atomically with authenticated progress. */\nexport async function savePrincipalHistoryStage(input: {\n readonly execSql: ExecSql;\n readonly stage: PrincipalHistoryStage;\n readonly evidence: PrincipalHistoryEvidencePage;\n readonly previousProgress: string | null;\n readonly stillCurrent: () => boolean;\n}): Promise<void> {\n const stage = { ...input.stage };\n const evidence = structuredClone(input.evidence);\n await ensureSqlTables(input.execSql, [\n ...principalHistoryStageTables,\n ...principalHistoryEvidenceTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const saved = await runtime.guardedTransaction(\n async (tx) => {\n const [previous] = await tx\n .select({ progress: principalHistoryStages.progress })\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, stage.id))\n .limit(1);\n if ((previous?.progress ?? null) !== input.previousProgress)\n throw new PrincipalHistoryStageChangedError();\n await writePrincipalHistoryEvidencePage(tx, evidence);\n await tx\n .insert(principalHistoryStages)\n .values(stage)\n .onConflictDoUpdate({ target: principalHistoryStages.id, set: stage })\n .run();\n await recordPrincipalHistoryStageScope(tx, {\n id: stage.id,\n afterVersion: stage.afterVersion,\n complete: stage.complete,\n organizationId: stage.organizationId,\n scopeId: evidence.scopeId,\n });\n await retainPrincipalHistoryRoot(tx, {\n id: `stage:${stage.id}`,\n scopeId: evidence.scopeId,\n organizationId: stage.organizationId,\n rootHash: evidence.indexRootHash,\n });\n await reclaimIncompletePrincipalHistoryStages(tx, {\n ...stage,\n scopeId: evidence.scopeId,\n });\n await reclaimUnpublishedPrincipalHistoryStages(tx, {\n ...stage,\n scopeId: evidence.scopeId,\n });\n await reclaimPrincipalHistoryNodes(tx, evidence);\n },\n input.stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => saved.committed);\n}\n\nexport async function discardPrincipalHistoryStage(\n execSql: ExecSql,\n stage: PrincipalHistoryStage,\n stillCurrent: () => boolean,\n) {\n await ensureSqlTables(execSql, [\n ...principalHistoryStageTables,\n ...principalHistoryNodeRetentionTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const discarded = await runtime.guardedTransaction(\n async (db) => {\n const [current] = await db\n .select({ progress: principalHistoryStages.progress })\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, stage.id))\n .limit(1);\n if (current?.progress !== stage.progress) return;\n await releasePrincipalHistoryRoot(db, `stage:${stage.id}`);\n await db\n .delete(principalHistoryStages)\n .where(\n and(\n eq(principalHistoryStages.id, stage.id),\n eq(principalHistoryStages.progress, stage.progress),\n ),\n )\n .run();\n await db\n .delete(principalHistoryStageScopes)\n .where(eq(principalHistoryStageScopes.id, stage.id))\n .run();\n },\n stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => discarded.committed);\n}\n"]}
1
+ {"version":3,"file":"principalHistoryStagePersistence.js","sourceRoot":"","sources":["principalHistoryStagePersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { assertProjectionVerificationCurrent } from \"../keyingProjectionVerification/types\";\nimport { principalHistoryEvidenceTables } from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { principalHistoryNodeRetentionTables } from \"../sqlite/principalHistoryNodeRetentionSchema\";\nimport {\n principalHistoryStages,\n principalHistoryStageTables,\n} from \"../sqlite/principalHistoryStageSchema\";\nimport { getClientSQLitePersistenceRuntime } from \"../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\nimport {\n type PrincipalHistoryEvidencePage,\n writePrincipalHistoryEvidencePage,\n} from \"./principalHistoryEvidencePersistence\";\nimport { reclaimIncompletePrincipalHistoryStages } from \"./principalHistoryIncompleteRetention\";\nimport { reclaimPrincipalHistoryNodes } from \"./principalHistoryNodeRetention\";\nimport {\n releasePrincipalHistoryRoot,\n retainPrincipalHistoryRoot,\n} from \"./principalHistoryRootOwnership\";\nimport { reclaimUnpublishedPrincipalHistoryStages } from \"./principalHistoryUnpublishedRetention\";\n\n/** A stage's verification work; its retention hints are written by persistence. */\nexport type PrincipalHistoryStage = Omit<\n typeof principalHistoryStages.$inferSelect,\n \"scopeId\" | \"touchedAt\"\n>;\n\nconst principalHistoryStageColumns = {\n id: principalHistoryStages.id,\n organizationId: principalHistoryStages.organizationId,\n currentJson: principalHistoryStages.currentJson,\n afterVersion: principalHistoryStages.afterVersion,\n complete: principalHistoryStages.complete,\n progress: principalHistoryStages.progress,\n};\n\nclass PrincipalHistoryStageChangedError extends Error {\n readonly code = \"principal_history_stage_changed\";\n constructor() {\n super(\n \"Another operation changed the saved principal history; resume from its current state\",\n );\n this.name = \"PrincipalHistoryStageChangedError\";\n }\n}\n\nexport async function loadPrincipalHistoryStage(\n execSql: ExecSql,\n id: string,\n): Promise<PrincipalHistoryStage | null> {\n await ensureSqlTables(execSql, principalHistoryStageTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [row] = await db\n .select(principalHistoryStageColumns)\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, id))\n .limit(1);\n return row ?? null;\n}\n\n/** Publish accepted entries and index nodes atomically with authenticated progress. */\nexport async function savePrincipalHistoryStage(input: {\n readonly execSql: ExecSql;\n readonly stage: PrincipalHistoryStage;\n readonly evidence: PrincipalHistoryEvidencePage;\n readonly previousProgress: string | null;\n readonly stillCurrent: () => boolean;\n}): Promise<void> {\n const evidence = structuredClone(input.evidence);\n const stage = { ...input.stage, scopeId: evidence.scopeId };\n await ensureSqlTables(input.execSql, [\n ...principalHistoryStageTables,\n ...principalHistoryEvidenceTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const saved = await runtime.guardedTransaction(\n async (tx) => {\n const [previous] = await tx\n .select({ progress: principalHistoryStages.progress })\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, stage.id))\n .limit(1);\n if ((previous?.progress ?? null) !== input.previousProgress)\n throw new PrincipalHistoryStageChangedError();\n await writePrincipalHistoryEvidencePage(tx, evidence);\n const row = { ...stage, touchedAt: Date.now() };\n await tx\n .insert(principalHistoryStages)\n .values(row)\n .onConflictDoUpdate({ target: principalHistoryStages.id, set: row })\n .run();\n await retainPrincipalHistoryRoot(tx, {\n id: `stage:${stage.id}`,\n scopeId: evidence.scopeId,\n organizationId: stage.organizationId,\n rootHash: evidence.indexRootHash,\n });\n await reclaimIncompletePrincipalHistoryStages(tx, stage);\n await reclaimUnpublishedPrincipalHistoryStages(tx, stage);\n await reclaimPrincipalHistoryNodes(tx, evidence);\n },\n input.stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => saved.committed);\n}\n\nexport async function discardPrincipalHistoryStage(\n execSql: ExecSql,\n stage: PrincipalHistoryStage,\n stillCurrent: () => boolean,\n) {\n await ensureSqlTables(execSql, [\n ...principalHistoryStageTables,\n ...principalHistoryNodeRetentionTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(execSql);\n const discarded = await runtime.guardedTransaction(\n async (db) => {\n const [current] = await db\n .select({ progress: principalHistoryStages.progress })\n .from(principalHistoryStages)\n .where(eq(principalHistoryStages.id, stage.id))\n .limit(1);\n if (current?.progress !== stage.progress) return;\n await releasePrincipalHistoryRoot(db, `stage:${stage.id}`);\n await db\n .delete(principalHistoryStages)\n .where(\n and(\n eq(principalHistoryStages.id, stage.id),\n eq(principalHistoryStages.progress, stage.progress),\n ),\n )\n .run();\n },\n stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => discarded.committed);\n}\n"]}
@@ -1,26 +1,31 @@
1
1
  import { and, asc, desc, eq, lt } from "drizzle-orm";
2
- import { principalHistoryStageScopes } from "../sqlite/principalHistoryRetentionSchema.js";
3
2
  import { principalHistoryStages } from "../sqlite/principalHistoryStageSchema.js";
4
3
  import { releasePrincipalHistoryRoot } from "./principalHistoryRootOwnership.js";
5
4
  import { archivePrincipalHistoryKeyEnvelopes } from "./principalKeyEnvelopeArchivePersistence.js";
6
5
  /** Each publication reclaims at most this many obsolete completed heads. */
7
6
  export const PRINCIPAL_HISTORY_STAGE_RECLAIM_LIMIT = 16;
8
- export async function recordPrincipalHistoryStageScope(tx, input) {
9
- const row = { ...input, touchedAt: Date.now() };
7
+ /** Mark a stored stage as just used by a writer for its principal history. */
8
+ export async function touchPrincipalHistoryStage(tx, input) {
10
9
  await tx
11
- .insert(principalHistoryStageScopes)
12
- .values(row)
13
- .onConflictDoUpdate({ target: principalHistoryStageScopes.id, set: row })
10
+ .update(principalHistoryStages)
11
+ .set({ scopeId: input.scopeId, touchedAt: Date.now() })
12
+ .where(eq(principalHistoryStages.id, input.id))
14
13
  .run();
15
14
  }
16
15
  /** The prefix and its key candidates must be published in this transaction. */
17
16
  export async function reclaimCompletedPrincipalHistoryStages(tx, prefix) {
18
17
  const obsolete = await tx
19
- .select({ stage: principalHistoryStages })
20
- .from(principalHistoryStageScopes)
21
- .innerJoin(principalHistoryStages, eq(principalHistoryStageScopes.id, principalHistoryStages.id))
22
- .where(and(eq(principalHistoryStageScopes.scopeId, prefix.scopeId), eq(principalHistoryStageScopes.organizationId, prefix.organizationId), eq(principalHistoryStageScopes.complete, true), lt(principalHistoryStageScopes.afterVersion, prefix.version), eq(principalHistoryStages.organizationId, prefix.organizationId), eq(principalHistoryStages.complete, true), lt(principalHistoryStages.afterVersion, prefix.version)))
23
- .orderBy(desc(principalHistoryStageScopes.afterVersion), desc(principalHistoryStageScopes.touchedAt), asc(principalHistoryStageScopes.id))
18
+ .select({
19
+ stage: {
20
+ id: principalHistoryStages.id,
21
+ organizationId: principalHistoryStages.organizationId,
22
+ currentJson: principalHistoryStages.currentJson,
23
+ afterVersion: principalHistoryStages.afterVersion,
24
+ },
25
+ })
26
+ .from(principalHistoryStages)
27
+ .where(and(eq(principalHistoryStages.scopeId, prefix.scopeId), eq(principalHistoryStages.organizationId, prefix.organizationId), eq(principalHistoryStages.complete, true), lt(principalHistoryStages.afterVersion, prefix.version)))
28
+ .orderBy(desc(principalHistoryStages.afterVersion), desc(principalHistoryStages.touchedAt), asc(principalHistoryStages.id))
24
29
  // Keep the current completed head and its newest completed predecessor.
25
30
  // This also preserves the prefix-only predecessor restored after an ack.
26
31
  .offset(2)
@@ -35,10 +40,6 @@ export async function reclaimCompletedPrincipalHistoryStages(tx, prefix) {
35
40
  .delete(principalHistoryStages)
36
41
  .where(eq(principalHistoryStages.id, stage.id))
37
42
  .run();
38
- await tx
39
- .delete(principalHistoryStageScopes)
40
- .where(eq(principalHistoryStageScopes.id, stage.id))
41
- .run();
42
43
  }
43
44
  }
44
45
  //# sourceMappingURL=principalHistoryStageRetention.js.map