@tearleads/client-sdk 0.1.47 → 0.1.49

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 (56) hide show
  1. package/client/organizations/currentPrincipalMutations.js +36 -17
  2. package/client/organizations/currentPrincipalMutations.js.map +1 -1
  3. package/client/organizations/principalContainerMutations.js +1 -0
  4. package/client/organizations/principalContainerMutations.js.map +1 -1
  5. package/client/organizations/principalMutations.js +2 -2
  6. package/client/organizations/principalMutations.js.map +1 -1
  7. package/data/persistence/documents/internal/documentRows.js +37 -41
  8. package/data/persistence/documents/internal/documentRows.js.map +1 -1
  9. package/data/persistence/principalHistoryEvidencePersistence.js +17 -18
  10. package/data/persistence/principalHistoryEvidencePersistence.js.map +1 -1
  11. package/data/persistence/principalHistoryNodeRetention.js +164 -39
  12. package/data/persistence/principalHistoryNodeRetention.js.map +1 -1
  13. package/data/persistence/principalPolicyReferenceIndexPersistence.js +101 -38
  14. package/data/persistence/principalPolicyReferenceIndexPersistence.js.map +1 -1
  15. package/data/principals/principalMutationJournalShape.js +6 -0
  16. package/data/principals/principalMutationJournalShape.js.map +1 -1
  17. package/data/sqlite/sqliteUpsert.js +9 -0
  18. package/data/sqlite/sqliteUpsert.js.map +1 -0
  19. package/internal/api-client/ApiClient.d.ts +122 -0
  20. package/internal/api-client/principalPolicyRequests.d.ts +122 -0
  21. package/internal/validators/operation/principals.d.ts +1026 -206
  22. package/internal/validators/request/principal.d.ts +332 -0
  23. package/internal/validators/request/principal.js +1 -0
  24. package/internal/validators/request/principal.js.map +1 -1
  25. package/internal/validators/response/principal.d.ts +488 -0
  26. package/internal/validators/response/principal.js +1 -0
  27. package/internal/validators/response/principal.js.map +1 -1
  28. package/package.json +1 -1
  29. package/workflows/containers/child/rekeyProjection.d.ts +6 -1
  30. package/workflows/containers/child/rekeyProjection.js +41 -0
  31. package/workflows/containers/child/rekeyProjection.js.map +1 -1
  32. package/workflows/organizations/authoredPrincipalPolicyWarmer.js +25 -0
  33. package/workflows/organizations/authoredPrincipalPolicyWarmer.js.map +1 -0
  34. package/workflows/organizations/commitCurrentGroupPolicyMutation.js +101 -34
  35. package/workflows/organizations/commitCurrentGroupPolicyMutation.js.map +1 -1
  36. package/workflows/organizations/groupPolicyMutationContext.d.ts +5 -52
  37. package/workflows/organizations/groupPolicyMutationContext.js +2 -65
  38. package/workflows/organizations/groupPolicyMutationContext.js.map +1 -1
  39. package/workflows/organizations/precedingPrincipalRematerialization.js +31 -0
  40. package/workflows/organizations/precedingPrincipalRematerialization.js.map +1 -0
  41. package/workflows/organizations/principalContainerRematerialization.js +39 -7
  42. package/workflows/organizations/principalContainerRematerialization.js.map +1 -1
  43. package/workflows/organizations/principalContainerRematerializationPlanning.js +6 -1
  44. package/workflows/organizations/principalContainerRematerializationPlanning.js.map +1 -1
  45. package/workflows/organizations/principalContainerRematerializationTargets.js +0 -11
  46. package/workflows/organizations/principalContainerRematerializationTargets.js.map +1 -1
  47. package/workflows/organizations/principalMutationReceipt.js +6 -0
  48. package/workflows/organizations/principalMutationReceipt.js.map +1 -1
  49. package/workflows/organizations/principalPolicyApi.d.ts +33 -0
  50. package/workflows/organizations/principalPolicyApi.js +2 -0
  51. package/workflows/organizations/principalPolicyApi.js.map +1 -0
  52. package/workflows/organizations/runtimeCurrentGroupMutation.js +36 -19
  53. package/workflows/organizations/runtimeCurrentGroupMutation.js.map +1 -1
  54. package/workflows/organizations/submitGroupPolicyCommit.d.ts +22 -0
  55. package/workflows/organizations/submitGroupPolicyCommit.js +71 -0
  56. package/workflows/organizations/submitGroupPolicyCommit.js.map +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"rekeyProjection.js","sourceRoot":"","sources":["rekeyProjection.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type ContainerKekRecipientTarget,\n computeContainerKekRecipientTargetHash,\n computeContainerKeyEpochHash,\n} from \"@tearleads/crypto\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport type { MaterializedContainerRekeyPlan } from \"../../../data/containers/shared/types\";\nimport {\n canonicalKeyingJsonString,\n readCanonicalRecord,\n readCanonicalRecords,\n} from \"../../../data/keyingCanonicalJson\";\nimport { mergeProjectionPolicyEvidence } from \"../../../data/keyingProjectionVerification/mergeProjectionPolicyEvidence\";\n\nconst speculativeProjections = new WeakSet<ContainerWriterProjectionResponse>();\n\nexport function isSpeculativeContainerWriterProjection(\n projection: ContainerWriterProjectionResponse,\n): boolean {\n return speculativeProjections.has(projection);\n}\n\n/**\n * What any rotation plan (rekey, revoke, move) holds in common: enough to say\n * what its container's path entry will be once the server accepts it.\n */\nexport interface RotationPlanArtifacts {\n readonly body: object;\n readonly containerId: string;\n readonly containerKeyEpochId: string;\n readonly event: MaterializedContainerRekeyPlan[\"plan\"][\"event\"];\n readonly eventHash: string;\n readonly keyEpoch: MaterializedContainerRekeyPlan[\"plan\"][\"keyEpoch\"];\n readonly manifest: MaterializedContainerRekeyPlan[\"plan\"][\"manifest\"];\n readonly manifestHash: string;\n readonly previousManifest: MaterializedContainerRekeyPlan[\"plan\"][\"previousManifest\"];\n readonly keyring: MaterializedContainerRekeyPlan[\"plan\"][\"keyring\"];\n readonly state: MaterializedContainerRekeyPlan[\"plan\"][\"state\"];\n readonly wraps: MaterializedContainerRekeyPlan[\"plan\"][\"wraps\"];\n}\n\nfunction recipientTargets(\n plan: RotationPlanArtifacts,\n): ContainerKekRecipientTarget[] {\n return plan.wraps\n .map((wrap) => ({\n recipientId: wrap.recipientId,\n recipientKeyEpochId: wrap.recipientKeyEpochId,\n recipientKeyFingerprint: wrap.recipientKeyFingerprint,\n recipientKind: wrap.recipientKind,\n }))\n .sort((left, right) =>\n canonicalKeyingJsonString(\n left,\n \"Container rekey recipient\",\n ).localeCompare(\n canonicalKeyingJsonString(right, \"Container rekey recipient\"),\n ),\n );\n}\n\n/**\n * The projection a rotation will leave behind, before the server has accepted\n * it. `ancestors` is the path above the container afterwards: unchanged for a\n * rekey or revoke, the destination parent's path for a move.\n */\nexport async function containerWriterProjectionFromRotationPlan(input: {\n ancestors?: Pick<\n ContainerWriterProjectionResponse,\n \"containerKeks\" | \"path\" | \"policyEvidence\"\n >;\n plan: RotationPlanArtifacts;\n previousProjection: ContainerWriterProjectionResponse;\n}): Promise<ContainerWriterProjectionResponse> {\n const { plan } = input;\n const previousManifest = input.previousProjection.path.at(-1);\n const previousKek = input.previousProjection.containerKeks.at(-1);\n if (\n !previousManifest ||\n !previousKek ||\n previousManifest.manifestHash !== plan.previousManifest.manifestHash ||\n previousKek.containerId !== plan.containerId\n ) {\n throw new Error(\"Container rekey projection predecessor mismatch\");\n }\n const targets = recipientTargets(plan);\n const nextManifest = {\n event: {\n body: readCanonicalRecord(plan.body, \"Container rekey body\"),\n event: readCanonicalRecord(plan.event, \"Container rekey event\"),\n eventHash: plan.eventHash,\n },\n manifest: readCanonicalRecord(plan.manifest, \"Container rekey manifest\"),\n manifestHash: plan.manifestHash,\n state: readCanonicalRecord(plan.state, \"Container rekey state\"),\n };\n const nextKek = {\n accessManifestHash: plan.manifestHash,\n containerId: plan.containerId,\n containerKeyEpoch: plan.keyEpoch.keyEpoch,\n containerKeyEpochId: plan.containerKeyEpochId,\n containerManifestHistory: [\n previousManifest,\n ...previousKek.containerManifestHistory,\n ],\n keyEpoch: readCanonicalRecord(plan.keyEpoch, \"Container rekey key epoch\"),\n keyEpochHash: await computeContainerKeyEpochHash(plan.keyEpoch),\n keyring: { ...plan.keyring },\n keyTargetHash: await computeContainerKekRecipientTargetHash(targets),\n parentContainerKeyEpochId: plan.keyEpoch.parentContainerKeyEpochId,\n recipientTargets: readCanonicalRecords(\n targets,\n \"Container rekey recipient targets\",\n ),\n wraps: readCanonicalRecords(plan.wraps, \"Container rekey wraps\"),\n };\n const ancestors = input.ancestors ?? {\n containerKeks: input.previousProjection.containerKeks.slice(0, -1),\n path: input.previousProjection.path.slice(0, -1),\n };\n const projection = {\n ...input.previousProjection,\n policyEvidence: mergeProjectionPolicyEvidence([\n input.previousProjection.policyEvidence,\n ...(input.ancestors ? [input.ancestors.policyEvidence] : []),\n ]),\n path: [...ancestors.path, nextManifest],\n containerKeks: [...ancestors.containerKeks, nextKek],\n };\n speculativeProjections.add(projection);\n return projection;\n}\n\nexport function containerWriterProjectionFromRekeyPlan(input: {\n materializedPlan: Pick<\n MaterializedContainerRekeyPlan,\n \"containerKey\" | \"plan\"\n >;\n previousProjection: ContainerWriterProjectionResponse;\n}): Promise<ContainerWriterProjectionResponse> {\n return containerWriterProjectionFromRotationPlan({\n plan: input.materializedPlan.plan,\n previousProjection: input.previousProjection,\n });\n}\n\n/**\n * A descendant's served projection, re-rooted on rotations the server has not\n * accepted yet: `rotated` replaces the path down to and including its own\n * container. Null when the descendant does not sit below that container.\n */\nexport function rebaseContainerWriterProjection(\n descendant: ContainerWriterProjectionResponse,\n rotated: Pick<\n ContainerWriterProjectionResponse,\n \"containerKeks\" | \"path\" | \"policyEvidence\"\n >,\n): ContainerWriterProjectionResponse | null {\n const rotatedId = rotated.containerKeks.at(-1)?.containerId;\n const index = descendant.containerKeks.findIndex(\n (kek) => kek.containerId === rotatedId,\n );\n if (rotatedId === undefined || index < 0) return null;\n if (index === descendant.containerKeks.length - 1) return null;\n const projection = {\n ...descendant,\n policyEvidence: mergeProjectionPolicyEvidence([\n descendant.policyEvidence,\n rotated.policyEvidence,\n ]),\n path: [...rotated.path, ...descendant.path.slice(index + 1)],\n containerKeks: [\n ...rotated.containerKeks,\n ...descendant.containerKeks.slice(index + 1),\n ],\n };\n speculativeProjections.add(projection);\n return projection;\n}\n"]}
