@tearleads/client-sdk 0.1.55 → 0.1.57

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 (55) hide show
  1. package/data/containers/shared/types.d.ts +1 -2
  2. package/data/containers/shared/types.js.map +1 -1
  3. package/data/persistence/organizations/organizationAccessRevocationPersistence.js +2 -2
  4. package/data/persistence/organizations/organizationAccessRevocationPersistence.js.map +1 -1
  5. package/data/persistence/organizations/organizationReadModelPersistence.js +2 -2
  6. package/data/persistence/organizations/organizationReadModelPersistence.js.map +1 -1
  7. package/data/principals/principalPolicyCoordinator.js +40 -0
  8. package/data/principals/principalPolicyCoordinator.js.map +1 -0
  9. package/package.json +1 -1
  10. package/workflows/container-contents/container-state/types.js.map +1 -1
  11. package/workflows/container-contents/remoteHydration/types.d.ts +5 -4
  12. package/workflows/container-contents/remoteHydration/types.js.map +1 -1
  13. package/workflows/containers/child/sharePrincipalPolicy.d.ts +3 -3
  14. package/workflows/containers/child/sharePrincipalPolicy.js +1 -1
  15. package/workflows/containers/child/sharePrincipalPolicy.js.map +1 -1
  16. package/workflows/organizations/deleteOrganizationGroup.js +1 -1
  17. package/workflows/organizations/deleteOrganizationGroup.js.map +1 -1
  18. package/workflows/organizations/groupCreation.js +1 -1
  19. package/workflows/organizations/groupCreation.js.map +1 -1
  20. package/workflows/organizations/groupNameUniqueness.js.map +1 -1
  21. package/workflows/organizations/groupPolicyMutationContext.js +1 -1
  22. package/workflows/organizations/groupPolicyMutationContext.js.map +1 -1
  23. package/workflows/organizations/organizationGroupNamePolicies.js +19 -5
  24. package/workflows/organizations/organizationGroupNamePolicies.js.map +1 -1
  25. package/workflows/organizations/principalMutationJournalManagement.js +3 -3
  26. package/workflows/organizations/principalMutationJournalManagement.js.map +1 -1
  27. package/workflows/organizations/principalMutationJournalSession.js +3 -3
  28. package/workflows/organizations/principalMutationJournalSession.js.map +1 -1
  29. package/workflows/organizations/principalPolicyApi.d.ts +3 -3
  30. package/workflows/organizations/principalPolicyApi.js.map +1 -1
  31. package/workflows/principals/currentPrincipalPolicyReader.d.ts +12 -0
  32. package/workflows/principals/currentPrincipalPolicyReader.js +2 -0
  33. package/workflows/principals/currentPrincipalPolicyReader.js.map +1 -0
  34. package/workflows/principals/externalAdminPolicy.d.ts +2 -1
  35. package/workflows/principals/externalAdminPolicy.js +3 -3
  36. package/workflows/principals/externalAdminPolicy.js.map +1 -1
  37. package/workflows/principals/policyCache.d.ts +3 -2
  38. package/workflows/principals/policyCache.js +9 -9
  39. package/workflows/principals/policyCache.js.map +1 -1
  40. package/workflows/principals/principalPolicyOrganizationScope.js +1 -1
  41. package/workflows/principals/principalPolicyOrganizationScope.js.map +1 -1
  42. package/workflows/principals/principalRecoveryMemoValidity.js +2 -2
  43. package/workflows/principals/principalRecoveryMemoValidity.js.map +1 -1
  44. package/workflows/principals/runtimePolicyRecovery.js +2 -2
  45. package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
  46. package/workflows/principals/runtimeProjectionPolicyRecovery.js +2 -2
  47. package/workflows/principals/runtimeProjectionPolicyRecovery.js.map +1 -1
  48. package/workflows/registration/provisionedPrincipalHistory.js +2 -2
  49. package/workflows/registration/provisionedPrincipalHistory.js.map +1 -1
  50. package/data/principals/principalRecoveryEpoch.js +0 -18
  51. package/data/principals/principalRecoveryEpoch.js.map +0 -1
  52. package/workflows/organizations/principalMutationJournalLane.js +0 -26
  53. package/workflows/organizations/principalMutationJournalLane.js.map +0 -1
  54. package/workflows/principals/principalRecoveryQueue.js +0 -22
  55. package/workflows/principals/principalRecoveryQueue.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  import type { AccessEvent, AccessManifest, ContainerAccessManifestState, ContainerCreateAccessEventBody, ContainerDirectGrant, ContainerGrantAccessEventBody, ContainerGrantPrincipalHead, ContainerKekKeyring, ContainerKekRecipientTarget, ContainerKeyEpoch, ContainerKeyWrap, ContainerMoveAccessEventBody, ContainerRekeyAccessEventBody, ContainerRevokeAccessEventBody, ContainerUserRecipientKey } from "../../../internal/crypto/index.js";
2
2
  import type { AccessManifestBundleWire, ContainerCreateWithMetadataDocumentRequest, ContainerMutationRequest, ContainerRotationRequest } from "../../../internal/validators/request/index.js";
3
- import type { ContainerCreateWithMetadataDocumentResponse, ContainerKekResponse, ContainerMutationResponse, ContainerRotationResponse, ContainerWriterProjectionResponse, PrincipalPolicyBundleResponse, ReferencedPrincipalStateResponse } from "../../../internal/validators/response/index.js";
3
+ import type { ContainerCreateWithMetadataDocumentResponse, ContainerKekResponse, ContainerMutationResponse, ContainerRotationResponse, ContainerWriterProjectionResponse, ReferencedPrincipalStateResponse } from "../../../internal/validators/response/index.js";
4
4
  import type { PrincipalPolicyCurrentEvidence } from "../../principals/principalPolicyEvidence.js";
5
5
  import type { ExecSql } from "../../sqlite/sqlSchema.js";
6
6
  import type { ContainerMutationRequestOptions } from "./mutationRequestOptions.js";
@@ -87,7 +87,6 @@ export interface ContainerCreateApi {
87
87
  createContainerWithMetadataDocumentResult(input: ContainerCreateWithMetadataDocumentRequest, options?: ContainerMutationRequestOptions): Promise<ContainerMutationSubmitResult<ContainerCreateWithMetadataDocumentResponse>>;
88
88
  evictContainerWriterProjection(containerId: string): void;
89
89
  getContainerWriterProjection(containerId: string): Promise<ContainerWriterProjectionResponse | null>;
90
- getCurrentPrincipalPolicy(principalType: "group" | "organization", principalId: string): Promise<PrincipalPolicyBundleResponse | null>;
91
90
  }
92
91
  export interface ContainerShareApi extends ContainerReciteApi {
93
92
  getContainerWriterProjection(containerId: string): Promise<ContainerWriterProjectionResponse | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AccessEvent,\n AccessManifest,\n ContainerAccessManifestState,\n ContainerCreateAccessEventBody,\n ContainerDirectGrant,\n ContainerGrantAccessEventBody,\n ContainerGrantPrincipalHead,\n ContainerKekKeyring,\n ContainerKekRecipientTarget,\n ContainerKeyEpoch,\n ContainerKeyWrap,\n ContainerMoveAccessEventBody,\n ContainerRekeyAccessEventBody,\n ContainerRevokeAccessEventBody,\n ContainerUserRecipientKey,\n} from \"@tearleads/crypto\";\nimport type {\n AccessManifestBundleWire,\n ContainerCreateWithMetadataDocumentRequest,\n ContainerMutationRequest,\n ContainerRotationRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n ContainerCreateWithMetadataDocumentResponse,\n ContainerKekResponse,\n ContainerMutationResponse,\n ContainerRotationResponse,\n ContainerWriterProjectionResponse,\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { PrincipalPolicyCurrentEvidence } from \"../../principals/principalPolicyEvidence\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport type { ContainerMutationRequestOptions } from \"./mutationRequestOptions\";\nimport type { ContainerReciteApi } from \"./reciteApi\";\n\nexport interface ContainerMutationAuthor {\n organizationId: string;\n signerDeviceId: string;\n signerKeyFingerprint: string;\n signerPrivateKey: Uint8Array;\n signerUserId: string;\n}\n\nexport interface BuildContainerCreatePlanInput {\n author: ContainerMutationAuthor;\n containerId?: string | undefined;\n containerKey: Uint8Array;\n containerKeyEpochId?: string | undefined;\n eventId?: string | undefined;\n /**\n * Optional group grant to bake into the\n * create manifest, wrapping the new container key to that principal in\n * addition to the normal parent-inheritance wrap. Used to birth a child\n * container that is directly readable by a group — e.g. the org public\n * metadata container granted to the reserved Members group. The principal's\n * verified policy must also be supplied via `principalPolicies` so the server\n * can derive the recipient target.\n */\n managedPrincipalGrant?:\n | {\n accessLevel: ContainerDirectGrant[\"accessLevel\"];\n principalEncapsulationPublicKey: string;\n principalHead: ContainerGrantPrincipalHead;\n }\n | undefined;\n metadataDocumentId?: string | undefined;\n systemSlot?: string | null | undefined;\n parentProjection: ContainerWriterProjectionResponse;\n principalPolicies?: readonly PrincipalPolicyCurrentEvidence[] | undefined;\n signedAt?: string | undefined;\n}\n\nexport interface ContainerCreatePlan {\n body: ContainerCreateAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyEpochHash: string;\n keyTargetHash: string;\n manifest: AccessManifest;\n manifestHash: string;\n metadataDocumentId: string;\n parentContainerId: string | null;\n parentManifestHash: string | null;\n recipientTargets: ContainerKekRecipientTarget[];\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerCreatePlan {\n containerKey: Uint8Array;\n plan: ContainerCreatePlan;\n}\n\nexport interface ContainerCreatePlanContext\n extends BuildContainerCreatePlanInput {\n containerKeyPublicKey: string;\n containerId: string;\n containerKeyEpochId: string;\n eventId: string;\n metadataDocumentId: string;\n parent: ParentContainerCreateContext;\n signedAt: string;\n}\n\nexport interface ContainerMutationSubmitFailure {\n readonly code?: string | undefined;\n readonly message: string;\n readonly ok: false;\n readonly report: () => void;\n readonly stalePrincipalHeads?:\n | readonly ReferencedPrincipalStateResponse[]\n | undefined;\n readonly status: number | null;\n}\n\nexport type ContainerMutationSubmitResult<T> =\n | {\n readonly data: T;\n readonly ok: true;\n }\n | ContainerMutationSubmitFailure;\n\nexport interface ContainerCreateApi {\n createContainerResult(\n input: ContainerMutationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerMutationSubmitResult<ContainerMutationResponse>>;\n createContainerWithMetadataDocumentResult(\n input: ContainerCreateWithMetadataDocumentRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<\n ContainerMutationSubmitResult<ContainerCreateWithMetadataDocumentResponse>\n >;\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n getCurrentPrincipalPolicy(\n principalType: \"group\" | \"organization\",\n principalId: string,\n ): Promise<PrincipalPolicyBundleResponse | null>;\n}\n\nexport interface ContainerShareApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n shareContainer(\n containerId: string,\n input: ContainerMutationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerMutationResponse | null>;\n}\n\n/**\n * A status-bearing rotation outcome. A refusal that names\n * `requiredContainerIds` is answerable: sign those descendant rekeys and\n * resubmit with them carried.\n */\nexport type ContainerRotationResult =\n | { readonly ok: true; readonly data: ContainerRotationResponse }\n | {\n readonly ok: false;\n readonly code?: string | undefined;\n /** Surface a failure that was submitted with `reportErrors: false`. */\n readonly report?: (() => void) | undefined;\n readonly requiredContainerIds?: readonly string[] | undefined;\n readonly status: number | null;\n };\n\nexport interface ContainerMoveApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n moveContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; without it nothing can be carried. */\n moveContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface ContainerRevokeApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n revokeContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; without it nothing can be carried. */\n revokeContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface CreateRemoteContainerResult {\n containerKey: Uint8Array;\n containerId: string;\n metadataDocumentId: string;\n plan: ContainerCreatePlan;\n response: ContainerMutationResponse;\n}\n\nexport interface ContainerSharePlan {\n body: ContainerGrantAccessEventBody;\n /** The unchanged epoch's stored keyring, echoed back by the server. */\n previousKeyring?: Record<string, unknown> | null | undefined;\n containerId: string;\n event: AccessEvent;\n eventHash: string;\n grant: ContainerDirectGrant;\n keyEpoch: ContainerKeyEpoch;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n recipientTarget: ContainerKekRecipientTarget;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerSharePlan {\n containerKey: Uint8Array;\n plan: ContainerSharePlan;\n}\n\nexport interface ContainerRevokePlan {\n body: ContainerRevokeAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerRevokePlan {\n containerKey: Uint8Array;\n plan: ContainerRevokePlan;\n}\n\nexport interface ContainerRekeyPlan {\n body: ContainerRekeyAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerRekeyPlan {\n containerKey: Uint8Array;\n plan: ContainerRekeyPlan;\n writerProjection: ContainerWriterProjectionResponse;\n}\n\nexport interface ContainerRekeyApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n rekeyContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; see DocumentSyncApi. */\n rekeyContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface ContainerMovePlan {\n body: ContainerMoveAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerMovePlan {\n containerKey: Uint8Array;\n plan: ContainerMovePlan;\n}\n\nexport interface BuildMaterializedContainerMovePlanInput {\n author: ContainerMutationAuthor;\n containerKeyEpochId?: string | undefined;\n destinationParentProjection: ContainerWriterProjectionResponse;\n eventId?: string | undefined;\n execSql: ExecSql;\n previousProjection: ContainerWriterProjectionResponse;\n signedAt?: string | undefined;\n targetSecretKey: Uint8Array;\n}\n\nexport interface ParentContainerCreateContext {\n manifest: ContainerWriterProjectionResponse[\"path\"][number];\n kek: ContainerKekResponse;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AccessEvent,\n AccessManifest,\n ContainerAccessManifestState,\n ContainerCreateAccessEventBody,\n ContainerDirectGrant,\n ContainerGrantAccessEventBody,\n ContainerGrantPrincipalHead,\n ContainerKekKeyring,\n ContainerKekRecipientTarget,\n ContainerKeyEpoch,\n ContainerKeyWrap,\n ContainerMoveAccessEventBody,\n ContainerRekeyAccessEventBody,\n ContainerRevokeAccessEventBody,\n ContainerUserRecipientKey,\n} from \"@tearleads/crypto\";\nimport type {\n AccessManifestBundleWire,\n ContainerCreateWithMetadataDocumentRequest,\n ContainerMutationRequest,\n ContainerRotationRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n ContainerCreateWithMetadataDocumentResponse,\n ContainerKekResponse,\n ContainerMutationResponse,\n ContainerRotationResponse,\n ContainerWriterProjectionResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { PrincipalPolicyCurrentEvidence } from \"../../principals/principalPolicyEvidence\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport type { ContainerMutationRequestOptions } from \"./mutationRequestOptions\";\nimport type { ContainerReciteApi } from \"./reciteApi\";\n\nexport interface ContainerMutationAuthor {\n organizationId: string;\n signerDeviceId: string;\n signerKeyFingerprint: string;\n signerPrivateKey: Uint8Array;\n signerUserId: string;\n}\n\nexport interface BuildContainerCreatePlanInput {\n author: ContainerMutationAuthor;\n containerId?: string | undefined;\n containerKey: Uint8Array;\n containerKeyEpochId?: string | undefined;\n eventId?: string | undefined;\n /**\n * Optional group grant to bake into the\n * create manifest, wrapping the new container key to that principal in\n * addition to the normal parent-inheritance wrap. Used to birth a child\n * container that is directly readable by a group — e.g. the org public\n * metadata container granted to the reserved Members group. The principal's\n * verified policy must also be supplied via `principalPolicies` so the server\n * can derive the recipient target.\n */\n managedPrincipalGrant?:\n | {\n accessLevel: ContainerDirectGrant[\"accessLevel\"];\n principalEncapsulationPublicKey: string;\n principalHead: ContainerGrantPrincipalHead;\n }\n | undefined;\n metadataDocumentId?: string | undefined;\n systemSlot?: string | null | undefined;\n parentProjection: ContainerWriterProjectionResponse;\n principalPolicies?: readonly PrincipalPolicyCurrentEvidence[] | undefined;\n signedAt?: string | undefined;\n}\n\nexport interface ContainerCreatePlan {\n body: ContainerCreateAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyEpochHash: string;\n keyTargetHash: string;\n manifest: AccessManifest;\n manifestHash: string;\n metadataDocumentId: string;\n parentContainerId: string | null;\n parentManifestHash: string | null;\n recipientTargets: ContainerKekRecipientTarget[];\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerCreatePlan {\n containerKey: Uint8Array;\n plan: ContainerCreatePlan;\n}\n\nexport interface ContainerCreatePlanContext\n extends BuildContainerCreatePlanInput {\n containerKeyPublicKey: string;\n containerId: string;\n containerKeyEpochId: string;\n eventId: string;\n metadataDocumentId: string;\n parent: ParentContainerCreateContext;\n signedAt: string;\n}\n\nexport interface ContainerMutationSubmitFailure {\n readonly code?: string | undefined;\n readonly message: string;\n readonly ok: false;\n readonly report: () => void;\n readonly stalePrincipalHeads?:\n | readonly ReferencedPrincipalStateResponse[]\n | undefined;\n readonly status: number | null;\n}\n\nexport type ContainerMutationSubmitResult<T> =\n | {\n readonly data: T;\n readonly ok: true;\n }\n | ContainerMutationSubmitFailure;\n\nexport interface ContainerCreateApi {\n createContainerResult(\n input: ContainerMutationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerMutationSubmitResult<ContainerMutationResponse>>;\n createContainerWithMetadataDocumentResult(\n input: ContainerCreateWithMetadataDocumentRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<\n ContainerMutationSubmitResult<ContainerCreateWithMetadataDocumentResponse>\n >;\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n}\n\nexport interface ContainerShareApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n shareContainer(\n containerId: string,\n input: ContainerMutationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerMutationResponse | null>;\n}\n\n/**\n * A status-bearing rotation outcome. A refusal that names\n * `requiredContainerIds` is answerable: sign those descendant rekeys and\n * resubmit with them carried.\n */\nexport type ContainerRotationResult =\n | { readonly ok: true; readonly data: ContainerRotationResponse }\n | {\n readonly ok: false;\n readonly code?: string | undefined;\n /** Surface a failure that was submitted with `reportErrors: false`. */\n readonly report?: (() => void) | undefined;\n readonly requiredContainerIds?: readonly string[] | undefined;\n readonly status: number | null;\n };\n\nexport interface ContainerMoveApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n moveContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; without it nothing can be carried. */\n moveContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface ContainerRevokeApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n revokeContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; without it nothing can be carried. */\n revokeContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface CreateRemoteContainerResult {\n containerKey: Uint8Array;\n containerId: string;\n metadataDocumentId: string;\n plan: ContainerCreatePlan;\n response: ContainerMutationResponse;\n}\n\nexport interface ContainerSharePlan {\n body: ContainerGrantAccessEventBody;\n /** The unchanged epoch's stored keyring, echoed back by the server. */\n previousKeyring?: Record<string, unknown> | null | undefined;\n containerId: string;\n event: AccessEvent;\n eventHash: string;\n grant: ContainerDirectGrant;\n keyEpoch: ContainerKeyEpoch;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n recipientTarget: ContainerKekRecipientTarget;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerSharePlan {\n containerKey: Uint8Array;\n plan: ContainerSharePlan;\n}\n\nexport interface ContainerRevokePlan {\n body: ContainerRevokeAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerRevokePlan {\n containerKey: Uint8Array;\n plan: ContainerRevokePlan;\n}\n\nexport interface ContainerRekeyPlan {\n body: ContainerRekeyAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerRekeyPlan {\n containerKey: Uint8Array;\n plan: ContainerRekeyPlan;\n writerProjection: ContainerWriterProjectionResponse;\n}\n\nexport interface ContainerRekeyApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n rekeyContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; see DocumentSyncApi. */\n rekeyContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface ContainerMovePlan {\n body: ContainerMoveAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerMovePlan {\n containerKey: Uint8Array;\n plan: ContainerMovePlan;\n}\n\nexport interface BuildMaterializedContainerMovePlanInput {\n author: ContainerMutationAuthor;\n containerKeyEpochId?: string | undefined;\n destinationParentProjection: ContainerWriterProjectionResponse;\n eventId?: string | undefined;\n execSql: ExecSql;\n previousProjection: ContainerWriterProjectionResponse;\n signedAt?: string | undefined;\n targetSecretKey: Uint8Array;\n}\n\nexport interface ParentContainerCreateContext {\n manifest: ContainerWriterProjectionResponse[\"path\"][number];\n kek: ContainerKekResponse;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { and, eq } from "drizzle-orm";
