@tearleads/client-sdk 0.1.18 → 0.1.20

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 (214) hide show
  1. package/client/organizations/index.js +2 -2
  2. package/client/organizations/index.js.map +1 -1
  3. package/client/organizations/organizationHistory.js +66 -0
  4. package/client/organizations/organizationHistory.js.map +1 -0
  5. package/client/organizations/organizationReadModels.d.ts +1 -1
  6. package/client/organizations/organizationReadModels.js +7 -27
  7. package/client/organizations/organizationReadModels.js.map +1 -1
  8. package/client/organizations/organizationsTypes.d.ts +1 -1
  9. package/client/organizations/organizationsTypes.js.map +1 -1
  10. package/data/containers/shared/types.d.ts +2 -2
  11. package/data/containers/shared/types.js.map +1 -1
  12. package/data/documents/shared/syncSubmitFailure.d.ts +2 -2
  13. package/data/documents/shared/syncSubmitFailure.js.map +1 -1
  14. package/data/keyingProjectionVerification/documentPurgeCheckpointCurrency.js +34 -8
  15. package/data/keyingProjectionVerification/documentPurgeCheckpointCurrency.js.map +1 -1
  16. package/data/keyingProjectionVerification/documentPurgeDocumentChainVerification.js.map +1 -1
  17. package/data/keyingProjectionVerification/documentPurgePolicyCheckpoints.js +17 -0
  18. package/data/keyingProjectionVerification/documentPurgePolicyCheckpoints.js.map +1 -0
  19. package/data/keyingProjectionVerification/documentPurgePolicyCurrency.js +55 -0
  20. package/data/keyingProjectionVerification/documentPurgePolicyCurrency.js.map +1 -0
  21. package/data/keyingProjectionVerification/documentPurgePrincipalEvidence.js +25 -13
  22. package/data/keyingProjectionVerification/documentPurgePrincipalEvidence.js.map +1 -1
  23. package/data/keyingProjectionVerification/documentPurgeProofVerification.d.ts +5 -3
  24. package/data/keyingProjectionVerification/documentPurgeProofVerification.js +52 -8
  25. package/data/keyingProjectionVerification/documentPurgeProofVerification.js.map +1 -1
  26. package/data/keyingProjectionVerification/projectionPolicyEvidence.js.map +1 -1
  27. package/data/keyingProjectionVerification/types.d.ts +4 -0
  28. package/data/keyingProjectionVerification/types.js.map +1 -1
  29. package/data/persistence/container-contents/documentMoveIntentPersistence.js.map +1 -1
  30. package/data/persistence/principalAuthorizationCheckpoints.js +1 -1
  31. package/data/persistence/principalAuthorizationCheckpoints.js.map +1 -1
  32. package/data/persistence/principalCurrentAcknowledgementPersistence.d.ts +1 -0
  33. package/data/persistence/principalCurrentAcknowledgementPersistence.js +58 -12
  34. package/data/persistence/principalCurrentAcknowledgementPersistence.js.map +1 -1
  35. package/data/persistence/principalHistoryEvidencePersistence.d.ts +2 -0
  36. package/data/persistence/principalHistoryEvidencePersistence.js +2 -0
  37. package/data/persistence/principalHistoryEvidencePersistence.js.map +1 -1
  38. package/data/persistence/principalHistoryIncompleteRetention.js +32 -0
  39. package/data/persistence/principalHistoryIncompleteRetention.js.map +1 -0
  40. package/data/persistence/principalHistoryNodeRetention.js +76 -0
  41. package/data/persistence/principalHistoryNodeRetention.js.map +1 -0
  42. package/data/persistence/principalHistoryPrefixPersistence.d.ts +1 -0
  43. package/data/persistence/principalHistoryPrefixPersistence.js +30 -1
  44. package/data/persistence/principalHistoryPrefixPersistence.js.map +1 -1
  45. package/data/persistence/principalHistoryRootOwnership.js +71 -0
  46. package/data/persistence/principalHistoryRootOwnership.js.map +1 -0
  47. package/data/persistence/principalHistoryStagePersistence.js +45 -0
  48. package/data/persistence/principalHistoryStagePersistence.js.map +1 -1
  49. package/data/persistence/principalHistoryStageRetention.js +44 -0
  50. package/data/persistence/principalHistoryStageRetention.js.map +1 -0
  51. package/data/persistence/principalHistoryUnpublishedRetention.js +71 -0
  52. package/data/persistence/principalHistoryUnpublishedRetention.js.map +1 -0
  53. package/data/persistence/principalKeyEnvelopeArchivePersistence.js +63 -0
  54. package/data/persistence/principalKeyEnvelopeArchivePersistence.js.map +1 -0
  55. package/data/persistence/principalKeyEnvelopeCandidates.js +10 -1
  56. package/data/persistence/principalKeyEnvelopeCandidates.js.map +1 -1
  57. package/data/sqlite/principalHistoryEvidenceSchema.js +2 -0
  58. package/data/sqlite/principalHistoryEvidenceSchema.js.map +1 -1
  59. package/data/sqlite/principalHistoryNodeRetentionSchema.js +30 -0
  60. package/data/sqlite/principalHistoryNodeRetentionSchema.js.map +1 -0
  61. package/data/sqlite/principalHistoryRetentionSchema.js +40 -0
  62. package/data/sqlite/principalHistoryRetentionSchema.js.map +1 -0
  63. package/data/sqlite/principalHistoryStageSchema.js +2 -0
  64. package/data/sqlite/principalHistoryStageSchema.js.map +1 -1
  65. package/data/sqlite/schema.d.ts +453 -0
  66. package/data/sqlite/schema.js +6 -0
  67. package/data/sqlite/schema.js.map +1 -1
  68. package/data/sqlite/sqliteCommitGuards.js +49 -0
  69. package/data/sqlite/sqliteCommitGuards.js.map +1 -0
  70. package/data/sqlite/sqlitePersistenceRuntime.js +14 -2
  71. package/data/sqlite/sqlitePersistenceRuntime.js.map +1 -1
  72. package/index.d.ts +3 -1
  73. package/index.js.map +1 -1
  74. package/internal/api-client/ApiClient.d.ts +60 -137
  75. package/internal/api-client/ApiClient.js +7 -5
  76. package/internal/api-client/ApiClient.js.map +1 -1
  77. package/internal/api-client/apiRequestRuntime.js +7 -7
  78. package/internal/api-client/apiRequestRuntime.js.map +1 -1
  79. package/internal/api-client/documentPurge.js +25 -0
  80. package/internal/api-client/documentPurge.js.map +1 -0
  81. package/internal/api-client/organizationPolicyHistory.d.ts +53 -0
  82. package/internal/api-client/organizationPolicyHistory.js +15 -0
  83. package/internal/api-client/organizationPolicyHistory.js.map +1 -0
  84. package/internal/api-client/requestInternals.js +4 -4
  85. package/internal/api-client/requestInternals.js.map +1 -1
  86. package/internal/api-client/types.d.ts +2 -2
  87. package/internal/api-client/types.js.map +1 -1
  88. package/internal/crypto/keying/principalPolicyHistory.js +4 -0
  89. package/internal/crypto/keying/principalPolicyHistory.js.map +1 -1
  90. package/internal/crypto/keying/principalPolicyHistoryTypes.d.ts +2 -0
  91. package/internal/crypto/keying/principalPolicyHistoryTypes.js.map +1 -1
  92. package/internal/loro/document.js +8 -11
  93. package/internal/loro/document.js.map +1 -1
  94. package/internal/validators/operation/containerMutations.d.ts +448 -6528
  95. package/internal/validators/operation/documentMutations.d.ts +323 -579
  96. package/internal/validators/operation/documentMutations.js +22 -4
  97. package/internal/validators/operation/documentMutations.js.map +1 -1
  98. package/internal/validators/operation/documentSync.d.ts +26 -406
  99. package/internal/validators/operation/organizationPolicyHistory.d.ts +188 -310
  100. package/internal/validators/operation/organizationPolicyHistory.js +17 -3
  101. package/internal/validators/operation/organizationPolicyHistory.js.map +1 -1
  102. package/internal/validators/response/containerMutationError.d.ts +28 -408
  103. package/internal/validators/response/documentMutation.d.ts +232 -544
  104. package/internal/validators/response/documentMutation.js +1 -2
  105. package/internal/validators/response/documentMutation.js.map +1 -1
  106. package/internal/validators/response/documentSyncError.d.ts +26 -406
  107. package/internal/validators/response/documentSyncError.js +4 -3
  108. package/internal/validators/response/documentSyncError.js.map +1 -1
  109. package/internal/validators/response/index.d.ts +1 -1
  110. package/internal/validators/response/index.js +1 -1
  111. package/internal/validators/response/index.js.map +1 -1
  112. package/internal/validators/response/organizationPolicyHistory.d.ts +127 -309
  113. package/internal/validators/response/organizationPolicyHistory.js +10 -4
  114. package/internal/validators/response/organizationPolicyHistory.js.map +1 -1
  115. package/internal/validators/response/principal.d.ts +28 -408
  116. package/internal/validators/response/principal.js +3 -1
  117. package/internal/validators/response/principal.js.map +1 -1
  118. package/internal/validators/util/index.d.ts +1 -1
  119. package/internal/validators/util/index.js +1 -1
  120. package/internal/validators/util/index.js.map +1 -1
  121. package/internal/validators/util/principalHistoryWire.d.ts +2 -0
  122. package/internal/validators/util/principalHistoryWire.js +2 -0
  123. package/internal/validators/util/principalHistoryWire.js.map +1 -1
  124. package/package.json +4 -4
  125. package/workflows/container-contents/container-state/createWithMetadata.js +19 -49
  126. package/workflows/container-contents/container-state/createWithMetadata.js.map +1 -1
  127. package/workflows/container-contents/container-state/groupGrantVerification.js +29 -0
  128. package/workflows/container-contents/container-state/groupGrantVerification.js.map +1 -1
  129. package/workflows/container-contents/container-state/groupShareEpoch.js +33 -0
  130. package/workflows/container-contents/container-state/groupShareEpoch.js.map +1 -0
  131. package/workflows/container-contents/container-state/remote.js +4 -1
  132. package/workflows/container-contents/container-state/remote.js.map +1 -1
  133. package/workflows/container-contents/container-state/share.js +2 -24
  134. package/workflows/container-contents/container-state/share.js.map +1 -1
  135. package/workflows/container-contents/documentTombstoneEvidence.js +23 -12
  136. package/workflows/container-contents/documentTombstoneEvidence.js.map +1 -1
  137. package/workflows/containers/child/create.js +1 -3
  138. package/workflows/containers/child/create.js.map +1 -1
  139. package/workflows/containers/child/createSubmission.d.ts +1 -3
  140. package/workflows/containers/child/createSubmission.js +13 -15
  141. package/workflows/containers/child/createSubmission.js.map +1 -1
  142. package/workflows/containers/child/currentShareGrantMutation.js +82 -0
  143. package/workflows/containers/child/currentShareGrantMutation.js.map +1 -0
  144. package/workflows/containers/child/currentSharePrincipalPolicy.js +83 -0
  145. package/workflows/containers/child/currentSharePrincipalPolicy.js.map +1 -0
  146. package/workflows/containers/child/mutationSubmit.js +1 -1
  147. package/workflows/containers/child/mutationSubmit.js.map +1 -1
  148. package/workflows/containers/child/runtimeCurrentGroupShare.js +82 -0
  149. package/workflows/containers/child/runtimeCurrentGroupShare.js.map +1 -0
  150. package/workflows/containers/child/share.js +1 -14
  151. package/workflows/containers/child/share.js.map +1 -1
  152. package/workflows/containers/child/shareProjection.js +15 -0
  153. package/workflows/containers/child/shareProjection.js.map +1 -0
  154. package/workflows/documents/purge.d.ts +3 -0
  155. package/workflows/documents/purge.js +13 -0
  156. package/workflows/documents/purge.js.map +1 -1
  157. package/workflows/documents/sync.js +2 -0
  158. package/workflows/documents/sync.js.map +1 -1
  159. package/workflows/documents/syncFailures.js +2 -2
  160. package/workflows/documents/syncFailures.js.map +1 -1
  161. package/workflows/documents/syncPolicyRepair.js +15 -13
  162. package/workflows/documents/syncPolicyRepair.js.map +1 -1
  163. package/workflows/organizations/initialPrincipalCurrentPublication.js +2 -0
  164. package/workflows/organizations/initialPrincipalCurrentPublication.js.map +1 -1
  165. package/workflows/organizations/loadPolicyHistoryDetails.js +31 -32
  166. package/workflows/organizations/loadPolicyHistoryDetails.js.map +1 -1
  167. package/workflows/organizations/organizationPolicyHistoryDetails.js +37 -15
  168. package/workflows/organizations/organizationPolicyHistoryDetails.js.map +1 -1
  169. package/workflows/organizations/organizationPolicyHistoryVerification.js +56 -69
  170. package/workflows/organizations/organizationPolicyHistoryVerification.js.map +1 -1
  171. package/workflows/organizations/policyHistoryCache.js +5 -2
  172. package/workflows/organizations/policyHistoryCache.js.map +1 -1
  173. package/workflows/organizations/policyHistoryTypes.d.ts +1 -0
  174. package/workflows/organizations/policyHistoryTypes.js.map +1 -1
  175. package/workflows/organizations/retainAcknowledgedPrincipalCurrents.js +4 -1
  176. package/workflows/organizations/retainAcknowledgedPrincipalCurrents.js.map +1 -1
  177. package/workflows/principals/loadRecoveredPrincipalHistoryPage.d.ts +0 -1
  178. package/workflows/principals/loadRecoveredPrincipalHistoryPage.js +1 -2
  179. package/workflows/principals/loadRecoveredPrincipalHistoryPage.js.map +1 -1
  180. package/workflows/principals/policyRepair.js +38 -0
  181. package/workflows/principals/policyRepair.js.map +1 -0
  182. package/workflows/principals/policyRepairBudget.d.ts +2 -2
  183. package/workflows/principals/policyRepairBudget.js +3 -3
  184. package/workflows/principals/policyRepairBudget.js.map +1 -1
  185. package/workflows/principals/principalHistoryRecoveryPrefix.js +4 -0
  186. package/workflows/principals/principalHistoryRecoveryPrefix.js.map +1 -1
  187. package/workflows/principals/principalRecoveryLocalPreference.js +3 -2
  188. package/workflows/principals/principalRecoveryLocalPreference.js.map +1 -1
  189. package/workflows/principals/publicPrincipalHistoryPage.js +1 -0
  190. package/workflows/principals/publicPrincipalHistoryPage.js.map +1 -1
  191. package/workflows/principals/publicPrincipalHistoryProgress.js +1 -0
  192. package/workflows/principals/publicPrincipalHistoryProgress.js.map +1 -1
  193. package/workflows/principals/publicPrincipalHistoryTypes.d.ts +2 -0
  194. package/workflows/principals/publicPrincipalHistoryTypes.js.map +1 -1
  195. package/workflows/principals/publicProjectionPrincipal.js +1 -0
  196. package/workflows/principals/publicProjectionPrincipal.js.map +1 -1
  197. package/workflows/principals/recoverPrincipalHistoryPage.js +2 -1
  198. package/workflows/principals/recoverPrincipalHistoryPage.js.map +1 -1
  199. package/workflows/principals/recoverPrincipalPolicyHistory.js +1 -0
  200. package/workflows/principals/recoverPrincipalPolicyHistory.js.map +1 -1
  201. package/workflows/principals/recoverPublicPrincipalHistory.js +2 -0
  202. package/workflows/principals/recoverPublicPrincipalHistory.js.map +1 -1
  203. package/workflows/principals/runtimePolicyRecovery.js +2 -1
  204. package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
  205. package/workflows/principals/selectPublicPrincipalHistory.js +21 -15
  206. package/workflows/principals/selectPublicPrincipalHistory.js.map +1 -1
  207. package/workflows/sync/remoteResetPrincipalScope.js +6 -0
  208. package/workflows/sync/remoteResetPrincipalScope.js.map +1 -1
  209. package/data/keyingProjectionVerification/principalPolicySnapshotVerification.js +0 -208
  210. package/data/keyingProjectionVerification/principalPolicySnapshotVerification.js.map +0 -1
  211. package/data/keyingProjectionVerification/snapshotVerificationCache.js +0 -39
  212. package/data/keyingProjectionVerification/snapshotVerificationCache.js.map +0 -1
  213. package/workflows/containers/child/policyRepair.js +0 -23
  214. package/workflows/containers/child/policyRepair.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principalHistoryNodeRetention.js","sourceRoot":"","sources":["principalHistoryNodeRetention.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PrincipalHistoryIndexNode } from \"@tearleads/crypto\";\nimport { and, asc, eq } from \"drizzle-orm\";\nimport { principalHistoryNodes } from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { principalHistoryNodeReferences } from \"../sqlite/principalHistoryNodeRetentionSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../sqlite/sqlitePersistenceRuntime\";\nimport {\n decrementPrincipalHistoryNodeReference,\n incrementPrincipalHistoryNodeReference,\n type PrincipalHistoryNodeScope,\n} from \"./principalHistoryRootOwnership\";\n\n/** Register edges before inserting their proof node, preserving pre-index evidence. */\nexport async function recordPrincipalHistoryNodeEdges(\n tx: ClientSQLiteTransactionScope,\n scope: PrincipalHistoryNodeScope,\n node: PrincipalHistoryIndexNode,\n) {\n const key = and(\n eq(principalHistoryNodeReferences.scopeId, scope.scopeId),\n eq(principalHistoryNodeReferences.hash, node.hash),\n );\n const [previous] = await tx\n .select()\n .from(principalHistoryNodeReferences)\n .where(key)\n .limit(1);\n if (\n previous?.leftHash === node.leftHash &&\n previous.rightHash === node.rightHash\n )\n return;\n const [existing] = await tx\n .select({ hash: principalHistoryNodes.hash })\n .from(principalHistoryNodes)\n .where(\n and(\n eq(principalHistoryNodes.scopeId, scope.scopeId),\n eq(principalHistoryNodes.hash, node.hash),\n ),\n )\n .limit(1);\n for (const hash of [node.leftHash, node.rightHash])\n await incrementPrincipalHistoryNodeReference(tx, scope, hash);\n await tx\n .insert(principalHistoryNodeReferences)\n .values({\n ...scope,\n ...node,\n referenceCount: previous?.referenceCount ?? 0,\n managed: previous?.managed || !existing,\n })\n .onConflictDoUpdate({\n target: [\n principalHistoryNodeReferences.scopeId,\n principalHistoryNodeReferences.hash,\n ],\n set: {\n leftHash: node.leftHash,\n rightHash: node.rightHash,\n managed: previous?.managed || !existing,\n },\n })\n .run();\n for (const hash of [previous?.leftHash, previous?.rightHash])\n if (hash) await decrementPrincipalHistoryNodeReference(tx, scope, hash);\n}\n\n/** A bounded cascade releases only managed proof nodes with no live incoming edge. */\nexport async function reclaimPrincipalHistoryNodes(\n tx: ClientSQLiteTransactionScope,\n scope: PrincipalHistoryNodeScope,\n): Promise<number> {\n let reclaimed = 0;\n while (reclaimed < 64) {\n const obsolete = await tx\n .select()\n .from(principalHistoryNodeReferences)\n .where(\n and(\n eq(principalHistoryNodeReferences.scopeId, scope.scopeId),\n eq(\n principalHistoryNodeReferences.organizationId,\n scope.organizationId,\n ),\n eq(principalHistoryNodeReferences.managed, true),\n eq(principalHistoryNodeReferences.referenceCount, 0),\n ),\n )\n .orderBy(asc(principalHistoryNodeReferences.hash))\n .limit(64 - reclaimed);\n if (obsolete.length === 0) break;\n for (const node of obsolete) {\n await tx\n .delete(principalHistoryNodes)\n .where(\n and(\n eq(principalHistoryNodes.scopeId, scope.scopeId),\n eq(principalHistoryNodes.organizationId, scope.organizationId),\n eq(principalHistoryNodes.hash, node.hash),\n ),\n )\n .run();\n await tx\n .delete(principalHistoryNodeReferences)\n .where(\n and(\n eq(principalHistoryNodeReferences.scopeId, scope.scopeId),\n eq(principalHistoryNodeReferences.hash, node.hash),\n ),\n )\n .run();\n for (const hash of [node.leftHash, node.rightHash])\n if (hash) await decrementPrincipalHistoryNodeReference(tx, scope, hash);\n reclaimed += 1;\n }\n }\n return reclaimed;\n}\n"]}
