@tearleads/client-sdk 0.1.105 → 0.1.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/Tearleads.js +5 -3
  2. package/Tearleads.js.map +1 -1
  3. package/client/blobs/index.d.ts +4 -0
  4. package/client/blobs/index.js +26 -7
  5. package/client/blobs/index.js.map +1 -1
  6. package/client/identity/index.d.ts +11 -2
  7. package/client/identity/index.js +80 -72
  8. package/client/identity/index.js.map +1 -1
  9. package/client/organizations/index.js +5 -2
  10. package/client/organizations/index.js.map +1 -1
  11. package/client/organizations/organizationMemberships.js +26 -18
  12. package/client/organizations/organizationMemberships.js.map +1 -1
  13. package/client/organizations/organizationReadModels.d.ts +9 -4
  14. package/client/organizations/organizationReadModels.js +20 -9
  15. package/client/organizations/organizationReadModels.js.map +1 -1
  16. package/client/session/index.js +6 -3
  17. package/client/session/index.js.map +1 -1
  18. package/client/session/sessionTypes.d.ts +1 -1
  19. package/client/session/sessionTypes.js.map +1 -1
  20. package/index.js +1 -1
  21. package/index.js.map +1 -1
  22. package/internal/api-client/ApiClient.d.ts +7 -0
  23. package/internal/crypto/organizationLeaveRequest.d.ts +1 -1
  24. package/internal/crypto/organizationLeaveRequest.js +9 -2
  25. package/internal/crypto/organizationLeaveRequest.js.map +1 -1
  26. package/internal/validators/operation/organizationMemberships.d.ts +7 -0
  27. package/internal/validators/operation/organizations.d.ts +56 -0
  28. package/internal/validators/response/organization.d.ts +91 -0
  29. package/internal/validators/response/organizationReadModel.d.ts +70 -0
  30. package/internal/validators/util/index.d.ts +1 -1
  31. package/internal/validators/util/index.js.map +1 -1
  32. package/internal/validators/util/organizationLeaveRequest.d.ts +25 -2
  33. package/internal/validators/util/organizationLeaveRequest.js +19 -3
  34. package/internal/validators/util/organizationLeaveRequest.js.map +1 -1
  35. package/package.json +1 -1
  36. package/workflows/organizations/memberships/leaveRequest.d.ts +6 -2
  37. package/workflows/organizations/memberships/leaveRequest.js +20 -5
  38. package/workflows/organizations/memberships/leaveRequest.js.map +1 -1
  39. package/workflows/organizations/memberships/leaveRequestMembersEvidence.js +80 -0
  40. package/workflows/organizations/memberships/leaveRequestMembersEvidence.js.map +1 -0
  41. package/workflows/organizations/memberships/leaveRequestMembership.d.ts +24 -0
  42. package/workflows/organizations/memberships/leaveRequestMembership.js +65 -0
  43. package/workflows/organizations/memberships/leaveRequestMembership.js.map +1 -0
  44. package/workflows/organizations/memberships/leaveRequestVerification.d.ts +15 -7
  45. package/workflows/organizations/memberships/leaveRequestVerification.js +18 -11
  46. package/workflows/organizations/memberships/leaveRequestVerification.js.map +1 -1
@@ -15,6 +15,13 @@ declare const OrganizationReadModelDirectoryResponseSchema: z.ZodObject<{
15
15
  organizationId: string;
16
16
  userId: string;
17
17
  requestedAt: string;
18
+ membersHead: {
19
+ principalId: string;
20
+ version: number;
21
+ keyEpoch: number;
22
+ stateHash: string;
23
+ keyFingerprint: string;
24
+ };
18
25
  signature: string;
19
26
  } | null;
20
27
  profileDocumentId: string | null;
@@ -36,6 +43,13 @@ declare const OrganizationReadModelDirectoryResponseSchema: z.ZodObject<{
36
43
  organizationId: string;
37
44
  userId: string;
38
45
  requestedAt: string;
46
+ membersHead: {
47
+ principalId: string;
48
+ version: number;
49
+ keyEpoch: number;
50
+ stateHash: string;
51
+ keyFingerprint: string;
52
+ };
39
53
  signature: string;
40
54
  } | null;
41
55
  profileDocumentId: string | null;
@@ -156,6 +170,13 @@ declare const OrganizationReadModelSnapshotResponseSchema: z.ZodObject<{
156
170
  organizationId: string;
157
171
  userId: string;
158
172
  requestedAt: string;
173
+ membersHead: {
174
+ principalId: string;
175
+ version: number;
176
+ keyEpoch: number;
177
+ stateHash: string;
178
+ keyFingerprint: string;
179
+ };
159
180
  signature: string;
160
181
  } | null;
161
182
  profileDocumentId: string | null;
@@ -177,6 +198,13 @@ declare const OrganizationReadModelSnapshotResponseSchema: z.ZodObject<{
177
198
  organizationId: string;
178
199
  userId: string;
179
200
  requestedAt: string;
201
+ membersHead: {
202
+ principalId: string;
203
+ version: number;
204
+ keyEpoch: number;
205
+ stateHash: string;
206
+ keyFingerprint: string;
207
+ };
180
208
  signature: string;
181
209
  } | null;
182
210
  profileDocumentId: string | null;
@@ -309,6 +337,13 @@ declare const OrganizationReadModelDeltaResponseSchema: z.ZodObject<{
309
337
  organizationId: string;
310
338
  userId: string;
311
339
  requestedAt: string;
340
+ membersHead: {
341
+ principalId: string;
342
+ version: number;
343
+ keyEpoch: number;
344
+ stateHash: string;
345
+ keyFingerprint: string;
346
+ };
312
347
  signature: string;
313
348
  } | null;
314
349
  profileDocumentId: string | null;
@@ -330,6 +365,13 @@ declare const OrganizationReadModelDeltaResponseSchema: z.ZodObject<{
330
365
  organizationId: string;
331
366
  userId: string;
332
367
  requestedAt: string;
368
+ membersHead: {
369
+ principalId: string;
370
+ version: number;
371
+ keyEpoch: number;
372
+ stateHash: string;
373
+ keyFingerprint: string;
374
+ };
333
375
  signature: string;
334
376
  } | null;
335
377
  profileDocumentId: string | null;
@@ -463,6 +505,13 @@ export declare const OrganizationReadModelResponseSchema: z.ZodDiscriminatedUnio
463
505
  organizationId: string;
464
506
  userId: string;
465
507
  requestedAt: string;
508
+ membersHead: {
509
+ principalId: string;
510
+ version: number;
511
+ keyEpoch: number;
512
+ stateHash: string;
513
+ keyFingerprint: string;
514
+ };
466
515
  signature: string;
467
516
  } | null;
468
517
  profileDocumentId: string | null;
@@ -484,6 +533,13 @@ export declare const OrganizationReadModelResponseSchema: z.ZodDiscriminatedUnio
484
533
  organizationId: string;
485
534
  userId: string;
486
535
  requestedAt: string;
536
+ membersHead: {
537
+ principalId: string;
538
+ version: number;
539
+ keyEpoch: number;
540
+ stateHash: string;
541
+ keyFingerprint: string;
542
+ };
487
543
  signature: string;
488
544
  } | null;
489
545
  profileDocumentId: string | null;
@@ -616,6 +672,13 @@ export declare const OrganizationReadModelResponseSchema: z.ZodDiscriminatedUnio
616
672
  organizationId: string;
617
673
  userId: string;
618
674
  requestedAt: string;
675
+ membersHead: {
676
+ principalId: string;
677
+ version: number;
678
+ keyEpoch: number;
679
+ stateHash: string;
680
+ keyFingerprint: string;
681
+ };
619
682
  signature: string;
620
683
  } | null;
621
684
  profileDocumentId: string | null;
@@ -637,6 +700,13 @@ export declare const OrganizationReadModelResponseSchema: z.ZodDiscriminatedUnio
637
700
  organizationId: string;
638
701
  userId: string;
639
702
  requestedAt: string;
703
+ membersHead: {
704
+ principalId: string;
705
+ version: number;
706
+ keyEpoch: number;
707
+ stateHash: string;
708
+ keyFingerprint: string;
709
+ };
640
710
  signature: string;
641
711
  } | null;
642
712
  profileDocumentId: string | null;
