@company-semantics/contracts 1.0.0 → 1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = 'af4199988265' as const;
3
- export const SPEC_HASH_FULL = 'af4199988265ad0b2a80b107680fa1bbd60043ebcc0ccad9b9245f9a0358f661' as const;
2
+ export const SPEC_HASH = '0e90960db25d' as const;
3
+ export const SPEC_HASH_FULL = '0e90960db25d88b3945b8fa9533362f10776b9d1d59ead1291806198b1f3c703' as const;
@@ -1605,6 +1605,58 @@ export interface paths {
1605
1605
  patch?: never;
1606
1606
  trace?: never;
1607
1607
  };
1608
+ "/api/org-units/{unitId}/memberships": {
1609
+ parameters: {
1610
+ query?: never;
1611
+ header?: never;
1612
+ path?: never;
1613
+ cookie?: never;
1614
+ };
1615
+ /** List memberships of an org unit */
1616
+ get: operations["listOrgUnitMemberships"];
1617
+ put?: never;
1618
+ /** Add a manual membership to an org unit */
1619
+ post: operations["createOrgUnitMembership"];
1620
+ delete?: never;
1621
+ options?: never;
1622
+ head?: never;
1623
+ patch?: never;
1624
+ trace?: never;
1625
+ };
1626
+ "/api/org-units/{unitId}/memberships/{userId}/role": {
1627
+ parameters: {
1628
+ query?: never;
1629
+ header?: never;
1630
+ path?: never;
1631
+ cookie?: never;
1632
+ };
1633
+ get?: never;
1634
+ /** Update a manual membership role */
1635
+ put: operations["updateOrgUnitMembershipRole"];
1636
+ post?: never;
1637
+ delete?: never;
1638
+ options?: never;
1639
+ head?: never;
1640
+ patch?: never;
1641
+ trace?: never;
1642
+ };
1643
+ "/api/org-units/{unitId}/memberships/{userId}": {
1644
+ parameters: {
1645
+ query?: never;
1646
+ header?: never;
1647
+ path?: never;
1648
+ cookie?: never;
1649
+ };
1650
+ get?: never;
1651
+ put?: never;
1652
+ post?: never;
1653
+ /** Remove a manual membership */
1654
+ delete: operations["deleteOrgUnitMembership"];
1655
+ options?: never;
1656
+ head?: never;
1657
+ patch?: never;
1658
+ trace?: never;
1659
+ };
1608
1660
  "/api/drive/files": {
1609
1661
  parameters: {
1610
1662
  query?: never;
@@ -3089,6 +3141,50 @@ export interface components {
3089
3141
  updatedAt: string;
3090
3142
  }[];
3091
3143
  };
3144
+ OrgUnitMembershipListResponse: {
3145
+ /** Format: uuid */
3146
+ unitId: string;
3147
+ memberships: {
3148
+ /** Format: uuid */
3149
+ id: string;
3150
+ /** Format: uuid */
3151
+ orgId: string;
3152
+ /** Format: uuid */
3153
+ unitId: string;
3154
+ /** Format: uuid */
3155
+ userId: string;
3156
+ /** @enum {string} */
3157
+ membershipRole: "member" | "manager" | "owner";
3158
+ /** @enum {string} */
3159
+ status: "active" | "pending" | "removed";
3160
+ /** @enum {string} */
3161
+ source: "manual" | "google_groups" | "scim" | "hris";
3162
+ sourceRef: string | null;
3163
+ createdAt: string;
3164
+ updatedAt: string;
3165
+ }[];
3166
+ };
3167
+ OrgUnitMembershipResponse: {
3168
+ membership: {
3169
+ /** Format: uuid */
3170
+ id: string;
3171
+ /** Format: uuid */
3172
+ orgId: string;
3173
+ /** Format: uuid */
3174
+ unitId: string;
3175
+ /** Format: uuid */
3176
+ userId: string;
3177
+ /** @enum {string} */
3178
+ membershipRole: "member" | "manager" | "owner";
3179
+ /** @enum {string} */
3180
+ status: "active" | "pending" | "removed";
3181
+ /** @enum {string} */
3182
+ source: "manual" | "google_groups" | "scim" | "hris";
3183
+ sourceRef: string | null;
3184
+ createdAt: string;
3185
+ updatedAt: string;
3186
+ };
3187
+ };
3092
3188
  DriveFileListResponse: {
3093
3189
  files: {
3094
3190
  id: string;
@@ -5723,6 +5819,110 @@ export interface operations {
5723
5819
  };
5724
5820
  };
5725
5821
  };
5822
+ listOrgUnitMemberships: {
5823
+ parameters: {
5824
+ query?: never;
5825
+ header?: never;
5826
+ path: {
5827
+ unitId: string;
5828
+ };
5829
+ cookie?: never;
5830
+ };
5831
+ requestBody?: never;
5832
+ responses: {
5833
+ /** @description Memberships of the unit */
5834
+ 200: {
5835
+ headers: {
5836
+ [name: string]: unknown;
5837
+ };
5838
+ content: {
5839
+ "application/json": components["schemas"]["OrgUnitMembershipListResponse"];
5840
+ };
5841
+ };
5842
+ };
5843
+ };
5844
+ createOrgUnitMembership: {
5845
+ parameters: {
5846
+ query?: never;
5847
+ header?: never;
5848
+ path: {
5849
+ unitId: string;
5850
+ };
5851
+ cookie?: never;
5852
+ };
5853
+ requestBody: {
5854
+ content: {
5855
+ "application/json": {
5856
+ /** Format: uuid */
5857
+ userId: string;
5858
+ /** @enum {string} */
5859
+ role?: "member" | "manager" | "owner";
5860
+ };
5861
+ };
5862
+ };
5863
+ responses: {
5864
+ /** @description Created membership */
5865
+ 201: {
5866
+ headers: {
5867
+ [name: string]: unknown;
5868
+ };
5869
+ content: {
5870
+ "application/json": components["schemas"]["OrgUnitMembershipResponse"];
5871
+ };
5872
+ };
5873
+ };
5874
+ };
5875
+ updateOrgUnitMembershipRole: {
5876
+ parameters: {
5877
+ query?: never;
5878
+ header?: never;
5879
+ path: {
5880
+ unitId: string;
5881
+ userId: string;
5882
+ };
5883
+ cookie?: never;
5884
+ };
5885
+ requestBody: {
5886
+ content: {
5887
+ "application/json": {
5888
+ /** @enum {string} */
5889
+ role: "member" | "manager" | "owner";
5890
+ };
5891
+ };
5892
+ };
5893
+ responses: {
5894
+ /** @description Updated membership */
5895
+ 200: {
5896
+ headers: {
5897
+ [name: string]: unknown;
5898
+ };
5899
+ content: {
5900
+ "application/json": components["schemas"]["OrgUnitMembershipResponse"];
5901
+ };
5902
+ };
5903
+ };
5904
+ };
5905
+ deleteOrgUnitMembership: {
5906
+ parameters: {
5907
+ query?: never;
5908
+ header?: never;
5909
+ path: {
5910
+ unitId: string;
5911
+ userId: string;
5912
+ };
5913
+ cookie?: never;
5914
+ };
5915
+ requestBody?: never;
5916
+ responses: {
5917
+ /** @description Membership removed */
5918
+ 204: {
5919
+ headers: {
5920
+ [name: string]: unknown;
5921
+ };
5922
+ content?: never;
5923
+ };
5924
+ };
5925
+ };
5726
5926
  listDriveFiles: {
5727
5927
  parameters: {
5728
5928
  query?: never;
package/src/org/index.ts CHANGED
@@ -228,6 +228,8 @@ export {
228
228
  OrgUnitDescendantsResponseSchema,
229
229
  OrgUnitRelationshipsResponseSchema,
230
230
  OrgLevelConfigResponseSchema,
231
+ OrgUnitMembershipResponseSchema,
232
+ OrgUnitMembershipListResponseSchema,
231
233
  } from './schemas';
232
234
  export type {
233
235
  OrgUnit,
@@ -242,4 +244,6 @@ export type {
242
244
  OrgUnitDescendantsResponse,
243
245
  OrgUnitRelationshipsResponse,
244
246
  OrgLevelConfigResponse,
247
+ OrgUnitMembershipResponse,
248
+ OrgUnitMembershipListResponse,
245
249
  } from './schemas';
@@ -747,6 +747,15 @@ export const OrgLevelConfigResponseSchema = z.object({
747
747
  entries: z.array(OrgLevelConfigSchema),
748
748
  });
749
749
 
750
+ export const OrgUnitMembershipResponseSchema = z.object({
751
+ membership: OrgUnitMembershipSchema,
752
+ });
753
+
754
+ export const OrgUnitMembershipListResponseSchema = z.object({
755
+ unitId: z.string().uuid(),
756
+ memberships: z.array(OrgUnitMembershipSchema),
757
+ });
758
+
750
759
  // --- Inferred types ---
751
760
 
752
761
  export type OrgUnit = z.infer<typeof OrgUnitSchema>;
@@ -761,3 +770,5 @@ export type OrgUnitAncestorsResponse = z.infer<typeof OrgUnitAncestorsResponseSc
761
770
  export type OrgUnitDescendantsResponse = z.infer<typeof OrgUnitDescendantsResponseSchema>;
762
771
  export type OrgUnitRelationshipsResponse = z.infer<typeof OrgUnitRelationshipsResponseSchema>;
763
772
  export type OrgLevelConfigResponse = z.infer<typeof OrgLevelConfigResponseSchema>;
773
+ export type OrgUnitMembershipResponse = z.infer<typeof OrgUnitMembershipResponseSchema>;
774
+ export type OrgUnitMembershipListResponse = z.infer<typeof OrgUnitMembershipListResponseSchema>;
@@ -28,6 +28,13 @@ export type ResourceKey =
28
28
  | { type: 'deletionEligibility'; orgId: string }
29
29
  | { type: 'transferOwnership'; orgId: string }
30
30
  | { type: 'companyMdDocs'; orgId: string }
31
+ // OrgUnit canonical model (ADR-BE-120) — Phase 2 Wave 4
32
+ | { type: 'orgTree'; orgId: string }
33
+ | { type: 'orgLevelConfig'; orgId: string }
34
+ | { type: 'orgUnit'; orgId: string; unitId: string }
35
+ | { type: 'orgUnitChildren'; orgId: string; unitId: string }
36
+ | { type: 'orgUnitAncestors'; orgId: string; unitId: string }
37
+ | { type: 'orgUnitMemberships'; orgId: string; unitId: string }
31
38
  // User-scoped
32
39
  | { type: 'dismissedBanners'; userId: string }
33
40
  | { type: 'userOrgs'; userId: string }
@@ -54,6 +61,7 @@ const ORG_SCOPED_TYPES = [
54
61
  'members', 'departments', 'chats', 'teams', 'integrations', 'invites',
55
62
  'auditEvents', 'timeline', 'workspace', 'workspaceDomains', 'authSettings',
56
63
  'billing', 'aiUsage', 'deletionEligibility', 'transferOwnership', 'companyMdDocs',
64
+ 'orgTree', 'orgLevelConfig',
57
65
  ] as const;
58
66
 
59
67
  const USER_SCOPED_TYPES = ['dismissedBanners', 'userOrgs', 'sessions', 'viewer'] as const;
@@ -82,6 +90,13 @@ export function toQueryKey(key: ResourceKey): readonly string[] {
82
90
  case 'companyMdContextBank':
83
91
  return [key.type, key.orgId, key.slug] as const;
84
92
 
93
+ // OrgUnit identity keys (ADR-BE-120)
94
+ case 'orgUnit':
95
+ case 'orgUnitChildren':
96
+ case 'orgUnitAncestors':
97
+ case 'orgUnitMemberships':
98
+ return [key.type, key.orgId, key.unitId] as const;
99
+
85
100
  // User-scoped
86
101
  case 'dismissedBanners':
87
102
  case 'userOrgs':
@@ -106,6 +121,8 @@ export function toQueryKey(key: ResourceKey): readonly string[] {
106
121
  case 'deletionEligibility':
107
122
  case 'transferOwnership':
108
123
  case 'companyMdDocs':
124
+ case 'orgTree':
125
+ case 'orgLevelConfig':
109
126
  return [key.type, key.orgId] as const;
110
127
 
111
128
  default: {
@@ -134,6 +151,10 @@ export function fromQueryKey(queryKey: readonly string[]): ResourceKey {
134
151
  chat: 'chatId',
135
152
  companyMdDoc: 'slug',
136
153
  companyMdContextBank: 'slug',
154
+ orgUnit: 'unitId',
155
+ orgUnitChildren: 'unitId',
156
+ orgUnitAncestors: 'unitId',
157
+ orgUnitMemberships: 'unitId',
137
158
  };
138
159
 
139
160
  if (type in identityFields) {
@@ -172,6 +193,13 @@ export const resourceRelationships: Record<string, string[]> = {
172
193
  team: ['teams'],
173
194
  companyMdDocs: ['companyMdDoc'],
174
195
  companyMdDoc: ['companyMdDocs'],
196
+ // OrgUnit: reparent/create/archive invalidates the tree view of the whole org;
197
+ // membership mutations invalidate the unit + its memberships list.
198
+ orgTree: ['orgUnit', 'orgUnitChildren', 'orgUnitAncestors'],
199
+ orgUnit: ['orgTree'],
200
+ orgUnitChildren: ['orgTree'],
201
+ orgUnitAncestors: ['orgTree'],
202
+ orgUnitMemberships: ['orgUnit'],
175
203
  };
176
204
 
177
205
  /**
@@ -200,6 +228,7 @@ export function matchesResourceKey(queryKey: readonly unknown[], targetKey: Reso
200
228
  if ('departmentId' in parsed && 'departmentId' in targetKey && parsed.departmentId !== targetKey.departmentId) return false;
201
229
  if ('chatId' in parsed && 'chatId' in targetKey && parsed.chatId !== targetKey.chatId) return false;
202
230
  if ('slug' in parsed && 'slug' in targetKey && parsed.slug !== targetKey.slug) return false;
231
+ if ('unitId' in parsed && 'unitId' in targetKey && parsed.unitId !== targetKey.unitId) return false;
203
232
 
204
233
  return true;
205
234
  }