@tearleads/client-sdk 0.1.102 → 0.1.103

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.
@@ -118,6 +118,8 @@ export interface ContainerMoveApi extends ContainerReciteApi {
118
118
  moveContainerResult?(containerId: string, input: ContainerRotationRequest, options?: ContainerMutationRequestOptions): Promise<ContainerRotationResult>;
119
119
  }
120
120
  export interface ContainerRevokeApi extends ContainerReciteApi {
121
+ evictContainerWriterProjection?(containerId: string): void;
122
+ evictWriterProjectionsCiting?(containerIds: readonly string[]): void;
121
123
  getContainerWriterProjection(containerId: string): Promise<ContainerWriterProjectionResponse | null>;
122
124
  revokeContainer(containerId: string, input: ContainerRotationRequest, options?: ContainerMutationRequestOptions): Promise<ContainerRotationResponse | null>;
123
125
  /** Optional status-bearing variant; without it nothing can be carried. */
@@ -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 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 /** Tells a stale path, which a fresh projection clears, from a refusal. */\n shareContainerResult?(\n containerId: string,\n input: ContainerMutationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerMutationSubmitResult<ContainerMutationResponse>>;\n evictContainerWriterProjection?(containerId: string): void;\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 /** Tells a stale path, which a fresh projection clears, from a refusal. */\n shareContainerResult?(\n containerId: string,\n input: ContainerMutationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerMutationSubmitResult<ContainerMutationResponse>>;\n evictContainerWriterProjection?(containerId: string): void;\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 evictContainerWriterProjection?(containerId: string): void;\n evictWriterProjectionsCiting?(containerIds: readonly string[]): void;\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tearleads/client-sdk",
3
- "version": "0.1.102",
3
+ "version": "0.1.103",
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,36 +1,6 @@
1
- import { invalidateContainerProjections } from "../../../workflows/container-contents/container-state/projectionCache.js";
2
- /**
3
- * Drop every cached writer projection a hint invalidates, before hydration
4
- * lands, so the next share or move fetches a fresh one instead of submitting
5
- * against a stale manifest and conflicting. Descendants cite the same ancestor
6
- * manifests in their paths, so the locally known subtree under each invalidated
7
- * container is dropped too (an unhydrated subtree has nothing cached). Both
8
- * layers go: the in-memory copies on each container state, and the api-client's
9
- * cached writer projections that cite an invalidated container. Document
10
- * projections cite their paths too, so they are found by citation rather than
11
- * through the links, which live in SQLite; projections citing none of them stay
12
- * cached (#2395). Each open document store drops its own in-memory copy on the
13
- * same hint.
14
- */
1
+ import { invalidateContainerProjectionCaches } from "../../../workflows/container-contents/container-state/projectionCache.js";
2
+ /** Store-facing adapter shared by realtime event handlers. */
15
3
  export function invalidateCachedProjections(state, invalidated) {
16
- if (invalidated.length === 0)
17
- return;
18
- const roots = new Set(invalidated);
19
- const stale = new Set(invalidated);
20
- for (const [id, containerState] of state.containersById) {
21
- let parentId = containerState.container.parentId;
22
- for (let depth = 0; parentId !== null &&
23
- !roots.has(parentId) &&
24
- depth < state.containersById.size; depth++)
25
- parentId = state.containersById.get(parentId)?.container.parentId ?? null;
26
- if (parentId !== null)
27
- stale.add(id);
28
- }
29
- for (const id of stale) {
30
- const containerState = state.containersById.get(id);
31
- if (containerState)
32
- invalidateContainerProjections(containerState);
33
- }
34
- state.runtime.apiClient.evictWriterProjectionsCiting(invalidated);
4
+ invalidateContainerProjectionCaches(state, invalidated);
35
5
  }
36
6
  //# sourceMappingURL=remoteEventProjectionInvalidation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"remoteEventProjectionInvalidation.js","sourceRoot":"","sources":["remoteEventProjectionInvalidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,uEAAuE,CAAC;AAGvH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,2BAA2B,CACzC,KAA0E,EAC1E,WAA8B;IAE9B,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACrC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IACnC,KAAK,MAAM,CAAC,EAAE,EAAE,cAAc,CAAC,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QACxD,IAAI,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,QAAQ,CAAC;QACjD,KACE,IAAI,KAAK,GAAG,CAAC,EACb,QAAQ,KAAK,IAAI;YACjB,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;YACpB,KAAK,GAAG,KAAK,CAAC,cAAc,CAAC,IAAI,EACjC,KAAK,EAAE;YAEP,QAAQ,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC,QAAQ,IAAI,IAAI,CAAC;QAC5E,IAAI,QAAQ,KAAK,IAAI;YAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpD,IAAI,cAAc;YAAE,8BAA8B,CAAC,cAAc,CAAC,CAAC;IACrE,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,4BAA4B,CAAC,WAAW,CAAC,CAAC;AACpE,CAAC","sourcesContent":["import { invalidateContainerProjections } from \"../../../workflows/container-contents/container-state/projectionCache\";\nimport type { ContainerContentsStoreSyncState } from \"../syncAgent/syncAgentTypes\";\n\n/**\n * Drop every cached writer projection a hint invalidates, before hydration\n * lands, so the next share or move fetches a fresh one instead of submitting\n * against a stale manifest and conflicting. Descendants cite the same ancestor\n * manifests in their paths, so the locally known subtree under each invalidated\n * container is dropped too (an unhydrated subtree has nothing cached). Both\n * layers go: the in-memory copies on each container state, and the api-client's\n * cached writer projections that cite an invalidated container. Document\n * projections cite their paths too, so they are found by citation rather than\n * through the links, which live in SQLite; projections citing none of them stay\n * cached (#2395). Each open document store drops its own in-memory copy on the\n * same hint.\n */\nexport function invalidateCachedProjections(\n state: Pick<ContainerContentsStoreSyncState, \"containersById\" | \"runtime\">,\n invalidated: readonly string[],\n): void {\n if (invalidated.length === 0) return;\n const roots = new Set(invalidated);\n const stale = new Set(invalidated);\n for (const [id, containerState] of state.containersById) {\n let parentId = containerState.container.parentId;\n for (\n let depth = 0;\n parentId !== null &&\n !roots.has(parentId) &&\n depth < state.containersById.size;\n depth++\n )\n parentId = state.containersById.get(parentId)?.container.parentId ?? null;\n if (parentId !== null) stale.add(id);\n }\n for (const id of stale) {\n const containerState = state.containersById.get(id);\n if (containerState) invalidateContainerProjections(containerState);\n }\n state.runtime.apiClient.evictWriterProjectionsCiting(invalidated);\n}\n"]}
1
+ {"version":3,"file":"remoteEventProjectionInvalidation.js","sourceRoot":"","sources":["remoteEventProjectionInvalidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,uEAAuE,CAAC;AAG5H,8DAA8D;AAC9D,MAAM,UAAU,2BAA2B,CACzC,KAA0E,EAC1E,WAA8B;IAE9B,mCAAmC,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;AAC1D,CAAC","sourcesContent":["import { invalidateContainerProjectionCaches } from \"../../../workflows/container-contents/container-state/projectionCache\";\nimport type { ContainerContentsStoreSyncState } from \"../syncAgent/syncAgentTypes\";\n\n/** Store-facing adapter shared by realtime event handlers. */\nexport function invalidateCachedProjections(\n state: Pick<ContainerContentsStoreSyncState, \"containersById\" | \"runtime\">,\n invalidated: readonly string[],\n): void {\n invalidateContainerProjectionCaches(state, invalidated);\n}\n"]}
@@ -68,4 +68,38 @@ export async function loadContainerWriterProjectionForState(input) {
68
68
  }
69
69
  return projection;
70
70
  }
71
+ /**
72
+ * Drop every cached writer projection a hint or listing invalidates before hydration
73
+ * lands, so the next share or move fetches a fresh one instead of submitting
74
+ * against a stale manifest and conflicting. Descendants cite the same ancestor
75
+ * manifests in their paths, so the locally known subtree under each invalidated
76
+ * container is dropped too (an unhydrated subtree has nothing cached). Both
77
+ * layers go: the in-memory copies on each container state, and the api-client's
78
+ * cached writer projections that cite an invalidated container. Document
79
+ * projections cite their paths too, so they are found by citation rather than
80
+ * through the links, which live in SQLite; projections citing none of them stay
81
+ * cached (#2395). Each open document store drops its own in-memory copy on the
82
+ * same realtime hint; explicit document pulls obtain fresh projections.
83
+ */
84
+ export function invalidateContainerProjectionCaches(state, invalidated) {
85
+ if (invalidated.length === 0)
86
+ return;
87
+ const roots = new Set(invalidated);
88
+ const stale = new Set(invalidated);
89
+ for (const [id, containerState] of state.containersById) {
90
+ let parentId = containerState.container.parentId;
91
+ for (let depth = 0; parentId !== null &&
92
+ !roots.has(parentId) &&
93
+ depth < state.containersById.size; depth++)
94
+ parentId = state.containersById.get(parentId)?.container.parentId ?? null;
95
+ if (parentId !== null)
96
+ stale.add(id);
97
+ }
98
+ for (const id of stale) {
99
+ const containerState = state.containersById.get(id);
100
+ if (containerState)
101
+ invalidateContainerProjections(containerState);
102
+ }
103
+ state.runtime.apiClient.evictWriterProjectionsCiting(invalidated);
104
+ }
71
105
  //# sourceMappingURL=projectionCache.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"projectionCache.js","sourceRoot":"","sources":["projectionCache.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport { projectionGeneration } from \"../projectionGeneration\";\nimport type { ContainerState } from \"../remoteHydration\";\nimport type { ContainerWorkflowRuntime } from \"./types\";\n\nfunction cachedProjectionMatchesContainerState(input: {\n containerState: ContainerState;\n projection: ContainerWriterProjectionResponse;\n}): boolean {\n const { containerState, projection } = input;\n if (projection.containerId !== containerState.container.id) {\n return false;\n }\n\n const targetManifest = projection.path.at(-1);\n const targetKek = projection.containerKeks.at(-1);\n if (\n !targetManifest ||\n !targetKek ||\n targetKek.containerId !== projection.containerId ||\n targetKek.accessManifestHash !== targetManifest.manifestHash\n ) {\n return false;\n }\n\n const expectedAccessStateHash = containerState.record.accessStateHash;\n return (\n typeof expectedAccessStateHash !== \"string\" ||\n expectedAccessStateHash.length === 0 ||\n targetManifest.manifestHash === expectedAccessStateHash\n );\n}\n\nexport function getCachedContainerWriterProjection(\n containerState: ContainerState,\n): ContainerWriterProjectionResponse | null {\n const projection = containerState.containerWriterProjection ?? null;\n return projection &&\n cachedProjectionMatchesContainerState({ containerState, projection })\n ? projection\n : null;\n}\n\n// A hint can invalidate a container while its load is in flight; each retry\n// re-reads the generation, so this only bounds a pathological hint storm.\nconst MAX_PROJECTION_LOAD_ATTEMPTS = 3;\n\n/**\n * Drop both cached writer projections (the container's and its metadata\n * document's) and advance their shared generation, so an operation that\n * started before this invalidation (and may carry a pre-hint manifest) cannot\n * install its result.\n */\nexport function invalidateContainerProjections(\n containerState: ContainerState,\n): void {\n containerState.containerWriterProjection = null;\n containerState.metadataWriterProjection = null;\n containerState.writerProjectionGeneration =\n projectionGeneration(containerState) + 1;\n}\n\n/**\n * Install a container projection an operation fetched or was handed back,\n * unless a hint invalidated the container while that operation ran: its\n * answer may predate the hint, so the slot is left as the hint left it and the\n * next operation fetches fresh.\n */\nexport function installContainerWriterProjection(\n containerState: ContainerState,\n projection: ContainerWriterProjectionResponse | null,\n capturedGeneration: number,\n): void {\n if (projectionGeneration(containerState) !== capturedGeneration) return;\n containerState.containerWriterProjection = projection;\n}\n\nexport async function loadContainerWriterProjectionForState(input: {\n containerState: ContainerState;\n runtime: ContainerWorkflowRuntime;\n}): Promise<ContainerWriterProjectionResponse | null> {\n const cachedProjection = getCachedContainerWriterProjection(\n input.containerState,\n );\n if (cachedProjection) {\n return cachedProjection;\n }\n\n let projection: ContainerWriterProjectionResponse | null = null;\n for (let attempt = 0; attempt < MAX_PROJECTION_LOAD_ATTEMPTS; attempt++) {\n const generation = projectionGeneration(input.containerState);\n projection = await input.runtime.apiClient.getContainerWriterProjection(\n input.containerState.container.id,\n );\n if (projectionGeneration(input.containerState) === generation) {\n installContainerWriterProjection(\n input.containerState,\n projection,\n generation,\n );\n return projection;\n }\n // Invalidated while in flight (the api-client entry went with it): this\n // answer may predate the hint, so fetch again instead of caching it.\n }\n return projection;\n}\n"]}
1
+ {"version":3,"file":"projectionCache.js","sourceRoot":"","sources":["projectionCache.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport { projectionGeneration } from \"../projectionGeneration\";\nimport type {\n ContainerState,\n RemoteContainerHydrationState,\n} from \"../remoteHydration/types\";\nimport type { ContainerWorkflowRuntime } from \"./types\";\n\nfunction cachedProjectionMatchesContainerState(input: {\n containerState: ContainerState;\n projection: ContainerWriterProjectionResponse;\n}): boolean {\n const { containerState, projection } = input;\n if (projection.containerId !== containerState.container.id) {\n return false;\n }\n\n const targetManifest = projection.path.at(-1);\n const targetKek = projection.containerKeks.at(-1);\n if (\n !targetManifest ||\n !targetKek ||\n targetKek.containerId !== projection.containerId ||\n targetKek.accessManifestHash !== targetManifest.manifestHash\n ) {\n return false;\n }\n\n const expectedAccessStateHash = containerState.record.accessStateHash;\n return (\n typeof expectedAccessStateHash !== \"string\" ||\n expectedAccessStateHash.length === 0 ||\n targetManifest.manifestHash === expectedAccessStateHash\n );\n}\n\nexport function getCachedContainerWriterProjection(\n containerState: ContainerState,\n): ContainerWriterProjectionResponse | null {\n const projection = containerState.containerWriterProjection ?? null;\n return projection &&\n cachedProjectionMatchesContainerState({ containerState, projection })\n ? projection\n : null;\n}\n\n// A hint can invalidate a container while its load is in flight; each retry\n// re-reads the generation, so this only bounds a pathological hint storm.\nconst MAX_PROJECTION_LOAD_ATTEMPTS = 3;\n\n/**\n * Drop both cached writer projections (the container's and its metadata\n * document's) and advance their shared generation, so an operation that\n * started before this invalidation (and may carry a pre-hint manifest) cannot\n * install its result.\n */\nexport function invalidateContainerProjections(\n containerState: ContainerState,\n): void {\n containerState.containerWriterProjection = null;\n containerState.metadataWriterProjection = null;\n containerState.writerProjectionGeneration =\n projectionGeneration(containerState) + 1;\n}\n\n/**\n * Install a container projection an operation fetched or was handed back,\n * unless a hint invalidated the container while that operation ran: its\n * answer may predate the hint, so the slot is left as the hint left it and the\n * next operation fetches fresh.\n */\nexport function installContainerWriterProjection(\n containerState: ContainerState,\n projection: ContainerWriterProjectionResponse | null,\n capturedGeneration: number,\n): void {\n if (projectionGeneration(containerState) !== capturedGeneration) return;\n containerState.containerWriterProjection = projection;\n}\n\nexport async function loadContainerWriterProjectionForState(input: {\n containerState: ContainerState;\n runtime: ContainerWorkflowRuntime;\n}): Promise<ContainerWriterProjectionResponse | null> {\n const cachedProjection = getCachedContainerWriterProjection(\n input.containerState,\n );\n if (cachedProjection) {\n return cachedProjection;\n }\n\n let projection: ContainerWriterProjectionResponse | null = null;\n for (let attempt = 0; attempt < MAX_PROJECTION_LOAD_ATTEMPTS; attempt++) {\n const generation = projectionGeneration(input.containerState);\n projection = await input.runtime.apiClient.getContainerWriterProjection(\n input.containerState.container.id,\n );\n if (projectionGeneration(input.containerState) === generation) {\n installContainerWriterProjection(\n input.containerState,\n projection,\n generation,\n );\n return projection;\n }\n // Invalidated while in flight (the api-client entry went with it): this\n // answer may predate the hint, so fetch again instead of caching it.\n }\n return projection;\n}\n\n/**\n * Drop every cached writer projection a hint or listing invalidates before hydration\n * lands, so the next share or move fetches a fresh one instead of submitting\n * against a stale manifest and conflicting. Descendants cite the same ancestor\n * manifests in their paths, so the locally known subtree under each invalidated\n * container is dropped too (an unhydrated subtree has nothing cached). Both\n * layers go: the in-memory copies on each container state, and the api-client's\n * cached writer projections that cite an invalidated container. Document\n * projections cite their paths too, so they are found by citation rather than\n * through the links, which live in SQLite; projections citing none of them stay\n * cached (#2395). Each open document store drops its own in-memory copy on the\n * same realtime hint; explicit document pulls obtain fresh projections.\n */\nexport function invalidateContainerProjectionCaches(\n state: Pick<RemoteContainerHydrationState, \"containersById\" | \"runtime\">,\n invalidated: readonly string[],\n): void {\n if (invalidated.length === 0) return;\n const roots = new Set(invalidated);\n const stale = new Set(invalidated);\n for (const [id, containerState] of state.containersById) {\n let parentId = containerState.container.parentId;\n for (\n let depth = 0;\n parentId !== null &&\n !roots.has(parentId) &&\n depth < state.containersById.size;\n depth++\n )\n parentId = state.containersById.get(parentId)?.container.parentId ?? null;\n if (parentId !== null) stale.add(id);\n }\n for (const id of stale) {\n const containerState = state.containersById.get(id);\n if (containerState) invalidateContainerProjections(containerState);\n }\n state.runtime.apiClient.evictWriterProjectionsCiting(invalidated);\n}\n"]}
@@ -1 +1 @@
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 { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport type { DocumentRecord } from \"../../../data/persistence/documents/records\";\nimport type {\n createRemoteContainer,\n moveRemoteContainer,\n shareRemoteContainer,\n} from \"../../containers\";\nimport type { createRemoteDocument } from \"../../documents\";\nimport type { shareRemoteContainerWithGroup } from \"../../organizations/groupShare\";\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 { createInitializedContainerMetadataDocument } from \"../metadata/containerMetadataDocument\";\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 /**\n * The metadata seed the create committed, settled by the caller instead of\n * a first metadata sync. Absent when the create carried no seed or fell back\n * to a plain create.\n */\n metadataSeed?:\n | { readonly commitLsn: string | null; readonly updateId: string }\n | undefined;\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 /**\n * Metadata update ids this device sent, so their own update echoes are\n * classified as self-authored (shared with the metadata sync).\n */\n locallyAcceptedMetadataUpdateIds?: Set<string> | undefined;\n /** Metadata document ids of folder creates in flight (shared with hints). */\n plannedMetadataDocumentIds?: Set<string> | undefined;\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\n/** Later passes for moves the server accepted but no pass settled. */\nexport interface AcceptedMoveRetry {\n /** The server holds the move: schedule a replay to settle it. */\n arm: (intentId: string) => void;\n /** A replay failed; continue an accepted move's retries, if it has some. */\n resume: (intentId: string) => void;\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 acceptedMoveRetry: AcceptedMoveRetry;\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 { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport type { DocumentRecord } from \"../../../data/persistence/documents/records\";\nimport type {\n createRemoteContainer,\n moveRemoteContainer,\n shareRemoteContainer,\n} from \"../../containers\";\nimport type { createRemoteDocument } from \"../../documents\";\nimport type { shareRemoteContainerWithGroup } from \"../../organizations/groupShare\";\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 { createInitializedContainerMetadataDocument } from \"../metadata/containerMetadataDocument\";\nimport type {\n ContainerState,\n RemoteContainer as HydratedRemoteContainer,\n RemoteContainerHydrationHost,\n} from \"../remoteHydration/types\";\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 /**\n * The metadata seed the create committed, settled by the caller instead of\n * a first metadata sync. Absent when the create carried no seed or fell back\n * to a plain create.\n */\n metadataSeed?:\n | { readonly commitLsn: string | null; readonly updateId: string }\n | undefined;\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 /**\n * Metadata update ids this device sent, so their own update echoes are\n * classified as self-authored (shared with the metadata sync).\n */\n locallyAcceptedMetadataUpdateIds?: Set<string> | undefined;\n /** Metadata document ids of folder creates in flight (shared with hints). */\n plannedMetadataDocumentIds?: Set<string> | undefined;\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\n/** Later passes for moves the server accepted but no pass settled. */\nexport interface AcceptedMoveRetry {\n /** The server holds the move: schedule a replay to settle it. */\n arm: (intentId: string) => void;\n /** A replay failed; continue an accepted move's retries, if it has some. */\n resume: (intentId: string) => void;\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 acceptedMoveRetry: AcceptedMoveRetry;\n state: ContainerMoveIntentSyncState;\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { isKeyingVerificationError, runWithSecurityIncidentReporting, } from "../../../../data/keyingProjectionVerification/error.js";
2
2
  import { isProjectionVerificationCancelledError } from "../../../../data/keyingProjectionVerification/types.js";
3
3
  import { heldContainerBinding, listingRepeatsHeldOrdinaryBinding, } from "../../../../data/persistence/container-contents/containerBinding.js";
4
+ import { invalidateContainerProjectionCaches } from "../../container-state/projectionCache.js";
4
5
  import { installContainerMetadataRecord } from "../../metadata/metadataPersistence.js";
5
6
  import { projectionGeneration } from "../../projectionGeneration.js";
6
7
  import { verifyRemoteContainerDestination } from "../destination/verifiedDestination.js";
@@ -165,6 +166,21 @@ async function updateExistingRemoteContainerState(input) {
165
166
  });
166
167
  return existingState;
167
168
  }
169
+ /** A pull can observe the same manifest change as a missed realtime hint. */
170
+ function invalidateListedProjectionCaches(input) {
171
+ const held = input.state.containersById.get(input.remoteContainer.id);
172
+ if (held &&
173
+ (held.record.accessStateHash !==
174
+ input.remoteContainer.metadataAccessStateHash ||
175
+ (held.container.effectiveAccessLevel ?? "read") !==
176
+ input.remoteContainer.effectiveAccessLevel)) {
177
+ // Eviction admits no remote state. Drop citations before verification can
178
+ // advance local checkpoints past those cached container/document paths.
179
+ invalidateContainerProjectionCaches(input.state, [
180
+ input.remoteContainer.id,
181
+ ]);
182
+ }
183
+ }
168
184
  export async function upsertRemoteContainerState(input) {
169
185
  let expectedPlacementCheckpoint;
170
186
  const heldState = input.state.containersById.get(input.remoteContainer.id);
@@ -178,6 +194,7 @@ export async function upsertRemoteContainerState(input) {
178
194
  (await input.state.persistence.isSupersededContainerBinding(input.state.runtime.infra.execSql, { containerId: listed.id, organizationId: listed.organizationId }));
179
195
  if (input.isCurrent?.() === false)
180
196
  return null;
197
+ invalidateListedProjectionCaches(input);
181
198
  const verified = await verifyRemoteContainerDestination({
182
199
  ...input,
183
200
  heldBinding,
@@ -1 +1 @@
1
- {"version":3,"file":"remoteContainerState.js","sourceRoot":"","sources":["remoteContainerState.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccessManifestCheckpoint } from \"@tearleads/crypto\";\nimport {\n isKeyingVerificationError,\n runWithSecurityIncidentReporting,\n} from \"../../../../data/keyingProjectionVerification/error\";\nimport { isProjectionVerificationCancelledError } from \"../../../../data/keyingProjectionVerification/types\";\nimport {\n heldContainerBinding,\n listingRepeatsHeldOrdinaryBinding,\n} from \"../../../../data/persistence/container-contents/containerBinding\";\nimport type { ContainerHydrationTombstone } from \"../../containerPersistence\";\nimport { installContainerMetadataRecord } from \"../../metadata/metadataPersistence\";\nimport { projectionGeneration } from \"../../projectionGeneration\";\nimport type { PrefetchedDestinationProjection } from \"../destination/destinationPrefetch\";\nimport { verifyRemoteContainerDestination } from \"../destination/verifiedDestination\";\nimport { rebindReHomedContainer } from \"../heldContainerRebind\";\nimport {\n moveIndexedContainerChild,\n removeIndexedContainerChild,\n} from \"../lanes/childIndex\";\nimport {\n reconcileLocalOnlyRootContainers,\n reconcileLocalOnlySystemContainers,\n} from \"../reconciliation\";\nimport type {\n ContainerChildIndex,\n ContainerState,\n RemoteContainer,\n RemoteContainerHydrationHost,\n RemoteContainerHydrationState,\n} from \"../types\";\nimport { insertRemoteContainerState } from \"./insertRemoteContainer\";\nimport {\n applyRemoteContainerTimestamps,\n remoteContainerHydrationSaveOptions,\n resolveRemoteContainerHydrationLocalUpdatedAt,\n} from \"./remoteContainerTimestamps\";\n\n// Container ids (restricted to the inbound page) that carry an unsynced local\n// create or move intent. Such a container's parent and local-edit timestamp are\n// owned by its structural-intent lane until that lane reconciles, so inbound\n// hydration must not revert parentId to the server value nor collapse\n// localUpdatedAt — doing so silently undoes a queued move and falsely reads\n// \"synced\". Move intents are read via listUnsyncedMoveIntents so a blocked\n// move — one whose destination parent has not synced yet, the common boot-time\n// case — is protected too. Pending *metadata*\n// updates are handled separately (listRemoteContainerIdsWithPendingMetadataUpdates);\n// structural intents live in the local outbox's container lanes, which that\n// query does not cover.\nexport async function listRemoteContainerIdsWithPendingStructuralIntents(input: {\n remoteContainers: ReadonlyArray<RemoteContainer>;\n state: RemoteContainerHydrationState;\n}): Promise<Set<string>> {\n if (input.remoteContainers.length === 0) {\n return new Set();\n }\n const remoteContainerIds = new Set(\n input.remoteContainers.map((remoteContainer) => remoteContainer.id),\n );\n\n const execSql = input.state.runtime.infra.execSql;\n const [pendingCreateIntents, unsyncedMoveIntents] = await Promise.all([\n input.state.persistence.listPendingCreateIntents(execSql),\n input.state.persistence.listUnsyncedMoveIntents(execSql),\n ]);\n const containerIdsWithPendingStructuralIntents = new Set<string>();\n for (const intent of pendingCreateIntents) {\n if (remoteContainerIds.has(intent.containerId)) {\n containerIdsWithPendingStructuralIntents.add(intent.containerId);\n }\n }\n for (const intent of unsyncedMoveIntents) {\n if (remoteContainerIds.has(intent.containerId)) {\n containerIdsWithPendingStructuralIntents.add(intent.containerId);\n }\n }\n return containerIdsWithPendingStructuralIntents;\n}\n\nexport async function listRemoteContainerIdsWithPendingMetadataUpdates(input: {\n remoteContainers: ReadonlyArray<RemoteContainer>;\n state: RemoteContainerHydrationState;\n}): Promise<Set<string>> {\n const containerIds = input.remoteContainers.flatMap((remoteContainer) => {\n const previousLocalUpdatedAt = input.state.containersById.get(\n remoteContainer.id,\n )?.container.localUpdatedAt;\n\n return previousLocalUpdatedAt &&\n previousLocalUpdatedAt.localeCompare(remoteContainer.updatedAt) > 0\n ? [remoteContainer.id]\n : [];\n });\n if (containerIds.length === 0) {\n return new Set();\n }\n\n const execSql = input.state.runtime.infra.execSql;\n return new Set(\n await input.state.persistence.listContainerIdsWithPendingUpdates(\n execSql,\n containerIds,\n ),\n );\n}\n\nfunction removeMissingHydratedContainer(input: {\n childIdsByParentId?: ContainerChildIndex | undefined;\n existingState: ContainerState;\n host: RemoteContainerHydrationHost;\n previousParentId: string | null;\n remoteContainer: RemoteContainer;\n state: RemoteContainerHydrationState;\n}): void {\n const { existingState, remoteContainer, state } = input;\n if (state.containersById.get(remoteContainer.id) !== existingState) {\n return;\n }\n\n state.containersById.delete(remoteContainer.id);\n if (input.childIdsByParentId) {\n removeIndexedContainerChild(\n input.childIdsByParentId,\n remoteContainer.id,\n input.previousParentId,\n );\n input.childIdsByParentId.delete(remoteContainer.id);\n }\n input.host.updateSnapshot();\n}\n\nfunction createUpdatedRemoteContainerState(\n existingState: ContainerState,\n remoteContainer: RemoteContainer,\n): ContainerState {\n return {\n ...existingState,\n container: applyRemoteContainerTimestamps(\n existingState.container,\n remoteContainer,\n ),\n containerWriterProjection: null,\n metadataReferencedPrincipals: remoteContainer.metadataReferencedPrincipals,\n };\n}\n\nfunction installUpdatedRemoteContainerState(\n existingState: ContainerState,\n nextState: ContainerState,\n host: RemoteContainerHydrationHost,\n): void {\n const wasLocalOnly = existingState.container.serverCreatedAt == null;\n existingState.container = nextState.container;\n existingState.containerWriterProjection = nextState.containerWriterProjection;\n existingState.metadataReferencedPrincipals =\n nextState.metadataReferencedPrincipals;\n // The snapshot copied the live generation when it was taken; a hint that\n // landed during the persist leaves the live metadata projection dropped.\n if (projectionGeneration(existingState) === projectionGeneration(nextState))\n existingState.metadataWriterProjection = nextState.metadataWriterProjection;\n installContainerMetadataRecord(existingState, nextState.record);\n // A document pass may have deferred while this root was awaiting proof.\n // The first durable remote acknowledgement makes its pending creates runnable.\n if (wasLocalOnly && existingState.container.serverCreatedAt != null)\n host.requestDocumentPriming?.();\n}\n\nasync function updateExistingRemoteContainerState(input: {\n childIdsByParentId?: ContainerChildIndex | undefined;\n containerIdsWithPendingMetadataUpdates: ReadonlySet<string>;\n containerIdsWithPendingStructuralIntents: ReadonlySet<string>;\n host: RemoteContainerHydrationHost;\n isCurrent?: (() => boolean) | undefined;\n existingState: ContainerState;\n remoteContainer: RemoteContainer;\n state: RemoteContainerHydrationState;\n}): Promise<ContainerState | null> {\n const { childIdsByParentId, existingState, host, remoteContainer, state } =\n input;\n const previousParentId = existingState.container.parentId;\n const previousLocalUpdatedAt = existingState.container.localUpdatedAt;\n // A queued, not-yet-synced local move/create owns this container's parent\n // until its intent lane reconciles. Omit the remote parent when the page scan\n // saw one; persistence also rechecks the intent transactionally so a move\n // created after that scan wins. Both paths preserve the durable local clock.\n const hasPendingStructuralIntent =\n input.containerIdsWithPendingStructuralIntents.has(remoteContainer.id);\n const nextParentId = hasPendingStructuralIntent\n ? undefined\n : remoteContainer.parentId;\n const localUpdatedAt = resolveRemoteContainerHydrationLocalUpdatedAt({\n containerIdsWithPendingMetadataUpdates:\n input.containerIdsWithPendingMetadataUpdates,\n hasPendingStructuralIntent,\n previousLocalUpdatedAt,\n remoteContainer,\n });\n const nextState = createUpdatedRemoteContainerState(\n existingState,\n remoteContainer,\n );\n const persistenceResult = await host.persistContainerState(\n nextState,\n {\n accessEpoch: remoteContainer.metadataAccessEpoch,\n accessStateHash: remoteContainer.metadataAccessStateHash,\n documentId: remoteContainer.metadataDocumentId,\n effectiveAccessLevel: remoteContainer.effectiveAccessLevel,\n metadataDocumentId: remoteContainer.metadataDocumentId,\n systemSlot: remoteContainer.systemSlot ?? null,\n organizationId: remoteContainer.organizationId,\n ...(nextParentId !== undefined ? { parentId: nextParentId } : {}),\n },\n false,\n remoteContainerHydrationSaveOptions({\n localUpdatedAt,\n remoteContainer,\n }),\n {\n preserveDurableStructureWhenPending: true,\n },\n );\n if (persistenceResult.status === \"missing\") {\n // Hydration persists a detached candidate so an in-flight response cannot\n // mutate mapped state before its guards settle. Retire the exact state this\n // hydration read because store-level reference cleanup cannot see the clone.\n removeMissingHydratedContainer({\n childIdsByParentId,\n existingState,\n host,\n previousParentId,\n remoteContainer,\n state,\n });\n return null;\n }\n if (persistenceResult.status !== \"persisted\") return null;\n const { record: nextRecord } = persistenceResult;\n installContainerMetadataRecord(nextState, nextRecord);\n if (input.isCurrent?.() === false) {\n return existingState;\n }\n nextState.container = {\n ...nextState.container,\n metadataDocumentId: remoteContainer.metadataDocumentId,\n organizationId: remoteContainer.organizationId,\n parentId: nextState.container.parentId,\n };\n\n installUpdatedRemoteContainerState(existingState, nextState, host);\n moveIndexedContainerChild(\n childIdsByParentId,\n remoteContainer.id,\n previousParentId,\n existingState.container.parentId,\n );\n await reconcileLocalOnlyRootContainers({\n childIdsByParentId,\n isCurrent: input.isCurrent,\n remoteRootState: existingState,\n requestDocumentPriming: host.requestDocumentPriming,\n state,\n });\n return existingState;\n}\n\ninterface UpsertRemoteContainerStateInput {\n childIdsByParentId?: ContainerChildIndex | undefined;\n containerIdsWithPendingMetadataUpdates: ReadonlySet<string>;\n containerIdsWithPendingStructuralIntents: ReadonlySet<string>;\n host: RemoteContainerHydrationHost;\n expectedHydrationTombstone?: ContainerHydrationTombstone | null | undefined;\n isCurrent?: (() => boolean) | undefined;\n prefetchedProjection?: PrefetchedDestinationProjection | undefined;\n remoteContainer: RemoteContainer;\n state: RemoteContainerHydrationState;\n}\n\nexport async function upsertRemoteContainerState(\n input: UpsertRemoteContainerStateInput,\n): Promise<ContainerState | null> {\n let expectedPlacementCheckpoint: AccessManifestCheckpoint | undefined;\n const heldState = input.state.containersById.get(input.remoteContainer.id);\n const heldBinding = heldState\n ? heldContainerBinding(heldState)\n : await input.state.persistence.loadHeldContainerBinding(\n input.state.runtime.infra.execSql,\n input.remoteContainer.id,\n );\n const listed = input.remoteContainer;\n // A folder re-homed away from the listed organization never returns there,\n // whatever binding (possibly none, after a reset) the device holds now.\n const supersededListing =\n !listingRepeatsHeldOrdinaryBinding(heldBinding, listed) &&\n (await input.state.persistence.isSupersededContainerBinding(\n input.state.runtime.infra.execSql,\n { containerId: listed.id, organizationId: listed.organizationId },\n ));\n if (input.isCurrent?.() === false) return null;\n const verified = await verifyRemoteContainerDestination({\n ...input,\n heldBinding,\n supersededListing,\n refresh: !!input.expectedHydrationTombstone,\n onVerifiedCheckpoint: (checkpoint) => {\n expectedPlacementCheckpoint = checkpoint;\n },\n });\n if (!verified || input.isCurrent?.() === false) return null;\n input = { ...input, remoteContainer: verified };\n const existingState = input.state.containersById.get(\n input.remoteContainer.id,\n );\n const remoteState = await runWithSecurityIncidentReporting(\n input.state.runtime.util.reportSecurityIncident,\n {\n objectId: verified.id,\n objectKind: \"container\",\n operation: \"container.binding.persist\",\n organizationId: verified.organizationId,\n },\n async () =>\n (await rebindReHomedContainer({\n existingState,\n heldBinding,\n isCurrent: input.isCurrent,\n state: input.state,\n verified,\n }))\n ? persistVerifiedRemoteContainer({\n ...input,\n existingState,\n expectedPlacementCheckpoint,\n })\n : null,\n );\n if (!remoteState) {\n return null;\n }\n await reconcileLocalOnlySystemContainers({\n childIdsByParentId: input.childIdsByParentId,\n isCurrent: input.isCurrent,\n requestDocumentPriming: input.host.requestDocumentPriming,\n remoteSystemState: remoteState,\n state: input.state,\n });\n return remoteState;\n}\n\n/**\n * Upsert one listed folder, isolating a rejected proof or binding. The refusal\n * was already reported; returning no state leaves the item unapplied without\n * blocking independent folders.\n */\nexport async function upsertIsolatedRemoteContainerState(\n input: UpsertRemoteContainerStateInput,\n): Promise<ContainerState | null> {\n try {\n return await upsertRemoteContainerState(input);\n } catch (error) {\n if (\n !isKeyingVerificationError(error) &&\n !isProjectionVerificationCancelledError(error)\n )\n throw error;\n return null;\n }\n}\n\nfunction persistVerifiedRemoteContainer(\n input: UpsertRemoteContainerStateInput & {\n existingState: ContainerState | undefined;\n expectedPlacementCheckpoint: AccessManifestCheckpoint | undefined;\n },\n): Promise<ContainerState | null> {\n const { existingState, expectedPlacementCheckpoint } = input;\n return existingState\n ? updateExistingRemoteContainerState({\n childIdsByParentId: input.childIdsByParentId,\n containerIdsWithPendingMetadataUpdates:\n input.containerIdsWithPendingMetadataUpdates,\n containerIdsWithPendingStructuralIntents:\n input.containerIdsWithPendingStructuralIntents,\n existingState,\n host: input.host,\n isCurrent: input.isCurrent,\n remoteContainer: input.remoteContainer,\n state: input.state,\n })\n : insertRemoteContainerState({\n childIdsByParentId: input.childIdsByParentId,\n host: input.host,\n expectedHydrationTombstone: input.expectedHydrationTombstone ?? null,\n expectedPlacementCheckpoint,\n isCurrent: input.isCurrent,\n remoteContainer: input.remoteContainer,\n state: input.state,\n });\n}\n"]}
1
+ {"version":3,"file":"remoteContainerState.js","sourceRoot":"","sources":["remoteContainerState.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccessManifestCheckpoint } from \"@tearleads/crypto\";\nimport {\n isKeyingVerificationError,\n runWithSecurityIncidentReporting,\n} from \"../../../../data/keyingProjectionVerification/error\";\nimport { isProjectionVerificationCancelledError } from \"../../../../data/keyingProjectionVerification/types\";\nimport {\n heldContainerBinding,\n listingRepeatsHeldOrdinaryBinding,\n} from \"../../../../data/persistence/container-contents/containerBinding\";\nimport { invalidateContainerProjectionCaches } from \"../../container-state/projectionCache\";\nimport type { ContainerHydrationTombstone } from \"../../containerPersistence\";\nimport { installContainerMetadataRecord } from \"../../metadata/metadataPersistence\";\nimport { projectionGeneration } from \"../../projectionGeneration\";\nimport type { PrefetchedDestinationProjection } from \"../destination/destinationPrefetch\";\nimport { verifyRemoteContainerDestination } from \"../destination/verifiedDestination\";\nimport { rebindReHomedContainer } from \"../heldContainerRebind\";\nimport {\n moveIndexedContainerChild,\n removeIndexedContainerChild,\n} from \"../lanes/childIndex\";\nimport {\n reconcileLocalOnlyRootContainers,\n reconcileLocalOnlySystemContainers,\n} from \"../reconciliation\";\nimport type {\n ContainerChildIndex,\n ContainerState,\n RemoteContainer,\n RemoteContainerHydrationHost,\n RemoteContainerHydrationState,\n} from \"../types\";\nimport { insertRemoteContainerState } from \"./insertRemoteContainer\";\nimport {\n applyRemoteContainerTimestamps,\n remoteContainerHydrationSaveOptions,\n resolveRemoteContainerHydrationLocalUpdatedAt,\n} from \"./remoteContainerTimestamps\";\n\n// Container ids (restricted to the inbound page) that carry an unsynced local\n// create or move intent. Such a container's parent and local-edit timestamp are\n// owned by its structural-intent lane until that lane reconciles, so inbound\n// hydration must not revert parentId to the server value nor collapse\n// localUpdatedAt — doing so silently undoes a queued move and falsely reads\n// \"synced\". Move intents are read via listUnsyncedMoveIntents so a blocked\n// move — one whose destination parent has not synced yet, the common boot-time\n// case — is protected too. Pending *metadata*\n// updates are handled separately (listRemoteContainerIdsWithPendingMetadataUpdates);\n// structural intents live in the local outbox's container lanes, which that\n// query does not cover.\nexport async function listRemoteContainerIdsWithPendingStructuralIntents(input: {\n remoteContainers: ReadonlyArray<RemoteContainer>;\n state: RemoteContainerHydrationState;\n}): Promise<Set<string>> {\n if (input.remoteContainers.length === 0) {\n return new Set();\n }\n const remoteContainerIds = new Set(\n input.remoteContainers.map((remoteContainer) => remoteContainer.id),\n );\n\n const execSql = input.state.runtime.infra.execSql;\n const [pendingCreateIntents, unsyncedMoveIntents] = await Promise.all([\n input.state.persistence.listPendingCreateIntents(execSql),\n input.state.persistence.listUnsyncedMoveIntents(execSql),\n ]);\n const containerIdsWithPendingStructuralIntents = new Set<string>();\n for (const intent of pendingCreateIntents) {\n if (remoteContainerIds.has(intent.containerId)) {\n containerIdsWithPendingStructuralIntents.add(intent.containerId);\n }\n }\n for (const intent of unsyncedMoveIntents) {\n if (remoteContainerIds.has(intent.containerId)) {\n containerIdsWithPendingStructuralIntents.add(intent.containerId);\n }\n }\n return containerIdsWithPendingStructuralIntents;\n}\n\nexport async function listRemoteContainerIdsWithPendingMetadataUpdates(input: {\n remoteContainers: ReadonlyArray<RemoteContainer>;\n state: RemoteContainerHydrationState;\n}): Promise<Set<string>> {\n const containerIds = input.remoteContainers.flatMap((remoteContainer) => {\n const previousLocalUpdatedAt = input.state.containersById.get(\n remoteContainer.id,\n )?.container.localUpdatedAt;\n\n return previousLocalUpdatedAt &&\n previousLocalUpdatedAt.localeCompare(remoteContainer.updatedAt) > 0\n ? [remoteContainer.id]\n : [];\n });\n if (containerIds.length === 0) {\n return new Set();\n }\n\n const execSql = input.state.runtime.infra.execSql;\n return new Set(\n await input.state.persistence.listContainerIdsWithPendingUpdates(\n execSql,\n containerIds,\n ),\n );\n}\n\nfunction removeMissingHydratedContainer(input: {\n childIdsByParentId?: ContainerChildIndex | undefined;\n existingState: ContainerState;\n host: RemoteContainerHydrationHost;\n previousParentId: string | null;\n remoteContainer: RemoteContainer;\n state: RemoteContainerHydrationState;\n}): void {\n const { existingState, remoteContainer, state } = input;\n if (state.containersById.get(remoteContainer.id) !== existingState) {\n return;\n }\n\n state.containersById.delete(remoteContainer.id);\n if (input.childIdsByParentId) {\n removeIndexedContainerChild(\n input.childIdsByParentId,\n remoteContainer.id,\n input.previousParentId,\n );\n input.childIdsByParentId.delete(remoteContainer.id);\n }\n input.host.updateSnapshot();\n}\n\nfunction createUpdatedRemoteContainerState(\n existingState: ContainerState,\n remoteContainer: RemoteContainer,\n): ContainerState {\n return {\n ...existingState,\n container: applyRemoteContainerTimestamps(\n existingState.container,\n remoteContainer,\n ),\n containerWriterProjection: null,\n metadataReferencedPrincipals: remoteContainer.metadataReferencedPrincipals,\n };\n}\n\nfunction installUpdatedRemoteContainerState(\n existingState: ContainerState,\n nextState: ContainerState,\n host: RemoteContainerHydrationHost,\n): void {\n const wasLocalOnly = existingState.container.serverCreatedAt == null;\n existingState.container = nextState.container;\n existingState.containerWriterProjection = nextState.containerWriterProjection;\n existingState.metadataReferencedPrincipals =\n nextState.metadataReferencedPrincipals;\n // The snapshot copied the live generation when it was taken; a hint that\n // landed during the persist leaves the live metadata projection dropped.\n if (projectionGeneration(existingState) === projectionGeneration(nextState))\n existingState.metadataWriterProjection = nextState.metadataWriterProjection;\n installContainerMetadataRecord(existingState, nextState.record);\n // A document pass may have deferred while this root was awaiting proof.\n // The first durable remote acknowledgement makes its pending creates runnable.\n if (wasLocalOnly && existingState.container.serverCreatedAt != null)\n host.requestDocumentPriming?.();\n}\n\nasync function updateExistingRemoteContainerState(input: {\n childIdsByParentId?: ContainerChildIndex | undefined;\n containerIdsWithPendingMetadataUpdates: ReadonlySet<string>;\n containerIdsWithPendingStructuralIntents: ReadonlySet<string>;\n host: RemoteContainerHydrationHost;\n isCurrent?: (() => boolean) | undefined;\n existingState: ContainerState;\n remoteContainer: RemoteContainer;\n state: RemoteContainerHydrationState;\n}): Promise<ContainerState | null> {\n const { childIdsByParentId, existingState, host, remoteContainer, state } =\n input;\n const previousParentId = existingState.container.parentId;\n const previousLocalUpdatedAt = existingState.container.localUpdatedAt;\n // A queued, not-yet-synced local move/create owns this container's parent\n // until its intent lane reconciles. Omit the remote parent when the page scan\n // saw one; persistence also rechecks the intent transactionally so a move\n // created after that scan wins. Both paths preserve the durable local clock.\n const hasPendingStructuralIntent =\n input.containerIdsWithPendingStructuralIntents.has(remoteContainer.id);\n const nextParentId = hasPendingStructuralIntent\n ? undefined\n : remoteContainer.parentId;\n const localUpdatedAt = resolveRemoteContainerHydrationLocalUpdatedAt({\n containerIdsWithPendingMetadataUpdates:\n input.containerIdsWithPendingMetadataUpdates,\n hasPendingStructuralIntent,\n previousLocalUpdatedAt,\n remoteContainer,\n });\n const nextState = createUpdatedRemoteContainerState(\n existingState,\n remoteContainer,\n );\n const persistenceResult = await host.persistContainerState(\n nextState,\n {\n accessEpoch: remoteContainer.metadataAccessEpoch,\n accessStateHash: remoteContainer.metadataAccessStateHash,\n documentId: remoteContainer.metadataDocumentId,\n effectiveAccessLevel: remoteContainer.effectiveAccessLevel,\n metadataDocumentId: remoteContainer.metadataDocumentId,\n systemSlot: remoteContainer.systemSlot ?? null,\n organizationId: remoteContainer.organizationId,\n ...(nextParentId !== undefined ? { parentId: nextParentId } : {}),\n },\n false,\n remoteContainerHydrationSaveOptions({\n localUpdatedAt,\n remoteContainer,\n }),\n {\n preserveDurableStructureWhenPending: true,\n },\n );\n if (persistenceResult.status === \"missing\") {\n // Hydration persists a detached candidate so an in-flight response cannot\n // mutate mapped state before its guards settle. Retire the exact state this\n // hydration read because store-level reference cleanup cannot see the clone.\n removeMissingHydratedContainer({\n childIdsByParentId,\n existingState,\n host,\n previousParentId,\n remoteContainer,\n state,\n });\n return null;\n }\n if (persistenceResult.status !== \"persisted\") return null;\n const { record: nextRecord } = persistenceResult;\n installContainerMetadataRecord(nextState, nextRecord);\n if (input.isCurrent?.() === false) {\n return existingState;\n }\n nextState.container = {\n ...nextState.container,\n metadataDocumentId: remoteContainer.metadataDocumentId,\n organizationId: remoteContainer.organizationId,\n parentId: nextState.container.parentId,\n };\n\n installUpdatedRemoteContainerState(existingState, nextState, host);\n moveIndexedContainerChild(\n childIdsByParentId,\n remoteContainer.id,\n previousParentId,\n existingState.container.parentId,\n );\n await reconcileLocalOnlyRootContainers({\n childIdsByParentId,\n isCurrent: input.isCurrent,\n remoteRootState: existingState,\n requestDocumentPriming: host.requestDocumentPriming,\n state,\n });\n return existingState;\n}\n\ninterface UpsertRemoteContainerStateInput {\n childIdsByParentId?: ContainerChildIndex | undefined;\n containerIdsWithPendingMetadataUpdates: ReadonlySet<string>;\n containerIdsWithPendingStructuralIntents: ReadonlySet<string>;\n host: RemoteContainerHydrationHost;\n expectedHydrationTombstone?: ContainerHydrationTombstone | null | undefined;\n isCurrent?: (() => boolean) | undefined;\n prefetchedProjection?: PrefetchedDestinationProjection | undefined;\n remoteContainer: RemoteContainer;\n state: RemoteContainerHydrationState;\n}\n\n/** A pull can observe the same manifest change as a missed realtime hint. */\nfunction invalidateListedProjectionCaches(\n input: UpsertRemoteContainerStateInput,\n) {\n const held = input.state.containersById.get(input.remoteContainer.id);\n if (\n held &&\n (held.record.accessStateHash !==\n input.remoteContainer.metadataAccessStateHash ||\n (held.container.effectiveAccessLevel ?? \"read\") !==\n input.remoteContainer.effectiveAccessLevel)\n ) {\n // Eviction admits no remote state. Drop citations before verification can\n // advance local checkpoints past those cached container/document paths.\n invalidateContainerProjectionCaches(input.state, [\n input.remoteContainer.id,\n ]);\n }\n}\n\nexport async function upsertRemoteContainerState(\n input: UpsertRemoteContainerStateInput,\n): Promise<ContainerState | null> {\n let expectedPlacementCheckpoint: AccessManifestCheckpoint | undefined;\n const heldState = input.state.containersById.get(input.remoteContainer.id);\n const heldBinding = heldState\n ? heldContainerBinding(heldState)\n : await input.state.persistence.loadHeldContainerBinding(\n input.state.runtime.infra.execSql,\n input.remoteContainer.id,\n );\n const listed = input.remoteContainer;\n // A folder re-homed away from the listed organization never returns there,\n // whatever binding (possibly none, after a reset) the device holds now.\n const supersededListing =\n !listingRepeatsHeldOrdinaryBinding(heldBinding, listed) &&\n (await input.state.persistence.isSupersededContainerBinding(\n input.state.runtime.infra.execSql,\n { containerId: listed.id, organizationId: listed.organizationId },\n ));\n if (input.isCurrent?.() === false) return null;\n invalidateListedProjectionCaches(input);\n const verified = await verifyRemoteContainerDestination({\n ...input,\n heldBinding,\n supersededListing,\n refresh: !!input.expectedHydrationTombstone,\n onVerifiedCheckpoint: (checkpoint) => {\n expectedPlacementCheckpoint = checkpoint;\n },\n });\n if (!verified || input.isCurrent?.() === false) return null;\n input = { ...input, remoteContainer: verified };\n const existingState = input.state.containersById.get(\n input.remoteContainer.id,\n );\n const remoteState = await runWithSecurityIncidentReporting(\n input.state.runtime.util.reportSecurityIncident,\n {\n objectId: verified.id,\n objectKind: \"container\",\n operation: \"container.binding.persist\",\n organizationId: verified.organizationId,\n },\n async () =>\n (await rebindReHomedContainer({\n existingState,\n heldBinding,\n isCurrent: input.isCurrent,\n state: input.state,\n verified,\n }))\n ? persistVerifiedRemoteContainer({\n ...input,\n existingState,\n expectedPlacementCheckpoint,\n })\n : null,\n );\n if (!remoteState) {\n return null;\n }\n await reconcileLocalOnlySystemContainers({\n childIdsByParentId: input.childIdsByParentId,\n isCurrent: input.isCurrent,\n requestDocumentPriming: input.host.requestDocumentPriming,\n remoteSystemState: remoteState,\n state: input.state,\n });\n return remoteState;\n}\n\n/**\n * Upsert one listed folder, isolating a rejected proof or binding. The refusal\n * was already reported; returning no state leaves the item unapplied without\n * blocking independent folders.\n */\nexport async function upsertIsolatedRemoteContainerState(\n input: UpsertRemoteContainerStateInput,\n): Promise<ContainerState | null> {\n try {\n return await upsertRemoteContainerState(input);\n } catch (error) {\n if (\n !isKeyingVerificationError(error) &&\n !isProjectionVerificationCancelledError(error)\n )\n throw error;\n return null;\n }\n}\n\nfunction persistVerifiedRemoteContainer(\n input: UpsertRemoteContainerStateInput & {\n existingState: ContainerState | undefined;\n expectedPlacementCheckpoint: AccessManifestCheckpoint | undefined;\n },\n): Promise<ContainerState | null> {\n const { existingState, expectedPlacementCheckpoint } = input;\n return existingState\n ? updateExistingRemoteContainerState({\n childIdsByParentId: input.childIdsByParentId,\n containerIdsWithPendingMetadataUpdates:\n input.containerIdsWithPendingMetadataUpdates,\n containerIdsWithPendingStructuralIntents:\n input.containerIdsWithPendingStructuralIntents,\n existingState,\n host: input.host,\n isCurrent: input.isCurrent,\n remoteContainer: input.remoteContainer,\n state: input.state,\n })\n : insertRemoteContainerState({\n childIdsByParentId: input.childIdsByParentId,\n host: input.host,\n expectedHydrationTombstone: input.expectedHydrationTombstone ?? null,\n expectedPlacementCheckpoint,\n isCurrent: input.isCurrent,\n remoteContainer: input.remoteContainer,\n state: input.state,\n });\n}\n"]}
@@ -18,7 +18,7 @@ export type RemoteContainer = Pick<ContainerSummary, "createdAt" | "effectiveAcc
18
18
  export interface ContainerState {
19
19
  containerWriterProjection?: ContainerWriterProjectionResponse | null | undefined;
20
20
  /**
21
- * Bumped whenever a realtime hint drops this container's cached writer
21
+ * Bumped whenever a realtime hint or a pull drops this container's cached writer
22
22
  * projections (container and metadata document alike); an operation that
23
23
  * fetched or carried a projection installs it only if this is unchanged
24
24
  * since it started, so a pre-hint answer never lands after the hint.
@@ -35,6 +35,7 @@ export interface ContainerState {
35
35
  interface RemoteContainerHydrationApi extends Partial<PrincipalPolicyHistoryReader>, CurrentPrincipalPolicyReader {
36
36
  getContainerReplacementAuthorizationsResult(containerId: string, replacesOrganizationId: string): Promise<RequestResult<ContainerReplacementAuthorizationsResponse>>;
37
37
  evictContainerWriterProjection(containerId: string): void;
38
+ evictWriterProjectionsCiting(containerIds: readonly string[]): void;
38
39
  getContainerWriterProjection(containerId: string): Promise<ContainerWriterProjectionResponse | null>;
39
40
  listContainerParentLanes(input: ListContainerParentLanesRequest): Promise<ListContainerParentLanesResponse | null>;
40
41
  }
@@ -1 +1 @@
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 { 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 { createContainerMetadataDocument } from \"../metadata/containerMetadataDocument\";\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 /** Queued metadata rows the server committed, settled in this write. */\n acceptedPendingUpdateIds?: readonly string[] | undefined;\n clearSyncFailure?: boolean | undefined;\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 { 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 { createContainerMetadataDocument } from \"../metadata/containerMetadataDocument\";\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 or a pull 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 evictWriterProjectionsCiting(containerIds: readonly 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 /** Queued metadata rows the server committed, settled in this write. */\n acceptedPendingUpdateIds?: readonly string[] | undefined;\n clearSyncFailure?: boolean | undefined;\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"]}
@@ -24,7 +24,7 @@ export declare function buildMaterializedContainerRevokePlan(input: {
24
24
  targetSecretKey: Uint8Array;
25
25
  warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;
26
26
  }): Promise<MaterializedContainerRevokePlan>;
27
- export declare function revokeRemoteContainer(input: {
27
+ interface RevokeRemoteContainerInput {
28
28
  reportSecurityIncident: SecurityIncidentReporter;
29
29
  apiClient: ContainerRevokeApi;
30
30
  author: ContainerMutationAuthor;
@@ -37,8 +37,12 @@ export declare function revokeRemoteContainer(input: {
37
37
  stillCurrent?: (() => boolean) | undefined;
38
38
  targetSecretKey: Uint8Array;
39
39
  warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;
40
- }): Promise<{
40
+ }
41
+ type RevokedRemoteContainer = {
41
42
  containerKey: Uint8Array;
42
43
  plan: ContainerRevokePlan;
43
44
  response: ContainerRotationResponse;
44
- } | null>;
45
+ };
46
+ /** A concurrent mutation can advance the path while a revoke is in flight. */
47
+ export declare function revokeRemoteContainer(input: RevokeRemoteContainerInput): Promise<RevokedRemoteContainer | null>;
48
+ export {};
@@ -1,6 +1,7 @@
1
- import { computeContainerKekKeyringHash, computeContainerKekPredecessorBridgeHash, deriveContainerKekWrappingPublicKey, } from "../../../internal/crypto/index.js";
1
+ import { computeContainerKekKeyringHash, computeContainerKekPredecessorBridgeHash, deriveContainerKekWrappingPublicKey, KeyingVerificationError, } from "../../../internal/crypto/index.js";
2
2
  import { assertContainerAuthorAccess } from "../../../data/containers/shared/authorAccess.js";
3
3
  import { buildContainerCreateKeyEpoch, signContainerMutationEvent, } from "../../../data/containers/shared/events.js";
4
+ import { isStaleParentContainerPathFailure } from "../../../data/containers/shared/mutationFailures.js";
4
5
  import { asContainerManifestBundle, uniqueSortedManifestHashes, } from "../../../data/containers/shared/projection.js";
5
6
  import { readCanonicalRecord } from "../../../data/keying/canonicalJson.js";
6
7
  import { requireProjectionUserKeyResolver, } from "../../../data/keyingProjectionVerification/index.js";
@@ -162,7 +163,23 @@ export async function buildMaterializedContainerRevokePlan(input) {
162
163
  wraps,
163
164
  });
164
165
  }
165
- export async function revokeRemoteContainer(input) {
166
+ const STALE_PATH = Symbol("stale-revoke-path");
167
+ async function submitRevocation(input, plan, carried) {
168
+ const request = carried.length
169
+ ? { ...plan.request, containerRekeys: [...carried] }
170
+ : plan.request;
171
+ const options = {
172
+ expectedPaymentRequiredOrganizationId: input.author.organizationId,
173
+ };
174
+ const { revokeContainerResult } = input.apiClient;
175
+ return submitContainerRotation({
176
+ plain: () => input.apiClient.revokeContainer(input.containerId, request, options),
177
+ result: revokeContainerResult
178
+ ? () => revokeContainerResult.call(input.apiClient, input.containerId, request, { ...options, reportErrors: false })
179
+ : undefined,
180
+ });
181
+ }
182
+ async function planRevoke(input) {
166
183
  const previousProjection = await input.apiClient.getContainerWriterProjection(input.containerId);
167
184
  if (!previousProjection) {
168
185
  return null;
@@ -179,7 +196,16 @@ export async function revokeRemoteContainer(input) {
179
196
  targetSecretKey: input.targetSecretKey,
180
197
  warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,
181
198
  });
182
- return submitAcknowledgedContainerMutation({
199
+ return { previousProjection, materializedPlan };
200
+ }
201
+ async function revokeRemoteContainerOnce(input, retriesStalePath, onAccepted) {
202
+ const planned = await planRevoke(input);
203
+ if (!planned)
204
+ return null;
205
+ const { previousProjection, materializedPlan } = planned;
206
+ let stalePath = false;
207
+ const revoked = await submitAcknowledgedContainerMutation({
208
+ onAccepted,
183
209
  reportSecurityIncident: input.reportSecurityIncident,
184
210
  recitationPolicies: [],
185
211
  apiClient: input.apiClient,
@@ -209,21 +235,51 @@ export async function revokeRemoteContainer(input) {
209
235
  execSql: input.execSql,
210
236
  plan: materializedPlan.plan,
211
237
  stillCurrent: input.stillCurrent,
212
- submit: (carried) => {
213
- const request = carried.length
214
- ? { ...materializedPlan.plan.request, containerRekeys: [...carried] }
215
- : materializedPlan.plan.request;
216
- const options = {
217
- expectedPaymentRequiredOrganizationId: input.author.organizationId,
218
- };
219
- const { revokeContainerResult } = input.apiClient;
220
- return submitContainerRotation({
221
- plain: () => input.apiClient.revokeContainer(input.containerId, request, options),
222
- result: revokeContainerResult
223
- ? () => revokeContainerResult.call(input.apiClient, input.containerId, request, { ...options, reportErrors: false })
224
- : undefined,
225
- });
238
+ submit: async (carried) => {
239
+ const result = await submitRevocation(input, materializedPlan.plan, carried);
240
+ if (!result.ok &&
241
+ retriesStalePath &&
242
+ isStaleParentContainerPathFailure(result)) {
243
+ stalePath = true;
244
+ // The refused transaction rolled back. Replan it without reporting an
245
+ // error that the caller can repair; a second refusal is terminal.
246
+ return { ok: false, status: result.status, code: result.code };
247
+ }
248
+ return result;
226
249
  },
227
250
  });
251
+ return revoked ?? (stalePath ? STALE_PATH : null);
252
+ }
253
+ async function revokeRemoteContainerAttempt(input, retriesStalePath) {
254
+ let accepted = false;
255
+ try {
256
+ return await revokeRemoteContainerOnce(input, retriesStalePath, () => {
257
+ accepted = true;
258
+ });
259
+ }
260
+ catch (error) {
261
+ // Our own or a carried descendant's cached projection may trail a pin.
262
+ // Refresh only a refused/unsubmitted attempt, never an accepted rotation.
263
+ if (retriesStalePath &&
264
+ !accepted &&
265
+ error instanceof KeyingVerificationError &&
266
+ error.code === "rollback")
267
+ return STALE_PATH;
268
+ throw error;
269
+ }
270
+ }
271
+ /** A concurrent mutation can advance the path while a revoke is in flight. */
272
+ export async function revokeRemoteContainer(input) {
273
+ if (input.stillCurrent?.() === false)
274
+ return null;
275
+ const first = await revokeRemoteContainerAttempt(input, true);
276
+ if (first !== STALE_PATH)
277
+ return first;
278
+ if (input.stillCurrent?.() === false)
279
+ return null;
280
+ input.apiClient.evictContainerWriterProjection?.(input.containerId);
281
+ input.apiClient.evictWriterProjectionsCiting?.([input.containerId]);
282
+ const second = await revokeRemoteContainerAttempt(input, false);
283
+ return second === STALE_PATH ? null : second;
228
284
  }
229
285
  //# sourceMappingURL=revoke.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"revoke.js","sourceRoot":"","sources":["revoke.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccessEvent,\n AccessManifest,\n ContainerAccessManifestState,\n ContainerKekKeyring,\n ContainerKekPredecessorBridge,\n ContainerKeyEpoch,\n ContainerKeyWrap,\n ContainerRevokeAccessEventBody,\n ContainerUserRecipientKey,\n} from \"@tearleads/crypto\";\nimport {\n computeContainerKekKeyringHash,\n computeContainerKekPredecessorBridgeHash,\n deriveContainerKekWrappingPublicKey,\n} from \"@tearleads/crypto\";\nimport type {\n AccessManifestBundleWire,\n ContainerMutationRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n ContainerKekResponse,\n ContainerRotationResponse,\n ContainerWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { assertContainerAuthorAccess } from \"../../../data/containers/shared/authorAccess\";\nimport {\n buildContainerCreateKeyEpoch,\n signContainerMutationEvent,\n} from \"../../../data/containers/shared/events\";\nimport {\n asContainerManifestBundle,\n uniqueSortedManifestHashes,\n} from \"../../../data/containers/shared/projection\";\nimport type {\n ContainerMutationAuthor,\n ContainerRevokeApi,\n ContainerRevokePlan,\n MaterializedContainerRevokePlan,\n} from \"../../../data/containers/shared/types\";\nimport { readCanonicalRecord } from \"../../../data/keying/canonicalJson\";\nimport {\n type PrincipalPolicyCache,\n type ProjectionUserKeyResolver,\n type ReferencedPrincipalPolicyWarmer,\n requireProjectionUserKeyResolver,\n} from \"../../../data/keyingProjectionVerification\";\nimport type { PrincipalPolicyCurrentEvidence } from \"../../../data/principals/principalPolicyEvidence\";\nimport type { SecurityIncidentReporter } from \"../../../data/shared/securityIncidents\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { buildContainerRotationArtifacts } from \"./move/moveRotation\";\nimport {\n containerMutationRequestCore,\n previousPathRequestFields,\n readCanonicalRecordOrNull,\n} from \"./mutationRequestCore\";\nimport {\n submitAcknowledgedContainerMutation,\n submitContainerRotation,\n} from \"./mutationSubmit\";\nimport { containerWriterProjectionFromRotationPlan } from \"./rekey/rekeyProjection\";\nimport {\n type ContainerRevokeSubject,\n deriveContainerRevokeManifest,\n} from \"./revokeManifest\";\nimport { resolveRotationContext } from \"./rotation/rotationContext\";\nimport { collectContainerRevokePrincipalPolicies } from \"./rotation/rotationPrincipalPolicies\";\nimport { buildContainerRotationWraps } from \"./rotation/rotationWraps\";\n\nfunction buildContainerRevokeRequest(input: {\n body: ContainerRevokeAccessEventBody;\n event: AccessEvent;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n parentKek: ContainerKekResponse | null;\n predecessorBridge: ContainerKekPredecessorBridge;\n previousManifest: AccessManifestBundleWire;\n previousProjection: ContainerWriterProjectionResponse;\n principalPolicies: readonly PrincipalPolicyCurrentEvidence[];\n userRecipientKeys: readonly ContainerUserRecipientKey[];\n wraps: readonly ContainerKeyWrap[];\n}): ContainerMutationRequest {\n return {\n ...containerMutationRequestCore(\"revoke\", input),\n ...previousPathRequestFields(\n input.previousManifest,\n input.previousProjection,\n ),\n predecessorBridge: readCanonicalRecord(\n input.predecessorBridge,\n \"Container revoke predecessor bridge\",\n ),\n keyring: readCanonicalRecord(input.keyring, \"Container revoke keyring\"),\n parentKekState: readCanonicalRecordOrNull(\n input.parentKek,\n \"Container revoke parent KEK state\",\n ),\n };\n}\n\nfunction buildContainerRevokePlanResult(input: {\n body: ContainerRevokeAccessEventBody;\n containerId: string;\n containerKey: Uint8Array;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n parentKek: ContainerKekResponse | null;\n predecessorBridge: ContainerKekPredecessorBridge;\n previousManifest: AccessManifestBundleWire;\n previousProjection: ContainerWriterProjectionResponse;\n principalPolicies: readonly PrincipalPolicyCurrentEvidence[];\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}): MaterializedContainerRevokePlan {\n const plan: ContainerRevokePlan = {\n body: input.body,\n containerId: input.containerId,\n containerKeyEpochId: input.containerKeyEpochId,\n event: input.event,\n eventHash: input.eventHash,\n keyEpoch: input.keyEpoch,\n keyring: input.keyring,\n manifest: input.manifest,\n manifestHash: input.manifestHash,\n previousManifest: input.previousManifest,\n request: buildContainerRevokeRequest({\n body: input.body,\n event: input.event,\n keyEpoch: input.keyEpoch,\n keyring: input.keyring,\n manifest: input.manifest,\n manifestHash: input.manifestHash,\n parentKek: input.parentKek,\n predecessorBridge: input.predecessorBridge,\n previousManifest: input.previousManifest,\n previousProjection: input.previousProjection,\n principalPolicies: input.principalPolicies,\n userRecipientKeys: input.userRecipientKeys,\n wraps: input.wraps,\n }),\n state: input.state,\n userRecipientKeys: input.userRecipientKeys,\n wraps: input.wraps,\n };\n\n return { containerKey: input.containerKey, plan };\n}\n\n// biome-ignore lint/complexity/noExcessiveLinesPerFunction: Revoke planning has to keep the manifest, KEK, wraps, and signed event in one auditable transition.\nexport async function buildMaterializedContainerRevokePlan(input: {\n author: ContainerMutationAuthor;\n eventId?: string | undefined;\n execSql: ExecSql;\n /** Keys a batch minted above this container and has yet to commit. */\n knownContainerKeks?: ReadonlyMap<string, Uint8Array> | undefined;\n /** False for a projection re-rooted on rotations not yet acknowledged. */\n persistVerificationCheckpoints?: boolean | undefined;\n previousProjection: ContainerWriterProjectionResponse;\n /** Verified policies the path may cite before they are stored locally. */\n principalPolicyCache?: PrincipalPolicyCache | undefined;\n replacementPrincipalPolicy?: PrincipalPolicyCurrentEvidence | undefined;\n revokedSubject: ContainerRevokeSubject;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n signedAt?: string | undefined;\n stillCurrent?: (() => boolean) | undefined;\n targetSecretKey: Uint8Array;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n}): Promise<MaterializedContainerRevokePlan> {\n const resolveProjectionUserKey = requireProjectionUserKeyResolver(\n input.resolveProjectionUserKey,\n \"Remote container revoke\",\n );\n const {\n parentKek,\n parentPublicKey,\n predecessorContainerKey,\n previousState,\n target,\n } = await resolveRotationContext(input, \"revoke\");\n const principalPolicies = await collectContainerRevokePrincipalPolicies({\n execSql: input.execSql,\n persistVerificationCheckpoints: input.persistVerificationCheckpoints,\n previousProjection: input.previousProjection,\n principalPolicyCache: input.principalPolicyCache,\n resolveUserKey: resolveProjectionUserKey,\n stillCurrent: input.stillCurrent,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n assertContainerAuthorAccess({\n author: input.author,\n projection: input.previousProjection,\n principalPolicies,\n minimumAccess: \"admin\",\n });\n const nextContainerKeyEpoch = target.kek.containerKeyEpoch + 1;\n const { containerKey, containerKeyEpochId, keyring, predecessorBridge } =\n await buildContainerRotationArtifacts({\n containerId: previousState.containerId,\n currentKek: target.kek,\n currentKeyMaterial: predecessorContainerKey,\n keyEpoch: nextContainerKeyEpoch,\n signedEpochIds: target.signedEpochIds,\n });\n const body: ContainerRevokeAccessEventBody = {\n eventType: \"container.revoke\",\n parentManifestHash:\n input.previousProjection.path.at(-2)?.manifestHash ?? null,\n containerKeyPublicKey: await deriveContainerKekWrappingPublicKey({\n containerId: previousState.containerId,\n keyMaterial: containerKey,\n }),\n containerKeyEpochId,\n keyringHash: await computeContainerKekKeyringHash(keyring),\n predecessorBridgeHash:\n await computeContainerKekPredecessorBridgeHash(predecessorBridge),\n subjectId: input.revokedSubject.subjectId,\n subjectType: input.revokedSubject.subjectType,\n };\n const { event, eventHash } = await signContainerMutationEvent({\n author: input.author,\n body,\n containerId: previousState.containerId,\n dependencyManifestHashes: uniqueSortedManifestHashes(\n input.previousProjection.path,\n ),\n eventId: input.eventId ?? crypto.randomUUID(),\n previousManifestHash: target.manifest.manifestHash,\n signedAt: input.signedAt ?? new Date().toISOString(),\n });\n const { manifest, manifestHash, state } = await deriveContainerRevokeManifest(\n {\n containerKeyPublicKey: body.containerKeyPublicKey,\n parentManifestHash: body.parentManifestHash,\n containerKeyEpochId,\n eventHash,\n previousManifest: target.manifest,\n revokedSubject: input.revokedSubject,\n },\n );\n const keyEpoch = buildContainerCreateKeyEpoch({\n containerId: previousState.containerId,\n containerKeyEpochId,\n eventHash,\n keyEpoch: nextContainerKeyEpoch,\n manifestHash,\n parentContainerKeyEpochId: parentKek?.containerKeyEpochId ?? null,\n });\n\n const replacementPrincipalPolicy = input.replacementPrincipalPolicy;\n if (\n replacementPrincipalPolicy &&\n (replacementPrincipalPolicy.principalType !==\n input.revokedSubject.subjectType ||\n replacementPrincipalPolicy.principalId !== input.revokedSubject.subjectId)\n ) {\n throw new Error(\"Container revoke replacement principal does not match\");\n }\n const currentPrincipalPolicies = replacementPrincipalPolicy\n ? principalPolicies.filter(\n (policy) =>\n policy.principalType !== replacementPrincipalPolicy.principalType ||\n policy.principalId !== replacementPrincipalPolicy.principalId,\n )\n : principalPolicies;\n const { userRecipientKeys, wraps } = await buildContainerRotationWraps({\n containerKey,\n containerKeyEpochId,\n manifestHash,\n operationLabel: \"Container revoke\",\n parentKek,\n parentPublicKey,\n principalPolicies: currentPrincipalPolicies,\n resolveUserKey: resolveProjectionUserKey,\n state,\n });\n\n return buildContainerRevokePlanResult({\n body,\n containerId: previousState.containerId,\n containerKey,\n containerKeyEpochId,\n event,\n eventHash,\n keyEpoch,\n keyring,\n manifest,\n manifestHash,\n parentKek,\n predecessorBridge,\n previousManifest: asContainerManifestBundle(target.manifest),\n previousProjection: input.previousProjection,\n principalPolicies: currentPrincipalPolicies,\n state,\n userRecipientKeys,\n wraps,\n });\n}\n\nexport async function revokeRemoteContainer(input: {\n reportSecurityIncident: SecurityIncidentReporter;\n apiClient: ContainerRevokeApi;\n author: ContainerMutationAuthor;\n containerId: string;\n eventId?: string | undefined;\n execSql: ExecSql;\n revokedSubject: ContainerRevokeSubject;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n signedAt?: string | undefined;\n stillCurrent?: (() => boolean) | undefined;\n targetSecretKey: Uint8Array;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n}): Promise<{\n containerKey: Uint8Array;\n plan: ContainerRevokePlan;\n response: ContainerRotationResponse;\n} | null> {\n const previousProjection = await input.apiClient.getContainerWriterProjection(\n input.containerId,\n );\n if (!previousProjection) {\n return null;\n }\n\n const materializedPlan = await buildMaterializedContainerRevokePlan({\n author: input.author,\n eventId: input.eventId,\n execSql: input.execSql,\n previousProjection,\n revokedSubject: input.revokedSubject,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n signedAt: input.signedAt,\n stillCurrent: input.stillCurrent,\n targetSecretKey: input.targetSecretKey,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n return submitAcknowledgedContainerMutation({\n reportSecurityIncident: input.reportSecurityIncident,\n recitationPolicies: [],\n apiClient: input.apiClient,\n author: input.author,\n carriedRekeys: {\n planning: {\n apiClient: input.apiClient,\n author: input.author,\n execSql: input.execSql,\n knownContainerKeks: new Map([\n [\n materializedPlan.plan.containerKeyEpochId,\n materializedPlan.containerKey,\n ],\n ]),\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n stillCurrent: input.stillCurrent,\n targetSecretKey: input.targetSecretKey,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n },\n rotated: () =>\n containerWriterProjectionFromRotationPlan({\n plan: materializedPlan.plan,\n previousProjection,\n }),\n },\n containerKey: materializedPlan.containerKey,\n execSql: input.execSql,\n plan: materializedPlan.plan,\n stillCurrent: input.stillCurrent,\n submit: (carried) => {\n const request = carried.length\n ? { ...materializedPlan.plan.request, containerRekeys: [...carried] }\n : materializedPlan.plan.request;\n const options = {\n expectedPaymentRequiredOrganizationId: input.author.organizationId,\n };\n const { revokeContainerResult } = input.apiClient;\n return submitContainerRotation({\n plain: () =>\n input.apiClient.revokeContainer(input.containerId, request, options),\n result: revokeContainerResult\n ? () =>\n revokeContainerResult.call(\n input.apiClient,\n input.containerId,\n request,\n { ...options, reportErrors: false },\n )\n : undefined,\n });\n },\n });\n}\n"]}
1
+ {"version":3,"file":"revoke.js","sourceRoot":"","sources":["revoke.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccessEvent,\n AccessManifest,\n ContainerAccessManifestState,\n ContainerKekKeyring,\n ContainerKekPredecessorBridge,\n ContainerKeyEpoch,\n ContainerKeyWrap,\n ContainerRevokeAccessEventBody,\n ContainerUserRecipientKey,\n} from \"@tearleads/crypto\";\nimport {\n computeContainerKekKeyringHash,\n computeContainerKekPredecessorBridgeHash,\n deriveContainerKekWrappingPublicKey,\n KeyingVerificationError,\n} from \"@tearleads/crypto\";\nimport type {\n AccessManifestBundleWire,\n ContainerMutationRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n ContainerKekResponse,\n ContainerRotationResponse,\n ContainerWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { assertContainerAuthorAccess } from \"../../../data/containers/shared/authorAccess\";\nimport {\n buildContainerCreateKeyEpoch,\n signContainerMutationEvent,\n} from \"../../../data/containers/shared/events\";\nimport { isStaleParentContainerPathFailure } from \"../../../data/containers/shared/mutationFailures\";\nimport {\n asContainerManifestBundle,\n uniqueSortedManifestHashes,\n} from \"../../../data/containers/shared/projection\";\nimport type {\n ContainerMutationAuthor,\n ContainerRevokeApi,\n ContainerRevokePlan,\n ContainerRotationResult,\n MaterializedContainerRevokePlan,\n} from \"../../../data/containers/shared/types\";\nimport { readCanonicalRecord } from \"../../../data/keying/canonicalJson\";\nimport {\n type PrincipalPolicyCache,\n type ProjectionUserKeyResolver,\n type ReferencedPrincipalPolicyWarmer,\n requireProjectionUserKeyResolver,\n} from \"../../../data/keyingProjectionVerification\";\nimport type { PrincipalPolicyCurrentEvidence } from \"../../../data/principals/principalPolicyEvidence\";\nimport type { SecurityIncidentReporter } from \"../../../data/shared/securityIncidents\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport { buildContainerRotationArtifacts } from \"./move/moveRotation\";\nimport {\n containerMutationRequestCore,\n previousPathRequestFields,\n readCanonicalRecordOrNull,\n} from \"./mutationRequestCore\";\nimport {\n submitAcknowledgedContainerMutation,\n submitContainerRotation,\n} from \"./mutationSubmit\";\nimport { containerWriterProjectionFromRotationPlan } from \"./rekey/rekeyProjection\";\nimport {\n type ContainerRevokeSubject,\n deriveContainerRevokeManifest,\n} from \"./revokeManifest\";\nimport { resolveRotationContext } from \"./rotation/rotationContext\";\nimport { collectContainerRevokePrincipalPolicies } from \"./rotation/rotationPrincipalPolicies\";\nimport { buildContainerRotationWraps } from \"./rotation/rotationWraps\";\n\nfunction buildContainerRevokeRequest(input: {\n body: ContainerRevokeAccessEventBody;\n event: AccessEvent;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n parentKek: ContainerKekResponse | null;\n predecessorBridge: ContainerKekPredecessorBridge;\n previousManifest: AccessManifestBundleWire;\n previousProjection: ContainerWriterProjectionResponse;\n principalPolicies: readonly PrincipalPolicyCurrentEvidence[];\n userRecipientKeys: readonly ContainerUserRecipientKey[];\n wraps: readonly ContainerKeyWrap[];\n}): ContainerMutationRequest {\n return {\n ...containerMutationRequestCore(\"revoke\", input),\n ...previousPathRequestFields(\n input.previousManifest,\n input.previousProjection,\n ),\n predecessorBridge: readCanonicalRecord(\n input.predecessorBridge,\n \"Container revoke predecessor bridge\",\n ),\n keyring: readCanonicalRecord(input.keyring, \"Container revoke keyring\"),\n parentKekState: readCanonicalRecordOrNull(\n input.parentKek,\n \"Container revoke parent KEK state\",\n ),\n };\n}\n\nfunction buildContainerRevokePlanResult(input: {\n body: ContainerRevokeAccessEventBody;\n containerId: string;\n containerKey: Uint8Array;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n parentKek: ContainerKekResponse | null;\n predecessorBridge: ContainerKekPredecessorBridge;\n previousManifest: AccessManifestBundleWire;\n previousProjection: ContainerWriterProjectionResponse;\n principalPolicies: readonly PrincipalPolicyCurrentEvidence[];\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}): MaterializedContainerRevokePlan {\n const plan: ContainerRevokePlan = {\n body: input.body,\n containerId: input.containerId,\n containerKeyEpochId: input.containerKeyEpochId,\n event: input.event,\n eventHash: input.eventHash,\n keyEpoch: input.keyEpoch,\n keyring: input.keyring,\n manifest: input.manifest,\n manifestHash: input.manifestHash,\n previousManifest: input.previousManifest,\n request: buildContainerRevokeRequest({\n body: input.body,\n event: input.event,\n keyEpoch: input.keyEpoch,\n keyring: input.keyring,\n manifest: input.manifest,\n manifestHash: input.manifestHash,\n parentKek: input.parentKek,\n predecessorBridge: input.predecessorBridge,\n previousManifest: input.previousManifest,\n previousProjection: input.previousProjection,\n principalPolicies: input.principalPolicies,\n userRecipientKeys: input.userRecipientKeys,\n wraps: input.wraps,\n }),\n state: input.state,\n userRecipientKeys: input.userRecipientKeys,\n wraps: input.wraps,\n };\n\n return { containerKey: input.containerKey, plan };\n}\n\n// biome-ignore lint/complexity/noExcessiveLinesPerFunction: Revoke planning has to keep the manifest, KEK, wraps, and signed event in one auditable transition.\nexport async function buildMaterializedContainerRevokePlan(input: {\n author: ContainerMutationAuthor;\n eventId?: string | undefined;\n execSql: ExecSql;\n /** Keys a batch minted above this container and has yet to commit. */\n knownContainerKeks?: ReadonlyMap<string, Uint8Array> | undefined;\n /** False for a projection re-rooted on rotations not yet acknowledged. */\n persistVerificationCheckpoints?: boolean | undefined;\n previousProjection: ContainerWriterProjectionResponse;\n /** Verified policies the path may cite before they are stored locally. */\n principalPolicyCache?: PrincipalPolicyCache | undefined;\n replacementPrincipalPolicy?: PrincipalPolicyCurrentEvidence | undefined;\n revokedSubject: ContainerRevokeSubject;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n signedAt?: string | undefined;\n stillCurrent?: (() => boolean) | undefined;\n targetSecretKey: Uint8Array;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n}): Promise<MaterializedContainerRevokePlan> {\n const resolveProjectionUserKey = requireProjectionUserKeyResolver(\n input.resolveProjectionUserKey,\n \"Remote container revoke\",\n );\n const {\n parentKek,\n parentPublicKey,\n predecessorContainerKey,\n previousState,\n target,\n } = await resolveRotationContext(input, \"revoke\");\n const principalPolicies = await collectContainerRevokePrincipalPolicies({\n execSql: input.execSql,\n persistVerificationCheckpoints: input.persistVerificationCheckpoints,\n previousProjection: input.previousProjection,\n principalPolicyCache: input.principalPolicyCache,\n resolveUserKey: resolveProjectionUserKey,\n stillCurrent: input.stillCurrent,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n assertContainerAuthorAccess({\n author: input.author,\n projection: input.previousProjection,\n principalPolicies,\n minimumAccess: \"admin\",\n });\n const nextContainerKeyEpoch = target.kek.containerKeyEpoch + 1;\n const { containerKey, containerKeyEpochId, keyring, predecessorBridge } =\n await buildContainerRotationArtifacts({\n containerId: previousState.containerId,\n currentKek: target.kek,\n currentKeyMaterial: predecessorContainerKey,\n keyEpoch: nextContainerKeyEpoch,\n signedEpochIds: target.signedEpochIds,\n });\n const body: ContainerRevokeAccessEventBody = {\n eventType: \"container.revoke\",\n parentManifestHash:\n input.previousProjection.path.at(-2)?.manifestHash ?? null,\n containerKeyPublicKey: await deriveContainerKekWrappingPublicKey({\n containerId: previousState.containerId,\n keyMaterial: containerKey,\n }),\n containerKeyEpochId,\n keyringHash: await computeContainerKekKeyringHash(keyring),\n predecessorBridgeHash:\n await computeContainerKekPredecessorBridgeHash(predecessorBridge),\n subjectId: input.revokedSubject.subjectId,\n subjectType: input.revokedSubject.subjectType,\n };\n const { event, eventHash } = await signContainerMutationEvent({\n author: input.author,\n body,\n containerId: previousState.containerId,\n dependencyManifestHashes: uniqueSortedManifestHashes(\n input.previousProjection.path,\n ),\n eventId: input.eventId ?? crypto.randomUUID(),\n previousManifestHash: target.manifest.manifestHash,\n signedAt: input.signedAt ?? new Date().toISOString(),\n });\n const { manifest, manifestHash, state } = await deriveContainerRevokeManifest(\n {\n containerKeyPublicKey: body.containerKeyPublicKey,\n parentManifestHash: body.parentManifestHash,\n containerKeyEpochId,\n eventHash,\n previousManifest: target.manifest,\n revokedSubject: input.revokedSubject,\n },\n );\n const keyEpoch = buildContainerCreateKeyEpoch({\n containerId: previousState.containerId,\n containerKeyEpochId,\n eventHash,\n keyEpoch: nextContainerKeyEpoch,\n manifestHash,\n parentContainerKeyEpochId: parentKek?.containerKeyEpochId ?? null,\n });\n\n const replacementPrincipalPolicy = input.replacementPrincipalPolicy;\n if (\n replacementPrincipalPolicy &&\n (replacementPrincipalPolicy.principalType !==\n input.revokedSubject.subjectType ||\n replacementPrincipalPolicy.principalId !== input.revokedSubject.subjectId)\n ) {\n throw new Error(\"Container revoke replacement principal does not match\");\n }\n const currentPrincipalPolicies = replacementPrincipalPolicy\n ? principalPolicies.filter(\n (policy) =>\n policy.principalType !== replacementPrincipalPolicy.principalType ||\n policy.principalId !== replacementPrincipalPolicy.principalId,\n )\n : principalPolicies;\n const { userRecipientKeys, wraps } = await buildContainerRotationWraps({\n containerKey,\n containerKeyEpochId,\n manifestHash,\n operationLabel: \"Container revoke\",\n parentKek,\n parentPublicKey,\n principalPolicies: currentPrincipalPolicies,\n resolveUserKey: resolveProjectionUserKey,\n state,\n });\n\n return buildContainerRevokePlanResult({\n body,\n containerId: previousState.containerId,\n containerKey,\n containerKeyEpochId,\n event,\n eventHash,\n keyEpoch,\n keyring,\n manifest,\n manifestHash,\n parentKek,\n predecessorBridge,\n previousManifest: asContainerManifestBundle(target.manifest),\n previousProjection: input.previousProjection,\n principalPolicies: currentPrincipalPolicies,\n state,\n userRecipientKeys,\n wraps,\n });\n}\n\ninterface RevokeRemoteContainerInput {\n reportSecurityIncident: SecurityIncidentReporter;\n apiClient: ContainerRevokeApi;\n author: ContainerMutationAuthor;\n containerId: string;\n eventId?: string | undefined;\n execSql: ExecSql;\n revokedSubject: ContainerRevokeSubject;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n signedAt?: string | undefined;\n stillCurrent?: (() => boolean) | undefined;\n targetSecretKey: Uint8Array;\n warmReferencedPrincipalPolicies?: ReferencedPrincipalPolicyWarmer | undefined;\n}\n\ntype RevokedRemoteContainer = {\n containerKey: Uint8Array;\n plan: ContainerRevokePlan;\n response: ContainerRotationResponse;\n};\n\nconst STALE_PATH = Symbol(\"stale-revoke-path\");\n\nasync function submitRevocation(\n input: RevokeRemoteContainerInput,\n plan: ContainerRevokePlan,\n carried: readonly ContainerMutationRequest[],\n): Promise<ContainerRotationResult> {\n const request = carried.length\n ? { ...plan.request, containerRekeys: [...carried] }\n : plan.request;\n const options = {\n expectedPaymentRequiredOrganizationId: input.author.organizationId,\n };\n const { revokeContainerResult } = input.apiClient;\n return submitContainerRotation({\n plain: () =>\n input.apiClient.revokeContainer(input.containerId, request, options),\n result: revokeContainerResult\n ? () =>\n revokeContainerResult.call(\n input.apiClient,\n input.containerId,\n request,\n { ...options, reportErrors: false },\n )\n : undefined,\n });\n}\n\nasync function planRevoke(input: RevokeRemoteContainerInput) {\n const previousProjection = await input.apiClient.getContainerWriterProjection(\n input.containerId,\n );\n if (!previousProjection) {\n return null;\n }\n\n const materializedPlan = await buildMaterializedContainerRevokePlan({\n author: input.author,\n eventId: input.eventId,\n execSql: input.execSql,\n previousProjection,\n revokedSubject: input.revokedSubject,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n signedAt: input.signedAt,\n stillCurrent: input.stillCurrent,\n targetSecretKey: input.targetSecretKey,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n return { previousProjection, materializedPlan };\n}\n\nasync function revokeRemoteContainerOnce(\n input: RevokeRemoteContainerInput,\n retriesStalePath: boolean,\n onAccepted: () => void,\n): Promise<RevokedRemoteContainer | typeof STALE_PATH | null> {\n const planned = await planRevoke(input);\n if (!planned) return null;\n const { previousProjection, materializedPlan } = planned;\n let stalePath = false;\n const revoked = await submitAcknowledgedContainerMutation({\n onAccepted,\n reportSecurityIncident: input.reportSecurityIncident,\n recitationPolicies: [],\n apiClient: input.apiClient,\n author: input.author,\n carriedRekeys: {\n planning: {\n apiClient: input.apiClient,\n author: input.author,\n execSql: input.execSql,\n knownContainerKeks: new Map([\n [\n materializedPlan.plan.containerKeyEpochId,\n materializedPlan.containerKey,\n ],\n ]),\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n stillCurrent: input.stillCurrent,\n targetSecretKey: input.targetSecretKey,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n },\n rotated: () =>\n containerWriterProjectionFromRotationPlan({\n plan: materializedPlan.plan,\n previousProjection,\n }),\n },\n containerKey: materializedPlan.containerKey,\n execSql: input.execSql,\n plan: materializedPlan.plan,\n stillCurrent: input.stillCurrent,\n submit: async (carried) => {\n const result = await submitRevocation(\n input,\n materializedPlan.plan,\n carried,\n );\n if (\n !result.ok &&\n retriesStalePath &&\n isStaleParentContainerPathFailure(result)\n ) {\n stalePath = true;\n // The refused transaction rolled back. Replan it without reporting an\n // error that the caller can repair; a second refusal is terminal.\n return { ok: false, status: result.status, code: result.code };\n }\n return result;\n },\n });\n return revoked ?? (stalePath ? STALE_PATH : null);\n}\n\nasync function revokeRemoteContainerAttempt(\n input: RevokeRemoteContainerInput,\n retriesStalePath: boolean,\n): Promise<RevokedRemoteContainer | typeof STALE_PATH | null> {\n let accepted = false;\n try {\n return await revokeRemoteContainerOnce(input, retriesStalePath, () => {\n accepted = true;\n });\n } catch (error) {\n // Our own or a carried descendant's cached projection may trail a pin.\n // Refresh only a refused/unsubmitted attempt, never an accepted rotation.\n if (\n retriesStalePath &&\n !accepted &&\n error instanceof KeyingVerificationError &&\n error.code === \"rollback\"\n )\n return STALE_PATH;\n throw error;\n }\n}\n\n/** A concurrent mutation can advance the path while a revoke is in flight. */\nexport async function revokeRemoteContainer(\n input: RevokeRemoteContainerInput,\n): Promise<RevokedRemoteContainer | null> {\n if (input.stillCurrent?.() === false) return null;\n const first = await revokeRemoteContainerAttempt(input, true);\n if (first !== STALE_PATH) return first;\n if (input.stillCurrent?.() === false) return null;\n input.apiClient.evictContainerWriterProjection?.(input.containerId);\n input.apiClient.evictWriterProjectionsCiting?.([input.containerId]);\n const second = await revokeRemoteContainerAttempt(input, false);\n return second === STALE_PATH ? null : second;\n}\n"]}