@@ -6,7 +6,7 @@ export { MAX_CONTAINER_PATH_LENGTH } from "./containerLimits.js";
6
6
  export { MAX_DOCUMENT_SYNC_AUTHORIZATION_PATH_REFS, MAX_DOCUMENT_SYNC_AUTHORIZATION_PATHS, MAX_DOCUMENT_SYNC_CONTENT_KEY_TARGETS, MAX_DOCUMENT_SYNC_OUTGOING_UPDATES, MAX_DOCUMENT_SYNC_PULL_CURSOR_LENGTH, MAX_DOCUMENT_SYNC_REQUEST_BYTES, MAX_DOCUMENT_SYNC_RESPONSE_ENVELOPE_BYTES, MAX_DOCUMENT_SYNC_RESPONSE_PAGE_BYTES, MAX_DOCUMENT_SYNC_RESPONSE_PAGE_UPDATES, MAX_DOCUMENT_SYNC_RESPONSE_UPDATE_PAGE_BYTES, } from "./documentSyncLimits.js";
7
7
  export { isSerializedKeyEnvelope, isSerializedKeyEnvelopeArray, type SerializedKeyEnvelope, } from "./keyEnvelope.js";
8
8
  export { KNOWN_MANIFEST_HINT_LIMIT, KnownManifestsHeadersSchema, parseKnownManifestHints, } from "./knownManifestWire.js";
9
- export { isOrganizationLeaveRequest, type OrganizationLeaveRequest, OrganizationLeaveRequestSchema, } from "./organizationLeaveRequest.js";
9
+ export { isOrganizationLeaveRequest, type OrganizationLeaveRequest, type OrganizationLeaveRequestMembersHead, OrganizationLeaveRequestSchema, } from "./organizationLeaveRequest.js";
10
10
  export { isOrganizationReplacementAuthorization, type OrganizationReplacementAuthorization, OrganizationReplacementAuthorizationSchema, } from "./organizationReplacement.js";
11
11
  export { PRINCIPAL_POLICY_HISTORY_PAGE_LIMIT, PRINCIPAL_POLICY_REPAIR_HEAD_LIMIT, } from "./principalHistoryWire.js";