1
+ {"version":3,"file":"rekeyProjection.js","sourceRoot":"","sources":["rekeyProjection.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type ContainerKekRecipientTarget,\n computeContainerKekRecipientTargetHash,\n computeContainerKeyEpochHash,\n} from \"@tearleads/crypto\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport type {\n MaterializedContainerRekeyPlan,\n MaterializedContainerSharePlan,\n} from \"../../../data/containers/shared/types\";\nimport {\n canonicalKeyingJsonString,\n readCanonicalRecord,\n readCanonicalRecords,\n} from \"../../../data/keyingCanonicalJson\";\nimport { mergeProjectionPolicyEvidence } from \"../../../data/keyingProjectionVerification/mergeProjectionPolicyEvidence\";\n\nconst speculativeProjections = new WeakSet<ContainerWriterProjectionResponse>();\n\nexport function isSpeculativeContainerWriterProjection(\n projection: ContainerWriterProjectionResponse,\n): boolean {\n return speculativeProjections.has(projection);\n}\n\n/**\n * What any rotation plan (rekey, revoke, move) holds in common: enough to say\n * what its container's path entry will be once the server accepts it.\n */\nexport interface RotationPlanArtifacts {\n readonly body: object;\n readonly containerId: string;\n readonly containerKeyEpochId: string;\n readonly event: MaterializedContainerRekeyPlan[\"plan\"][\"event\"];\n readonly eventHash: string;\n readonly keyEpoch: MaterializedContainerRekeyPlan[\"plan\"][\"keyEpoch\"];\n readonly manifest: MaterializedContainerRekeyPlan[\"plan\"][\"manifest\"];\n readonly manifestHash: string;\n readonly previousManifest: MaterializedContainerRekeyPlan[\"plan\"][\"previousManifest\"];\n readonly keyring: MaterializedContainerRekeyPlan[\"plan\"][\"keyring\"];\n readonly state: MaterializedContainerRekeyPlan[\"plan\"][\"state\"];\n readonly wraps: MaterializedContainerRekeyPlan[\"plan\"][\"wraps\"];\n}\n\nfunction recipientTargets(\n plan: Pick<RotationPlanArtifacts, \"wraps\">,\n): ContainerKekRecipientTarget[] {\n return plan.wraps\n .map((wrap) => ({\n recipientId: wrap.recipientId,\n recipientKeyEpochId: wrap.recipientKeyEpochId,\n recipientKeyFingerprint: wrap.recipientKeyFingerprint,\n recipientKind: wrap.recipientKind,\n }))\n .sort((left, right) =>\n canonicalKeyingJsonString(\n left,\n \"Container rekey recipient\",\n ).localeCompare(\n canonicalKeyingJsonString(right, \"Container rekey recipient\"),\n ),\n );\n}\n\n/**\n * The projection a rotation will leave behind, before the server has accepted\n * it. `ancestors` is the path above the container afterwards: unchanged for a\n * rekey or revoke, the destination parent's path for a move.\n */\nexport async function containerWriterProjectionFromRotationPlan(input: {\n ancestors?: Pick<\n ContainerWriterProjectionResponse,\n \"containerKeks\" | \"path\" | \"policyEvidence\"\n >;\n plan: RotationPlanArtifacts;\n previousProjection: ContainerWriterProjectionResponse;\n}): Promise<ContainerWriterProjectionResponse> {\n const { plan } = input;\n const previousManifest = input.previousProjection.path.at(-1);\n const previousKek = input.previousProjection.containerKeks.at(-1);\n if (\n !previousManifest ||\n !previousKek ||\n previousManifest.manifestHash !== plan.previousManifest.manifestHash ||\n previousKek.containerId !== plan.containerId\n ) {\n throw new Error(\"Container rekey projection predecessor mismatch\");\n }\n const targets = recipientTargets(plan);\n const nextManifest = {\n event: {\n body: readCanonicalRecord(plan.body, \"Container rekey body\"),\n event: readCanonicalRecord(plan.event, \"Container rekey event\"),\n eventHash: plan.eventHash,\n },\n manifest: readCanonicalRecord(plan.manifest, \"Container rekey manifest\"),\n manifestHash: plan.manifestHash,\n state: readCanonicalRecord(plan.state, \"Container rekey state\"),\n };\n const nextKek = {\n accessManifestHash: plan.manifestHash,\n containerId: plan.containerId,\n containerKeyEpoch: plan.keyEpoch.keyEpoch,\n containerKeyEpochId: plan.containerKeyEpochId,\n containerManifestHistory: [\n previousManifest,\n ...previousKek.containerManifestHistory,\n ],\n keyEpoch: readCanonicalRecord(plan.keyEpoch, \"Container rekey key epoch\"),\n keyEpochHash: await computeContainerKeyEpochHash(plan.keyEpoch),\n keyring: { ...plan.keyring },\n keyTargetHash: await computeContainerKekRecipientTargetHash(targets),\n parentContainerKeyEpochId: plan.keyEpoch.parentContainerKeyEpochId,\n recipientTargets: readCanonicalRecords(\n targets,\n \"Container rekey recipient targets\",\n ),\n wraps: readCanonicalRecords(plan.wraps, \"Container rekey wraps\"),\n };\n const ancestors = input.ancestors ?? {\n containerKeks: input.previousProjection.containerKeks.slice(0, -1),\n path: input.previousProjection.path.slice(0, -1),\n };\n const projection = {\n ...input.previousProjection,\n policyEvidence: mergeProjectionPolicyEvidence([\n input.previousProjection.policyEvidence,\n ...(input.ancestors ? [input.ancestors.policyEvidence] : []),\n ]),\n path: [...ancestors.path, nextManifest],\n containerKeks: [...ancestors.containerKeks, nextKek],\n };\n speculativeProjections.add(projection);\n return projection;\n}\n\nexport function containerWriterProjectionFromRekeyPlan(input: {\n materializedPlan: Pick<\n MaterializedContainerRekeyPlan,\n \"containerKey\" | \"plan\"\n >;\n previousProjection: ContainerWriterProjectionResponse;\n}): Promise<ContainerWriterProjectionResponse> {\n return containerWriterProjectionFromRotationPlan({\n plan: input.materializedPlan.plan,\n previousProjection: input.previousProjection,\n });\n}\n\n/**\n * A descendant's served projection, re-rooted on rotations the server has not\n * accepted yet: `rotated` replaces the path down to and including its own\n * container. Null when the descendant does not sit below that container.\n */\nexport function rebaseContainerWriterProjection(\n descendant: ContainerWriterProjectionResponse,\n rotated: Pick<\n ContainerWriterProjectionResponse,\n \"containerKeks\" | \"path\" | \"policyEvidence\"\n >,\n): ContainerWriterProjectionResponse | null {\n const rotatedId = rotated.containerKeks.at(-1)?.containerId;\n const index = descendant.containerKeks.findIndex(\n (kek) => kek.containerId === rotatedId,\n );\n if (rotatedId === undefined || index < 0) return null;\n if (index === descendant.containerKeks.length - 1) return null;\n const projection = {\n ...descendant,\n policyEvidence: mergeProjectionPolicyEvidence([\n descendant.policyEvidence,\n rotated.policyEvidence,\n ]),\n path: [...rotated.path, ...descendant.path.slice(index + 1)],\n containerKeks: [\n ...rotated.containerKeks,\n ...descendant.containerKeks.slice(index + 1),\n ],\n };\n speculativeProjections.add(projection);\n return projection;\n}\n\n/** A grant keeps its key epoch while advancing the manifest and recipient wraps. */\nexport async function containerWriterProjectionFromSharePlan(input: {\n plan: MaterializedContainerSharePlan[\"plan\"];\n previousProjection: ContainerWriterProjectionResponse;\n}): Promise<ContainerWriterProjectionResponse> {\n const { plan, previousProjection } = input;\n const previous = previousProjection.path.at(-1);\n const kek = previousProjection.containerKeks.at(-1);\n if (\n !previous ||\n !kek ||\n previous.manifestHash !== plan.previousManifest.manifestHash ||\n kek.containerId !== plan.containerId\n )\n throw new Error(\"Container grant projection predecessor mismatch\");\n const targets = recipientTargets({ wraps: plan.wraps });\n const projection = {\n ...previousProjection,\n path: [\n ...previousProjection.path.slice(0, -1),\n {\n event: {\n body: readCanonicalRecord(plan.body, \"Container grant body\"),\n event: readCanonicalRecord(plan.event, \"Container grant event\"),\n eventHash: plan.eventHash,\n },\n manifest: readCanonicalRecord(\n plan.manifest,\n \"Container grant manifest\",\n ),\n manifestHash: plan.manifestHash,\n state: readCanonicalRecord(plan.state, \"Container grant state\"),\n },\n ],\n containerKeks: [\n ...previousProjection.containerKeks.slice(0, -1),\n {\n ...kek,\n accessManifestHash: plan.manifestHash,\n containerManifestHistory: [previous, ...kek.containerManifestHistory],\n keyTargetHash: await computeContainerKekRecipientTargetHash(targets),\n recipientTargets: readCanonicalRecords(\n targets,\n \"Container grant targets\",\n ),\n wraps: readCanonicalRecords(plan.wraps, \"Container grant wraps\"),\n },\n ],\n };\n speculativeProjections.add(projection);\n return projection;\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import { assertProjectionVerificationCurrent, } from "../../data/keyingProjectionVerification/types.js";
2
+ import { principalStateMatchesReference } from "../../data/principalPolicyStates.js";
3
+ /** Only exact, locally authored successors may resolve before their transaction commits. */
4
+ export function withAuthoredPrincipalPolicies(warmer, authored) {
5
+ if (!warmer?.resolveReference || authored.length === 0)
6
+ return warmer;
7
+ const resolve = warmer.resolveReference;
8
+ const find = (organizationId, reference) => authored.find((entry) => entry.organizationId === organizationId &&
9
+ principalStateMatchesReference(entry.policy.state, reference));
10
+ return Object.assign((input) => warmer({
11
+ ...input,
12
+ references: input.references.filter((reference) => !find(input.organizationId, reference)),
13
+ }), {
14
+ ...warmer,
15
+ resolveReference: async (input) => {
16
+ const evidence = find(input.organizationId, input.reference);
17
+ if (!evidence)
18
+ return resolve(input);
19
+ assertProjectionVerificationCurrent(input.stillCurrent);
20
+ assertProjectionVerificationCurrent(evidence.stillCurrent);
21
+ return evidence;
22
+ },
23
+ });
24
+ }
25
+ //# sourceMappingURL=authoredPrincipalPolicyWarmer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authoredPrincipalPolicyWarmer.js","sourceRoot":"","sources":["authoredPrincipalPolicyWarmer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mCAAmC,GAGpC,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AAElF,4FAA4F;AAC5F,MAAM,UAAU,6BAA6B,CAC3C,MAAmD,EACnD,QAAoD;IAEpD,IAAI,CAAC,MAAM,EAAE,gBAAgB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IACtE,MAAM,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;IACxC,MAAM,IAAI,GAAG,CACX,cAAsB,EACtB,SAAqD,EACrD,EAAE,CACF,QAAQ,CAAC,IAAI,CACX,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,cAAc,KAAK,cAAc;QACvC,8BAA8B,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,CAChE,CAAC;IACJ,OAAO,MAAM,CAAC,MAAM,CAClB,CAAC,KAAqD,EAAE,EAAE,CACxD,MAAM,CAAC;QACL,GAAG,KAAK;QACR,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM,CACjC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,SAAS,CAAC,CACtD;KACF,CAAC,EACJ;QACE,GAAG,MAAM;QACT,gBAAgB,EAAE,KAAK,EAAE,KAAoC,EAAE,EAAE;YAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;YAC7D,IAAI,CAAC,QAAQ;gBAAE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;YACrC,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACxD,mCAAmC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC3D,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CACF,CAAC;AACJ,CAAC","sourcesContent":["import {\n assertProjectionVerificationCurrent,\n type ReferencedPrincipalPolicyWarmer,\n type ResolvedPrincipalPolicyEvidence,\n} from \"../../data/keyingProjectionVerification/types\";\nimport { principalStateMatchesReference } from \"../../data/principalPolicyStates\";\n\n/** Only exact, locally authored successors may resolve before their transaction commits. */\nexport function withAuthoredPrincipalPolicies(\n warmer: ReferencedPrincipalPolicyWarmer | undefined,\n authored: readonly ResolvedPrincipalPolicyEvidence[],\n): ReferencedPrincipalPolicyWarmer | undefined {\n if (!warmer?.resolveReference || authored.length === 0) return warmer;\n const resolve = warmer.resolveReference;\n const find = (\n organizationId: string,\n reference: Parameters<typeof resolve>[0][\"reference\"],\n ) =>\n authored.find(\n (entry) =>\n entry.organizationId === organizationId &&\n principalStateMatchesReference(entry.policy.state, reference),\n );\n return Object.assign(\n (input: Parameters<ReferencedPrincipalPolicyWarmer>[0]) =>\n warmer({\n ...input,\n references: input.references.filter(\n (reference) => !find(input.organizationId, reference),\n ),\n }),\n {\n ...warmer,\n resolveReference: async (input: Parameters<typeof resolve>[0]) => {\n const evidence = find(input.organizationId, input.reference);\n if (!evidence) return resolve(input);\n assertProjectionVerificationCurrent(input.stillCurrent);\n assertProjectionVerificationCurrent(evidence.stillCurrent);\n return evidence;\n },\n },\n );\n}\n"]}
@@ -3,8 +3,9 @@ import { prepareAuthoredGroupPolicy } from "./groupPolicyMutationAcknowledgement
3
3
  import { submitGroupPolicyCommit, } from "./groupPolicyMutationContext.js";
4
4
  import { groupPolicyMutationHead } from "./groupPolicyMutationHead.js";
5
5
  import { buildOrganizationGroupDirectoryPolicyRequest, replaceOrganizationGroupHead, } from "./organizationGroupDirectory.js";
6
+ import { assertAuthoredPrincipalMutationReceipt } from "./principalMutationReceipt.js";
6
7
  import { projectionUserIds, resolveRequiredUserIdentities, } from "./trustedOrganizationUsers.js";
7
- /** Commit and retain one exact compound successor without rebuilding full history. */
8
+ /** Commit and retain the group, optional roster enrollment and directory together. */
8
9
  export async function commitCurrentGroupPolicyMutation(input) {
9
10
  const { context } = input;
10
11
  assertProjectionVerificationCurrent(context.stillCurrent);
@@ -14,38 +15,11 @@ export async function commitCurrentGroupPolicyMutation(input) {
14
15
  });
15
16
  if (!signer)
16
17
  throw new Error("Group policy signer is unavailable");
17
- const expectedHead = await groupPolicyMutationHead(input.request);
18
- const nextPolicy = await prepareAuthoredGroupPolicy({
19
- verifiedCurrentPolicy: context.verifiedCurrentPolicy,
20
- expectedHead,
21
- request: input.request,
22
- signerPublicKeys: [signer],
23
- externalAuthority: context.externalAuthority,
24
- localPolicyCheckpoint: context.localPolicyCheckpoint,
25
- stillCurrent: context.stillCurrent,
26
- });
27
- const prepared = await input.prepareContainerMutations(nextPolicy);
28
- input.request.containerMutations = [...prepared.requests];
29
- const adminProjection = context.isOrganizationAdminsGroup
30
- ? input.request.projection
31
- : context.adminCurrent.current.currentProjection;
32
- const adminUsers = await resolveRequiredUserIdentities({
33
- resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
34
- userIds: projectionUserIds(adminProjection),
35
- });
36
- const organizationRequest = await buildOrganizationGroupDirectoryPolicyRequest({
37
- adminProjection,
38
- adminUsers,
39
- currentPolicy: context.organizationCurrent.current,
40
- descriptor: context.organizationDescriptor,
41
- groupHeads: replaceOrganizationGroupHead({
42
- descriptor: context.organizationDescriptor,
43
- nextHead: expectedHead,
44
- }),
45
- signerUserId: input.signerUserId,
46
- signingFingerprint: input.signingFingerprint,
47
- signingKeyPair: input.signingKeyPair,
48
- });
18
+ const member = input.member
19
+ ? await prepareChange(input.member, signer)
20
+ : null;
21
+ const { expectedHead, prepared } = await prepareChange(input, signer, member?.preceding);
22
+ const organizationRequest = await prepareDirectory(input, expectedHead, member?.expectedHead);
49
23
  assertProjectionVerificationCurrent(context.stillCurrent);
50
24
  const response = await submitGroupPolicyCommit({
51
25
  apiClient: input.apiClient,
@@ -53,20 +27,113 @@ export async function commitCurrentGroupPolicyMutation(input) {
53
27
  organizationId: input.organizationId,
54
28
  request: input.request,
55
29
  organizationRequest,
56
- carryDescendantRekeys: prepared.carry,
30
+ memberRequest: input.member?.request,
31
+ // A compound retry must rebuild both batches' shared speculative heads.
32
+ // Refuse the whole transaction if a damaged-key repair needs extra rekeys;
33
+ // ordinary adds preserve key epochs and need no descendant carry retry.
34
+ carryDescendantRekeys: member ? undefined : prepared.carry,
57
35
  stillCurrent: context.stillCurrent,
58
36
  });
59
37
  if (!response)
60
38
  throw new Error("Group policy update failed");
61
39
  assertProjectionVerificationCurrent(context.stillCurrent);
40
+ await assertAuthoredPrincipalMutationReceipt({
41
+ groupPolicy: input.request,
42
+ organizationPolicy: organizationRequest,
43
+ ...(input.member ? { memberPolicy: input.member.request } : {}),
44
+ }, response);
62
45
  await context.retainAcknowledged({
46
+ additionalGroups: input.member && response.memberPolicy
47
+ ? [
48
+ {
49
+ ...input.member.context.groupRetention,
50
+ request: input.member.request,
51
+ response: response.memberPolicy,
52
+ retiredContainerIds: member?.prepared.retiredContainerIds,
53
+ },
54
+ ]
55
+ : [],
63
56
  request: input.request,
64
57
  organizationRequest,
65
58
  response,
66
59
  retiredContainerIds: prepared.retiredContainerIds,
67
60
  });
61
+ if (member && response.memberPolicy) {
62
+ await member.prepared.acknowledge(response.memberPolicy.containerMutations, context.stillCurrent);
63
+ }
68
64
  await prepared.acknowledge(response.groupPolicy.containerMutations, context.stillCurrent);
69
65
  assertProjectionVerificationCurrent(context.stillCurrent);
70
66
  return response.groupPolicy;
71
67
  }
68
+ async function prepareChange(change, signer, preceding) {
69
+ const expectedHead = await groupPolicyMutationHead(change.request);
70
+ const nextPolicy = await prepareAuthoredGroupPolicy({
71
+ verifiedCurrentPolicy: change.context.verifiedCurrentPolicy,
72
+ expectedHead,
73
+ request: change.request,
74
+ signerPublicKeys: [signer],
75
+ externalAuthority: change.context.externalAuthority,
76
+ localPolicyCheckpoint: change.context.localPolicyCheckpoint,
77
+ stillCurrent: change.context.stillCurrent,
78
+ });
79
+ const prepared = await change.prepareContainerMutations(nextPolicy, preceding);
80
+ change.request.containerMutations = [...prepared.requests];
81
+ return {
82
+ expectedHead,
83
+ prepared,
84
+ preceding: prepared.preceding
85
+ ? {
86
+ ...prepared.preceding,
87
+ authored: [
88
+ ...(preceding?.authored ?? []),
89
+ {
90
+ organizationId: change.context.organizationDescriptor.organizationId,
91
+ policy: nextPolicy,
92
+ dependencies: [
93
+ change.context.organizationCurrent.policy,
94
+ change.context.adminCurrent.policy,
95
+ ],
96
+ stillCurrent: change.context.stillCurrent,
97
+ },
98
+ ],
99
+ }
100
+ : undefined,
101
+ };
102
+ }
103
+ async function prepareDirectory(input, expectedHead, memberHead) {
104
+ const { context } = input;
105
+ if (input.member &&
106
+ (input.member.context.currentPolicy.currentState.principalId !==
107
+ context.memberGroupId ||
108
+ input.member.context.organizationCurrent.current.currentState
109
+ .stateHash !==
110
+ context.organizationCurrent.current.currentState.stateHash))
111
+ throw new Error("Roster and group changes must share the same organization policy");
112
+ let groupHeads = replaceOrganizationGroupHead({
113
+ descriptor: context.organizationDescriptor,
114
+ nextHead: expectedHead,
115
+ });
116
+ if (memberHead)
117
+ groupHeads = replaceOrganizationGroupHead({
118
+ descriptor: { ...context.organizationDescriptor, groupHeads },
119
+ nextHead: memberHead,
120
+ });
121
+ const adminProjection = context.isOrganizationAdminsGroup
122
+ ? input.request.projection
123
+ : context.adminCurrent.current.currentProjection;
124
+ const adminUsers = await resolveRequiredUserIdentities({
125
+ resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
126
+ userIds: projectionUserIds(adminProjection),
127
+ });
128
+ return buildOrganizationGroupDirectoryPolicyRequest({
129
+ adminProjection,
130
+ adminUsers,
131
+ currentPolicy: context.organizationCurrent.current,
132
+ descriptor: context.organizationDescriptor,
133
+ groupHeads,
134
+ signerUserId: input.signerUserId,
135
+ signingFingerprint: input.signingFingerprint,
136
+ signingKeyPair: input.signingKeyPair,
137
+ });
138
+ }
72
139
  //# sourceMappingURL=commitCurrentGroupPolicyMutation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"commitCurrentGroupPolicyMutation.js","sourceRoot":"","sources":["commitCurrentGroupPolicyMutation.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n SigningKeyPair,\n VerifiedPrincipalPolicyCurrent,\n} from \"@tearleads/crypto\";\nimport type { PutPrincipalPolicyRequest } from \"@tearleads/validators/request\";\nimport type { PrincipalPolicyMutationResponse } from \"@tearleads/validators/response\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { prepareAuthoredGroupPolicy } from \"./groupPolicyMutationAcknowledgement\";\nimport {\n type PrincipalPolicyReadWriteApi,\n submitGroupPolicyCommit,\n} from \"./groupPolicyMutationContext\";\nimport { groupPolicyMutationHead } from \"./groupPolicyMutationHead\";\nimport {\n buildOrganizationGroupDirectoryPolicyRequest,\n replaceOrganizationGroupHead,\n} from \"./organizationGroupDirectory\";\nimport type { PreparedPrincipalContainerRematerializationBatch } from \"./principalContainerRematerialization\";\nimport type { RuntimeCurrentGroupMutationContext } from \"./runtimeCurrentGroupMutation\";\nimport {\n projectionUserIds,\n resolveRequiredUserIdentities,\n} from \"./trustedOrganizationUsers\";\n\n/** Commit and retain one exact compound successor without rebuilding full history. */\nexport async function commitCurrentGroupPolicyMutation(input: {\n readonly apiClient: PrincipalPolicyReadWriteApi;\n readonly context: RuntimeCurrentGroupMutationContext;\n readonly groupId: string;\n readonly organizationId: string;\n readonly request: PutPrincipalPolicyRequest;\n readonly signerUserId: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly prepareContainerMutations: (\n nextPolicy: VerifiedPrincipalPolicyCurrent,\n ) => Promise<PreparedPrincipalContainerRematerializationBatch>;\n}): Promise<PrincipalPolicyMutationResponse> {\n const { context } = input;\n assertProjectionVerificationCurrent(context.stillCurrent);\n const [signer] = await resolveRequiredUserIdentities({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n userIds: [input.signerUserId],\n });\n if (!signer) throw new Error(\"Group policy signer is unavailable\");\n const expectedHead = await groupPolicyMutationHead(input.request);\n const nextPolicy = await prepareAuthoredGroupPolicy({\n verifiedCurrentPolicy: context.verifiedCurrentPolicy,\n expectedHead,\n request: input.request,\n signerPublicKeys: [signer],\n externalAuthority: context.externalAuthority,\n localPolicyCheckpoint: context.localPolicyCheckpoint,\n stillCurrent: context.stillCurrent,\n });\n const prepared = await input.prepareContainerMutations(nextPolicy);\n input.request.containerMutations = [...prepared.requests];\n const adminProjection = context.isOrganizationAdminsGroup\n ? input.request.projection\n : context.adminCurrent.current.currentProjection;\n const adminUsers = await resolveRequiredUserIdentities({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n userIds: projectionUserIds(adminProjection),\n });\n const organizationRequest =\n await buildOrganizationGroupDirectoryPolicyRequest({\n adminProjection,\n adminUsers,\n currentPolicy: context.organizationCurrent.current,\n descriptor: context.organizationDescriptor,\n groupHeads: replaceOrganizationGroupHead({\n descriptor: context.organizationDescriptor,\n nextHead: expectedHead,\n }),\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n });\n assertProjectionVerificationCurrent(context.stillCurrent);\n const response = await submitGroupPolicyCommit({\n apiClient: input.apiClient,\n groupId: input.groupId,\n organizationId: input.organizationId,\n request: input.request,\n organizationRequest,\n carryDescendantRekeys: prepared.carry,\n stillCurrent: context.stillCurrent,\n });\n if (!response) throw new Error(\"Group policy update failed\");\n assertProjectionVerificationCurrent(context.stillCurrent);\n await context.retainAcknowledged({\n request: input.request,\n organizationRequest,\n response,\n retiredContainerIds: prepared.retiredContainerIds,\n });\n await prepared.acknowledge(\n response.groupPolicy.containerMutations,\n context.stillCurrent,\n );\n assertProjectionVerificationCurrent(context.stillCurrent);\n return response.groupPolicy;\n}\n"]}
1
+ {"version":3,"file":"commitCurrentGroupPolicyMutation.js","sourceRoot":"","sources":["commitCurrentGroupPolicyMutation.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n SigningKeyPair,\n VerifiedPrincipalPolicyCurrent,\n} from \"@tearleads/crypto\";\nimport type { PutPrincipalPolicyRequest } from \"@tearleads/validators/request\";\nimport type { PrincipalPolicyMutationResponse } from \"@tearleads/validators/response\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { prepareAuthoredGroupPolicy } from \"./groupPolicyMutationAcknowledgement\";\nimport {\n type PrincipalPolicyReadWriteApi,\n submitGroupPolicyCommit,\n} from \"./groupPolicyMutationContext\";\nimport { groupPolicyMutationHead } from \"./groupPolicyMutationHead\";\nimport {\n buildOrganizationGroupDirectoryPolicyRequest,\n replaceOrganizationGroupHead,\n} from \"./organizationGroupDirectory\";\nimport type { PreparedPrincipalContainerRematerializationBatch } from \"./principalContainerRematerialization\";\nimport type { PrincipalRematerializationPredecessor } from \"./principalContainerRematerializationPlanning\";\nimport { assertAuthoredPrincipalMutationReceipt } from \"./principalMutationReceipt\";\nimport type { RuntimeCurrentGroupMutationContext } from \"./runtimeCurrentGroupMutation\";\nimport {\n projectionUserIds,\n resolveRequiredUserIdentities,\n} from \"./trustedOrganizationUsers\";\n\ninterface GroupPolicyChange {\n readonly context: RuntimeCurrentGroupMutationContext;\n readonly request: PutPrincipalPolicyRequest;\n readonly prepareContainerMutations: (\n nextPolicy: VerifiedPrincipalPolicyCurrent,\n preceding?: PrincipalRematerializationPredecessor | undefined,\n ) => Promise<PreparedPrincipalContainerRematerializationBatch>;\n}\n\ninterface CommitInput extends GroupPolicyChange {\n readonly member?: GroupPolicyChange | undefined;\n readonly apiClient: PrincipalPolicyReadWriteApi;\n readonly groupId: string;\n readonly organizationId: string;\n readonly signerUserId: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}\n\n/** Commit and retain the group, optional roster enrollment and directory together. */\nexport async function commitCurrentGroupPolicyMutation(\n input: CommitInput,\n): Promise<PrincipalPolicyMutationResponse> {\n const { context } = input;\n assertProjectionVerificationCurrent(context.stillCurrent);\n const [signer] = await resolveRequiredUserIdentities({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n userIds: [input.signerUserId],\n });\n if (!signer) throw new Error(\"Group policy signer is unavailable\");\n const member = input.member\n ? await prepareChange(input.member, signer)\n : null;\n const { expectedHead, prepared } = await prepareChange(\n input,\n signer,\n member?.preceding,\n );\n const organizationRequest = await prepareDirectory(\n input,\n expectedHead,\n member?.expectedHead,\n );\n assertProjectionVerificationCurrent(context.stillCurrent);\n const response = await submitGroupPolicyCommit({\n apiClient: input.apiClient,\n groupId: input.groupId,\n organizationId: input.organizationId,\n request: input.request,\n organizationRequest,\n memberRequest: input.member?.request,\n // A compound retry must rebuild both batches' shared speculative heads.\n // Refuse the whole transaction if a damaged-key repair needs extra rekeys;\n // ordinary adds preserve key epochs and need no descendant carry retry.\n carryDescendantRekeys: member ? undefined : prepared.carry,\n stillCurrent: context.stillCurrent,\n });\n if (!response) throw new Error(\"Group policy update failed\");\n assertProjectionVerificationCurrent(context.stillCurrent);\n await assertAuthoredPrincipalMutationReceipt(\n {\n groupPolicy: input.request,\n organizationPolicy: organizationRequest,\n ...(input.member ? { memberPolicy: input.member.request } : {}),\n },\n response,\n );\n await context.retainAcknowledged({\n additionalGroups:\n input.member && response.memberPolicy\n ? [\n {\n ...input.member.context.groupRetention,\n request: input.member.request,\n response: response.memberPolicy,\n retiredContainerIds: member?.prepared.retiredContainerIds,\n },\n ]\n : [],\n request: input.request,\n organizationRequest,\n response,\n retiredContainerIds: prepared.retiredContainerIds,\n });\n if (member && response.memberPolicy) {\n await member.prepared.acknowledge(\n response.memberPolicy.containerMutations,\n context.stillCurrent,\n );\n }\n await prepared.acknowledge(\n response.groupPolicy.containerMutations,\n context.stillCurrent,\n );\n assertProjectionVerificationCurrent(context.stillCurrent);\n return response.groupPolicy;\n}\n\nasync function prepareChange(\n change: GroupPolicyChange,\n signer: Awaited<ReturnType<typeof resolveRequiredUserIdentities>>[number],\n preceding?: PrincipalRematerializationPredecessor,\n) {\n const expectedHead = await groupPolicyMutationHead(change.request);\n const nextPolicy = await prepareAuthoredGroupPolicy({\n verifiedCurrentPolicy: change.context.verifiedCurrentPolicy,\n expectedHead,\n request: change.request,\n signerPublicKeys: [signer],\n externalAuthority: change.context.externalAuthority,\n localPolicyCheckpoint: change.context.localPolicyCheckpoint,\n stillCurrent: change.context.stillCurrent,\n });\n const prepared = await change.prepareContainerMutations(\n nextPolicy,\n preceding,\n );\n change.request.containerMutations = [...prepared.requests];\n return {\n expectedHead,\n prepared,\n preceding: prepared.preceding\n ? {\n ...prepared.preceding,\n authored: [\n ...(preceding?.authored ?? []),\n {\n organizationId:\n change.context.organizationDescriptor.organizationId,\n policy: nextPolicy,\n dependencies: [\n change.context.organizationCurrent.policy,\n change.context.adminCurrent.policy,\n ],\n stillCurrent: change.context.stillCurrent,\n },\n ],\n }\n : undefined,\n };\n}\n\nasync function prepareDirectory(\n input: CommitInput,\n expectedHead: Awaited<ReturnType<typeof groupPolicyMutationHead>>,\n memberHead: Awaited<ReturnType<typeof groupPolicyMutationHead>> | undefined,\n) {\n const { context } = input;\n if (\n input.member &&\n (input.member.context.currentPolicy.currentState.principalId !==\n context.memberGroupId ||\n input.member.context.organizationCurrent.current.currentState\n .stateHash !==\n context.organizationCurrent.current.currentState.stateHash)\n )\n throw new Error(\n \"Roster and group changes must share the same organization policy\",\n );\n let groupHeads = replaceOrganizationGroupHead({\n descriptor: context.organizationDescriptor,\n nextHead: expectedHead,\n });\n if (memberHead)\n groupHeads = replaceOrganizationGroupHead({\n descriptor: { ...context.organizationDescriptor, groupHeads },\n nextHead: memberHead,\n });\n const adminProjection = context.isOrganizationAdminsGroup\n ? input.request.projection\n : context.adminCurrent.current.currentProjection;\n const adminUsers = await resolveRequiredUserIdentities({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n userIds: projectionUserIds(adminProjection),\n });\n return buildOrganizationGroupDirectoryPolicyRequest({\n adminProjection,\n adminUsers,\n currentPolicy: context.organizationCurrent.current,\n descriptor: context.organizationDescriptor,\n groupHeads,\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n });\n}\n"]}
@@ -1,41 +1,13 @@
1
- import type { RequestFailureKind } from "../../internal/api-client/index.js";
2
1
  import type { PrincipalPolicyCheckpoint, PrincipalPolicyExternalAuthority, ReferencedPrincipalHead, SigningKeyPair } from "../../internal/crypto/index.js";
3
- import type { CommitOrganizationGroupPolicyRequest, ContainerMutationRequest, CreateOrganizationGroupWithPolicyRequest, PutPrincipalPolicyRequest } from "../../internal/validators/request/index.js";
4
- import { type CommitOrganizationGroupPolicyResponse, type CreateOrganizationGroupResponse, type CurrentPrincipalMemberEnvelopesResponse, type PrincipalPolicyBundleResponse, type PrincipalPolicyMutationResponse } from "../../internal/validators/response/index.js";
2
+ import type { ContainerMutationRequest, PutPrincipalPolicyRequest } from "../../internal/validators/request/index.js";
3
+ import type { CurrentPrincipalMemberEnvelopesResponse, PrincipalPolicyBundleResponse, PrincipalPolicyMutationResponse } from "../../internal/validators/response/index.js";
5
4
  import type { ExecSql } from "../../data/sqlite/sqlSchema.js";
6
5
  import type { TrustedUserIdentityResolver } from "../../data/trustedUserIdentity/index.js";
7
6
  import { type VerifiedExternalAdminPolicy } from "../principals/externalAdminPolicy.js";
8
7
  import type { FullGroupMembershipMutationInput } from "./groupPolicyMutationEvidence.js";
9
- export interface PrincipalPolicyReadApi {
10
- /** Resolve authored work before reading the state for a new mutation. */
11
- readonly recoverPendingPrincipalMutation?: ((organizationId: string) => Promise<void>) | undefined;
12
- getCurrentPrincipalPolicy: (principalType: "group" | "organization", principalId: string) => Promise<PrincipalPolicyBundleResponse | null>;
13
- }
14
- export interface PrincipalPolicyReadWriteApi extends PrincipalPolicyReadApi {
15
- commitOrganizationGroupPolicy: (organizationId: string, groupId: string, input: CommitOrganizationGroupPolicyRequest) => Promise<CommitOrganizationGroupPolicyResponse | null>;
16
- /**
17
- * Optional status-bearing variant. A rematerialized rotation the server
18
- * refuses for stranding a granted path names the descendant rekeys the batch
19
- * must carry; without this the refusal is a plain failure.
20
- */
21
- commitOrganizationGroupPolicyResult?: (organizationId: string, groupId: string, input: CommitOrganizationGroupPolicyRequest, options?: {
22
- readonly reportErrors?: boolean | undefined;
23
- }) => Promise<{
24
- readonly ok: true;
25
- readonly data: CommitOrganizationGroupPolicyResponse;
26
- } | {
27
- readonly ok: false;
28
- readonly kind?: RequestFailureKind | undefined;
29
- readonly message?: string | undefined;
30
- readonly code?: string | undefined;
31
- readonly report?: (() => void) | undefined;
32
- readonly requiredContainerIds?: readonly string[] | undefined;
33
- readonly status: number | null;
34
- }>;
35
- }
36
- export interface OrganizationPrincipalPolicyApi extends PrincipalPolicyReadApi {
37
- createOrganizationGroup: (organizationId: string, input: CreateOrganizationGroupWithPolicyRequest) => Promise<CreateOrganizationGroupResponse | null>;
38
- }
8
+ import type { PrincipalPolicyReadApi, PrincipalPolicyReadWriteApi } from "./principalPolicyApi.js";
9
+ export type { OrganizationPrincipalPolicyApi, PrincipalPolicyReadApi, PrincipalPolicyReadWriteApi, } from "./principalPolicyApi.js";
10
+ export { submitGroupPolicyCommit } from "./submitGroupPolicyCommit.js";
39
11
  interface LoadedGroupPolicyMutationContext extends FullGroupMembershipMutationInput {
40
12
  readonly adminGroupId: string;
41
13
  readonly adminPolicyBundle: PrincipalPolicyBundleResponse;
@@ -66,24 +38,6 @@ export declare function loadGroupPolicyMutationContext(input: {
66
38
  readonly signingFingerprint: string;
67
39
  readonly signingKeyPair: SigningKeyPair;
68
40
  }): Promise<LoadedGroupPolicyMutationContext>;
69
- /**
70
- * Commit, answering one refusal: a rematerialized rekey or revoke that would
71
- * strand a level above a directly granted container is refused with the
72
- * descendant rekeys it must carry. The refused attempt rolled back whole, so
73
- * the served heads still stand; the batch is signed again with the named
74
- * levels woven in, replaces the container mutations, and the commit is retried
75
- * once. A second refusal means the tree moved underneath, and the caller's own
76
- * retry starts from a fresh policy.
77
- */
78
- export declare function submitGroupPolicyCommit(input: {
79
- readonly apiClient: PrincipalPolicyReadWriteApi;
80
- readonly carryDescendantRekeys?: ((requiredContainerIds: readonly string[]) => Promise<readonly ContainerMutationRequest[]>) | undefined;
81
- readonly groupId: string;
82
- readonly organizationId: string;
83
- readonly organizationRequest: PutPrincipalPolicyRequest;
84
- readonly request: PutPrincipalPolicyRequest;
85
- readonly stillCurrent?: (() => boolean) | undefined;
86
- }): Promise<CommitOrganizationGroupPolicyResponse | null>;
87
41
  export declare function commitGroupPolicyMutation(input: {
88
42
  readonly retiredContainerIds?: (() => readonly string[]) | undefined;
89
43
  readonly apiClient: PrincipalPolicyReadWriteApi;
@@ -102,4 +56,3 @@ export declare function commitGroupPolicyMutation(input: {
102
56
  readonly request: PutPrincipalPolicyRequest;
103
57
  readonly stillCurrent?: (() => boolean) | undefined;
104
58
  }): Promise<PrincipalPolicyBundleResponse & PrincipalPolicyMutationResponse>;
105
- export {};
@@ -1,4 +1,3 @@
1
- import { CONTAINER_MUTATION_ERROR_CODES, } from "../../internal/validators/response/index.js";
2
1
  import { persistVerifiedPrincipalPolicyBundlesAtomically } from "../../data/persistence/keyingCheckpointAdvancePersistence.js";
3
2
  import { retainLocallyAcknowledgedPrincipalPolicyBundles } from "../../data/persistence/locallyAcknowledgedCheckpointPersistence.js";
4
3
  import { requireOrganizationGroupHead } from "../../data/principals/organizationAuthorityDescriptor.js";
@@ -8,6 +7,8 @@ import { requireSignerCanManageGroup } from "./groupMutationAuthorization.js";
8
7
  import { acknowledgeGroupPolicyState, assertGroupPolicyEnvelopesMatchAcknowledgement, buildAcknowledgedGroupPolicyBundle, } from "./groupPolicyMutationAcknowledgement.js";
9
8
  import { assertPrincipalPolicyCurrentStateMatchesHead, groupPolicyMutationHead, } from "./groupPolicyMutationHead.js";
10
9
  import { collectGroupPolicySignerPublicKeys, prepareGroupPolicyVerification, verifyGroupPolicy, verifyGroupPolicyWithExternalOrganizationAdmins, } from "./groupPolicyVerification.js";
10
+ import { submitGroupPolicyCommit } from "./submitGroupPolicyCommit.js";
11
+ export { submitGroupPolicyCommit } from "./submitGroupPolicyCommit.js";
11
12
  export async function cacheGroupPolicy(input) {
12
13
  const bundle = await input.apiClient.getCurrentPrincipalPolicy("group", input.groupId);
13
14
  if (!bundle) {
@@ -111,70 +112,6 @@ export async function loadGroupPolicyMutationContext(input) {
111
112
  signingKeyPair: input.signingKeyPair,
112
113
  };
113
114
  }
114
- /**
115
- * Commit, answering one refusal: a rematerialized rekey or revoke that would
116
- * strand a level above a directly granted container is refused with the
117
- * descendant rekeys it must carry. The refused attempt rolled back whole, so
118
- * the served heads still stand; the batch is signed again with the named
119
- * levels woven in, replaces the container mutations, and the commit is retried
120
- * once. A second refusal means the tree moved underneath, and the caller's own
121
- * retry starts from a fresh policy.
122
- */
123
- export async function submitGroupPolicyCommit(input) {
124
- const body = () => ({
125
- groupPolicy: input.request,
126
- organizationPolicy: input.organizationRequest,
127
- });
128
- const { commitOrganizationGroupPolicyResult } = input.apiClient;
129
- if (!commitOrganizationGroupPolicyResult) {
130
- return input.apiClient.commitOrganizationGroupPolicy(input.organizationId, input.groupId, body());
131
- }
132
- const first = await commitOrganizationGroupPolicyResult.call(input.apiClient, input.organizationId, input.groupId, body(), { reportErrors: false });
133
- if (first.ok)
134
- return first.data;
135
- if (first.kind === "cancelled")
136
- return null;
137
- if (first.kind === "outcome-unknown")
138
- throw new Error(first.message ??
139
- "Policy request may have committed; refresh before retrying");
140
- if (!input.carryDescendantRekeys ||
141
- first.code !== CONTAINER_MUTATION_ERROR_CODES.descendantRekeysRequired ||
142
- !first.requiredContainerIds?.length) {
143
- first.report?.();
144
- return null;
145
- }
146
- let carried;
147
- try {
148
- carried = await input.carryDescendantRekeys(first.requiredContainerIds);
149
- }
150
- catch (error) {
151
- // The refusal was answerable, and the answer failed: surface both. Once
152
- // the generation that owned the attempt is gone, only the failure is
153
- // surfaced; a superseded refusal is not an error.
154
- if (input.stillCurrent?.() !== false)
155
- first.report?.();
156
- throw error;
157
- }
158
- if (input.stillCurrent?.() === false)
159
- return null;
160
- // Nothing signed means the same refusal again; report the one already had.
161
- if (carried.length === 0) {
162
- first.report?.();
163
- return null;
164
- }
165
- input.request.containerMutations = [...carried];
166
- const second = await commitOrganizationGroupPolicyResult.call(input.apiClient, input.organizationId, input.groupId, body(), { reportErrors: false });
167
- if (!second.ok) {
168
- if (second.kind === "cancelled")
169
- return null;
170
- if (second.kind === "outcome-unknown")
171
- throw new Error(second.message ??
172
- "Policy request may have committed; refresh before retrying");
173
- second.report?.();
174
- return null;
175
- }
176
- return second.data;
177
- }
178
115
  export async function commitGroupPolicyMutation(input) {
179
116
  const stored = await submitGroupPolicyCommit(input);
180
117
  if (!stored) {
@@ -1 +1 @@
1
- {"version":3,"file":"groupPolicyMutationContext.js","sourceRoot":"","sources":["groupPolicyMutationContext.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RequestFailureKind } from \"@tearleads/api-client\";\nimport type {\n PrincipalPolicyCheckpoint,\n PrincipalPolicyExternalAuthority,\n ReferencedPrincipalHead,\n SigningKeyPair,\n} from \"@tearleads/crypto\";\nimport type {\n CommitOrganizationGroupPolicyRequest,\n ContainerMutationRequest,\n CreateOrganizationGroupWithPolicyRequest,\n PutPrincipalPolicyRequest,\n} from \"@tearleads/validators/request\";\nimport {\n CONTAINER_MUTATION_ERROR_CODES,\n type CommitOrganizationGroupPolicyResponse,\n type CreateOrganizationGroupResponse,\n type CurrentPrincipalMemberEnvelopesResponse,\n type PrincipalPolicyBundleResponse,\n type PrincipalPolicyMutationResponse,\n} from \"@tearleads/validators/response\";\nimport { persistVerifiedPrincipalPolicyBundlesAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { retainLocallyAcknowledgedPrincipalPolicyBundles } from \"../../data/persistence/locallyAcknowledgedCheckpointPersistence\";\nimport { requireOrganizationGroupHead } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"../principals/externalAdminPolicy\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport { requireSignerCanManageGroup } from \"./groupMutationAuthorization\";\nimport {\n acknowledgeGroupPolicyState,\n assertGroupPolicyEnvelopesMatchAcknowledgement,\n buildAcknowledgedGroupPolicyBundle,\n} from \"./groupPolicyMutationAcknowledgement\";\nimport type { FullGroupMembershipMutationInput } from \"./groupPolicyMutationEvidence\";\nimport {\n assertPrincipalPolicyCurrentStateMatchesHead,\n groupPolicyMutationHead,\n} from \"./groupPolicyMutationHead\";\nimport {\n collectGroupPolicySignerPublicKeys,\n prepareGroupPolicyVerification,\n verifyGroupPolicy,\n verifyGroupPolicyWithExternalOrganizationAdmins,\n} from \"./groupPolicyVerification\";\n\nexport interface PrincipalPolicyReadApi {\n /** Resolve authored work before reading the state for a new mutation. */\n readonly recoverPendingPrincipalMutation?:\n | ((organizationId: string) => Promise<void>)\n | undefined;\n getCurrentPrincipalPolicy: (\n principalType: \"group\" | \"organization\",\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n}\n\nexport interface PrincipalPolicyReadWriteApi extends PrincipalPolicyReadApi {\n commitOrganizationGroupPolicy: (\n organizationId: string,\n groupId: string,\n input: CommitOrganizationGroupPolicyRequest,\n ) => Promise<CommitOrganizationGroupPolicyResponse | null>;\n /**\n * Optional status-bearing variant. A rematerialized rotation the server\n * refuses for stranding a granted path names the descendant rekeys the batch\n * must carry; without this the refusal is a plain failure.\n */\n commitOrganizationGroupPolicyResult?: (\n organizationId: string,\n groupId: string,\n input: CommitOrganizationGroupPolicyRequest,\n options?: { readonly reportErrors?: boolean | undefined },\n ) => Promise<\n | {\n readonly ok: true;\n readonly data: CommitOrganizationGroupPolicyResponse;\n }\n | {\n readonly ok: false;\n readonly kind?: RequestFailureKind | undefined;\n readonly message?: string | undefined;\n readonly code?: string | undefined;\n readonly report?: (() => void) | undefined;\n readonly requiredContainerIds?: readonly string[] | undefined;\n readonly status: number | null;\n }\n >;\n}\n\nexport interface OrganizationPrincipalPolicyApi extends PrincipalPolicyReadApi {\n createOrganizationGroup: (\n organizationId: string,\n input: CreateOrganizationGroupWithPolicyRequest,\n ) => Promise<CreateOrganizationGroupResponse | null>;\n}\n\ninterface LoadedGroupPolicyMutationContext\n extends FullGroupMembershipMutationInput {\n readonly adminGroupId: string;\n readonly adminPolicyBundle: PrincipalPolicyBundleResponse;\n readonly currentOrgAdminUserIds: readonly string[];\n readonly organizationDescriptor: VerifiedExternalAdminPolicy[\"descriptor\"];\n readonly organizationPolicyBundle: PrincipalPolicyBundleResponse;\n readonly memberGroupId: string;\n}\n\nexport async function cacheGroupPolicy(input: {\n readonly acknowledgedMemberEnvelopes?:\n | CurrentPrincipalMemberEnvelopesResponse\n | undefined;\n readonly apiClient: PrincipalPolicyReadApi;\n readonly externalAuthority?: PrincipalPolicyExternalAuthority | undefined;\n readonly execSql: ExecSql;\n readonly expectedCurrentHead?: ReferencedPrincipalHead | undefined;\n readonly groupId: string;\n readonly localPolicyCheckpoint?: PrincipalPolicyCheckpoint | null;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<PrincipalPolicyBundleResponse> {\n const bundle = await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n input.groupId,\n );\n\n if (!bundle) {\n throw new Error(\"Updated group policy could not be loaded\");\n }\n\n const signerPublicKeys = await collectGroupPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n const verified = await verifyGroupPolicy({\n currentPolicy: bundle,\n ...(input.externalAuthority\n ? { externalAuthority: input.externalAuthority }\n : {}),\n localPolicyCheckpoint: input.localPolicyCheckpoint ?? null,\n signerPublicKeys,\n });\n if (input.expectedCurrentHead) {\n assertPrincipalPolicyCurrentStateMatchesHead(\n bundle.currentState,\n input.expectedCurrentHead,\n );\n }\n if (input.acknowledgedMemberEnvelopes) {\n assertGroupPolicyEnvelopesMatchAcknowledgement(\n input.acknowledgedMemberEnvelopes,\n bundle.currentMemberEnvelopes,\n );\n }\n await persistVerifiedPrincipalPolicyBundlesAtomically({\n entries: [{ bundle, policy: verified }],\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: new Date().toISOString(),\n });\n return bundle;\n}\n\nexport async function loadGroupPolicyMutationContext(input: {\n readonly apiClient: PrincipalPolicyReadApi;\n readonly execSql: ExecSql;\n readonly groupId: string;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly signerUserId: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n}): Promise<LoadedGroupPolicyMutationContext> {\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\n const adminPolicy = await loadOrganizationExternalAdminPolicy({\n execSql: input.execSql,\n getCurrentPrincipalPolicy: (principalType, principalId) =>\n input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (!adminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const expectedGroupHead = requireOrganizationGroupHead(\n adminPolicy.descriptor,\n input.groupId,\n );\n const currentPolicy =\n input.groupId === adminPolicy.adminGroupId\n ? adminPolicy.adminBundle\n : await input.apiClient.getCurrentPrincipalPolicy(\"group\", input.groupId);\n if (!currentPolicy) {\n throw new Error(\"Group policy could not be loaded\");\n }\n assertPrincipalPolicyCurrentStateMatchesHead(\n currentPolicy.currentState,\n expectedGroupHead,\n );\n const verification = await prepareGroupPolicyVerification({\n currentPolicy,\n execSql: input.execSql,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n const isOrganizationAdminsGroup = adminPolicy.adminGroupId === input.groupId;\n const currentOrgAdminUserIds = adminPolicy.signerUserIds;\n const externalAuthority = adminPolicy.externalAuthority;\n if (!currentOrgAdminUserIds.includes(input.signerUserId)) {\n throw new Error(\"Organization admin authority is required\");\n }\n const verified = isOrganizationAdminsGroup\n ? await verifyGroupPolicy({\n currentPolicy,\n localPolicyCheckpoint: verification.localPolicyCheckpoint,\n signerPublicKeys: verification.currentPolicySignerPublicKeys,\n })\n : await verifyGroupPolicyWithExternalOrganizationAdmins({\n currentPolicy,\n loadExternalAuthority: async () => externalAuthority,\n localPolicyCheckpoint: verification.localPolicyCheckpoint,\n signerPublicKeys: verification.currentPolicySignerPublicKeys,\n });\n assertGroupMetadataBinding(currentPolicy, adminPolicy.descriptor);\n requireSignerCanManageGroup(\n currentPolicy,\n currentOrgAdminUserIds,\n input.signerUserId,\n );\n await persistVerifiedPrincipalPolicyBundlesAtomically({\n entries: [\n ...externalAdminPolicyPersistenceEntries(adminPolicy).filter(\n (entry) =>\n entry.policy.principalType !== verified.principalType ||\n entry.policy.principalId !== verified.principalId,\n ),\n { bundle: currentPolicy, policy: verified },\n ],\n execSql: input.execSql,\n organizationId: input.organizationId,\n updatedAt: new Date().toISOString(),\n });\n\n return {\n adminGroupId: adminPolicy.adminGroupId,\n adminPolicyBundle: adminPolicy.adminBundle,\n currentPolicy,\n currentOrgAdminUserIds,\n externalAuthority,\n isOrganizationAdminsGroup,\n memberGroupId: adminPolicy.memberGroupId,\n organizationDescriptor: adminPolicy.descriptor,\n organizationPolicyBundle: adminPolicy.bundle,\n ...verification,\n localPolicyCheckpoint: verified.checkpoint,\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n };\n}\n\n/**\n * Commit, answering one refusal: a rematerialized rekey or revoke that would\n * strand a level above a directly granted container is refused with the\n * descendant rekeys it must carry. The refused attempt rolled back whole, so\n * the served heads still stand; the batch is signed again with the named\n * levels woven in, replaces the container mutations, and the commit is retried\n * once. A second refusal means the tree moved underneath, and the caller's own\n * retry starts from a fresh policy.\n */\nexport async function submitGroupPolicyCommit(input: {\n readonly apiClient: PrincipalPolicyReadWriteApi;\n readonly carryDescendantRekeys?:\n | ((\n requiredContainerIds: readonly string[],\n ) => Promise<readonly ContainerMutationRequest[]>)\n | undefined;\n readonly groupId: string;\n readonly organizationId: string;\n readonly organizationRequest: PutPrincipalPolicyRequest;\n readonly request: PutPrincipalPolicyRequest;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<CommitOrganizationGroupPolicyResponse | null> {\n const body = () => ({\n groupPolicy: input.request,\n organizationPolicy: input.organizationRequest,\n });\n const { commitOrganizationGroupPolicyResult } = input.apiClient;\n if (!commitOrganizationGroupPolicyResult) {\n return input.apiClient.commitOrganizationGroupPolicy(\n input.organizationId,\n input.groupId,\n body(),\n );\n }\n const first = await commitOrganizationGroupPolicyResult.call(\n input.apiClient,\n input.organizationId,\n input.groupId,\n body(),\n { reportErrors: false },\n );\n if (first.ok) return first.data;\n if (first.kind === \"cancelled\") return null;\n if (first.kind === \"outcome-unknown\")\n throw new Error(\n first.message ??\n \"Policy request may have committed; refresh before retrying\",\n );\n if (\n !input.carryDescendantRekeys ||\n first.code !== CONTAINER_MUTATION_ERROR_CODES.descendantRekeysRequired ||\n !first.requiredContainerIds?.length\n ) {\n first.report?.();\n return null;\n }\n let carried: readonly ContainerMutationRequest[];\n try {\n carried = await input.carryDescendantRekeys(first.requiredContainerIds);\n } catch (error) {\n // The refusal was answerable, and the answer failed: surface both. Once\n // the generation that owned the attempt is gone, only the failure is\n // surfaced; a superseded refusal is not an error.\n if (input.stillCurrent?.() !== false) first.report?.();\n throw error;\n }\n if (input.stillCurrent?.() === false) return null;\n // Nothing signed means the same refusal again; report the one already had.\n if (carried.length === 0) {\n first.report?.();\n return null;\n }\n input.request.containerMutations = [...carried];\n const second = await commitOrganizationGroupPolicyResult.call(\n input.apiClient,\n input.organizationId,\n input.groupId,\n body(),\n { reportErrors: false },\n );\n if (!second.ok) {\n if (second.kind === \"cancelled\") return null;\n if (second.kind === \"outcome-unknown\")\n throw new Error(\n second.message ??\n \"Policy request may have committed; refresh before retrying\",\n );\n second.report?.();\n return null;\n }\n return second.data;\n}\n\nexport async function commitGroupPolicyMutation(input: {\n readonly retiredContainerIds?: (() => readonly string[]) | undefined;\n readonly apiClient: PrincipalPolicyReadWriteApi;\n readonly currentPolicy: PrincipalPolicyBundleResponse;\n readonly execSql: ExecSql;\n readonly expectedHead: ReferencedPrincipalHead;\n readonly groupId: string;\n readonly organizationId: string;\n readonly organizationPolicy: PrincipalPolicyBundleResponse;\n readonly organizationRequest: PutPrincipalPolicyRequest;\n /**\n * Re-sign the batch with the descendant rekeys a refusal named woven in; the\n * result replaces `containerMutations` and the commit is retried once.\n */\n readonly carryDescendantRekeys?:\n | ((\n requiredContainerIds: readonly string[],\n ) => Promise<readonly ContainerMutationRequest[]>)\n | undefined;\n readonly request: PutPrincipalPolicyRequest;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<PrincipalPolicyBundleResponse & PrincipalPolicyMutationResponse> {\n const stored = await submitGroupPolicyCommit(input);\n if (!stored) {\n throw new Error(\"Group policy update failed\");\n }\n const storedPolicy = stored.groupPolicy;\n const acknowledgedPolicy = await acknowledgeGroupPolicyState({\n currentPolicy: input.currentPolicy,\n expectedHead: input.expectedHead,\n request: input.request,\n response: storedPolicy.currentState,\n });\n const groupBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: input.currentPolicy,\n expectedHead: input.expectedHead,\n request: input.request,\n response: storedPolicy,\n });\n const organizationHead = await groupPolicyMutationHead(\n input.organizationRequest,\n );\n const acknowledgedOrganization = await acknowledgeGroupPolicyState({\n currentPolicy: input.organizationPolicy,\n expectedHead: organizationHead,\n request: input.organizationRequest,\n response: stored.organizationPolicy.currentState,\n });\n const organizationBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: input.organizationPolicy,\n expectedHead: organizationHead,\n request: input.organizationRequest,\n response: stored.organizationPolicy,\n });\n await retainLocallyAcknowledgedPrincipalPolicyBundles({\n retiredContainers: {\n containerIds: input.retiredContainerIds?.() ?? [],\n organizationId: input.organizationId,\n policy: acknowledgedPolicy,\n },\n entries: [\n { bundle: groupBundle, policy: acknowledgedPolicy },\n {\n bundle: organizationBundle,\n policy: acknowledgedOrganization,\n },\n ],\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: new Date().toISOString(),\n });\n return groupBundle;\n}\n"]}
1
+ {"version":3,"file":"groupPolicyMutationContext.js","sourceRoot":"","sources":["groupPolicyMutationContext.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n PrincipalPolicyCheckpoint,\n PrincipalPolicyExternalAuthority,\n ReferencedPrincipalHead,\n SigningKeyPair,\n} from \"@tearleads/crypto\";\nimport type {\n ContainerMutationRequest,\n PutPrincipalPolicyRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n CurrentPrincipalMemberEnvelopesResponse,\n PrincipalPolicyBundleResponse,\n PrincipalPolicyMutationResponse,\n} from \"@tearleads/validators/response\";\nimport { persistVerifiedPrincipalPolicyBundlesAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { retainLocallyAcknowledgedPrincipalPolicyBundles } from \"../../data/persistence/locallyAcknowledgedCheckpointPersistence\";\nimport { requireOrganizationGroupHead } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"../principals/externalAdminPolicy\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport { requireSignerCanManageGroup } from \"./groupMutationAuthorization\";\nimport {\n acknowledgeGroupPolicyState,\n assertGroupPolicyEnvelopesMatchAcknowledgement,\n buildAcknowledgedGroupPolicyBundle,\n} from \"./groupPolicyMutationAcknowledgement\";\nimport type { FullGroupMembershipMutationInput } from \"./groupPolicyMutationEvidence\";\nimport {\n assertPrincipalPolicyCurrentStateMatchesHead,\n groupPolicyMutationHead,\n} from \"./groupPolicyMutationHead\";\nimport {\n collectGroupPolicySignerPublicKeys,\n prepareGroupPolicyVerification,\n verifyGroupPolicy,\n verifyGroupPolicyWithExternalOrganizationAdmins,\n} from \"./groupPolicyVerification\";\nimport type {\n PrincipalPolicyReadApi,\n PrincipalPolicyReadWriteApi,\n} from \"./principalPolicyApi\";\nimport { submitGroupPolicyCommit } from \"./submitGroupPolicyCommit\";\n\nexport type {\n OrganizationPrincipalPolicyApi,\n PrincipalPolicyReadApi,\n PrincipalPolicyReadWriteApi,\n} from \"./principalPolicyApi\";\nexport { submitGroupPolicyCommit } from \"./submitGroupPolicyCommit\";\n\ninterface LoadedGroupPolicyMutationContext\n extends FullGroupMembershipMutationInput {\n readonly adminGroupId: string;\n readonly adminPolicyBundle: PrincipalPolicyBundleResponse;\n readonly currentOrgAdminUserIds: readonly string[];\n readonly organizationDescriptor: VerifiedExternalAdminPolicy[\"descriptor\"];\n readonly organizationPolicyBundle: PrincipalPolicyBundleResponse;\n readonly memberGroupId: string;\n}\n\nexport async function cacheGroupPolicy(input: {\n readonly acknowledgedMemberEnvelopes?:\n | CurrentPrincipalMemberEnvelopesResponse\n | undefined;\n readonly apiClient: PrincipalPolicyReadApi;\n readonly externalAuthority?: PrincipalPolicyExternalAuthority | undefined;\n readonly execSql: ExecSql;\n readonly expectedCurrentHead?: ReferencedPrincipalHead | undefined;\n readonly groupId: string;\n readonly localPolicyCheckpoint?: PrincipalPolicyCheckpoint | null;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<PrincipalPolicyBundleResponse> {\n const bundle = await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n input.groupId,\n );\n\n if (!bundle) {\n throw new Error(\"Updated group policy could not be loaded\");\n }\n\n const signerPublicKeys = await collectGroupPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n const verified = await verifyGroupPolicy({\n currentPolicy: bundle,\n ...(input.externalAuthority\n ? { externalAuthority: input.externalAuthority }\n : {}),\n localPolicyCheckpoint: input.localPolicyCheckpoint ?? null,\n signerPublicKeys,\n });\n if (input.expectedCurrentHead) {\n assertPrincipalPolicyCurrentStateMatchesHead(\n bundle.currentState,\n input.expectedCurrentHead,\n );\n }\n if (input.acknowledgedMemberEnvelopes) {\n assertGroupPolicyEnvelopesMatchAcknowledgement(\n input.acknowledgedMemberEnvelopes,\n bundle.currentMemberEnvelopes,\n );\n }\n await persistVerifiedPrincipalPolicyBundlesAtomically({\n entries: [{ bundle, policy: verified }],\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: new Date().toISOString(),\n });\n return bundle;\n}\n\nexport async function loadGroupPolicyMutationContext(input: {\n readonly apiClient: PrincipalPolicyReadApi;\n readonly execSql: ExecSql;\n readonly groupId: string;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly signerUserId: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n}): Promise<LoadedGroupPolicyMutationContext> {\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\n const adminPolicy = await loadOrganizationExternalAdminPolicy({\n execSql: input.execSql,\n getCurrentPrincipalPolicy: (principalType, principalId) =>\n input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (!adminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const expectedGroupHead = requireOrganizationGroupHead(\n adminPolicy.descriptor,\n input.groupId,\n );\n const currentPolicy =\n input.groupId === adminPolicy.adminGroupId\n ? adminPolicy.adminBundle\n : await input.apiClient.getCurrentPrincipalPolicy(\"group\", input.groupId);\n if (!currentPolicy) {\n throw new Error(\"Group policy could not be loaded\");\n }\n assertPrincipalPolicyCurrentStateMatchesHead(\n currentPolicy.currentState,\n expectedGroupHead,\n );\n const verification = await prepareGroupPolicyVerification({\n currentPolicy,\n execSql: input.execSql,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n const isOrganizationAdminsGroup = adminPolicy.adminGroupId === input.groupId;\n const currentOrgAdminUserIds = adminPolicy.signerUserIds;\n const externalAuthority = adminPolicy.externalAuthority;\n if (!currentOrgAdminUserIds.includes(input.signerUserId)) {\n throw new Error(\"Organization admin authority is required\");\n }\n const verified = isOrganizationAdminsGroup\n ? await verifyGroupPolicy({\n currentPolicy,\n localPolicyCheckpoint: verification.localPolicyCheckpoint,\n signerPublicKeys: verification.currentPolicySignerPublicKeys,\n })\n : await verifyGroupPolicyWithExternalOrganizationAdmins({\n currentPolicy,\n loadExternalAuthority: async () => externalAuthority,\n localPolicyCheckpoint: verification.localPolicyCheckpoint,\n signerPublicKeys: verification.currentPolicySignerPublicKeys,\n });\n assertGroupMetadataBinding(currentPolicy, adminPolicy.descriptor);\n requireSignerCanManageGroup(\n currentPolicy,\n currentOrgAdminUserIds,\n input.signerUserId,\n );\n await persistVerifiedPrincipalPolicyBundlesAtomically({\n entries: [\n ...externalAdminPolicyPersistenceEntries(adminPolicy).filter(\n (entry) =>\n entry.policy.principalType !== verified.principalType ||\n entry.policy.principalId !== verified.principalId,\n ),\n { bundle: currentPolicy, policy: verified },\n ],\n execSql: input.execSql,\n organizationId: input.organizationId,\n updatedAt: new Date().toISOString(),\n });\n\n return {\n adminGroupId: adminPolicy.adminGroupId,\n adminPolicyBundle: adminPolicy.adminBundle,\n currentPolicy,\n currentOrgAdminUserIds,\n externalAuthority,\n isOrganizationAdminsGroup,\n memberGroupId: adminPolicy.memberGroupId,\n organizationDescriptor: adminPolicy.descriptor,\n organizationPolicyBundle: adminPolicy.bundle,\n ...verification,\n localPolicyCheckpoint: verified.checkpoint,\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n };\n}\n\nexport async function commitGroupPolicyMutation(input: {\n readonly retiredContainerIds?: (() => readonly string[]) | undefined;\n readonly apiClient: PrincipalPolicyReadWriteApi;\n readonly currentPolicy: PrincipalPolicyBundleResponse;\n readonly execSql: ExecSql;\n readonly expectedHead: ReferencedPrincipalHead;\n readonly groupId: string;\n readonly organizationId: string;\n readonly organizationPolicy: PrincipalPolicyBundleResponse;\n readonly organizationRequest: PutPrincipalPolicyRequest;\n /**\n * Re-sign the batch with the descendant rekeys a refusal named woven in; the\n * result replaces `containerMutations` and the commit is retried once.\n */\n readonly carryDescendantRekeys?:\n | ((\n requiredContainerIds: readonly string[],\n ) => Promise<readonly ContainerMutationRequest[]>)\n | undefined;\n readonly request: PutPrincipalPolicyRequest;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<PrincipalPolicyBundleResponse & PrincipalPolicyMutationResponse> {\n const stored = await submitGroupPolicyCommit(input);\n if (!stored) {\n throw new Error(\"Group policy update failed\");\n }\n const storedPolicy = stored.groupPolicy;\n const acknowledgedPolicy = await acknowledgeGroupPolicyState({\n currentPolicy: input.currentPolicy,\n expectedHead: input.expectedHead,\n request: input.request,\n response: storedPolicy.currentState,\n });\n const groupBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: input.currentPolicy,\n expectedHead: input.expectedHead,\n request: input.request,\n response: storedPolicy,\n });\n const organizationHead = await groupPolicyMutationHead(\n input.organizationRequest,\n );\n const acknowledgedOrganization = await acknowledgeGroupPolicyState({\n currentPolicy: input.organizationPolicy,\n expectedHead: organizationHead,\n request: input.organizationRequest,\n response: stored.organizationPolicy.currentState,\n });\n const organizationBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: input.organizationPolicy,\n expectedHead: organizationHead,\n request: input.organizationRequest,\n response: stored.organizationPolicy,\n });\n await retainLocallyAcknowledgedPrincipalPolicyBundles({\n retiredContainers: {\n containerIds: input.retiredContainerIds?.() ?? [],\n organizationId: input.organizationId,\n policy: acknowledgedPolicy,\n },\n entries: [\n { bundle: groupBundle, policy: acknowledgedPolicy },\n {\n bundle: organizationBundle,\n policy: acknowledgedOrganization,\n },\n ],\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: new Date().toISOString(),\n });\n return groupBundle;\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import { rebaseOnDeepestAncestor } from "../containers/child/carriedDescendantRekeys.js";
2
+ import { withAuthoredPrincipalPolicies } from "./authoredPrincipalPolicyWarmer.js";
3
+ /** Plan the next group against container heads authored earlier in the same transaction. */
4
+ export function withPrecedingRematerialization(input) {
5
+ const preceding = input.preceding;
6
+ if (!preceding)
7
+ return input;
8
+ const rebase = (served) => {
9
+ const own = preceding.projections.findLast((projection) => projection.containerId === served.containerId);
10
+ const projection = own ?? served;
11
+ return (rebaseOnDeepestAncestor(projection, preceding.projections)?.projection ??
12
+ projection);
13
+ };
14
+ return {
15
+ ...input,
16
+ warmReferencedPrincipalPolicies: withAuthoredPrincipalPolicies(input.warmReferencedPrincipalPolicies, preceding.authored ?? []),
17
+ apiClient: {
18
+ ...input.apiClient,
19
+ reciteContainer: input.apiClient.reciteContainer.bind(input.apiClient),
20
+ getContainerWriterProjection: async (containerId) => {
21
+ const projection = await input.apiClient.getContainerWriterProjection(containerId);
22
+ return projection ? rebase(projection) : null;
23
+ },
24
+ getContainerWriterProjectionResult: async (containerId, options) => {
25
+ const result = await input.apiClient.getContainerWriterProjectionResult(containerId, options);
26
+ return result.ok ? { ...result, data: rebase(result.data) } : result;
27
+ },
28
+ },
29
+ };
30
+ }
31
+ //# sourceMappingURL=precedingPrincipalRematerialization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"precedingPrincipalRematerialization.js","sourceRoot":"","sources":["precedingPrincipalRematerialization.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,6CAA6C,CAAC;AACtF,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAGhF,4FAA4F;AAC5F,MAAM,UAAU,8BAA8B,CAC5C,KAA+C;IAE/C,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;IAClC,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,MAAM,MAAM,GAAG,CAAC,MAAyC,EAAE,EAAE;QAC3D,MAAM,GAAG,GAAG,SAAS,CAAC,WAAW,CAAC,QAAQ,CACxC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,WAAW,KAAK,MAAM,CAAC,WAAW,CAC9D,CAAC;QACF,MAAM,UAAU,GAAG,GAAG,IAAI,MAAM,CAAC;QACjC,OAAO,CACL,uBAAuB,CAAC,UAAU,EAAE,SAAS,CAAC,WAAW,CAAC,EAAE,UAAU;YACtE,UAAU,CACX,CAAC;IACJ,CAAC,CAAC;IACF,OAAO;QACL,GAAG,KAAK;QACR,+BAA+B,EAAE,6BAA6B,CAC5D,KAAK,CAAC,+BAA+B,EACrC,SAAS,CAAC,QAAQ,IAAI,EAAE,CACzB;QACD,SAAS,EAAE;YACT,GAAG,KAAK,CAAC,SAAS;YAClB,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;YACtE,4BAA4B,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE;gBAClD,MAAM,UAAU,GACd,MAAM,KAAK,CAAC,SAAS,CAAC,4BAA4B,CAAC,WAAW,CAAC,CAAC;gBAClE,OAAO,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAChD,CAAC;YACD,kCAAkC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE;gBACjE,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,kCAAkC,CACrE,WAAW,EACX,OAAO,CACR,CAAC;gBACF,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;YACvE,CAAC;SACF;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport { rebaseOnDeepestAncestor } from \"../containers/child/carriedDescendantRekeys\";\nimport { withAuthoredPrincipalPolicies } from \"./authoredPrincipalPolicyWarmer\";\nimport type { PrincipalContainerRematerializationInput } from \"./principalContainerRematerializationPlanning\";\n\n/** Plan the next group against container heads authored earlier in the same transaction. */\nexport function withPrecedingRematerialization(\n input: PrincipalContainerRematerializationInput,\n): PrincipalContainerRematerializationInput {\n const preceding = input.preceding;\n if (!preceding) return input;\n const rebase = (served: ContainerWriterProjectionResponse) => {\n const own = preceding.projections.findLast(\n (projection) => projection.containerId === served.containerId,\n );\n const projection = own ?? served;\n return (\n rebaseOnDeepestAncestor(projection, preceding.projections)?.projection ??\n projection\n );\n };\n return {\n ...input,\n warmReferencedPrincipalPolicies: withAuthoredPrincipalPolicies(\n input.warmReferencedPrincipalPolicies,\n preceding.authored ?? [],\n ),\n apiClient: {\n ...input.apiClient,\n reciteContainer: input.apiClient.reciteContainer.bind(input.apiClient),\n getContainerWriterProjection: async (containerId) => {\n const projection =\n await input.apiClient.getContainerWriterProjection(containerId);\n return projection ? rebase(projection) : null;\n },\n getContainerWriterProjectionResult: async (containerId, options) => {\n const result = await input.apiClient.getContainerWriterProjectionResult(\n containerId,\n options,\n );\n return result.ok ? { ...result, data: rebase(result.data) } : result;\n },\n },\n };\n}\n"]}