@shuffl/api-contracts 0.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/LICENSE +21 -0
- package/README.md +19 -0
- package/dist/base.d.ts +77 -0
- package/dist/base.js +92 -0
- package/dist/index.d.ts +14018 -0
- package/dist/index.js +329 -0
- package/dist/management-operations.d.ts +1 -0
- package/dist/management-operations.js +973 -0
- package/dist/management.generated.d.ts +11915 -0
- package/dist/management.generated.js +10400 -0
- package/dist/permissions.d.ts +548 -0
- package/dist/permissions.js +115 -0
- package/dist/schemas.d.ts +219 -0
- package/dist/schemas.js +143 -0
- package/openapi.json +148813 -0
- package/package.json +44 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { scopeSchema } from './base.js';
|
|
3
|
+
import { managementOperations } from './management-operations.js';
|
|
4
|
+
// Reviewed foundation operations; sdk:check compares these and the management
|
|
5
|
+
// registry with every actual public procedure so permissions cannot drift.
|
|
6
|
+
const foundation = [
|
|
7
|
+
['channels.list', 'programs:read', 'read', 'List available Slack channels'],
|
|
8
|
+
['commands.get', null, 'read', 'Read your own command receipt'],
|
|
9
|
+
[
|
|
10
|
+
'knowledge.context',
|
|
11
|
+
'knowledge:read',
|
|
12
|
+
'read',
|
|
13
|
+
'Read nearby approved passages',
|
|
14
|
+
],
|
|
15
|
+
['knowledge.search', 'knowledge:read', 'read', 'Search approved knowledge'],
|
|
16
|
+
['manager.current', 'manager:read', 'read', 'Read your own manager context'],
|
|
17
|
+
[
|
|
18
|
+
'permissions.list',
|
|
19
|
+
null,
|
|
20
|
+
'read',
|
|
21
|
+
'Discover available and granted permissions',
|
|
22
|
+
],
|
|
23
|
+
['programs.archive', 'programs:write', 'write', 'Archive a program'],
|
|
24
|
+
['programs.create', 'programs:write', 'write', 'Create a program'],
|
|
25
|
+
['programs.get', 'programs:read', 'read', 'Read a program'],
|
|
26
|
+
['programs.list', 'programs:read', 'read', 'List programs'],
|
|
27
|
+
['programs.options', 'programs:read', 'read', 'List program setting choices'],
|
|
28
|
+
['programs.pause', 'programs:write', 'write', 'Pause a program'],
|
|
29
|
+
[
|
|
30
|
+
'programs.preview',
|
|
31
|
+
'programs:read',
|
|
32
|
+
'read',
|
|
33
|
+
'Preview matches without sending',
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
'programs.resume',
|
|
37
|
+
'programs:write',
|
|
38
|
+
'write',
|
|
39
|
+
'Activate a program and schedule',
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
'programs.syncRoster',
|
|
43
|
+
'programs:write',
|
|
44
|
+
'write',
|
|
45
|
+
'Refresh channel membership',
|
|
46
|
+
],
|
|
47
|
+
['programs.update', 'programs:write', 'write', 'Update a program'],
|
|
48
|
+
['runs.get', 'runs:read', 'read', 'Read a run status'],
|
|
49
|
+
[
|
|
50
|
+
'runs.request',
|
|
51
|
+
'runs:write',
|
|
52
|
+
'write',
|
|
53
|
+
'Request introductions from a preview',
|
|
54
|
+
],
|
|
55
|
+
['users.get', 'users:read', 'read', 'Read a person'],
|
|
56
|
+
['users.list', 'users:read', 'read', 'List people'],
|
|
57
|
+
['workspaces.current', null, 'read', 'Read this token’s workspace'],
|
|
58
|
+
];
|
|
59
|
+
// Permissions checked by another Shuffl protocol rather than a public HTTP
|
|
60
|
+
// procedure (#187): one API key reaches the MCP server only when it carries
|
|
61
|
+
// this grant. There its other permissions are its action tools (#662).
|
|
62
|
+
const protocols = [
|
|
63
|
+
['mcp.knowledge', 'knowledge:read', 'read', 'Connect an agent over MCP'],
|
|
64
|
+
];
|
|
65
|
+
export const protocolPermissionIds = protocols.map(([id]) => id);
|
|
66
|
+
export const alwaysAllowedPermissions = [
|
|
67
|
+
'permissions.list',
|
|
68
|
+
'workspaces.current',
|
|
69
|
+
];
|
|
70
|
+
function definition(id, scope, access, description) {
|
|
71
|
+
return {
|
|
72
|
+
access,
|
|
73
|
+
alwaysAllowed: alwaysAllowedPermissions.includes(id),
|
|
74
|
+
description: description ??
|
|
75
|
+
id
|
|
76
|
+
.slice(id.lastIndexOf('.') + 1)
|
|
77
|
+
.replace(/([A-Z])/g, ' $1')
|
|
78
|
+
.replace(/^./, (s) => s.toUpperCase()),
|
|
79
|
+
id,
|
|
80
|
+
resource: id.slice(0, id.lastIndexOf('.')),
|
|
81
|
+
scope,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export const permissionCatalog = [
|
|
85
|
+
...[...foundation, ...protocols].map(([id, scope, access, description]) => definition(id, scope, access, description)),
|
|
86
|
+
...managementOperations.map(([id, , scope, access, description]) => definition(id, scope, access, description)),
|
|
87
|
+
].sort((a, b) => a.resource.localeCompare(b.resource) ||
|
|
88
|
+
a.access.localeCompare(b.access) ||
|
|
89
|
+
a.id.localeCompare(b.id));
|
|
90
|
+
export const permissionIds = permissionCatalog.map((p) => p.id);
|
|
91
|
+
export const permissionSchema = z.enum(permissionIds);
|
|
92
|
+
export const permissionDefinitionSchema = z.object({
|
|
93
|
+
access: z.enum(['read', 'write']),
|
|
94
|
+
alwaysAllowed: z.boolean(),
|
|
95
|
+
description: z.string(),
|
|
96
|
+
id: permissionSchema,
|
|
97
|
+
resource: z.string(),
|
|
98
|
+
scope: scopeSchema.nullable(),
|
|
99
|
+
});
|
|
100
|
+
export const permissionById = new Map(permissionCatalog.map((p) => [p.id, p]));
|
|
101
|
+
export function scopesForPermissions(permissions) {
|
|
102
|
+
return [
|
|
103
|
+
...new Set(permissions
|
|
104
|
+
.map((id) => permissionById.get(id).scope)
|
|
105
|
+
.filter((scope) => scope !== null)),
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
export function grantedPermissions(grant) {
|
|
109
|
+
return [
|
|
110
|
+
...new Set([...alwaysAllowedPermissions, ...(grant.permissions ?? [])]),
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
export function permitsOperation(grant, operation) {
|
|
114
|
+
return grantedPermissions(grant).some((id) => id === operation);
|
|
115
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const strategySchema: z.ZodEnum<{
|
|
3
|
+
"cross-unit": "cross-unit";
|
|
4
|
+
"group-frequency-weighted": "group-frequency-weighted";
|
|
5
|
+
"group-frequency-weighted-v2": "group-frequency-weighted-v2";
|
|
6
|
+
hosted: "hosted";
|
|
7
|
+
"new-connections": "new-connections";
|
|
8
|
+
random: "random";
|
|
9
|
+
"unit-lottery-weighted": "unit-lottery-weighted";
|
|
10
|
+
"unit-weighted": "unit-weighted";
|
|
11
|
+
"unit-weighted-multiple-hosts-per-group": "unit-weighted-multiple-hosts-per-group";
|
|
12
|
+
"unit-weighted-one-host-per-group": "unit-weighted-one-host-per-group";
|
|
13
|
+
"within-unit": "within-unit";
|
|
14
|
+
}>;
|
|
15
|
+
export declare const scheduleSchema: z.ZodObject<{
|
|
16
|
+
anchor: z.ZodString;
|
|
17
|
+
frequency: z.ZodEnum<{
|
|
18
|
+
days: "days";
|
|
19
|
+
hours: "hours";
|
|
20
|
+
minutes: "minutes";
|
|
21
|
+
"monthly-date": "monthly-date";
|
|
22
|
+
"monthly-last-weekday": "monthly-last-weekday";
|
|
23
|
+
"monthly-weekday": "monthly-weekday";
|
|
24
|
+
weeks: "weeks";
|
|
25
|
+
}>;
|
|
26
|
+
interval: z.ZodNumber;
|
|
27
|
+
timezone: z.ZodString;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
export declare const keepApartRelationshipSchema: z.ZodEnum<{
|
|
30
|
+
department: "department";
|
|
31
|
+
"know-each-other": "know-each-other";
|
|
32
|
+
"reporting-line": "reporting-line";
|
|
33
|
+
team: "team";
|
|
34
|
+
}>;
|
|
35
|
+
export declare const keepApartSchema: z.ZodObject<{
|
|
36
|
+
relationships: z.ZodArray<z.ZodEnum<{
|
|
37
|
+
department: "department";
|
|
38
|
+
"know-each-other": "know-each-other";
|
|
39
|
+
"reporting-line": "reporting-line";
|
|
40
|
+
team: "team";
|
|
41
|
+
}>>;
|
|
42
|
+
strict: z.ZodBoolean;
|
|
43
|
+
}, z.core.$strict>;
|
|
44
|
+
export declare const currentStrategies: string[];
|
|
45
|
+
export declare const fineTuneSchema: z.ZodObject<{
|
|
46
|
+
commonGround: z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>]>;
|
|
47
|
+
meetRequests: z.ZodBoolean;
|
|
48
|
+
mentionCommonGround: z.ZodBoolean;
|
|
49
|
+
reach: z.ZodEnum<{
|
|
50
|
+
any: "any";
|
|
51
|
+
far: "far";
|
|
52
|
+
fof: "fof";
|
|
53
|
+
}>;
|
|
54
|
+
}, z.core.$strict>;
|
|
55
|
+
export declare const matchSchema: z.ZodObject<{
|
|
56
|
+
fineTune: z.ZodOptional<z.ZodObject<{
|
|
57
|
+
commonGround: z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>]>;
|
|
58
|
+
meetRequests: z.ZodBoolean;
|
|
59
|
+
mentionCommonGround: z.ZodBoolean;
|
|
60
|
+
reach: z.ZodEnum<{
|
|
61
|
+
any: "any";
|
|
62
|
+
far: "far";
|
|
63
|
+
fof: "fof";
|
|
64
|
+
}>;
|
|
65
|
+
}, z.core.$strict>>;
|
|
66
|
+
groupSize: z.ZodNumber;
|
|
67
|
+
iterations: z.ZodNumber;
|
|
68
|
+
keepApart: z.ZodOptional<z.ZodObject<{
|
|
69
|
+
relationships: z.ZodArray<z.ZodEnum<{
|
|
70
|
+
department: "department";
|
|
71
|
+
"know-each-other": "know-each-other";
|
|
72
|
+
"reporting-line": "reporting-line";
|
|
73
|
+
team: "team";
|
|
74
|
+
}>>;
|
|
75
|
+
strict: z.ZodBoolean;
|
|
76
|
+
}, z.core.$strict>>;
|
|
77
|
+
maxGroupSize: z.ZodNumber;
|
|
78
|
+
strategy: z.ZodEnum<{
|
|
79
|
+
"cross-unit": "cross-unit";
|
|
80
|
+
"group-frequency-weighted": "group-frequency-weighted";
|
|
81
|
+
"group-frequency-weighted-v2": "group-frequency-weighted-v2";
|
|
82
|
+
hosted: "hosted";
|
|
83
|
+
"new-connections": "new-connections";
|
|
84
|
+
random: "random";
|
|
85
|
+
"unit-lottery-weighted": "unit-lottery-weighted";
|
|
86
|
+
"unit-weighted": "unit-weighted";
|
|
87
|
+
"unit-weighted-multiple-hosts-per-group": "unit-weighted-multiple-hosts-per-group";
|
|
88
|
+
"unit-weighted-one-host-per-group": "unit-weighted-one-host-per-group";
|
|
89
|
+
"within-unit": "within-unit";
|
|
90
|
+
}>;
|
|
91
|
+
unitIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
92
|
+
unitKind: z.ZodOptional<z.ZodEnum<{
|
|
93
|
+
department: "department";
|
|
94
|
+
team: "team";
|
|
95
|
+
}>>;
|
|
96
|
+
}, z.core.$strict>;
|
|
97
|
+
export declare const programInput: z.ZodObject<{
|
|
98
|
+
channelId: z.ZodString;
|
|
99
|
+
channelName: z.ZodString;
|
|
100
|
+
includeBios: z.ZodBoolean;
|
|
101
|
+
intro: z.ZodString;
|
|
102
|
+
match: z.ZodObject<{
|
|
103
|
+
fineTune: z.ZodOptional<z.ZodObject<{
|
|
104
|
+
commonGround: z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>]>;
|
|
105
|
+
meetRequests: z.ZodBoolean;
|
|
106
|
+
mentionCommonGround: z.ZodBoolean;
|
|
107
|
+
reach: z.ZodEnum<{
|
|
108
|
+
any: "any";
|
|
109
|
+
far: "far";
|
|
110
|
+
fof: "fof";
|
|
111
|
+
}>;
|
|
112
|
+
}, z.core.$strict>>;
|
|
113
|
+
groupSize: z.ZodNumber;
|
|
114
|
+
iterations: z.ZodNumber;
|
|
115
|
+
keepApart: z.ZodOptional<z.ZodObject<{
|
|
116
|
+
relationships: z.ZodArray<z.ZodEnum<{
|
|
117
|
+
department: "department";
|
|
118
|
+
"know-each-other": "know-each-other";
|
|
119
|
+
"reporting-line": "reporting-line";
|
|
120
|
+
team: "team";
|
|
121
|
+
}>>;
|
|
122
|
+
strict: z.ZodBoolean;
|
|
123
|
+
}, z.core.$strict>>;
|
|
124
|
+
maxGroupSize: z.ZodNumber;
|
|
125
|
+
strategy: z.ZodEnum<{
|
|
126
|
+
"cross-unit": "cross-unit";
|
|
127
|
+
"group-frequency-weighted": "group-frequency-weighted";
|
|
128
|
+
"group-frequency-weighted-v2": "group-frequency-weighted-v2";
|
|
129
|
+
hosted: "hosted";
|
|
130
|
+
"new-connections": "new-connections";
|
|
131
|
+
random: "random";
|
|
132
|
+
"unit-lottery-weighted": "unit-lottery-weighted";
|
|
133
|
+
"unit-weighted": "unit-weighted";
|
|
134
|
+
"unit-weighted-multiple-hosts-per-group": "unit-weighted-multiple-hosts-per-group";
|
|
135
|
+
"unit-weighted-one-host-per-group": "unit-weighted-one-host-per-group";
|
|
136
|
+
"within-unit": "within-unit";
|
|
137
|
+
}>;
|
|
138
|
+
unitIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
139
|
+
unitKind: z.ZodOptional<z.ZodEnum<{
|
|
140
|
+
department: "department";
|
|
141
|
+
team: "team";
|
|
142
|
+
}>>;
|
|
143
|
+
}, z.core.$strict>;
|
|
144
|
+
name: z.ZodString;
|
|
145
|
+
schedule: z.ZodObject<{
|
|
146
|
+
anchor: z.ZodString;
|
|
147
|
+
frequency: z.ZodEnum<{
|
|
148
|
+
days: "days";
|
|
149
|
+
hours: "hours";
|
|
150
|
+
minutes: "minutes";
|
|
151
|
+
"monthly-date": "monthly-date";
|
|
152
|
+
"monthly-last-weekday": "monthly-last-weekday";
|
|
153
|
+
"monthly-weekday": "monthly-weekday";
|
|
154
|
+
weeks: "weeks";
|
|
155
|
+
}>;
|
|
156
|
+
interval: z.ZodNumber;
|
|
157
|
+
timezone: z.ZodString;
|
|
158
|
+
}, z.core.$strip>;
|
|
159
|
+
}, z.core.$strip>;
|
|
160
|
+
export declare const profileSchema: z.ZodObject<{
|
|
161
|
+
asks: z.ZodOptional<z.ZodString>;
|
|
162
|
+
bio: z.ZodOptional<z.ZodString>;
|
|
163
|
+
interests: z.ZodOptional<z.ZodString>;
|
|
164
|
+
location: z.ZodOptional<z.ZodString>;
|
|
165
|
+
offers: z.ZodOptional<z.ZodString>;
|
|
166
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
167
|
+
pronouns: z.ZodOptional<z.ZodString>;
|
|
168
|
+
role: z.ZodOptional<z.ZodString>;
|
|
169
|
+
}, z.core.$strip>;
|
|
170
|
+
export declare const programPatch: z.ZodObject<{
|
|
171
|
+
channelId: z.ZodOptional<z.ZodString>;
|
|
172
|
+
channelName: z.ZodOptional<z.ZodString>;
|
|
173
|
+
includeBios: z.ZodOptional<z.ZodBoolean>;
|
|
174
|
+
intro: z.ZodOptional<z.ZodString>;
|
|
175
|
+
match: z.ZodOptional<z.ZodObject<{
|
|
176
|
+
fineTune: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
177
|
+
commonGround: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>]>>;
|
|
178
|
+
meetRequests: z.ZodOptional<z.ZodBoolean>;
|
|
179
|
+
mentionCommonGround: z.ZodOptional<z.ZodBoolean>;
|
|
180
|
+
reach: z.ZodOptional<z.ZodEnum<{
|
|
181
|
+
any: "any";
|
|
182
|
+
far: "far";
|
|
183
|
+
fof: "fof";
|
|
184
|
+
}>>;
|
|
185
|
+
}, z.core.$strict>>>;
|
|
186
|
+
groupSize: z.ZodOptional<z.ZodNumber>;
|
|
187
|
+
iterations: z.ZodOptional<z.ZodNumber>;
|
|
188
|
+
keepApart: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
189
|
+
relationships: z.ZodArray<z.ZodEnum<{
|
|
190
|
+
department: "department";
|
|
191
|
+
"know-each-other": "know-each-other";
|
|
192
|
+
"reporting-line": "reporting-line";
|
|
193
|
+
team: "team";
|
|
194
|
+
}>>;
|
|
195
|
+
strict: z.ZodBoolean;
|
|
196
|
+
}, z.core.$strict>>>;
|
|
197
|
+
maxGroupSize: z.ZodOptional<z.ZodNumber>;
|
|
198
|
+
strategy: z.ZodOptional<z.ZodEnum<{
|
|
199
|
+
"cross-unit": "cross-unit";
|
|
200
|
+
"group-frequency-weighted": "group-frequency-weighted";
|
|
201
|
+
"group-frequency-weighted-v2": "group-frequency-weighted-v2";
|
|
202
|
+
hosted: "hosted";
|
|
203
|
+
"new-connections": "new-connections";
|
|
204
|
+
random: "random";
|
|
205
|
+
"unit-lottery-weighted": "unit-lottery-weighted";
|
|
206
|
+
"unit-weighted": "unit-weighted";
|
|
207
|
+
"unit-weighted-multiple-hosts-per-group": "unit-weighted-multiple-hosts-per-group";
|
|
208
|
+
"unit-weighted-one-host-per-group": "unit-weighted-one-host-per-group";
|
|
209
|
+
"within-unit": "within-unit";
|
|
210
|
+
}>>;
|
|
211
|
+
unitIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
212
|
+
unitKind: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
213
|
+
department: "department";
|
|
214
|
+
team: "team";
|
|
215
|
+
}>>>;
|
|
216
|
+
}, z.core.$strict>>;
|
|
217
|
+
name: z.ZodOptional<z.ZodString>;
|
|
218
|
+
schedule: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
219
|
+
}, z.core.$strict>;
|
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Temporal } from '@js-temporal/polyfill';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export const strategySchema = z.enum([
|
|
4
|
+
'new-connections',
|
|
5
|
+
'cross-unit',
|
|
6
|
+
'within-unit',
|
|
7
|
+
'hosted',
|
|
8
|
+
// Legacy strategies keep their existing behavior for saved programs.
|
|
9
|
+
'random',
|
|
10
|
+
'group-frequency-weighted',
|
|
11
|
+
'group-frequency-weighted-v2',
|
|
12
|
+
'unit-weighted',
|
|
13
|
+
'unit-weighted-one-host-per-group',
|
|
14
|
+
'unit-weighted-multiple-hosts-per-group',
|
|
15
|
+
'unit-lottery-weighted',
|
|
16
|
+
]);
|
|
17
|
+
export const scheduleSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
anchor: z.string().max(100),
|
|
20
|
+
frequency: z.enum([
|
|
21
|
+
'minutes',
|
|
22
|
+
'hours',
|
|
23
|
+
'days',
|
|
24
|
+
'weeks',
|
|
25
|
+
'monthly-date',
|
|
26
|
+
'monthly-weekday',
|
|
27
|
+
'monthly-last-weekday',
|
|
28
|
+
]),
|
|
29
|
+
interval: z.number().int().min(1).max(366),
|
|
30
|
+
timezone: z.string().max(100),
|
|
31
|
+
})
|
|
32
|
+
.superRefine((rule, ctx) => {
|
|
33
|
+
try {
|
|
34
|
+
Temporal.PlainDateTime.from(rule.anchor).toZonedDateTime(rule.timezone);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
ctx.addIssue({
|
|
38
|
+
code: 'custom',
|
|
39
|
+
message: 'Enter a valid local date, time and IANA timezone',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
export const keepApartRelationshipSchema = z.enum([
|
|
44
|
+
'reporting-line',
|
|
45
|
+
'team',
|
|
46
|
+
'department',
|
|
47
|
+
'know-each-other',
|
|
48
|
+
]);
|
|
49
|
+
export const keepApartSchema = z
|
|
50
|
+
.strictObject({
|
|
51
|
+
relationships: z.array(keepApartRelationshipSchema).min(1).max(4),
|
|
52
|
+
strict: z.boolean(),
|
|
53
|
+
})
|
|
54
|
+
.refine((x) => new Set(x.relationships).size === x.relationships.length, 'Choose each relationship only once');
|
|
55
|
+
// The matching purposes the editor offers today; the rest are legacy.
|
|
56
|
+
export const currentStrategies = [
|
|
57
|
+
'new-connections',
|
|
58
|
+
'cross-unit',
|
|
59
|
+
'within-unit',
|
|
60
|
+
'hosted',
|
|
61
|
+
];
|
|
62
|
+
export const fineTuneSchema = z.strictObject({
|
|
63
|
+
commonGround: z
|
|
64
|
+
.union([z.literal(0), z.literal(1), z.literal(2)])
|
|
65
|
+
.describe('Shared interests and "can help with" / "looking for" topics pick between people who are equally new to each other. 0 off, 1 prefer, 2 strongly prefer.'),
|
|
66
|
+
meetRequests: z
|
|
67
|
+
.boolean()
|
|
68
|
+
.describe('Group someone with a person they privately asked to meet, at most once per person per round. Nobody, including admins, is told who asked.'),
|
|
69
|
+
mentionCommonGround: z
|
|
70
|
+
.boolean()
|
|
71
|
+
.describe('Name a topic both people show publicly on their profile in the introduction.'),
|
|
72
|
+
reach: z
|
|
73
|
+
.enum(['any', 'fof', 'far'])
|
|
74
|
+
.describe('any: anyone new. fof: friends of friends first (people in common). far: far corners first (nobody in common).'),
|
|
75
|
+
});
|
|
76
|
+
export const matchSchema = z
|
|
77
|
+
.strictObject({
|
|
78
|
+
fineTune: fineTuneSchema.optional(),
|
|
79
|
+
groupSize: z.number().int().min(2).max(8),
|
|
80
|
+
iterations: z.number().int().min(1).max(50),
|
|
81
|
+
keepApart: keepApartSchema.optional(),
|
|
82
|
+
maxGroupSize: z.number().int().min(2).max(8),
|
|
83
|
+
strategy: strategySchema,
|
|
84
|
+
unitIds: z.array(z.string().trim().min(1).max(200)).max(20).optional(),
|
|
85
|
+
unitKind: z.enum(['department', 'team']).optional(),
|
|
86
|
+
})
|
|
87
|
+
.refine((x) => x.maxGroupSize >= x.groupSize, 'Maximum group size must be at least the target size')
|
|
88
|
+
.refine((x) => (!x.strategy.startsWith('unit-') && x.strategy !== 'hosted') ||
|
|
89
|
+
!!x.unitIds?.length, 'Choose at least one team or department')
|
|
90
|
+
.refine((x) => x.strategy.startsWith('unit-') ||
|
|
91
|
+
x.strategy === 'hosted' ||
|
|
92
|
+
!x.unitIds?.length, 'Host selections are only used for hosted introductions')
|
|
93
|
+
.refine((x) => !x.unitIds || new Set(x.unitIds).size === x.unitIds.length, 'Choose each team or department only once')
|
|
94
|
+
.refine((x) => x.strategy === 'cross-unit' || x.strategy === 'within-unit'
|
|
95
|
+
? !!x.unitKind
|
|
96
|
+
: x.unitKind === undefined, 'Choose teams or departments only when mixing across or within them')
|
|
97
|
+
.refine((x) => !currentStrategies.includes(x.strategy) ||
|
|
98
|
+
x.maxGroupSize <= x.groupSize + 1, 'Allow at most one extra person per group')
|
|
99
|
+
.refine((x) => !x.keepApart || currentStrategies.includes(x.strategy), 'Keeping coworkers apart needs a current matching purpose')
|
|
100
|
+
.refine((x) => !x.fineTune || currentStrategies.includes(x.strategy), 'Fine-tuning needs a current matching purpose');
|
|
101
|
+
export const programInput = z.object({
|
|
102
|
+
channelId: z.string().regex(/^[CG][A-Z0-9]+$/),
|
|
103
|
+
channelName: z.string().trim().min(1).max(100),
|
|
104
|
+
includeBios: z.boolean(),
|
|
105
|
+
intro: z.string().trim().min(1).max(2000),
|
|
106
|
+
match: matchSchema,
|
|
107
|
+
name: z.string().trim().min(1).max(120),
|
|
108
|
+
schedule: scheduleSchema,
|
|
109
|
+
});
|
|
110
|
+
export const profileSchema = z.object({
|
|
111
|
+
asks: z.string().max(500).optional(),
|
|
112
|
+
bio: z.string().max(1000).optional(),
|
|
113
|
+
interests: z.string().max(500).optional(),
|
|
114
|
+
location: z.string().max(200).optional(),
|
|
115
|
+
offers: z.string().max(500).optional(),
|
|
116
|
+
phone: z.string().max(40).optional(),
|
|
117
|
+
pronouns: z.string().max(40).optional(),
|
|
118
|
+
role: z.string().max(200).optional(),
|
|
119
|
+
});
|
|
120
|
+
// A partial program change for `programs.update` (#745): only the fields
|
|
121
|
+
// given change; `match`, its `fineTune` and `schedule` merge field by field.
|
|
122
|
+
// `keepApart: null` or `fineTune: null` removes that setting. The merged
|
|
123
|
+
// program is validated as a whole before it is saved.
|
|
124
|
+
export const programPatch = z.strictObject({
|
|
125
|
+
channelId: programInput.shape.channelId.optional(),
|
|
126
|
+
channelName: programInput.shape.channelName.optional(),
|
|
127
|
+
includeBios: z.boolean().optional(),
|
|
128
|
+
intro: programInput.shape.intro.optional(),
|
|
129
|
+
match: z
|
|
130
|
+
.strictObject({
|
|
131
|
+
fineTune: fineTuneSchema.partial().nullable().optional(),
|
|
132
|
+
groupSize: z.number().int().min(2).max(8).optional(),
|
|
133
|
+
iterations: z.number().int().min(1).max(50).optional(),
|
|
134
|
+
keepApart: keepApartSchema.nullable().optional(),
|
|
135
|
+
maxGroupSize: z.number().int().min(2).max(8).optional(),
|
|
136
|
+
strategy: strategySchema.optional(),
|
|
137
|
+
unitIds: z.array(z.string().trim().min(1).max(200)).max(20).optional(),
|
|
138
|
+
unitKind: z.enum(['department', 'team']).nullable().optional(),
|
|
139
|
+
})
|
|
140
|
+
.optional(),
|
|
141
|
+
name: programInput.shape.name.optional(),
|
|
142
|
+
schedule: z.record(z.string(), z.unknown()).optional(),
|
|
143
|
+
});
|