12
12
  export { hasArrayProperty, hasBooleanProperty, hasNonEmptyStringProperty, hasNullableNumberProperty, hasNullableStringProperty, hasNumberProperty, hasObjectProperty, hasOptionalStringProperty, hasPositiveIntegerProperty, hasPropertyValue, hasStringProperty, isNonEmptyStringArray, isOptionalRecordArray, isOptionalRecordArrayArray, isRecordArray, isRecordArrayArray, isStringArray, } from "./properties.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mCAAmC,EAGnC,sCAAsC,EACtC,8BAA8B,EAC9B,+BAA+B,EAC/B,0BAA0B,EAC1B,+BAA+B,EAC/B,kCAAkC,EAClC,uCAAuC,GACxC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,QAAQ,CAAC;AAChB,OAAO,EACL,wBAAwB,EACxB,8BAA8B,EAE9B,iCAAiC,EAEjC,6BAA6B,EAE7B,mCAAmC,EACnC,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,4BAA4B,EAC5B,uCAAuC,EACvC,mCAAmC,EACnC,mCAAmC,EACnC,+BAA+B,EAC/B,uBAAuB,EACvB,2BAA2B,EAC3B,6BAA6B,EAC7B,8CAA8C,EAC9C,gDAAgD,EAChD,wCAAwC,EACxC,yCAAyC,EACzC,8BAA8B,GAC/B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EACL,yCAAyC,EACzC,qCAAqC,EACrC,qCAAqC,EACrC,kCAAkC,EAClC,oCAAoC,EACpC,+BAA+B,EAC/B,yCAAyC,EACzC,qCAAqC,EACrC,uCAAuC,EACvC,4CAA4C,GAC7C,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,uBAAuB,EACvB,4BAA4B,GAE7B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAE1B,8BAA8B,GAC/B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,sCAAsC,EAEtC,0CAA0C,GAC3C,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,mCAAmC,EACnC,kCAAkC,GACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,0BAA0B,EAC1B,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,0BAA0B,EAC1B,aAAa,EACb,kBAAkB,EAClB,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,EACxB,2BAA2B,EAC3B,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC","sourcesContent":["export {\n type AccessEventBundleWireResponse,\n AccessEventBundleWireResponseSchema,\n type AccessManifestBundleWire,\n type AccessManifestBundleWireResponse,\n AccessManifestBundleWireResponseSchema,\n AccessManifestBundleWireSchema,\n isAccessEventBundleWireResponse,\n isAccessManifestBundleWire,\n isAccessManifestBundleWireArray,\n isAccessManifestBundleWireResponse,\n isOptionalAccessManifestBundleWireArray,\n} from \"./accessManifestBundle\";\nexport {\n MAX_INITIATE_PART_UPLOADS,\n MAX_MULTIPART_BLOB_PART_BYTES,\n} from \"./blob\";\nexport {\n blobPartContentMd5Schema,\n MAX_MULTIPART_BLOB_PART_NUMBER,\n type MultipartBlobPartDescriptor,\n MultipartBlobPartDescriptorSchema,\n type MultipartBlobPartUpload,\n MultipartBlobPartUploadSchema,\n type MultipartBlobPartUploadSource,\n MultipartBlobPartUploadSourceSchema,\n signedBlobUrlExpirySchema,\n signedBlobUrlSchema,\n} from \"./blobTransferSchemas\";\nexport {\n CONTAINER_KEK_LOG_PAGE_LIMIT,\n CONTAINER_KEK_LOG_PRINCIPAL_SCOPE_LIMIT,\n CONTAINER_KEK_WRAPS_PER_EPOCH_LIMIT,\n ContainerKekKeyringWireRecordSchema,\n isContainerKekKeyringWireRecord,\n MAX_CONTAINER_KEY_EPOCH,\n MAX_INLINE_CONTAINER_REKEYS,\n MAX_ROTATION_CONTAINER_REKEYS,\n MAX_SEALED_CONTAINER_KEK_KEYRING_BASE64_LENGTH,\n SEALED_CONTAINER_KEK_KEYRING_AEAD_OVERHEAD_BYTES,\n SEALED_CONTAINER_KEK_KEYRING_ENTRY_BYTES,\n SEALED_CONTAINER_KEK_KEYRING_HEADER_BYTES,\n sealedContainerKekKeyringBytes,\n} from \"./containerKekKeyringWire\";\nexport { MAX_CONTAINER_PATH_LENGTH } from \"./containerLimits\";\nexport {\n MAX_DOCUMENT_SYNC_AUTHORIZATION_PATH_REFS,\n MAX_DOCUMENT_SYNC_AUTHORIZATION_PATHS,\n MAX_DOCUMENT_SYNC_CONTENT_KEY_TARGETS,\n MAX_DOCUMENT_SYNC_OUTGOING_UPDATES,\n MAX_DOCUMENT_SYNC_PULL_CURSOR_LENGTH,\n MAX_DOCUMENT_SYNC_REQUEST_BYTES,\n MAX_DOCUMENT_SYNC_RESPONSE_ENVELOPE_BYTES,\n MAX_DOCUMENT_SYNC_RESPONSE_PAGE_BYTES,\n MAX_DOCUMENT_SYNC_RESPONSE_PAGE_UPDATES,\n MAX_DOCUMENT_SYNC_RESPONSE_UPDATE_PAGE_BYTES,\n} from \"./documentSyncLimits\";\nexport {\n isSerializedKeyEnvelope,\n isSerializedKeyEnvelopeArray,\n type SerializedKeyEnvelope,\n} from \"./keyEnvelope\";\nexport {\n KNOWN_MANIFEST_HINT_LIMIT,\n KnownManifestsHeadersSchema,\n parseKnownManifestHints,\n} from \"./knownManifestWire\";\nexport {\n isOrganizationLeaveRequest,\n type OrganizationLeaveRequest,\n OrganizationLeaveRequestSchema,\n} from \"./organizationLeaveRequest\";\nexport {\n isOrganizationReplacementAuthorization,\n type OrganizationReplacementAuthorization,\n OrganizationReplacementAuthorizationSchema,\n} from \"./organizationReplacement\";\nexport {\n PRINCIPAL_POLICY_HISTORY_PAGE_LIMIT,\n PRINCIPAL_POLICY_REPAIR_HEAD_LIMIT,\n} from \"./principalHistoryWire\";\nexport {\n hasArrayProperty,\n hasBooleanProperty,\n hasNonEmptyStringProperty,\n hasNullableNumberProperty,\n hasNullableStringProperty,\n hasNumberProperty,\n hasObjectProperty,\n hasOptionalStringProperty,\n hasPositiveIntegerProperty,\n hasPropertyValue,\n hasStringProperty,\n isNonEmptyStringArray,\n isOptionalRecordArray,\n isOptionalRecordArrayArray,\n isRecordArray,\n isRecordArrayArray,\n isStringArray,\n} from \"./properties\";\nexport {\n AUTH_CHALLENGE_HEX_LENGTH,\n isAuthChallengeHexString,\n isByteArray,\n isByteArrayOfLength,\n isSha256HexString,\n ML_DSA87_PUBLIC_KEY_BYTES,\n ML_DSA87_SIGNATURE_BYTES,\n ML_KEM1024_PUBLIC_KEY_BYTES,\n SHA256_HEX_LENGTH,\n} from \"./protocol\";\nexport {\n CANONICAL_SIGNED_AT_PATTERN,\n isCanonicalSignedAt,\n MAX_SIGNED_AT_YEAR,\n MIN_SIGNED_AT_YEAR,\n SIGNED_AT_CONTRACT,\n} from \"./signedAt\";\nexport { isUuidV4String } from \"./uuid\";\nexport { isWalLsnString, parseWalLsn } from \"./walLsn\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mCAAmC,EAGnC,sCAAsC,EACtC,8BAA8B,EAC9B,+BAA+B,EAC/B,0BAA0B,EAC1B,+BAA+B,EAC/B,kCAAkC,EAClC,uCAAuC,GACxC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,QAAQ,CAAC;AAChB,OAAO,EACL,wBAAwB,EACxB,8BAA8B,EAE9B,iCAAiC,EAEjC,6BAA6B,EAE7B,mCAAmC,EACnC,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,4BAA4B,EAC5B,uCAAuC,EACvC,mCAAmC,EACnC,mCAAmC,EACnC,+BAA+B,EAC/B,uBAAuB,EACvB,2BAA2B,EAC3B,6BAA6B,EAC7B,8CAA8C,EAC9C,gDAAgD,EAChD,wCAAwC,EACxC,yCAAyC,EACzC,8BAA8B,GAC/B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EACL,yCAAyC,EACzC,qCAAqC,EACrC,qCAAqC,EACrC,kCAAkC,EAClC,oCAAoC,EACpC,+BAA+B,EAC/B,yCAAyC,EACzC,qCAAqC,EACrC,uCAAuC,EACvC,4CAA4C,GAC7C,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,uBAAuB,EACvB,4BAA4B,GAE7B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAG1B,8BAA8B,GAC/B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,sCAAsC,EAEtC,0CAA0C,GAC3C,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,mCAAmC,EACnC,kCAAkC,GACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,0BAA0B,EAC1B,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,0BAA0B,EAC1B,aAAa,EACb,kBAAkB,EAClB,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,EACxB,2BAA2B,EAC3B,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC","sourcesContent":["export {\n type AccessEventBundleWireResponse,\n AccessEventBundleWireResponseSchema,\n type AccessManifestBundleWire,\n type AccessManifestBundleWireResponse,\n AccessManifestBundleWireResponseSchema,\n AccessManifestBundleWireSchema,\n isAccessEventBundleWireResponse,\n isAccessManifestBundleWire,\n isAccessManifestBundleWireArray,\n isAccessManifestBundleWireResponse,\n isOptionalAccessManifestBundleWireArray,\n} from \"./accessManifestBundle\";\nexport {\n MAX_INITIATE_PART_UPLOADS,\n MAX_MULTIPART_BLOB_PART_BYTES,\n} from \"./blob\";\nexport {\n blobPartContentMd5Schema,\n MAX_MULTIPART_BLOB_PART_NUMBER,\n type MultipartBlobPartDescriptor,\n MultipartBlobPartDescriptorSchema,\n type MultipartBlobPartUpload,\n MultipartBlobPartUploadSchema,\n type MultipartBlobPartUploadSource,\n MultipartBlobPartUploadSourceSchema,\n signedBlobUrlExpirySchema,\n signedBlobUrlSchema,\n} from \"./blobTransferSchemas\";\nexport {\n CONTAINER_KEK_LOG_PAGE_LIMIT,\n CONTAINER_KEK_LOG_PRINCIPAL_SCOPE_LIMIT,\n CONTAINER_KEK_WRAPS_PER_EPOCH_LIMIT,\n ContainerKekKeyringWireRecordSchema,\n isContainerKekKeyringWireRecord,\n MAX_CONTAINER_KEY_EPOCH,\n MAX_INLINE_CONTAINER_REKEYS,\n MAX_ROTATION_CONTAINER_REKEYS,\n MAX_SEALED_CONTAINER_KEK_KEYRING_BASE64_LENGTH,\n SEALED_CONTAINER_KEK_KEYRING_AEAD_OVERHEAD_BYTES,\n SEALED_CONTAINER_KEK_KEYRING_ENTRY_BYTES,\n SEALED_CONTAINER_KEK_KEYRING_HEADER_BYTES,\n sealedContainerKekKeyringBytes,\n} from \"./containerKekKeyringWire\";\nexport { MAX_CONTAINER_PATH_LENGTH } from \"./containerLimits\";\nexport {\n MAX_DOCUMENT_SYNC_AUTHORIZATION_PATH_REFS,\n MAX_DOCUMENT_SYNC_AUTHORIZATION_PATHS,\n MAX_DOCUMENT_SYNC_CONTENT_KEY_TARGETS,\n MAX_DOCUMENT_SYNC_OUTGOING_UPDATES,\n MAX_DOCUMENT_SYNC_PULL_CURSOR_LENGTH,\n MAX_DOCUMENT_SYNC_REQUEST_BYTES,\n MAX_DOCUMENT_SYNC_RESPONSE_ENVELOPE_BYTES,\n MAX_DOCUMENT_SYNC_RESPONSE_PAGE_BYTES,\n MAX_DOCUMENT_SYNC_RESPONSE_PAGE_UPDATES,\n MAX_DOCUMENT_SYNC_RESPONSE_UPDATE_PAGE_BYTES,\n} from \"./documentSyncLimits\";\nexport {\n isSerializedKeyEnvelope,\n isSerializedKeyEnvelopeArray,\n type SerializedKeyEnvelope,\n} from \"./keyEnvelope\";\nexport {\n KNOWN_MANIFEST_HINT_LIMIT,\n KnownManifestsHeadersSchema,\n parseKnownManifestHints,\n} from \"./knownManifestWire\";\nexport {\n isOrganizationLeaveRequest,\n type OrganizationLeaveRequest,\n type OrganizationLeaveRequestMembersHead,\n OrganizationLeaveRequestSchema,\n} from \"./organizationLeaveRequest\";\nexport {\n isOrganizationReplacementAuthorization,\n type OrganizationReplacementAuthorization,\n OrganizationReplacementAuthorizationSchema,\n} from \"./organizationReplacement\";\nexport {\n PRINCIPAL_POLICY_HISTORY_PAGE_LIMIT,\n PRINCIPAL_POLICY_REPAIR_HEAD_LIMIT,\n} from \"./principalHistoryWire\";\nexport {\n hasArrayProperty,\n hasBooleanProperty,\n hasNonEmptyStringProperty,\n hasNullableNumberProperty,\n hasNullableStringProperty,\n hasNumberProperty,\n hasObjectProperty,\n hasOptionalStringProperty,\n hasPositiveIntegerProperty,\n hasPropertyValue,\n hasStringProperty,\n isNonEmptyStringArray,\n isOptionalRecordArray,\n isOptionalRecordArrayArray,\n isRecordArray,\n isRecordArrayArray,\n isStringArray,\n} from \"./properties\";\nexport {\n AUTH_CHALLENGE_HEX_LENGTH,\n isAuthChallengeHexString,\n isByteArray,\n isByteArrayOfLength,\n isSha256HexString,\n ML_DSA87_PUBLIC_KEY_BYTES,\n ML_DSA87_SIGNATURE_BYTES,\n ML_KEM1024_PUBLIC_KEY_BYTES,\n SHA256_HEX_LENGTH,\n} from \"./protocol\";\nexport {\n CANONICAL_SIGNED_AT_PATTERN,\n isCanonicalSignedAt,\n MAX_SIGNED_AT_YEAR,\n MIN_SIGNED_AT_YEAR,\n SIGNED_AT_CONTRACT,\n} from \"./signedAt\";\nexport { isUuidV4String } from \"./uuid\";\nexport { isWalLsnString, parseWalLsn } from \"./walLsn\";\n"]}
