@company-semantics/contracts 13.15.0 → 13.17.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/__tests__/resource-keys.test.ts +28 -0
- package/src/api/generated.ts +87 -0
- package/src/identity/index.ts +4 -0
- package/src/identity/people-org-chart.ts +24 -0
- package/src/index.ts +4 -0
- package/src/org/__tests__/org-units.test.ts +4 -0
- package/src/org/schemas.ts +10 -0
- package/src/resource-keys.ts +7 -0
package/package.json
CHANGED
|
@@ -31,6 +31,34 @@ describe("resource-keys: orgUnitOwners (org-scoped)", () => {
|
|
|
31
31
|
});
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
describe("resource-keys: orgUnitOpenRoles (per-unit identity)", () => {
|
|
35
|
+
const UNIT_ID = "33333333-3333-4333-8333-333333333333";
|
|
36
|
+
const key: ResourceKey = {
|
|
37
|
+
type: "orgUnitOpenRoles",
|
|
38
|
+
orgId: ORG_ID,
|
|
39
|
+
unitId: UNIT_ID,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
it("toQueryKey produces [type, orgId, unitId]", () => {
|
|
43
|
+
const qk = toQueryKey(key);
|
|
44
|
+
expect(qk).toEqual(["orgUnitOpenRoles", ORG_ID, UNIT_ID]);
|
|
45
|
+
expect(qk).toHaveLength(3);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("roundtrips through fromQueryKey", () => {
|
|
49
|
+
const parsed = fromQueryKey(toQueryKey(key));
|
|
50
|
+
expect(parsed).toEqual(key);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("matchesResourceKey is unitId-scoped: rejects a different unit", () => {
|
|
54
|
+
const otherUnit = "44444444-4444-4444-8444-444444444444";
|
|
55
|
+
expect(matchesResourceKey(toQueryKey(key), key)).toBe(true);
|
|
56
|
+
expect(
|
|
57
|
+
matchesResourceKey(["orgUnitOpenRoles", ORG_ID, otherUnit], key),
|
|
58
|
+
).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
34
62
|
describe("resource-keys: system-scoped internalAdmin* types", () => {
|
|
35
63
|
const systemTypes = [
|
|
36
64
|
"internalAdminAiProviders",
|
package/src/api/generated.ts
CHANGED
|
@@ -1972,6 +1972,24 @@ export interface paths {
|
|
|
1972
1972
|
patch?: never;
|
|
1973
1973
|
trace?: never;
|
|
1974
1974
|
};
|
|
1975
|
+
"/api/org-units/{unitId}/head": {
|
|
1976
|
+
parameters: {
|
|
1977
|
+
query?: never;
|
|
1978
|
+
header?: never;
|
|
1979
|
+
path?: never;
|
|
1980
|
+
cookie?: never;
|
|
1981
|
+
};
|
|
1982
|
+
get?: never;
|
|
1983
|
+
/** Pin an explicit head (anchor person) for an org unit */
|
|
1984
|
+
put: operations["setOrgUnitHead"];
|
|
1985
|
+
post?: never;
|
|
1986
|
+
/** Revert an org unit head to the system-inferred default */
|
|
1987
|
+
delete: operations["revertOrgUnitHead"];
|
|
1988
|
+
options?: never;
|
|
1989
|
+
head?: never;
|
|
1990
|
+
patch?: never;
|
|
1991
|
+
trace?: never;
|
|
1992
|
+
};
|
|
1975
1993
|
"/api/org-units/{unitId}/my-authority": {
|
|
1976
1994
|
parameters: {
|
|
1977
1995
|
query?: never;
|
|
@@ -4287,6 +4305,8 @@ export interface components {
|
|
|
4287
4305
|
count: number;
|
|
4288
4306
|
userIds: string[];
|
|
4289
4307
|
} | null;
|
|
4308
|
+
headUserId: string | null;
|
|
4309
|
+
headOrigin: ("user" | "inferred") | null;
|
|
4290
4310
|
}[];
|
|
4291
4311
|
};
|
|
4292
4312
|
OrgUnitDescendantsResponse: {
|
|
@@ -4435,6 +4455,8 @@ export interface components {
|
|
|
4435
4455
|
count: number;
|
|
4436
4456
|
userIds: string[];
|
|
4437
4457
|
} | null;
|
|
4458
|
+
headUserId: string | null;
|
|
4459
|
+
headOrigin: ("user" | "inferred") | null;
|
|
4438
4460
|
}[];
|
|
4439
4461
|
levelConfig: {
|
|
4440
4462
|
/** Format: uuid */
|
|
@@ -4579,6 +4601,12 @@ export interface components {
|
|
|
4579
4601
|
revokedAt: string | null;
|
|
4580
4602
|
reason: string | null;
|
|
4581
4603
|
};
|
|
4604
|
+
UnitHeadResponse: {
|
|
4605
|
+
/** Format: uuid */
|
|
4606
|
+
unitId: string;
|
|
4607
|
+
headUserId: string | null;
|
|
4608
|
+
origin: ("user" | "inferred") | null;
|
|
4609
|
+
};
|
|
4582
4610
|
OrgUnitMyAuthorityResponse: {
|
|
4583
4611
|
/** Format: uuid */
|
|
4584
4612
|
unitId: string;
|
|
@@ -4693,6 +4721,14 @@ export interface components {
|
|
|
4693
4721
|
unitId: string;
|
|
4694
4722
|
title: string | null;
|
|
4695
4723
|
}[];
|
|
4724
|
+
unitHeads: {
|
|
4725
|
+
/** Format: uuid */
|
|
4726
|
+
unitId: string;
|
|
4727
|
+
/** Format: uuid */
|
|
4728
|
+
headUserId: string;
|
|
4729
|
+
/** @enum {string} */
|
|
4730
|
+
origin: "user" | "inferred";
|
|
4731
|
+
}[];
|
|
4696
4732
|
};
|
|
4697
4733
|
/** @description Polling snapshot of a generic ingestion operation. */
|
|
4698
4734
|
IngestionOperationPollResponse: {
|
|
@@ -8444,6 +8480,57 @@ export interface operations {
|
|
|
8444
8480
|
};
|
|
8445
8481
|
};
|
|
8446
8482
|
};
|
|
8483
|
+
setOrgUnitHead: {
|
|
8484
|
+
parameters: {
|
|
8485
|
+
query?: never;
|
|
8486
|
+
header?: never;
|
|
8487
|
+
path: {
|
|
8488
|
+
unitId: string;
|
|
8489
|
+
};
|
|
8490
|
+
cookie?: never;
|
|
8491
|
+
};
|
|
8492
|
+
requestBody: {
|
|
8493
|
+
content: {
|
|
8494
|
+
"application/json": {
|
|
8495
|
+
/** Format: uuid */
|
|
8496
|
+
userId: string;
|
|
8497
|
+
};
|
|
8498
|
+
};
|
|
8499
|
+
};
|
|
8500
|
+
responses: {
|
|
8501
|
+
/** @description The pinned unit head */
|
|
8502
|
+
200: {
|
|
8503
|
+
headers: {
|
|
8504
|
+
[name: string]: unknown;
|
|
8505
|
+
};
|
|
8506
|
+
content: {
|
|
8507
|
+
"application/json": components["schemas"]["UnitHeadResponse"];
|
|
8508
|
+
};
|
|
8509
|
+
};
|
|
8510
|
+
};
|
|
8511
|
+
};
|
|
8512
|
+
revertOrgUnitHead: {
|
|
8513
|
+
parameters: {
|
|
8514
|
+
query?: never;
|
|
8515
|
+
header?: never;
|
|
8516
|
+
path: {
|
|
8517
|
+
unitId: string;
|
|
8518
|
+
};
|
|
8519
|
+
cookie?: never;
|
|
8520
|
+
};
|
|
8521
|
+
requestBody?: never;
|
|
8522
|
+
responses: {
|
|
8523
|
+
/** @description The re-inferred unit head (or null when none resolves) */
|
|
8524
|
+
200: {
|
|
8525
|
+
headers: {
|
|
8526
|
+
[name: string]: unknown;
|
|
8527
|
+
};
|
|
8528
|
+
content: {
|
|
8529
|
+
"application/json": components["schemas"]["UnitHeadResponse"];
|
|
8530
|
+
};
|
|
8531
|
+
};
|
|
8532
|
+
};
|
|
8533
|
+
};
|
|
8447
8534
|
getMyOrgUnitAuthority: {
|
|
8448
8535
|
parameters: {
|
|
8449
8536
|
query?: never;
|
package/src/identity/index.ts
CHANGED
|
@@ -67,6 +67,8 @@ export {
|
|
|
67
67
|
PeopleOrgChartNodeSchema,
|
|
68
68
|
PeopleOrgChartEdgeSchema,
|
|
69
69
|
PeopleOrgChartOpenRoleSchema,
|
|
70
|
+
UnitHeadOriginSchema,
|
|
71
|
+
PeopleOrgChartUnitHeadSchema,
|
|
70
72
|
PeopleOrgChartResponseSchema,
|
|
71
73
|
} from "./people-org-chart";
|
|
72
74
|
export type {
|
|
@@ -74,5 +76,7 @@ export type {
|
|
|
74
76
|
PeopleOrgChartNode,
|
|
75
77
|
PeopleOrgChartEdge,
|
|
76
78
|
PeopleOrgChartOpenRole,
|
|
79
|
+
UnitHeadOrigin,
|
|
80
|
+
PeopleOrgChartUnitHead,
|
|
77
81
|
PeopleOrgChartResponse,
|
|
78
82
|
} from "./people-org-chart";
|
|
@@ -66,6 +66,29 @@ export type PeopleOrgChartOpenRole = z.infer<
|
|
|
66
66
|
typeof PeopleOrgChartOpenRoleSchema
|
|
67
67
|
>;
|
|
68
68
|
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Unit head — the person a unit (and its person-less children: empty-unit
|
|
71
|
+
// placeholders, pending-invite ghosts, open-role seats) anchors under in the
|
|
72
|
+
// people chart. A presentation/anchor "tether" (ADR-BE-284), NOT authority.
|
|
73
|
+
// `origin` distinguishes an explicit user choice from a system-inferred default
|
|
74
|
+
// so the UI can render "Pinned" vs "Auto" and offer revert-to-auto. The client
|
|
75
|
+
// prefers the head of a unit's PARENT when anchoring its person-less children,
|
|
76
|
+
// falling back to the render-time heuristic only when no head row exists.
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
export const UnitHeadOriginSchema = z.enum(["user", "inferred"]);
|
|
80
|
+
export type UnitHeadOrigin = z.infer<typeof UnitHeadOriginSchema>;
|
|
81
|
+
|
|
82
|
+
export const PeopleOrgChartUnitHeadSchema = z.object({
|
|
83
|
+
unitId: z.string().uuid(),
|
|
84
|
+
headUserId: z.string().uuid(),
|
|
85
|
+
origin: UnitHeadOriginSchema,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export type PeopleOrgChartUnitHead = z.infer<
|
|
89
|
+
typeof PeopleOrgChartUnitHeadSchema
|
|
90
|
+
>;
|
|
91
|
+
|
|
69
92
|
// ---------------------------------------------------------------------------
|
|
70
93
|
// GET /api/users/org-chart
|
|
71
94
|
// ---------------------------------------------------------------------------
|
|
@@ -74,6 +97,7 @@ export const PeopleOrgChartResponseSchema = z.object({
|
|
|
74
97
|
nodes: z.array(PeopleOrgChartNodeSchema),
|
|
75
98
|
edges: z.array(PeopleOrgChartEdgeSchema),
|
|
76
99
|
openRoles: z.array(PeopleOrgChartOpenRoleSchema),
|
|
100
|
+
unitHeads: z.array(PeopleOrgChartUnitHeadSchema),
|
|
77
101
|
});
|
|
78
102
|
|
|
79
103
|
export type PeopleOrgChartResponse = z.infer<
|
package/src/index.ts
CHANGED
|
@@ -173,6 +173,8 @@ export {
|
|
|
173
173
|
PeopleOrgChartNodeSchema,
|
|
174
174
|
PeopleOrgChartEdgeSchema,
|
|
175
175
|
PeopleOrgChartOpenRoleSchema,
|
|
176
|
+
UnitHeadOriginSchema,
|
|
177
|
+
PeopleOrgChartUnitHeadSchema,
|
|
176
178
|
PeopleOrgChartResponseSchema,
|
|
177
179
|
} from "./identity/index";
|
|
178
180
|
export type {
|
|
@@ -180,6 +182,8 @@ export type {
|
|
|
180
182
|
PeopleOrgChartNode,
|
|
181
183
|
PeopleOrgChartEdge,
|
|
182
184
|
PeopleOrgChartOpenRole,
|
|
185
|
+
UnitHeadOrigin,
|
|
186
|
+
PeopleOrgChartUnitHead,
|
|
183
187
|
PeopleOrgChartResponse,
|
|
184
188
|
} from "./identity/index";
|
|
185
189
|
|
|
@@ -98,6 +98,8 @@ describe("OrgUnitTreeNodeSchema", () => {
|
|
|
98
98
|
memberCount: 5,
|
|
99
99
|
openRoleCount: 1,
|
|
100
100
|
missingAtNextLevel: null,
|
|
101
|
+
headUserId: null,
|
|
102
|
+
headOrigin: null,
|
|
101
103
|
};
|
|
102
104
|
expect(() => OrgUnitTreeNodeSchema.parse(base)).not.toThrow();
|
|
103
105
|
expect(() => OrgUnitTreeNodeSchema.parse({ ...base, depth: 0 })).toThrow();
|
|
@@ -112,6 +114,8 @@ describe("OrgUnitTreeNodeSchema", () => {
|
|
|
112
114
|
memberCount: 5,
|
|
113
115
|
openRoleCount: 0,
|
|
114
116
|
missingAtNextLevel: null,
|
|
117
|
+
headUserId: null,
|
|
118
|
+
headOrigin: null,
|
|
115
119
|
};
|
|
116
120
|
expect(() => OrgUnitTreeNodeSchema.parse(base)).not.toThrow();
|
|
117
121
|
expect(() =>
|
package/src/org/schemas.ts
CHANGED
|
@@ -947,6 +947,16 @@ export const OrgUnitTreeNodeSchema = OrgUnitSchema.extend({
|
|
|
947
947
|
*/
|
|
948
948
|
openRoleCount: z.number().int().min(0),
|
|
949
949
|
missingAtNextLevel: MissingAtNextLevelSchema.nullable(),
|
|
950
|
+
/**
|
|
951
|
+
* The unit head (ADR-BE-284): the person this unit (and its person-less
|
|
952
|
+
* children) anchors under in the people chart — a presentation "tether", NOT
|
|
953
|
+
* authority. `headUserId` is null when no head is resolved (e.g. an empty
|
|
954
|
+
* unit with no members). `headOrigin` is `'user'` for an explicit choice,
|
|
955
|
+
* `'inferred'` for a system default (so the management UI can show Pinned vs
|
|
956
|
+
* Auto and offer revert-to-auto), or null when there is no head.
|
|
957
|
+
*/
|
|
958
|
+
headUserId: z.string().uuid().nullable(),
|
|
959
|
+
headOrigin: z.enum(["user", "inferred"]).nullable(),
|
|
950
960
|
});
|
|
951
961
|
|
|
952
962
|
export const OrgUnitMembershipSchema = z.object({
|
package/src/resource-keys.ts
CHANGED
|
@@ -36,6 +36,11 @@ export type ResourceKey =
|
|
|
36
36
|
| { type: "orgUnitAncestors"; orgId: string; unitId: string }
|
|
37
37
|
| { type: "orgUnitMemberships"; orgId: string; unitId: string }
|
|
38
38
|
| { type: "orgUnitPermissions"; orgId: string; unitId: string }
|
|
39
|
+
// Open roles seated in a unit (ADR-BE-277) — a per-unit list, unitId-scoped
|
|
40
|
+
// like memberships/permissions. Read in the unit members view and the
|
|
41
|
+
// org-chart card; refreshed as a graph target of orgTree on structural
|
|
42
|
+
// mutations (create / advance lifecycle / fill). See ADR-CONTRACTS-065.
|
|
43
|
+
| { type: "orgUnitOpenRoles"; orgId: string; unitId: string }
|
|
39
44
|
// Org-unit owners list (ADR-CONTRACTS-052) — owners are an org-wide
|
|
40
45
|
// projection, not a per-unit collection, so unitId is intentionally excluded.
|
|
41
46
|
| { type: "orgUnitOwners"; orgId: string }
|
|
@@ -145,6 +150,7 @@ export function toQueryKey(key: ResourceKey): readonly string[] {
|
|
|
145
150
|
case "orgUnitAncestors":
|
|
146
151
|
case "orgUnitMemberships":
|
|
147
152
|
case "orgUnitPermissions":
|
|
153
|
+
case "orgUnitOpenRoles":
|
|
148
154
|
return [key.type, key.orgId, key.unitId] as const;
|
|
149
155
|
|
|
150
156
|
// User-scoped
|
|
@@ -218,6 +224,7 @@ export function fromQueryKey(queryKey: readonly string[]): ResourceKey {
|
|
|
218
224
|
orgUnitAncestors: "unitId",
|
|
219
225
|
orgUnitMemberships: "unitId",
|
|
220
226
|
orgUnitPermissions: "unitId",
|
|
227
|
+
orgUnitOpenRoles: "unitId",
|
|
221
228
|
};
|
|
222
229
|
|
|
223
230
|
if (type in identityFields) {
|