@tearleads/client-sdk 0.1.37 → 0.1.39

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 (80) hide show
  1. package/client/Tearleads.d.ts +3 -0
  2. package/client/Tearleads.js +20 -12
  3. package/client/Tearleads.js.map +1 -1
  4. package/client/organizations/organizationGroupHistory.js +1 -1
  5. package/client/organizations/organizationGroupHistory.js.map +1 -1
  6. package/client/organizations/organizationHistory.js +1 -1
  7. package/client/organizations/organizationHistory.js.map +1 -1
  8. package/client/session/index.js +2 -2
  9. package/client/session/index.js.map +1 -1
  10. package/client/session/sessionOrganizationCreation.js +1 -0
  11. package/client/session/sessionOrganizationCreation.js.map +1 -1
  12. package/client/session/sessionTypes.d.ts +3 -0
  13. package/client/session/sessionTypes.js.map +1 -1
  14. package/client/workflowRuntime.d.ts +2 -0
  15. package/client/workflowRuntime.js +34 -16
  16. package/client/workflowRuntime.js.map +1 -1
  17. package/data/keyingProjectionVerification/principalPolicyVerification.js +1 -2
  18. package/data/keyingProjectionVerification/principalPolicyVerification.js.map +1 -1
  19. package/data/keyingProjectionVerification/resolvedPrincipalPolicy.js +39 -51
  20. package/data/keyingProjectionVerification/resolvedPrincipalPolicy.js.map +1 -1
  21. package/data/keyingProjectionVerification/types.d.ts +1 -1
  22. package/data/keyingProjectionVerification/types.js.map +1 -1
  23. package/data/persistence/documents/documentDiscoveryEvidencePersistence.d.ts +16 -0
  24. package/data/persistence/documents/documentDiscoveryEvidencePersistence.js +23 -0
  25. package/data/persistence/documents/documentDiscoveryEvidencePersistence.js.map +1 -1
  26. package/data/persistence/organizations/organizationAccessRevocationPersistence.js +4 -0
  27. package/data/persistence/organizations/organizationAccessRevocationPersistence.js.map +1 -1
  28. package/data/persistence/organizations/organizationReadModelPersistence.js +7 -1
  29. package/data/persistence/organizations/organizationReadModelPersistence.js.map +1 -1
  30. package/data/principals/principalRecoveryEpoch.js +18 -0
  31. package/data/principals/principalRecoveryEpoch.js.map +1 -0
  32. package/data/trustedUserIdentity/index.d.ts +1 -1
  33. package/data/trustedUserIdentity/index.js +1 -1
  34. package/data/trustedUserIdentity/index.js.map +1 -1
  35. package/data/trustedUserIdentity/service.d.ts +12 -1
  36. package/data/trustedUserIdentity/service.js +27 -3
  37. package/data/trustedUserIdentity/service.js.map +1 -1
  38. package/package.json +1 -1
  39. package/workflows/organizations/createOrganization.d.ts +3 -0
  40. package/workflows/organizations/createOrganization.js +1 -0
  41. package/workflows/organizations/createOrganization.js.map +1 -1
  42. package/workflows/organizations/currentGroupPolicyMutationContext.js +5 -5
  43. package/workflows/organizations/currentGroupPolicyMutationContext.js.map +1 -1
  44. package/workflows/principals/principalCurrentRecoveryBatch.js +1 -1
  45. package/workflows/principals/principalCurrentRecoveryBatch.js.map +1 -1
  46. package/workflows/principals/principalRecoveryLocalPreference.js.map +1 -1
  47. package/workflows/principals/principalRecoveryMemo.d.ts +9 -4
  48. package/workflows/principals/principalRecoveryMemo.js +94 -34
  49. package/workflows/principals/principalRecoveryMemo.js.map +1 -1
  50. package/workflows/principals/principalRecoveryMemoValidity.js +33 -0
  51. package/workflows/principals/principalRecoveryMemoValidity.js.map +1 -0
  52. package/workflows/principals/principalRecoveryOutage.js +2 -2
  53. package/workflows/principals/principalRecoveryOutage.js.map +1 -1
  54. package/workflows/principals/recoverCurrentOrganizationPolicy.js +8 -3
  55. package/workflows/principals/recoverCurrentOrganizationPolicy.js.map +1 -1
  56. package/workflows/principals/recoverPrincipalPolicyHistory.js +42 -8
  57. package/workflows/principals/recoverPrincipalPolicyHistory.js.map +1 -1
  58. package/workflows/principals/recoverPublicPrincipalHistory.js +5 -5
  59. package/workflows/principals/recoverPublicPrincipalHistory.js.map +1 -1
  60. package/workflows/principals/recoverScopedPrincipalPolicyHistory.d.ts +9 -2
  61. package/workflows/principals/recoverScopedPrincipalPolicyHistory.js +8 -2
  62. package/workflows/principals/recoverScopedPrincipalPolicyHistory.js.map +1 -1
  63. package/workflows/principals/runtimePolicyRecovery.js +4 -1
  64. package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
  65. package/workflows/principals/runtimePolicyWarmer.js +12 -4
  66. package/workflows/principals/runtimePolicyWarmer.js.map +1 -1
  67. package/workflows/registration/organizationProvisioningPersistence.d.ts +3 -0
  68. package/workflows/registration/organizationProvisioningPersistence.js +27 -1
  69. package/workflows/registration/organizationProvisioningPersistence.js.map +1 -1
  70. package/workflows/registration/persistRegistrationBootstrap.d.ts +3 -0
  71. package/workflows/registration/persistRegistrationBootstrap.js +7 -1
  72. package/workflows/registration/persistRegistrationBootstrap.js.map +1 -1
  73. package/workflows/registration/provisionedPrincipalHistory.d.ts +45 -0
  74. package/workflows/registration/provisionedPrincipalHistory.js +104 -0
  75. package/workflows/registration/provisionedPrincipalHistory.js.map +1 -0
  76. package/workflows/registration/provisioningAcknowledgement.js +18 -0
  77. package/workflows/registration/provisioningAcknowledgement.js.map +1 -1
  78. package/workflows/registration/registerIdentity.d.ts +3 -0
  79. package/workflows/registration/registerIdentity.js +1 -0
  80. package/workflows/registration/registerIdentity.js.map +1 -1
@@ -30,6 +30,7 @@ export async function loadCurrentGroupPolicyMutationContext(input) {
30
30
  { head: adminHead, projection: authority.admins.policy.projection },
31
31
  ],
32
32
  };
33
+ // A reused citation keeps the lifetime of the read that produced it.
33
34
  const references = new Map();
34
35
  return {
35
36
  adminGroupId: authority.descriptor.adminGroupId,
@@ -60,18 +61,17 @@ export async function loadCurrentGroupPolicyMutationContext(input) {
60
61
  if (oldest !== undefined)
61
62
  references.delete(oldest);
62
63
  }
63
- pending = authority
64
- .readGroup(input.groupId, owned)
65
- .then((selected) => selected.policy);
64
+ pending = authority.readGroup(input.groupId, owned);
66
65
  references.set(key, pending);
67
66
  }
68
- const policy = await pending.catch((error) => {
67
+ const selected = await pending.catch((error) => {
69
68
  if (references.get(key) === pending)
70
69
  references.delete(key);
71
70
  throw error;
72
71
  });
73
72
  assertProjectionVerificationCurrent(stillCurrent);
74
- return policy;
73
+ assertProjectionVerificationCurrent(selected.stillCurrent);
74
+ return selected.policy;
75
75
  },
76
76
  stillCurrent,
77
77
  };