@@ -1,14 +1,37 @@
1
1
  import { z } from "zod";
2
+ /**
3
+ * The exact signed head of the organization's reserved Members group that the
4
+ * member saw when asking. Removing anyone from Members rotates its key epoch,
5
+ * so a request stays current only while the member has stayed in Members
6
+ * since this head: a request from an earlier membership never is.
7
+ */
8
+ export declare const OrganizationLeaveRequestMembersHeadSchema: z.ZodObject<{
9
+ principalId: z.ZodCustom<string, string>;
10
+ version: z.ZodCustom<number, number>;
11
+ keyEpoch: z.ZodCustom<number, number>;
12
+ stateHash: z.ZodString;
13
+ keyFingerprint: z.ZodString;
14
+ }, z.core.$strict>;
15
+ export type OrganizationLeaveRequestMembersHead = z.infer<typeof OrganizationLeaveRequestMembersHeadSchema>;
2
16
  /**
3
17
  * A member's signed request to leave an organization. Signed with the member's
4
18
  * identity key so an administrator can tell the request came from the member,
5
- * not from the server. It authorizes nothing by itself: leaving still needs an
6
- * administrator to sign the member's removal from the organization's groups.
19
+ * not from the server, and bound to the Members head the member saw so it
20
+ * cannot outlive their membership. It authorizes nothing by itself: leaving
21
+ * still needs an administrator to sign the member's removal from the
22
+ * organization's groups.
7
23
  */
8
24
  export declare const OrganizationLeaveRequestSchema: z.ZodObject<{
9
25
  organizationId: z.ZodCustom<string, string>;
10
26
  userId: z.ZodCustom<string, string>;
11
27
  requestedAt: z.ZodString;
28
+ membersHead: z.ZodObject<{
29
+ principalId: z.ZodCustom<string, string>;
30
+ version: z.ZodCustom<number, number>;
31
+ keyEpoch: z.ZodCustom<number, number>;
32
+ stateHash: z.ZodString;
33
+ keyFingerprint: z.ZodString;
34
+ }, z.core.$strict>;
12
35
  signature: z.ZodString;
13
36
  }, z.core.$strict>;
14
37
  export type OrganizationLeaveRequest = z.infer<typeof OrganizationLeaveRequestSchema>;
@@ -1,12 +1,27 @@
1
1
  import { z } from "zod";
2
2
  import { registerJsonSchemaFragment } from "../jsonSchema.js";
3
- import { nonEmptyStringSchema, uuidV4StringSchema } from "../schema.js";
3
+ import { boundedPositiveIntegerSchema, nonEmptyStringSchema, uuidV4StringSchema, } from "../schema.js";
4
4
  import { CANONICAL_SIGNED_AT_PATTERN, isCanonicalSignedAt, SIGNED_AT_CONTRACT, } from "./signedAt.js";
5
+ /**
6
+ * The exact signed head of the organization's reserved Members group that the
7
+ * member saw when asking. Removing anyone from Members rotates its key epoch,
8
+ * so a request stays current only while the member has stayed in Members
9
+ * since this head: a request from an earlier membership never is.
10
+ */
11
+ export const OrganizationLeaveRequestMembersHeadSchema = z.strictObject({
12
+ principalId: uuidV4StringSchema,
13
+ version: boundedPositiveIntegerSchema(Number.MAX_SAFE_INTEGER),
14
+ keyEpoch: boundedPositiveIntegerSchema(Number.MAX_SAFE_INTEGER),
15
+ stateHash: nonEmptyStringSchema,
16
+ keyFingerprint: nonEmptyStringSchema,
17
+ });
5
18
  /**
6
19
  * A member's signed request to leave an organization. Signed with the member's
7
20
  * identity key so an administrator can tell the request came from the member,
8
- * not from the server. It authorizes nothing by itself: leaving still needs an
9
- * administrator to sign the member's removal from the organization's groups.
21
+ * not from the server, and bound to the Members head the member saw so it
22
+ * cannot outlive their membership. It authorizes nothing by itself: leaving
23
+ * still needs an administrator to sign the member's removal from the
24
+ * organization's groups.
10
25
  */
