@company-semantics/contracts 56.0.0 → 56.1.0
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.
- package/package.json +1 -1
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +107 -0
- package/src/generated/openapi-routes.ts +2 -0
- package/src/index.ts +25 -0
- package/src/org/README.md +12 -0
- package/src/org/index.ts +13 -0
- package/src/org/schemas.ts +110 -0
- package/src/resource-key-types.ts +3 -0
- package/src/resource-keys.ts +2 -0
package/package.json
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '373ff35b2847' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '373ff35b284714e9030fd0c611b3d4c1be255cd12df7f6adec2b6f7e6ba9179f' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -926,6 +926,23 @@ export interface paths {
|
|
|
926
926
|
patch?: never;
|
|
927
927
|
trace?: never;
|
|
928
928
|
};
|
|
929
|
+
"/api/workspace/invites/batch": {
|
|
930
|
+
parameters: {
|
|
931
|
+
query?: never;
|
|
932
|
+
header?: never;
|
|
933
|
+
path?: never;
|
|
934
|
+
cookie?: never;
|
|
935
|
+
};
|
|
936
|
+
get?: never;
|
|
937
|
+
put?: never;
|
|
938
|
+
/** Invite many directory people at once */
|
|
939
|
+
post: operations["batchInvite"];
|
|
940
|
+
delete?: never;
|
|
941
|
+
options?: never;
|
|
942
|
+
head?: never;
|
|
943
|
+
patch?: never;
|
|
944
|
+
trace?: never;
|
|
945
|
+
};
|
|
929
946
|
"/api/workspace/invites/accept": {
|
|
930
947
|
parameters: {
|
|
931
948
|
query?: never;
|
|
@@ -977,6 +994,23 @@ export interface paths {
|
|
|
977
994
|
patch?: never;
|
|
978
995
|
trace?: never;
|
|
979
996
|
};
|
|
997
|
+
"/api/workspace/directory": {
|
|
998
|
+
parameters: {
|
|
999
|
+
query?: never;
|
|
1000
|
+
header?: never;
|
|
1001
|
+
path?: never;
|
|
1002
|
+
cookie?: never;
|
|
1003
|
+
};
|
|
1004
|
+
/** List the org people directory */
|
|
1005
|
+
get: operations["listOrgDirectory"];
|
|
1006
|
+
put?: never;
|
|
1007
|
+
post?: never;
|
|
1008
|
+
delete?: never;
|
|
1009
|
+
options?: never;
|
|
1010
|
+
head?: never;
|
|
1011
|
+
patch?: never;
|
|
1012
|
+
trace?: never;
|
|
1013
|
+
};
|
|
980
1014
|
"/api/workspace/domains": {
|
|
981
1015
|
parameters: {
|
|
982
1016
|
query?: never;
|
|
@@ -4577,6 +4611,26 @@ export interface components {
|
|
|
4577
4611
|
contributesTo: string[];
|
|
4578
4612
|
};
|
|
4579
4613
|
};
|
|
4614
|
+
BatchInviteResponse: {
|
|
4615
|
+
invited: string[];
|
|
4616
|
+
skipped: {
|
|
4617
|
+
/** Format: uuid */
|
|
4618
|
+
personId: string;
|
|
4619
|
+
/** @enum {string} */
|
|
4620
|
+
reason: "already-member" | "invite-pending" | "no-email" | "send-failed";
|
|
4621
|
+
}[];
|
|
4622
|
+
failed: {
|
|
4623
|
+
/** Format: uuid */
|
|
4624
|
+
personId: string;
|
|
4625
|
+
/** @enum {string} */
|
|
4626
|
+
reason: "already-member" | "invite-pending" | "no-email" | "send-failed";
|
|
4627
|
+
}[];
|
|
4628
|
+
};
|
|
4629
|
+
BatchInviteRequest: {
|
|
4630
|
+
personIds: string[];
|
|
4631
|
+
/** @enum {string} */
|
|
4632
|
+
role: "admin" | "member";
|
|
4633
|
+
};
|
|
4580
4634
|
InviteListResponse: {
|
|
4581
4635
|
id: string;
|
|
4582
4636
|
orgId: string;
|
|
@@ -4625,6 +4679,15 @@ export interface components {
|
|
|
4625
4679
|
status: string;
|
|
4626
4680
|
orgName?: string;
|
|
4627
4681
|
};
|
|
4682
|
+
OrgDirectoryResponse: {
|
|
4683
|
+
people: {
|
|
4684
|
+
/** Format: uuid */
|
|
4685
|
+
personId: string;
|
|
4686
|
+
displayName: string;
|
|
4687
|
+
primaryEmail: string | null;
|
|
4688
|
+
userId: string | null;
|
|
4689
|
+
}[];
|
|
4690
|
+
};
|
|
4628
4691
|
DomainListResponse: {
|
|
4629
4692
|
id: string;
|
|
4630
4693
|
orgId: string;
|
|
@@ -8230,6 +8293,30 @@ export interface operations {
|
|
|
8230
8293
|
};
|
|
8231
8294
|
};
|
|
8232
8295
|
};
|
|
8296
|
+
batchInvite: {
|
|
8297
|
+
parameters: {
|
|
8298
|
+
query?: never;
|
|
8299
|
+
header?: never;
|
|
8300
|
+
path?: never;
|
|
8301
|
+
cookie?: never;
|
|
8302
|
+
};
|
|
8303
|
+
requestBody: {
|
|
8304
|
+
content: {
|
|
8305
|
+
"application/json": components["schemas"]["BatchInviteRequest"];
|
|
8306
|
+
};
|
|
8307
|
+
};
|
|
8308
|
+
responses: {
|
|
8309
|
+
/** @description Per-person outcome; partial success is a normal result */
|
|
8310
|
+
200: {
|
|
8311
|
+
headers: {
|
|
8312
|
+
[name: string]: unknown;
|
|
8313
|
+
};
|
|
8314
|
+
content: {
|
|
8315
|
+
"application/json": components["schemas"]["BatchInviteResponse"];
|
|
8316
|
+
};
|
|
8317
|
+
};
|
|
8318
|
+
};
|
|
8319
|
+
};
|
|
8233
8320
|
acceptInvite: {
|
|
8234
8321
|
parameters: {
|
|
8235
8322
|
query?: never;
|
|
@@ -8296,6 +8383,26 @@ export interface operations {
|
|
|
8296
8383
|
};
|
|
8297
8384
|
};
|
|
8298
8385
|
};
|
|
8386
|
+
listOrgDirectory: {
|
|
8387
|
+
parameters: {
|
|
8388
|
+
query?: never;
|
|
8389
|
+
header?: never;
|
|
8390
|
+
path?: never;
|
|
8391
|
+
cookie?: never;
|
|
8392
|
+
};
|
|
8393
|
+
requestBody?: never;
|
|
8394
|
+
responses: {
|
|
8395
|
+
/** @description Every person in the org directory */
|
|
8396
|
+
200: {
|
|
8397
|
+
headers: {
|
|
8398
|
+
[name: string]: unknown;
|
|
8399
|
+
};
|
|
8400
|
+
content: {
|
|
8401
|
+
"application/json": components["schemas"]["OrgDirectoryResponse"];
|
|
8402
|
+
};
|
|
8403
|
+
};
|
|
8404
|
+
};
|
|
8405
|
+
};
|
|
8299
8406
|
listDomains: {
|
|
8300
8407
|
parameters: {
|
|
8301
8408
|
query?: never;
|
|
@@ -185,12 +185,14 @@ export const openApiRoutes = {
|
|
|
185
185
|
'/api/workspace/auth-policy/test-sso': ['POST'],
|
|
186
186
|
'/api/workspace/auth-policy/test-sso/{attemptId}': ['GET'],
|
|
187
187
|
'/api/workspace/auth-policy/validate-oidc': ['POST'],
|
|
188
|
+
'/api/workspace/directory': ['GET'],
|
|
188
189
|
'/api/workspace/domains': ['GET', 'POST'],
|
|
189
190
|
'/api/workspace/domains/{id}': ['DELETE'],
|
|
190
191
|
'/api/workspace/domains/{id}/verify': ['POST'],
|
|
191
192
|
'/api/workspace/handle': ['PATCH'],
|
|
192
193
|
'/api/workspace/invites': ['GET', 'POST'],
|
|
193
194
|
'/api/workspace/invites/accept': ['POST'],
|
|
195
|
+
'/api/workspace/invites/batch': ['POST'],
|
|
194
196
|
'/api/workspace/invites/validate': ['GET'],
|
|
195
197
|
'/api/workspace/invites/{id}': ['DELETE'],
|
|
196
198
|
'/api/workspace/members': ['GET'],
|
package/src/index.ts
CHANGED
|
@@ -620,6 +620,31 @@ export type {
|
|
|
620
620
|
PositionReporting,
|
|
621
621
|
} from "./org/index";
|
|
622
622
|
|
|
623
|
+
// People directory: every `persons` row in the org, account-bearing or not.
|
|
624
|
+
// Neither the member list (RBAC principals) nor the org chart (topology) — the
|
|
625
|
+
// set an HRIS sync populates, and the only surface carrying a directory email.
|
|
626
|
+
// Capability-gated, deliberately: the org-chart read is authenticated-only.
|
|
627
|
+
export {
|
|
628
|
+
OrgDirectoryPersonSchema,
|
|
629
|
+
OrgDirectoryResponseSchema,
|
|
630
|
+
} from "./org/index";
|
|
631
|
+
export type { OrgDirectoryPerson, OrgDirectoryResponse } from "./org/index";
|
|
632
|
+
|
|
633
|
+
// Bulk invite, keyed by personId so recipients are resolved server-side from the
|
|
634
|
+
// caller's own directory rather than supplied by the caller.
|
|
635
|
+
export {
|
|
636
|
+
BATCH_INVITE_PERSONS_MAX,
|
|
637
|
+
BATCH_INVITE_SKIP_REASONS,
|
|
638
|
+
BatchInviteSkipReasonSchema,
|
|
639
|
+
BatchInviteRequestSchema,
|
|
640
|
+
BatchInviteResponseSchema,
|
|
641
|
+
} from "./org/index";
|
|
642
|
+
export type {
|
|
643
|
+
BatchInviteSkipReason,
|
|
644
|
+
BatchInviteRequest,
|
|
645
|
+
BatchInviteResponse,
|
|
646
|
+
} from "./org/index";
|
|
647
|
+
|
|
623
648
|
// Source authority: per-field ownership — who may WRITE a given entity field.
|
|
624
649
|
// Orthogonal to provenance (who reported). (ADR-CONT-083 / ADR-CTRL-182)
|
|
625
650
|
export { SourceAuthoritySchema } from "./org/index";
|
package/src/org/README.md
CHANGED
|
@@ -37,6 +37,14 @@ Shared type vocabulary for organization ownership, type classification, and tran
|
|
|
37
37
|
- `AuthoritySource` _(type)_
|
|
38
38
|
- `AuthoritySourceSchema` — Engine-internal authority source.
|
|
39
39
|
- `AuthorizableView` _(type)_ — Type for views that can be checked against VIEWSCOPEMAP.
|
|
40
|
+
- `BATCH_INVITE_PERSONS_MAX` — Cap on one batch.
|
|
41
|
+
- `BATCH_INVITE_SKIP_REASONS` — Why one person in a batch did not receive an invite. - `already-member` — holds an account in this org…
|
|
42
|
+
- `BatchInviteRequest` _(type)_
|
|
43
|
+
- `BatchInviteRequestSchema`
|
|
44
|
+
- `BatchInviteResponse` _(type)_
|
|
45
|
+
- `BatchInviteResponseSchema`
|
|
46
|
+
- `BatchInviteSkipReason` _(type)_
|
|
47
|
+
- `BatchInviteSkipReasonSchema`
|
|
40
48
|
- `COMPANY_MD_COLLAB_MAX_DOC_TEXT_BYTES` — Cap on the MATERIALIZED document text, in UTF-8 bytes of the expanded result.
|
|
41
49
|
- `COMPANY_MD_COLLAB_MAX_PRESENCE_POSITION_CHARS` — Cap on ONE presence position blob (`anchor` or `head`), in base64 chars.
|
|
42
50
|
- `COMPANY_MD_COLLAB_MAX_UPDATE_B64_CHARS` — Cap on ONE encoded update, measured on the base64 payload as it is sent.
|
|
@@ -178,6 +186,10 @@ Shared type vocabulary for organization ownership, type classification, and tran
|
|
|
178
186
|
- `OrgBudgetConfigSchema`
|
|
179
187
|
- `OrgDeletionStatus` _(type)_
|
|
180
188
|
- `OrgDeletionStatusSchema`
|
|
189
|
+
- `OrgDirectoryPerson` _(type)_
|
|
190
|
+
- `OrgDirectoryPersonSchema`
|
|
191
|
+
- `OrgDirectoryResponse` _(type)_
|
|
192
|
+
- `OrgDirectoryResponseSchema`
|
|
181
193
|
- `OrgDivergence` _(type)_
|
|
182
194
|
- `OrgDivergenceResolution` _(type)_
|
|
183
195
|
- `OrgDivergenceResolutionSchema` — What resolving a divergence means today.
|
package/src/org/index.ts
CHANGED
|
@@ -354,6 +354,14 @@ export {
|
|
|
354
354
|
CreateInviteRequestSchema,
|
|
355
355
|
InviteResponseSchema,
|
|
356
356
|
InviteListResponseSchema,
|
|
357
|
+
// People directory + bulk invite (personId-keyed, capability-gated)
|
|
358
|
+
OrgDirectoryPersonSchema,
|
|
359
|
+
OrgDirectoryResponseSchema,
|
|
360
|
+
BATCH_INVITE_PERSONS_MAX,
|
|
361
|
+
BATCH_INVITE_SKIP_REASONS,
|
|
362
|
+
BatchInviteSkipReasonSchema,
|
|
363
|
+
BatchInviteRequestSchema,
|
|
364
|
+
BatchInviteResponseSchema,
|
|
357
365
|
DomainResponseSchema,
|
|
358
366
|
DomainListResponseSchema,
|
|
359
367
|
OrgBillingResponseSchema,
|
|
@@ -366,6 +374,11 @@ export type {
|
|
|
366
374
|
CreateInviteRequestPayload,
|
|
367
375
|
InviteResponse,
|
|
368
376
|
InviteListResponse,
|
|
377
|
+
OrgDirectoryPerson,
|
|
378
|
+
OrgDirectoryResponse,
|
|
379
|
+
BatchInviteSkipReason,
|
|
380
|
+
BatchInviteRequest,
|
|
381
|
+
BatchInviteResponse,
|
|
369
382
|
DomainResponse,
|
|
370
383
|
DomainListResponse,
|
|
371
384
|
OrgBillingResponse,
|
package/src/org/schemas.ts
CHANGED
|
@@ -781,6 +781,116 @@ export type CreateInviteRequestPayload = z.infer<
|
|
|
781
781
|
typeof CreateInviteRequestSchema
|
|
782
782
|
>;
|
|
783
783
|
|
|
784
|
+
// ---------------------------------------------------------------------------
|
|
785
|
+
// GET /api/workspace/directory
|
|
786
|
+
//
|
|
787
|
+
// The org's PEOPLE DIRECTORY — every `persons` row, account-bearing or not.
|
|
788
|
+
// Distinct from the member list (`user_org_memberships`, RBAC principals) and
|
|
789
|
+
// from the org chart (`positions` + occupancy, topology): this is the answer to
|
|
790
|
+
// "who does this org know about", which after an HRIS sync is a much larger set
|
|
791
|
+
// than either.
|
|
792
|
+
//
|
|
793
|
+
// It exists because inviting a synced person needs their address, and the org
|
|
794
|
+
// chart cannot carry one: `GET /api/users/org-chart` is authenticated-only, so a
|
|
795
|
+
// `primaryEmail` there would let any member enumerate the whole company's email
|
|
796
|
+
// addresses. This surface is capability-gated instead.
|
|
797
|
+
// ---------------------------------------------------------------------------
|
|
798
|
+
|
|
799
|
+
export const OrgDirectoryPersonSchema = z.object({
|
|
800
|
+
personId: z.string().uuid(),
|
|
801
|
+
displayName: z.string(),
|
|
802
|
+
/**
|
|
803
|
+
* Nullable and NOT optional. A directory person with no address is a real,
|
|
804
|
+
* common state — an HRIS export with the email column unmapped produces a
|
|
805
|
+
* whole org of them — and it is the difference between "cannot be invited" and
|
|
806
|
+
* "not yet loaded". Callers must render the distinction rather than treat a
|
|
807
|
+
* missing address as absence of the person.
|
|
808
|
+
*/
|
|
809
|
+
primaryEmail: z.string().nullable(),
|
|
810
|
+
/** The linked account, or null when this person has never signed up. */
|
|
811
|
+
userId: z.string().uuid().nullable(),
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
export type OrgDirectoryPerson = z.infer<typeof OrgDirectoryPersonSchema>;
|
|
815
|
+
|
|
816
|
+
export const OrgDirectoryResponseSchema = z.object({
|
|
817
|
+
people: z.array(OrgDirectoryPersonSchema),
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
export type OrgDirectoryResponse = z.infer<typeof OrgDirectoryResponseSchema>;
|
|
821
|
+
|
|
822
|
+
// ---------------------------------------------------------------------------
|
|
823
|
+
// POST /api/workspace/invites/batch
|
|
824
|
+
//
|
|
825
|
+
// Bulk invite, keyed by `personId` rather than by email. That is a security
|
|
826
|
+
// property, not a convenience: the single-invite route takes a caller-supplied
|
|
827
|
+
// address and can therefore reach any mailbox on the internet, which is why it
|
|
828
|
+
// carries a strict per-minute limit. This route's recipients are resolved
|
|
829
|
+
// server-side from the caller's own `persons` rows, so the reachable set is
|
|
830
|
+
// bounded by the org's own directory and the arbitrary-recipient risk is gone.
|
|
831
|
+
//
|
|
832
|
+
// Consequence for callers: the client never needs to hold an email address to
|
|
833
|
+
// send an invite, and an unknown or cross-org `personId` is silently omitted
|
|
834
|
+
// from the response rather than reported — reporting it would make this a
|
|
835
|
+
// membership oracle.
|
|
836
|
+
// ---------------------------------------------------------------------------
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Cap on one batch. Bounds worst-case request latency, since the invite emails
|
|
840
|
+
* are sent before the response returns (the raw token exists only in memory and
|
|
841
|
+
* must not be parked in a queue).
|
|
842
|
+
*/
|
|
843
|
+
export const BATCH_INVITE_PERSONS_MAX = 100;
|
|
844
|
+
|
|
845
|
+
export const BatchInviteRequestSchema = z.object({
|
|
846
|
+
personIds: z.array(z.string().uuid()).min(1).max(BATCH_INVITE_PERSONS_MAX),
|
|
847
|
+
// Same restricted {admin, member} domain as a single invite.
|
|
848
|
+
role: z.enum(["admin", "member"]),
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
export type BatchInviteRequest = z.infer<typeof BatchInviteRequestSchema>;
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Why one person in a batch did not receive an invite.
|
|
855
|
+
*
|
|
856
|
+
* - `already-member` — holds an account in this org already.
|
|
857
|
+
* - `invite-pending` — an unexpired invite is already outstanding.
|
|
858
|
+
* - `no-email` — the directory row carries no address to send to.
|
|
859
|
+
* - `send-failed` — the invite row COMMITTED but the email did not leave.
|
|
860
|
+
* Recoverable by resending; never a reason to roll the
|
|
861
|
+
* invite back.
|
|
862
|
+
*/
|
|
863
|
+
export const BATCH_INVITE_SKIP_REASONS = [
|
|
864
|
+
"already-member",
|
|
865
|
+
"invite-pending",
|
|
866
|
+
"no-email",
|
|
867
|
+
"send-failed",
|
|
868
|
+
] as const;
|
|
869
|
+
|
|
870
|
+
export const BatchInviteSkipReasonSchema = z.enum(BATCH_INVITE_SKIP_REASONS);
|
|
871
|
+
|
|
872
|
+
export type BatchInviteSkipReason = z.infer<typeof BatchInviteSkipReasonSchema>;
|
|
873
|
+
|
|
874
|
+
const BatchInviteOutcomeSchema = z.object({
|
|
875
|
+
personId: z.string().uuid(),
|
|
876
|
+
reason: BatchInviteSkipReasonSchema,
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
export const BatchInviteResponseSchema = z.object({
|
|
880
|
+
/** Person ids that now hold a pending invite AND were emailed. */
|
|
881
|
+
invited: z.array(z.string().uuid()),
|
|
882
|
+
/** Never attempted — see {@link BatchInviteSkipReason}. */
|
|
883
|
+
skipped: z.array(BatchInviteOutcomeSchema),
|
|
884
|
+
/**
|
|
885
|
+
* Invite row exists, delivery did not. Separate from `skipped` because the
|
|
886
|
+
* remedy differs: a skip needs the underlying state to change, a failure needs
|
|
887
|
+
* a resend.
|
|
888
|
+
*/
|
|
889
|
+
failed: z.array(BatchInviteOutcomeSchema),
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
export type BatchInviteResponse = z.infer<typeof BatchInviteResponseSchema>;
|
|
893
|
+
|
|
784
894
|
// ---------------------------------------------------------------------------
|
|
785
895
|
// OrgDomain sub-schema
|
|
786
896
|
// ---------------------------------------------------------------------------
|
|
@@ -20,6 +20,9 @@ export type ResourceKey =
|
|
|
20
20
|
| { type: "teams"; orgId: string }
|
|
21
21
|
| { type: "integrations"; orgId: string }
|
|
22
22
|
| { type: "invites"; orgId: string }
|
|
23
|
+
// The people directory (`persons`) — every person the org knows about,
|
|
24
|
+
// account-bearing or not. Distinct from `members`, which is accounts only.
|
|
25
|
+
| { type: "orgDirectory"; orgId: string }
|
|
23
26
|
| { type: "auditEvents"; orgId: string }
|
|
24
27
|
| { type: "timeline"; orgId: string }
|
|
25
28
|
// Identities (singular) — single entities
|
package/src/resource-keys.ts
CHANGED
|
@@ -129,6 +129,7 @@ const ORG_SCOPED_TYPES = [
|
|
|
129
129
|
"teams",
|
|
130
130
|
"integrations",
|
|
131
131
|
"invites",
|
|
132
|
+
"orgDirectory",
|
|
132
133
|
"auditEvents",
|
|
133
134
|
"timeline",
|
|
134
135
|
"workspace",
|
|
@@ -266,6 +267,7 @@ export function toQueryKey(key: ResourceKey): readonly string[] {
|
|
|
266
267
|
case "teams":
|
|
267
268
|
case "integrations":
|
|
268
269
|
case "invites":
|
|
270
|
+
case "orgDirectory":
|
|
269
271
|
case "auditEvents":
|
|
270
272
|
case "timeline":
|
|
271
273
|
case "workspace":
|