2
- import { invalidatePrincipalRecovery } from "../../principals/principalRecoveryEpoch.js";
2
+ import { getOrCreatePrincipalPolicyCoordinator } from "../../principals/principalPolicyCoordinator.js";
3
3
  import { organizationDataUsageTables } from "../../sqlite/organizationDataUsageSchema.js";
4
4
  import { organizationReadModelRequesters } from "../../sqlite/organizationReadModelSchema.js";
5
5
  import { organizationReadModelTables } from "../../sqlite/schema.js";
@@ -44,6 +44,6 @@ export async function purgeOrganizationAccessProjection(input) {
44
44
  });
45
45
  // A denial is a realtime-hinted access change: in-flight operations recover
46
46
  // the organization's directory again instead of reusing their memo.
47
- invalidatePrincipalRecovery(input.execSql, input.organizationId);
47
+ getOrCreatePrincipalPolicyCoordinator(input.execSql).invalidateRecovery(input.organizationId);
48
48
  }
49
49
  //# sourceMappingURL=organizationAccessRevocationPersistence.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"organizationAccessRevocationPersistence.js","sourceRoot":"","sources":["organizationAccessRevocationPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,yCAAyC,CAAC;AACtF,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,EAAE,+BAA+B,EAAE,MAAM,0CAA0C,CAAC;AAC3F,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAE1F,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,iDAAiD,EAAE,MAAM,oCAAoC,CAAC;AACvG,OAAO,EAAE,iDAAiD,EAAE,MAAM,8BAA8B,CAAC;AAEjG,MAAM,kCAAkC,GAAG;IACzC,GAAG,2BAA2B;IAC9B,GAAG,2BAA2B;CAC/B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC,CAAC,KAIvD;IACC,MAAM,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,kCAAkC,CAAC,CAAC;IACzE,MAAM,iCAAiC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,WAAW,CAChE,KAAK,EAAE,EAAE,EAAE,EAAE;QACX,MAAM,EAAE;aACL,MAAM,CAAC,+BAA+B,CAAC;aACvC,KAAK,CACJ,GAAG,CACD,EAAE,CACA,+BAA+B,CAAC,cAAc,EAC9C,KAAK,CAAC,cAAc,CACrB,EACD,EAAE,CAAC,+BAA+B,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAClE,CACF;aACA,GAAG,EAAE,CAAC;QACT,MAAM,CAAC,kBAAkB,CAAC,GAAG,MAAM,EAAE;aAClC,MAAM,CAAC,EAAE,MAAM,EAAE,+BAA+B,CAAC,MAAM,EAAE,CAAC;aAC1D,IAAI,CAAC,+BAA+B,CAAC;aACrC,KAAK,CACJ,EAAE,CACA,+BAA+B,CAAC,cAAc,EAC9C,KAAK,CAAC,cAAc,CACrB,CACF;aACA,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxB,MAAM,iDAAiD,CAAC;gBACtD,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,EAAE;aACH,CAAC,CAAC;QACL,CAAC;QACD,MAAM,iDAAiD,CAAC;YACtD,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,EAAE;SACH,CAAC,CAAC;IACL,CAAC,CACF,CAAC;IACF,4EAA4E;IAC5E,oEAAoE;IACpE,2BAA2B,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;AACnE,CAAC","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { invalidatePrincipalRecovery } from \"../../principals/principalRecoveryEpoch\";\nimport { organizationDataUsageTables } from \"../../sqlite/organizationDataUsageSchema\";\nimport { organizationReadModelRequesters } from \"../../sqlite/organizationReadModelSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { ensureSqlTables } from \"../../sqlite/sqlTableSchema\";\nimport { purgeOrganizationDataUsageProjectionInTransaction } from \"./organizationDataUsagePersistence\";\nimport { purgeOrganizationReadModelProjectionInTransaction } from \"./organizationReadModelPurge\";\n\nconst organizationAccessProjectionTables = [\n ...organizationReadModelTables,\n ...organizationDataUsageTables,\n];\n\n/**\n * Purge one requester's access to an organization's presentation projections\n * after the server denied that requester. The denial is requester-scoped: the\n * shared read-model rows stay while another local identity in the same\n * database still holds a requester row, and organization-wide rows fall with\n * the last requester.\n */\nexport async function purgeOrganizationAccessProjection(input: {\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly requesterUserId: string;\n}): Promise<void> {\n await ensureSqlTables(input.execSql, organizationAccessProjectionTables);\n await getClientSQLitePersistenceRuntime(input.execSql).transaction(\n async (tx) => {\n await tx\n .delete(organizationReadModelRequesters)\n .where(\n and(\n eq(\n organizationReadModelRequesters.organizationId,\n input.organizationId,\n ),\n eq(organizationReadModelRequesters.userId, input.requesterUserId),\n ),\n )\n .run();\n const [remainingRequester] = await tx\n .select({ userId: organizationReadModelRequesters.userId })\n .from(organizationReadModelRequesters)\n .where(\n eq(\n organizationReadModelRequesters.organizationId,\n input.organizationId,\n ),\n )\n .limit(1);\n if (!remainingRequester) {\n await purgeOrganizationReadModelProjectionInTransaction({\n organizationId: input.organizationId,\n tx,\n });\n }\n await purgeOrganizationDataUsageProjectionInTransaction({\n organizationId: input.organizationId,\n requesterUserId: input.requesterUserId,\n tx,\n });\n },\n );\n // A denial is a realtime-hinted access change: in-flight operations recover\n // the organization's directory again instead of reusing their memo.\n invalidatePrincipalRecovery(input.execSql, input.organizationId);\n}\n"]}