@@ -1 +1 @@
1
- {"version":3,"file":"currentGroupPolicyMutationContext.js","sourceRoot":"","sources":["currentGroupPolicyMutationContext.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type PrincipalPolicyExternalAuthority,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n type VerifiedPrincipalPolicyCurrent,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { advanceKeyingCheckpointsAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { requireOrganizationGroupHead } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { createRuntimePrincipalPolicyCurrentResolver } from \"../principals/runtimePolicyRecovery\";\nimport { createSelectedCurrentGroupMetadataContainerVerifier } from \"./currentGroupMetadataAuthority\";\nimport { loadCurrentOrganizationMutationAuthority } from \"./currentOrganizationMutationAuthority\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport { requireSignerCanManageGroup } from \"./groupMutationAuthorization\";\n\n/** Exact current directory, strict Admins and group evidence for one mutation. */\nexport async function loadCurrentGroupPolicyMutationContext(input: {\n readonly execSql: ExecSql;\n readonly groupId: string;\n readonly organizationId: string;\n readonly signerUserId: string;\n readonly resolveCurrentPolicy: NonNullable<\n ReturnType<typeof createRuntimePrincipalPolicyCurrentResolver>\n >;\n readonly recoverPendingPrincipalMutation: (\n organizationId: string,\n ) => Promise<void>;\n readonly stillCurrent: () => boolean;\n}) {\n input = { ...input };\n const { authority, currentOrgAdminUserIds } =\n await loadCurrentOrganizationMutationAuthority(input);\n const group = await authority.readGroup(input.groupId);\n const stillCurrent = () =>\n input.stillCurrent() && authority.stillCurrent() && group.stillCurrent();\n assertProjectionVerificationCurrent(stillCurrent);\n assertGroupMetadataBinding(group.current, authority.descriptor);\n requireSignerCanManageGroup(\n group.current,\n currentOrgAdminUserIds,\n input.signerUserId,\n );\n await advanceKeyingCheckpointsAtomically({\n access: [],\n execSql: input.execSql,\n organizationId: input.organizationId,\n policies: [...group.dependencies, group.policy],\n stillCurrent,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n const adminHead = requireOrganizationGroupHead(\n authority.descriptor,\n authority.descriptor.adminGroupId,\n );\n const externalAuthority: PrincipalPolicyExternalAuthority = {\n currentHead: adminHead,\n states: [\n { head: adminHead, projection: authority.admins.policy.projection },\n ],\n };\n const references = new Map<string, Promise<VerifiedPrincipalPolicyCurrent>>();\n return {\n adminGroupId: authority.descriptor.adminGroupId,\n adminCurrent: authority.admins,\n currentPolicy: group.current,\n verifiedCurrentPolicy: group.policy,\n verifyMetadataContainer:\n createSelectedCurrentGroupMetadataContainerVerifier({\n authority,\n organizationId: input.organizationId,\n stillCurrent,\n }),\n currentOrgAdminUserIds,\n externalAuthority,\n isOrganizationAdminsGroup:\n input.groupId === authority.descriptor.adminGroupId,\n localPolicyCheckpoint: group.policy.checkpoint,\n memberGroupId: authority.descriptor.memberGroupId,\n organizationDescriptor: authority.descriptor,\n organizationCurrent: authority.directory,\n async readPredecessorReference(reference: ReferencedPrincipalHead) {\n assertProjectionVerificationCurrent(stillCurrent);\n const owned = structuredClone(reference);\n const key = serializeKeyingCanonicalJson({ ...owned });\n let pending = references.get(key);\n if (!pending) {\n // Keep at most one bounded selection's citations, even across many paths.\n if (references.size === 128) {\n const oldest = references.keys().next().value;\n if (oldest !== undefined) references.delete(oldest);\n }\n pending = authority\n .readGroup(input.groupId, owned)\n .then((selected) => selected.policy);\n references.set(key, pending);\n }\n const policy = await pending.catch((error: unknown) => {\n if (references.get(key) === pending) references.delete(key);\n throw error;\n });\n assertProjectionVerificationCurrent(stillCurrent);\n return policy;\n },\n stillCurrent,\n };\n}\n"]}
1
+ {"version":3,"file":"currentGroupPolicyMutationContext.js","sourceRoot":"","sources":["currentGroupPolicyMutationContext.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type PrincipalPolicyExternalAuthority,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n type VerifiedPrincipalPolicyCurrent,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { advanceKeyingCheckpointsAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { requireOrganizationGroupHead } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { createRuntimePrincipalPolicyCurrentResolver } from \"../principals/runtimePolicyRecovery\";\nimport { createSelectedCurrentGroupMetadataContainerVerifier } from \"./currentGroupMetadataAuthority\";\nimport { loadCurrentOrganizationMutationAuthority } from \"./currentOrganizationMutationAuthority\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport { requireSignerCanManageGroup } from \"./groupMutationAuthorization\";\n\n/** Exact current directory, strict Admins and group evidence for one mutation. */\nexport async function loadCurrentGroupPolicyMutationContext(input: {\n readonly execSql: ExecSql;\n readonly groupId: string;\n readonly organizationId: string;\n readonly signerUserId: string;\n readonly resolveCurrentPolicy: NonNullable<\n ReturnType<typeof createRuntimePrincipalPolicyCurrentResolver>\n >;\n readonly recoverPendingPrincipalMutation: (\n organizationId: string,\n ) => Promise<void>;\n readonly stillCurrent: () => boolean;\n}) {\n input = { ...input };\n const { authority, currentOrgAdminUserIds } =\n await loadCurrentOrganizationMutationAuthority(input);\n const group = await authority.readGroup(input.groupId);\n const stillCurrent = () =>\n input.stillCurrent() && authority.stillCurrent() && group.stillCurrent();\n assertProjectionVerificationCurrent(stillCurrent);\n assertGroupMetadataBinding(group.current, authority.descriptor);\n requireSignerCanManageGroup(\n group.current,\n currentOrgAdminUserIds,\n input.signerUserId,\n );\n await advanceKeyingCheckpointsAtomically({\n access: [],\n execSql: input.execSql,\n organizationId: input.organizationId,\n policies: [...group.dependencies, group.policy],\n stillCurrent,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n const adminHead = requireOrganizationGroupHead(\n authority.descriptor,\n authority.descriptor.adminGroupId,\n );\n const externalAuthority: PrincipalPolicyExternalAuthority = {\n currentHead: adminHead,\n states: [\n { head: adminHead, projection: authority.admins.policy.projection },\n ],\n };\n // A reused citation keeps the lifetime of the read that produced it.\n const references = new Map<\n string,\n Promise<{\n readonly policy: VerifiedPrincipalPolicyCurrent;\n readonly stillCurrent: () => boolean;\n }>\n >();\n return {\n adminGroupId: authority.descriptor.adminGroupId,\n adminCurrent: authority.admins,\n currentPolicy: group.current,\n verifiedCurrentPolicy: group.policy,\n verifyMetadataContainer:\n createSelectedCurrentGroupMetadataContainerVerifier({\n authority,\n organizationId: input.organizationId,\n stillCurrent,\n }),\n currentOrgAdminUserIds,\n externalAuthority,\n isOrganizationAdminsGroup:\n input.groupId === authority.descriptor.adminGroupId,\n localPolicyCheckpoint: group.policy.checkpoint,\n memberGroupId: authority.descriptor.memberGroupId,\n organizationDescriptor: authority.descriptor,\n organizationCurrent: authority.directory,\n async readPredecessorReference(reference: ReferencedPrincipalHead) {\n assertProjectionVerificationCurrent(stillCurrent);\n const owned = structuredClone(reference);\n const key = serializeKeyingCanonicalJson({ ...owned });\n let pending = references.get(key);\n if (!pending) {\n // Keep at most one bounded selection's citations, even across many paths.\n if (references.size === 128) {\n const oldest = references.keys().next().value;\n if (oldest !== undefined) references.delete(oldest);\n }\n pending = authority.readGroup(input.groupId, owned);\n references.set(key, pending);\n }\n const selected = await pending.catch((error: unknown) => {\n if (references.get(key) === pending) references.delete(key);\n throw error;\n });\n assertProjectionVerificationCurrent(stillCurrent);\n assertProjectionVerificationCurrent(selected.stillCurrent);\n return selected.policy;\n },\n stillCurrent,\n };\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { recoverCurrentOrganizationPolicy } from "./recoverCurrentOrganizationPolicy.js";
2
2
  import { createScopedPrincipalPolicyHistoryBatch } from "./recoverScopedPrincipalPolicyHistory.js";
3
- /** Discovery and selected references share one authenticated collection view. */
3
+ /** Discovery and selected references share one authenticated batch view. */
4
4
  export function createPrincipalCurrentRecoveryBatch() {
5
5
  const memo = {
6
6
  directories: new Map(),
@@ -1 +1 @@
1
- {"version":3,"file":"principalCurrentRecoveryBatch.js","sourceRoot":"","sources":["principalCurrentRecoveryBatch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,uCAAuC,EAAE,MAAM,uCAAuC,CAAC;AAEhG,iFAAiF;AACjF,MAAM,UAAU,mCAAmC;IACjD,MAAM,IAAI,GAA0B;QAClC,WAAW,EAAE,IAAI,GAAG,EAAE;QACtB,MAAM,EAAE,IAAI,GAAG,EAAE;KAClB,CAAC;IACF,OAAO;QACL,OAAO,EAAE,uCAAuC,CAAC,IAAI,CAAC;QACtD,QAAQ,EAAE,CAAC,KAA+B,EAAE,EAAE,CAC5C,gCAAgC,CAAC,KAAK,EAAE,IAAI,CAAC;KAChD,CAAC;AACJ,CAAC","sourcesContent":["import type { PrincipalRecoveryContext } from \"./principalRecoveryDirectory\";\nimport type { PrincipalRecoveryMemo } from \"./principalRecoveryMemo\";\nimport { recoverCurrentOrganizationPolicy } from \"./recoverCurrentOrganizationPolicy\";\nimport { createScopedPrincipalPolicyHistoryBatch } from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Discovery and selected references share one authenticated collection view. */\nexport function createPrincipalCurrentRecoveryBatch() {\n const memo: PrincipalRecoveryMemo = {\n directories: new Map(),\n admins: new Map(),\n };\n return {\n recover: createScopedPrincipalPolicyHistoryBatch(memo),\n discover: (input: PrincipalRecoveryContext) =>\n recoverCurrentOrganizationPolicy(input, memo),\n };\n}\n"]}
1
+ {"version":3,"file":"principalCurrentRecoveryBatch.js","sourceRoot":"","sources":["principalCurrentRecoveryBatch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,uCAAuC,EAAE,MAAM,uCAAuC,CAAC;AAEhG,4EAA4E;AAC5E,MAAM,UAAU,mCAAmC;IACjD,MAAM,IAAI,GAA0B;QAClC,WAAW,EAAE,IAAI,GAAG,EAAE;QACtB,MAAM,EAAE,IAAI,GAAG,EAAE;KAClB,CAAC;IACF,OAAO;QACL,OAAO,EAAE,uCAAuC,CAAC,IAAI,CAAC;QACtD,QAAQ,EAAE,CAAC,KAA+B,EAAE,EAAE,CAC5C,gCAAgC,CAAC,KAAK,EAAE,IAAI,CAAC;KAChD,CAAC;AACJ,CAAC","sourcesContent":["import type { PrincipalRecoveryContext } from \"./principalRecoveryDirectory\";\nimport type { PrincipalRecoveryMemo } from \"./principalRecoveryMemo\";\nimport { recoverCurrentOrganizationPolicy } from \"./recoverCurrentOrganizationPolicy\";\nimport { createScopedPrincipalPolicyHistoryBatch } from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Discovery and selected references share one authenticated batch view. */\nexport function createPrincipalCurrentRecoveryBatch() {\n const memo: PrincipalRecoveryMemo = {\n directories: new Map(),\n admins: new Map(),\n };\n return {\n recover: createScopedPrincipalPolicyHistoryBatch(memo),\n discover: (input: PrincipalRecoveryContext) =>\n recoverCurrentOrganizationPolicy(input, memo),\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"principalRecoveryLocalPreference.js","sourceRoot":"","sources":["principalRecoveryLocalPreference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,kCAAkC,EAAE,MAAM,2BAA2B,CAAC;AAG/E,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,OAAmD,EACnD,OAAkE,EAClE,kBAA2B,EAC3B,wBAAwB,GAAG,KAAK;IAEhC,IAAI,kBAAkB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC;gBAC1B,GAAG,OAAO;gBACV,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;YACH,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC1D,IACE,wBAAwB;gBACxB,OAAO,CAAC,WAAW;gBACnB,6BAA6B,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC;gBAEpE,OAAO,KAAK,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC1D,IACE,CAAC,CAAC,KAAK,YAAY,iCAAiC,CAAC;gBACrD,CAAC,CACC,KAAK,YAAY,uBAAuB;oBACxC,KAAK,CAAC,IAAI,KAAK,oBAAoB,CACpC;gBAED,MAAM,KAAK,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,kCAAkC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { principalHeadMatchesReference } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport { PrincipalHistoryRecoveryRaceError } from \"./principalHistoryRecoveryTypes\";\nimport { recoverWithPrincipalOutageFallback } from \"./principalRecoveryOutage\";\nimport type { recoverScopedPrincipalPolicyHistory } from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Reuse exact current artifacts, or a proved historical page within a newer prefix. */\nexport async function recoverWithPrincipalLocalPreference(\n recover: typeof recoverScopedPrincipalPolicyHistory,\n options: Parameters<typeof recoverScopedPrincipalPolicyHistory>[0],\n preferLocalCurrent: boolean,\n allowHistoricalReference = false,\n) {\n if (preferLocalCurrent && !options.offline) {\n try {\n const local = await recover({\n ...options,\n offline: true,\n });\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (\n allowHistoricalReference ||\n options.historyPage ||\n principalHeadMatchesReference(local.policy.state, options.reference)\n )\n return local;\n } catch (error) {\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (\n !(error instanceof PrincipalHistoryRecoveryRaceError) &&\n !(\n error instanceof KeyingVerificationError &&\n error.code === \"missing_dependency\"\n )\n )\n throw error;\n }\n }\n return recoverWithPrincipalOutageFallback(recover, options);\n}\n"]}
1
+ {"version":3,"file":"principalRecoveryLocalPreference.js","sourceRoot":"","sources":["principalRecoveryLocalPreference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,kCAAkC,EAAE,MAAM,2BAA2B,CAAC;AAM/E,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,OAA2C,EAC3C,OAAmD,EACnD,kBAA2B,EAC3B,wBAAwB,GAAG,KAAK;IAEhC,IAAI,kBAAkB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC;gBAC1B,GAAG,OAAO;gBACV,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;YACH,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC1D,IACE,wBAAwB;gBACxB,OAAO,CAAC,WAAW;gBACnB,6BAA6B,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC;gBAEpE,OAAO,KAAK,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC1D,IACE,CAAC,CAAC,KAAK,YAAY,iCAAiC,CAAC;gBACrD,CAAC,CACC,KAAK,YAAY,uBAAuB;oBACxC,KAAK,CAAC,IAAI,KAAK,oBAAoB,CACpC;gBAED,MAAM,KAAK,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,kCAAkC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { principalHeadMatchesReference } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport { PrincipalHistoryRecoveryRaceError } from \"./principalHistoryRecoveryTypes\";\nimport { recoverWithPrincipalOutageFallback } from \"./principalRecoveryOutage\";\nimport type {\n RecoverScopedPrincipalPolicyHistoryOptions,\n ScopedPrincipalPolicyRecoveryBatch,\n} from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Reuse exact current artifacts, or a proved historical page within a newer prefix. */\nexport async function recoverWithPrincipalLocalPreference(\n recover: ScopedPrincipalPolicyRecoveryBatch,\n options: RecoverScopedPrincipalPolicyHistoryOptions,\n preferLocalCurrent: boolean,\n allowHistoricalReference = false,\n) {\n if (preferLocalCurrent && !options.offline) {\n try {\n const local = await recover({\n ...options,\n offline: true,\n });\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (\n allowHistoricalReference ||\n options.historyPage ||\n principalHeadMatchesReference(local.policy.state, options.reference)\n )\n return local;\n } catch (error) {\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (\n !(error instanceof PrincipalHistoryRecoveryRaceError) &&\n !(\n error instanceof KeyingVerificationError &&\n error.code === \"missing_dependency\"\n )\n )\n throw error;\n }\n }\n return recoverWithPrincipalOutageFallback(recover, options);\n}\n"]}
@@ -1,15 +1,20 @@
1
1
  import { type ReferencedPrincipalHead } from "../../internal/crypto/index.js";
2
2
  import type { RecoveredPrincipalPolicyHistory } from "./principalHistoryRecoveryTypes.js";
3
3
  import { type PrincipalRecoveryContext, type RecoveredPolicyDirectory } from "./principalRecoveryDirectory.js";
4
- type Memo<T> = Map<string, Promise<{
5
- value: T;
6
- stillCurrent: () => boolean;
7
- }>>;
4
+ interface MemoEntry<T> {
5
+ readonly value: T;
6
+ /** The producing call's private lease and caller lifetime. */
7
+ readonly stillCurrent: () => boolean;
8
+ /** Organization hint epoch observed before the reads that produced value. */
9
+ readonly epoch: number;
10
+ }
11
+ type Memo<T> = Map<string, Promise<MemoEntry<T>>>;
8
12
  export interface PrincipalRecoveryMemo {
9
13
  readonly directories: Memo<RecoveredPolicyDirectory>;
10
14
  readonly admins: Memo<RecoveredPrincipalPolicyHistory>;
11
15
  }
12
16
  export declare function createPrincipalRecoveryReader(input: PrincipalRecoveryContext, memo?: PrincipalRecoveryMemo): {
17
+ stillCurrent: () => boolean;
13
18
  directory: (references: readonly ReferencedPrincipalHead[]) => Promise<RecoveredPolicyDirectory>;
14
19
  admins: (expectedHead: ReferencedPrincipalHead, retainedReferences?: readonly ReferencedPrincipalHead[]) => Promise<RecoveredPrincipalPolicyHistory>;
15
20
  };
@@ -2,23 +2,81 @@ import { KeyingVerificationError, serializeKeyingCanonicalJson, } from "../../in
2
2
  import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
3
3
  import { principalHeadMatchesReference } from "../../data/principals/organizationAuthorityDescriptor.js";
4
4
  import { recoverPolicyDirectory, } from "./principalRecoveryDirectory.js";
5
+ import { adminsMemoValidity, directoryMemoValidity, } from "./principalRecoveryMemoValidity.js";
5
6
  import { recoverPrincipalPolicyHistory } from "./recoverPrincipalPolicyHistory.js";
6
- /** Only share authenticated results within one projection collection lifetime. */
7
- async function memoPrincipalRecovery(memo, key, stillCurrent, work) {
7
+ /** Synchronous: no hint and no lease end since the entry's reads began. */
8
+ function entryLive(entry, validity) {
9
+ return entry.epoch === validity.epoch() && entry.stillCurrent();
10
+ }
11
+ function evict(memo, key, pending) {
12
+ if (memo?.get(key) === pending)
13
+ memo.delete(key);
14
+ }
15
+ /**
16
+ * Return a live entry, evicting one whose producer lease ended, that predates
17
+ * an organization hint, or that a durable pin has moved past. Liveness is
18
+ * checked again after every await, so a hint or lease end during the pin
19
+ * reads also evicts. The caller rechecks synchronously before use. A pin read
20
+ * is a snapshot; a pin that advances later is caught by the checkpoint reload
21
+ * in recovery, the caller's checkpoint refresh, and atomic admission.
22
+ */
23
+ async function reusableEntry(memo, key, validity) {
24
+ const pending = memo?.get(key);
25
+ if (!pending)
26
+ return undefined;
27
+ const entry = await pending.catch(() => undefined);
28
+ if (entry &&
29
+ entryLive(entry, validity) &&
30
+ (await validity.covers(entry.value)) &&
31
+ entryLive(entry, validity))
32
+ return { entry, pending };
33
+ evict(memo, key, pending);
34
+ return undefined;
35
+ }
36
+ /**
37
+ * Share authenticated results within one workflow operation. A later caller
38
+ * never receives a result whose producer lease ended, that predates an
39
+ * organization hint, or that a durable pin has moved past; it recovers again.
40
+ * A result derived from a reused source keeps the source's epoch, so a hint
41
+ * that arrives before it is stored still evicts it. Every value is handed out
42
+ * with the consumer's lifetime and its producer's, so a producer that expires
43
+ * after hand-out still invalidates the consumer's use.
44
+ */
45
+ async function memoPrincipalRecovery(memo, key, stillCurrent, validity, sourceEpoch, work) {
8
46
  assertProjectionVerificationCurrent(stillCurrent);
9
- let pending = memo?.get(key);
10
- if (!pending) {
11
- pending = work().then((value) => ({ value, stillCurrent }));
12
- memo?.set(key, pending);
47
+ const reused = await reusableEntry(memo, key, validity);
48
+ if (reused) {
49
+ const { entry } = reused;
50
+ if (entryLive(entry, validity)) {
51
+ assertProjectionVerificationCurrent(stillCurrent);
52
+ return {
53
+ value: entry.value,
54
+ stillCurrent: () => stillCurrent() && entry.stillCurrent(),
55
+ };
56
+ }
57
+ evict(memo, key, reused.pending);
13
58
  }
59
+ // Observed before work starts its reads; a derived result keeps its source's.
60
+ const epoch = sourceEpoch ?? validity.epoch();
61
+ const pending = work().then((value) => ({ value, stillCurrent, epoch }));
62
+ memo?.set(key, pending);
14
63
  const result = await pending.catch((error) => {
15
- if (memo?.get(key) === pending)
16
- memo.delete(key);
64
+ evict(memo, key, pending);
17
65
  throw error;
18
66
  });
67
+ // A hint during production leaves this caller's in-flight view, never a
68
+ // stored one: the stale epoch would evict it anyway.
69
+ if (result.epoch !== validity.epoch())
70
+ evict(memo, key, pending);
19
71
  assertProjectionVerificationCurrent(result.stillCurrent);
20
72
  assertProjectionVerificationCurrent(stillCurrent);
21
- return result.value;
73
+ // stillCurrent already includes the lifetime of a derived value's source.
74
+ return { value: result.value, stillCurrent };
75
+ }
76
+ function fallsBackOnline(input, error) {
77
+ return (!input.offline &&
78
+ error instanceof KeyingVerificationError &&
79
+ error.code === "missing_dependency");
22
80
  }
23
81
  async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedReferences) {
24
82
  // Every recovered Current already retains its own exact head.
@@ -28,10 +86,11 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
28
86
  { ...expectedHead },
29
87
  selected.map((reference) => ({ ...reference })),
30
88
  ]);
31
- const known = memo?.get(key([]));
32
- let sourceCurrent = () => true;
89
+ const validity = adminsMemoValidity(input, expectedHead);
90
+ const known = (await reusableEntry(memo, key([]), validity))?.entry;
91
+ const sourceCurrent = known?.stillCurrent ?? (() => true);
33
92
  const current = () => !input.signal?.aborted && input.stillCurrent() && sourceCurrent();
34
- return memoPrincipalRecovery(memo, key(references), current, async () => {
93
+ return memoPrincipalRecovery(memo, key(references), current, validity, known?.epoch, async () => {
35
94
  const options = {
36
95
  ...input,
37
96
  expectedHead,
@@ -40,8 +99,6 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
40
99
  stillCurrent: current,
41
100
  };
42
101
  if (known) {
43
- sourceCurrent = (await known).stillCurrent;
44
- assertProjectionVerificationCurrent(current);
45
102
  try {
46
103
  // A freshly verified exact head only needs local proofs for additional
47
104
  // citations; their signature and private-root checks still run.
@@ -52,9 +109,7 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
52
109
  }
53
110
  catch (error) {
54
111
  assertProjectionVerificationCurrent(current);
55
- if (input.offline ||
56
- !(error instanceof KeyingVerificationError) ||
57
- error.code !== "missing_dependency")
112
+ if (!fallsBackOnline(input, error))
58
113
  throw error;
59
114
  }
60
115
  }
@@ -62,28 +117,26 @@ async function readMemoizedAdmins(input, memo, scope, expectedHead, retainedRefe
62
117
  });
63
118
  }
64
119
  async function readMemoizedDirectory(input, memo, key, references) {
65
- const known = references.length > 0 ? memo?.get(key([])) : undefined;
66
- let sourceCurrent = () => true;
120
+ const validity = directoryMemoValidity(input);
121
+ const known = references.length > 0
122
+ ? (await reusableEntry(memo, key([]), validity))?.entry
123
+ : undefined;
124
+ const sourceCurrent = known?.stillCurrent ?? (() => true);
67
125
  const current = () => !input.signal?.aborted && input.stillCurrent() && sourceCurrent();
68
- return memoPrincipalRecovery(memo, key(references), current, async () => {
126
+ return memoPrincipalRecovery(memo, key(references), current, validity, known?.epoch, async () => {
69
127
  if (!known)
70
128
  return recoverPolicyDirectory(input, references);
71
- const source = await known;
72
- sourceCurrent = source.stillCurrent;
73
- assertProjectionVerificationCurrent(current);
74
129
  try {
75
- // Add authenticated historical citations to this batch's exact directory
76
- // view. A newer private prefix must not silently replace that view.
77
- return await recoverPolicyDirectory({ ...input, offline: true, stillCurrent: current }, references, source.value);
130
+ // Add authenticated historical citations to this operation's exact
131
+ // directory view. A newer private prefix must not silently replace it.
132
+ return await recoverPolicyDirectory({ ...input, offline: true, stillCurrent: current }, references, known.value);
78
133
  }
79
134
  catch (error) {
80
135
  assertProjectionVerificationCurrent(current);
81
- if (input.offline ||
82
- !(error instanceof KeyingVerificationError) ||
83
- error.code !== "missing_dependency")
136
+ if (!fallsBackOnline(input, error))
84
137
  throw error;
85
138
  }
86
- return recoverPolicyDirectory({ ...input, stillCurrent: current }, references, source.value);
139
+ return recoverPolicyDirectory({ ...input, stillCurrent: current }, references, known.value);
87
140
  });
88
141
  }
89
142
  export function createPrincipalRecoveryReader(input, memo) {
@@ -96,9 +149,17 @@ export function createPrincipalRecoveryReader(input, memo) {
96
149
  ...scope,
97
150
  references.map((reference) => ({ ...reference })),
98
151
  ]);
152
+ // Every lifetime behind a value this reader handed out; its caller must
153
+ // keep checking them at the point of use.
154
+ const lifetimes = [];
155
+ const lease = (leased) => {
156
+ lifetimes.push(leased.stillCurrent);
157
+ return leased.value;
158
+ };
99
159
  return {
160
+ stillCurrent: () => lifetimes.every((current) => current()),
100
161
  directory: async (references) => {
101
- const value = await readMemoizedDirectory(input, memo?.directories, directoryKey, references);
162
+ const value = lease(await readMemoizedDirectory(input, memo?.directories, directoryKey, references));
102
163
  // Retaining extra citations does not prevent this authenticated directory
103
164
  // from satisfying a later read with no selected historical references.
104
165
  const unselectedKey = directoryKey([]);
@@ -107,12 +168,11 @@ export function createPrincipalRecoveryReader(input, memo) {
107
168
  selected &&
108
169
  references.length > 0 &&
109
170
  !memo.directories.has(unselectedKey))
110
- // Keep the original lease: its expiry requires a new batch, even if
111
- // a later caller still has a live lease of its own.
171
+ // The alias keeps the original lease and validity; either ending evicts it.
112
172
  memo.directories.set(unselectedKey, selected);
113
173
  return value;
114
174
  },
115
- admins: (expectedHead, retainedReferences = []) => readMemoizedAdmins(input, memo?.admins, scope, expectedHead, retainedReferences),
175
+ admins: async (expectedHead, retainedReferences = []) => lease(await readMemoizedAdmins(input, memo?.admins, scope, expectedHead, retainedReferences)),
116
176
  };
117
177
  }
118
178
  //# sourceMappingURL=principalRecoveryMemo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"principalRecoveryMemo.js","sourceRoot":"","sources":["principalRecoveryMemo.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { principalHeadMatchesReference } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { RecoveredPrincipalPolicyHistory } from \"./principalHistoryRecoveryTypes\";\nimport {\n type PrincipalRecoveryContext,\n type RecoveredPolicyDirectory,\n recoverPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\nimport { recoverPrincipalPolicyHistory } from \"./recoverPrincipalPolicyHistory\";\n\ntype Memo<T> = Map<string, Promise<{ value: T; stillCurrent: () => boolean }>>;\n\nexport interface PrincipalRecoveryMemo {\n readonly directories: Memo<RecoveredPolicyDirectory>;\n readonly admins: Memo<RecoveredPrincipalPolicyHistory>;\n}\n\n/** Only share authenticated results within one projection collection lifetime. */\nasync function memoPrincipalRecovery<T>(\n memo: Memo<T> | undefined,\n key: string,\n stillCurrent: () => boolean,\n work: () => Promise<T>,\n): Promise<T> {\n assertProjectionVerificationCurrent(stillCurrent);\n let pending = memo?.get(key);\n if (!pending) {\n pending = work().then((value) => ({ value, stillCurrent }));\n memo?.set(key, pending);\n }\n const result = await pending.catch((error: unknown) => {\n if (memo?.get(key) === pending) memo.delete(key);\n throw error;\n });\n assertProjectionVerificationCurrent(result.stillCurrent);\n assertProjectionVerificationCurrent(stillCurrent);\n return result.value;\n}\n\nasync function readMemoizedAdmins(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPrincipalPolicyHistory> | undefined,\n scope: readonly (string | boolean)[],\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[],\n) {\n // Every recovered Current already retains its own exact head.\n const references = retainedReferences.filter(\n (reference) => !principalHeadMatchesReference(expectedHead, reference),\n );\n const key = (selected: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n { ...expectedHead },\n selected.map((reference) => ({ ...reference })),\n ]);\n const known = memo?.get(key([]));\n let sourceCurrent = () => true;\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(memo, key(references), current, async () => {\n const options = {\n ...input,\n expectedHead,\n retainedReferences,\n historyVerification: \"direct-admins\" as const,\n stillCurrent: current,\n };\n if (known) {\n sourceCurrent = (await known).stillCurrent;\n assertProjectionVerificationCurrent(current);\n try {\n // A freshly verified exact head only needs local proofs for additional\n // citations; their signature and private-root checks still run.\n return await recoverPrincipalPolicyHistory({\n ...options,\n offline: true,\n });\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (\n input.offline ||\n !(error instanceof KeyingVerificationError) ||\n error.code !== \"missing_dependency\"\n )\n throw error;\n }\n }\n return recoverPrincipalPolicyHistory(options);\n });\n}\n\nasync function readMemoizedDirectory(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPolicyDirectory> | undefined,\n key: (references: readonly ReferencedPrincipalHead[]) => string,\n references: readonly ReferencedPrincipalHead[],\n) {\n const known = references.length > 0 ? memo?.get(key([])) : undefined;\n let sourceCurrent = () => true;\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(memo, key(references), current, async () => {\n if (!known) return recoverPolicyDirectory(input, references);\n const source = await known;\n sourceCurrent = source.stillCurrent;\n assertProjectionVerificationCurrent(current);\n try {\n // Add authenticated historical citations to this batch's exact directory\n // view. A newer private prefix must not silently replace that view.\n return await recoverPolicyDirectory(\n { ...input, offline: true, stillCurrent: current },\n references,\n source.value,\n );\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (\n input.offline ||\n !(error instanceof KeyingVerificationError) ||\n error.code !== \"missing_dependency\"\n )\n throw error;\n }\n return recoverPolicyDirectory(\n { ...input, stillCurrent: current },\n references,\n source.value,\n );\n });\n}\n\nexport function createPrincipalRecoveryReader(\n input: PrincipalRecoveryContext,\n memo?: PrincipalRecoveryMemo,\n) {\n const scope = [\n input.organizationId,\n input.protection.context,\n input.offline === true,\n ];\n const directoryKey = (references: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n references.map((reference) => ({ ...reference })),\n ]);\n return {\n directory: async (references: readonly ReferencedPrincipalHead[]) => {\n const value = await readMemoizedDirectory(\n input,\n memo?.directories,\n directoryKey,\n references,\n );\n // Retaining extra citations does not prevent this authenticated directory\n // from satisfying a later read with no selected historical references.\n const unselectedKey = directoryKey([]);\n const selected = memo?.directories.get(directoryKey(references));\n if (\n memo &&\n selected &&\n references.length > 0 &&\n !memo.directories.has(unselectedKey)\n )\n // Keep the original lease: its expiry requires a new batch, even if\n // a later caller still has a live lease of its own.\n memo.directories.set(unselectedKey, selected);\n return value;\n },\n admins: (\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[] = [],\n ) =>\n readMemoizedAdmins(\n input,\n memo?.admins,\n scope,\n expectedHead,\n retainedReferences,\n ),\n };\n}\n"]}
1
+ {"version":3,"file":"principalRecoveryMemo.js","sourceRoot":"","sources":["principalRecoveryMemo.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type ReferencedPrincipalHead,\n serializeKeyingCanonicalJson,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { principalHeadMatchesReference } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { RecoveredPrincipalPolicyHistory } from \"./principalHistoryRecoveryTypes\";\nimport {\n type PrincipalRecoveryContext,\n type RecoveredPolicyDirectory,\n recoverPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\nimport {\n adminsMemoValidity,\n directoryMemoValidity,\n type PrincipalRecoveryMemoValidity,\n} from \"./principalRecoveryMemoValidity\";\nimport { recoverPrincipalPolicyHistory } from \"./recoverPrincipalPolicyHistory\";\n\ninterface MemoEntry<T> {\n readonly value: T;\n /** The producing call's private lease and caller lifetime. */\n readonly stillCurrent: () => boolean;\n /** Organization hint epoch observed before the reads that produced value. */\n readonly epoch: number;\n}\n\ntype Memo<T> = Map<string, Promise<MemoEntry<T>>>;\n\n/** A value handed out with every lifetime it depends on. */\ninterface Leased<T> {\n readonly value: T;\n /** The consumer's lifetime and every producer lifetime behind value. */\n readonly stillCurrent: () => boolean;\n}\n\nexport interface PrincipalRecoveryMemo {\n readonly directories: Memo<RecoveredPolicyDirectory>;\n readonly admins: Memo<RecoveredPrincipalPolicyHistory>;\n}\n\n/** Synchronous: no hint and no lease end since the entry's reads began. */\nfunction entryLive<T>(\n entry: MemoEntry<T>,\n validity: PrincipalRecoveryMemoValidity<T>,\n): boolean {\n return entry.epoch === validity.epoch() && entry.stillCurrent();\n}\n\nfunction evict<T>(\n memo: Memo<T> | undefined,\n key: string,\n pending: Promise<MemoEntry<T>>,\n): void {\n if (memo?.get(key) === pending) memo.delete(key);\n}\n\n/**\n * Return a live entry, evicting one whose producer lease ended, that predates\n * an organization hint, or that a durable pin has moved past. Liveness is\n * checked again after every await, so a hint or lease end during the pin\n * reads also evicts. The caller rechecks synchronously before use. A pin read\n * is a snapshot; a pin that advances later is caught by the checkpoint reload\n * in recovery, the caller's checkpoint refresh, and atomic admission.\n */\nasync function reusableEntry<T>(\n memo: Memo<T> | undefined,\n key: string,\n validity: PrincipalRecoveryMemoValidity<T>,\n): Promise<\n { entry: MemoEntry<T>; pending: Promise<MemoEntry<T>> } | undefined\n> {\n const pending = memo?.get(key);\n if (!pending) return undefined;\n const entry = await pending.catch(() => undefined);\n if (\n entry &&\n entryLive(entry, validity) &&\n (await validity.covers(entry.value)) &&\n entryLive(entry, validity)\n )\n return { entry, pending };\n evict(memo, key, pending);\n return undefined;\n}\n\n/**\n * Share authenticated results within one workflow operation. A later caller\n * never receives a result whose producer lease ended, that predates an\n * organization hint, or that a durable pin has moved past; it recovers again.\n * A result derived from a reused source keeps the source's epoch, so a hint\n * that arrives before it is stored still evicts it. Every value is handed out\n * with the consumer's lifetime and its producer's, so a producer that expires\n * after hand-out still invalidates the consumer's use.\n */\nasync function memoPrincipalRecovery<T>(\n memo: Memo<T> | undefined,\n key: string,\n stillCurrent: () => boolean,\n validity: PrincipalRecoveryMemoValidity<T>,\n sourceEpoch: number | undefined,\n work: () => Promise<T>,\n): Promise<Leased<T>> {\n assertProjectionVerificationCurrent(stillCurrent);\n const reused = await reusableEntry(memo, key, validity);\n if (reused) {\n const { entry } = reused;\n if (entryLive(entry, validity)) {\n assertProjectionVerificationCurrent(stillCurrent);\n return {\n value: entry.value,\n stillCurrent: () => stillCurrent() && entry.stillCurrent(),\n };\n }\n evict(memo, key, reused.pending);\n }\n // Observed before work starts its reads; a derived result keeps its source's.\n const epoch = sourceEpoch ?? validity.epoch();\n const pending = work().then((value) => ({ value, stillCurrent, epoch }));\n memo?.set(key, pending);\n const result = await pending.catch((error: unknown) => {\n evict(memo, key, pending);\n throw error;\n });\n // A hint during production leaves this caller's in-flight view, never a\n // stored one: the stale epoch would evict it anyway.\n if (result.epoch !== validity.epoch()) evict(memo, key, pending);\n assertProjectionVerificationCurrent(result.stillCurrent);\n assertProjectionVerificationCurrent(stillCurrent);\n // stillCurrent already includes the lifetime of a derived value's source.\n return { value: result.value, stillCurrent };\n}\n\nfunction fallsBackOnline(\n input: PrincipalRecoveryContext,\n error: unknown,\n): boolean {\n return (\n !input.offline &&\n error instanceof KeyingVerificationError &&\n error.code === \"missing_dependency\"\n );\n}\n\nasync function readMemoizedAdmins(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPrincipalPolicyHistory> | undefined,\n scope: readonly (string | boolean)[],\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[],\n) {\n // Every recovered Current already retains its own exact head.\n const references = retainedReferences.filter(\n (reference) => !principalHeadMatchesReference(expectedHead, reference),\n );\n const key = (selected: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n { ...expectedHead },\n selected.map((reference) => ({ ...reference })),\n ]);\n const validity = adminsMemoValidity(input, expectedHead);\n const known = (await reusableEntry(memo, key([]), validity))?.entry;\n const sourceCurrent = known?.stillCurrent ?? (() => true);\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(\n memo,\n key(references),\n current,\n validity,\n known?.epoch,\n async () => {\n const options = {\n ...input,\n expectedHead,\n retainedReferences,\n historyVerification: \"direct-admins\" as const,\n stillCurrent: current,\n };\n if (known) {\n try {\n // A freshly verified exact head only needs local proofs for additional\n // citations; their signature and private-root checks still run.\n return await recoverPrincipalPolicyHistory({\n ...options,\n offline: true,\n });\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (!fallsBackOnline(input, error)) throw error;\n }\n }\n return recoverPrincipalPolicyHistory(options);\n },\n );\n}\n\nasync function readMemoizedDirectory(\n input: PrincipalRecoveryContext,\n memo: Memo<RecoveredPolicyDirectory> | undefined,\n key: (references: readonly ReferencedPrincipalHead[]) => string,\n references: readonly ReferencedPrincipalHead[],\n) {\n const validity = directoryMemoValidity(input);\n const known =\n references.length > 0\n ? (await reusableEntry(memo, key([]), validity))?.entry\n : undefined;\n const sourceCurrent = known?.stillCurrent ?? (() => true);\n const current = () =>\n !input.signal?.aborted && input.stillCurrent() && sourceCurrent();\n return memoPrincipalRecovery(\n memo,\n key(references),\n current,\n validity,\n known?.epoch,\n async () => {\n if (!known) return recoverPolicyDirectory(input, references);\n try {\n // Add authenticated historical citations to this operation's exact\n // directory view. A newer private prefix must not silently replace it.\n return await recoverPolicyDirectory(\n { ...input, offline: true, stillCurrent: current },\n references,\n known.value,\n );\n } catch (error) {\n assertProjectionVerificationCurrent(current);\n if (!fallsBackOnline(input, error)) throw error;\n }\n return recoverPolicyDirectory(\n { ...input, stillCurrent: current },\n references,\n known.value,\n );\n },\n );\n}\n\nexport function createPrincipalRecoveryReader(\n input: PrincipalRecoveryContext,\n memo?: PrincipalRecoveryMemo,\n) {\n const scope = [\n input.organizationId,\n input.protection.context,\n input.offline === true,\n ];\n const directoryKey = (references: readonly ReferencedPrincipalHead[]) =>\n serializeKeyingCanonicalJson([\n ...scope,\n references.map((reference) => ({ ...reference })),\n ]);\n // Every lifetime behind a value this reader handed out; its caller must\n // keep checking them at the point of use.\n const lifetimes: (() => boolean)[] = [];\n const lease = <T>(leased: Leased<T>): T => {\n lifetimes.push(leased.stillCurrent);\n return leased.value;\n };\n return {\n stillCurrent: () => lifetimes.every((current) => current()),\n directory: async (references: readonly ReferencedPrincipalHead[]) => {\n const value = lease(\n await readMemoizedDirectory(\n input,\n memo?.directories,\n directoryKey,\n references,\n ),\n );\n // Retaining extra citations does not prevent this authenticated directory\n // from satisfying a later read with no selected historical references.\n const unselectedKey = directoryKey([]);\n const selected = memo?.directories.get(directoryKey(references));\n if (\n memo &&\n selected &&\n references.length > 0 &&\n !memo.directories.has(unselectedKey)\n )\n // The alias keeps the original lease and validity; either ending evicts it.\n memo.directories.set(unselectedKey, selected);\n return value;\n },\n admins: async (\n expectedHead: ReferencedPrincipalHead,\n retainedReferences: readonly ReferencedPrincipalHead[] = [],\n ) =>\n lease(\n await readMemoizedAdmins(\n input,\n memo?.admins,\n scope,\n expectedHead,\n retainedReferences,\n ),\n ),\n };\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import { loadPrincipalPolicyCheckpoint } from "../../data/persistence/keyingCheckpointPersistence.js";
2
+ import { principalRecoveryEpoch } from "../../data/principals/principalRecoveryEpoch.js";
3
+ async function pinCovers(input, head) {
4
+ const pin = await loadPrincipalPolicyCheckpoint(input.execSql, head.principalType, head.principalId);
5
+ return (!pin ||
6
+ pin.version < head.version ||
7
+ (pin.version === head.version && pin.stateHash === head.stateHash));
8
+ }
9
+ function organizationEpoch(input) {
10
+ return () => principalRecoveryEpoch(input.execSql, input.organizationId);
11
+ }
12
+ /** A directory names every group head; a newer pin on any of them evicts it. */
13
+ export function directoryMemoValidity(input) {
14
+ return {
15
+ epoch: organizationEpoch(input),
16
+ covers: async (directory) => {
17
+ for (const head of [
18
+ directory.policy.state,
19
+ ...directory.descriptor.groupHeads,
20
+ ])
21
+ if (!(await pinCovers(input, head)))
22
+ return false;
23
+ return true;
24
+ },
25
+ };
26
+ }
27
+ export function adminsMemoValidity(input, expectedHead) {
28
+ return {
29
+ epoch: organizationEpoch(input),
30
+ covers: () => pinCovers(input, expectedHead),
31
+ };
32
+ }
33
+ //# sourceMappingURL=principalRecoveryMemoValidity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principalRecoveryMemoValidity.js","sourceRoot":"","sources":["principalRecoveryMemoValidity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AAoBtF,KAAK,UAAU,SAAS,CACtB,KAA+B,EAC/B,IAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,6BAA6B,CAC7C,KAAK,CAAC,OAAO,EACb,IAAI,CAAC,aAAa,EAClB,IAAI,CAAC,WAAW,CACjB,CAAC;IACF,OAAO,CACL,CAAC,GAAG;QACJ,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO;QAC1B,CAAC,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,IAAI,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,CAAC,CACnE,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,KAA+B;IACxD,OAAO,GAAG,EAAE,CAAC,sBAAsB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;AAC3E,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,qBAAqB,CACnC,KAA+B;IAE/B,OAAO;QACL,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC;QAC/B,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;YAC1B,KAAK,MAAM,IAAI,IAAI;gBACjB,SAAS,CAAC,MAAM,CAAC,KAAK;gBACtB,GAAG,SAAS,CAAC,UAAU,CAAC,UAAU;aACnC;gBACC,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;oBAAE,OAAO,KAAK,CAAC;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,KAA+B,EAC/B,YAAwB;IAExB,OAAO;QACL,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC;QAC/B,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC;KAC7C,CAAC;AACJ,CAAC","sourcesContent":["import type { ReferencedPrincipalHead } from \"@tearleads/crypto\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { principalRecoveryEpoch } from \"../../data/principals/principalRecoveryEpoch\";\nimport type { RecoveredPrincipalPolicyHistory } from \"./principalHistoryRecoveryTypes\";\nimport type {\n PrincipalRecoveryContext,\n RecoveredPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\n\n/** What keeps a memo entry valid beyond its producer's lease. */\nexport interface PrincipalRecoveryMemoValidity<T> {\n /** Organization hint epoch; production records it before its reads. */\n readonly epoch: () => number;\n /** No durable pin has moved past or forked from a memoized head. */\n readonly covers: (value: T) => Promise<boolean>;\n}\n\ntype PinnedHead = Pick<\n ReferencedPrincipalHead,\n \"principalType\" | \"principalId\" | \"version\" | \"stateHash\"\n>;\n\nasync function pinCovers(\n input: PrincipalRecoveryContext,\n head: PinnedHead,\n): Promise<boolean> {\n const pin = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n head.principalType,\n head.principalId,\n );\n return (\n !pin ||\n pin.version < head.version ||\n (pin.version === head.version && pin.stateHash === head.stateHash)\n );\n}\n\nfunction organizationEpoch(input: PrincipalRecoveryContext) {\n return () => principalRecoveryEpoch(input.execSql, input.organizationId);\n}\n\n/** A directory names every group head; a newer pin on any of them evicts it. */\nexport function directoryMemoValidity(\n input: PrincipalRecoveryContext,\n): PrincipalRecoveryMemoValidity<RecoveredPolicyDirectory> {\n return {\n epoch: organizationEpoch(input),\n covers: async (directory) => {\n for (const head of [\n directory.policy.state,\n ...directory.descriptor.groupHeads,\n ])\n if (!(await pinCovers(input, head))) return false;\n return true;\n },\n };\n}\n\nexport function adminsMemoValidity(\n input: PrincipalRecoveryContext,\n expectedHead: PinnedHead,\n): PrincipalRecoveryMemoValidity<RecoveredPrincipalPolicyHistory> {\n return {\n epoch: organizationEpoch(input),\n covers: () => pinCovers(input, expectedHead),\n };\n}\n"]}
@@ -2,7 +2,7 @@ import { KeyingVerificationError } from "../../internal/crypto/index.js";
2
2
  import { ProjectionDependencyUnavailableError } from "../../data/keyingProjectionVerification/dependencyUnavailable.js";
3
3
  import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
4
4
  import { PrincipalPolicyHistoryReadError } from "./principalHistoryRecoveryTypes.js";
5
- import { recoverScopedPrincipalPolicyHistory } from "./recoverScopedPrincipalPolicyHistory.js";
5
+ import { recoverLeasedScopedPrincipalPolicyHistory, } from "./recoverScopedPrincipalPolicyHistory.js";
6
6
  /** Refusals and invalid evidence must never silently select a cached policy. */
7
7
  export function isPrincipalRecoveryOutage(error) {
8
8
  if (!(error instanceof PrincipalPolicyHistoryReadError))
@@ -24,7 +24,7 @@ export async function recoverWithPrincipalOutageFallback(recover, options) {
24
24
  if (options.offline || !isPrincipalRecoveryOutage(error))
25
25
  throw error;
26
26
  try {
27
- return await recoverScopedPrincipalPolicyHistory({
27
+ return await recoverLeasedScopedPrincipalPolicyHistory({
28
28
  ...options,
29
29
  offline: true,
30
30
  });
@@ -1 +1 @@
1
- {"version":3,"file":"principalRecoveryOutage.js","sourceRoot":"","sources":["principalRecoveryOutage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,oCAAoC,EAAE,MAAM,+DAA+D,CAAC;AACrH,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,mCAAmC,EAAE,MAAM,uCAAuC,CAAC;AAE5F,gFAAgF;AAChF,MAAM,UAAU,yBAAyB,CAAC,KAAc;IACtD,IAAI,CAAC,CAAC,KAAK,YAAY,+BAA+B,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1B,OAAO,CACL,OAAO,CAAC,IAAI,KAAK,SAAS;QAC1B,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM;YACtB,OAAO,CAAC,MAAM,KAAK,IAAI;YACvB,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC;QACxB,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW;YAC3B,OAAO,CAAC,IAAI,KAAK,qCAAqC,CAAC,CAC1D,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kCAAkC,CACtD,OAAmD,EACnD,OAAkE;IAElE,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC1D,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;QACtE,IAAI,CAAC;YACH,OAAO,MAAM,mCAAmC,CAAC;gBAC/C,GAAG,OAAO;gBACV,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,IACE,UAAU,YAAY,uBAAuB;gBAC7C,UAAU,CAAC,IAAI,KAAK,oBAAoB;gBAExC,MAAM,IAAI,oCAAoC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACrE,MAAM,UAAU,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { ProjectionDependencyUnavailableError } from \"../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { PrincipalPolicyHistoryReadError } from \"./principalHistoryRecoveryTypes\";\nimport { recoverScopedPrincipalPolicyHistory } from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Refusals and invalid evidence must never silently select a cached policy. */\nexport function isPrincipalRecoveryOutage(error: unknown): boolean {\n if (!(error instanceof PrincipalPolicyHistoryReadError)) return false;\n const { failure } = error;\n return (\n failure.kind === \"network\" ||\n (failure.kind === \"http\" &&\n failure.status !== null &&\n failure.status >= 500) ||\n (failure.kind === \"cancelled\" &&\n failure.code === \"principal_history_request_timed_out\")\n );\n}\n\nexport async function recoverWithPrincipalOutageFallback(\n recover: typeof recoverScopedPrincipalPolicyHistory,\n options: Parameters<typeof recoverScopedPrincipalPolicyHistory>[0],\n) {\n try {\n return await recover(options);\n } catch (error) {\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (options.offline || !isPrincipalRecoveryOutage(error)) throw error;\n try {\n return await recoverScopedPrincipalPolicyHistory({\n ...options,\n offline: true,\n });\n } catch (localError) {\n if (\n localError instanceof KeyingVerificationError &&\n localError.code === \"missing_dependency\"\n )\n throw new ProjectionDependencyUnavailableError(localError.message);\n throw localError;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"principalRecoveryOutage.js","sourceRoot":"","sources":["principalRecoveryOutage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,oCAAoC,EAAE,MAAM,+DAA+D,CAAC;AACrH,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAEL,yCAAyC,GAE1C,MAAM,uCAAuC,CAAC;AAE/C,gFAAgF;AAChF,MAAM,UAAU,yBAAyB,CAAC,KAAc;IACtD,IAAI,CAAC,CAAC,KAAK,YAAY,+BAA+B,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1B,OAAO,CACL,OAAO,CAAC,IAAI,KAAK,SAAS;QAC1B,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM;YACtB,OAAO,CAAC,MAAM,KAAK,IAAI;YACvB,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC;QACxB,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW;YAC3B,OAAO,CAAC,IAAI,KAAK,qCAAqC,CAAC,CAC1D,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kCAAkC,CACtD,OAA2C,EAC3C,OAAmD;IAEnD,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC1D,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;QACtE,IAAI,CAAC;YACH,OAAO,MAAM,yCAAyC,CAAC;gBACrD,GAAG,OAAO;gBACV,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,IACE,UAAU,YAAY,uBAAuB;gBAC7C,UAAU,CAAC,IAAI,KAAK,oBAAoB;gBAExC,MAAM,IAAI,oCAAoC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACrE,MAAM,UAAU,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { ProjectionDependencyUnavailableError } from \"../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { PrincipalPolicyHistoryReadError } from \"./principalHistoryRecoveryTypes\";\nimport {\n type RecoverScopedPrincipalPolicyHistoryOptions,\n recoverLeasedScopedPrincipalPolicyHistory,\n type ScopedPrincipalPolicyRecoveryBatch,\n} from \"./recoverScopedPrincipalPolicyHistory\";\n\n/** Refusals and invalid evidence must never silently select a cached policy. */\nexport function isPrincipalRecoveryOutage(error: unknown): boolean {\n if (!(error instanceof PrincipalPolicyHistoryReadError)) return false;\n const { failure } = error;\n return (\n failure.kind === \"network\" ||\n (failure.kind === \"http\" &&\n failure.status !== null &&\n failure.status >= 500) ||\n (failure.kind === \"cancelled\" &&\n failure.code === \"principal_history_request_timed_out\")\n );\n}\n\nexport async function recoverWithPrincipalOutageFallback(\n recover: ScopedPrincipalPolicyRecoveryBatch,\n options: RecoverScopedPrincipalPolicyHistoryOptions,\n) {\n try {\n return await recover(options);\n } catch (error) {\n assertProjectionVerificationCurrent(options.stillCurrent);\n if (options.offline || !isPrincipalRecoveryOutage(error)) throw error;\n try {\n return await recoverLeasedScopedPrincipalPolicyHistory({\n ...options,\n offline: true,\n });\n } catch (localError) {\n if (\n localError instanceof KeyingVerificationError &&\n localError.code === \"missing_dependency\"\n )\n throw new ProjectionDependencyUnavailableError(localError.message);\n throw localError;\n }\n }\n}\n"]}
@@ -10,20 +10,25 @@ export async function recoverCurrentOrganizationPolicy(options, memo) {
10
10
  };
11
11
  try {
12
12
  let directory;
13
+ let reader = createPrincipalRecoveryReader(input, memo);
13
14
  try {
14
- directory = await createPrincipalRecoveryReader(input, memo).directory([]);
15
+ directory = await reader.directory([]);
15
16
  }
16
17
  catch (error) {
17
18
  assertProjectionVerificationCurrent(input.stillCurrent);
18
19
  if (input.offline || !isPrincipalRecoveryOutage(error))
19
20
  throw error;
20
- directory = await createPrincipalRecoveryReader({ ...input, offline: true }, memo).directory([]);
21
+ reader = createPrincipalRecoveryReader({ ...input, offline: true }, memo);
22
+ directory = await reader.directory([]);
21
23
  }
22
- assertProjectionVerificationCurrent(input.stillCurrent);
24
+ // A reused directory keeps its producer's lifetime to the point of use.
25
+ const stillCurrent = () => input.stillCurrent() && reader.stillCurrent();
26
+ assertProjectionVerificationCurrent(stillCurrent);
23
27
  return {
24
28
  current: directory.current,
25
29
  policy: directory.policy,
26
30
  dependencies: [],
31
+ stillCurrent,
27
32
  };
28
33
  }
29
34
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"recoverCurrentOrganizationPolicy.js","sourceRoot":"","sources":["recoverCurrentOrganizationPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,kDAAkD,CAAC;AAKjG,OAAO,EACL,6BAA6B,GAE9B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,OAAiC,EACjC,IAA4B;IAE5B,MAAM,KAAK,GAAG;QACZ,GAAG,OAAO;QACV,UAAU,EAAE,6BAA6B,CAAC,OAAO,CAAC,UAAU,CAAC;KAC9D,CAAC;IACF,IAAI,CAAC;QACH,IAAI,SAAmC,CAAC;QACxC,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,6BAA6B,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,SAAS,CACpE,EAAE,CACH,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACxD,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YACpE,SAAS,GAAG,MAAM,6BAA6B,CAC7C,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAC3B,IAAI,CACL,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;QACD,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACxD,OAAO;YACL,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,YAAY,EAAE,EAAE;SACjB,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC;AACH,CAAC","sourcesContent":["import { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { ownPrincipalHistoryProtection } from \"../../data/principals/principalHistoryProtection\";\nimport type {\n PrincipalRecoveryContext,\n RecoveredPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\nimport {\n createPrincipalRecoveryReader,\n type PrincipalRecoveryMemo,\n} from \"./principalRecoveryMemo\";\nimport { isPrincipalRecoveryOutage } from \"./principalRecoveryOutage\";\n\n/** Discover and verify current directory artifacts, including protected offline recovery. */\nexport async function recoverCurrentOrganizationPolicy(\n options: PrincipalRecoveryContext,\n memo?: PrincipalRecoveryMemo,\n) {\n const input = {\n ...options,\n protection: ownPrincipalHistoryProtection(options.protection),\n };\n try {\n let directory: RecoveredPolicyDirectory;\n try {\n directory = await createPrincipalRecoveryReader(input, memo).directory(\n [],\n );\n } catch (error) {\n assertProjectionVerificationCurrent(input.stillCurrent);\n if (input.offline || !isPrincipalRecoveryOutage(error)) throw error;\n directory = await createPrincipalRecoveryReader(\n { ...input, offline: true },\n memo,\n ).directory([]);\n }\n assertProjectionVerificationCurrent(input.stillCurrent);\n return {\n current: directory.current,\n policy: directory.policy,\n dependencies: [],\n };\n } finally {\n input.protection.localKey.fill(0);\n }\n}\n"]}
1
+ {"version":3,"file":"recoverCurrentOrganizationPolicy.js","sourceRoot":"","sources":["recoverCurrentOrganizationPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,6BAA6B,EAAE,MAAM,kDAAkD,CAAC;AAKjG,OAAO,EACL,6BAA6B,GAE9B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,OAAiC,EACjC,IAA4B;IAE5B,MAAM,KAAK,GAAG;QACZ,GAAG,OAAO;QACV,UAAU,EAAE,6BAA6B,CAAC,OAAO,CAAC,UAAU,CAAC;KAC9D,CAAC;IACF,IAAI,CAAC;QACH,IAAI,SAAmC,CAAC;QACxC,IAAI,MAAM,GAAG,6BAA6B,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACxD,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACxD,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YACpE,MAAM,GAAG,6BAA6B,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;YAC1E,SAAS,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,wEAAwE;QACxE,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACzE,mCAAmC,CAAC,YAAY,CAAC,CAAC;QAClD,OAAO;YACL,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,YAAY,EAAE,EAAE;YAChB,YAAY;SACb,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC;AACH,CAAC","sourcesContent":["import { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { ownPrincipalHistoryProtection } from \"../../data/principals/principalHistoryProtection\";\nimport type {\n PrincipalRecoveryContext,\n RecoveredPolicyDirectory,\n} from \"./principalRecoveryDirectory\";\nimport {\n createPrincipalRecoveryReader,\n type PrincipalRecoveryMemo,\n} from \"./principalRecoveryMemo\";\nimport { isPrincipalRecoveryOutage } from \"./principalRecoveryOutage\";\n\n/** Discover and verify current directory artifacts, including protected offline recovery. */\nexport async function recoverCurrentOrganizationPolicy(\n options: PrincipalRecoveryContext,\n memo?: PrincipalRecoveryMemo,\n) {\n const input = {\n ...options,\n protection: ownPrincipalHistoryProtection(options.protection),\n };\n try {\n let directory: RecoveredPolicyDirectory;\n let reader = createPrincipalRecoveryReader(input, memo);\n try {\n directory = await reader.directory([]);\n } catch (error) {\n assertProjectionVerificationCurrent(input.stillCurrent);\n if (input.offline || !isPrincipalRecoveryOutage(error)) throw error;\n reader = createPrincipalRecoveryReader({ ...input, offline: true }, memo);\n directory = await reader.directory([]);\n }\n // A reused directory keeps its producer's lifetime to the point of use.\n const stillCurrent = () => input.stillCurrent() && reader.stillCurrent();\n assertProjectionVerificationCurrent(stillCurrent);\n return {\n current: directory.current,\n policy: directory.policy,\n dependencies: [],\n stillCurrent,\n };\n } finally {\n input.protection.localKey.fill(0);\n }\n}\n"]}