@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
@@ -45,7 +45,7 @@ export type RalphIteration = {
45
45
  /**
46
46
  * Ralph operation mode.
47
47
  */
48
- export type RalphMode = 'hitl' | 'afk';
48
+ export type RalphMode = "hitl" | "afk";
49
49
 
50
50
  /**
51
51
  * Complete progress state for a Ralph session.
@@ -18,8 +18,8 @@ Configuration for rate limit windows:
18
18
 
19
19
  ```typescript
20
20
  interface RateLimitConfig {
21
- maxRequests: number; // Maximum requests allowed in window
22
- windowMs: number; // Window duration in milliseconds
21
+ maxRequests: number; // Maximum requests allowed in window
22
+ windowMs: number; // Window duration in milliseconds
23
23
  }
24
24
  ```
25
25
 
@@ -29,9 +29,9 @@ Result of a rate limit check:
29
29
 
30
30
  ```typescript
31
31
  interface RateLimitResult {
32
- allowed: boolean; // true = permitted, false = rate limited
32
+ allowed: boolean; // true = permitted, false = rate limited
33
33
  remaining: number; // Requests remaining in current window
34
- resetAt: number; // Unix timestamp when window resets
34
+ resetAt: number; // Unix timestamp when window resets
35
35
  }
36
36
  ```
37
37
 
@@ -5,9 +5,9 @@
5
5
  */
6
6
 
7
7
  export enum RateLimitTier {
8
- FREE = 'free',
9
- PRO = 'pro',
10
- ENTERPRISE = 'enterprise',
8
+ FREE = "free",
9
+ PRO = "pro",
10
+ ENTERPRISE = "enterprise",
11
11
  }
12
12
 
13
13
  export interface RateLimitConfig {
package/src/requests.ts CHANGED
@@ -1,6 +1,6 @@
1
- export type Tier = 'P0' | 'P1' | 'P2' | 'P3';
1
+ export type Tier = "P0" | "P1" | "P2" | "P3";
2
2
 
3
- export type MutationBehavior = 'collapse' | 'queue' | 'parallel';
3
+ export type MutationBehavior = "collapse" | "queue" | "parallel";
4
4
 
5
5
  export type SchedulerResourceKey = {
6
6
  type: string;
@@ -9,7 +9,7 @@ export type SchedulerResourceKey = {
9
9
  };
10
10
 
11
11
  export type RequestDescriptor = {
12
- method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
12
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
13
13
  path: string;
14
14
  params?: Record<string, unknown>;
15
15
  tier: Tier;
@@ -21,8 +21,36 @@ export type RequestDescriptor = {
21
21
  };
22
22
 
23
23
  export type SchedulerEvent =
24
- | { type: 'enqueued'; id: string; descriptor: RequestDescriptor; queuedAt: number }
25
- | { type: 'started'; id: string; descriptor: RequestDescriptor; startedAt: number; waitedMs: number }
26
- | { type: 'settled'; id: string; descriptor: RequestDescriptor; durationMs: number; ok: boolean; status?: number }
27
- | { type: 'aborted'; id: string; descriptor: RequestDescriptor; reason: 'superseded' | 'invalidated' | 'unmounted' | 'external' }
28
- | { type: 'deduped'; id: string; descriptor: RequestDescriptor; sharedWith: string };
24
+ | {
25
+ type: "enqueued";
26
+ id: string;
27
+ descriptor: RequestDescriptor;
28
+ queuedAt: number;
29
+ }
30
+ | {
31
+ type: "started";
32
+ id: string;
33
+ descriptor: RequestDescriptor;
34
+ startedAt: number;
35
+ waitedMs: number;
36
+ }
37
+ | {
38
+ type: "settled";
39
+ id: string;
40
+ descriptor: RequestDescriptor;
41
+ durationMs: number;
42
+ ok: boolean;
43
+ status?: number;
44
+ }
45
+ | {
46
+ type: "aborted";
47
+ id: string;
48
+ descriptor: RequestDescriptor;
49
+ reason: "superseded" | "invalidated" | "unmounted" | "external";
50
+ }
51
+ | {
52
+ type: "deduped";
53
+ id: string;
54
+ descriptor: RequestDescriptor;
55
+ sharedWith: string;
56
+ };
@@ -5,52 +5,56 @@
5
5
  */
6
6
  export type ResourceKey =
7
7
  // Collections (plural) — lists of entities
8
- | { type: 'members'; orgId: string }
9
- | { type: 'departments'; orgId: string }
10
- | { type: 'chats'; orgId: string }
11
- | { type: 'teams'; orgId: string }
12
- | { type: 'integrations'; orgId: string }
13
- | { type: 'invites'; orgId: string }
14
- | { type: 'auditEvents'; orgId: string }
15
- | { type: 'timeline'; orgId: string }
8
+ | { type: "members"; orgId: string }
9
+ | { type: "departments"; orgId: string }
10
+ | { type: "chats"; orgId: string }
11
+ | { type: "teams"; orgId: string }
12
+ | { type: "integrations"; orgId: string }
13
+ | { type: "invites"; orgId: string }
14
+ | { type: "auditEvents"; orgId: string }
15
+ | { type: "timeline"; orgId: string }
16
16
  // Identities (singular) — single entities
17
- | { type: 'member'; orgId: string; memberId: string }
18
- | { type: 'team'; orgId: string; teamId: string }
19
- | { type: 'department'; orgId: string; departmentId: string }
20
- | { type: 'chat'; orgId: string; chatId: string }
21
- | { type: 'companyMdDoc'; orgId: string; slug: string }
22
- | { type: 'companyMdContextBank'; orgId: string; slug: string }
23
- | { type: 'workspace'; orgId: string }
24
- | { type: 'workspaceDomains'; orgId: string }
25
- | { type: 'authSettings'; orgId: string }
26
- | { type: 'billing'; orgId: string }
27
- | { type: 'aiUsage'; orgId: string }
28
- | { type: 'deletionEligibility'; orgId: string }
29
- | { type: 'transferOwnership'; orgId: string }
30
- | { type: 'companyMdDocs'; orgId: string }
17
+ | { type: "member"; orgId: string; memberId: string }
18
+ | { type: "team"; orgId: string; teamId: string }
19
+ | { type: "department"; orgId: string; departmentId: string }
20
+ | { type: "chat"; orgId: string; chatId: string }
21
+ | { type: "companyMdDoc"; orgId: string; slug: string }
22
+ | { type: "companyMdContextBank"; orgId: string; slug: string }
23
+ | { type: "workspace"; orgId: string }
24
+ | { type: "workspaceDomains"; orgId: string }
25
+ | { type: "authSettings"; orgId: string }
26
+ | { type: "billing"; orgId: string }
27
+ | { type: "aiUsage"; orgId: string }
28
+ | { type: "deletionEligibility"; orgId: string }
29
+ | { type: "transferOwnership"; orgId: string }
30
+ | { type: "companyMdDocs"; orgId: string }
31
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 }
38
- | { type: 'orgUnitPermissions'; orgId: string; unitId: string }
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 }
38
+ | { type: "orgUnitPermissions"; orgId: string; unitId: string }
39
39
  // Org-unit owners list (ADR-CONTRACTS-052) — owners are an org-wide
40
40
  // projection, not a per-unit collection, so unitId is intentionally excluded.
41
- | { type: 'orgUnitOwners'; orgId: string }
41
+ | { type: "orgUnitOwners"; orgId: string }
42
42
  // People reporting (ADR-BE-166) — drives the settings Org chart drill-down
43
- | { type: 'peopleOrgChart'; orgId: string }
43
+ | { type: "peopleOrgChart"; orgId: string }
44
44
  // System-scoped (ADR-CONTRACTS-052) — tenant-less super-admin resources.
45
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' }
46
+ | { type: "internalAdminAiProviders"; scope: "system" }
47
+ | { type: "internalAdminPrompts"; scope: "system" }
48
+ | { type: "internalAdminAiRuntimeDefaults"; scope: "system" }
49
+ // Software-factory surfaces (ADR-BE-239 / ADR-BE-243) — internal-admin
50
+ // dashboard reads over Global-infra factory tables; tenant-less, scope 'system'.
51
+ | { type: "factoryFloor"; scope: "system" }
52
+ | { type: "factorySnapshot"; scope: "system" }
49
53
  // User-scoped
50
- | { type: 'dismissedBanners'; userId: string }
51
- | { type: 'userOrgs'; userId: string }
52
- | { type: 'sessions'; userId: string }
53
- | { type: 'viewer'; userId: string };
54
+ | { type: "dismissedBanners"; userId: string }
55
+ | { type: "userOrgs"; userId: string }
56
+ | { type: "sessions"; userId: string }
57
+ | { type: "viewer"; userId: string };
54
58
 
55
59
  /**
56
60
  * Action — structured mutation key used across execution system, audit, permissions.
@@ -69,13 +73,34 @@ export function resolveScope(context: {
69
73
 
70
74
  /** All ResourceKey type literals for exhaustive checking. */
71
75
  const ORG_SCOPED_TYPES = [
72
- 'members', 'departments', 'chats', 'teams', 'integrations', 'invites',
73
- 'auditEvents', 'timeline', 'workspace', 'workspaceDomains', 'authSettings',
74
- 'billing', 'aiUsage', 'deletionEligibility', 'transferOwnership', 'companyMdDocs',
75
- 'orgTree', 'orgLevelConfig', 'peopleOrgChart', 'orgUnitOwners',
76
+ "members",
77
+ "departments",
78
+ "chats",
79
+ "teams",
80
+ "integrations",
81
+ "invites",
82
+ "auditEvents",
83
+ "timeline",
84
+ "workspace",
85
+ "workspaceDomains",
86
+ "authSettings",
87
+ "billing",
88
+ "aiUsage",
89
+ "deletionEligibility",
90
+ "transferOwnership",
91
+ "companyMdDocs",
92
+ "orgTree",
93
+ "orgLevelConfig",
94
+ "peopleOrgChart",
95
+ "orgUnitOwners",
76
96
  ] as const;
77
97
 
78
- const USER_SCOPED_TYPES = ['dismissedBanners', 'userOrgs', 'sessions', 'viewer'] as const;
98
+ const USER_SCOPED_TYPES = [
99
+ "dismissedBanners",
100
+ "userOrgs",
101
+ "sessions",
102
+ "viewer",
103
+ ] as const;
79
104
 
80
105
  /**
81
106
  * System-scoped types (ADR-CONTRACTS-052) — tenant-less super-admin resources.
@@ -83,7 +108,11 @@ const USER_SCOPED_TYPES = ['dismissedBanners', 'userOrgs', 'sessions', 'viewer']
83
108
  * Intentionally NOT exported — internal to the parser like the other scope arrays.
84
109
  */
85
110
  const SYSTEM_SCOPED_TYPES = [
86
- 'internalAdminAiProviders', 'internalAdminPrompts', 'internalAdminAiRuntimeDefaults',
111
+ "internalAdminAiProviders",
112
+ "internalAdminPrompts",
113
+ "internalAdminAiRuntimeDefaults",
114
+ "factoryFloor",
115
+ "factorySnapshot",
87
116
  ] as const;
88
117
 
89
118
  /**
@@ -98,60 +127,62 @@ const SYSTEM_SCOPED_TYPES = [
98
127
  export function toQueryKey(key: ResourceKey): readonly string[] {
99
128
  switch (key.type) {
100
129
  // Identities with extra field
101
- case 'member':
130
+ case "member":
102
131
  return [key.type, key.orgId, key.memberId] as const;
103
- case 'team':
132
+ case "team":
104
133
  return [key.type, key.orgId, key.teamId] as const;
105
- case 'department':
134
+ case "department":
106
135
  return [key.type, key.orgId, key.departmentId] as const;
107
- case 'chat':
136
+ case "chat":
108
137
  return [key.type, key.orgId, key.chatId] as const;
109
- case 'companyMdDoc':
110
- case 'companyMdContextBank':
138
+ case "companyMdDoc":
139
+ case "companyMdContextBank":
111
140
  return [key.type, key.orgId, key.slug] as const;
112
141
 
113
142
  // OrgUnit identity keys (ADR-BE-120)
114
- case 'orgUnit':
115
- case 'orgUnitChildren':
116
- case 'orgUnitAncestors':
117
- case 'orgUnitMemberships':
118
- case 'orgUnitPermissions':
143
+ case "orgUnit":
144
+ case "orgUnitChildren":
145
+ case "orgUnitAncestors":
146
+ case "orgUnitMemberships":
147
+ case "orgUnitPermissions":
119
148
  return [key.type, key.orgId, key.unitId] as const;
120
149
 
121
150
  // User-scoped
122
- case 'dismissedBanners':
123
- case 'userOrgs':
124
- case 'sessions':
125
- case 'viewer':
151
+ case "dismissedBanners":
152
+ case "userOrgs":
153
+ case "sessions":
154
+ case "viewer":
126
155
  return [key.type, key.userId] as const;
127
156
 
128
157
  // Org-scoped collections and identities
129
- case 'members':
130
- case 'departments':
131
- case 'chats':
132
- case 'teams':
133
- case 'integrations':
134
- case 'invites':
135
- case 'auditEvents':
136
- case 'timeline':
137
- case 'workspace':
138
- case 'workspaceDomains':
139
- case 'authSettings':
140
- case 'billing':
141
- case 'aiUsage':
142
- case 'deletionEligibility':
143
- case 'transferOwnership':
144
- case 'companyMdDocs':
145
- case 'orgTree':
146
- case 'orgLevelConfig':
147
- case 'peopleOrgChart':
148
- case 'orgUnitOwners':
158
+ case "members":
159
+ case "departments":
160
+ case "chats":
161
+ case "teams":
162
+ case "integrations":
163
+ case "invites":
164
+ case "auditEvents":
165
+ case "timeline":
166
+ case "workspace":
167
+ case "workspaceDomains":
168
+ case "authSettings":
169
+ case "billing":
170
+ case "aiUsage":
171
+ case "deletionEligibility":
172
+ case "transferOwnership":
173
+ case "companyMdDocs":
174
+ case "orgTree":
175
+ case "orgLevelConfig":
176
+ case "peopleOrgChart":
177
+ case "orgUnitOwners":
149
178
  return [key.type, key.orgId] as const;
150
179
 
151
180
  // System-scoped (ADR-CONTRACTS-052) — tenant-less super-admin resources
152
- case 'internalAdminAiProviders':
153
- case 'internalAdminPrompts':
154
- case 'internalAdminAiRuntimeDefaults':
181
+ case "internalAdminAiProviders":
182
+ case "internalAdminPrompts":
183
+ case "internalAdminAiRuntimeDefaults":
184
+ case "factoryFloor":
185
+ case "factorySnapshot":
155
186
  return [key.type, key.scope] as const;
156
187
 
157
188
  default: {
@@ -169,34 +200,42 @@ export function fromQueryKey(queryKey: readonly string[]): ResourceKey {
169
200
  const [type, ...rest] = queryKey;
170
201
 
171
202
  if (!type || rest.length === 0) {
172
- throw new Error(`Invalid query key: expected at least [type, scope], got ${JSON.stringify(queryKey)}`);
203
+ throw new Error(
204
+ `Invalid query key: expected at least [type, scope], got ${JSON.stringify(queryKey)}`,
205
+ );
173
206
  }
174
207
 
175
208
  // Identities with extra field
176
209
  const identityFields: Record<string, string> = {
177
- member: 'memberId',
178
- team: 'teamId',
179
- department: 'departmentId',
180
- chat: 'chatId',
181
- companyMdDoc: 'slug',
182
- companyMdContextBank: 'slug',
183
- orgUnit: 'unitId',
184
- orgUnitChildren: 'unitId',
185
- orgUnitAncestors: 'unitId',
186
- orgUnitMemberships: 'unitId',
187
- orgUnitPermissions: 'unitId',
210
+ member: "memberId",
211
+ team: "teamId",
212
+ department: "departmentId",
213
+ chat: "chatId",
214
+ companyMdDoc: "slug",
215
+ companyMdContextBank: "slug",
216
+ orgUnit: "unitId",
217
+ orgUnitChildren: "unitId",
218
+ orgUnitAncestors: "unitId",
219
+ orgUnitMemberships: "unitId",
220
+ orgUnitPermissions: "unitId",
188
221
  };
189
222
 
190
223
  if (type in identityFields) {
191
224
  if (rest.length !== 2) {
192
- throw new Error(`Invalid query key for '${type}': expected [type, orgId, ${identityFields[type]}], got ${JSON.stringify(queryKey)}`);
225
+ throw new Error(
226
+ `Invalid query key for '${type}': expected [type, orgId, ${identityFields[type]}], got ${JSON.stringify(queryKey)}`,
227
+ );
193
228
  }
194
- return { type, orgId: rest[0], [identityFields[type]]: rest[1] } as ResourceKey;
229
+ return {
230
+ type,
231
+ orgId: rest[0],
232
+ [identityFields[type]]: rest[1],
233
+ } as ResourceKey;
195
234
  }
196
235
 
197
236
  // System-scoped types (ADR-CONTRACTS-052) — query key is [type, 'system']
198
237
  if ((SYSTEM_SCOPED_TYPES as readonly string[]).includes(type)) {
199
- return { type, scope: 'system' } as ResourceKey;
238
+ return { type, scope: "system" } as ResourceKey;
200
239
  }
201
240
 
202
241
  // User-scoped types
@@ -218,26 +257,26 @@ export function fromQueryKey(queryKey: readonly string[]): ResourceKey {
218
257
  * When invalidating a collection, we must also invalidate related identity entries (and vice versa).
219
258
  */
220
259
  export const resourceRelationships: Record<string, string[]> = {
221
- members: ['member'],
222
- member: ['members'],
223
- departments: ['department'],
224
- department: ['departments'],
225
- chats: ['chat'],
226
- chat: ['chats'],
227
- teams: ['team'],
228
- team: ['teams'],
229
- companyMdDocs: ['companyMdDoc'],
230
- companyMdDoc: ['companyMdDocs'],
260
+ members: ["member"],
261
+ member: ["members"],
262
+ departments: ["department"],
263
+ department: ["departments"],
264
+ chats: ["chat"],
265
+ chat: ["chats"],
266
+ teams: ["team"],
267
+ team: ["teams"],
268
+ companyMdDocs: ["companyMdDoc"],
269
+ companyMdDoc: ["companyMdDocs"],
231
270
  // OrgUnit: reparent/create/archive invalidates the tree view of the whole org;
232
271
  // membership mutations invalidate the unit + its memberships list.
233
- orgTree: ['orgUnit', 'orgUnitChildren', 'orgUnitAncestors'],
234
- orgUnit: ['orgTree'],
235
- orgUnitChildren: ['orgTree'],
236
- orgUnitAncestors: ['orgTree'],
272
+ orgTree: ["orgUnit", "orgUnitChildren", "orgUnitAncestors"],
273
+ orgUnit: ["orgTree"],
274
+ orgUnitChildren: ["orgTree"],
275
+ orgUnitAncestors: ["orgTree"],
237
276
  // Subtree manage is driven by memberships (ADR-BE-151); mutating memberships
238
277
  // invalidates both the direct roster and the effective-managers read.
239
- orgUnitMemberships: ['orgUnit', 'orgUnitPermissions'],
240
- orgUnitPermissions: ['orgUnitMemberships'],
278
+ orgUnitMemberships: ["orgUnit", "orgUnitPermissions"],
279
+ orgUnitPermissions: ["orgUnitMemberships"],
241
280
  };
242
281
 
243
282
  /**
@@ -245,9 +284,12 @@ export const resourceRelationships: Record<string, string[]> = {
245
284
  * Compares type + scope fields exactly. No partial matching. No loose comparisons.
246
285
  * Used by invalidateResource with TanStack's predicate-based invalidation.
247
286
  */
248
- export function matchesResourceKey(queryKey: readonly unknown[], targetKey: ResourceKey): boolean {
287
+ export function matchesResourceKey(
288
+ queryKey: readonly unknown[],
289
+ targetKey: ResourceKey,
290
+ ): boolean {
249
291
  if (!Array.isArray(queryKey) && !isReadonlyArray(queryKey)) return false;
250
- const stringKey = queryKey.filter((v): v is string => typeof v === 'string');
292
+ const stringKey = queryKey.filter((v): v is string => typeof v === "string");
251
293
  if (stringKey.length !== queryKey.length) return false;
252
294
 
253
295
  let parsed: ResourceKey;
@@ -259,15 +301,56 @@ export function matchesResourceKey(queryKey: readonly unknown[], targetKey: Reso
259
301
 
260
302
  if (parsed.type !== targetKey.type) return false;
261
303
 
262
- if ('orgId' in parsed && 'orgId' in targetKey && parsed.orgId !== targetKey.orgId) return false;
263
- if ('userId' in parsed && 'userId' in targetKey && parsed.userId !== targetKey.userId) return false;
264
- if ('memberId' in parsed && 'memberId' in targetKey && parsed.memberId !== targetKey.memberId) return false;
265
- if ('teamId' in parsed && 'teamId' in targetKey && parsed.teamId !== targetKey.teamId) return false;
266
- if ('departmentId' in parsed && 'departmentId' in targetKey && parsed.departmentId !== targetKey.departmentId) return false;
267
- if ('chatId' in parsed && 'chatId' in targetKey && parsed.chatId !== targetKey.chatId) return false;
268
- if ('slug' in parsed && 'slug' in targetKey && parsed.slug !== targetKey.slug) return false;
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;
304
+ if (
305
+ "orgId" in parsed &&
306
+ "orgId" in targetKey &&
307
+ parsed.orgId !== targetKey.orgId
308
+ )
309
+ return false;
310
+ if (
311
+ "userId" in parsed &&
312
+ "userId" in targetKey &&
313
+ parsed.userId !== targetKey.userId
314
+ )
315
+ return false;
316
+ if (
317
+ "memberId" in parsed &&
318
+ "memberId" in targetKey &&
319
+ parsed.memberId !== targetKey.memberId
320
+ )
321
+ return false;
322
+ if (
323
+ "teamId" in parsed &&
324
+ "teamId" in targetKey &&
325
+ parsed.teamId !== targetKey.teamId
326
+ )
327
+ return false;
328
+ if (
329
+ "departmentId" in parsed &&
330
+ "departmentId" in targetKey &&
331
+ parsed.departmentId !== targetKey.departmentId
332
+ )
333
+ return false;
334
+ if (
335
+ "chatId" in parsed &&
336
+ "chatId" in targetKey &&
337
+ parsed.chatId !== targetKey.chatId
338
+ )
339
+ return false;
340
+ if ("slug" in parsed && "slug" in targetKey && parsed.slug !== targetKey.slug)
341
+ return false;
342
+ if (
343
+ "unitId" in parsed &&
344
+ "unitId" in targetKey &&
345
+ parsed.unitId !== targetKey.unitId
346
+ )
347
+ return false;
348
+ if (
349
+ "scope" in parsed &&
350
+ "scope" in targetKey &&
351
+ parsed.scope !== targetKey.scope
352
+ )
353
+ return false;
271
354
 
272
355
  return true;
273
356
  }
@@ -1,5 +1,5 @@
1
- import { z } from 'zod';
2
- import type { Tier } from './requests';
1
+ import { z } from "zod";
2
+ import type { Tier } from "./requests";
3
3
 
4
4
  declare const ResourceKeyBrand: unique symbol;
5
5
  declare const SectionKeyBrand: unique symbol;
@@ -9,15 +9,15 @@ export type ResourceKey = string & { readonly [ResourceKeyBrand]: true };
9
9
  export type SectionKey = string & { readonly [SectionKeyBrand]: true };
10
10
  export type Duration = number & { readonly [DurationBrand]: true };
11
11
 
12
- const TIERS = ['P0', 'P1', 'P2', 'P3'] as const satisfies readonly Tier[];
12
+ const TIERS = ["P0", "P1", "P2", "P3"] as const satisfies readonly Tier[];
13
13
 
14
14
  export const ResourceEntrySchema = z
15
15
  .object({
16
16
  resource: z.string().transform((s) => s as ResourceKey),
17
17
  priority: z.enum(TIERS),
18
- hydrationPhase: z.enum(['critical', 'interactive', 'background']),
18
+ hydrationPhase: z.enum(["critical", "interactive", "background"]),
19
19
  hydrationDepends: z.array(z.string().transform((s) => s as ResourceKey)),
20
- mutationBehavior: z.enum(['collapse', 'queue', 'serial']),
20
+ mutationBehavior: z.enum(["collapse", "queue", "serial"]),
21
21
  staleTimeMs: z
22
22
  .number()
23
23
  .int()
@@ -11,10 +11,10 @@
11
11
  * backend call site to the appropriate FastifyRequest/FastifyReply
12
12
  * signature.
13
13
  */
14
- import type { Tier } from './requests';
15
- import type { ResourceKey } from './resource-keys';
14
+ import type { Tier } from "./requests";
15
+ import type { ResourceKey } from "./resource-keys";
16
16
 
17
- export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
17
+ export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
18
18
 
19
19
  export type RouteDefinition<Handler = unknown> = {
20
20
  readonly tier: Tier;
package/src/safe-mode.ts CHANGED
@@ -19,14 +19,14 @@
19
19
  * Canonical response header signalling safe-mode state to clients.
20
20
  * Frontend imports this to avoid stringly-typed header lookups.
21
21
  */
22
- export const SYSTEM_SAFE_MODE_HEADER = 'X-System-Safe-Mode';
22
+ export const SYSTEM_SAFE_MODE_HEADER = "X-System-Safe-Mode";
23
23
 
24
24
  /**
25
25
  * Values carried on the `X-System-Safe-Mode` header.
26
26
  * `on` indicates the backend has entered safe-mode and is rejecting all
27
27
  * non-P0 traffic with 503. `off` is the normal quiescent state.
28
28
  */
29
- export type SafeModeValue = 'on' | 'off';
29
+ export type SafeModeValue = "on" | "off";
30
30
 
31
31
  /**
32
32
  * Observable safe-mode state. Shared verbatim between backend (source of
@@ -1,5 +1,5 @@
1
- export type { Secret } from './secret';
2
- export { wrapSecret, unwrapSecret } from './secret';
1
+ export type { Secret } from "./secret";
2
+ export { wrapSecret, unwrapSecret } from "./secret";
3
3
 
4
4
  export {
5
5
  UsageClassSchema,
@@ -9,7 +9,7 @@ export {
9
9
  CreateSecretRequestSchema,
10
10
  RotateSecretRequestSchema,
11
11
  DisableSecretRequestSchema,
12
- } from './org-secrets';
12
+ } from "./org-secrets";
13
13
  export type {
14
14
  UsageClass,
15
15
  OrgSecretsAction,
@@ -18,4 +18,4 @@ export type {
18
18
  CreateSecretRequest,
19
19
  RotateSecretRequest,
20
20
  DisableSecretRequest,
21
- } from './org-secrets';
21
+ } from "./org-secrets";