1
+ {"version":3,"file":"organizationAccessRevocationPersistence.js","sourceRoot":"","sources":["organizationAccessRevocationPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,qCAAqC,EAAE,MAAM,6CAA6C,CAAC;AACpG,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,EAAE,+BAA+B,EAAE,MAAM,0CAA0C,CAAC;AAC3F,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAE1F,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,iDAAiD,EAAE,MAAM,oCAAoC,CAAC;AACvG,OAAO,EAAE,iDAAiD,EAAE,MAAM,8BAA8B,CAAC;AAEjG,MAAM,kCAAkC,GAAG;IACzC,GAAG,2BAA2B;IAC9B,GAAG,2BAA2B;CAC/B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC,CAAC,KAIvD;IACC,MAAM,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,kCAAkC,CAAC,CAAC;IACzE,MAAM,iCAAiC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,WAAW,CAChE,KAAK,EAAE,EAAE,EAAE,EAAE;QACX,MAAM,EAAE;aACL,MAAM,CAAC,+BAA+B,CAAC;aACvC,KAAK,CACJ,GAAG,CACD,EAAE,CACA,+BAA+B,CAAC,cAAc,EAC9C,KAAK,CAAC,cAAc,CACrB,EACD,EAAE,CAAC,+BAA+B,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAClE,CACF;aACA,GAAG,EAAE,CAAC;QACT,MAAM,CAAC,kBAAkB,CAAC,GAAG,MAAM,EAAE;aAClC,MAAM,CAAC,EAAE,MAAM,EAAE,+BAA+B,CAAC,MAAM,EAAE,CAAC;aAC1D,IAAI,CAAC,+BAA+B,CAAC;aACrC,KAAK,CACJ,EAAE,CACA,+BAA+B,CAAC,cAAc,EAC9C,KAAK,CAAC,cAAc,CACrB,CACF;aACA,KAAK,CAAC,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxB,MAAM,iDAAiD,CAAC;gBACtD,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,EAAE;aACH,CAAC,CAAC;QACL,CAAC;QACD,MAAM,iDAAiD,CAAC;YACtD,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,EAAE;SACH,CAAC,CAAC;IACL,CAAC,CACF,CAAC;IACF,4EAA4E;IAC5E,oEAAoE;IACpE,qCAAqC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,kBAAkB,CACrE,KAAK,CAAC,cAAc,CACrB,CAAC;AACJ,CAAC","sourcesContent":["import { and, eq } from \"drizzle-orm\";\nimport { getOrCreatePrincipalPolicyCoordinator } from \"../../principals/principalPolicyCoordinator\";\nimport { organizationDataUsageTables } from \"../../sqlite/organizationDataUsageSchema\";\nimport { organizationReadModelRequesters } from \"../../sqlite/organizationReadModelSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport { ensureSqlTables } from \"../../sqlite/sqlTableSchema\";\nimport { purgeOrganizationDataUsageProjectionInTransaction } from \"./organizationDataUsagePersistence\";\nimport { purgeOrganizationReadModelProjectionInTransaction } from \"./organizationReadModelPurge\";\n\nconst organizationAccessProjectionTables = [\n ...organizationReadModelTables,\n ...organizationDataUsageTables,\n];\n\n/**\n * Purge one requester's access to an organization's presentation projections\n * after the server denied that requester. The denial is requester-scoped: the\n * shared read-model rows stay while another local identity in the same\n * database still holds a requester row, and organization-wide rows fall with\n * the last requester.\n */\nexport async function purgeOrganizationAccessProjection(input: {\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly requesterUserId: string;\n}): Promise<void> {\n await ensureSqlTables(input.execSql, organizationAccessProjectionTables);\n await getClientSQLitePersistenceRuntime(input.execSql).transaction(\n async (tx) => {\n await tx\n .delete(organizationReadModelRequesters)\n .where(\n and(\n eq(\n organizationReadModelRequesters.organizationId,\n input.organizationId,\n ),\n eq(organizationReadModelRequesters.userId, input.requesterUserId),\n ),\n )\n .run();\n const [remainingRequester] = await tx\n .select({ userId: organizationReadModelRequesters.userId })\n .from(organizationReadModelRequesters)\n .where(\n eq(\n organizationReadModelRequesters.organizationId,\n input.organizationId,\n ),\n )\n .limit(1);\n if (!remainingRequester) {\n await purgeOrganizationReadModelProjectionInTransaction({\n organizationId: input.organizationId,\n tx,\n });\n }\n await purgeOrganizationDataUsageProjectionInTransaction({\n organizationId: input.organizationId,\n requesterUserId: input.requesterUserId,\n tx,\n });\n },\n );\n // A denial is a realtime-hinted access change: in-flight operations recover\n // the organization's directory again instead of reusing their memo.\n getOrCreatePrincipalPolicyCoordinator(input.execSql).invalidateRecovery(\n input.organizationId,\n );\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { eq } from "drizzle-orm";
2
- import { invalidatePrincipalRecovery } from "../../principals/principalRecoveryEpoch.js";
2
+ import { getOrCreatePrincipalPolicyCoordinator } from "../../principals/principalPolicyCoordinator.js";
3
3
  import { organizationDataUsageTables } from "../../sqlite/organizationDataUsageSchema.js";
4
4
  import { organizationReadModelDirectoryUsers, organizationReadModelGroups, organizationReadModelRequesters, organizationReadModelState, } from "../../sqlite/organizationReadModelSchema.js";
5
5
  import { organizationReadModelTables } from "../../sqlite/schema.js";
@@ -313,7 +313,7 @@ export async function applyOrganizationReadModelResponse(input) {
313
313
  // An applied change follows an organization hint or mutation; in-flight
314
314
  // operations recover the directory again instead of reusing their memo.
315
315
  if (disposition === "applied")
316
- invalidatePrincipalRecovery(input.execSql, input.response.organizationId);
316
+ getOrCreatePrincipalPolicyCoordinator(input.execSql).invalidateRecovery(input.response.organizationId);
317
317
  return disposition;
318
318
  }
319
319
  export async function purgeOrganizationReadModelProjection(execSql, organizationId, options = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"organizationReadModelPersistence.js","sourceRoot":"","sources":["organizationReadModelPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ListOrganizationGroupsResponse,\n OrganizationGroupCurrentStateResponse,\n OrganizationReadModelDirectoryResponse,\n OrganizationReadModelResponse,\n} from \"@tearleads/validators/response\";\nimport { eq } from \"drizzle-orm\";\nimport { invalidatePrincipalRecovery } from \"../../principals/principalRecoveryEpoch\";\nimport { organizationDataUsageTables } from \"../../sqlite/organizationDataUsageSchema\";\nimport {\n organizationReadModelDirectoryUsers,\n organizationReadModelGroups,\n organizationReadModelRequesters,\n organizationReadModelState,\n} from \"../../sqlite/organizationReadModelSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/schema\";\nimport { sqliteInsertBatchSize } from \"../../sqlite/sqliteBindBudget\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport { clearOrganizationPresentationDenialInTransaction } from \"./organizationPresentationDenialPersistence\";\nimport { applyOrganizationReadModelGrantsLane } from \"./organizationReadModelGrantPersistence\";\nimport {\n applyGroupMembershipsLane,\n assertStoredGroupMembershipBindings,\n OrganizationReadModelBindingError,\n} from \"./organizationReadModelMembershipPersistence\";\nimport {\n loadOrganizationReadModelProjectionInTransaction,\n type OrganizationReadModelProjection,\n} from \"./organizationReadModelProjection\";\nimport {\n loadWithOrganizationReadModelIntegrityPurge,\n pruneInactiveOrganizationRequestersInTransaction,\n purgeOrganizationReadModelProjectionInTransaction,\n} from \"./organizationReadModelPurge\";\nimport { assertOrganizationReadModelResponse } from \"./organizationReadModelResponseAssertions\";\n\nexport type { OrganizationReadModelProjection } from \"./organizationReadModelProjection\";\n\nconst DIRECTORY_INSERT_BATCH_SIZE = sqliteInsertBatchSize(\n organizationReadModelDirectoryUsers,\n);\nconst GROUP_INSERT_BATCH_SIZE = sqliteInsertBatchSize(\n organizationReadModelGroups,\n);\n\ntype ApplyOrganizationReadModelResponseResult =\n | \"already-applied\"\n | \"applied\"\n | \"stale\";\n\ninterface CurrentOrganizationReadModelState {\n readonly cursor: string;\n readonly memberGroupId: string;\n readonly memberGroupStateHash: string;\n readonly organizationHead: OrganizationGroupCurrentStateResponse;\n readonly profileDocumentId: string | null;\n}\n\nasync function replaceDirectoryLane(input: {\n readonly directory: OrganizationReadModelDirectoryResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await input.tx\n .delete(organizationReadModelDirectoryUsers)\n .where(\n eq(\n organizationReadModelDirectoryUsers.organizationId,\n input.organizationId,\n ),\n )\n .run();\n\n const rows = input.directory.users.map((user, sortOrder) => ({\n organizationId: input.organizationId,\n userId: user.userId,\n sortOrder,\n signingKeyFingerprint: user.signingKeyFingerprint,\n signingPublicKey: user.signingPublicKey,\n encapsulationPublicKey: user.encapsulationPublicKey,\n encapsulationKeyFingerprint: user.encapsulationKeyFingerprint,\n isPersonalOrganizationOwner: user.isPersonalOrganizationOwner,\n createdAt: user.createdAt,\n status: user.status,\n profileDocumentId: user.profileDocumentId,\n joinedAt: user.joinedAt,\n updatedAt: user.updatedAt,\n disabledAt: user.disabledAt,\n disabledByUserId: user.disabledByUserId,\n }));\n for (\n let index = 0;\n index < rows.length;\n index += DIRECTORY_INSERT_BATCH_SIZE\n ) {\n await input.tx\n .insert(organizationReadModelDirectoryUsers)\n .values(rows.slice(index, index + DIRECTORY_INSERT_BATCH_SIZE))\n .run();\n }\n}\n\nasync function upsertRequester(input: {\n readonly currentUserId: string;\n readonly isOrgAdmin: boolean;\n readonly now: string;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const requesterRow = {\n organizationId: input.organizationId,\n userId: input.currentUserId,\n isOrgAdmin: input.isOrgAdmin,\n updatedAt: input.now,\n };\n await input.tx\n .insert(organizationReadModelRequesters)\n .values(requesterRow)\n .onConflictDoUpdate({\n target: [\n organizationReadModelRequesters.organizationId,\n organizationReadModelRequesters.userId,\n ],\n set: {\n isOrgAdmin: requesterRow.isOrgAdmin,\n updatedAt: requesterRow.updatedAt,\n },\n })\n .run();\n // Storing an authoritative response proves access: retire the durable\n // denial marker for this scope in the same transaction.\n await clearOrganizationPresentationDenialInTransaction({\n organizationId: input.organizationId,\n projection: \"readModel\",\n requesterUserId: input.currentUserId,\n tx: input.tx,\n });\n}\n\nasync function replaceGroupsLane(input: {\n readonly groups: ListOrganizationGroupsResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n // Hydrated names and delivered membership heads outlive a catalog\n // replacement; the groupMemberships lane rebinds any head that changed.\n const previousGroups = await input.tx\n .select({\n groupId: organizationReadModelGroups.groupId,\n membersStateHash: organizationReadModelGroups.membersStateHash,\n name: organizationReadModelGroups.name,\n })\n .from(organizationReadModelGroups)\n .where(\n eq(organizationReadModelGroups.organizationId, input.organizationId),\n );\n const previousById = new Map(\n previousGroups.map((group) => [group.groupId, group]),\n );\n await input.tx\n .delete(organizationReadModelGroups)\n .where(eq(organizationReadModelGroups.organizationId, input.organizationId))\n .run();\n\n const rows = input.groups.groups.map((group, sortOrder) => ({\n organizationId: input.organizationId,\n groupId: group.groupId,\n sortOrder,\n name: previousById.get(group.groupId)?.name ?? \"\",\n createdAt: group.createdAt,\n isBuiltin: group.isBuiltin,\n stateHash: group.currentState?.stateHash ?? null,\n stateVersion: group.currentState?.version ?? null,\n keyEpoch: group.currentState?.keyEpoch ?? null,\n keyFingerprint: group.currentState?.keyFingerprint ?? null,\n memberCount: group.currentState?.memberCount ?? null,\n membersStateHash: previousById.get(group.groupId)?.membersStateHash ?? null,\n }));\n for (let index = 0; index < rows.length; index += GROUP_INSERT_BATCH_SIZE) {\n await input.tx\n .insert(organizationReadModelGroups)\n .values(rows.slice(index, index + GROUP_INSERT_BATCH_SIZE))\n .run();\n }\n}\n\nasync function loadCurrentState(\n tx: ClientSQLiteTransactionScope,\n organizationId: string,\n): Promise<CurrentOrganizationReadModelState | null> {\n const [current] = await tx\n .select({\n cursor: organizationReadModelState.cursor,\n keyEpoch: organizationReadModelState.organizationKeyEpoch,\n keyFingerprint: organizationReadModelState.organizationKeyFingerprint,\n memberCount: organizationReadModelState.organizationMemberCount,\n memberGroupId: organizationReadModelState.memberGroupId,\n memberGroupStateHash: organizationReadModelState.memberGroupStateHash,\n profileDocumentId: organizationReadModelState.profileDocumentId,\n stateHash: organizationReadModelState.organizationStateHash,\n version: organizationReadModelState.organizationStateVersion,\n })\n .from(organizationReadModelState)\n .where(eq(organizationReadModelState.organizationId, organizationId))\n .limit(1);\n if (!current) {\n return null;\n }\n const { keyEpoch, keyFingerprint, memberCount, stateHash, version, ...rest } =\n current;\n return {\n ...rest,\n organizationHead: {\n keyEpoch,\n keyFingerprint,\n memberCount,\n stateHash,\n version,\n },\n };\n}\n\nfunction resolveApplyDisposition(input: {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly requestedCursor: string | null;\n readonly response: OrganizationReadModelResponse;\n}): ApplyOrganizationReadModelResponseResult | null {\n const currentCursor = input.current?.cursor ?? null;\n if (currentCursor !== input.requestedCursor) {\n return currentCursor === input.response.nextCursor\n ? \"already-applied\"\n : \"stale\";\n }\n if (!input.current && input.response.mode !== \"snapshot\") {\n throw new Error(\"Organization read-model delta requires a local snapshot\");\n }\n return null;\n}\n\ninterface ResponseLanesInput {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly memberGroupId: string;\n readonly response: OrganizationReadModelResponse;\n readonly tx: ClientSQLiteTransactionScope;\n}\n\n/** Apply every carried lane and return the reserved Members head. */\nasync function replaceResponseLanes(\n input: ResponseLanesInput,\n): Promise<string | null> {\n const { organizationId } = input.response;\n const directory = input.response.lanes.directory;\n if (directory) {\n await replaceDirectoryLane({ directory, organizationId, tx: input.tx });\n }\n const groups = input.response.lanes.groups;\n if (groups) {\n await replaceGroupsLane({ groups, organizationId, tx: input.tx });\n }\n const groupMemberships = input.response.lanes.groupMemberships;\n const memberGroupStateHash = groupMemberships\n ? await applyGroupMembershipsLane({\n currentMemberGroupStateHash:\n input.current?.memberGroupStateHash ?? null,\n lane: groupMemberships,\n memberGroupId: input.memberGroupId,\n organizationId,\n replaceAll: input.response.mode === \"snapshot\",\n tx: input.tx,\n })\n : (input.current?.memberGroupStateHash ?? null);\n const grants = input.response.lanes.grants;\n if (grants) {\n await applyOrganizationReadModelGrantsLane({\n lane: grants,\n organizationId,\n tx: input.tx,\n });\n }\n return memberGroupStateHash;\n}\n\nasync function applyResponseLanes(\n input: ResponseLanesInput & { readonly currentUserId: string },\n): Promise<string | null> {\n const memberGroupStateHash = await replaceResponseLanes(input);\n // The directory lane is the authoritative roster, already persisted above:\n // requester rows for users it no longer lists as active fall with it.\n if (input.response.lanes.directory) {\n await pruneInactiveOrganizationRequestersInTransaction({\n organizationId: input.response.organizationId,\n retainUserId: input.currentUserId,\n tx: input.tx,\n });\n }\n if (input.response.lanes.groups || input.response.lanes.groupMemberships) {\n await assertStoredGroupMembershipBindings({\n memberGroupId: input.memberGroupId,\n memberGroupStateHash,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n return memberGroupStateHash;\n}\n\n/**\n * Write the cursor with the organization policy head and the reserved Members\n * head in one row, so a failure anywhere in the page rolls them back together.\n */\nasync function upsertOrganizationReadModelState(input: {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly memberGroupId: string;\n readonly memberGroupStateHash: string | null;\n readonly now: string;\n readonly response: OrganizationReadModelResponse;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const organizationHead =\n input.response.lanes.organizationPolicy?.currentState ??\n input.current?.organizationHead;\n if (!organizationHead || input.memberGroupStateHash === null) {\n throw new OrganizationReadModelBindingError(\n \"Organization read-model policy or Members head is missing\",\n );\n }\n const directoryLane = input.response.lanes.directory;\n const values = {\n cursor: input.response.nextCursor,\n profileDocumentId:\n directoryLane !== undefined\n ? directoryLane.profileDocumentId\n : (input.current?.profileDocumentId ?? null),\n memberGroupId: input.memberGroupId,\n memberGroupStateHash: input.memberGroupStateHash,\n organizationStateHash: organizationHead.stateHash,\n organizationStateVersion: organizationHead.version,\n organizationKeyEpoch: organizationHead.keyEpoch,\n organizationKeyFingerprint: organizationHead.keyFingerprint,\n organizationMemberCount: organizationHead.memberCount,\n updatedAt: input.now,\n };\n await input.tx\n .insert(organizationReadModelState)\n .values({ ...values, organizationId: input.response.organizationId })\n .onConflictDoUpdate({\n target: organizationReadModelState.organizationId,\n set: values,\n })\n .run();\n}\n\nfunction requireMemberGroupId(input: {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly response: OrganizationReadModelResponse;\n}): string {\n const memberGroupId =\n input.response.lanes.groups?.memberGroupId ?? input.current?.memberGroupId;\n if (!memberGroupId) {\n throw new Error(\"Organization read-model member group is missing\");\n }\n return memberGroupId;\n}\n\nexport async function loadOrganizationReadModelProjection(\n execSql: ExecSql,\n organizationId: string,\n currentUserId: string,\n): Promise<OrganizationReadModelProjection | null> {\n return loadWithOrganizationReadModelIntegrityPurge({\n execSql,\n organizationId,\n load: (tx) =>\n loadOrganizationReadModelProjectionInTransaction({\n currentUserId,\n organizationId,\n tx,\n }),\n });\n}\n\nexport async function applyOrganizationReadModelResponse(input: {\n readonly currentUserId: string;\n readonly execSql: ExecSql;\n readonly requestedCursor: string | null;\n readonly response: OrganizationReadModelResponse;\n}): Promise<ApplyOrganizationReadModelResponseResult> {\n assertOrganizationReadModelResponse({\n currentUserId: input.currentUserId,\n response: input.response,\n });\n // The requester prune below also retires pruned users' usage projections.\n await ensureSqlTables(input.execSql, [\n ...organizationReadModelTables,\n ...organizationDataUsageTables,\n ]);\n\n const disposition = await getClientSQLitePersistenceRuntime(\n input.execSql,\n ).transaction(\n async (tx): Promise<ApplyOrganizationReadModelResponseResult> => {\n const current = await loadCurrentState(tx, input.response.organizationId);\n const disposition = resolveApplyDisposition({\n current,\n requestedCursor: input.requestedCursor,\n response: input.response,\n });\n if (disposition === \"stale\") {\n return disposition;\n }\n const now = new Date().toISOString();\n await upsertRequester({\n currentUserId: input.currentUserId,\n isOrgAdmin: input.response.currentUser.isOrgAdmin,\n now,\n organizationId: input.response.organizationId,\n tx,\n });\n if (disposition === \"already-applied\") {\n return disposition;\n }\n const memberGroupId = requireMemberGroupId({\n current,\n response: input.response,\n });\n const memberGroupStateHash = await applyResponseLanes({\n current,\n currentUserId: input.currentUserId,\n memberGroupId,\n response: input.response,\n tx,\n });\n await upsertOrganizationReadModelState({\n current,\n memberGroupId,\n memberGroupStateHash,\n now,\n response: input.response,\n tx,\n });\n\n return \"applied\";\n },\n );\n // An applied change follows an organization hint or mutation; in-flight\n // operations recover the directory again instead of reusing their memo.\n if (disposition === \"applied\")\n invalidatePrincipalRecovery(input.execSql, input.response.organizationId);\n return disposition;\n}\n\nexport async function purgeOrganizationReadModelProjection(\n execSql: ExecSql,\n organizationId: string,\n options: { readonly onlyRequesterUserId?: string } = {},\n): Promise<void> {\n await ensureSqlTables(execSql, organizationReadModelTables);\n await getClientSQLitePersistenceRuntime(execSql).transaction(async (tx) => {\n await purgeOrganizationReadModelProjectionInTransaction({\n onlyRequesterUserId: options.onlyRequesterUserId,\n organizationId,\n tx,\n });\n });\n}\n"]}
1
+ {"version":3,"file":"organizationReadModelPersistence.js","sourceRoot":"","sources":["organizationReadModelPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ListOrganizationGroupsResponse,\n OrganizationGroupCurrentStateResponse,\n OrganizationReadModelDirectoryResponse,\n OrganizationReadModelResponse,\n} from \"@tearleads/validators/response\";\nimport { eq } from \"drizzle-orm\";\nimport { getOrCreatePrincipalPolicyCoordinator } from \"../../principals/principalPolicyCoordinator\";\nimport { organizationDataUsageTables } from \"../../sqlite/organizationDataUsageSchema\";\nimport {\n organizationReadModelDirectoryUsers,\n organizationReadModelGroups,\n organizationReadModelRequesters,\n organizationReadModelState,\n} from \"../../sqlite/organizationReadModelSchema\";\nimport { organizationReadModelTables } from \"../../sqlite/schema\";\nimport { sqliteInsertBatchSize } from \"../../sqlite/sqliteBindBudget\";\nimport {\n type ClientSQLiteTransactionScope,\n getClientSQLitePersistenceRuntime,\n} from \"../../sqlite/sqlitePersistenceRuntime\";\nimport { type ExecSql, ensureSqlTables } from \"../../sqlite/sqlSchema\";\nimport { clearOrganizationPresentationDenialInTransaction } from \"./organizationPresentationDenialPersistence\";\nimport { applyOrganizationReadModelGrantsLane } from \"./organizationReadModelGrantPersistence\";\nimport {\n applyGroupMembershipsLane,\n assertStoredGroupMembershipBindings,\n OrganizationReadModelBindingError,\n} from \"./organizationReadModelMembershipPersistence\";\nimport {\n loadOrganizationReadModelProjectionInTransaction,\n type OrganizationReadModelProjection,\n} from \"./organizationReadModelProjection\";\nimport {\n loadWithOrganizationReadModelIntegrityPurge,\n pruneInactiveOrganizationRequestersInTransaction,\n purgeOrganizationReadModelProjectionInTransaction,\n} from \"./organizationReadModelPurge\";\nimport { assertOrganizationReadModelResponse } from \"./organizationReadModelResponseAssertions\";\n\nexport type { OrganizationReadModelProjection } from \"./organizationReadModelProjection\";\n\nconst DIRECTORY_INSERT_BATCH_SIZE = sqliteInsertBatchSize(\n organizationReadModelDirectoryUsers,\n);\nconst GROUP_INSERT_BATCH_SIZE = sqliteInsertBatchSize(\n organizationReadModelGroups,\n);\n\ntype ApplyOrganizationReadModelResponseResult =\n | \"already-applied\"\n | \"applied\"\n | \"stale\";\n\ninterface CurrentOrganizationReadModelState {\n readonly cursor: string;\n readonly memberGroupId: string;\n readonly memberGroupStateHash: string;\n readonly organizationHead: OrganizationGroupCurrentStateResponse;\n readonly profileDocumentId: string | null;\n}\n\nasync function replaceDirectoryLane(input: {\n readonly directory: OrganizationReadModelDirectoryResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n await input.tx\n .delete(organizationReadModelDirectoryUsers)\n .where(\n eq(\n organizationReadModelDirectoryUsers.organizationId,\n input.organizationId,\n ),\n )\n .run();\n\n const rows = input.directory.users.map((user, sortOrder) => ({\n organizationId: input.organizationId,\n userId: user.userId,\n sortOrder,\n signingKeyFingerprint: user.signingKeyFingerprint,\n signingPublicKey: user.signingPublicKey,\n encapsulationPublicKey: user.encapsulationPublicKey,\n encapsulationKeyFingerprint: user.encapsulationKeyFingerprint,\n isPersonalOrganizationOwner: user.isPersonalOrganizationOwner,\n createdAt: user.createdAt,\n status: user.status,\n profileDocumentId: user.profileDocumentId,\n joinedAt: user.joinedAt,\n updatedAt: user.updatedAt,\n disabledAt: user.disabledAt,\n disabledByUserId: user.disabledByUserId,\n }));\n for (\n let index = 0;\n index < rows.length;\n index += DIRECTORY_INSERT_BATCH_SIZE\n ) {\n await input.tx\n .insert(organizationReadModelDirectoryUsers)\n .values(rows.slice(index, index + DIRECTORY_INSERT_BATCH_SIZE))\n .run();\n }\n}\n\nasync function upsertRequester(input: {\n readonly currentUserId: string;\n readonly isOrgAdmin: boolean;\n readonly now: string;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const requesterRow = {\n organizationId: input.organizationId,\n userId: input.currentUserId,\n isOrgAdmin: input.isOrgAdmin,\n updatedAt: input.now,\n };\n await input.tx\n .insert(organizationReadModelRequesters)\n .values(requesterRow)\n .onConflictDoUpdate({\n target: [\n organizationReadModelRequesters.organizationId,\n organizationReadModelRequesters.userId,\n ],\n set: {\n isOrgAdmin: requesterRow.isOrgAdmin,\n updatedAt: requesterRow.updatedAt,\n },\n })\n .run();\n // Storing an authoritative response proves access: retire the durable\n // denial marker for this scope in the same transaction.\n await clearOrganizationPresentationDenialInTransaction({\n organizationId: input.organizationId,\n projection: \"readModel\",\n requesterUserId: input.currentUserId,\n tx: input.tx,\n });\n}\n\nasync function replaceGroupsLane(input: {\n readonly groups: ListOrganizationGroupsResponse;\n readonly organizationId: string;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n // Hydrated names and delivered membership heads outlive a catalog\n // replacement; the groupMemberships lane rebinds any head that changed.\n const previousGroups = await input.tx\n .select({\n groupId: organizationReadModelGroups.groupId,\n membersStateHash: organizationReadModelGroups.membersStateHash,\n name: organizationReadModelGroups.name,\n })\n .from(organizationReadModelGroups)\n .where(\n eq(organizationReadModelGroups.organizationId, input.organizationId),\n );\n const previousById = new Map(\n previousGroups.map((group) => [group.groupId, group]),\n );\n await input.tx\n .delete(organizationReadModelGroups)\n .where(eq(organizationReadModelGroups.organizationId, input.organizationId))\n .run();\n\n const rows = input.groups.groups.map((group, sortOrder) => ({\n organizationId: input.organizationId,\n groupId: group.groupId,\n sortOrder,\n name: previousById.get(group.groupId)?.name ?? \"\",\n createdAt: group.createdAt,\n isBuiltin: group.isBuiltin,\n stateHash: group.currentState?.stateHash ?? null,\n stateVersion: group.currentState?.version ?? null,\n keyEpoch: group.currentState?.keyEpoch ?? null,\n keyFingerprint: group.currentState?.keyFingerprint ?? null,\n memberCount: group.currentState?.memberCount ?? null,\n membersStateHash: previousById.get(group.groupId)?.membersStateHash ?? null,\n }));\n for (let index = 0; index < rows.length; index += GROUP_INSERT_BATCH_SIZE) {\n await input.tx\n .insert(organizationReadModelGroups)\n .values(rows.slice(index, index + GROUP_INSERT_BATCH_SIZE))\n .run();\n }\n}\n\nasync function loadCurrentState(\n tx: ClientSQLiteTransactionScope,\n organizationId: string,\n): Promise<CurrentOrganizationReadModelState | null> {\n const [current] = await tx\n .select({\n cursor: organizationReadModelState.cursor,\n keyEpoch: organizationReadModelState.organizationKeyEpoch,\n keyFingerprint: organizationReadModelState.organizationKeyFingerprint,\n memberCount: organizationReadModelState.organizationMemberCount,\n memberGroupId: organizationReadModelState.memberGroupId,\n memberGroupStateHash: organizationReadModelState.memberGroupStateHash,\n profileDocumentId: organizationReadModelState.profileDocumentId,\n stateHash: organizationReadModelState.organizationStateHash,\n version: organizationReadModelState.organizationStateVersion,\n })\n .from(organizationReadModelState)\n .where(eq(organizationReadModelState.organizationId, organizationId))\n .limit(1);\n if (!current) {\n return null;\n }\n const { keyEpoch, keyFingerprint, memberCount, stateHash, version, ...rest } =\n current;\n return {\n ...rest,\n organizationHead: {\n keyEpoch,\n keyFingerprint,\n memberCount,\n stateHash,\n version,\n },\n };\n}\n\nfunction resolveApplyDisposition(input: {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly requestedCursor: string | null;\n readonly response: OrganizationReadModelResponse;\n}): ApplyOrganizationReadModelResponseResult | null {\n const currentCursor = input.current?.cursor ?? null;\n if (currentCursor !== input.requestedCursor) {\n return currentCursor === input.response.nextCursor\n ? \"already-applied\"\n : \"stale\";\n }\n if (!input.current && input.response.mode !== \"snapshot\") {\n throw new Error(\"Organization read-model delta requires a local snapshot\");\n }\n return null;\n}\n\ninterface ResponseLanesInput {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly memberGroupId: string;\n readonly response: OrganizationReadModelResponse;\n readonly tx: ClientSQLiteTransactionScope;\n}\n\n/** Apply every carried lane and return the reserved Members head. */\nasync function replaceResponseLanes(\n input: ResponseLanesInput,\n): Promise<string | null> {\n const { organizationId } = input.response;\n const directory = input.response.lanes.directory;\n if (directory) {\n await replaceDirectoryLane({ directory, organizationId, tx: input.tx });\n }\n const groups = input.response.lanes.groups;\n if (groups) {\n await replaceGroupsLane({ groups, organizationId, tx: input.tx });\n }\n const groupMemberships = input.response.lanes.groupMemberships;\n const memberGroupStateHash = groupMemberships\n ? await applyGroupMembershipsLane({\n currentMemberGroupStateHash:\n input.current?.memberGroupStateHash ?? null,\n lane: groupMemberships,\n memberGroupId: input.memberGroupId,\n organizationId,\n replaceAll: input.response.mode === \"snapshot\",\n tx: input.tx,\n })\n : (input.current?.memberGroupStateHash ?? null);\n const grants = input.response.lanes.grants;\n if (grants) {\n await applyOrganizationReadModelGrantsLane({\n lane: grants,\n organizationId,\n tx: input.tx,\n });\n }\n return memberGroupStateHash;\n}\n\nasync function applyResponseLanes(\n input: ResponseLanesInput & { readonly currentUserId: string },\n): Promise<string | null> {\n const memberGroupStateHash = await replaceResponseLanes(input);\n // The directory lane is the authoritative roster, already persisted above:\n // requester rows for users it no longer lists as active fall with it.\n if (input.response.lanes.directory) {\n await pruneInactiveOrganizationRequestersInTransaction({\n organizationId: input.response.organizationId,\n retainUserId: input.currentUserId,\n tx: input.tx,\n });\n }\n if (input.response.lanes.groups || input.response.lanes.groupMemberships) {\n await assertStoredGroupMembershipBindings({\n memberGroupId: input.memberGroupId,\n memberGroupStateHash,\n organizationId: input.response.organizationId,\n tx: input.tx,\n });\n }\n return memberGroupStateHash;\n}\n\n/**\n * Write the cursor with the organization policy head and the reserved Members\n * head in one row, so a failure anywhere in the page rolls them back together.\n */\nasync function upsertOrganizationReadModelState(input: {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly memberGroupId: string;\n readonly memberGroupStateHash: string | null;\n readonly now: string;\n readonly response: OrganizationReadModelResponse;\n readonly tx: ClientSQLiteTransactionScope;\n}): Promise<void> {\n const organizationHead =\n input.response.lanes.organizationPolicy?.currentState ??\n input.current?.organizationHead;\n if (!organizationHead || input.memberGroupStateHash === null) {\n throw new OrganizationReadModelBindingError(\n \"Organization read-model policy or Members head is missing\",\n );\n }\n const directoryLane = input.response.lanes.directory;\n const values = {\n cursor: input.response.nextCursor,\n profileDocumentId:\n directoryLane !== undefined\n ? directoryLane.profileDocumentId\n : (input.current?.profileDocumentId ?? null),\n memberGroupId: input.memberGroupId,\n memberGroupStateHash: input.memberGroupStateHash,\n organizationStateHash: organizationHead.stateHash,\n organizationStateVersion: organizationHead.version,\n organizationKeyEpoch: organizationHead.keyEpoch,\n organizationKeyFingerprint: organizationHead.keyFingerprint,\n organizationMemberCount: organizationHead.memberCount,\n updatedAt: input.now,\n };\n await input.tx\n .insert(organizationReadModelState)\n .values({ ...values, organizationId: input.response.organizationId })\n .onConflictDoUpdate({\n target: organizationReadModelState.organizationId,\n set: values,\n })\n .run();\n}\n\nfunction requireMemberGroupId(input: {\n readonly current: CurrentOrganizationReadModelState | null;\n readonly response: OrganizationReadModelResponse;\n}): string {\n const memberGroupId =\n input.response.lanes.groups?.memberGroupId ?? input.current?.memberGroupId;\n if (!memberGroupId) {\n throw new Error(\"Organization read-model member group is missing\");\n }\n return memberGroupId;\n}\n\nexport async function loadOrganizationReadModelProjection(\n execSql: ExecSql,\n organizationId: string,\n currentUserId: string,\n): Promise<OrganizationReadModelProjection | null> {\n return loadWithOrganizationReadModelIntegrityPurge({\n execSql,\n organizationId,\n load: (tx) =>\n loadOrganizationReadModelProjectionInTransaction({\n currentUserId,\n organizationId,\n tx,\n }),\n });\n}\n\nexport async function applyOrganizationReadModelResponse(input: {\n readonly currentUserId: string;\n readonly execSql: ExecSql;\n readonly requestedCursor: string | null;\n readonly response: OrganizationReadModelResponse;\n}): Promise<ApplyOrganizationReadModelResponseResult> {\n assertOrganizationReadModelResponse({\n currentUserId: input.currentUserId,\n response: input.response,\n });\n // The requester prune below also retires pruned users' usage projections.\n await ensureSqlTables(input.execSql, [\n ...organizationReadModelTables,\n ...organizationDataUsageTables,\n ]);\n\n const disposition = await getClientSQLitePersistenceRuntime(\n input.execSql,\n ).transaction(\n async (tx): Promise<ApplyOrganizationReadModelResponseResult> => {\n const current = await loadCurrentState(tx, input.response.organizationId);\n const disposition = resolveApplyDisposition({\n current,\n requestedCursor: input.requestedCursor,\n response: input.response,\n });\n if (disposition === \"stale\") {\n return disposition;\n }\n const now = new Date().toISOString();\n await upsertRequester({\n currentUserId: input.currentUserId,\n isOrgAdmin: input.response.currentUser.isOrgAdmin,\n now,\n organizationId: input.response.organizationId,\n tx,\n });\n if (disposition === \"already-applied\") {\n return disposition;\n }\n const memberGroupId = requireMemberGroupId({\n current,\n response: input.response,\n });\n const memberGroupStateHash = await applyResponseLanes({\n current,\n currentUserId: input.currentUserId,\n memberGroupId,\n response: input.response,\n tx,\n });\n await upsertOrganizationReadModelState({\n current,\n memberGroupId,\n memberGroupStateHash,\n now,\n response: input.response,\n tx,\n });\n\n return \"applied\";\n },\n );\n // An applied change follows an organization hint or mutation; in-flight\n // operations recover the directory again instead of reusing their memo.\n if (disposition === \"applied\")\n getOrCreatePrincipalPolicyCoordinator(input.execSql).invalidateRecovery(\n input.response.organizationId,\n );\n return disposition;\n}\n\nexport async function purgeOrganizationReadModelProjection(\n execSql: ExecSql,\n organizationId: string,\n options: { readonly onlyRequesterUserId?: string } = {},\n): Promise<void> {\n await ensureSqlTables(execSql, organizationReadModelTables);\n await getClientSQLitePersistenceRuntime(execSql).transaction(async (tx) => {\n await purgeOrganizationReadModelProjectionInTransaction({\n onlyRequesterUserId: options.onlyRequesterUserId,\n organizationId,\n tx,\n });\n });\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import { resolveCanonicalExecSql } from "../sqlite/sqlSchema.js";
2
+ import { principalMutationJournalScopeId, } from "./principalMutationJournal.js";
3
+ /** Runs work for a key after the work already queued for it; idle keys hold nothing. */
4
+ function createKeyedQueue() {
5
+ const tails = new Map();
6
+ return (key, work) => {
7
+ const operation = (tails.get(key) ?? Promise.resolve()).then(work);
8
+ const settled = operation.then(() => undefined, () => undefined);
9
+ tails.set(key, settled);
10
+ void settled.then(() => {
11
+ if (tails.get(key) === settled)
12
+ tails.delete(key);
13
+ });
14
+ return operation;
15
+ };
16
+ }
17
+ function createPrincipalPolicyCoordinator() {
18
+ const recoveries = createKeyedQueue();
19
+ const journalScopes = createKeyedQueue();
20
+ const epochs = new Map();
21
+ return {
22
+ queueRecovery: recoveries,
23
+ recoveryEpoch: (organizationId) => epochs.get(organizationId) ?? 0,
24
+ invalidateRecovery: (organizationId) => {
25
+ epochs.set(organizationId, (epochs.get(organizationId) ?? 0) + 1);
26
+ },
27
+ runJournalOperation: async (scope, run) => journalScopes(await principalMutationJournalScopeId(scope), run),
28
+ };
29
+ }
30
+ const coordinators = new WeakMap();
31
+ export function getOrCreatePrincipalPolicyCoordinator(execSql) {
32
+ const database = resolveCanonicalExecSql(execSql);
33
+ let coordinator = coordinators.get(database);
34
+ if (!coordinator) {
35
+ coordinator = createPrincipalPolicyCoordinator();
36
+ coordinators.set(database, coordinator);
37
+ }
38
+ return coordinator;
39
+ }
40
+ //# sourceMappingURL=principalPolicyCoordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principalPolicyCoordinator.js","sourceRoot":"","sources":["principalPolicyCoordinator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAEL,+BAA+B,GAChC,MAAM,4BAA4B,CAAC;AA+BpC,wFAAwF;AACxF,SAAS,gBAAgB;IACvB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC/C,OAAO,CAAI,GAAW,EAAE,IAAsB,EAAc,EAAE;QAC5D,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAC5B,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;QACF,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACxB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE;YACrB,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,OAAO;gBAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QACH,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,gCAAgC;IACvC,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;IACtC,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,OAAO;QACL,aAAa,EAAE,UAAU;QACzB,aAAa,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC;QAClE,kBAAkB,EAAE,CAAC,cAAc,EAAE,EAAE;YACrC,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,CAAC;QACD,mBAAmB,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CACxC,aAAa,CAAC,MAAM,+BAA+B,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC;KACnE,CAAC;AACJ,CAAC;AAED,MAAM,YAAY,GAAG,IAAI,OAAO,EAAuC,CAAC;AAExE,MAAM,UAAU,qCAAqC,CACnD,OAAgB;IAEhB,MAAM,QAAQ,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,WAAW,GAAG,gCAAgC,EAAE,CAAC;QACjD,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["import { type ExecSql, resolveCanonicalExecSql } from \"../sqlite/sqlSchema\";\nimport {\n type PrincipalMutationJournalScope,\n principalMutationJournalScopeId,\n} from \"./principalMutationJournal\";\n\n/**\n * The principal-policy work one database coordinates in this process. Every\n * executor for a connection, including a transaction's locked one, resolves to\n * the same coordinator; different databases never share one.\n */\nexport interface PrincipalPolicyCoordinator {\n /**\n * Runs recovery for an organization after the recovery already queued for\n * it. Directory and Admins stages are shared by every reference in an\n * organization, so warmers and registration take turns. Queued work must not\n * queue again for the same organization.\n */\n queueRecovery<T>(organizationId: string, work: () => Promise<T>): Promise<T>;\n /**\n * The organization hints applied in this process. Operation-scoped recovery\n * records it before producing a result and recovers again once it changes.\n * Hints are lossy, so this only shortens reuse; each operation still\n * discovers the current head.\n */\n recoveryEpoch(organizationId: string): number;\n /** Evicts recovered directory and Admins views for this organization. */\n invalidateRecovery(organizationId: string): void;\n /** An owned dispatch finishes before another caller can recover or abandon it. */\n runJournalOperation<T>(\n scope: PrincipalMutationJournalScope,\n run: () => Promise<T>,\n ): Promise<T>;\n}\n\n/** Runs work for a key after the work already queued for it; idle keys hold nothing. */\nfunction createKeyedQueue() {\n const tails = new Map<string, Promise<void>>();\n return <T>(key: string, work: () => Promise<T>): Promise<T> => {\n const operation = (tails.get(key) ?? Promise.resolve()).then(work);\n const settled = operation.then(\n () => undefined,\n () => undefined,\n );\n tails.set(key, settled);\n void settled.then(() => {\n if (tails.get(key) === settled) tails.delete(key);\n });\n return operation;\n };\n}\n\nfunction createPrincipalPolicyCoordinator(): PrincipalPolicyCoordinator {\n const recoveries = createKeyedQueue();\n const journalScopes = createKeyedQueue();\n const epochs = new Map<string, number>();\n return {\n queueRecovery: recoveries,\n recoveryEpoch: (organizationId) => epochs.get(organizationId) ?? 0,\n invalidateRecovery: (organizationId) => {\n epochs.set(organizationId, (epochs.get(organizationId) ?? 0) + 1);\n },\n runJournalOperation: async (scope, run) =>\n journalScopes(await principalMutationJournalScopeId(scope), run),\n };\n}\n\nconst coordinators = new WeakMap<ExecSql, PrincipalPolicyCoordinator>();\n\nexport function getOrCreatePrincipalPolicyCoordinator(\n execSql: ExecSql,\n): PrincipalPolicyCoordinator {\n const database = resolveCanonicalExecSql(execSql);\n let coordinator = coordinators.get(database);\n if (!coordinator) {\n coordinator = createPrincipalPolicyCoordinator();\n coordinators.set(database, coordinator);\n }\n return coordinator;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tearleads/client-sdk",
3
- "version": "0.1.55",
3
+ "version": "0.1.57",
4
4
  "description": "React-free Tearleads client runtime: local SQLite persistence, keys, sync, encrypted blobs, and domain workflows.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type {\n ContainerDeleteResponse,\n ContainerWriterProjectionResponse,\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createInitializedContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport type { DocumentRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type {\n createRemoteContainer,\n moveRemoteContainer,\n shareRemoteContainer,\n shareRemoteContainerWithGroup,\n} from \"../../containers\";\nimport type { createRemoteDocument } from \"../../documents\";\nimport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerMoveIntentRecord,\n} from \"../containerPersistence\";\nimport type {\n ContainerState,\n RemoteContainer as HydratedRemoteContainer,\n RemoteContainerHydrationHost,\n} from \"../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ContainerWorkflowApi = Parameters<\n typeof createRemoteContainer\n>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainerWithGroup>[0][\"apiClient\"] &\n Parameters<typeof moveRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof createRemoteDocument>[0][\"apiClient\"] &\n Partial<\n Pick<\n ApiClient,\n \"getPrincipalPolicyPages\" | \"getProjectionPolicyHistoryPages\"\n >\n > & {\n deleteContainerResult(\n containerId: string,\n options?: {\n expectedPaymentRequiredOrganizationId?: string;\n reportErrors?: boolean;\n requiredAncestorId?: string | undefined;\n },\n ): Promise<\n | { data: ContainerDeleteResponse; ok: true }\n | {\n ok: false;\n report: () => void;\n status: number | null;\n }\n >;\n primeContainerWriterProjection(\n containerId: string,\n projection: ContainerWriterProjectionResponse,\n ): void;\n getCurrentPrincipalPolicy(\n principalType: \"group\" | \"organization\",\n principalId: string,\n ): Promise<PrincipalPolicyBundleResponse | null>;\n };\n\nexport interface ContainerWorkflowRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n readonly apiClient: ContainerWorkflowApi;\n}\n\nexport interface CreatedRemoteContainerState {\n accessManifestHash: string;\n /** The slot this client signed into the create; never the server's echo. */\n systemSlot: ContainerSystemSlot | null;\n containerId: string;\n createdAt: string;\n metadataDocumentId: string;\n organizationId: string;\n /** A created container is a child; the acknowledgement echoed this parent. */\n parentId: string;\n persistedMetadataState: Pick<\n DocumentRecord,\n | \"documentId\"\n | \"contentKeyBundle\"\n | \"documentKekTargets\"\n | \"documentManifestBundle\"\n >;\n updatedAt: string;\n}\n\nexport interface SharedRemoteContainerState {\n accessEpoch: number;\n accessManifestHash: string;\n createdAt: string;\n metadataDocumentId: string;\n referencedPrincipalHeads: ReferencedPrincipalStateResponse[];\n updatedAt: string;\n writerProjection: ContainerWriterProjectionResponse | null;\n}\n\nexport interface SharedContainerState {\n container: ContainerState[\"container\"];\n record: ContainerState[\"record\"];\n}\n\nexport type SharedContainerStateResult =\n | { status: \"missing\" }\n | { status: \"confirmed\" }\n | (SharedContainerState & {\n status: \"identity-superseded\" | \"persisted\";\n });\n\nexport type RemoteContainer = HydratedRemoteContainer;\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createInitializedContainerMetadataDocument>\n>[\"doc\"];\n\nexport interface CreatedChildContainerState {\n containerState: ContainerState;\n initialUpdate: Uint8Array;\n shouldRequestSync: boolean;\n}\n\nexport interface ContainerIntentSyncState {\n containersById: ReadonlyMap<string, ContainerState>;\n persistence: ContainerContentsPersistence;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n /**\n * Verifies that a listed container is this device's pending create before it\n * is adopted and returns where it sits; `assertContainerCreateAdoptable`\n * unless a test replaces it.\n */\n verifyCreateAdoption?: (input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n }) => Promise<AdoptedContainerPlacement>;\n}\n\n/** Where a verified listed create committed, and where its container is now. */\nexport interface AdoptedContainerPlacement {\n /** The parent its signed epoch-1 `container.create` named. */\n readonly createdParentId: string;\n /** The verified head's parent; another writer may have moved it since. */\n readonly currentParentId: string;\n}\n\nexport type ContainerCreateIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerCreateIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\"\n>;\n\nexport interface ContainerCreateIntentSyncInput {\n host: ContainerCreateIntentSyncHost;\n /** A parent whose own create has not settled takes no child create. */\n isCreatePending: (containerId: string) => boolean;\n isCurrent: () => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerCreateIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerCreateIntentSyncState;\n}\n\nexport type ContainerMoveIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerMoveIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\" | \"updateSnapshot\"\n>;\n\nexport interface ContainerMoveIntentSyncInput {\n host: ContainerMoveIntentSyncHost;\n isCurrent: () => boolean;\n /** A folder whose create has not settled takes no structural writes. */\n isCreatePending: (containerId: string) => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerMoveIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerMoveIntentSyncState;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type {\n ContainerDeleteResponse,\n ContainerWriterProjectionResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createInitializedContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport type { DocumentRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type {\n createRemoteContainer,\n moveRemoteContainer,\n shareRemoteContainer,\n shareRemoteContainerWithGroup,\n} from \"../../containers\";\nimport type { createRemoteDocument } from \"../../documents\";\nimport type { CurrentPrincipalPolicyReader } from \"../../principals/currentPrincipalPolicyReader\";\nimport type { PrincipalPolicyHistoryReader } from \"../../principals/principalPolicyHistoryReader\";\nimport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerMoveIntentRecord,\n} from \"../containerPersistence\";\nimport type {\n ContainerState,\n RemoteContainer as HydratedRemoteContainer,\n RemoteContainerHydrationHost,\n} from \"../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ContainerWorkflowApi = Parameters<\n typeof createRemoteContainer\n>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainerWithGroup>[0][\"apiClient\"] &\n Parameters<typeof moveRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof createRemoteDocument>[0][\"apiClient\"] &\n Partial<PrincipalPolicyHistoryReader> &\n CurrentPrincipalPolicyReader & {\n deleteContainerResult(\n containerId: string,\n options?: {\n expectedPaymentRequiredOrganizationId?: string;\n reportErrors?: boolean;\n requiredAncestorId?: string | undefined;\n },\n ): Promise<\n | { data: ContainerDeleteResponse; ok: true }\n | {\n ok: false;\n report: () => void;\n status: number | null;\n }\n >;\n primeContainerWriterProjection(\n containerId: string,\n projection: ContainerWriterProjectionResponse,\n ): void;\n };\n\nexport interface ContainerWorkflowRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n readonly apiClient: ContainerWorkflowApi;\n}\n\nexport interface CreatedRemoteContainerState {\n accessManifestHash: string;\n /** The slot this client signed into the create; never the server's echo. */\n systemSlot: ContainerSystemSlot | null;\n containerId: string;\n createdAt: string;\n metadataDocumentId: string;\n organizationId: string;\n /** A created container is a child; the acknowledgement echoed this parent. */\n parentId: string;\n persistedMetadataState: Pick<\n DocumentRecord,\n | \"documentId\"\n | \"contentKeyBundle\"\n | \"documentKekTargets\"\n | \"documentManifestBundle\"\n >;\n updatedAt: string;\n}\n\nexport interface SharedRemoteContainerState {\n accessEpoch: number;\n accessManifestHash: string;\n createdAt: string;\n metadataDocumentId: string;\n referencedPrincipalHeads: ReferencedPrincipalStateResponse[];\n updatedAt: string;\n writerProjection: ContainerWriterProjectionResponse | null;\n}\n\nexport interface SharedContainerState {\n container: ContainerState[\"container\"];\n record: ContainerState[\"record\"];\n}\n\nexport type SharedContainerStateResult =\n | { status: \"missing\" }\n | { status: \"confirmed\" }\n | (SharedContainerState & {\n status: \"identity-superseded\" | \"persisted\";\n });\n\nexport type RemoteContainer = HydratedRemoteContainer;\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createInitializedContainerMetadataDocument>\n>[\"doc\"];\n\nexport interface CreatedChildContainerState {\n containerState: ContainerState;\n initialUpdate: Uint8Array;\n shouldRequestSync: boolean;\n}\n\nexport interface ContainerIntentSyncState {\n containersById: ReadonlyMap<string, ContainerState>;\n persistence: ContainerContentsPersistence;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n /**\n * Verifies that a listed container is this device's pending create before it\n * is adopted and returns where it sits; `assertContainerCreateAdoptable`\n * unless a test replaces it.\n */\n verifyCreateAdoption?: (input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n }) => Promise<AdoptedContainerPlacement>;\n}\n\n/** Where a verified listed create committed, and where its container is now. */\nexport interface AdoptedContainerPlacement {\n /** The parent its signed epoch-1 `container.create` named. */\n readonly createdParentId: string;\n /** The verified head's parent; another writer may have moved it since. */\n readonly currentParentId: string;\n}\n\nexport type ContainerCreateIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerCreateIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\"\n>;\n\nexport interface ContainerCreateIntentSyncInput {\n host: ContainerCreateIntentSyncHost;\n /** A parent whose own create has not settled takes no child create. */\n isCreatePending: (containerId: string) => boolean;\n isCurrent: () => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerCreateIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerCreateIntentSyncState;\n}\n\nexport type ContainerMoveIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerMoveIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\" | \"updateSnapshot\"\n>;\n\nexport interface ContainerMoveIntentSyncInput {\n host: ContainerMoveIntentSyncHost;\n isCurrent: () => boolean;\n /** A folder whose create has not settled takes no structural writes. */\n isCreatePending: (containerId: string) => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerMoveIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerMoveIntentSyncState;\n}\n"]}
@@ -1,8 +1,10 @@
1
- import type { ApiClient, RequestResult } from "../../../internal/api-client/index.js";
1
+ import type { RequestResult } from "../../../internal/api-client/index.js";
2
2
  import type { ContainerReplacementAuthorizationsResponse } from "../../../internal/validators/operation/index.js";
3
3
  import type { ListContainerParentLanesRequest } from "../../../internal/validators/request/index.js";
4
- import type { ContainerSummary, ContainerWriterProjectionResponse, DocumentWriterProjectionResponse, ListContainerParentLanesResponse, ListContainersResponse, PrincipalPolicyBundleResponse, ReferencedPrincipalStateResponse } from "../../../internal/validators/response/index.js";
4
+ import type { ContainerSummary, ContainerWriterProjectionResponse, DocumentWriterProjectionResponse, ListContainerParentLanesResponse, ListContainersResponse, ReferencedPrincipalStateResponse } from "../../../internal/validators/response/index.js";
5
5
  import type { createContainerMetadataDocument } from "../../../data/containers/containerMetadataDocument.js";
6
+ import type { CurrentPrincipalPolicyReader } from "../../principals/currentPrincipalPolicyReader.js";
7
+ import type { PrincipalPolicyHistoryReader } from "../../principals/principalPolicyHistoryReader.js";
6
8
  import type { ContainerContentsPersistence, ContainerDocumentRecord, ContainerHydrationTombstone, ContainerRecord, createContainerParentSyncLane } from "../containerPersistence.js";
7
9
  import type { ContainerMetadataPatch } from "../metadata.js";
8
10
  import type { ContainerContentsWorkflowRuntime } from "../runtime.js";
@@ -30,11 +32,10 @@ export interface ContainerState {
30
32
  pullContinuation?: ContainerDocumentRecord["pullContinuation"] | undefined;
31
33
  record: ContainerDocumentRecord;
32
34
  }
33
- interface RemoteContainerHydrationApi extends Partial<Pick<ApiClient, "getPrincipalPolicyPages" | "getProjectionPolicyHistoryPages">> {
35
+ interface RemoteContainerHydrationApi extends Partial<PrincipalPolicyHistoryReader>, CurrentPrincipalPolicyReader {
34
36
  getContainerReplacementAuthorizationsResult(containerId: string, replacesOrganizationId: string): Promise<RequestResult<ContainerReplacementAuthorizationsResponse>>;
35
37
  evictContainerWriterProjection(containerId: string): void;
36
38
  getContainerWriterProjection(containerId: string): Promise<ContainerWriterProjectionResponse | null>;
37
- getCurrentPrincipalPolicy(principalType: "group" | "organization", principalId: string): Promise<PrincipalPolicyBundleResponse | null>;
38
39
  listContainerParentLanes(input: ListContainerParentLanesRequest): Promise<ListContainerParentLanesResponse | null>;
39
40
  }
40
41
  interface RemoteContainerHydrationRuntime extends Pick<ContainerContentsWorkflowRuntime, "auth" | "adoptRootContainer" | "crypto" | "infra" | "resolveTrustedUserIdentity" | "state" | "util"> {
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ApiClient, RequestResult } from \"@tearleads/api-client\";\nimport type { ContainerReplacementAuthorizationsResponse } from \"@tearleads/validators/operation\";\nimport type { ListContainerParentLanesRequest } from \"@tearleads/validators/request\";\nimport type {\n ContainerSummary,\n ContainerWriterProjectionResponse,\n DocumentWriterProjectionResponse,\n ListContainerParentLanesResponse,\n ListContainersResponse,\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type {\n ContainerContentsPersistence,\n ContainerDocumentRecord,\n ContainerHydrationTombstone,\n ContainerRecord,\n createContainerParentSyncLane,\n} from \"../containerPersistence\";\nimport type { ContainerMetadataPatch } from \"../metadata\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ListedRemoteContainerPageItem =\n ListContainersResponse[\"items\"][number];\nexport type ContainerChildIndex = Map<string, Set<string>>;\nexport type QueueContainerParentLane = (parentId: string | null) => void;\nexport type RemoteContainerIngestQueue = Map<string, RemoteContainer>;\nexport type SaveContainerOptions = Parameters<\n ContainerContentsPersistence[\"saveContainer\"]\n>[3];\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createContainerMetadataDocument>\n>;\nexport type RemoteContainer = Pick<\n ContainerSummary,\n | \"createdAt\"\n | \"effectiveAccessLevel\"\n | \"systemSlot\"\n | \"id\"\n | \"metadataAccessEpoch\"\n | \"metadataAccessStateHash\"\n | \"metadataDocumentId\"\n | \"metadataReferencedPrincipals\"\n | \"organizationId\"\n | \"parentId\"\n | \"updatedAt\"\n>;\n\nexport interface ContainerState {\n containerWriterProjection?:\n | ContainerWriterProjectionResponse\n | null\n | undefined;\n /**\n * Bumped whenever a realtime hint drops this container's cached writer\n * projections (container and metadata document alike); an operation that\n * fetched or carried a projection installs it only if this is unchanged\n * since it started, so a pre-hint answer never lands after the hint.\n */\n writerProjectionGeneration?: number | undefined;\n metadataReferencedPrincipals?:\n | readonly ReferencedPrincipalStateResponse[]\n | undefined;\n container: ContainerRecord;\n doc: ContainerMetadataDocumentState;\n metadataWriterProjection?:\n | DocumentWriterProjectionResponse\n | null\n | undefined;\n /** Durable pull progress mirrored from the metadata document record. */\n pullContinuation?: ContainerDocumentRecord[\"pullContinuation\"] | undefined;\n record: ContainerDocumentRecord;\n}\n\ninterface RemoteContainerHydrationApi\n extends Partial<\n Pick<\n ApiClient,\n \"getPrincipalPolicyPages\" | \"getProjectionPolicyHistoryPages\"\n >\n > {\n getContainerReplacementAuthorizationsResult(\n containerId: string,\n replacesOrganizationId: string,\n ): Promise<RequestResult<ContainerReplacementAuthorizationsResponse>>;\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n getCurrentPrincipalPolicy(\n principalType: \"group\" | \"organization\",\n principalId: string,\n ): Promise<PrincipalPolicyBundleResponse | null>;\n listContainerParentLanes(\n input: ListContainerParentLanesRequest,\n ): Promise<ListContainerParentLanesResponse | null>;\n}\n\ninterface RemoteContainerHydrationRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"adoptRootContainer\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: RemoteContainerHydrationApi;\n}\n\nexport interface RemoteContainerHydrationState {\n containersById: Map<string, ContainerState>;\n lifecycleGeneration?: number | undefined;\n persistence: ContainerContentsPersistence;\n rootLaneHydrated?: boolean | undefined;\n runtime: RemoteContainerHydrationRuntime;\n structuralGeneration?: number | undefined;\n}\n\nexport type PersistContainerStateResult =\n | { status: \"identity-superseded\"; record: ContainerDocumentRecord }\n | { status: \"missing\" }\n | {\n status: \"persisted\";\n record: ContainerDocumentRecord;\n createIntentSettled?: true | undefined;\n moveIntentSettled?: true | undefined;\n }\n | { status: \"stale-generation\" };\n\nexport interface RemoteContainerHydrationHost {\n persistContainerState: (\n containerState: ContainerState,\n patch?: Partial<ContainerMetadataPatch>,\n updateView?: boolean,\n saveOptions?: SaveContainerOptions,\n mutationOptions?: {\n expectedStateWhenMissing?: ContainerState | undefined;\n isCurrent?: (() => boolean) | undefined;\n createIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"createIntentSettlement\"];\n moveIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"moveIntentSettlement\"];\n preserveDurableStructureWhenPending?: boolean | undefined;\n },\n ) => Promise<PersistContainerStateResult>;\n requestDocumentPriming?: (() => void) | undefined;\n updateSnapshot: () => void;\n}\n\nexport interface ContainerParentHydrationLane {\n parentId: string | null;\n watermark?: ListContainersResponse[\"nextWatermark\"];\n}\n\nexport interface ExpectedContainerState {\n container: ContainerRecord;\n fingerprint: string;\n}\n\nexport interface FetchedContainerParentLanePage {\n expectedContainerStates: ReadonlyMap<string, ExpectedContainerState>;\n expectedHydrationTombstones: ReadonlyMap<string, ContainerHydrationTombstone>;\n lane: ContainerParentHydrationLane;\n response: ListContainersResponse;\n syncLane: ReturnType<typeof createContainerParentSyncLane>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { RequestResult } from \"@tearleads/api-client\";\nimport type { ContainerReplacementAuthorizationsResponse } from \"@tearleads/validators/operation\";\nimport type { ListContainerParentLanesRequest } from \"@tearleads/validators/request\";\nimport type {\n ContainerSummary,\n ContainerWriterProjectionResponse,\n DocumentWriterProjectionResponse,\n ListContainerParentLanesResponse,\n ListContainersResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type { CurrentPrincipalPolicyReader } from \"../../principals/currentPrincipalPolicyReader\";\nimport type { PrincipalPolicyHistoryReader } from \"../../principals/principalPolicyHistoryReader\";\nimport type {\n ContainerContentsPersistence,\n ContainerDocumentRecord,\n ContainerHydrationTombstone,\n ContainerRecord,\n createContainerParentSyncLane,\n} from \"../containerPersistence\";\nimport type { ContainerMetadataPatch } from \"../metadata\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ListedRemoteContainerPageItem =\n ListContainersResponse[\"items\"][number];\nexport type ContainerChildIndex = Map<string, Set<string>>;\nexport type QueueContainerParentLane = (parentId: string | null) => void;\nexport type RemoteContainerIngestQueue = Map<string, RemoteContainer>;\nexport type SaveContainerOptions = Parameters<\n ContainerContentsPersistence[\"saveContainer\"]\n>[3];\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createContainerMetadataDocument>\n>;\nexport type RemoteContainer = Pick<\n ContainerSummary,\n | \"createdAt\"\n | \"effectiveAccessLevel\"\n | \"systemSlot\"\n | \"id\"\n | \"metadataAccessEpoch\"\n | \"metadataAccessStateHash\"\n | \"metadataDocumentId\"\n | \"metadataReferencedPrincipals\"\n | \"organizationId\"\n | \"parentId\"\n | \"updatedAt\"\n>;\n\nexport interface ContainerState {\n containerWriterProjection?:\n | ContainerWriterProjectionResponse\n | null\n | undefined;\n /**\n * Bumped whenever a realtime hint drops this container's cached writer\n * projections (container and metadata document alike); an operation that\n * fetched or carried a projection installs it only if this is unchanged\n * since it started, so a pre-hint answer never lands after the hint.\n */\n writerProjectionGeneration?: number | undefined;\n metadataReferencedPrincipals?:\n | readonly ReferencedPrincipalStateResponse[]\n | undefined;\n container: ContainerRecord;\n doc: ContainerMetadataDocumentState;\n metadataWriterProjection?:\n | DocumentWriterProjectionResponse\n | null\n | undefined;\n /** Durable pull progress mirrored from the metadata document record. */\n pullContinuation?: ContainerDocumentRecord[\"pullContinuation\"] | undefined;\n record: ContainerDocumentRecord;\n}\n\ninterface RemoteContainerHydrationApi\n extends Partial<PrincipalPolicyHistoryReader>,\n CurrentPrincipalPolicyReader {\n getContainerReplacementAuthorizationsResult(\n containerId: string,\n replacesOrganizationId: string,\n ): Promise<RequestResult<ContainerReplacementAuthorizationsResponse>>;\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n listContainerParentLanes(\n input: ListContainerParentLanesRequest,\n ): Promise<ListContainerParentLanesResponse | null>;\n}\n\ninterface RemoteContainerHydrationRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"adoptRootContainer\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: RemoteContainerHydrationApi;\n}\n\nexport interface RemoteContainerHydrationState {\n containersById: Map<string, ContainerState>;\n lifecycleGeneration?: number | undefined;\n persistence: ContainerContentsPersistence;\n rootLaneHydrated?: boolean | undefined;\n runtime: RemoteContainerHydrationRuntime;\n structuralGeneration?: number | undefined;\n}\n\nexport type PersistContainerStateResult =\n | { status: \"identity-superseded\"; record: ContainerDocumentRecord }\n | { status: \"missing\" }\n | {\n status: \"persisted\";\n record: ContainerDocumentRecord;\n createIntentSettled?: true | undefined;\n moveIntentSettled?: true | undefined;\n }\n | { status: \"stale-generation\" };\n\nexport interface RemoteContainerHydrationHost {\n persistContainerState: (\n containerState: ContainerState,\n patch?: Partial<ContainerMetadataPatch>,\n updateView?: boolean,\n saveOptions?: SaveContainerOptions,\n mutationOptions?: {\n expectedStateWhenMissing?: ContainerState | undefined;\n isCurrent?: (() => boolean) | undefined;\n createIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"createIntentSettlement\"];\n moveIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"moveIntentSettlement\"];\n preserveDurableStructureWhenPending?: boolean | undefined;\n },\n ) => Promise<PersistContainerStateResult>;\n requestDocumentPriming?: (() => void) | undefined;\n updateSnapshot: () => void;\n}\n\nexport interface ContainerParentHydrationLane {\n parentId: string | null;\n watermark?: ListContainersResponse[\"nextWatermark\"];\n}\n\nexport interface ExpectedContainerState {\n container: ContainerRecord;\n fingerprint: string;\n}\n\nexport interface FetchedContainerParentLanePage {\n expectedContainerStates: ReadonlyMap<string, ExpectedContainerState>;\n expectedHydrationTombstones: ReadonlyMap<string, ContainerHydrationTombstone>;\n lane: ContainerParentHydrationLane;\n response: ListContainersResponse;\n syncLane: ReturnType<typeof createContainerParentSyncLane>;\n}\n"]}
@@ -1,15 +1,15 @@
1
1
  import type { ApiClient } from "../../../internal/api-client/index.js";
2
- import { KeyingVerificationError, type ManagedPrincipalKind, type ReferencedPrincipalHead, type VerifiedPrincipalPolicy } from "../../../internal/crypto/index.js";
2
+ import { KeyingVerificationError, type ReferencedPrincipalHead, type VerifiedPrincipalPolicy } from "../../../internal/crypto/index.js";
3
3
  import type { CommitOrganizationGroupPolicyRequest } from "../../../internal/validators/request/index.js";
4
4
  import type { CommitOrganizationGroupPolicyResponse, PrincipalPolicyBundleResponse } from "../../../internal/validators/response/index.js";
5
5
  import type { ContainerShareApi } from "../../../data/containers/shared/types.js";
6
6
  import type { ExecSql } from "../../../data/sqlite/sqlSchema.js";
7
7
  import type { TrustedUserIdentityResolver } from "../../../data/trustedUserIdentity/index.js";
8
8
  import { type GroupPolicyNameReader } from "../../organizations/principalPolicyRequest.js";
9
- export interface ContainerManagedPrincipalShareApi extends ContainerShareApi, Pick<ApiClient, "evictContainerWriterProjection" | "getContainerWriterProjectionResult"> {
9
+ import type { CurrentPrincipalPolicyReader } from "../../principals/currentPrincipalPolicyReader.js";
10
+ export interface ContainerManagedPrincipalShareApi extends ContainerShareApi, CurrentPrincipalPolicyReader, Pick<ApiClient, "evictContainerWriterProjection" | "getContainerWriterProjectionResult"> {
10
11
  readonly recoverPendingPrincipalMutation?: ((organizationId: string) => Promise<void>) | undefined;
11
12
  commitOrganizationGroupPolicy: (organizationId: string, groupId: string, input: CommitOrganizationGroupPolicyRequest) => Promise<CommitOrganizationGroupPolicyResponse | null>;
12
- getCurrentPrincipalPolicy: (principalType: ManagedPrincipalKind, principalId: string) => Promise<PrincipalPolicyBundleResponse | null>;
13
13
  }
14
14
  export interface VerifiedSharePrincipalPolicy {
15
15
  readonly bundle: PrincipalPolicyBundleResponse;
@@ -130,8 +130,8 @@ async function assertShareGroupName(input) {
130
130
  }
131
131
  export async function loadVerifiedGroupSharePrincipalPolicy(input) {
132
132
  const organizationAdminPolicy = await loadOrganizationExternalAdminPolicy({
133
+ currentPolicyReader: input.apiClient,
133
134
  execSql: input.execSql,
134
- getCurrentPrincipalPolicy: (principalType, principalId) => input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),
135
135
  organizationId: input.organizationId,
136
136
  resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
137
137
  stillCurrent: input.stillCurrent,
@@ -1 +1 @@
1
- {"version":3,"file":"sharePrincipalPolicy.js","sourceRoot":"","sources":["sharePrincipalPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport {\n KeyingVerificationError,\n type ManagedPrincipalKind,\n type PrincipalPolicyCheckpoint,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type { CommitOrganizationGroupPolicyRequest } from \"@tearleads/validators/request\";\nimport type {\n CommitOrganizationGroupPolicyResponse,\n PrincipalPolicyBundleResponse,\n} from \"@tearleads/validators/response\";\nimport type { ContainerShareApi } from \"../../../data/containers/shared/types\";\nimport { throwKeyingVerificationErrorWithContext } from \"../../../data/keyingProjectionVerification/error\";\nimport { advanceKeyingCheckpointsAtomically } from \"../../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../../data/persistence/keyingCheckpointPersistence\";\nimport { savePrincipalPolicyBundle } from \"../../../data/persistence/principalPolicyPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../../data/persistence/principalPolicyReferencePersistence\";\nimport { retainVerifiedPrincipalPolicyBundle } from \"../../../data/persistence/verifiedPrincipalPolicyRetentionPersistence\";\nimport {\n principalHeadMatchesReference,\n requireOrganizationGroupHead,\n} from \"../../../data/principals/organizationAuthorityDescriptor\";\nimport {\n principalPolicyReferenceFromBundle,\n verifyPrincipalPolicyBundleWithExternalOrganizationAdmins,\n} from \"../../../data/principals/principalPolicyAdminSigners\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../../data/trustedUserIdentity\";\nimport { assertGroupMetadataBinding } from \"../../organizations/groupMetadataBinding\";\nimport {\n type GroupPolicyNameReader,\n groupPolicyNameMismatch,\n} from \"../../organizations/principalPolicyRequest\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"../../principals/externalAdminPolicy\";\nimport {\n collectPrincipalPolicySignerPublicKeys,\n type PrincipalPolicySignerPublicKeyLoadErrorCode,\n} from \"../../principals/policyVerification\";\n\nexport interface ContainerManagedPrincipalShareApi\n extends ContainerShareApi,\n Pick<\n ApiClient,\n \"evictContainerWriterProjection\" | \"getContainerWriterProjectionResult\"\n > {\n readonly recoverPendingPrincipalMutation?:\n | ((organizationId: string) => Promise<void>)\n | undefined;\n commitOrganizationGroupPolicy: (\n organizationId: string,\n groupId: string,\n input: CommitOrganizationGroupPolicyRequest,\n ) => Promise<CommitOrganizationGroupPolicyResponse | null>;\n getCurrentPrincipalPolicy: (\n principalType: ManagedPrincipalKind,\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n}\n\nexport interface VerifiedSharePrincipalPolicy {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly checkpointPolicies: readonly VerifiedPrincipalPolicy[];\n readonly dependencyBundles: readonly PrincipalPolicyBundleResponse[];\n readonly organizationId: string;\n readonly policy: VerifiedPrincipalPolicy;\n}\n\nasync function retainVerifiedSharePolicies(input: {\n bundle: PrincipalPolicyBundleResponse;\n execSql: ExecSql;\n organizationId: string;\n organizationPolicy: VerifiedExternalAdminPolicy | null;\n policy: VerifiedPrincipalPolicy;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<VerifiedPrincipalPolicy[]> {\n const retainedAt = new Date().toISOString();\n if (input.organizationPolicy) {\n for (const entry of externalAdminPolicyPersistenceEntries(\n input.organizationPolicy,\n )) {\n await retainVerifiedPrincipalPolicyBundle({\n ...entry,\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: retainedAt,\n });\n }\n }\n await retainVerifiedPrincipalPolicyBundle({\n bundle: input.bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n policy: input.policy,\n stillCurrent: input.stillCurrent,\n updatedAt: retainedAt,\n });\n return [\n ...(input.organizationPolicy\n ? externalAdminPolicyPersistenceEntries(input.organizationPolicy).map(\n (entry) => entry.policy,\n )\n : []),\n input.policy,\n ];\n}\n\n/**\n * Commits the verification a loadVerifiedGroupSharePrincipalPolicy call\n * deferred: advances the keying checkpoints for the verified policies and\n * caches the dependency bundles they were verified against.\n */\nexport async function advanceVerifiedSharePolicies(\n execSql: ExecSql,\n verified: Pick<\n VerifiedSharePrincipalPolicy,\n \"checkpointPolicies\" | \"dependencyBundles\" | \"organizationId\"\n >,\n stillCurrent?: (() => boolean) | undefined,\n): Promise<void> {\n await advanceKeyingCheckpointsAtomically({\n access: [],\n execSql,\n organizationId: verified.organizationId,\n policies: verified.checkpointPolicies,\n stillCurrent,\n });\n if (stillCurrent?.() === false) return;\n for (const bundle of verified.dependencyBundles) {\n await savePrincipalPolicyBundle(\n execSql,\n bundle,\n new Date().toISOString(),\n verified.organizationId,\n { stillCurrent },\n );\n if (stillCurrent?.() === false) return;\n }\n}\n\nfunction assertGroupPolicyTarget(\n bundle: PrincipalPolicyBundleResponse,\n groupId: string,\n): void {\n if (\n bundle.currentState.principalType !== \"group\" ||\n bundle.currentState.principalId !== groupId\n ) {\n throw new Error(\"Container share principal policy target mismatch\");\n }\n}\n\nfunction signerPublicKeyLoadErrorMessage(\n code: PrincipalPolicySignerPublicKeyLoadErrorCode,\n): string {\n switch (code) {\n case \"fingerprint-mismatch\":\n return \"principal policy signer key fingerprint mismatch\";\n case \"not-found\":\n return \"principal policy signer key could not be loaded\";\n }\n}\n\nasync function loadGroupSharePolicyBundle(input: {\n apiClient: ContainerManagedPrincipalShareApi;\n execSql: ExecSql;\n expectedGroupHead?: ReferencedPrincipalHead | undefined;\n groupId: string;\n localCheckpoint: PrincipalPolicyCheckpoint | null;\n}): Promise<PrincipalPolicyBundleResponse> {\n if (\n input.expectedGroupHead &&\n (input.expectedGroupHead.principalType !== \"group\" ||\n input.expectedGroupHead.principalId !== input.groupId)\n ) {\n throw new Error(\"Container share expected group policy target mismatch\");\n }\n const bundleFromCache = input.expectedGroupHead\n ? await loadPrincipalPolicyBundleForReference(\n input.execSql,\n input.expectedGroupHead,\n input.localCheckpoint,\n )\n : null;\n let bundle = bundleFromCache;\n bundle ??= await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n input.groupId,\n );\n if (!bundle) {\n throw new Error(\"Container share principal policy could not be loaded\");\n }\n assertGroupPolicyTarget(bundle, input.groupId);\n if (\n input.expectedGroupHead &&\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n input.expectedGroupHead,\n )\n ) {\n throw new Error(\n \"Container share group policy does not match the signed organization directory\",\n );\n }\n return bundle;\n}\n\n/**\n * The chosen group label does not match the name in the group's verified\n * policy. A KeyingVerificationError (so it is reported as an incident), with\n * its own class so a caller can tell this refusal from every other\n * `object_mismatch` and say what happened.\n */\nexport class GroupShareNameMismatchError extends KeyingVerificationError {\n constructor(message: string) {\n super(\"object_mismatch\", message);\n this.name = \"GroupShareNameMismatchError\";\n }\n}\n\n/**\n * The share picker labels groups from the organization read model, which a\n * compromised server can relabel, so the name the user chose must equal the\n * name committed in the target's verified policy. Names compare by their\n * canonical key so look-alike spellings count as the same name. Uniqueness of\n * signed names within an organization is enforced where names enter — group\n * creation verifies every group in the signed directory — so a single\n * verified match identifies the group.\n */\nasync function assertShareGroupName(input: {\n readEncryptedName?: GroupPolicyNameReader | undefined;\n bundle: PrincipalPolicyBundleResponse;\n expectedGroupName: string;\n}): Promise<void> {\n // The label comes from the untrusted read model. Signed names never carry\n // an invisible code point, so a label that does is a look-alike that would\n // only match after canonicalization strips it; refuse it outright.\n const mismatch = await groupPolicyNameMismatch(\n input.bundle,\n input.expectedGroupName,\n input.readEncryptedName,\n );\n if (mismatch === \"forbidden_characters\") {\n throw new GroupShareNameMismatchError(\n \"Container share group name contains control or format characters\",\n );\n }\n if (mismatch === \"name_mismatch\") {\n throw new GroupShareNameMismatchError(\n \"Container share group name does not match the signed group policy\",\n );\n }\n}\n\nexport async function loadVerifiedGroupSharePrincipalPolicy(input: {\n readEncryptedName?: GroupPolicyNameReader | undefined;\n apiClient: ContainerManagedPrincipalShareApi;\n execSql: ExecSql;\n expectedGroupHead?: ReferencedPrincipalHead | undefined;\n /**\n * The display name the user chose the group by. The read model that labels\n * groups is a server projection; only the name committed in the signed\n * group payload can confirm the share lands on the group the user saw.\n */\n expectedGroupName?: string | undefined;\n groupId: string;\n organizationId: string;\n resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<VerifiedSharePrincipalPolicy> {\n const organizationAdminPolicy = 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 stillCurrent: input.stillCurrent,\n });\n if (!organizationAdminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const committedGroupHead = requireOrganizationGroupHead(\n organizationAdminPolicy.descriptor,\n input.groupId,\n );\n if (\n input.expectedGroupHead &&\n !principalHeadMatchesReference(input.expectedGroupHead, committedGroupHead)\n ) {\n throw new Error(\n \"Container share expected group head conflicts with the signed organization directory\",\n );\n }\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"group\",\n input.groupId,\n );\n const bundle = await loadGroupSharePolicyBundle({\n ...input,\n expectedGroupHead: committedGroupHead,\n localCheckpoint,\n });\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n throw new Error(signerPublicKeyLoadErrorMessage(signerPublicKeys.error));\n }\n\n const verified =\n await verifyPrincipalPolicyBundleWithExternalOrganizationAdmins({\n bundle,\n expectedReference: committedGroupHead,\n loadExternalAuthority: async () =>\n organizationAdminPolicy.externalAuthority,\n localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throwKeyingVerificationErrorWithContext(\n verified.error,\n \"Container share principal policy verification failed\",\n );\n }\n assertGroupMetadataBinding(bundle, organizationAdminPolicy.descriptor);\n if (input.expectedGroupName !== undefined) {\n await assertShareGroupName({\n readEncryptedName: input.readEncryptedName,\n bundle,\n expectedGroupName: input.expectedGroupName,\n });\n }\n\n const checkpointPolicies = await retainVerifiedSharePolicies({\n bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n organizationPolicy: organizationAdminPolicy,\n policy: verified.value,\n stillCurrent: input.stillCurrent,\n });\n return {\n bundle,\n checkpointPolicies,\n dependencyBundles: externalAdminPolicyPersistenceEntries(\n organizationAdminPolicy,\n ).map((entry) => entry.bundle),\n organizationId: input.organizationId,\n policy: verified.value,\n };\n}\n"]}
1
+ {"version":3,"file":"sharePrincipalPolicy.js","sourceRoot":"","sources":["sharePrincipalPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport {\n KeyingVerificationError,\n type PrincipalPolicyCheckpoint,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type { CommitOrganizationGroupPolicyRequest } from \"@tearleads/validators/request\";\nimport type {\n CommitOrganizationGroupPolicyResponse,\n PrincipalPolicyBundleResponse,\n} from \"@tearleads/validators/response\";\nimport type { ContainerShareApi } from \"../../../data/containers/shared/types\";\nimport { throwKeyingVerificationErrorWithContext } from \"../../../data/keyingProjectionVerification/error\";\nimport { advanceKeyingCheckpointsAtomically } from \"../../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../../data/persistence/keyingCheckpointPersistence\";\nimport { savePrincipalPolicyBundle } from \"../../../data/persistence/principalPolicyPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../../data/persistence/principalPolicyReferencePersistence\";\nimport { retainVerifiedPrincipalPolicyBundle } from \"../../../data/persistence/verifiedPrincipalPolicyRetentionPersistence\";\nimport {\n principalHeadMatchesReference,\n requireOrganizationGroupHead,\n} from \"../../../data/principals/organizationAuthorityDescriptor\";\nimport {\n principalPolicyReferenceFromBundle,\n verifyPrincipalPolicyBundleWithExternalOrganizationAdmins,\n} from \"../../../data/principals/principalPolicyAdminSigners\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../../data/trustedUserIdentity\";\nimport { assertGroupMetadataBinding } from \"../../organizations/groupMetadataBinding\";\nimport {\n type GroupPolicyNameReader,\n groupPolicyNameMismatch,\n} from \"../../organizations/principalPolicyRequest\";\nimport type { CurrentPrincipalPolicyReader } from \"../../principals/currentPrincipalPolicyReader\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"../../principals/externalAdminPolicy\";\nimport {\n collectPrincipalPolicySignerPublicKeys,\n type PrincipalPolicySignerPublicKeyLoadErrorCode,\n} from \"../../principals/policyVerification\";\n\nexport interface ContainerManagedPrincipalShareApi\n extends ContainerShareApi,\n CurrentPrincipalPolicyReader,\n Pick<\n ApiClient,\n \"evictContainerWriterProjection\" | \"getContainerWriterProjectionResult\"\n > {\n readonly recoverPendingPrincipalMutation?:\n | ((organizationId: string) => Promise<void>)\n | undefined;\n commitOrganizationGroupPolicy: (\n organizationId: string,\n groupId: string,\n input: CommitOrganizationGroupPolicyRequest,\n ) => Promise<CommitOrganizationGroupPolicyResponse | null>;\n}\n\nexport interface VerifiedSharePrincipalPolicy {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly checkpointPolicies: readonly VerifiedPrincipalPolicy[];\n readonly dependencyBundles: readonly PrincipalPolicyBundleResponse[];\n readonly organizationId: string;\n readonly policy: VerifiedPrincipalPolicy;\n}\n\nasync function retainVerifiedSharePolicies(input: {\n bundle: PrincipalPolicyBundleResponse;\n execSql: ExecSql;\n organizationId: string;\n organizationPolicy: VerifiedExternalAdminPolicy | null;\n policy: VerifiedPrincipalPolicy;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<VerifiedPrincipalPolicy[]> {\n const retainedAt = new Date().toISOString();\n if (input.organizationPolicy) {\n for (const entry of externalAdminPolicyPersistenceEntries(\n input.organizationPolicy,\n )) {\n await retainVerifiedPrincipalPolicyBundle({\n ...entry,\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: retainedAt,\n });\n }\n }\n await retainVerifiedPrincipalPolicyBundle({\n bundle: input.bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n policy: input.policy,\n stillCurrent: input.stillCurrent,\n updatedAt: retainedAt,\n });\n return [\n ...(input.organizationPolicy\n ? externalAdminPolicyPersistenceEntries(input.organizationPolicy).map(\n (entry) => entry.policy,\n )\n : []),\n input.policy,\n ];\n}\n\n/**\n * Commits the verification a loadVerifiedGroupSharePrincipalPolicy call\n * deferred: advances the keying checkpoints for the verified policies and\n * caches the dependency bundles they were verified against.\n */\nexport async function advanceVerifiedSharePolicies(\n execSql: ExecSql,\n verified: Pick<\n VerifiedSharePrincipalPolicy,\n \"checkpointPolicies\" | \"dependencyBundles\" | \"organizationId\"\n >,\n stillCurrent?: (() => boolean) | undefined,\n): Promise<void> {\n await advanceKeyingCheckpointsAtomically({\n access: [],\n execSql,\n organizationId: verified.organizationId,\n policies: verified.checkpointPolicies,\n stillCurrent,\n });\n if (stillCurrent?.() === false) return;\n for (const bundle of verified.dependencyBundles) {\n await savePrincipalPolicyBundle(\n execSql,\n bundle,\n new Date().toISOString(),\n verified.organizationId,\n { stillCurrent },\n );\n if (stillCurrent?.() === false) return;\n }\n}\n\nfunction assertGroupPolicyTarget(\n bundle: PrincipalPolicyBundleResponse,\n groupId: string,\n): void {\n if (\n bundle.currentState.principalType !== \"group\" ||\n bundle.currentState.principalId !== groupId\n ) {\n throw new Error(\"Container share principal policy target mismatch\");\n }\n}\n\nfunction signerPublicKeyLoadErrorMessage(\n code: PrincipalPolicySignerPublicKeyLoadErrorCode,\n): string {\n switch (code) {\n case \"fingerprint-mismatch\":\n return \"principal policy signer key fingerprint mismatch\";\n case \"not-found\":\n return \"principal policy signer key could not be loaded\";\n }\n}\n\nasync function loadGroupSharePolicyBundle(input: {\n apiClient: ContainerManagedPrincipalShareApi;\n execSql: ExecSql;\n expectedGroupHead?: ReferencedPrincipalHead | undefined;\n groupId: string;\n localCheckpoint: PrincipalPolicyCheckpoint | null;\n}): Promise<PrincipalPolicyBundleResponse> {\n if (\n input.expectedGroupHead &&\n (input.expectedGroupHead.principalType !== \"group\" ||\n input.expectedGroupHead.principalId !== input.groupId)\n ) {\n throw new Error(\"Container share expected group policy target mismatch\");\n }\n const bundleFromCache = input.expectedGroupHead\n ? await loadPrincipalPolicyBundleForReference(\n input.execSql,\n input.expectedGroupHead,\n input.localCheckpoint,\n )\n : null;\n let bundle = bundleFromCache;\n bundle ??= await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n input.groupId,\n );\n if (!bundle) {\n throw new Error(\"Container share principal policy could not be loaded\");\n }\n assertGroupPolicyTarget(bundle, input.groupId);\n if (\n input.expectedGroupHead &&\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n input.expectedGroupHead,\n )\n ) {\n throw new Error(\n \"Container share group policy does not match the signed organization directory\",\n );\n }\n return bundle;\n}\n\n/**\n * The chosen group label does not match the name in the group's verified\n * policy. A KeyingVerificationError (so it is reported as an incident), with\n * its own class so a caller can tell this refusal from every other\n * `object_mismatch` and say what happened.\n */\nexport class GroupShareNameMismatchError extends KeyingVerificationError {\n constructor(message: string) {\n super(\"object_mismatch\", message);\n this.name = \"GroupShareNameMismatchError\";\n }\n}\n\n/**\n * The share picker labels groups from the organization read model, which a\n * compromised server can relabel, so the name the user chose must equal the\n * name committed in the target's verified policy. Names compare by their\n * canonical key so look-alike spellings count as the same name. Uniqueness of\n * signed names within an organization is enforced where names enter — group\n * creation verifies every group in the signed directory — so a single\n * verified match identifies the group.\n */\nasync function assertShareGroupName(input: {\n readEncryptedName?: GroupPolicyNameReader | undefined;\n bundle: PrincipalPolicyBundleResponse;\n expectedGroupName: string;\n}): Promise<void> {\n // The label comes from the untrusted read model. Signed names never carry\n // an invisible code point, so a label that does is a look-alike that would\n // only match after canonicalization strips it; refuse it outright.\n const mismatch = await groupPolicyNameMismatch(\n input.bundle,\n input.expectedGroupName,\n input.readEncryptedName,\n );\n if (mismatch === \"forbidden_characters\") {\n throw new GroupShareNameMismatchError(\n \"Container share group name contains control or format characters\",\n );\n }\n if (mismatch === \"name_mismatch\") {\n throw new GroupShareNameMismatchError(\n \"Container share group name does not match the signed group policy\",\n );\n }\n}\n\nexport async function loadVerifiedGroupSharePrincipalPolicy(input: {\n readEncryptedName?: GroupPolicyNameReader | undefined;\n apiClient: ContainerManagedPrincipalShareApi;\n execSql: ExecSql;\n expectedGroupHead?: ReferencedPrincipalHead | undefined;\n /**\n * The display name the user chose the group by. The read model that labels\n * groups is a server projection; only the name committed in the signed\n * group payload can confirm the share lands on the group the user saw.\n */\n expectedGroupName?: string | undefined;\n groupId: string;\n organizationId: string;\n resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<VerifiedSharePrincipalPolicy> {\n const organizationAdminPolicy = await loadOrganizationExternalAdminPolicy({\n currentPolicyReader: input.apiClient,\n execSql: input.execSql,\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n });\n if (!organizationAdminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const committedGroupHead = requireOrganizationGroupHead(\n organizationAdminPolicy.descriptor,\n input.groupId,\n );\n if (\n input.expectedGroupHead &&\n !principalHeadMatchesReference(input.expectedGroupHead, committedGroupHead)\n ) {\n throw new Error(\n \"Container share expected group head conflicts with the signed organization directory\",\n );\n }\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"group\",\n input.groupId,\n );\n const bundle = await loadGroupSharePolicyBundle({\n ...input,\n expectedGroupHead: committedGroupHead,\n localCheckpoint,\n });\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n throw new Error(signerPublicKeyLoadErrorMessage(signerPublicKeys.error));\n }\n\n const verified =\n await verifyPrincipalPolicyBundleWithExternalOrganizationAdmins({\n bundle,\n expectedReference: committedGroupHead,\n loadExternalAuthority: async () =>\n organizationAdminPolicy.externalAuthority,\n localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throwKeyingVerificationErrorWithContext(\n verified.error,\n \"Container share principal policy verification failed\",\n );\n }\n assertGroupMetadataBinding(bundle, organizationAdminPolicy.descriptor);\n if (input.expectedGroupName !== undefined) {\n await assertShareGroupName({\n readEncryptedName: input.readEncryptedName,\n bundle,\n expectedGroupName: input.expectedGroupName,\n });\n }\n\n const checkpointPolicies = await retainVerifiedSharePolicies({\n bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n organizationPolicy: organizationAdminPolicy,\n policy: verified.value,\n stillCurrent: input.stillCurrent,\n });\n return {\n bundle,\n checkpointPolicies,\n dependencyBundles: externalAdminPolicyPersistenceEntries(\n organizationAdminPolicy,\n ).map((entry) => entry.bundle),\n organizationId: input.organizationId,\n policy: verified.value,\n };\n}\n"]}
@@ -11,8 +11,8 @@ export async function deleteOrganizationGroup(input) {
11
11
  await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);
12
12
  assertProjectionVerificationCurrent(stillCurrent);
13
13
  const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({
14
+ currentPolicyReader: input.apiClient,
14
15
  execSql: input.execSql,
15
- getCurrentPrincipalPolicy: (principalType, principalId) => input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),
16
16
  organizationId: input.organizationId,
17
17
  resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
18
18
  stillCurrent,