@company-semantics/contracts 9.1.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.
- package/package.json +4 -1
- package/src/__tests__/resource-keys.test.ts +30 -23
- package/src/admin/authz-simulate.ts +4 -4
- package/src/admin/direct-grants.ts +2 -2
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +97 -0
- package/src/api/http/routes/ai-chat.ts +3 -3
- package/src/api/http/utils/resource-response.ts +5 -2
- package/src/api/index.ts +4 -4
- package/src/api/primitives.ts +6 -2
- package/src/auth/README.md +1 -0
- package/src/auth/index.ts +12 -5
- package/src/autotune.ts +5 -1
- package/src/billing/index.ts +1 -1
- package/src/billing/types.ts +1 -1
- package/src/chat/README.md +3 -0
- package/src/chat/__tests__/runtime-profile.test.ts +68 -48
- package/src/chat/index.ts +10 -4
- package/src/chat/runtime-profile.ts +25 -10
- package/src/chat/schemas.ts +49 -41
- package/src/chat/types.ts +48 -42
- package/src/ci-envelope/README.md +2 -0
- package/src/ci-envelope/__tests__/transitions.test.ts +56 -56
- package/src/ci-envelope/index.ts +2 -2
- package/src/ci-envelope/types.ts +20 -20
- package/src/ci-results/index.ts +2 -2
- package/src/ci-results/repo-ci-result.ts +15 -12
- package/src/compatibility.ts +6 -6
- package/src/content/index.ts +10 -4
- package/src/content/schemas.ts +42 -24
- package/src/dispatch/index.ts +18 -15
- package/src/email/__tests__/registry.test.ts +81 -77
- package/src/email/index.ts +3 -3
- package/src/email/registry.ts +25 -25
- package/src/email/types.ts +43 -43
- package/src/errors/index.ts +8 -8
- package/src/execution/__tests__/events.test.ts +42 -42
- package/src/execution/__tests__/lifecycle.test.ts +192 -190
- package/src/execution/__tests__/registry.test.ts +114 -114
- package/src/execution/audit-export.ts +4 -4
- package/src/execution/errors.ts +7 -7
- package/src/execution/event-metadata.ts +4 -4
- package/src/execution/events.ts +23 -21
- package/src/execution/expiry.ts +5 -5
- package/src/execution/hash-chain.ts +2 -2
- package/src/execution/index.ts +19 -28
- package/src/execution/kinds.ts +7 -7
- package/src/execution/lifecycle.ts +33 -33
- package/src/execution/registry.ts +63 -63
- package/src/execution/schemas.ts +31 -23
- package/src/execution/status.ts +45 -26
- package/src/execution/summary.ts +16 -17
- package/src/execution/timeline-ui.ts +9 -9
- package/src/execution/types.ts +31 -25
- package/src/generated/openapi-routes.ts +1 -0
- package/src/guards/config.ts +22 -18
- package/src/guards/index.ts +4 -4
- package/src/guards/types.ts +32 -24
- package/src/identity/__tests__/avatar.test.ts +68 -59
- package/src/identity/avatar.ts +8 -8
- package/src/identity/display-name.ts +3 -3
- package/src/identity/index.ts +8 -8
- package/src/identity/people-org-chart.ts +8 -4
- package/src/identity/schemas.ts +28 -18
- package/src/identity/types.ts +5 -5
- package/src/impersonation/index.ts +5 -5
- package/src/impersonation/schemas.ts +15 -9
- package/src/impersonation-events.ts +21 -21
- package/src/impersonation.ts +25 -24
- package/src/index.ts +118 -90
- package/src/interfaces/mcp/tools/help.ts +19 -19
- package/src/internal-admin.ts +6 -6
- package/src/mcp/README.md +2 -0
- package/src/mcp/__tests__/capability-graph.test.ts +290 -290
- package/src/mcp/capability-graph.ts +42 -40
- package/src/mcp/failure-context.ts +1 -3
- package/src/mcp/index.ts +57 -57
- package/src/mcp/resources.ts +9 -9
- package/src/meetings/index.ts +2 -2
- package/src/meetings/schemas.ts +51 -34
- package/src/message-parts/README.md +2 -0
- package/src/message-parts/__tests__/builder.test.ts +142 -142
- package/src/message-parts/__tests__/confirmation.test.ts +100 -86
- package/src/message-parts/__tests__/preview.test.ts +63 -63
- package/src/message-parts/__tests__/wire.test.ts +130 -124
- package/src/message-parts/builder.ts +23 -23
- package/src/message-parts/confirmation.ts +17 -14
- package/src/message-parts/execution.ts +7 -7
- package/src/message-parts/index.ts +10 -10
- package/src/message-parts/lifecycle.ts +25 -25
- package/src/message-parts/preview.ts +30 -30
- package/src/message-parts/types.ts +27 -27
- package/src/message-parts/wire.ts +24 -24
- package/src/mutations.ts +2 -2
- package/src/observability.ts +23 -11
- package/src/org/__tests__/org-units.test.ts +131 -96
- package/src/org/__tests__/tree-ordering.test.ts +57 -37
- package/src/org/__tests__/view-scopes.test.ts +40 -40
- package/src/org/domain.ts +9 -9
- package/src/org/index.ts +24 -21
- package/src/org/org-units.ts +34 -20
- package/src/org/schemas.ts +201 -127
- package/src/org/sharing.ts +17 -13
- package/src/org/tree-ordering.ts +3 -1
- package/src/org/types.ts +54 -47
- package/src/org/view-scopes.ts +9 -9
- package/src/permissions/access-levels.ts +7 -2
- package/src/permissions/access-source.ts +6 -6
- package/src/permissions/index.ts +5 -5
- package/src/permissions/orgchart-roles.ts +7 -7
- package/src/permissions/permission-introspection.ts +7 -5
- package/src/permissions/share-api.ts +19 -9
- package/src/pressure.ts +4 -4
- package/src/queryIntent.ts +21 -21
- package/src/ralph/__tests__/prd-groups.test.ts +159 -159
- package/src/ralph/__tests__/prd.test.ts +30 -30
- package/src/ralph/index.ts +3 -8
- package/src/ralph/prd.ts +33 -33
- package/src/ralph/progress.ts +1 -1
- package/src/rate-limit/README.md +4 -4
- package/src/rate-limit/index.ts +3 -3
- package/src/requests.ts +36 -8
- package/src/resource-keys.ts +207 -124
- package/src/resource-registry.ts +5 -5
- package/src/route-builder.ts +3 -3
- package/src/safe-mode.ts +2 -2
- package/src/security/index.ts +4 -4
- package/src/security/org-secrets.ts +13 -9
- package/src/security/secret.ts +3 -3
- package/src/sse.ts +3 -1
- package/src/system/README.md +3 -0
- package/src/system/capabilities.ts +22 -23
- package/src/system/diagram.ts +45 -45
- package/src/system/index.ts +14 -14
- package/src/tiers.ts +1 -1
- package/src/timeouts.ts +1 -1
- package/src/tracing.ts +30 -30
- package/src/types/analytics.ts +2 -2
- package/src/usage/README.md +3 -0
- package/src/usage/execution-types.ts +69 -69
- package/src/usage/types.ts +7 -3
package/src/ralph/progress.ts
CHANGED
package/src/rate-limit/README.md
CHANGED
|
@@ -18,8 +18,8 @@ Configuration for rate limit windows:
|
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
20
|
interface RateLimitConfig {
|
|
21
|
-
maxRequests: number;
|
|
22
|
-
windowMs: number;
|
|
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;
|
|
32
|
+
allowed: boolean; // true = permitted, false = rate limited
|
|
33
33
|
remaining: number; // Requests remaining in current window
|
|
34
|
-
resetAt: number;
|
|
34
|
+
resetAt: number; // Unix timestamp when window resets
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
package/src/rate-limit/index.ts
CHANGED
package/src/requests.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export type Tier =
|
|
1
|
+
export type Tier = "P0" | "P1" | "P2" | "P3";
|
|
2
2
|
|
|
3
|
-
export type MutationBehavior =
|
|
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:
|
|
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
|
-
| {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
+
};
|
package/src/resource-keys.ts
CHANGED
|
@@ -5,52 +5,56 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export type ResourceKey =
|
|
7
7
|
// Collections (plural) — lists of entities
|
|
8
|
-
| { type:
|
|
9
|
-
| { type:
|
|
10
|
-
| { type:
|
|
11
|
-
| { type:
|
|
12
|
-
| { type:
|
|
13
|
-
| { type:
|
|
14
|
-
| { type:
|
|
15
|
-
| { type:
|
|
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:
|
|
18
|
-
| { type:
|
|
19
|
-
| { type:
|
|
20
|
-
| { type:
|
|
21
|
-
| { type:
|
|
22
|
-
| { type:
|
|
23
|
-
| { type:
|
|
24
|
-
| { type:
|
|
25
|
-
| { type:
|
|
26
|
-
| { type:
|
|
27
|
-
| { type:
|
|
28
|
-
| { type:
|
|
29
|
-
| { type:
|
|
30
|
-
| { type:
|
|
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:
|
|
33
|
-
| { type:
|
|
34
|
-
| { type:
|
|
35
|
-
| { type:
|
|
36
|
-
| { type:
|
|
37
|
-
| { type:
|
|
38
|
-
| { type:
|
|
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:
|
|
41
|
+
| { type: "orgUnitOwners"; orgId: string }
|
|
42
42
|
// People reporting (ADR-BE-166) — drives the settings Org chart drill-down
|
|
43
|
-
| { type:
|
|
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:
|
|
47
|
-
| { type:
|
|
48
|
-
| { type:
|
|
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:
|
|
51
|
-
| { type:
|
|
52
|
-
| { type:
|
|
53
|
-
| { type:
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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 = [
|
|
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
|
-
|
|
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
|
|
130
|
+
case "member":
|
|
102
131
|
return [key.type, key.orgId, key.memberId] as const;
|
|
103
|
-
case
|
|
132
|
+
case "team":
|
|
104
133
|
return [key.type, key.orgId, key.teamId] as const;
|
|
105
|
-
case
|
|
134
|
+
case "department":
|
|
106
135
|
return [key.type, key.orgId, key.departmentId] as const;
|
|
107
|
-
case
|
|
136
|
+
case "chat":
|
|
108
137
|
return [key.type, key.orgId, key.chatId] as const;
|
|
109
|
-
case
|
|
110
|
-
case
|
|
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
|
|
115
|
-
case
|
|
116
|
-
case
|
|
117
|
-
case
|
|
118
|
-
case
|
|
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
|
|
123
|
-
case
|
|
124
|
-
case
|
|
125
|
-
case
|
|
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
|
|
130
|
-
case
|
|
131
|
-
case
|
|
132
|
-
case
|
|
133
|
-
case
|
|
134
|
-
case
|
|
135
|
-
case
|
|
136
|
-
case
|
|
137
|
-
case
|
|
138
|
-
case
|
|
139
|
-
case
|
|
140
|
-
case
|
|
141
|
-
case
|
|
142
|
-
case
|
|
143
|
-
case
|
|
144
|
-
case
|
|
145
|
-
case
|
|
146
|
-
case
|
|
147
|
-
case
|
|
148
|
-
case
|
|
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
|
|
153
|
-
case
|
|
154
|
-
case
|
|
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(
|
|
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:
|
|
178
|
-
team:
|
|
179
|
-
department:
|
|
180
|
-
chat:
|
|
181
|
-
companyMdDoc:
|
|
182
|
-
companyMdContextBank:
|
|
183
|
-
orgUnit:
|
|
184
|
-
orgUnitChildren:
|
|
185
|
-
orgUnitAncestors:
|
|
186
|
-
orgUnitMemberships:
|
|
187
|
-
orgUnitPermissions:
|
|
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(
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Invalid query key for '${type}': expected [type, orgId, ${identityFields[type]}], got ${JSON.stringify(queryKey)}`,
|
|
227
|
+
);
|
|
193
228
|
}
|
|
194
|
-
return {
|
|
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:
|
|
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: [
|
|
222
|
-
member: [
|
|
223
|
-
departments: [
|
|
224
|
-
department: [
|
|
225
|
-
chats: [
|
|
226
|
-
chat: [
|
|
227
|
-
teams: [
|
|
228
|
-
team: [
|
|
229
|
-
companyMdDocs: [
|
|
230
|
-
companyMdDoc: [
|
|
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: [
|
|
234
|
-
orgUnit: [
|
|
235
|
-
orgUnitChildren: [
|
|
236
|
-
orgUnitAncestors: [
|
|
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: [
|
|
240
|
-
orgUnitPermissions: [
|
|
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(
|
|
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 ===
|
|
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 (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
|
|
270
|
-
|
|
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
|
}
|
package/src/resource-registry.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import type { Tier } from
|
|
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 = [
|
|
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([
|
|
18
|
+
hydrationPhase: z.enum(["critical", "interactive", "background"]),
|
|
19
19
|
hydrationDepends: z.array(z.string().transform((s) => s as ResourceKey)),
|
|
20
|
-
mutationBehavior: z.enum([
|
|
20
|
+
mutationBehavior: z.enum(["collapse", "queue", "serial"]),
|
|
21
21
|
staleTimeMs: z
|
|
22
22
|
.number()
|
|
23
23
|
.int()
|
package/src/route-builder.ts
CHANGED
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
* backend call site to the appropriate FastifyRequest/FastifyReply
|
|
12
12
|
* signature.
|
|
13
13
|
*/
|
|
14
|
-
import type { Tier } from
|
|
15
|
-
import type { ResourceKey } from
|
|
14
|
+
import type { Tier } from "./requests";
|
|
15
|
+
import type { ResourceKey } from "./resource-keys";
|
|
16
16
|
|
|
17
|
-
export type HttpMethod =
|
|
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 =
|
|
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 =
|
|
29
|
+
export type SafeModeValue = "on" | "off";
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Observable safe-mode state. Shared verbatim between backend (source of
|
package/src/security/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { Secret } from
|
|
2
|
-
export { wrapSecret, unwrapSecret } from
|
|
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
|
|
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
|
|
21
|
+
} from "./org-secrets";
|