@@ -13,6 +13,7 @@ export declare function loadPrincipalHistoryPrefix(execSql: ExecSql, scopeId: st
13
13
  export declare function savePrincipalHistoryPrefix(input: {
14
14
  readonly execSql: ExecSql;
15
15
  readonly prefix: PrincipalHistoryPrefix;
16
+ readonly indexRootHash: string;
16
17
  readonly stillCurrent: () => boolean;
17
18
  readonly rejectedPrefix?: PrincipalHistoryPrefix | null;
18
19
  }): Promise<void>;
@@ -1,8 +1,13 @@
1
1
  import { and, eq } from "drizzle-orm";
2
2
  import { assertProjectionVerificationCurrent } from "../keyingProjectionVerification/types.js";
3
3
  import { principalHistoryEvidenceTables, principalHistoryPrefixes, } from "../sqlite/principalHistoryEvidenceSchema.js";
4
+ import { principalHistoryStageTables } from "../sqlite/principalHistoryStageSchema.js";
4
5
  import { getClientSQLitePersistenceRuntime } from "../sqlite/sqlitePersistenceRuntime.js";
5
6
  import { ensureSqlTables } from "../sqlite/sqlSchema.js";
7
+ import { reclaimPrincipalHistoryNodes } from "./principalHistoryNodeRetention.js";
8
+ import { releasePrincipalHistoryRoot, retainPrincipalHistoryRoot, } from "./principalHistoryRootOwnership.js";
9
+ import { reclaimCompletedPrincipalHistoryStages } from "./principalHistoryStageRetention.js";
10
+ import { archivePrincipalHistoryKeyEnvelopes } from "./principalKeyEnvelopeArchivePersistence.js";
6
11
  export async function loadPrincipalHistoryPrefix(execSql, scopeId) {
7
12
  await ensureSqlTables(execSql, principalHistoryEvidenceTables);
8
13
  const { db } = getClientSQLitePersistenceRuntime(execSql);
@@ -16,8 +21,12 @@ export async function loadPrincipalHistoryPrefix(execSql, scopeId) {
16
21
  /** Keep the newest prefix unless replay replaces the exact rejected candidate. */
17
22
  export async function savePrincipalHistoryPrefix(input) {
18
23
  const prefix = { ...input.prefix };
24
+ const indexRootHash = input.indexRootHash;
19
25
  const rejected = input.rejectedPrefix ? { ...input.rejectedPrefix } : null;
20
- await ensureSqlTables(input.execSql, principalHistoryEvidenceTables);
26
+ await ensureSqlTables(input.execSql, [
27
+ ...principalHistoryEvidenceTables,
28
+ ...principalHistoryStageTables,
29
+ ]);
21
30
  const runtime = getClientSQLitePersistenceRuntime(input.execSql);
22
31
  const result = await runtime.guardedTransaction(async (tx) => {
23
32
  const [previous] = await tx
@@ -30,6 +39,9 @@ export async function savePrincipalHistoryPrefix(input) {
30
39
  !(rejected?.scopeId === previous.scopeId &&
31
40
  rejected.progress === previous.progress))
32
41
  return;
42
+ if (previous)
43
+ await archivePrincipalHistoryKeyEnvelopes(tx, previous);
44
+ await archivePrincipalHistoryKeyEnvelopes(tx, prefix);
33
45
  await tx
34
46
  .insert(principalHistoryPrefixes)
35
47
  .values(prefix)
@@ -38,14 +50,31 @@ export async function savePrincipalHistoryPrefix(input) {
38
50
  set: prefix,
39
51
  })
40
52
  .run();
53
+ await reclaimCompletedPrincipalHistoryStages(tx, prefix);
54
+ await retainPrincipalHistoryRoot(tx, {
55
+ id: `prefix:${prefix.scopeId}`,
56
+ scopeId: prefix.scopeId,
57
+ organizationId: prefix.organizationId,
58
+ rootHash: indexRootHash,
59
+ });
60
+ await reclaimPrincipalHistoryNodes(tx, prefix);
41
61
  }, input.stillCurrent, { behavior: "immediate" });
42
62
  assertProjectionVerificationCurrent(() => result.committed);
43
63
  }
44
64
  /** Invalid hints are disposable, but a losing reader must preserve a newer hint. */
45
65
  export async function discardPrincipalHistoryPrefix(input) {
46
66
  const prefix = { ...input.prefix };
67
+ await ensureSqlTables(input.execSql, principalHistoryEvidenceTables);
47
68
  const runtime = getClientSQLitePersistenceRuntime(input.execSql);
48
69
  const result = await runtime.guardedTransaction(async (tx) => {
70
+ const [current] = await tx
71
+ .select({ progress: principalHistoryPrefixes.progress })
72
+ .from(principalHistoryPrefixes)
73
+ .where(eq(principalHistoryPrefixes.scopeId, prefix.scopeId))
74
+ .limit(1);
75
+ if (current?.progress !== prefix.progress)
76
+ return;
77
+ await releasePrincipalHistoryRoot(tx, `prefix:${prefix.scopeId}`);
49
78
  await tx
50
79
  .delete(principalHistoryPrefixes)
51
80
  .where(and(eq(principalHistoryPrefixes.scopeId, prefix.scopeId), eq(principalHistoryPrefixes.progress, prefix.progress)))
@@ -1 +1 @@
1
- {"version":3,"file":"principalHistoryPrefixPersistence.js","sourceRoot":"","sources":["principalHistoryPrefixPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,mCAAmC,EAAE,MAAM,uCAAuC,CAAC;AAC5F,OAAO,EACL,8BAA8B,EAC9B,wBAAwB,GACzB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,iCAAiC,EAAE,MAAM,oCAAoC,CAAC;AACvF,OAAO,EAAgB,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAKpE,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,OAAgB,EAChB,OAAe;IAEf,MAAM,eAAe,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;IAC/D,MAAM,EAAE,EAAE,EAAE,GAAG,iCAAiC,CAAC,OAAO,CAAC,CAAC;IAC1D,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE;SACtB,MAAM,EAAE;SACR,IAAI,CAAC,wBAAwB,CAAC;SAC9B,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;SACpD,KAAK,CAAC,CAAC,CAAC,CAAC;IACZ,OAAO,MAAM,IAAI,IAAI,CAAC;AACxB,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,KAKhD;IACC,MAAM,MAAM,GAAG,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3E,MAAM,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;IACrE,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,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE;aACxB,MAAM,EAAE;aACR,IAAI,CAAC,wBAAwB,CAAC;aAC9B,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;aAC3D,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,IACE,QAAQ;YACR,QAAQ,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO;YACjC,CAAC,CACC,QAAQ,EAAE,OAAO,KAAK,QAAQ,CAAC,OAAO;gBACtC,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAC,QAAQ,CACxC;YAED,OAAO;QACT,MAAM,EAAE;aACL,MAAM,CAAC,wBAAwB,CAAC;aAChC,MAAM,CAAC,MAAM,CAAC;aACd,kBAAkB,CAAC;YAClB,MAAM,EAAE,wBAAwB,CAAC,OAAO;YACxC,GAAG,EAAE,MAAM;SACZ,CAAC;aACD,GAAG,EAAE,CAAC;IACX,CAAC,EACD,KAAK,CAAC,YAAY,EAClB,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CAAC;IACF,mCAAmC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAC9D,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,KAInD;IACC,MAAM,MAAM,GAAG,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IACnC,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,MAAM,EAAE;aACL,MAAM,CAAC,wBAAwB,CAAC;aAChC,KAAK,CACJ,GAAG,CACD,EAAE,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,EACpD,EAAE,CAAC,wBAAwB,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CACvD,CACF;aACA,GAAG,EAAE,CAAC;IACX,CAAC,EACD,KAAK,CAAC,YAAY,EAClB,EAAE,QAAQ,EAAE,WAAW,EAAE,CAC1B,CAAC;IACF,mCAAmC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { assertProjectionVerificationCurrent } from \"../keyingProjectionVerification/types\";\nimport {\n principalHistoryEvidenceTables,\n principalHistoryPrefixes,\n} from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { getClientSQLitePersistenceRuntime } from \"../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\n\nexport type PrincipalHistoryPrefix =\n typeof principalHistoryPrefixes.$inferSelect;\n\nexport async function loadPrincipalHistoryPrefix(\n execSql: ExecSql,\n scopeId: string,\n) {\n await ensureSqlTables(execSql, principalHistoryEvidenceTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [prefix] = await db\n .select()\n .from(principalHistoryPrefixes)\n .where(eq(principalHistoryPrefixes.scopeId, scopeId))\n .limit(1);\n return prefix ?? null;\n}\n\n/** Keep the newest prefix unless replay replaces the exact rejected candidate. */\nexport async function savePrincipalHistoryPrefix(input: {\n readonly execSql: ExecSql;\n readonly prefix: PrincipalHistoryPrefix;\n readonly stillCurrent: () => boolean;\n readonly rejectedPrefix?: PrincipalHistoryPrefix | null;\n}): Promise<void> {\n const prefix = { ...input.prefix };\n const rejected = input.rejectedPrefix ? { ...input.rejectedPrefix } : null;\n await ensureSqlTables(input.execSql, principalHistoryEvidenceTables);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const result = await runtime.guardedTransaction(\n async (tx) => {\n const [previous] = await tx\n .select()\n .from(principalHistoryPrefixes)\n .where(eq(principalHistoryPrefixes.scopeId, prefix.scopeId))\n .limit(1);\n if (\n previous &&\n previous.version > prefix.version &&\n !(\n rejected?.scopeId === previous.scopeId &&\n rejected.progress === previous.progress\n )\n )\n return;\n await tx\n .insert(principalHistoryPrefixes)\n .values(prefix)\n .onConflictDoUpdate({\n target: principalHistoryPrefixes.scopeId,\n set: prefix,\n })\n .run();\n },\n input.stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => result.committed);\n}\n\n/** Invalid hints are disposable, but a losing reader must preserve a newer hint. */\nexport async function discardPrincipalHistoryPrefix(input: {\n readonly execSql: ExecSql;\n readonly prefix: PrincipalHistoryPrefix;\n readonly stillCurrent: () => boolean;\n}): Promise<void> {\n const prefix = { ...input.prefix };\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const result = await runtime.guardedTransaction(\n async (tx) => {\n await tx\n .delete(principalHistoryPrefixes)\n .where(\n and(\n eq(principalHistoryPrefixes.scopeId, prefix.scopeId),\n eq(principalHistoryPrefixes.progress, prefix.progress),\n ),\n )\n .run();\n },\n input.stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => result.committed);\n}\n"]}
1
+ {"version":3,"file":"principalHistoryPrefixPersistence.js","sourceRoot":"","sources":["principalHistoryPrefixPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { assertProjectionVerificationCurrent } from \"../keyingProjectionVerification/types\";\nimport {\n principalHistoryEvidenceTables,\n principalHistoryPrefixes,\n} from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { principalHistoryStageTables } from \"../sqlite/principalHistoryStageSchema\";\nimport { getClientSQLitePersistenceRuntime } from \"../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../sqlite/sqlSchema\";\nimport { reclaimPrincipalHistoryNodes } from \"./principalHistoryNodeRetention\";\nimport {\n releasePrincipalHistoryRoot,\n retainPrincipalHistoryRoot,\n} from \"./principalHistoryRootOwnership\";\nimport { reclaimCompletedPrincipalHistoryStages } from \"./principalHistoryStageRetention\";\nimport { archivePrincipalHistoryKeyEnvelopes } from \"./principalKeyEnvelopeArchivePersistence\";\n\nexport type PrincipalHistoryPrefix =\n typeof principalHistoryPrefixes.$inferSelect;\n\nexport async function loadPrincipalHistoryPrefix(\n execSql: ExecSql,\n scopeId: string,\n) {\n await ensureSqlTables(execSql, principalHistoryEvidenceTables);\n const { db } = getClientSQLitePersistenceRuntime(execSql);\n const [prefix] = await db\n .select()\n .from(principalHistoryPrefixes)\n .where(eq(principalHistoryPrefixes.scopeId, scopeId))\n .limit(1);\n return prefix ?? null;\n}\n\n/** Keep the newest prefix unless replay replaces the exact rejected candidate. */\nexport async function savePrincipalHistoryPrefix(input: {\n readonly execSql: ExecSql;\n readonly prefix: PrincipalHistoryPrefix;\n readonly indexRootHash: string;\n readonly stillCurrent: () => boolean;\n readonly rejectedPrefix?: PrincipalHistoryPrefix | null;\n}): Promise<void> {\n const prefix = { ...input.prefix };\n const indexRootHash = input.indexRootHash;\n const rejected = input.rejectedPrefix ? { ...input.rejectedPrefix } : null;\n await ensureSqlTables(input.execSql, [\n ...principalHistoryEvidenceTables,\n ...principalHistoryStageTables,\n ]);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const result = await runtime.guardedTransaction(\n async (tx) => {\n const [previous] = await tx\n .select()\n .from(principalHistoryPrefixes)\n .where(eq(principalHistoryPrefixes.scopeId, prefix.scopeId))\n .limit(1);\n if (\n previous &&\n previous.version > prefix.version &&\n !(\n rejected?.scopeId === previous.scopeId &&\n rejected.progress === previous.progress\n )\n )\n return;\n if (previous) await archivePrincipalHistoryKeyEnvelopes(tx, previous);\n await archivePrincipalHistoryKeyEnvelopes(tx, prefix);\n await tx\n .insert(principalHistoryPrefixes)\n .values(prefix)\n .onConflictDoUpdate({\n target: principalHistoryPrefixes.scopeId,\n set: prefix,\n })\n .run();\n await reclaimCompletedPrincipalHistoryStages(tx, prefix);\n await retainPrincipalHistoryRoot(tx, {\n id: `prefix:${prefix.scopeId}`,\n scopeId: prefix.scopeId,\n organizationId: prefix.organizationId,\n rootHash: indexRootHash,\n });\n await reclaimPrincipalHistoryNodes(tx, prefix);\n },\n input.stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => result.committed);\n}\n\n/** Invalid hints are disposable, but a losing reader must preserve a newer hint. */\nexport async function discardPrincipalHistoryPrefix(input: {\n readonly execSql: ExecSql;\n readonly prefix: PrincipalHistoryPrefix;\n readonly stillCurrent: () => boolean;\n}): Promise<void> {\n const prefix = { ...input.prefix };\n await ensureSqlTables(input.execSql, principalHistoryEvidenceTables);\n const runtime = getClientSQLitePersistenceRuntime(input.execSql);\n const result = await runtime.guardedTransaction(\n async (tx) => {\n const [current] = await tx\n .select({ progress: principalHistoryPrefixes.progress })\n .from(principalHistoryPrefixes)\n .where(eq(principalHistoryPrefixes.scopeId, prefix.scopeId))\n .limit(1);\n if (current?.progress !== prefix.progress) return;\n await releasePrincipalHistoryRoot(tx, `prefix:${prefix.scopeId}`);\n await tx\n .delete(principalHistoryPrefixes)\n .where(\n and(\n eq(principalHistoryPrefixes.scopeId, prefix.scopeId),\n eq(principalHistoryPrefixes.progress, prefix.progress),\n ),\n )\n .run();\n },\n input.stillCurrent,\n { behavior: \"immediate\" },\n );\n assertProjectionVerificationCurrent(() => result.committed);\n}\n"]}
@@ -0,0 +1,71 @@
1
+ import { and, eq, sql } from "drizzle-orm";
2
+ import { principalHistoryNodeReferences, principalHistoryRootOwners, } from "../sqlite/principalHistoryNodeRetentionSchema.js";
3
+ export async function incrementPrincipalHistoryNodeReference(tx, scope, hash) {
4
+ await tx
5
+ .insert(principalHistoryNodeReferences)
6
+ .values({
7
+ ...scope,
8
+ hash,
9
+ referenceCount: 1,
10
+ managed: false,
11
+ leftHash: null,
12
+ rightHash: null,
13
+ })
14
+ .onConflictDoUpdate({
15
+ target: [
16
+ principalHistoryNodeReferences.scopeId,
17
+ principalHistoryNodeReferences.hash,
18
+ ],
19
+ set: {
20
+ referenceCount: sql `${principalHistoryNodeReferences.referenceCount} + 1`,
21
+ },
22
+ })
23
+ .run();
24
+ }
25
+ export async function decrementPrincipalHistoryNodeReference(tx, scope, hash) {
26
+ await tx
27
+ .update(principalHistoryNodeReferences)
28
+ .set({
29
+ referenceCount: sql `max(${principalHistoryNodeReferences.referenceCount} - 1, 0)`,
30
+ })
31
+ .where(and(eq(principalHistoryNodeReferences.scopeId, scope.scopeId), eq(principalHistoryNodeReferences.organizationId, scope.organizationId), eq(principalHistoryNodeReferences.hash, hash)))
32
+ .run();
33
+ }
34
+ /** Caller commits ownership together with the exact saved progress it describes. */
35
+ export async function retainPrincipalHistoryRoot(tx, input) {
36
+ const [previous] = await tx
37
+ .select()
38
+ .from(principalHistoryRootOwners)
39
+ .where(eq(principalHistoryRootOwners.id, input.id))
40
+ .limit(1);
41
+ if (previous?.scopeId === input.scopeId &&
42
+ previous.organizationId === input.organizationId &&
43
+ previous.rootHash === input.rootHash)
44
+ return;
45
+ await incrementPrincipalHistoryNodeReference(tx, input, input.rootHash);
46
+ await tx
47
+ .insert(principalHistoryRootOwners)
48
+ .values(input)
49
+ .onConflictDoUpdate({
50
+ target: principalHistoryRootOwners.id,
51
+ set: input,
52
+ })
53
+ .run();
54
+ if (previous)
55
+ await decrementPrincipalHistoryNodeReference(tx, previous, previous.rootHash);
56
+ }
57
+ export async function releasePrincipalHistoryRoot(tx, id) {
58
+ const [previous] = await tx
59
+ .select()
60
+ .from(principalHistoryRootOwners)
61
+ .where(eq(principalHistoryRootOwners.id, id))
62
+ .limit(1);
63
+ if (!previous)
64
+ return;
65
+ await tx
66
+ .delete(principalHistoryRootOwners)
67
+ .where(eq(principalHistoryRootOwners.id, id))
68
+ .run();
69
+ await decrementPrincipalHistoryNodeReference(tx, previous, previous.rootHash);
70
+ }
71
+ //# sourceMappingURL=principalHistoryRootOwnership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principalHistoryRootOwnership.js","sourceRoot":"","sources":["principalHistoryRootOwnership.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, eq, sql } from \"drizzle-orm\";\nimport {\n principalHistoryNodeReferences,\n principalHistoryRootOwners,\n} from \"../sqlite/principalHistoryNodeRetentionSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../sqlite/sqlitePersistenceRuntime\";\n\nexport interface PrincipalHistoryNodeScope {\n /** Evidence scope hashes include organizationId; one scope belongs to one organization. */\n readonly scopeId: string;\n readonly organizationId: string;\n}\n\nexport async function incrementPrincipalHistoryNodeReference(\n tx: ClientSQLiteTransactionScope,\n scope: PrincipalHistoryNodeScope,\n hash: string,\n) {\n await tx\n .insert(principalHistoryNodeReferences)\n .values({\n ...scope,\n hash,\n referenceCount: 1,\n managed: false,\n leftHash: null,\n rightHash: null,\n })\n .onConflictDoUpdate({\n target: [\n principalHistoryNodeReferences.scopeId,\n principalHistoryNodeReferences.hash,\n ],\n set: {\n referenceCount: sql`${principalHistoryNodeReferences.referenceCount} + 1`,\n },\n })\n .run();\n}\n\nexport async function decrementPrincipalHistoryNodeReference(\n tx: ClientSQLiteTransactionScope,\n scope: PrincipalHistoryNodeScope,\n hash: string,\n) {\n await tx\n .update(principalHistoryNodeReferences)\n .set({\n referenceCount: sql`max(${principalHistoryNodeReferences.referenceCount} - 1, 0)`,\n })\n .where(\n and(\n eq(principalHistoryNodeReferences.scopeId, scope.scopeId),\n eq(principalHistoryNodeReferences.organizationId, scope.organizationId),\n eq(principalHistoryNodeReferences.hash, hash),\n ),\n )\n .run();\n}\n\n/** Caller commits ownership together with the exact saved progress it describes. */\nexport async function retainPrincipalHistoryRoot(\n tx: ClientSQLiteTransactionScope,\n input: PrincipalHistoryNodeScope & {\n readonly id: string;\n readonly rootHash: string;\n },\n) {\n const [previous] = await tx\n .select()\n .from(principalHistoryRootOwners)\n .where(eq(principalHistoryRootOwners.id, input.id))\n .limit(1);\n if (\n previous?.scopeId === input.scopeId &&\n previous.organizationId === input.organizationId &&\n previous.rootHash === input.rootHash\n )\n return;\n await incrementPrincipalHistoryNodeReference(tx, input, input.rootHash);\n await tx\n .insert(principalHistoryRootOwners)\n .values(input)\n .onConflictDoUpdate({\n target: principalHistoryRootOwners.id,\n set: input,\n })\n .run();\n if (previous)\n await decrementPrincipalHistoryNodeReference(\n tx,\n previous,\n previous.rootHash,\n );\n}\n\nexport async function releasePrincipalHistoryRoot(\n tx: ClientSQLiteTransactionScope,\n id: string,\n) {\n const [previous] = await tx\n .select()\n .from(principalHistoryRootOwners)\n .where(eq(principalHistoryRootOwners.id, id))\n .limit(1);\n if (!previous) return;\n await tx\n .delete(principalHistoryRootOwners)\n .where(eq(principalHistoryRootOwners.id, id))\n .run();\n await decrementPrincipalHistoryNodeReference(tx, previous, previous.rootHash);\n}\n"]}
@@ -1,10 +1,17 @@
1
1
  import { and, eq } from "drizzle-orm";
2
2
  import { assertProjectionVerificationCurrent } from "../keyingProjectionVerification/types.js";
3
3
  import { principalHistoryEvidenceTables } from "../sqlite/principalHistoryEvidenceSchema.js";
4
+ import { principalHistoryNodeRetentionTables } from "../sqlite/principalHistoryNodeRetentionSchema.js";
5
+ import { principalHistoryStageScopes } from "../sqlite/principalHistoryRetentionSchema.js";
4
6
  import { principalHistoryStages, principalHistoryStageTables, } from "../sqlite/principalHistoryStageSchema.js";
5
7
  import { getClientSQLitePersistenceRuntime } from "../sqlite/sqlitePersistenceRuntime.js";
6
8
  import { ensureSqlTables } from "../sqlite/sqlSchema.js";
7
9
  import { writePrincipalHistoryEvidencePage, } from "./principalHistoryEvidencePersistence.js";
10
+ import { reclaimIncompletePrincipalHistoryStages } from "./principalHistoryIncompleteRetention.js";
11
+ import { reclaimPrincipalHistoryNodes } from "./principalHistoryNodeRetention.js";
12
+ import { releasePrincipalHistoryRoot, retainPrincipalHistoryRoot, } from "./principalHistoryRootOwnership.js";
13
+ import { recordPrincipalHistoryStageScope } from "./principalHistoryStageRetention.js";
14
+ import { reclaimUnpublishedPrincipalHistoryStages } from "./principalHistoryUnpublishedRetention.js";
8
15
  class PrincipalHistoryStageChangedError extends Error {
9
16
  code = "principal_history_stage_changed";
10
17
  constructor() {
@@ -45,16 +52,54 @@ export async function savePrincipalHistoryStage(input) {
45
52
  .values(stage)
46
53
  .onConflictDoUpdate({ target: principalHistoryStages.id, set: stage })
47
54
  .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
+ await retainPrincipalHistoryRoot(tx, {
63
+ id: `stage:${stage.id}`,
64
+ scopeId: evidence.scopeId,
65
+ organizationId: stage.organizationId,
66
+ rootHash: evidence.indexRootHash,
67
+ });
68
+ await reclaimIncompletePrincipalHistoryStages(tx, {
69
+ ...stage,
70
+ scopeId: evidence.scopeId,
71
+ });
72
+ await reclaimUnpublishedPrincipalHistoryStages(tx, {
73
+ ...stage,
74
+ scopeId: evidence.scopeId,
75
+ });
76
+ await reclaimPrincipalHistoryNodes(tx, evidence);
48
77
  }, input.stillCurrent, { behavior: "immediate" });
49
78
  assertProjectionVerificationCurrent(() => saved.committed);
50
79
  }
51
80
  export async function discardPrincipalHistoryStage(execSql, stage, stillCurrent) {
81
+ await ensureSqlTables(execSql, [
82
+ ...principalHistoryStageTables,
83
+ ...principalHistoryNodeRetentionTables,
84
+ ]);
52
85
  const runtime = getClientSQLitePersistenceRuntime(execSql);
53
86
  const discarded = await runtime.guardedTransaction(async (db) => {
87
+ const [current] = await db
88
+ .select({ progress: principalHistoryStages.progress })
89
+ .from(principalHistoryStages)
90
+ .where(eq(principalHistoryStages.id, stage.id))
91
+ .limit(1);
92
+ if (current?.progress !== stage.progress)
93
+ return;
94
+ await releasePrincipalHistoryRoot(db, `stage:${stage.id}`);
54
95
  await db
55
96
  .delete(principalHistoryStages)
56
97
  .where(and(eq(principalHistoryStages.id, stage.id), eq(principalHistoryStages.progress, stage.progress)))
57
98
  .run();
99
+ await db
100
+ .delete(principalHistoryStageScopes)
101
+ .where(eq(principalHistoryStageScopes.id, stage.id))
102
+ .run();
58
103
  }, stillCurrent, { behavior: "immediate" });
59
104
  assertProjectionVerificationCurrent(() => discarded.committed);
60
105
  }
@@ -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 {\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\";\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 },\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 const runtime = getClientSQLitePersistenceRuntime(execSql);\n const discarded = await runtime.guardedTransaction(\n async (db) => {\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
+ {"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"]}
@@ -0,0 +1,44 @@
1
+ import { and, asc, desc, eq, lt } from "drizzle-orm";
2
+ import { principalHistoryStageScopes } from "../sqlite/principalHistoryRetentionSchema.js";
3
+ import { principalHistoryStages } from "../sqlite/principalHistoryStageSchema.js";
4
+ import { releasePrincipalHistoryRoot } from "./principalHistoryRootOwnership.js";
5
+ import { archivePrincipalHistoryKeyEnvelopes } from "./principalKeyEnvelopeArchivePersistence.js";
6
+ /** Each publication reclaims at most this many obsolete completed heads. */
7
+ export const PRINCIPAL_HISTORY_STAGE_RECLAIM_LIMIT = 16;
8
+ export async function recordPrincipalHistoryStageScope(tx, input) {
9
+ const row = { ...input, touchedAt: Date.now() };
10
+ await tx
11
+ .insert(principalHistoryStageScopes)
12
+ .values(row)
13
+ .onConflictDoUpdate({ target: principalHistoryStageScopes.id, set: row })
14
+ .run();
15
+ }
16
+ /** The prefix and its key candidates must be published in this transaction. */
17
+ export async function reclaimCompletedPrincipalHistoryStages(tx, prefix) {
18
+ 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))
24
+ // Keep the current completed head and its newest completed predecessor.
25
+ // This also preserves the prefix-only predecessor restored after an ack.
26
+ .offset(2)
27
+ .limit(PRINCIPAL_HISTORY_STAGE_RECLAIM_LIMIT);
28
+ for (const { stage } of obsolete) {
29
+ await releasePrincipalHistoryRoot(tx, `stage:${stage.id}`);
30
+ await archivePrincipalHistoryKeyEnvelopes(tx, {
31
+ ...stage,
32
+ version: stage.afterVersion + 1,
33
+ });
34
+ await tx
35
+ .delete(principalHistoryStages)
36
+ .where(eq(principalHistoryStages.id, stage.id))
37
+ .run();
38
+ await tx
39
+ .delete(principalHistoryStageScopes)
40
+ .where(eq(principalHistoryStageScopes.id, stage.id))
41
+ .run();
42
+ }
43
+ }
44
+ //# sourceMappingURL=principalHistoryStageRetention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principalHistoryStageRetention.js","sourceRoot":"","sources":["principalHistoryStageRetention.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, desc, eq, lt } from \"drizzle-orm\";\nimport { principalHistoryStageScopes } from \"../sqlite/principalHistoryRetentionSchema\";\nimport { principalHistoryStages } from \"../sqlite/principalHistoryStageSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../sqlite/sqlitePersistenceRuntime\";\nimport { releasePrincipalHistoryRoot } from \"./principalHistoryRootOwnership\";\nimport { archivePrincipalHistoryKeyEnvelopes } from \"./principalKeyEnvelopeArchivePersistence\";\n\n/** Each publication reclaims at most this many obsolete completed heads. */\nexport const PRINCIPAL_HISTORY_STAGE_RECLAIM_LIMIT = 16;\n\nexport async function recordPrincipalHistoryStageScope(\n tx: ClientSQLiteTransactionScope,\n input: {\n readonly id: string;\n readonly organizationId: string;\n readonly scopeId: string;\n readonly afterVersion: number;\n readonly complete: boolean;\n },\n) {\n const row = { ...input, touchedAt: Date.now() };\n await tx\n .insert(principalHistoryStageScopes)\n .values(row)\n .onConflictDoUpdate({ target: principalHistoryStageScopes.id, set: row })\n .run();\n}\n\n/** The prefix and its key candidates must be published in this transaction. */\nexport async function reclaimCompletedPrincipalHistoryStages(\n tx: ClientSQLiteTransactionScope,\n prefix: {\n readonly scopeId: string;\n readonly organizationId: string;\n readonly version: number;\n },\n) {\n const obsolete = await tx\n .select({ stage: principalHistoryStages })\n .from(principalHistoryStageScopes)\n .innerJoin(\n principalHistoryStages,\n eq(principalHistoryStageScopes.id, principalHistoryStages.id),\n )\n .where(\n and(\n eq(principalHistoryStageScopes.scopeId, prefix.scopeId),\n eq(principalHistoryStageScopes.organizationId, prefix.organizationId),\n eq(principalHistoryStageScopes.complete, true),\n lt(principalHistoryStageScopes.afterVersion, prefix.version),\n eq(principalHistoryStages.organizationId, prefix.organizationId),\n eq(principalHistoryStages.complete, true),\n lt(principalHistoryStages.afterVersion, prefix.version),\n ),\n )\n .orderBy(\n desc(principalHistoryStageScopes.afterVersion),\n desc(principalHistoryStageScopes.touchedAt),\n asc(principalHistoryStageScopes.id),\n )\n // Keep the current completed head and its newest completed predecessor.\n // This also preserves the prefix-only predecessor restored after an ack.\n .offset(2)\n .limit(PRINCIPAL_HISTORY_STAGE_RECLAIM_LIMIT);\n for (const { stage } of obsolete) {\n await releasePrincipalHistoryRoot(tx, `stage:${stage.id}`);\n await archivePrincipalHistoryKeyEnvelopes(tx, {\n ...stage,\n version: stage.afterVersion + 1,\n });\n await tx\n .delete(principalHistoryStages)\n .where(eq(principalHistoryStages.id, stage.id))\n .run();\n await tx\n .delete(principalHistoryStageScopes)\n .where(eq(principalHistoryStageScopes.id, stage.id))\n .run();\n }\n}\n"]}
@@ -0,0 +1,71 @@
1
+ import { and, asc, desc, eq, inArray, lt, notInArray } from "drizzle-orm";
2
+ import { principalHistoryPrefixes } from "../sqlite/principalHistoryEvidenceSchema.js";
3
+ import { principalHistoryStageScopes } from "../sqlite/principalHistoryRetentionSchema.js";
4
+ import { principalHistoryStages } from "../sqlite/principalHistoryStageSchema.js";
5
+ import { releasePrincipalHistoryRoot } from "./principalHistoryRootOwnership.js";
6
+ import { PRINCIPAL_HISTORY_STAGE_RECLAIM_LIMIT } from "./principalHistoryStageRetention.js";
7
+ import { archivePrincipalHistoryKeyEnvelopes } from "./principalKeyEnvelopeArchivePersistence.js";
8
+ const COMPLETED_STAGE_LIMIT = 8;
9
+ function matchingCompletedStages(current) {
10
+ return and(eq(principalHistoryStageScopes.scopeId, current.scopeId), eq(principalHistoryStageScopes.organizationId, current.organizationId), eq(principalHistoryStageScopes.complete, true), eq(principalHistoryStages.organizationId, current.organizationId), eq(principalHistoryStages.complete, true), eq(principalHistoryStages.afterVersion, principalHistoryStageScopes.afterVersion));
11
+ }
12
+ async function protectedPublishedStages(tx, current) {
13
+ const [prefix] = await tx
14
+ .select({ version: principalHistoryPrefixes.version })
15
+ .from(principalHistoryPrefixes)
16
+ .where(and(eq(principalHistoryPrefixes.scopeId, current.scopeId), eq(principalHistoryPrefixes.organizationId, current.organizationId)))
17
+ .limit(1);
18
+ if (!prefix)
19
+ return [];
20
+ return tx
21
+ .select({ id: principalHistoryStages.id })
22
+ .from(principalHistoryStageScopes)
23
+ .innerJoin(principalHistoryStages, eq(principalHistoryStageScopes.id, principalHistoryStages.id))
24
+ .where(and(matchingCompletedStages(current), lt(principalHistoryStageScopes.afterVersion, prefix.version)))
25
+ .orderBy(desc(principalHistoryStageScopes.afterVersion), desc(principalHistoryStageScopes.touchedAt), asc(principalHistoryStageScopes.id))
26
+ .limit(2);
27
+ }
28
+ /** Bound completed attempts even when current-artifact verification never publishes. */
29
+ export async function reclaimUnpublishedPrincipalHistoryStages(tx, current) {
30
+ if (!current.complete)
31
+ return;
32
+ const published = await protectedPublishedStages(tx, current);
33
+ const protectedIds = [
34
+ ...new Set([current.id, ...published.map(({ id }) => id)]),
35
+ ];
36
+ const obsolete = await tx
37
+ .select({
38
+ stage: {
39
+ id: principalHistoryStages.id,
40
+ organizationId: principalHistoryStages.organizationId,
41
+ currentJson: principalHistoryStages.currentJson,
42
+ afterVersion: principalHistoryStages.afterVersion,
43
+ },
44
+ })
45
+ .from(principalHistoryStageScopes)
46
+ .innerJoin(principalHistoryStages, eq(principalHistoryStageScopes.id, principalHistoryStages.id))
47
+ .where(and(matchingCompletedStages(current), notInArray(principalHistoryStages.id, protectedIds)))
48
+ // The existing "incomplete" index also covers completed scoped recency.
49
+ .orderBy(desc(principalHistoryStageScopes.touchedAt), asc(principalHistoryStageScopes.id))
50
+ .offset(COMPLETED_STAGE_LIMIT - protectedIds.length)
51
+ .limit(PRINCIPAL_HISTORY_STAGE_RECLAIM_LIMIT);
52
+ if (obsolete.length === 0)
53
+ return;
54
+ for (const { stage } of obsolete)
55
+ await archivePrincipalHistoryKeyEnvelopes(tx, {
56
+ ...stage,
57
+ version: stage.afterVersion + 1,
58
+ });
59
+ const ids = obsolete.map(({ stage }) => stage.id);
60
+ for (const id of ids)
61
+ await releasePrincipalHistoryRoot(tx, `stage:${id}`);
62
+ await tx
63
+ .delete(principalHistoryStages)
64
+ .where(inArray(principalHistoryStages.id, ids))
65
+ .run();
66
+ await tx
67
+ .delete(principalHistoryStageScopes)
68
+ .where(inArray(principalHistoryStageScopes.id, ids))
69
+ .run();
70
+ }
71
+ //# sourceMappingURL=principalHistoryUnpublishedRetention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principalHistoryUnpublishedRetention.js","sourceRoot":"","sources":["principalHistoryUnpublishedRetention.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, desc, eq, inArray, lt, notInArray } from \"drizzle-orm\";\nimport { principalHistoryPrefixes } from \"../sqlite/principalHistoryEvidenceSchema\";\nimport { principalHistoryStageScopes } from \"../sqlite/principalHistoryRetentionSchema\";\nimport { principalHistoryStages } from \"../sqlite/principalHistoryStageSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../sqlite/sqlitePersistenceRuntime\";\nimport { releasePrincipalHistoryRoot } from \"./principalHistoryRootOwnership\";\nimport { PRINCIPAL_HISTORY_STAGE_RECLAIM_LIMIT } from \"./principalHistoryStageRetention\";\nimport { archivePrincipalHistoryKeyEnvelopes } from \"./principalKeyEnvelopeArchivePersistence\";\n\nconst COMPLETED_STAGE_LIMIT = 8;\n\ninterface CompletedWriter {\n readonly id: string;\n readonly scopeId: string;\n readonly organizationId: string;\n readonly complete: boolean;\n}\n\nfunction matchingCompletedStages(current: CompletedWriter) {\n return and(\n eq(principalHistoryStageScopes.scopeId, current.scopeId),\n eq(principalHistoryStageScopes.organizationId, current.organizationId),\n eq(principalHistoryStageScopes.complete, true),\n eq(principalHistoryStages.organizationId, current.organizationId),\n eq(principalHistoryStages.complete, true),\n eq(\n principalHistoryStages.afterVersion,\n principalHistoryStageScopes.afterVersion,\n ),\n );\n}\n\nasync function protectedPublishedStages(\n tx: ClientSQLiteTransactionScope,\n current: CompletedWriter,\n) {\n const [prefix] = await tx\n .select({ version: principalHistoryPrefixes.version })\n .from(principalHistoryPrefixes)\n .where(\n and(\n eq(principalHistoryPrefixes.scopeId, current.scopeId),\n eq(principalHistoryPrefixes.organizationId, current.organizationId),\n ),\n )\n .limit(1);\n if (!prefix) return [];\n return 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 matchingCompletedStages(current),\n lt(principalHistoryStageScopes.afterVersion, prefix.version),\n ),\n )\n .orderBy(\n desc(principalHistoryStageScopes.afterVersion),\n desc(principalHistoryStageScopes.touchedAt),\n asc(principalHistoryStageScopes.id),\n )\n .limit(2);\n}\n\n/** Bound completed attempts even when current-artifact verification never publishes. */\nexport async function reclaimUnpublishedPrincipalHistoryStages(\n tx: ClientSQLiteTransactionScope,\n current: CompletedWriter,\n) {\n if (!current.complete) return;\n const published = await protectedPublishedStages(tx, current);\n const protectedIds = [\n ...new Set([current.id, ...published.map(({ id }) => id)]),\n ];\n const obsolete = await tx\n .select({\n stage: {\n id: principalHistoryStages.id,\n organizationId: principalHistoryStages.organizationId,\n currentJson: principalHistoryStages.currentJson,\n afterVersion: principalHistoryStages.afterVersion,\n },\n })\n .from(principalHistoryStageScopes)\n .innerJoin(\n principalHistoryStages,\n eq(principalHistoryStageScopes.id, principalHistoryStages.id),\n )\n .where(\n and(\n matchingCompletedStages(current),\n notInArray(principalHistoryStages.id, protectedIds),\n ),\n )\n // The existing \"incomplete\" index also covers completed scoped recency.\n .orderBy(\n desc(principalHistoryStageScopes.touchedAt),\n asc(principalHistoryStageScopes.id),\n )\n .offset(COMPLETED_STAGE_LIMIT - protectedIds.length)\n .limit(PRINCIPAL_HISTORY_STAGE_RECLAIM_LIMIT);\n if (obsolete.length === 0) return;\n for (const { stage } of obsolete)\n await archivePrincipalHistoryKeyEnvelopes(tx, {\n ...stage,\n version: stage.afterVersion + 1,\n });\n const ids = obsolete.map(({ stage }) => stage.id);\n for (const id of ids) await releasePrincipalHistoryRoot(tx, `stage:${id}`);\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"]}
@@ -0,0 +1,63 @@
1
+ import { computePrincipalMemberEnvelopesRoot } from "../../internal/crypto/index.js";
2
+ import { isPrincipalPolicyPageResponse } from "../../internal/validators/response/index.js";
3
+ import { lte } from "drizzle-orm";
4
+ import { principalKeyEnvelopeArchive } from "../sqlite/principalHistoryRetentionSchema.js";
5
+ /**
6
+ * Preserve untrusted encrypted candidates, never history or authorization.
7
+ * The caller replaces progress in this same transaction. Public-history hints
8
+ * carry no private artifacts and deliberately contribute no candidates.
9
+ */
10
+ export async function archivePrincipalHistoryKeyEnvelopes(tx, input) {
11
+ let current;
12
+ try {
13
+ current = JSON.parse(input.currentJson);
14
+ }
15
+ catch {
16
+ return;
17
+ }
18
+ if (!current || typeof current !== "object" || Array.isArray(current))
19
+ return;
20
+ const page = {
21
+ ...current,
22
+ previousStates: [],
23
+ historyPage: { afterVersion: input.version - 1, nextAfterVersion: null },
24
+ };
25
+ if (!isPrincipalPolicyPageResponse(page) ||
26
+ page.currentState.version !== input.version ||
27
+ page.currentState.stateHash !== page.currentMemberEnvelopes.stateHash ||
28
+ page.currentState.principalId !== page.currentMemberEnvelopes.principalId ||
29
+ page.currentState.principalType !==
30
+ page.currentMemberEnvelopes.principalType ||
31
+ page.currentState.keyEpoch !== page.currentMemberEnvelopes.epoch)
32
+ return;
33
+ try {
34
+ if ((await computePrincipalMemberEnvelopesRoot(page.currentMemberEnvelopes.envelopes)) !== page.currentState.memberEnvelopesRoot)
35
+ return;
36
+ }
37
+ catch {
38
+ return;
39
+ }
40
+ const row = {
41
+ organizationId: input.organizationId,
42
+ principalId: page.currentState.principalId,
43
+ principalType: page.currentState.principalType,
44
+ keyFingerprint: page.currentState.keyFingerprint,
45
+ version: input.version,
46
+ envelopesJson: JSON.stringify(page.currentMemberEnvelopes),
47
+ };
48
+ await tx
49
+ .insert(principalKeyEnvelopeArchive)
50
+ .values(row)
51
+ .onConflictDoUpdate({
52
+ target: [
53
+ principalKeyEnvelopeArchive.organizationId,
54
+ principalKeyEnvelopeArchive.principalType,
55
+ principalKeyEnvelopeArchive.principalId,
56
+ principalKeyEnvelopeArchive.keyFingerprint,
57
+ ],
58
+ set: row,
59
+ setWhere: lte(principalKeyEnvelopeArchive.version, row.version),
60
+ })
61
+ .run();
62
+ }
63
+ //# sourceMappingURL=principalKeyEnvelopeArchivePersistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principalKeyEnvelopeArchivePersistence.js","sourceRoot":"","sources":["principalKeyEnvelopeArchivePersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAC/E,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AAGxF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,EAAgC,EAChC,KAIC;IAED,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO;IAC9E,MAAM,IAAI,GAAG;QACX,GAAG,OAAO;QACV,cAAc,EAAE,EAAE;QAClB,WAAW,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC,OAAO,GAAG,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE;KACzE,CAAC;IACF,IACE,CAAC,6BAA6B,CAAC,IAAI,CAAC;QACpC,IAAI,CAAC,YAAY,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO;QAC3C,IAAI,CAAC,YAAY,CAAC,SAAS,KAAK,IAAI,CAAC,sBAAsB,CAAC,SAAS;QACrE,IAAI,CAAC,YAAY,CAAC,WAAW,KAAK,IAAI,CAAC,sBAAsB,CAAC,WAAW;QACzE,IAAI,CAAC,YAAY,CAAC,aAAa;YAC7B,IAAI,CAAC,sBAAsB,CAAC,aAAa;QAC3C,IAAI,CAAC,YAAY,CAAC,QAAQ,KAAK,IAAI,CAAC,sBAAsB,CAAC,KAAK;QAEhE,OAAO;IACT,IAAI,CAAC;QACH,IACE,CAAC,MAAM,mCAAmC,CACxC,IAAI,CAAC,sBAAsB,CAAC,SAAS,CACtC,CAAC,KAAK,IAAI,CAAC,YAAY,CAAC,mBAAmB;YAE5C,OAAO;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG;QACV,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,WAAW;QAC1C,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC,aAAa;QAC9C,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc;QAChD,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,sBAAsB,CAAC;KAC3D,CAAC;IACF,MAAM,EAAE;SACL,MAAM,CAAC,2BAA2B,CAAC;SACnC,MAAM,CAAC,GAAG,CAAC;SACX,kBAAkB,CAAC;QAClB,MAAM,EAAE;YACN,2BAA2B,CAAC,cAAc;YAC1C,2BAA2B,CAAC,aAAa;YACzC,2BAA2B,CAAC,WAAW;YACvC,2BAA2B,CAAC,cAAc;SAC3C;QACD,GAAG,EAAE,GAAG;QACR,QAAQ,EAAE,GAAG,CAAC,2BAA2B,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC;KAChE,CAAC;SACD,GAAG,EAAE,CAAC;AACX,CAAC","sourcesContent":["import { computePrincipalMemberEnvelopesRoot } from \"@tearleads/crypto\";\nimport { isPrincipalPolicyPageResponse } from \"@tearleads/validators/response\";\nimport { lte } from \"drizzle-orm\";\nimport { principalKeyEnvelopeArchive } from \"../sqlite/principalHistoryRetentionSchema\";\nimport type { ClientSQLiteTransactionScope } from \"../sqlite/sqlitePersistenceRuntime\";\n\n/**\n * Preserve untrusted encrypted candidates, never history or authorization.\n * The caller replaces progress in this same transaction. Public-history hints\n * carry no private artifacts and deliberately contribute no candidates.\n */\nexport async function archivePrincipalHistoryKeyEnvelopes(\n tx: ClientSQLiteTransactionScope,\n input: {\n readonly organizationId: string;\n readonly currentJson: string;\n readonly version: number;\n },\n) {\n let current: unknown;\n try {\n current = JSON.parse(input.currentJson);\n } catch {\n return;\n }\n if (!current || typeof current !== \"object\" || Array.isArray(current)) return;\n const page = {\n ...current,\n previousStates: [],\n historyPage: { afterVersion: input.version - 1, nextAfterVersion: null },\n };\n if (\n !isPrincipalPolicyPageResponse(page) ||\n page.currentState.version !== input.version ||\n page.currentState.stateHash !== page.currentMemberEnvelopes.stateHash ||\n page.currentState.principalId !== page.currentMemberEnvelopes.principalId ||\n page.currentState.principalType !==\n page.currentMemberEnvelopes.principalType ||\n page.currentState.keyEpoch !== page.currentMemberEnvelopes.epoch\n )\n return;\n try {\n if (\n (await computePrincipalMemberEnvelopesRoot(\n page.currentMemberEnvelopes.envelopes,\n )) !== page.currentState.memberEnvelopesRoot\n )\n return;\n } catch {\n return;\n }\n const row = {\n organizationId: input.organizationId,\n principalId: page.currentState.principalId,\n principalType: page.currentState.principalType,\n keyFingerprint: page.currentState.keyFingerprint,\n version: input.version,\n envelopesJson: JSON.stringify(page.currentMemberEnvelopes),\n };\n await tx\n .insert(principalKeyEnvelopeArchive)\n .values(row)\n .onConflictDoUpdate({\n target: [\n principalKeyEnvelopeArchive.organizationId,\n principalKeyEnvelopeArchive.principalType,\n principalKeyEnvelopeArchive.principalId,\n principalKeyEnvelopeArchive.keyFingerprint,\n ],\n set: row,\n setWhere: lte(principalKeyEnvelopeArchive.version, row.version),\n })\n .run();\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { isCurrentPrincipalMemberEnvelopesResponse, } from "../../internal/validators/response/index.js";
2
2
  import { and, desc, eq, sql } from "drizzle-orm";
3
3
  import { principalHistoryEvidenceTables, principalHistoryPrefixes, } from "../sqlite/principalHistoryEvidenceSchema.js";
4
+ import { principalKeyEnvelopeArchive } from "../sqlite/principalHistoryRetentionSchema.js";
4
5
  import { principalHistoryStages } from "../sqlite/principalHistoryStageSchema.js";
5
6
  import { principalCurrentFingerprintJson, principalStateFingerprintJson, } from "../sqlite/principalKeyFingerprintJson.js";
6
7
  import { principalPolicies, principalPolicyBundleHistory, } from "../sqlite/principalPolicySchema.js";
@@ -22,6 +23,14 @@ export async function loadPrincipalKeyEnvelopeCandidates(execSql, fingerprints)
22
23
  const candidates = [];
23
24
  for (const fingerprint of new Set(fingerprints)) {
24
25
  const encoded = [];
26
+ const [archived] = await db
27
+ .select({ envelopes: principalKeyEnvelopeArchive.envelopesJson })
28
+ .from(principalKeyEnvelopeArchive)
29
+ .where(eq(principalKeyEnvelopeArchive.keyFingerprint, fingerprint))
30
+ .orderBy(desc(principalKeyEnvelopeArchive.version))
31
+ .limit(1);
32
+ if (archived)
33
+ encoded.push(archived.envelopes);
25
34
  for (const table of [principalPolicies, principalPolicyBundleHistory]) {
26
35
  const [row] = await db
27
36
  .select({ envelopes: table.currentMemberEnvelopesJson })
@@ -52,7 +61,7 @@ export async function loadPrincipalKeyEnvelopeCandidates(execSql, fingerprints)
52
61
  .limit(1);
53
62
  if (stage)
54
63
  encoded.push(stage.envelopes);
55
- for (const json of encoded) {
64
+ for (const json of new Set(encoded)) {
56
65
  let envelopes;
57
66
  try {
58
67
  envelopes = JSON.parse(json);