@company-semantics/contracts 9.0.0 → 9.2.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.
Files changed (141) hide show
  1. package/package.json +6 -3
  2. package/src/__tests__/resource-keys.test.ts +30 -23
  3. package/src/admin/authz-simulate.ts +4 -4
  4. package/src/admin/direct-grants.ts +2 -2
  5. package/src/api/generated-spec-hash.ts +2 -2
  6. package/src/api/generated.ts +97 -0
  7. package/src/api/http/routes/ai-chat.ts +3 -3
  8. package/src/api/http/utils/resource-response.ts +5 -2
  9. package/src/api/index.ts +4 -4
  10. package/src/api/primitives.ts +6 -2
  11. package/src/auth/README.md +1 -0
  12. package/src/auth/index.ts +12 -5
  13. package/src/autotune.ts +5 -1
  14. package/src/billing/index.ts +1 -1
  15. package/src/billing/types.ts +1 -1
  16. package/src/chat/README.md +3 -0
  17. package/src/chat/__tests__/runtime-profile.test.ts +68 -48
  18. package/src/chat/index.ts +10 -4
  19. package/src/chat/runtime-profile.ts +25 -10
  20. package/src/chat/schemas.ts +49 -41
  21. package/src/chat/types.ts +48 -42
  22. package/src/ci-envelope/README.md +2 -0
  23. package/src/ci-envelope/__tests__/transitions.test.ts +56 -56
  24. package/src/ci-envelope/index.ts +2 -2
  25. package/src/ci-envelope/types.ts +20 -20
  26. package/src/ci-results/index.ts +2 -2
  27. package/src/ci-results/repo-ci-result.ts +15 -12
  28. package/src/compatibility.ts +6 -6
  29. package/src/content/index.ts +10 -4
  30. package/src/content/schemas.ts +42 -24
  31. package/src/dispatch/index.ts +18 -15
  32. package/src/email/__tests__/registry.test.ts +81 -77
  33. package/src/email/index.ts +3 -3
  34. package/src/email/registry.ts +25 -25
  35. package/src/email/types.ts +43 -43
  36. package/src/errors/index.ts +8 -8
  37. package/src/execution/__tests__/events.test.ts +42 -42
  38. package/src/execution/__tests__/lifecycle.test.ts +192 -190
  39. package/src/execution/__tests__/registry.test.ts +114 -114
  40. package/src/execution/audit-export.ts +4 -4
  41. package/src/execution/errors.ts +7 -7
  42. package/src/execution/event-metadata.ts +4 -4
  43. package/src/execution/events.ts +23 -21
  44. package/src/execution/expiry.ts +5 -5
  45. package/src/execution/hash-chain.ts +2 -2
  46. package/src/execution/index.ts +19 -28
  47. package/src/execution/kinds.ts +7 -7
  48. package/src/execution/lifecycle.ts +33 -33
  49. package/src/execution/registry.ts +63 -63
  50. package/src/execution/schemas.ts +31 -23
  51. package/src/execution/status.ts +45 -26
  52. package/src/execution/summary.ts +16 -17
  53. package/src/execution/timeline-ui.ts +9 -9
  54. package/src/execution/types.ts +31 -25
  55. package/src/generated/openapi-routes.ts +2 -0
  56. package/src/guards/config.ts +22 -18
  57. package/src/guards/index.ts +4 -4
  58. package/src/guards/types.ts +32 -24
  59. package/src/identity/__tests__/avatar.test.ts +68 -59
  60. package/src/identity/avatar.ts +8 -8
  61. package/src/identity/display-name.ts +3 -3
  62. package/src/identity/index.ts +8 -8
  63. package/src/identity/people-org-chart.ts +8 -4
  64. package/src/identity/schemas.ts +28 -18
  65. package/src/identity/types.ts +5 -5
  66. package/src/impersonation/index.ts +5 -5
  67. package/src/impersonation/schemas.ts +15 -9
  68. package/src/impersonation-events.ts +21 -21
  69. package/src/impersonation.ts +25 -24
  70. package/src/index.ts +118 -90
  71. package/src/interfaces/mcp/tools/help.ts +19 -19
  72. package/src/internal-admin.ts +6 -6
  73. package/src/mcp/README.md +2 -0
  74. package/src/mcp/__tests__/capability-graph.test.ts +290 -290
  75. package/src/mcp/capability-graph.ts +42 -40
  76. package/src/mcp/failure-context.ts +1 -3
  77. package/src/mcp/index.ts +69 -56
  78. package/src/mcp/resources.ts +9 -9
  79. package/src/meetings/index.ts +2 -2
  80. package/src/meetings/schemas.ts +51 -34
  81. package/src/message-parts/README.md +2 -0
  82. package/src/message-parts/__tests__/builder.test.ts +142 -142
  83. package/src/message-parts/__tests__/confirmation.test.ts +100 -86
  84. package/src/message-parts/__tests__/preview.test.ts +63 -63
  85. package/src/message-parts/__tests__/wire.test.ts +130 -124
  86. package/src/message-parts/builder.ts +23 -23
  87. package/src/message-parts/confirmation.ts +17 -14
  88. package/src/message-parts/execution.ts +7 -7
  89. package/src/message-parts/index.ts +10 -10
  90. package/src/message-parts/lifecycle.ts +25 -25
  91. package/src/message-parts/preview.ts +30 -30
  92. package/src/message-parts/types.ts +27 -27
  93. package/src/message-parts/wire.ts +24 -24
  94. package/src/mutations.ts +2 -2
  95. package/src/observability.ts +23 -11
  96. package/src/org/__tests__/org-units.test.ts +131 -96
  97. package/src/org/__tests__/tree-ordering.test.ts +57 -37
  98. package/src/org/__tests__/view-scopes.test.ts +40 -40
  99. package/src/org/domain.ts +9 -9
  100. package/src/org/index.ts +31 -21
  101. package/src/org/org-units.ts +34 -20
  102. package/src/org/schemas.ts +261 -124
  103. package/src/org/sharing.ts +17 -13
  104. package/src/org/tree-ordering.ts +3 -1
  105. package/src/org/types.ts +54 -47
  106. package/src/org/view-scopes.ts +9 -9
  107. package/src/permissions/access-levels.ts +7 -2
  108. package/src/permissions/access-source.ts +6 -6
  109. package/src/permissions/index.ts +5 -5
  110. package/src/permissions/orgchart-roles.ts +7 -7
  111. package/src/permissions/permission-introspection.ts +7 -5
  112. package/src/permissions/share-api.ts +19 -9
  113. package/src/pressure.ts +4 -4
  114. package/src/queryIntent.ts +21 -21
  115. package/src/ralph/__tests__/prd-groups.test.ts +159 -159
  116. package/src/ralph/__tests__/prd.test.ts +30 -30
  117. package/src/ralph/index.ts +3 -8
  118. package/src/ralph/prd.ts +33 -33
  119. package/src/ralph/progress.ts +1 -1
  120. package/src/rate-limit/README.md +4 -4
  121. package/src/rate-limit/index.ts +3 -3
  122. package/src/requests.ts +36 -8
  123. package/src/resource-keys.ts +207 -124
  124. package/src/resource-registry.ts +5 -5
  125. package/src/route-builder.ts +3 -3
  126. package/src/safe-mode.ts +2 -2
  127. package/src/security/index.ts +4 -4
  128. package/src/security/org-secrets.ts +13 -9
  129. package/src/security/secret.ts +3 -3
  130. package/src/sse.ts +3 -1
  131. package/src/system/README.md +3 -0
  132. package/src/system/capabilities.ts +22 -23
  133. package/src/system/diagram.ts +45 -45
  134. package/src/system/index.ts +14 -14
  135. package/src/tiers.ts +1 -1
  136. package/src/timeouts.ts +1 -1
  137. package/src/tracing.ts +30 -30
  138. package/src/types/analytics.ts +2 -2
  139. package/src/usage/README.md +3 -0
  140. package/src/usage/execution-types.ts +69 -69
  141. package/src/usage/types.ts +7 -3
