@company-semantics/contracts 27.7.0 → 27.9.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 +1 -1
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +6 -72
- package/src/email/__tests__/registry.test.ts +18 -0
- package/src/generated/openapi-routes.ts +0 -1
- package/src/org/__tests__/transformation.test.ts +68 -0
- package/src/org/transformation.ts +28 -0
- package/src/permissions/share-api.ts +15 -0
package/package.json
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '3974d23a4348' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '3974d23a434834c5a1f81d93ef082b993014143e7b95e97dd775293045809af8' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -736,23 +736,6 @@ export interface paths {
|
|
|
736
736
|
patch: operations["setMemberManager"];
|
|
737
737
|
trace?: never;
|
|
738
738
|
};
|
|
739
|
-
"/api/reporting/bulk-reparent": {
|
|
740
|
-
parameters: {
|
|
741
|
-
query?: never;
|
|
742
|
-
header?: never;
|
|
743
|
-
path?: never;
|
|
744
|
-
cookie?: never;
|
|
745
|
-
};
|
|
746
|
-
get?: never;
|
|
747
|
-
put?: never;
|
|
748
|
-
/** Atomically re-point many members' solid manager to one new manager */
|
|
749
|
-
post: operations["bulkReparentReporting"];
|
|
750
|
-
delete?: never;
|
|
751
|
-
options?: never;
|
|
752
|
-
head?: never;
|
|
753
|
-
patch?: never;
|
|
754
|
-
trace?: never;
|
|
755
|
-
};
|
|
756
739
|
"/api/reporting/move/preview": {
|
|
757
740
|
parameters: {
|
|
758
741
|
query?: never;
|
|
@@ -3912,37 +3895,6 @@ export interface components {
|
|
|
3912
3895
|
openRoleId: string;
|
|
3913
3896
|
};
|
|
3914
3897
|
};
|
|
3915
|
-
BulkReparentReportingResponse: {
|
|
3916
|
-
/** @constant */
|
|
3917
|
-
success: true;
|
|
3918
|
-
movedCount: number;
|
|
3919
|
-
toManager: {
|
|
3920
|
-
/** @constant */
|
|
3921
|
-
kind: "member";
|
|
3922
|
-
/** Format: uuid */
|
|
3923
|
-
userId: string;
|
|
3924
|
-
} | {
|
|
3925
|
-
/** @constant */
|
|
3926
|
-
kind: "open-role";
|
|
3927
|
-
/** Format: uuid */
|
|
3928
|
-
openRoleId: string;
|
|
3929
|
-
};
|
|
3930
|
-
};
|
|
3931
|
-
BulkReparentReportingRequest: {
|
|
3932
|
-
memberIds: string[];
|
|
3933
|
-
toManager: {
|
|
3934
|
-
/** @constant */
|
|
3935
|
-
kind: "member";
|
|
3936
|
-
/** Format: uuid */
|
|
3937
|
-
userId: string;
|
|
3938
|
-
} | {
|
|
3939
|
-
/** @constant */
|
|
3940
|
-
kind: "open-role";
|
|
3941
|
-
/** Format: uuid */
|
|
3942
|
-
openRoleId: string;
|
|
3943
|
-
};
|
|
3944
|
-
idempotencyKey: string;
|
|
3945
|
-
};
|
|
3946
3898
|
MovePreviewRequest: {
|
|
3947
3899
|
/** @constant */
|
|
3948
3900
|
kind: "reporting.setManager";
|
|
@@ -3962,6 +3914,12 @@ export interface components {
|
|
|
3962
3914
|
/** Format: uuid */
|
|
3963
3915
|
positionId: string;
|
|
3964
3916
|
title: string;
|
|
3917
|
+
} | {
|
|
3918
|
+
/** @constant */
|
|
3919
|
+
kind: "reporting.setManagerBulk";
|
|
3920
|
+
reportPositionIds: string[];
|
|
3921
|
+
/** Format: uuid */
|
|
3922
|
+
managerPositionId: string;
|
|
3965
3923
|
};
|
|
3966
3924
|
MoveApplyRequest: {
|
|
3967
3925
|
mutations: ({
|
|
@@ -7103,30 +7061,6 @@ export interface operations {
|
|
|
7103
7061
|
};
|
|
7104
7062
|
};
|
|
7105
7063
|
};
|
|
7106
|
-
bulkReparentReporting: {
|
|
7107
|
-
parameters: {
|
|
7108
|
-
query?: never;
|
|
7109
|
-
header?: never;
|
|
7110
|
-
path?: never;
|
|
7111
|
-
cookie?: never;
|
|
7112
|
-
};
|
|
7113
|
-
requestBody: {
|
|
7114
|
-
content: {
|
|
7115
|
-
"application/json": components["schemas"]["BulkReparentReportingRequest"];
|
|
7116
|
-
};
|
|
7117
|
-
};
|
|
7118
|
-
responses: {
|
|
7119
|
-
/** @description Cluster reorg applied atomically */
|
|
7120
|
-
200: {
|
|
7121
|
-
headers: {
|
|
7122
|
-
[name: string]: unknown;
|
|
7123
|
-
};
|
|
7124
|
-
content: {
|
|
7125
|
-
"application/json": components["schemas"]["BulkReparentReportingResponse"];
|
|
7126
|
-
};
|
|
7127
|
-
};
|
|
7128
|
-
};
|
|
7129
|
-
};
|
|
7130
7064
|
previewReportingMove: {
|
|
7131
7065
|
parameters: {
|
|
7132
7066
|
query?: never;
|
|
@@ -62,6 +62,24 @@ describe("EMAIL_KINDS golden snapshot", () => {
|
|
|
62
62
|
plainTextRequired: true,
|
|
63
63
|
htmlSupported: true,
|
|
64
64
|
},
|
|
65
|
+
"companyMd.access_requested": {
|
|
66
|
+
kind: "companyMd.access_requested",
|
|
67
|
+
subject: "Someone requested access to a document",
|
|
68
|
+
plainTextRequired: true,
|
|
69
|
+
htmlSupported: true,
|
|
70
|
+
},
|
|
71
|
+
"companyMd.access_request_approved": {
|
|
72
|
+
kind: "companyMd.access_request_approved",
|
|
73
|
+
subject: "Your access request was approved",
|
|
74
|
+
plainTextRequired: true,
|
|
75
|
+
htmlSupported: true,
|
|
76
|
+
},
|
|
77
|
+
"companyMd.access_request_denied": {
|
|
78
|
+
kind: "companyMd.access_request_denied",
|
|
79
|
+
subject: "Your access request was reviewed",
|
|
80
|
+
plainTextRequired: true,
|
|
81
|
+
htmlSupported: true,
|
|
82
|
+
},
|
|
65
83
|
});
|
|
66
84
|
});
|
|
67
85
|
});
|
|
@@ -125,7 +125,6 @@ export const openApiRoutes = {
|
|
|
125
125
|
'/api/orgs/{orgId}/budget-config': ['GET', 'PUT'],
|
|
126
126
|
'/api/orgs/{orgId}/level-config': ['GET', 'PUT'],
|
|
127
127
|
'/api/rbac/roles': ['GET'],
|
|
128
|
-
'/api/reporting/bulk-reparent': ['POST'],
|
|
129
128
|
'/api/reporting/move/apply': ['POST'],
|
|
130
129
|
'/api/reporting/move/preview': ['POST'],
|
|
131
130
|
'/api/scope/check': ['GET'],
|
|
@@ -106,6 +106,55 @@ describe("OrgIntentSchema", () => {
|
|
|
106
106
|
}).success,
|
|
107
107
|
).toBe(false);
|
|
108
108
|
});
|
|
109
|
+
|
|
110
|
+
it("accepts a reporting.setManagerBulk intent (N seats -> one manager)", () => {
|
|
111
|
+
const intent = {
|
|
112
|
+
kind: "reporting.setManagerBulk" as const,
|
|
113
|
+
reportPositionIds: [REPORT, POSITION],
|
|
114
|
+
managerPositionId: MANAGER,
|
|
115
|
+
};
|
|
116
|
+
expect(OrgIntentSchema.parse(intent)).toEqual(intent);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("rejects an empty bulk reportPositionIds array", () => {
|
|
120
|
+
expect(
|
|
121
|
+
OrgIntentSchema.safeParse({
|
|
122
|
+
kind: "reporting.setManagerBulk",
|
|
123
|
+
reportPositionIds: [],
|
|
124
|
+
managerPositionId: MANAGER,
|
|
125
|
+
}).success,
|
|
126
|
+
).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("rejects a bulk array beyond the 500-seat transport cap", () => {
|
|
130
|
+
expect(
|
|
131
|
+
OrgIntentSchema.safeParse({
|
|
132
|
+
kind: "reporting.setManagerBulk",
|
|
133
|
+
reportPositionIds: Array.from({ length: 501 }, () => REPORT),
|
|
134
|
+
managerPositionId: MANAGER,
|
|
135
|
+
}).success,
|
|
136
|
+
).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("rejects a non-uuid entry in the bulk report set", () => {
|
|
140
|
+
expect(
|
|
141
|
+
OrgIntentSchema.safeParse({
|
|
142
|
+
kind: "reporting.setManagerBulk",
|
|
143
|
+
reportPositionIds: [REPORT, "x"],
|
|
144
|
+
managerPositionId: MANAGER,
|
|
145
|
+
}).success,
|
|
146
|
+
).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("rejects a null bulk managerPositionId (intents always set a manager)", () => {
|
|
150
|
+
expect(
|
|
151
|
+
OrgIntentSchema.safeParse({
|
|
152
|
+
kind: "reporting.setManagerBulk",
|
|
153
|
+
reportPositionIds: [REPORT],
|
|
154
|
+
managerPositionId: null,
|
|
155
|
+
}).success,
|
|
156
|
+
).toBe(false);
|
|
157
|
+
});
|
|
109
158
|
});
|
|
110
159
|
|
|
111
160
|
describe("FactMutationSchema", () => {
|
|
@@ -436,4 +485,23 @@ describe("TransformationPreviewSchema", () => {
|
|
|
436
485
|
false,
|
|
437
486
|
);
|
|
438
487
|
});
|
|
488
|
+
|
|
489
|
+
it("accepts a multi-headline preview carrying primaryMutations (bulk intent)", () => {
|
|
490
|
+
const second = { ...setManagerMutation, reportPositionId: POSITION };
|
|
491
|
+
expect(
|
|
492
|
+
TransformationPreviewSchema.safeParse({
|
|
493
|
+
...preview,
|
|
494
|
+
primaryMutations: [setManagerMutation, second],
|
|
495
|
+
}).success,
|
|
496
|
+
).toBe(true);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it("rejects a single-element primaryMutations (single moves omit the field)", () => {
|
|
500
|
+
expect(
|
|
501
|
+
TransformationPreviewSchema.safeParse({
|
|
502
|
+
...preview,
|
|
503
|
+
primaryMutations: [setManagerMutation],
|
|
504
|
+
}).success,
|
|
505
|
+
).toBe(false);
|
|
506
|
+
});
|
|
439
507
|
});
|
|
@@ -95,6 +95,25 @@ export const OrgIntentSchema = z.discriminatedUnion("kind", [
|
|
|
95
95
|
/** The new human-facing title for the seat (non-empty, matching PositionSchema.title). */
|
|
96
96
|
title: z.string().min(1),
|
|
97
97
|
}),
|
|
98
|
+
// reporting.setManagerBulk — N reporting seats re-pointed at ONE manager as a
|
|
99
|
+
// single atomic intent (the org-chart cluster-pill drop). The engine expands
|
|
100
|
+
// it into N `reporting.setManager` FactMutations — the existing
|
|
101
|
+
// Intent-vs-Mutation asymmetry; no bulk mutation kind exists or is needed.
|
|
102
|
+
// Cross-field rules (manager ∉ reports, dedupe) are enforced at the route: a
|
|
103
|
+
// discriminatedUnion member cannot carry a ZodEffects refinement.
|
|
104
|
+
z.object({
|
|
105
|
+
kind: z.literal("reporting.setManagerBulk"),
|
|
106
|
+
/**
|
|
107
|
+
* The reporting seats being re-pointed together (e.g. a unit cluster's
|
|
108
|
+
* direct members; their subtrees follow implicitly). Order-insensitive;
|
|
109
|
+
* the engine treats the set as one atomic intent. The cap mirrors the
|
|
110
|
+
* bulk-reparent transport bound (ADR-BE-318).
|
|
111
|
+
*/
|
|
112
|
+
reportPositionIds: z.array(z.string().uuid()).min(1).max(500),
|
|
113
|
+
/** The ONE manager seat every report re-points to. Non-null on an intent
|
|
114
|
+
* (un-setting is a mutation concern, exactly as on `reporting.setManager`). */
|
|
115
|
+
managerPositionId: z.string().uuid(),
|
|
116
|
+
}),
|
|
98
117
|
]);
|
|
99
118
|
export type OrgIntent = z.infer<typeof OrgIntentSchema>;
|
|
100
119
|
|
|
@@ -336,6 +355,15 @@ export const TransformationPreviewSchema = z.object({
|
|
|
336
355
|
intentLabel: z.string().min(1),
|
|
337
356
|
/** The headline fact change the preview is centered on. */
|
|
338
357
|
primary: FactMutationSchema,
|
|
358
|
+
/**
|
|
359
|
+
* Present only when the intent expands into MORE THAN ONE headline mutation
|
|
360
|
+
* (e.g. `reporting.setManagerBulk`): the full primary bundle, of which
|
|
361
|
+
* `primary` is the first element. Absent on single-mutation previews so
|
|
362
|
+
* existing consumers and goldens see byte-identical output. These are the
|
|
363
|
+
* INTENT's own edges — engine repair splices stay in the strategies'
|
|
364
|
+
* recommendations, never here.
|
|
365
|
+
*/
|
|
366
|
+
primaryMutations: z.array(FactMutationSchema).min(2).optional(),
|
|
339
367
|
/** The strategy the engine recommends by default. */
|
|
340
368
|
defaultStrategy: TransformStrategySchema,
|
|
341
369
|
/** Other viable strategies the operator may pick instead. */
|
|
@@ -62,14 +62,29 @@ export const AclListResponseSchema = z.object({
|
|
|
62
62
|
entity_type: z.string(),
|
|
63
63
|
entity_id: z.string().uuid(),
|
|
64
64
|
owner_user_id: z.string().uuid().nullable(),
|
|
65
|
+
/** General-access scope: WHO is eligible (the discoverability axis). */
|
|
65
66
|
visibility: EntityVisibilitySchema,
|
|
67
|
+
/**
|
|
68
|
+
* The ROLE the general-access scope grants its audience: WHAT they may do (the
|
|
69
|
+
* capability axis, ADR-BE-375). Orthogonal to `visibility` — e.g. "Everyone in
|
|
70
|
+
* Product · Commenter". Defaults to `viewer` (the historical view-only band);
|
|
71
|
+
* ignored when `visibility` is `private` (no general grant).
|
|
72
|
+
*/
|
|
73
|
+
general_access_role: GrantableAccessLevelSchema.default("viewer"),
|
|
66
74
|
editors_can_share: z.boolean(),
|
|
67
75
|
grants: z.array(AclGrantResponseSchema),
|
|
68
76
|
});
|
|
69
77
|
export type AclListResponse = z.infer<typeof AclListResponseSchema>;
|
|
70
78
|
|
|
71
79
|
export const VisibilityPatchRequestSchema = z.object({
|
|
80
|
+
/** The general-access scope (WHO is eligible). */
|
|
72
81
|
tier: EntityVisibilitySchema,
|
|
82
|
+
/**
|
|
83
|
+
* The role granted to that scope (WHAT they may do, ADR-BE-375). Optional and
|
|
84
|
+
* backward-compatible: omitted preserves the current role (or `viewer` on first
|
|
85
|
+
* set); ignored when `tier` is `private`.
|
|
86
|
+
*/
|
|
87
|
+
role: GrantableAccessLevelSchema.optional(),
|
|
73
88
|
});
|
|
74
89
|
export type VisibilityPatchRequest = z.infer<
|
|
75
90
|
typeof VisibilityPatchRequestSchema
|