@company-semantics/contracts 4.0.0 → 5.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "4.0.0",
3
+ "version": "5.1.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,64 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ toQueryKey,
4
+ fromQueryKey,
5
+ matchesResourceKey,
6
+ type ResourceKey,
7
+ } from '../resource-keys.js';
8
+
9
+ const ORG_ID = '11111111-1111-4111-8111-111111111111';
10
+
11
+ describe('resource-keys: orgUnitOwners (org-scoped)', () => {
12
+ const key: ResourceKey = { type: 'orgUnitOwners', orgId: ORG_ID };
13
+
14
+ it('toQueryKey produces [type, orgId] with no unitId', () => {
15
+ const qk = toQueryKey(key);
16
+ expect(qk).toEqual(['orgUnitOwners', ORG_ID]);
17
+ // org-scoped: exactly two segments, no per-unit identity
18
+ expect(qk).toHaveLength(2);
19
+ });
20
+
21
+ it('roundtrips through fromQueryKey', () => {
22
+ const parsed = fromQueryKey(toQueryKey(key));
23
+ expect(parsed).toEqual(key);
24
+ expect('unitId' in parsed).toBe(false);
25
+ });
26
+
27
+ it('matchesResourceKey is org-scoped: rejects a different orgId', () => {
28
+ const other = '22222222-2222-4222-8222-222222222222';
29
+ expect(matchesResourceKey(toQueryKey(key), key)).toBe(true);
30
+ expect(matchesResourceKey(['orgUnitOwners', other], key)).toBe(false);
31
+ });
32
+ });
33
+
34
+ describe('resource-keys: system-scoped internalAdmin* types', () => {
35
+ const systemTypes = [
36
+ 'internalAdminAiProviders',
37
+ 'internalAdminPrompts',
38
+ 'internalAdminAiRuntimeDefaults',
39
+ ] as const;
40
+
41
+ for (const type of systemTypes) {
42
+ const key = { type, scope: 'system' } as ResourceKey;
43
+
44
+ it(`${type}: toQueryKey produces [type, 'system']`, () => {
45
+ expect(toQueryKey(key)).toEqual([type, 'system']);
46
+ });
47
+
48
+ it(`${type}: roundtrips through fromQueryKey with scope: 'system'`, () => {
49
+ const parsed = fromQueryKey(toQueryKey(key));
50
+ expect(parsed).toEqual(key);
51
+ expect('orgId' in parsed).toBe(false);
52
+ expect('userId' in parsed).toBe(false);
53
+ });
54
+
55
+ it(`${type}: matchesResourceKey accepts a matching system key`, () => {
56
+ expect(matchesResourceKey(toQueryKey(key), key)).toBe(true);
57
+ });
58
+ }
59
+
60
+ it('matchesResourceKey rejects a mismatched system type', () => {
61
+ const target: ResourceKey = { type: 'internalAdminPrompts', scope: 'system' };
62
+ expect(matchesResourceKey(['internalAdminAiProviders', 'system'], target)).toBe(false);
63
+ });
64
+ });
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = '30c4d8eb9dce' as const;
3
- export const SPEC_HASH_FULL = '30c4d8eb9dcefed99d44a531c27a9e5a25eb4842c578e8933f64678a13816d07' as const;
2
+ export const SPEC_HASH = 'f8ecf637857f' as const;
3
+ export const SPEC_HASH_FULL = 'f8ecf637857fded90a144d5a76bfa2919d3494bb0b5b78d4131e906ab4a6edf5' as const;
@@ -1859,7 +1859,7 @@ export interface paths {
1859
1859
  };
1860
1860
  get?: never;
1861
1861
  /** Update a manual membership role */
1862
- put: operations["updateOrgUnitMembershipRole"];
1862
+ put: operations["updateUnitMembershipRole"];
1863
1863
  post?: never;
1864
1864
  delete?: never;
1865
1865
  options?: never;