11
26
  export const OrganizationLeaveRequestSchema = z.strictObject({
12
27
  organizationId: uuidV4StringSchema,
@@ -14,6 +29,7 @@ export const OrganizationLeaveRequestSchema = z.strictObject({
14
29
  requestedAt: registerJsonSchemaFragment(z.string().refine(isCanonicalSignedAt, {
15
30
  message: `requestedAt must be ${SIGNED_AT_CONTRACT}`,
16
31
  }), { pattern: CANONICAL_SIGNED_AT_PATTERN.source, type: "string" }),
32
+ membersHead: OrganizationLeaveRequestMembersHeadSchema,
17
33
  signature: nonEmptyStringSchema,
18
34
  });
19
35
  export function isOrganizationLeaveRequest(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"organizationLeaveRequest.js","sourceRoot":"","sources":["organizationLeaveRequest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,YAAY,CAAC;IAC3D,cAAc,EAAE,kBAAkB;IAClC,MAAM,EAAE,kBAAkB;IAC1B,WAAW,EAAE,0BAA0B,CACrC,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,mBAAmB,EAAE;QACrC,OAAO,EAAE,uBAAuB,kBAAkB,EAAE;KACrD,CAAC,EACF,EAAE,OAAO,EAAE,2BAA2B,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAChE;IACD,SAAS,EAAE,oBAAoB;CAChC,CAAC,CAAC;AAMH,MAAM,UAAU,0BAA0B,CACxC,KAAc;IAEd,OAAO,8BAA8B,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;AACjE,CAAC","sourcesContent":["import { z } from \"zod\";\nimport { registerJsonSchemaFragment } from \"../jsonSchema\";\nimport { nonEmptyStringSchema, uuidV4StringSchema } from \"../schema\";\nimport {\n CANONICAL_SIGNED_AT_PATTERN,\n isCanonicalSignedAt,\n SIGNED_AT_CONTRACT,\n} from \"./signedAt\";\n\n/**\n * A member's signed request to leave an organization. Signed with the member's\n * identity key so an administrator can tell the request came from the member,\n * not from the server. It authorizes nothing by itself: leaving still needs an\n * administrator to sign the member's removal from the organization's groups.\n */\nexport const OrganizationLeaveRequestSchema = z.strictObject({\n organizationId: uuidV4StringSchema,\n userId: uuidV4StringSchema,\n requestedAt: registerJsonSchemaFragment(\n z.string().refine(isCanonicalSignedAt, {\n message: `requestedAt must be ${SIGNED_AT_CONTRACT}`,\n }),\n { pattern: CANONICAL_SIGNED_AT_PATTERN.source, type: \"string\" },\n ),\n signature: nonEmptyStringSchema,\n});\n\nexport type OrganizationLeaveRequest = z.infer<\n typeof OrganizationLeaveRequestSchema\n>;\n\nexport function isOrganizationLeaveRequest(\n value: unknown,\n): value is OrganizationLeaveRequest {\n return OrganizationLeaveRequestSchema.safeParse(value).success;\n}\n"]}
1
+ {"version":3,"file":"organizationLeaveRequest.js","sourceRoot":"","sources":["organizationLeaveRequest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,4BAA4B,EAC5B,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yCAAyC,GAAG,CAAC,CAAC,YAAY,CAAC;IACtE,WAAW,EAAE,kBAAkB;IAC/B,OAAO,EAAE,4BAA4B,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAC9D,QAAQ,EAAE,4BAA4B,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAC/D,SAAS,EAAE,oBAAoB;IAC/B,cAAc,EAAE,oBAAoB;CACrC,CAAC,CAAC;AAMH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,YAAY,CAAC;IAC3D,cAAc,EAAE,kBAAkB;IAClC,MAAM,EAAE,kBAAkB;IAC1B,WAAW,EAAE,0BAA0B,CACrC,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,mBAAmB,EAAE;QACrC,OAAO,EAAE,uBAAuB,kBAAkB,EAAE;KACrD,CAAC,EACF,EAAE,OAAO,EAAE,2BAA2B,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAChE;IACD,WAAW,EAAE,yCAAyC;IACtD,SAAS,EAAE,oBAAoB;CAChC,CAAC,CAAC;AAMH,MAAM,UAAU,0BAA0B,CACxC,KAAc;IAEd,OAAO,8BAA8B,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;AACjE,CAAC","sourcesContent":["import { z } from \"zod\";\nimport { registerJsonSchemaFragment } from \"../jsonSchema\";\nimport {\n boundedPositiveIntegerSchema,\n nonEmptyStringSchema,\n uuidV4StringSchema,\n} from \"../schema\";\nimport {\n CANONICAL_SIGNED_AT_PATTERN,\n isCanonicalSignedAt,\n SIGNED_AT_CONTRACT,\n} from \"./signedAt\";\n\n/**\n * The exact signed head of the organization's reserved Members group that the\n * member saw when asking. Removing anyone from Members rotates its key epoch,\n * so a request stays current only while the member has stayed in Members\n * since this head: a request from an earlier membership never is.\n */\nexport const OrganizationLeaveRequestMembersHeadSchema = z.strictObject({\n principalId: uuidV4StringSchema,\n version: boundedPositiveIntegerSchema(Number.MAX_SAFE_INTEGER),\n keyEpoch: boundedPositiveIntegerSchema(Number.MAX_SAFE_INTEGER),\n stateHash: nonEmptyStringSchema,\n keyFingerprint: nonEmptyStringSchema,\n});\n\nexport type OrganizationLeaveRequestMembersHead = z.infer<\n typeof OrganizationLeaveRequestMembersHeadSchema\n>;\n\n/**\n * A member's signed request to leave an organization. Signed with the member's\n * identity key so an administrator can tell the request came from the member,\n * not from the server, and bound to the Members head the member saw so it\n * cannot outlive their membership. It authorizes nothing by itself: leaving\n * still needs an administrator to sign the member's removal from the\n * organization's groups.\n */\nexport const OrganizationLeaveRequestSchema = z.strictObject({\n organizationId: uuidV4StringSchema,\n userId: uuidV4StringSchema,\n requestedAt: registerJsonSchemaFragment(\n z.string().refine(isCanonicalSignedAt, {\n message: `requestedAt must be ${SIGNED_AT_CONTRACT}`,\n }),\n { pattern: CANONICAL_SIGNED_AT_PATTERN.source, type: \"string\" },\n ),\n membersHead: OrganizationLeaveRequestMembersHeadSchema,\n signature: nonEmptyStringSchema,\n});\n\nexport type OrganizationLeaveRequest = z.infer<\n typeof OrganizationLeaveRequestSchema\n>;\n\nexport function isOrganizationLeaveRequest(\n value: unknown,\n): value is OrganizationLeaveRequest {\n return OrganizationLeaveRequestSchema.safeParse(value).success;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tearleads/client-sdk",
3
- "version": "0.1.105",
3
+ "version": "0.1.107",
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,7 +1,7 @@
1
1
  import type { RequestResult } from "../../../internal/api-client/index.js";
2
2
  import { type SigningKeyPair } from "../../../internal/crypto/index.js";
3
3
  import type { OrganizationMembershipResponse } from "../../../internal/validators/response/index.js";
4
- import type { OrganizationLeaveRequest } from "../../../internal/validators/util/index.js";
4
+ import type { OrganizationLeaveRequest, OrganizationLeaveRequestMembersHead } from "../../../internal/validators/util/index.js";
5
5
  import type { OrganizationMembership } from "./organizationMemberships.js";
6
6
  export interface OrganizationLeaveRequestApi {
7
7
  setOrganizationLeaveRequest(organizationId: string, request: OrganizationLeaveRequest | null): Promise<RequestResult<OrganizationMembershipResponse>>;
@@ -17,10 +17,14 @@ export type OrganizationLeaveRequestResult = {
17
17
  /**
18
18
  * Files or withdraws the user's request to leave one organization. Filing
19
19
  * signs the request with the user's identity key, so administrators can tell
20
- * the member made it; it removes nothing until an administrator approves it.
20
+ * the member made it, and binds it to the organization's Members head as this
21
+ * device verified it, so it cannot outlive this membership. It removes nothing
22
+ * until an administrator approves it.
21
23
  */
22
24
  export declare function setOrganizationLeaveRequested(input: {
23
25
  readonly apiClient: OrganizationLeaveRequestApi;
26
+ /** The organization's current Members head, freshly verified. */
27
+ readonly loadMembersHead: () => Promise<OrganizationLeaveRequestMembersHead>;
24
28
  readonly organizationId: string;
25
29
  readonly requested: boolean;
26
30
  readonly signer: {
@@ -2,16 +2,31 @@ import { signOrganizationLeaveRequest, } from "../../../internal/crypto/index.js
2
2
  /**
3
3
  * Files or withdraws the user's request to leave one organization. Filing
4
4
  * signs the request with the user's identity key, so administrators can tell
5
- * the member made it; it removes nothing until an administrator approves it.
5
+ * the member made it, and binds it to the organization's Members head as this
6
+ * device verified it, so it cannot outlive this membership. It removes nothing
7
+ * until an administrator approves it.
6
8
  */
7
9
  export async function setOrganizationLeaveRequested(input) {
8
- const request = input.requested
9
- ? signOrganizationLeaveRequest({
10
+ let request = null;
11
+ if (input.requested) {
12
+ let membersHead;
13
+ try {
14
+ membersHead = await input.loadMembersHead();
15
+ }
16
+ catch {
17
+ return {
18
+ httpStatus: null,
19
+ message: "Could not confirm your current membership. Try again.",
20
+ status: "failed",
21
+ };
22
+ }
23
+ request = signOrganizationLeaveRequest({
24
+ membersHead,
10
25
  organizationId: input.organizationId,
11
26
  requestedAt: (input.now?.() ?? new Date()).toISOString(),
12
27
  userId: input.signer.userId,
13
- }, input.signer.signingKeyPair)
14
- : null;
28
+ }, input.signer.signingKeyPair);
29
+ }
15
30
  const result = await input.apiClient.setOrganizationLeaveRequest(input.organizationId, request);
16
31
  return result.ok
17
32
  ? { membership: result.data, status: "updated" }
@@ -1 +1 @@
1
- {"version":3,"file":"leaveRequest.js","sourceRoot":"","sources":["leaveRequest.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAoB3B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,KASnD;IACC,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS;QAC7B,CAAC,CAAC,4BAA4B,CAC1B;YACE,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE;YACxD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;SAC5B,EACD,KAAK,CAAC,MAAM,CAAC,cAAc,CAC5B;QACH,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,2BAA2B,CAC9D,KAAK,CAAC,cAAc,EACpB,OAAO,CACR,CAAC;IACF,OAAO,MAAM,CAAC,EAAE;QACd,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE;QAChD,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC/E,CAAC","sourcesContent":["import type { RequestResult } from \"@tearleads/api-client\";\nimport {\n type SigningKeyPair,\n signOrganizationLeaveRequest,\n} from \"@tearleads/crypto\";\nimport type { OrganizationMembershipResponse } from \"@tearleads/validators/response\";\nimport type { OrganizationLeaveRequest } from \"@tearleads/validators/util\";\nimport type { OrganizationMembership } from \"./organizationMemberships\";\n\nexport interface OrganizationLeaveRequestApi {\n setOrganizationLeaveRequest(\n organizationId: string,\n request: OrganizationLeaveRequest | null,\n ): Promise<RequestResult<OrganizationMembershipResponse>>;\n}\n\nexport type OrganizationLeaveRequestResult =\n | { readonly status: \"updated\"; readonly membership: OrganizationMembership }\n | {\n readonly status: \"failed\";\n readonly httpStatus: number | null;\n readonly message: string;\n };\n\n/**\n * Files or withdraws the user's request to leave one organization. Filing\n * signs the request with the user's identity key, so administrators can tell\n * the member made it; it removes nothing until an administrator approves it.\n */\nexport async function setOrganizationLeaveRequested(input: {\n readonly apiClient: OrganizationLeaveRequestApi;\n readonly organizationId: string;\n readonly requested: boolean;\n readonly signer: {\n readonly signingKeyPair: Pick<SigningKeyPair, \"signingPrivateKey\">;\n readonly userId: string;\n };\n readonly now?: () => Date;\n}): Promise<OrganizationLeaveRequestResult> {\n const request = input.requested\n ? signOrganizationLeaveRequest(\n {\n organizationId: input.organizationId,\n requestedAt: (input.now?.() ?? new Date()).toISOString(),\n userId: input.signer.userId,\n },\n input.signer.signingKeyPair,\n )\n : null;\n const result = await input.apiClient.setOrganizationLeaveRequest(\n input.organizationId,\n request,\n );\n return result.ok\n ? { membership: result.data, status: \"updated\" }\n : { httpStatus: result.status, message: result.message, status: \"failed\" };\n}\n"]}
1
+ {"version":3,"file":"leaveRequest.js","sourceRoot":"","sources":["leaveRequest.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAuB3B;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,KAWnD;IACC,IAAI,OAAO,GAAoC,IAAI,CAAC;IACpD,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,IAAI,WAAgD,CAAC;QACrD,IAAI,CAAC;YACH,WAAW,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,uDAAuD;gBAChE,MAAM,EAAE,QAAQ;aACjB,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,4BAA4B,CACpC;YACE,WAAW;YACX,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE;YACxD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;SAC5B,EACD,KAAK,CAAC,MAAM,CAAC,cAAc,CAC5B,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,2BAA2B,CAC9D,KAAK,CAAC,cAAc,EACpB,OAAO,CACR,CAAC;IACF,OAAO,MAAM,CAAC,EAAE;QACd,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE;QAChD,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC/E,CAAC","sourcesContent":["import type { RequestResult } from \"@tearleads/api-client\";\nimport {\n type SigningKeyPair,\n signOrganizationLeaveRequest,\n} from \"@tearleads/crypto\";\nimport type { OrganizationMembershipResponse } from \"@tearleads/validators/response\";\nimport type {\n OrganizationLeaveRequest,\n OrganizationLeaveRequestMembersHead,\n} from \"@tearleads/validators/util\";\nimport type { OrganizationMembership } from \"./organizationMemberships\";\n\nexport interface OrganizationLeaveRequestApi {\n setOrganizationLeaveRequest(\n organizationId: string,\n request: OrganizationLeaveRequest | null,\n ): Promise<RequestResult<OrganizationMembershipResponse>>;\n}\n\nexport type OrganizationLeaveRequestResult =\n | { readonly status: \"updated\"; readonly membership: OrganizationMembership }\n | {\n readonly status: \"failed\";\n readonly httpStatus: number | null;\n readonly message: string;\n };\n\n/**\n * Files or withdraws the user's request to leave one organization. Filing\n * signs the request with the user's identity key, so administrators can tell\n * the member made it, and binds it to the organization's Members head as this\n * device verified it, so it cannot outlive this membership. It removes nothing\n * until an administrator approves it.\n */\nexport async function setOrganizationLeaveRequested(input: {\n readonly apiClient: OrganizationLeaveRequestApi;\n /** The organization's current Members head, freshly verified. */\n readonly loadMembersHead: () => Promise<OrganizationLeaveRequestMembersHead>;\n readonly organizationId: string;\n readonly requested: boolean;\n readonly signer: {\n readonly signingKeyPair: Pick<SigningKeyPair, \"signingPrivateKey\">;\n readonly userId: string;\n };\n readonly now?: () => Date;\n}): Promise<OrganizationLeaveRequestResult> {\n let request: OrganizationLeaveRequest | null = null;\n if (input.requested) {\n let membersHead: OrganizationLeaveRequestMembersHead;\n try {\n membersHead = await input.loadMembersHead();\n } catch {\n return {\n httpStatus: null,\n message: \"Could not confirm your current membership. Try again.\",\n status: \"failed\",\n };\n }\n request = signOrganizationLeaveRequest(\n {\n membersHead,\n organizationId: input.organizationId,\n requestedAt: (input.now?.() ?? new Date()).toISOString(),\n userId: input.signer.userId,\n },\n input.signer.signingKeyPair,\n );\n }\n const result = await input.apiClient.setOrganizationLeaveRequest(\n input.organizationId,\n request,\n );\n return result.ok\n ? { membership: result.data, status: \"updated\" }\n : { httpStatus: result.status, message: result.message, status: \"failed\" };\n}\n"]}
@@ -0,0 +1,80 @@
1
+ import { ProjectionDependencyUnavailableError } from "../../../data/keyingProjectionVerification/dependencyUnavailable.js";
2
+ import { assertProjectionVerificationCurrent } from "../../../data/keyingProjectionVerification/types.js";
3
+ import { requireOrganizationGroupHead } from "../../../data/principals/organizationAuthorityDescriptor.js";
4
+ import { createRuntimePrincipalPolicyCurrentResolver, } from "../../principals/runtimePolicyRecovery.js";
5
+ import { loadCurrentOrganizationAuthority } from "../currentOrganizationAuthority.js";
6
+ import { loadLocalOrganizationPolicyReference } from "../readModel/localReadModelDetails.js";
7
+ import { createLeaveRequestMembershipCheck, } from "./leaveRequestMembership.js";
8
+ /**
9
+ * The organization's current Members head as the member's own device
10
+ * discovers and verifies it now, for a leave request to cite.
11
+ */
12
+ export async function loadCurrentMembersHead(input) {
13
+ const resolveCurrentPolicy = createRuntimePrincipalPolicyCurrentResolver(input.runtime);
14
+ if (!resolveCurrentPolicy)
15
+ throw new ProjectionDependencyUnavailableError("Asking to leave requires private paged recovery");
16
+ const authority = await loadCurrentOrganizationAuthority({
17
+ execSql: input.runtime.infra.execSql,
18
+ organizationId: input.organizationId,
19
+ resolveCurrentPolicy,
20
+ stillCurrent: input.stillCurrent,
21
+ });
22
+ assertProjectionVerificationCurrent(authority.stillCurrent);
23
+ const head = requireOrganizationGroupHead(authority.descriptor, authority.descriptor.memberGroupId);
24
+ return {
25
+ keyEpoch: head.keyEpoch,
26
+ keyFingerprint: head.keyFingerprint,
27
+ principalId: head.principalId,
28
+ stateHash: head.stateHash,
29
+ version: head.version,
30
+ };
31
+ }
32
+ /**
33
+ * Checks leave requests against the Members head in the organization policy
34
+ * this device's read model selected, verified exactly through the private
35
+ * paged verifier (from local evidence when it has it), and walks verified
36
+ * Members history the same way when a request cites an earlier key epoch.
37
+ */
38
+ export function createRuntimeLeaveRequestMembershipCheck(input) {
39
+ const resolve = createRuntimePrincipalPolicyCurrentResolver(input.runtime);
40
+ return createLeaveRequestMembershipCheck({
41
+ async loadMembersHead() {
42
+ if (!resolve)
43
+ return null;
44
+ const organizationReference = await loadLocalOrganizationPolicyReference({
45
+ currentUserId: input.currentUserId,
46
+ execSql: input.runtime.infra.execSql,
47
+ organizationId: input.organizationId,
48
+ principalId: input.organizationId,
49
+ principalType: "organization",
50
+ });
51
+ if (!organizationReference)
52
+ return null;
53
+ const authority = await loadCurrentOrganizationAuthority({
54
+ execSql: input.runtime.infra.execSql,
55
+ organizationId: input.organizationId,
56
+ organizationReference,
57
+ resolveCurrentPolicy: resolve,
58
+ stillCurrent: input.stillCurrent,
59
+ });
60
+ assertProjectionVerificationCurrent(authority.stillCurrent);
61
+ return requireOrganizationGroupHead(authority.descriptor, authority.descriptor.memberGroupId);
62
+ },
63
+ loadHistoryPage: resolve
64
+ ? async (head, beforeVersion) => {
65
+ const resolved = await resolve({
66
+ organizationId: input.organizationId,
67
+ reference: head,
68
+ preferLocalCurrent: true,
69
+ stillCurrent: input.stillCurrent,
70
+ historyPage: beforeVersion === undefined ? {} : { beforeVersion },
71
+ });
72
+ assertProjectionVerificationCurrent(resolved.stillCurrent);
73
+ if (!resolved.historyPage)
74
+ throw new Error("Verified Members history page is missing");
75
+ return resolved.historyPage;
76
+ }
77
+ : undefined,
78
+ });
79
+ }
80
+ //# sourceMappingURL=leaveRequestMembersEvidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaveRequestMembersEvidence.js","sourceRoot":"","sources":["leaveRequestMembersEvidence.ts"],"names":[],"mappings":"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","sourcesContent":["import type { OrganizationLeaveRequestMembersHead } from \"@tearleads/validators/util\";\nimport { ProjectionDependencyUnavailableError } from \"../../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport { assertProjectionVerificationCurrent } from \"../../../data/keyingProjectionVerification/types\";\nimport { requireOrganizationGroupHead } from \"../../../data/principals/organizationAuthorityDescriptor\";\nimport {\n createRuntimePrincipalPolicyCurrentResolver,\n type PrincipalPolicyRecoveryRuntime,\n} from \"../../principals/runtimePolicyRecovery\";\nimport { loadCurrentOrganizationAuthority } from \"../currentOrganizationAuthority\";\nimport { loadLocalOrganizationPolicyReference } from \"../readModel/localReadModelDetails\";\nimport {\n createLeaveRequestMembershipCheck,\n type LeaveRequestMembershipCheck,\n} from \"./leaveRequestMembership\";\n\ninterface MembersEvidenceInput {\n readonly runtime: PrincipalPolicyRecoveryRuntime;\n readonly organizationId: string;\n readonly stillCurrent: () => boolean;\n}\n\n/**\n * The organization's current Members head as the member's own device\n * discovers and verifies it now, for a leave request to cite.\n */\nexport async function loadCurrentMembersHead(\n input: MembersEvidenceInput,\n): Promise<OrganizationLeaveRequestMembersHead> {\n const resolveCurrentPolicy = createRuntimePrincipalPolicyCurrentResolver(\n input.runtime,\n );\n if (!resolveCurrentPolicy)\n throw new ProjectionDependencyUnavailableError(\n \"Asking to leave requires private paged recovery\",\n );\n const authority = await loadCurrentOrganizationAuthority({\n execSql: input.runtime.infra.execSql,\n organizationId: input.organizationId,\n resolveCurrentPolicy,\n stillCurrent: input.stillCurrent,\n });\n assertProjectionVerificationCurrent(authority.stillCurrent);\n const head = requireOrganizationGroupHead(\n authority.descriptor,\n authority.descriptor.memberGroupId,\n );\n return {\n keyEpoch: head.keyEpoch,\n keyFingerprint: head.keyFingerprint,\n principalId: head.principalId,\n stateHash: head.stateHash,\n version: head.version,\n };\n}\n\n/**\n * Checks leave requests against the Members head in the organization policy\n * this device's read model selected, verified exactly through the private\n * paged verifier (from local evidence when it has it), and walks verified\n * Members history the same way when a request cites an earlier key epoch.\n */\nexport function createRuntimeLeaveRequestMembershipCheck(\n input: MembersEvidenceInput & { readonly currentUserId: string },\n): LeaveRequestMembershipCheck {\n const resolve = createRuntimePrincipalPolicyCurrentResolver(input.runtime);\n return createLeaveRequestMembershipCheck({\n async loadMembersHead() {\n if (!resolve) return null;\n const organizationReference = await loadLocalOrganizationPolicyReference({\n currentUserId: input.currentUserId,\n execSql: input.runtime.infra.execSql,\n organizationId: input.organizationId,\n principalId: input.organizationId,\n principalType: \"organization\",\n });\n if (!organizationReference) return null;\n const authority = await loadCurrentOrganizationAuthority({\n execSql: input.runtime.infra.execSql,\n organizationId: input.organizationId,\n organizationReference,\n resolveCurrentPolicy: resolve,\n stillCurrent: input.stillCurrent,\n });\n assertProjectionVerificationCurrent(authority.stillCurrent);\n return requireOrganizationGroupHead(\n authority.descriptor,\n authority.descriptor.memberGroupId,\n );\n },\n loadHistoryPage: resolve\n ? async (head, beforeVersion) => {\n const resolved = await resolve({\n organizationId: input.organizationId,\n reference: head,\n preferLocalCurrent: true,\n stillCurrent: input.stillCurrent,\n historyPage: beforeVersion === undefined ? {} : { beforeVersion },\n });\n assertProjectionVerificationCurrent(resolved.stillCurrent);\n if (!resolved.historyPage)\n throw new Error(\"Verified Members history page is missing\");\n return resolved.historyPage;\n }\n : undefined,\n });\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { ReferencedPrincipalHead } from "../../../internal/crypto/index.js";
2
+ import type { OrganizationLeaveRequest } from "../../../internal/validators/util/index.js";
3
+ import type { RecoveredPrincipalHistoryPage } from "../../principals/historyRecovery/loadRecoveredPrincipalHistoryPage.js";
4
+ export interface LeaveRequestMembershipEvidence {
5
+ /** The Members head this device has verified, or null when it has none. */
6
+ readonly loadMembersHead: () => Promise<ReferencedPrincipalHead | null>;
7
+ /**
8
+ * Verified Members history ending at `head`, one page at a time and in
9
+ * ascending order. Absent where this device cannot read history.
10
+ */
11
+ readonly loadHistoryPage?: ((head: ReferencedPrincipalHead, beforeVersion: number | undefined) => Promise<RecoveredPrincipalHistoryPage>) | undefined;
12
+ }
13
+ /** Whether a leave request still belongs to its member's current membership. */
14
+ export type LeaveRequestMembershipCheck = (request: OrganizationLeaveRequest) => Promise<boolean>;
15
+ /**
16
+ * Checks each leave request against this device's verified Members head.
17
+ * Removing anyone from Members rotates its key epoch and key, so a request
18
+ * citing the head's own epoch and key has seen no removal since. One citing an earlier epoch
19
+ * stands only if verified history shows its member in every Members state
20
+ * from the cited one on; a request from a membership a removal ended never
21
+ * does, whatever the server serves beside it. Display only: a request this
22
+ * device cannot place is shown as no request.
23
+ */
24
+ export declare function createLeaveRequestMembershipCheck(evidence: LeaveRequestMembershipEvidence): LeaveRequestMembershipCheck;
@@ -0,0 +1,65 @@
1
+ // How far back one request's check walks verified Members history: a request
2
+ // stays pending for days, not for hundreds of Members changes.
3
+ const MAX_MEMBERS_HISTORY_PAGES = 8;
4
+ function citesState(request, state) {
5
+ const cited = request.membersHead;
6
+ return (state.version === cited.version &&
7
+ state.stateHash === cited.stateHash &&
8
+ state.keyEpoch === cited.keyEpoch &&
9
+ state.keyFingerprint === cited.keyFingerprint);
10
+ }
11
+ // Walks verified Members history back from the head to the cited state,
12
+ // requiring the member in every state on the way.
13
+ async function memberStayedSinceCitedState(request, head, loadHistoryPage) {
14
+ let expectedVersion = head.version;
15
+ let beforeVersion;
16
+ for (let pages = 0; pages < MAX_MEMBERS_HISTORY_PAGES; pages += 1) {
17
+ const page = await loadHistoryPage(head, beforeVersion);
18
+ for (const entry of [...page.entries].reverse()) {
19
+ const { state } = entry;
20
+ if (state.principalType !== head.principalType ||
21
+ state.principalId !== head.principalId ||
22
+ state.version !== expectedVersion ||
23
+ (state.version === head.version && state.stateHash !== head.stateHash))
24
+ return false;
25
+ if (!entry.projection.some((member) => member.userId === request.userId))
26
+ return false;
27
+ if (state.version === request.membersHead.version)
28
+ return citesState(request, state);
29
+ expectedVersion -= 1;
30
+ }
31
+ if (page.nextBeforeVersion === null || page.entries.length === 0)
32
+ return false;
33
+ beforeVersion = page.nextBeforeVersion;
34
+ }
35
+ return false;
36
+ }
37
+ /**
38
+ * Checks each leave request against this device's verified Members head.
39
+ * Removing anyone from Members rotates its key epoch and key, so a request
40
+ * citing the head's own epoch and key has seen no removal since. One citing an earlier epoch
41
+ * stands only if verified history shows its member in every Members state
42
+ * from the cited one on; a request from a membership a removal ended never
43
+ * does, whatever the server serves beside it. Display only: a request this
44
+ * device cannot place is shown as no request.
45
+ */
46
+ export function createLeaveRequestMembershipCheck(evidence) {
47
+ let membersHead;
48
+ return async (request) => {
49
+ membersHead ??= evidence.loadMembersHead().catch(() => null);
50
+ const head = await membersHead;
51
+ const cited = request.membersHead;
52
+ if (head?.principalType !== "group" ||
53
+ cited.principalId !== head.principalId ||
54
+ cited.keyEpoch > head.keyEpoch)
55
+ return false;
56
+ // One signed chain holds one key per epoch, so a matching epoch with a
57
+ // different key can only come from a fork, never from this history.
58
+ if (cited.keyEpoch === head.keyEpoch)
59
+ return cited.keyFingerprint === head.keyFingerprint;
60
+ if (!evidence.loadHistoryPage || cited.version >= head.version)
61
+ return false;
62
+ return memberStayedSinceCitedState(request, head, evidence.loadHistoryPage).catch(() => false);
63
+ };
64
+ }
65
+ //# sourceMappingURL=leaveRequestMembership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaveRequestMembership.js","sourceRoot":"","sources":["leaveRequestMembership.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReferencedPrincipalHead } from \"@tearleads/crypto\";\nimport type { OrganizationLeaveRequest } from \"@tearleads/validators/util\";\nimport type { RecoveredPrincipalHistoryPage } from \"../../principals/historyRecovery/loadRecoveredPrincipalHistoryPage\";\n\n// How far back one request's check walks verified Members history: a request\n// stays pending for days, not for hundreds of Members changes.\nconst MAX_MEMBERS_HISTORY_PAGES = 8;\n\nexport interface LeaveRequestMembershipEvidence {\n /** The Members head this device has verified, or null when it has none. */\n readonly loadMembersHead: () => Promise<ReferencedPrincipalHead | null>;\n /**\n * Verified Members history ending at `head`, one page at a time and in\n * ascending order. Absent where this device cannot read history.\n */\n readonly loadHistoryPage?:\n | ((\n head: ReferencedPrincipalHead,\n beforeVersion: number | undefined,\n ) => Promise<RecoveredPrincipalHistoryPage>)\n | undefined;\n}\n\n/** Whether a leave request still belongs to its member's current membership. */\nexport type LeaveRequestMembershipCheck = (\n request: OrganizationLeaveRequest,\n) => Promise<boolean>;\n\nfunction citesState(\n request: OrganizationLeaveRequest,\n state: RecoveredPrincipalHistoryPage[\"entries\"][number][\"state\"],\n): boolean {\n const cited = request.membersHead;\n return (\n state.version === cited.version &&\n state.stateHash === cited.stateHash &&\n state.keyEpoch === cited.keyEpoch &&\n state.keyFingerprint === cited.keyFingerprint\n );\n}\n\n// Walks verified Members history back from the head to the cited state,\n// requiring the member in every state on the way.\nasync function memberStayedSinceCitedState(\n request: OrganizationLeaveRequest,\n head: ReferencedPrincipalHead,\n loadHistoryPage: NonNullable<\n LeaveRequestMembershipEvidence[\"loadHistoryPage\"]\n >,\n): Promise<boolean> {\n let expectedVersion = head.version;\n let beforeVersion: number | undefined;\n for (let pages = 0; pages < MAX_MEMBERS_HISTORY_PAGES; pages += 1) {\n const page = await loadHistoryPage(head, beforeVersion);\n for (const entry of [...page.entries].reverse()) {\n const { state } = entry;\n if (\n state.principalType !== head.principalType ||\n state.principalId !== head.principalId ||\n state.version !== expectedVersion ||\n (state.version === head.version && state.stateHash !== head.stateHash)\n )\n return false;\n if (!entry.projection.some((member) => member.userId === request.userId))\n return false;\n if (state.version === request.membersHead.version)\n return citesState(request, state);\n expectedVersion -= 1;\n }\n if (page.nextBeforeVersion === null || page.entries.length === 0)\n return false;\n beforeVersion = page.nextBeforeVersion;\n }\n return false;\n}\n\n/**\n * Checks each leave request against this device's verified Members head.\n * Removing anyone from Members rotates its key epoch and key, so a request\n * citing the head's own epoch and key has seen no removal since. One citing an earlier epoch\n * stands only if verified history shows its member in every Members state\n * from the cited one on; a request from a membership a removal ended never\n * does, whatever the server serves beside it. Display only: a request this\n * device cannot place is shown as no request.\n */\nexport function createLeaveRequestMembershipCheck(\n evidence: LeaveRequestMembershipEvidence,\n): LeaveRequestMembershipCheck {\n let membersHead: Promise<ReferencedPrincipalHead | null> | undefined;\n return async (request) => {\n membersHead ??= evidence.loadMembersHead().catch(() => null);\n const head = await membersHead;\n const cited = request.membersHead;\n if (\n head?.principalType !== \"group\" ||\n cited.principalId !== head.principalId ||\n cited.keyEpoch > head.keyEpoch\n )\n return false;\n // One signed chain holds one key per epoch, so a matching epoch with a\n // different key can only come from a fork, never from this history.\n if (cited.keyEpoch === head.keyEpoch)\n return cited.keyFingerprint === head.keyFingerprint;\n if (!evidence.loadHistoryPage || cited.version >= head.version)\n return false;\n return memberStayedSinceCitedState(\n request,\n head,\n evidence.loadHistoryPage,\n ).catch(() => false);\n };\n}\n"]}
@@ -1,15 +1,23 @@
1
1
  import type { TrustedUserIdentityResolver } from "../../../data/trustedUserIdentity/index.js";
2
2
  import type { OrganizationDirectoryAndGroups, OrganizationDirectoryUser, OrganizationUserDetail } from "../readModel/index.js";
3
+ import type { LeaveRequestMembershipCheck } from "./leaveRequestMembership.js";
4
+ /** What a shown leave request is checked against. */
5
+ export interface LeaveRequestVerifier {
6
+ readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;
7
+ readonly membershipIsCurrent: LeaveRequestMembershipCheck;
8
+ }
3
9
  /**
4
10
  * The user as shown, with its leave request kept only when the member signed
5
- * it. Every directory user a client presents passes through here, whether a
6
- * read model or a mutation response carried it.
11
+ * it during their current membership. Every directory user a client presents
12
+ * passes through here, whether a read model or a mutation response carried
13
+ * it.
7
14
  */
8
- export declare function withVerifiedDirectoryUser(organizationId: string, user: OrganizationDirectoryUser, resolveTrustedUserIdentity: TrustedUserIdentityResolver): Promise<OrganizationDirectoryUser>;
15
+ export declare function withVerifiedDirectoryUser(organizationId: string, user: OrganizationDirectoryUser, verifier: LeaveRequestVerifier): Promise<OrganizationDirectoryUser>;
9
16
  /**
10
- * Keeps only leave requests their member signed, checked against the member's
11
- * trusted identity; any other is shown as no request at all.
17
+ * Keeps only leave requests their member signed during their current
18
+ * membership, checked against the member's trusted identity and this device's
19
+ * verified Members history; any other is shown as no request at all.
12
20
  */
13
- export declare function withVerifiedLeaveRequests<Result extends OrganizationDirectoryAndGroups | null | undefined>(result: Result, resolveTrustedUserIdentity: TrustedUserIdentityResolver): Promise<Result>;
21
+ export declare function withVerifiedLeaveRequests<Result extends OrganizationDirectoryAndGroups | null | undefined>(result: Result, verifier: LeaveRequestVerifier): Promise<Result>;
14
22
  /** The user detail counterpart of withVerifiedLeaveRequests. */
15
- export declare function withVerifiedUserLeaveRequest(detail: OrganizationUserDetail | null, organizationId: string, resolveTrustedUserIdentity: TrustedUserIdentityResolver): Promise<OrganizationUserDetail | null>;
23
+ export declare function withVerifiedUserLeaveRequest(detail: OrganizationUserDetail | null, organizationId: string, verifier: LeaveRequestVerifier): Promise<OrganizationUserDetail | null>;