@@ -1,4 +1,4 @@
1
- import { describe, it, expect } from 'vitest';
1
+ import { describe, it, expect } from "vitest";
2
2
  import {
3
3
  OrgUnitSchema,
4
4
  OrgUnitTreeNodeSchema,
@@ -14,70 +14,79 @@ import {
14
14
  OrgUnitMembershipSourceSchema,
15
15
  OrgUnitPermissionsEntrySchema,
16
16
  ListOrgUnitPermissionsResponseSchema,
17
- } from '../schemas.js';
17
+ } from "../schemas.js";
18
18
 
19
- const UUID_A = '11111111-1111-4111-8111-111111111111';
20
- const UUID_B = '22222222-2222-4222-8222-222222222222';
21
- const UUID_C = '33333333-3333-4333-8333-333333333333';
19
+ const UUID_A = "11111111-1111-4111-8111-111111111111";
20
+ const UUID_B = "22222222-2222-4222-8222-222222222222";
21
+ const UUID_C = "33333333-3333-4333-8333-333333333333";
22
22
 
23
23
  const makeUnit = (overrides: Record<string, unknown> = {}) => ({
24
24
  id: UUID_A,
25
25
  orgId: UUID_B,
26
26
  parentId: UUID_C,
27
- slug: 'engineering',
28
- name: 'Engineering',
27
+ slug: "engineering",
28
+ name: "Engineering",
29
29
  description: null,
30
- typeTag: 'department',
31
- classification: 'org_container',
32
- orderKey: 'a0',
33
- path: `${UUID_B.replace(/-/g, '_')}.${UUID_A.replace(/-/g, '_')}`,
34
- syncMode: 'manual_only',
30
+ typeTag: "department",
31
+ classification: "org_container",
32
+ orderKey: "a0",
33
+ path: `${UUID_B.replace(/-/g, "_")}.${UUID_A.replace(/-/g, "_")}`,
34
+ syncMode: "manual_only",
35
35
  visibilityDefault: null,
36
36
  metadata: {},
37
37
  archivedAt: null,
38
- createdAt: '2026-04-17T00:00:00Z',
39
- updatedAt: '2026-04-17T00:00:00Z',
38
+ createdAt: "2026-04-17T00:00:00Z",
39
+ updatedAt: "2026-04-17T00:00:00Z",
40
40
  ...overrides,
41
41
  });