@@ -3059,8 +3059,7 @@ export interface components {
3059
3059
  name: string;
3060
3060
  email: string;
3061
3061
  jobTitle: string | null;
3062
- /** @enum {string} */
3063
- role: "owner" | "admin" | "member" | "auditor";
3062
+ role: ("ceo" | "leader" | "delegate" | "admin") | null;
3064
3063
  roleNames: string[];
3065
3064
  joinedAt: string;
3066
3065
  lastActiveAt: string | null;
@@ -3248,8 +3247,7 @@ export interface components {
3248
3247
  name: string;
3249
3248
  email: string;
3250
3249
  jobTitle: string | null;
3251
- /** @enum {string} */
3252
- role: "owner" | "admin" | "member" | "auditor";
3250
+ role: ("ceo" | "leader" | "delegate" | "admin") | null;
3253
3251
  roleNames: string[];
3254
3252
  joinedAt: string;
3255
3253
  lastActiveAt: string | null;
@@ -3305,8 +3303,7 @@ export interface components {
3305
3303
  orgId: string;
3306
3304
  orgName: string;
3307
3305
  orgSlug: string;
3308
- /** @enum {string} */
3309
- role: "owner" | "admin" | "member" | "auditor";
3306
+ role: ("ceo" | "leader" | "delegate" | "admin") | null;
3310
3307
  joinedAt: string;
3311
3308
  isActive: boolean;
3312
3309
  /** @enum {string} */
@@ -3341,7 +3338,7 @@ export interface components {
3341
3338
  orgId: string;
3342
3339
  email: string;
3343
3340
  /** @enum {string} */
3344
- role: "owner" | "admin" | "member" | "auditor";
3341
+ role: "admin" | "member";
3345
3342
  invitedBy: {
3346
3343
  id: string;
3347
3344
  name: string;
@@ -3364,7 +3361,7 @@ export interface components {
3364
3361
  orgId: string;
3365
3362
  email: string;
3366
3363
  /** @enum {string} */
3367
- role: "owner" | "admin" | "member" | "auditor";
3364
+ role: "admin" | "member";
3368
3365
  invitedBy: {
3369
3366
  id: string;
3370
3367
  name: string;
@@ -7641,7 +7638,7 @@ export interface operations {
7641
7638
  };
7642
7639
  };
7643
7640
  };
7644
- updateOrgUnitMembershipRole: {
7641
+ updateUnitMembershipRole: {
7645
7642
  parameters: {
7646
7643
  query?: never;
7647
7644
  header?: never;
@@ -36,8 +36,16 @@ 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
+ // Org-unit owners list (ADR-CONTRACTS-052) — owners are an org-wide
40
+ // projection, not a per-unit collection, so unitId is intentionally excluded.
41
+ | { type: 'orgUnitOwners'; orgId: string }
39
42
  // People reporting (ADR-BE-166) — drives the settings Org chart drill-down
40
43
  | { type: 'peopleOrgChart'; orgId: string }
44
+ // System-scoped (ADR-CONTRACTS-052) — tenant-less super-admin resources.
45
+ // No orgId/userId: these live above any single org. scope is the literal 'system'.
46
+ | { type: 'internalAdminAiProviders'; scope: 'system' }
47
+ | { type: 'internalAdminPrompts'; scope: 'system' }
48
+ | { type: 'internalAdminAiRuntimeDefaults'; scope: 'system' }
41
49
  // User-scoped
42
50
  | { type: 'dismissedBanners'; userId: string }
43
51
  | { type: 'userOrgs'; userId: string }
@@ -64,11 +72,20 @@ const ORG_SCOPED_TYPES = [
64
72
  'members', 'departments', 'chats', 'teams', 'integrations', 'invites',
65
73
  'auditEvents', 'timeline', 'workspace', 'workspaceDomains', 'authSettings',
66
74
  'billing', 'aiUsage', 'deletionEligibility', 'transferOwnership', 'companyMdDocs',
67
- 'orgTree', 'orgLevelConfig', 'peopleOrgChart',
75
+ 'orgTree', 'orgLevelConfig', 'peopleOrgChart', 'orgUnitOwners',
68
76
  ] as const;
69
77
 
70
78
  const USER_SCOPED_TYPES = ['dismissedBanners', 'userOrgs', 'sessions', 'viewer'] as const;
71
79
 
80
+ /**
81
+ * System-scoped types (ADR-CONTRACTS-052) — tenant-less super-admin resources.
82
+ * Their query key is [type, 'system']; they carry no orgId or userId.
83
+ * Intentionally NOT exported — internal to the parser like the other scope arrays.
84
+ */
85
+ const SYSTEM_SCOPED_TYPES = [
86
+ 'internalAdminAiProviders', 'internalAdminPrompts', 'internalAdminAiRuntimeDefaults',
87
+ ] as const;
88
+
72
89
  /**
73
90
  * Canonical ResourceKey → query key conversion.
74
91
  * This is the ONLY function that constructs query keys — no ad-hoc key
@@ -128,8 +145,15 @@ export function toQueryKey(key: ResourceKey): readonly string[] {
128
145
  case 'orgTree':
129
146
  case 'orgLevelConfig':
130
147
  case 'peopleOrgChart':
148
+ case 'orgUnitOwners':
131
149
  return [key.type, key.orgId] as const;
132
150
 
151
+ // System-scoped (ADR-CONTRACTS-052) — tenant-less super-admin resources
152
+ case 'internalAdminAiProviders':
153
+ case 'internalAdminPrompts':
154
+ case 'internalAdminAiRuntimeDefaults':
155
+ return [key.type, key.scope] as const;
156
+
133
157
  default: {
134
158
  const _exhaustive: never = key;
135
159
  throw new Error(`Unknown resource type: ${JSON.stringify(_exhaustive)}`);
@@ -170,6 +194,11 @@ export function fromQueryKey(queryKey: readonly string[]): ResourceKey {
170
194
  return { type, orgId: rest[0], [identityFields[type]]: rest[1] } as ResourceKey;
171
195
  }
172
196
 
197
+ // System-scoped types (ADR-CONTRACTS-052) — query key is [type, 'system']
198
+ if ((SYSTEM_SCOPED_TYPES as readonly string[]).includes(type)) {
199
+ return { type, scope: 'system' } as ResourceKey;
200
+ }
201
+
173
202
  // User-scoped types
174
203
  if ((USER_SCOPED_TYPES as readonly string[]).includes(type)) {
175
204
  return { type, userId: rest[0] } as ResourceKey;
@@ -238,6 +267,7 @@ export function matchesResourceKey(queryKey: readonly unknown[], targetKey: Reso
238
267
  if ('chatId' in parsed && 'chatId' in targetKey && parsed.chatId !== targetKey.chatId) return false;
239
268
  if ('slug' in parsed && 'slug' in targetKey && parsed.slug !== targetKey.slug) return false;
240
269
  if ('unitId' in parsed && 'unitId' in targetKey && parsed.unitId !== targetKey.unitId) return false;
270
+ if ('scope' in parsed && 'scope' in targetKey && parsed.scope !== targetKey.scope) return false;
241
271
 
242
272
  return true;
243
273
  }