42
42
 
43
- describe('OrgUnitSchema', () => {
44
- it('accepts a complete unit', () => {
43
+ describe("OrgUnitSchema", () => {
44
+ it("accepts a complete unit", () => {
45
45
  const parsed = OrgUnitSchema.parse(makeUnit());
46
46
  expect(parsed.id).toBe(UUID_A);
47
- expect(parsed.typeTag).toBe('department');
47
+ expect(parsed.typeTag).toBe("department");
48
48
  });
49
49
 
50
- it('accepts a root unit with null parentId', () => {
51
- const root = makeUnit({ parentId: null, slug: 'company', name: 'Company', typeTag: 'company' });
50
+ it("accepts a root unit with null parentId", () => {
51
+ const root = makeUnit({
52
+ parentId: null,
53
+ slug: "company",
54
+ name: "Company",
55
+ typeTag: "company",
56
+ });
52
57
  expect(() => OrgUnitSchema.parse(root)).not.toThrow();
53
58
  });
54
59
 
55
- it('accepts archived unit (archivedAt non-null)', () => {
56
- const archived = makeUnit({ archivedAt: '2026-04-17T12:00:00Z' });
60
+ it("accepts archived unit (archivedAt non-null)", () => {
61
+ const archived = makeUnit({ archivedAt: "2026-04-17T12:00:00Z" });
57
62
  expect(() => OrgUnitSchema.parse(archived)).not.toThrow();
58
63
  });
59
64
 
60
- it('rejects invalid UUID for id', () => {
61
- expect(() => OrgUnitSchema.parse(makeUnit({ id: 'not-a-uuid' }))).toThrow();
65
+ it("rejects invalid UUID for id", () => {
66
+ expect(() => OrgUnitSchema.parse(makeUnit({ id: "not-a-uuid" }))).toThrow();
62
67
  });
63
68
 
64
- it('rejects invalid classification', () => {
65
- expect(() => OrgUnitSchema.parse(makeUnit({ classification: 'bogus' }))).toThrow();
69
+ it("rejects invalid classification", () => {
70
+ expect(() =>
71
+ OrgUnitSchema.parse(makeUnit({ classification: "bogus" })),
72
+ ).toThrow();
66
73
  });
67
74
 
68
- it('rejects missing required fields', () => {
75
+ it("rejects missing required fields", () => {
69
76
  const { name: _name, ...missingName } = makeUnit();
70
77
  expect(() => OrgUnitSchema.parse(missingName)).toThrow();
71
78
  });
72
79
 
73
- it('round-trips unknown metadata fields verbatim', () => {
74
- const parsed = OrgUnitSchema.parse(makeUnit({ metadata: { legacy_team_id: UUID_C } }));
80
+ it("round-trips unknown metadata fields verbatim", () => {
81
+ const parsed = OrgUnitSchema.parse(
82
+ makeUnit({ metadata: { legacy_team_id: UUID_C } }),
83
+ );
75
84
  expect(parsed.metadata).toEqual({ legacy_team_id: UUID_C });
76
85
  });
77
86
  });
78
87
 
79
- describe('OrgUnitTreeNodeSchema', () => {
80
- it('requires depth 1..5', () => {
88
+ describe("OrgUnitTreeNodeSchema", () => {
89
+ it("requires depth 1..5", () => {
81
90
  const base = {
82
91
  ...makeUnit(),
83
92
  depth: 3,
@@ -91,175 +100,201 @@ describe('OrgUnitTreeNodeSchema', () => {
91
100
  });
92
101
  });
93
102
 
94
- describe('OrgUnitMembershipSchema', () => {
95
- it('enforces role + status + source enums', () => {
103
+ describe("OrgUnitMembershipSchema", () => {
104
+ it("enforces role + status + source enums", () => {
96
105
  const membership = {
97
106
  id: UUID_A,
98
107
  orgId: UUID_B,
99
108
  unitId: UUID_C,
100
109
  userId: UUID_A,
101
- membershipRole: 'l1_unit_owner',
102
- status: 'active',
103
- source: 'google_groups',
104
- sourceRef: 'eng@example.com',
105
- createdAt: '2026-04-17T00:00:00Z',
106
- updatedAt: '2026-04-17T00:00:00Z',
110
+ membershipRole: "l1_unit_owner",
111
+ status: "active",
112
+ source: "google_groups",
113
+ sourceRef: "eng@example.com",
114
+ createdAt: "2026-04-17T00:00:00Z",
115
+ updatedAt: "2026-04-17T00:00:00Z",
107
116
  };
108
117
  expect(() => OrgUnitMembershipSchema.parse(membership)).not.toThrow();
109
118
  expect(() =>
110
- OrgUnitMembershipSchema.parse({ ...membership, membershipRole: 'root' })
119
+ OrgUnitMembershipSchema.parse({ ...membership, membershipRole: "root" }),
120
+ ).toThrow();
121
+ expect(() =>
122
+ OrgUnitMembershipSchema.parse({ ...membership, status: "terminated" }),
111
123
  ).toThrow();
112
- expect(() => OrgUnitMembershipSchema.parse({ ...membership, status: 'terminated' })).toThrow();
113
124
  });
114
125
  });
115
126
 
116
- describe('OrgUnitRelationshipSchema', () => {
117
- it('accepts graph edge with nullable contextKey', () => {
127
+ describe("OrgUnitRelationshipSchema", () => {
128
+ it("accepts graph edge with nullable contextKey", () => {
118
129
  const edge = {
119
130
  id: UUID_A,
120
131
  orgId: UUID_B,
121
132
  fromUnit: UUID_C,
122
133
  toUnit: UUID_A,
123
- type: 'collaborates_with' as const,
124
- role: 'participant' as const,
134
+ type: "collaborates_with" as const,
135
+ role: "participant" as const,
125
136
  contextKey: null,
126
137
  metadata: {},
127
- createdAt: '2026-04-17T00:00:00Z',
138
+ createdAt: "2026-04-17T00:00:00Z",
128
139
  };
129
140
  expect(() => OrgUnitRelationshipSchema.parse(edge)).not.toThrow();
130
141
  expect(() =>
131
- OrgUnitRelationshipSchema.parse({ ...edge, contextKey: 'infra-context' })
142
+ OrgUnitRelationshipSchema.parse({ ...edge, contextKey: "infra-context" }),
132
143
  ).not.toThrow();
133
144
  });
134
145
  });
135
146
 
136
- describe('OrgLevelConfigSchema', () => {
137
- it('enforces depth 1..5, non-empty-when-present label/labelPlural, and icon enum', () => {
147
+ describe("OrgLevelConfigSchema", () => {
148
+ it("enforces depth 1..5, non-empty-when-present label/labelPlural, and icon enum", () => {
138
149
  const entry = {
139
150
  orgId: UUID_B,
140
151
  depth: 2,
141
- label: 'Department',
142
- labelPlural: 'Departments',
143
- icon: 'users-four' as const,
144
- createdAt: '2026-04-17T00:00:00Z',
145
- updatedAt: '2026-04-17T00:00:00Z',
152
+ label: "Department",
153
+ labelPlural: "Departments",
154
+ icon: "users-four" as const,
155
+ createdAt: "2026-04-17T00:00:00Z",
156
+ updatedAt: "2026-04-17T00:00:00Z",
146
157
  };
147
158
  expect(() => OrgLevelConfigSchema.parse(entry)).not.toThrow();
148
159
  expect(() =>
149
- OrgLevelConfigSchema.parse({ ...entry, label: null, labelPlural: null, icon: null })
160
+ OrgLevelConfigSchema.parse({
161
+ ...entry,
162
+ label: null,
163
+ labelPlural: null,
164
+ icon: null,
165
+ }),
150
166
  ).not.toThrow();
151
167
  expect(() => OrgLevelConfigSchema.parse({ ...entry, depth: 6 })).toThrow();
152
- expect(() => OrgLevelConfigSchema.parse({ ...entry, label: '' })).toThrow();
153
- expect(() => OrgLevelConfigSchema.parse({ ...entry, labelPlural: '' })).toThrow();
168
+ expect(() => OrgLevelConfigSchema.parse({ ...entry, label: "" })).toThrow();
169
+ expect(() =>
170
+ OrgLevelConfigSchema.parse({ ...entry, labelPlural: "" }),
171
+ ).toThrow();
154
172
  expect(() =>
155
- OrgLevelConfigSchema.parse({ ...entry, icon: 'not-an-icon' })
173
+ OrgLevelConfigSchema.parse({ ...entry, icon: "not-an-icon" }),
156
174
  ).toThrow();
157
175
  });
158
176
  });
159
177
 
160
- describe('Response schemas', () => {
161
- it('OrgUnitTreeResponseSchema accepts empty tree', () => {
178
+ describe("Response schemas", () => {
179
+ it("OrgUnitTreeResponseSchema accepts empty tree", () => {
162
180
  expect(() =>
163
181
  OrgUnitTreeResponseSchema.parse({
164
182
  nodes: [],
165
183
  levelConfig: [],
166
- })
184
+ }),
167
185
  ).not.toThrow();
168
186
  });
169
187
 
170
- it('OrgUnitChildrenResponseSchema wraps parentId + children array', () => {
188
+ it("OrgUnitChildrenResponseSchema wraps parentId + children array", () => {
171
189
  expect(() =>
172
- OrgUnitChildrenResponseSchema.parse({ parentId: UUID_A, children: [] })
190
+ OrgUnitChildrenResponseSchema.parse({ parentId: UUID_A, children: [] }),
173
191
  ).not.toThrow();
174
192
  });
175
193
 
176
- it('OrgUnitRelationshipsResponseSchema has incoming + outgoing arrays', () => {
194
+ it("OrgUnitRelationshipsResponseSchema has incoming + outgoing arrays", () => {
177
195
  expect(() =>
178
196
  OrgUnitRelationshipsResponseSchema.parse({
179
197
  unitId: UUID_A,
180
198
  incoming: [],
181
199
  outgoing: [],
182
- })
200
+ }),
183
201
  ).not.toThrow();
184
202
  });
185
203
  });
186
204
 
187
- describe('Enum exhaustiveness', () => {
188
- it('OrgUnitErrorCodeSchema enumerates all 10 known codes', () => {
205
+ describe("Enum exhaustiveness", () => {
206
+ it("OrgUnitErrorCodeSchema enumerates all 10 known codes", () => {
189
207
  const codes = [
190
- 'CYCLE_BLOCKED',
191
- 'DEPTH_EXCEEDED',
192
- 'TARGET_ARCHIVED',
193
- 'ROOT_HAS_NO_PARENT',
194
- 'PARENT_ARCHIVED',
195
- 'SLUG_TAKEN',
196
- 'CROSS_ORG_REPARENT',
197
- 'ORDERKEY_CONFLICT',
198
- 'SIBLING_NOT_FOUND',
199
- 'SIBLINGS_DIFFERENT_PARENT',
208
+ "CYCLE_BLOCKED",
209
+ "DEPTH_EXCEEDED",
210
+ "TARGET_ARCHIVED",
211
+ "ROOT_HAS_NO_PARENT",
212
+ "PARENT_ARCHIVED",
213
+ "SLUG_TAKEN",
214
+ "CROSS_ORG_REPARENT",
215
+ "ORDERKEY_CONFLICT",
216
+ "SIBLING_NOT_FOUND",
217
+ "SIBLINGS_DIFFERENT_PARENT",
200
218
  ];
201
219
  for (const code of codes) {
202
220
  expect(() => OrgUnitErrorCodeSchema.parse(code)).not.toThrow();
203
221
  }
204
- expect(() => OrgUnitErrorCodeSchema.parse('OTHER')).toThrow();
222
+ expect(() => OrgUnitErrorCodeSchema.parse("OTHER")).toThrow();
205
223
  });
206
224
 
207
- it('classification enum matches backend _enums.ts', () => {
208
- for (const v of ['execution_unit', 'org_container', 'custom']) {
225
+ it("classification enum matches backend _enums.ts", () => {
226
+ for (const v of ["execution_unit", "org_container", "custom"]) {
209
227
  expect(() => OrgUnitClassificationSchema.parse(v)).not.toThrow();
210
228
  }
211
229
  });
212
230
 
213
- it('relationship type enum matches backend', () => {
214
- for (const v of ['collaborates_with', 'reports_to', 'depends_on', 'custom']) {
231
+ it("relationship type enum matches backend", () => {
232
+ for (const v of [
233
+ "collaborates_with",
234
+ "reports_to",
235
+ "depends_on",
236
+ "custom",
237
+ ]) {
215
238
  expect(() => OrgUnitRelationshipTypeSchema.parse(v)).not.toThrow();
216
239
  }
217
240
  });
218
241
 
219
- it('membership source enum matches backend', () => {
220
- for (const v of ['manual', 'google_groups', 'scim', 'hris']) {
242
+ it("membership source enum matches backend", () => {
243
+ for (const v of ["manual", "google_groups", "scim", "hris"]) {
221
244
  expect(() => OrgUnitMembershipSourceSchema.parse(v)).not.toThrow();
222
245
  }
223
246
  });
224
247
  });
225
248
 
226
- describe('OrgUnitPermissionsEntrySchema', () => {
249
+ describe("OrgUnitPermissionsEntrySchema", () => {
227
250
  const entry = {
228
251
  userId: UUID_A,
229
- membershipRole: 'l1_unit_owner' as const,
252
+ membershipRole: "l1_unit_owner" as const,
230
253
  inheritedFromUnitId: UUID_C,
231
- inheritedFromUnitName: 'Engineering',
254
+ inheritedFromUnitName: "Engineering",
232
255
  };
233
256
 
234
- it('accepts a well-formed entry', () => {
257
+ it("accepts a well-formed entry", () => {
235
258
  expect(() => OrgUnitPermissionsEntrySchema.parse(entry)).not.toThrow();
236
259
  });
237
260
 
238
- it('rejects invalid membershipRole', () => {
261
+ it("rejects invalid membershipRole", () => {
239
262
  expect(() =>
240
- OrgUnitPermissionsEntrySchema.parse({ ...entry, membershipRole: 'member' })
263
+ OrgUnitPermissionsEntrySchema.parse({
264
+ ...entry,
265
+ membershipRole: "member",
266
+ }),
241
267
  ).not.toThrow();
242
268
  expect(() =>
243
- OrgUnitPermissionsEntrySchema.parse({ ...entry, membershipRole: 'bogus' })
269
+ OrgUnitPermissionsEntrySchema.parse({
270
+ ...entry,
271
+ membershipRole: "bogus",
272
+ }),
244
273
  ).toThrow();
245
274
  });
246
275
 
247
- it('requires UUIDs for user and inherited unit', () => {
276
+ it("requires UUIDs for user and inherited unit", () => {
248
277
  expect(() =>
249
- OrgUnitPermissionsEntrySchema.parse({ ...entry, userId: 'nope' })
278
+ OrgUnitPermissionsEntrySchema.parse({ ...entry, userId: "nope" }),
250
279
  ).toThrow();
251
280
  expect(() =>
252
- OrgUnitPermissionsEntrySchema.parse({ ...entry, inheritedFromUnitId: 'nope' })
281
+ OrgUnitPermissionsEntrySchema.parse({
282
+ ...entry,
283
+ inheritedFromUnitId: "nope",
284
+ }),
253
285
  ).toThrow();
254
286
  });
255
287
 
256
- it('rejects empty inheritedFromUnitName', () => {
288
+ it("rejects empty inheritedFromUnitName", () => {
257
289
  expect(() =>
258
- OrgUnitPermissionsEntrySchema.parse({ ...entry, inheritedFromUnitName: '' })
290
+ OrgUnitPermissionsEntrySchema.parse({
291
+ ...entry,
292
+ inheritedFromUnitName: "",
293
+ }),
259
294
  ).toThrow();
260
295
  });
261
296
 
262
- it('list response wraps entries with unitId', () => {
297
+ it("list response wraps entries with unitId", () => {
263
298
  const parsed = ListOrgUnitPermissionsResponseSchema.parse({
264
299
  unitId: UUID_B,
265
300
  entries: [entry],
@@ -1,5 +1,5 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { orderTreeNodes, type TreeOrderableNode } from '../tree-ordering.js';
1
+ import { describe, it, expect } from "vitest";
2
+ import { orderTreeNodes, type TreeOrderableNode } from "../tree-ordering.js";
3
3
 
4
4
  interface TestNode extends TreeOrderableNode {
5
5
  label?: string;
@@ -19,31 +19,42 @@ function shuffle<T>(input: readonly T[], seed: number): T[] {
19
19
  }
20
20
 
21
21
  const FIXTURE: TestNode[] = [
22
- { id: 'root-1', parentId: null, orderKey: '10' },
23
- { id: 'root-2', parentId: null, orderKey: '20' },
24
- { id: 'a', parentId: 'root-1', orderKey: '20' },
25
- { id: 'b', parentId: 'root-1', orderKey: '10' },
26
- { id: 'c', parentId: 'root-1', orderKey: '30' },
27
- { id: 'aa', parentId: 'a', orderKey: '10' },
28
- { id: 'ab', parentId: 'a', orderKey: '20' },
29
- { id: 'ba', parentId: 'b', orderKey: '10' },
30
- { id: 'x', parentId: 'root-2', orderKey: '10' },
31
- { id: 'y', parentId: 'root-2', orderKey: '10' },
22
+ { id: "root-1", parentId: null, orderKey: "10" },
23
+ { id: "root-2", parentId: null, orderKey: "20" },
24
+ { id: "a", parentId: "root-1", orderKey: "20" },
25
+ { id: "b", parentId: "root-1", orderKey: "10" },
26
+ { id: "c", parentId: "root-1", orderKey: "30" },
27
+ { id: "aa", parentId: "a", orderKey: "10" },
28
+ { id: "ab", parentId: "a", orderKey: "20" },
29
+ { id: "ba", parentId: "b", orderKey: "10" },
30
+ { id: "x", parentId: "root-2", orderKey: "10" },
31
+ { id: "y", parentId: "root-2", orderKey: "10" },
32
32
  ];
33
33
 
34
- describe('orderTreeNodes', () => {
35
- it('emits a parent-before-children flat order sorted per parent', () => {
34
+ describe("orderTreeNodes", () => {
35
+ it("emits a parent-before-children flat order sorted per parent", () => {
36
36
  const ordered = orderTreeNodes(FIXTURE).map((n) => n.id);
37
- expect(ordered).toEqual(['root-1', 'b', 'ba', 'a', 'aa', 'ab', 'c', 'root-2', 'x', 'y']);
37
+ expect(ordered).toEqual([
38
+ "root-1",
39
+ "b",
40
+ "ba",
41
+ "a",
42
+ "aa",
43
+ "ab",
44
+ "c",
45
+ "root-2",
46
+ "x",
47
+ "y",
48
+ ]);
38
49
  });
39
50
 
40
- it('is idempotent — running twice produces the same result', () => {
51
+ it("is idempotent — running twice produces the same result", () => {
41
52
  const once = orderTreeNodes(FIXTURE);
42
53
  const twice = orderTreeNodes(once);
43
54
  expect(twice).toEqual(once);
44
55
  });
45
56
 
46
- it('is stable under input shuffle — order depends only on (orderKey, id), not insertion order', () => {
57
+ it("is stable under input shuffle — order depends only on (orderKey, id), not insertion order", () => {
47
58
  const baseline = orderTreeNodes(FIXTURE).map((n) => n.id);
48
59
  for (const seed of [1, 2, 3, 42, 1337, 999999]) {
49
60
  const shuffled = shuffle(FIXTURE, seed);
@@ -52,51 +63,60 @@ describe('orderTreeNodes', () => {
52
63
  }
53
64
  });
54
65
 
55
- it('uses id as deterministic tiebreaker when orderKey is identical', () => {
66
+ it("uses id as deterministic tiebreaker when orderKey is identical", () => {
56
67
  const ties: TestNode[] = [
57
- { id: 'zeta', parentId: null, orderKey: '50' },
58
- { id: 'alpha', parentId: null, orderKey: '50' },
59
- { id: 'mike', parentId: null, orderKey: '50' },
68
+ { id: "zeta", parentId: null, orderKey: "50" },
69
+ { id: "alpha", parentId: null, orderKey: "50" },
70
+ { id: "mike", parentId: null, orderKey: "50" },
60
71
  ];
61
72
  const order = orderTreeNodes(ties).map((n) => n.id);
62
- expect(order).toEqual(['alpha', 'mike', 'zeta']);
73
+ expect(order).toEqual(["alpha", "mike", "zeta"]);
63
74
 
64
75
  const shuffled = orderTreeNodes(shuffle(ties, 7)).map((n) => n.id);
65
- expect(shuffled).toEqual(['alpha', 'mike', 'zeta']);
76
+ expect(shuffled).toEqual(["alpha", "mike", "zeta"]);
66
77
  });
67
78
 
68
- it('places nodes with null parentId in the root group, before any descendants', () => {
79
+ it("places nodes with null parentId in the root group, before any descendants", () => {
69
80
  const nodes: TestNode[] = [
70
- { id: 'child-of-r2', parentId: 'r2', orderKey: '10' },
71
- { id: 'r1', parentId: null, orderKey: '20' },
72
- { id: 'r2', parentId: null, orderKey: '10' },
73
- { id: 'child-of-r1', parentId: 'r1', orderKey: '10' },
81
+ { id: "child-of-r2", parentId: "r2", orderKey: "10" },
82
+ { id: "r1", parentId: null, orderKey: "20" },
83
+ { id: "r2", parentId: null, orderKey: "10" },
84
+ { id: "child-of-r1", parentId: "r1", orderKey: "10" },
74
85
  ];
75
86
  const ordered = orderTreeNodes(nodes).map((n) => n.id);
76
- expect(ordered).toEqual(['r2', 'child-of-r2', 'r1', 'child-of-r1']);
87
+ expect(ordered).toEqual(["r2", "child-of-r2", "r1", "child-of-r1"]);
77
88
 
78
- const roots = orderTreeNodes(nodes).filter((n) => n.parentId === null).map((n) => n.id);
79
- expect(roots).toEqual(['r2', 'r1']);
89
+ const roots = orderTreeNodes(nodes)
90
+ .filter((n) => n.parentId === null)
91
+ .map((n) => n.id);
92
+ expect(roots).toEqual(["r2", "r1"]);
80
93
  });
81
94
 
82
- it('preserves all input fields on the output nodes', () => {
95
+ it("preserves all input fields on the output nodes", () => {
83
96
  const rich: TestNode[] = [
84
- { id: 'only', parentId: null, orderKey: '1', label: 'hello' },
97
+ { id: "only", parentId: null, orderKey: "1", label: "hello" },
85
98
  ];
86
99
  const [out] = orderTreeNodes(rich);
87
- expect(out).toEqual({ id: 'only', parentId: null, orderKey: '1', label: 'hello' });
100
+ expect(out).toEqual({
101
+ id: "only",
102
+ parentId: null,
103
+ orderKey: "1",
104
+ label: "hello",
105
+ });
88
106
  });
89
107
 
90
- it('returns a new array without mutating the input list', () => {
108
+ it("returns a new array without mutating the input list", () => {
91
109
  const input = FIXTURE.slice();
92
110
  const snapshotIds = input.map((n) => n.id);
93
111
  orderTreeNodes(input);
94
112
  expect(input.map((n) => n.id)).toEqual(snapshotIds);
95
113
  });
96
114
 
97
- it('is strictly synchronous (no Promise return)', () => {
115
+ it("is strictly synchronous (no Promise return)", () => {
98
116
  const result = orderTreeNodes(FIXTURE);
99
117
  expect(Array.isArray(result)).toBe(true);
100
- expect(typeof (result as unknown as { then?: unknown }).then).toBe('undefined');
118
+ expect(typeof (result as unknown as { then?: unknown }).then).toBe(
119
+ "undefined",
120
+ );
101
121
  });
102
122
  });
@@ -1,51 +1,51 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { VIEW_SCOPE_MAP, getViewScope } from '../view-scopes.js'
1
+ import { describe, it, expect } from "vitest";
2
+ import { VIEW_SCOPE_MAP, getViewScope } from "../view-scopes.js";
3
3
 
4
- describe('VIEW_SCOPE_MAP golden snapshot', () => {
5
- it('exact values are frozen', () => {
4
+ describe("VIEW_SCOPE_MAP golden snapshot", () => {
5
+ it("exact values are frozen", () => {
6
6
  expect(VIEW_SCOPE_MAP).toStrictEqual({
7
- workspace: 'org.view_workspace',
8
- timeline: 'org.view_timeline',
9
- teamwork: 'org.view_teamwork',
10
- 'teamwork-member': 'org.view_teamwork',
11
- 'company-md': 'org.view_company_md',
12
- teams: 'org.view_teams',
13
- 'internal-admin': 'internal.view_admin',
7
+ workspace: "org.view_workspace",
8
+ timeline: "org.view_timeline",
9
+ teamwork: "org.view_teamwork",
10
+ "teamwork-member": "org.view_teamwork",
11
+ "company-md": "org.view_company_md",
12
+ teams: "org.view_teams",
13
+ "internal-admin": "internal.view_admin",
14
14
  chat: null,
15
15
  settings: null,
16
16
  chats: null,
17
- 'my-work': null,
18
- 'user-md': null,
17
+ "my-work": null,
18
+ "user-md": null,
19
19
  upgrade: null,
20
- })
21
- })
22
- })
20
+ });
21
+ });
22
+ });
23
23
 
24
- describe('getViewScope', () => {
25
- it('returns the required scope for protected views', () => {
26
- expect(getViewScope('workspace')).toBe('org.view_workspace')
27
- expect(getViewScope('timeline')).toBe('org.view_timeline')
28
- expect(getViewScope('teamwork')).toBe('org.view_teamwork')
29
- expect(getViewScope('company-md')).toBe('org.view_company_md')
30
- expect(getViewScope('internal-admin')).toBe('internal.view_admin')
31
- })
24
+ describe("getViewScope", () => {
25
+ it("returns the required scope for protected views", () => {
26
+ expect(getViewScope("workspace")).toBe("org.view_workspace");
27
+ expect(getViewScope("timeline")).toBe("org.view_timeline");
28
+ expect(getViewScope("teamwork")).toBe("org.view_teamwork");
29
+ expect(getViewScope("company-md")).toBe("org.view_company_md");
30
+ expect(getViewScope("internal-admin")).toBe("internal.view_admin");
31
+ });
32
32
 
33
- it('returns null for public views', () => {
34
- expect(getViewScope('chat')).toBeNull()
35
- expect(getViewScope('settings')).toBeNull()
36
- expect(getViewScope('chats')).toBeNull()
37
- expect(getViewScope('upgrade')).toBeNull()
38
- })
33
+ it("returns null for public views", () => {
34
+ expect(getViewScope("chat")).toBeNull();
35
+ expect(getViewScope("settings")).toBeNull();
36
+ expect(getViewScope("chats")).toBeNull();
37
+ expect(getViewScope("upgrade")).toBeNull();
38
+ });
39
39
 
40
- it('returns null for unknown view string', () => {
41
- expect(getViewScope('nonexistent-view')).toBeNull()
42
- })
40
+ it("returns null for unknown view string", () => {
41
+ expect(getViewScope("nonexistent-view")).toBeNull();
42
+ });
43
43
 
44
- it('returns null for empty string', () => {
45
- expect(getViewScope('')).toBeNull()
46
- })
44
+ it("returns null for empty string", () => {
45
+ expect(getViewScope("")).toBeNull();
46
+ });
47
47
 
48
- it('returns null for view name that is a substring of a real view', () => {
49
- expect(getViewScope('work')).toBeNull()
50
- })
51
- })
48
+ it("returns null for view name that is a substring of a real view", () => {
49
+ expect(getViewScope("work")).toBeNull();
50
+ });
51
+ });