@shuffl/api-contracts 0.1.0 → 0.2.1
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/dist/base.d.ts +3 -5
- package/dist/base.js +6 -10
- package/dist/index.d.ts +9264 -3761
- package/dist/index.js +15 -15
- package/dist/management-operations.d.ts +1 -1
- package/dist/management-operations.js +600 -92
- package/dist/management.generated.d.ts +11091 -6140
- package/dist/management.generated.js +5823 -1017
- package/dist/permissions.d.ts +199 -51
- package/dist/permissions.js +177 -2
- package/dist/schemas.d.ts +164 -3
- package/dist/schemas.js +68 -3
- package/openapi.json +95542 -39946
- package/package.json +1 -1
package/dist/permissions.js
CHANGED
|
@@ -54,7 +54,7 @@ const foundation = [
|
|
|
54
54
|
],
|
|
55
55
|
['users.get', 'users:read', 'read', 'Read a person'],
|
|
56
56
|
['users.list', 'users:read', 'read', 'List people'],
|
|
57
|
-
['
|
|
57
|
+
['teams.current', null, 'read', 'Read this token’s team'],
|
|
58
58
|
];
|
|
59
59
|
// Permissions checked by another Shuffl protocol rather than a public HTTP
|
|
60
60
|
// procedure (#187): one API key reaches the MCP server only when it carries
|
|
@@ -65,13 +65,153 @@ const protocols = [
|
|
|
65
65
|
export const protocolPermissionIds = protocols.map(([id]) => id);
|
|
66
66
|
export const alwaysAllowedPermissions = [
|
|
67
67
|
'permissions.list',
|
|
68
|
-
'
|
|
68
|
+
'teams.current',
|
|
69
69
|
];
|
|
70
|
+
// What an operation does, for the ones whose registry entry carries no
|
|
71
|
+
// confirmation sentence (#1151). The API key picker, MCP tool list and
|
|
72
|
+
// Mello's findActions all read it; a derived "Save" or "Get" left the
|
|
73
|
+
// assistant unable to find a directory change by its words.
|
|
74
|
+
const operationDescriptions = {
|
|
75
|
+
'access.get': "Read one person's workspace role and app access",
|
|
76
|
+
'access.slackWaiting': 'List Slack members waiting for access to Shuffl',
|
|
77
|
+
'assistant.conversations': 'List your conversations with Mello',
|
|
78
|
+
'assistant.delete': 'Delete one of your conversations with Mello',
|
|
79
|
+
'assistant.history': 'Read the messages in one of your conversations with Mello',
|
|
80
|
+
'assistant.send': 'Ask Mello a question in a conversation',
|
|
81
|
+
'assistant.stop': 'Stop an answer Mello is still writing',
|
|
82
|
+
'badges.decideRequest': 'Approve or decline a request for a badge',
|
|
83
|
+
'badges.hideOwn': 'Hide or show one of your badges on your profile',
|
|
84
|
+
'badges.ofPerson': "List a person's badges",
|
|
85
|
+
'badges.request': 'Ask for a badge',
|
|
86
|
+
'badges.revoke': 'Take a badge back from a person',
|
|
87
|
+
'badges.setHidden': 'Hide or show a badge for the whole workspace',
|
|
88
|
+
'celebrations.deliveries': 'List the celebration messages Shuffl sent',
|
|
89
|
+
'celebrations.mine': 'List the celebrations you received',
|
|
90
|
+
'celebrations.preferences': 'Read your birthday and work anniversary sharing preferences',
|
|
91
|
+
'celebrations.preview': 'Preview a celebration message',
|
|
92
|
+
'celebrations.program': 'Read the Celebrations settings',
|
|
93
|
+
'celebrations.refresh': 'Look for new birthdays and work anniversaries to celebrate now',
|
|
94
|
+
'celebrations.review': 'Review upcoming birthdays and work anniversaries',
|
|
95
|
+
'celebrations.savePreferences': 'Save your birthday and work anniversary sharing preferences',
|
|
96
|
+
'communities.archive': 'Archive a community',
|
|
97
|
+
'communities.join': 'Join a community',
|
|
98
|
+
'communities.leave': 'Leave a community',
|
|
99
|
+
'communities.members': "List a community's members",
|
|
100
|
+
'communities.ofPerson': 'List the communities a person belongs to',
|
|
101
|
+
'communities.setVisibility': 'Show or hide a community on your profile',
|
|
102
|
+
'communities.sync': "Sync a community's members with its Slack channel now",
|
|
103
|
+
'credentials.api.list': "List the workspace's API keys and what each can do",
|
|
104
|
+
'credentials.api.revoke': 'Revoke an API key',
|
|
105
|
+
'exports.team': "Export the workspace's programs, people and introduction history",
|
|
106
|
+
'hr.command': 'Claim, release, reply to, note, categorize, resolve or reopen an HR request',
|
|
107
|
+
'hr.get': 'Read one HR request and its messages',
|
|
108
|
+
'hr.list': 'List the HR requests you asked or can answer',
|
|
109
|
+
'hr.readiness': 'Check whether HR requests have a destination and someone to answer them',
|
|
110
|
+
'hr.renameTeam': 'Rename the HR team employees send requests to',
|
|
111
|
+
'hr.settings': 'Read the HR team name and who answers HR requests',
|
|
112
|
+
'hr.shareSources': 'List your recent Mello conversations that an HR request can include',
|
|
113
|
+
'identities.list': 'List the Slack and web accounts linked to a person',
|
|
114
|
+
'identities.slackAccounts': 'List Slack accounts that can be linked to people',
|
|
115
|
+
'identities.webAccounts': 'List web accounts that can be linked to people',
|
|
116
|
+
'integrations.authorize': 'Get the link that connects Slack, Google Drive or Notion to the workspace',
|
|
117
|
+
'invitations.cancel': 'Cancel a pending workspace invitation',
|
|
118
|
+
'invitations.candidates': 'List people in the directory who can be invited to the workspace',
|
|
119
|
+
'invitations.list': 'List pending workspace invitations',
|
|
120
|
+
'knowledge.connections.list': 'List connected Knowledge providers',
|
|
121
|
+
'knowledge.connections.request': 'Ask an administrator to connect a Knowledge source',
|
|
122
|
+
'knowledge.connections.requests': 'List requests to connect a Knowledge source',
|
|
123
|
+
'knowledge.policies.coverage': 'Check which common HR policies Knowledge covers',
|
|
124
|
+
'knowledge.sources.get': 'Read a Knowledge source',
|
|
125
|
+
'knowledge.sources.history': "List a Knowledge source's versions",
|
|
126
|
+
'knowledge.sources.importWebsite': 'Import a website page into Knowledge for review',
|
|
127
|
+
'knowledge.sources.list': 'List Knowledge sources',
|
|
128
|
+
'knowledge.sources.refresh': 'Refresh a Knowledge source from its connected provider',
|
|
129
|
+
'knowledge.sources.save': 'Save a Knowledge source draft',
|
|
130
|
+
'knowledge.sources.search': 'Search Knowledge sources by title',
|
|
131
|
+
'knowledge.sources.withdraw': 'Withdraw a published Knowledge source',
|
|
132
|
+
'knowledge.uploads.appendPart': 'Upload the next part of a Knowledge file',
|
|
133
|
+
'knowledge.uploads.complete': 'Finish a Knowledge file upload and start reading it',
|
|
134
|
+
'knowledge.uploads.create': 'Start uploading a file to Knowledge',
|
|
135
|
+
'knowledge.uploads.get': 'Read the progress of a Knowledge file upload',
|
|
136
|
+
'knowledge.uploads.retry': 'Retry reading a Knowledge file that failed',
|
|
137
|
+
'notifications.preferences': 'Read your notification preferences',
|
|
138
|
+
'notifications.save': 'Save a notification preference',
|
|
139
|
+
'onboarding.answerCheckIn': 'Answer your onboarding program’s help check-in: all good, or ask HR for help',
|
|
140
|
+
'onboarding.assignBuddy': 'Assign an onboarding buddy to a new hire',
|
|
141
|
+
'onboarding.candidates': 'List people who could be enrolled in onboarding',
|
|
142
|
+
'onboarding.enrollmentPreview': 'Preview enrolling a new hire in onboarding',
|
|
143
|
+
'onboarding.mine': 'Read your own onboarding plan and buddy asks',
|
|
144
|
+
'onboarding.person': "Read a new hire's onboarding plan",
|
|
145
|
+
'onboarding.previewEnrollment': 'Preview the onboarding steps for a new hire',
|
|
146
|
+
'onboarding.program': 'Read the Onboarding program settings',
|
|
147
|
+
'onboarding.remove': 'Remove a new hire from onboarding',
|
|
148
|
+
'onboarding.respondBuddy': 'Accept or decline being an onboarding buddy',
|
|
149
|
+
'onboarding.retry': 'Retry a failed onboarding step',
|
|
150
|
+
'onboarding.update': "Change a new hire's onboarding start date or time zone",
|
|
151
|
+
'operations.activity': "Read the workspace's recent audit activity",
|
|
152
|
+
'operations.status': 'Read the workspace delivery and provider health summary',
|
|
153
|
+
'profiles.cover.delete': 'Remove your profile cover picture',
|
|
154
|
+
'profiles.photo.delete': 'Remove your profile photo',
|
|
155
|
+
'profiles.positions.delete': 'Remove a past position from your profile',
|
|
156
|
+
'profiles.positions.save': 'Add or change a past position on your profile',
|
|
157
|
+
'profiles.save': 'Save your own profile: pronouns, location, phone, bio, interests, links and what you are working on',
|
|
158
|
+
'programs.people': 'List the people in a program and whether they take part',
|
|
159
|
+
'programs.schedulePreview': "Preview a program's upcoming rounds",
|
|
160
|
+
'reports.answerFeedback': "Read feedback on Mello's answers",
|
|
161
|
+
'reports.connections': 'Read Connections insights',
|
|
162
|
+
'reports.knowledge': 'Read Knowledge activity',
|
|
163
|
+
'reports.knowledgeSource': 'Read activity for one Knowledge source',
|
|
164
|
+
'reports.needsYou': 'Read HR request and Knowledge draft insights',
|
|
165
|
+
'reports.onboarding': 'Read Onboarding insights',
|
|
166
|
+
'reports.overview': 'Read the workspace overview figures',
|
|
167
|
+
'reports.people': 'Read People insights: headcount, teams and growth',
|
|
168
|
+
'runs.details': 'Read one introduction round and its pairs',
|
|
169
|
+
'runs.history.get': 'Read one introduction round imported from the previous Shuffl',
|
|
170
|
+
'runs.history.list': 'List introduction rounds imported from the previous Shuffl',
|
|
171
|
+
'runs.list': "List a program's introduction rounds",
|
|
172
|
+
'support.assign': 'Assign a Shuffl support conversation',
|
|
173
|
+
'support.close': 'Close a Shuffl support conversation',
|
|
174
|
+
'support.create': 'Start a support conversation with Shuffl',
|
|
175
|
+
'support.get': 'Read one Shuffl support conversation',
|
|
176
|
+
'support.list': "List the workspace's support conversations with Shuffl",
|
|
177
|
+
'support.reply': 'Reply in a Shuffl support conversation',
|
|
178
|
+
'sync.current': 'Read the People sync provider, its last sync and what it found',
|
|
179
|
+
'sync.request': 'Ask for a People sync provider Shuffl does not support yet',
|
|
180
|
+
'sync.run': 'Sync People from the connected provider now',
|
|
181
|
+
'team.launchPost': 'Read the launch announcement for the workspace',
|
|
182
|
+
'teams.connectionPermissions': 'List connections whose permissions are behind what Shuffl asks for',
|
|
183
|
+
'teams.initialize': 'Finish setting up a new workspace',
|
|
184
|
+
'teams.rename': 'Rename the workspace',
|
|
185
|
+
'teams.saveAgentSetup': 'Choose the built-in assistant or an external agent for the workspace',
|
|
186
|
+
'teams.saveSlackJoinAccess': 'Choose whether Slack members can join Shuffl on their own',
|
|
187
|
+
'teams.saveSlackProfileLink': 'Turn the Shuffl profile link on Slack profiles on or off',
|
|
188
|
+
'teams.setup': "Read the workspace's setup checklist",
|
|
189
|
+
'teams.slackChannels': 'List community channels in the connected Slack and what each would become',
|
|
190
|
+
'teams.slackJoinAccess': 'Read whether Slack members can join Shuffl on their own',
|
|
191
|
+
'teams.slackProfileLink': 'Read the Shuffl profile link setting for Slack profiles',
|
|
192
|
+
'teams.status': 'Pause or resume the whole workspace',
|
|
193
|
+
'units.archive': 'Archive a team or department in the People directory',
|
|
194
|
+
'units.list': 'List the teams and departments in the People directory',
|
|
195
|
+
'units.save': 'Create or rename a team or department in the People directory',
|
|
196
|
+
'users.activity': "Read a person's recent activity in Shuffl",
|
|
197
|
+
'users.blockedPairs': 'List pairs of people who are never introduced',
|
|
198
|
+
'users.blockPair': 'Stop two people from being introduced to each other',
|
|
199
|
+
'users.details': "Read one person's People directory record",
|
|
200
|
+
'users.filterOptions': 'List the teams, departments, managers and locations People can filter by',
|
|
201
|
+
'users.managerHistory': "Read a person's recorded manager history",
|
|
202
|
+
'users.managers': 'List the managers in the People directory',
|
|
203
|
+
'users.page': 'List people in the People directory: search, or filter by team, department, manager, location, access or workspace role such as owner, admin or HR responder',
|
|
204
|
+
'users.participants': 'List the people who take part in programs',
|
|
205
|
+
'users.participation': 'Opt a person in to or out of a program',
|
|
206
|
+
'users.profile': "Edit a person's profile as an administrator",
|
|
207
|
+
'users.save': "Change someone's People directory record as an administrator: name, job title, manager, team, department or start date",
|
|
208
|
+
};
|
|
70
209
|
function definition(id, scope, access, description) {
|
|
71
210
|
return {
|
|
72
211
|
access,
|
|
73
212
|
alwaysAllowed: alwaysAllowedPermissions.includes(id),
|
|
74
213
|
description: description ??
|
|
214
|
+
operationDescriptions[id] ??
|
|
75
215
|
id
|
|
76
216
|
.slice(id.lastIndexOf('.') + 1)
|
|
77
217
|
.replace(/([A-Z])/g, ' $1')
|
|
@@ -98,6 +238,41 @@ export const permissionDefinitionSchema = z.object({
|
|
|
98
238
|
scope: scopeSchema.nullable(),
|
|
99
239
|
});
|
|
100
240
|
export const permissionById = new Map(permissionCatalog.map((p) => [p.id, p]));
|
|
241
|
+
// #1082: what an outside agent needs to set a team up end to end. The
|
|
242
|
+
// MCP consent screen offers these as one choice, and the agent setup guide
|
|
243
|
+
// lists them. Each still runs with the connected person's role, and sending,
|
|
244
|
+
// publishing or inviting is marked so the agent asks first.
|
|
245
|
+
export const teamSetupPermissions = [
|
|
246
|
+
'teams.setup',
|
|
247
|
+
'teams.saveAgentSetup',
|
|
248
|
+
'teams.slackChannels',
|
|
249
|
+
'teams.moveSlackChannels',
|
|
250
|
+
'knowledge.uploads.create',
|
|
251
|
+
'knowledge.uploads.appendPart',
|
|
252
|
+
'knowledge.uploads.complete',
|
|
253
|
+
'knowledge.uploads.get',
|
|
254
|
+
'knowledge.sources.importWebsite',
|
|
255
|
+
'knowledge.sources.list',
|
|
256
|
+
'knowledge.sources.get',
|
|
257
|
+
'knowledge.sources.write',
|
|
258
|
+
'knowledge.sources.publish',
|
|
259
|
+
'assistant.send',
|
|
260
|
+
'invitations.create',
|
|
261
|
+
'invitations.list',
|
|
262
|
+
'pulse.program',
|
|
263
|
+
'pulse.saveProgram',
|
|
264
|
+
'channels.list',
|
|
265
|
+
'programs.options',
|
|
266
|
+
'programs.create',
|
|
267
|
+
'onboarding.program',
|
|
268
|
+
'onboarding.saveProgram',
|
|
269
|
+
'celebrations.program',
|
|
270
|
+
'celebrations.preview',
|
|
271
|
+
'celebrations.save',
|
|
272
|
+
];
|
|
273
|
+
// Writes that remove, stop or withdraw something. MCP marks them destructive
|
|
274
|
+
// and Mello's Run control asks again before running one.
|
|
275
|
+
export const destructiveOperation = /\.(archive|withdraw|revoke|cancel|cancelSubscription|close|delete|disconnect|unlink|pause|blockPair|stop|stopKnownFailures)$/;
|
|
101
276
|
export function scopesForPermissions(permissions) {
|
|
102
277
|
return [
|
|
103
278
|
...new Set(permissions
|
package/dist/schemas.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare const strategySchema: z.ZodEnum<{
|
|
|
12
12
|
"unit-weighted-one-host-per-group": "unit-weighted-one-host-per-group";
|
|
13
13
|
"within-unit": "within-unit";
|
|
14
14
|
}>;
|
|
15
|
-
export declare const
|
|
15
|
+
export declare const nativeScheduleSchema: z.ZodObject<{
|
|
16
16
|
anchor: z.ZodString;
|
|
17
17
|
frequency: z.ZodEnum<{
|
|
18
18
|
days: "days";
|
|
@@ -26,6 +26,120 @@ export declare const scheduleSchema: z.ZodObject<{
|
|
|
26
26
|
interval: z.ZodNumber;
|
|
27
27
|
timezone: z.ZodString;
|
|
28
28
|
}, z.core.$strip>;
|
|
29
|
+
export declare const legacyMonthlyCalendarSchema: z.ZodObject<{
|
|
30
|
+
boundary: z.ZodEnum<{
|
|
31
|
+
end: "end";
|
|
32
|
+
start: "start";
|
|
33
|
+
}>;
|
|
34
|
+
hour: z.ZodNumber;
|
|
35
|
+
minute: z.ZodNumber;
|
|
36
|
+
second: z.ZodNumber;
|
|
37
|
+
timezone: z.ZodString;
|
|
38
|
+
weekday: z.ZodNumber;
|
|
39
|
+
}, z.core.$strict>;
|
|
40
|
+
export declare const legacyMonthlyScheduleSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
41
|
+
calendar: z.ZodObject<{
|
|
42
|
+
boundary: z.ZodEnum<{
|
|
43
|
+
end: "end";
|
|
44
|
+
start: "start";
|
|
45
|
+
}>;
|
|
46
|
+
hour: z.ZodNumber;
|
|
47
|
+
minute: z.ZodNumber;
|
|
48
|
+
second: z.ZodNumber;
|
|
49
|
+
timezone: z.ZodString;
|
|
50
|
+
weekday: z.ZodNumber;
|
|
51
|
+
}, z.core.$strict>;
|
|
52
|
+
frequency: z.ZodLiteral<"legacy-monthly">;
|
|
53
|
+
insufficientOptInsNotice: z.ZodObject<{
|
|
54
|
+
future: z.ZodNullable<z.ZodBoolean>;
|
|
55
|
+
pending: z.ZodNullable<z.ZodBoolean>;
|
|
56
|
+
}, z.core.$strict>;
|
|
57
|
+
interval: z.ZodLiteral<1>;
|
|
58
|
+
timezone: z.ZodString;
|
|
59
|
+
version: z.ZodLiteral<1>;
|
|
60
|
+
anchor: z.ZodISODateTime;
|
|
61
|
+
origin: z.ZodOptional<z.ZodLiteral<"source-pending">>;
|
|
62
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
63
|
+
calendar: z.ZodObject<{
|
|
64
|
+
boundary: z.ZodEnum<{
|
|
65
|
+
end: "end";
|
|
66
|
+
start: "start";
|
|
67
|
+
}>;
|
|
68
|
+
hour: z.ZodNumber;
|
|
69
|
+
minute: z.ZodNumber;
|
|
70
|
+
second: z.ZodNumber;
|
|
71
|
+
timezone: z.ZodString;
|
|
72
|
+
weekday: z.ZodNumber;
|
|
73
|
+
}, z.core.$strict>;
|
|
74
|
+
frequency: z.ZodLiteral<"legacy-monthly">;
|
|
75
|
+
insufficientOptInsNotice: z.ZodObject<{
|
|
76
|
+
future: z.ZodNullable<z.ZodBoolean>;
|
|
77
|
+
pending: z.ZodNullable<z.ZodBoolean>;
|
|
78
|
+
}, z.core.$strict>;
|
|
79
|
+
interval: z.ZodLiteral<1>;
|
|
80
|
+
timezone: z.ZodString;
|
|
81
|
+
version: z.ZodLiteral<1>;
|
|
82
|
+
anchor: z.ZodNullable<z.ZodISODateTime>;
|
|
83
|
+
origin: z.ZodLiteral<"operator-future-start">;
|
|
84
|
+
}, z.core.$strict>]>;
|
|
85
|
+
export declare const scheduleSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
86
|
+
anchor: z.ZodString;
|
|
87
|
+
frequency: z.ZodEnum<{
|
|
88
|
+
days: "days";
|
|
89
|
+
hours: "hours";
|
|
90
|
+
minutes: "minutes";
|
|
91
|
+
"monthly-date": "monthly-date";
|
|
92
|
+
"monthly-last-weekday": "monthly-last-weekday";
|
|
93
|
+
"monthly-weekday": "monthly-weekday";
|
|
94
|
+
weeks: "weeks";
|
|
95
|
+
}>;
|
|
96
|
+
interval: z.ZodNumber;
|
|
97
|
+
timezone: z.ZodString;
|
|
98
|
+
}, z.core.$strip>, z.ZodUnion<readonly [z.ZodObject<{
|
|
99
|
+
calendar: z.ZodObject<{
|
|
100
|
+
boundary: z.ZodEnum<{
|
|
101
|
+
end: "end";
|
|
102
|
+
start: "start";
|
|
103
|
+
}>;
|
|
104
|
+
hour: z.ZodNumber;
|
|
105
|
+
minute: z.ZodNumber;
|
|
106
|
+
second: z.ZodNumber;
|
|
107
|
+
timezone: z.ZodString;
|
|
108
|
+
weekday: z.ZodNumber;
|
|
109
|
+
}, z.core.$strict>;
|
|
110
|
+
frequency: z.ZodLiteral<"legacy-monthly">;
|
|
111
|
+
insufficientOptInsNotice: z.ZodObject<{
|
|
112
|
+
future: z.ZodNullable<z.ZodBoolean>;
|
|
113
|
+
pending: z.ZodNullable<z.ZodBoolean>;
|
|
114
|
+
}, z.core.$strict>;
|
|
115
|
+
interval: z.ZodLiteral<1>;
|
|
116
|
+
timezone: z.ZodString;
|
|
117
|
+
version: z.ZodLiteral<1>;
|
|
118
|
+
anchor: z.ZodISODateTime;
|
|
119
|
+
origin: z.ZodOptional<z.ZodLiteral<"source-pending">>;
|
|
120
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
121
|
+
calendar: z.ZodObject<{
|
|
122
|
+
boundary: z.ZodEnum<{
|
|
123
|
+
end: "end";
|
|
124
|
+
start: "start";
|
|
125
|
+
}>;
|
|
126
|
+
hour: z.ZodNumber;
|
|
127
|
+
minute: z.ZodNumber;
|
|
128
|
+
second: z.ZodNumber;
|
|
129
|
+
timezone: z.ZodString;
|
|
130
|
+
weekday: z.ZodNumber;
|
|
131
|
+
}, z.core.$strict>;
|
|
132
|
+
frequency: z.ZodLiteral<"legacy-monthly">;
|
|
133
|
+
insufficientOptInsNotice: z.ZodObject<{
|
|
134
|
+
future: z.ZodNullable<z.ZodBoolean>;
|
|
135
|
+
pending: z.ZodNullable<z.ZodBoolean>;
|
|
136
|
+
}, z.core.$strict>;
|
|
137
|
+
interval: z.ZodLiteral<1>;
|
|
138
|
+
timezone: z.ZodString;
|
|
139
|
+
version: z.ZodLiteral<1>;
|
|
140
|
+
anchor: z.ZodNullable<z.ZodISODateTime>;
|
|
141
|
+
origin: z.ZodLiteral<"operator-future-start">;
|
|
142
|
+
}, z.core.$strict>]>]>;
|
|
29
143
|
export declare const keepApartRelationshipSchema: z.ZodEnum<{
|
|
30
144
|
department: "department";
|
|
31
145
|
"know-each-other": "know-each-other";
|
|
@@ -52,6 +166,7 @@ export declare const fineTuneSchema: z.ZodObject<{
|
|
|
52
166
|
fof: "fof";
|
|
53
167
|
}>;
|
|
54
168
|
}, z.core.$strict>;
|
|
169
|
+
export declare const maximumMatchingIterations = 1000;
|
|
55
170
|
export declare const matchSchema: z.ZodObject<{
|
|
56
171
|
fineTune: z.ZodOptional<z.ZodObject<{
|
|
57
172
|
commonGround: z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>]>;
|
|
@@ -98,6 +213,7 @@ export declare const programInput: z.ZodObject<{
|
|
|
98
213
|
channelId: z.ZodString;
|
|
99
214
|
channelName: z.ZodString;
|
|
100
215
|
includeBios: z.ZodBoolean;
|
|
216
|
+
includeSchedulingLink: z.ZodOptional<z.ZodBoolean>;
|
|
101
217
|
intro: z.ZodString;
|
|
102
218
|
match: z.ZodObject<{
|
|
103
219
|
fineTune: z.ZodOptional<z.ZodObject<{
|
|
@@ -142,7 +258,7 @@ export declare const programInput: z.ZodObject<{
|
|
|
142
258
|
}>>;
|
|
143
259
|
}, z.core.$strict>;
|
|
144
260
|
name: z.ZodString;
|
|
145
|
-
schedule: z.ZodObject<{
|
|
261
|
+
schedule: z.ZodUnion<readonly [z.ZodObject<{
|
|
146
262
|
anchor: z.ZodString;
|
|
147
263
|
frequency: z.ZodEnum<{
|
|
148
264
|
days: "days";
|
|
@@ -155,7 +271,51 @@ export declare const programInput: z.ZodObject<{
|
|
|
155
271
|
}>;
|
|
156
272
|
interval: z.ZodNumber;
|
|
157
273
|
timezone: z.ZodString;
|
|
158
|
-
}, z.core.$strip
|
|
274
|
+
}, z.core.$strip>, z.ZodUnion<readonly [z.ZodObject<{
|
|
275
|
+
calendar: z.ZodObject<{
|
|
276
|
+
boundary: z.ZodEnum<{
|
|
277
|
+
end: "end";
|
|
278
|
+
start: "start";
|
|
279
|
+
}>;
|
|
280
|
+
hour: z.ZodNumber;
|
|
281
|
+
minute: z.ZodNumber;
|
|
282
|
+
second: z.ZodNumber;
|
|
283
|
+
timezone: z.ZodString;
|
|
284
|
+
weekday: z.ZodNumber;
|
|
285
|
+
}, z.core.$strict>;
|
|
286
|
+
frequency: z.ZodLiteral<"legacy-monthly">;
|
|
287
|
+
insufficientOptInsNotice: z.ZodObject<{
|
|
288
|
+
future: z.ZodNullable<z.ZodBoolean>;
|
|
289
|
+
pending: z.ZodNullable<z.ZodBoolean>;
|
|
290
|
+
}, z.core.$strict>;
|
|
291
|
+
interval: z.ZodLiteral<1>;
|
|
292
|
+
timezone: z.ZodString;
|
|
293
|
+
version: z.ZodLiteral<1>;
|
|
294
|
+
anchor: z.ZodISODateTime;
|
|
295
|
+
origin: z.ZodOptional<z.ZodLiteral<"source-pending">>;
|
|
296
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
297
|
+
calendar: z.ZodObject<{
|
|
298
|
+
boundary: z.ZodEnum<{
|
|
299
|
+
end: "end";
|
|
300
|
+
start: "start";
|
|
301
|
+
}>;
|
|
302
|
+
hour: z.ZodNumber;
|
|
303
|
+
minute: z.ZodNumber;
|
|
304
|
+
second: z.ZodNumber;
|
|
305
|
+
timezone: z.ZodString;
|
|
306
|
+
weekday: z.ZodNumber;
|
|
307
|
+
}, z.core.$strict>;
|
|
308
|
+
frequency: z.ZodLiteral<"legacy-monthly">;
|
|
309
|
+
insufficientOptInsNotice: z.ZodObject<{
|
|
310
|
+
future: z.ZodNullable<z.ZodBoolean>;
|
|
311
|
+
pending: z.ZodNullable<z.ZodBoolean>;
|
|
312
|
+
}, z.core.$strict>;
|
|
313
|
+
interval: z.ZodLiteral<1>;
|
|
314
|
+
timezone: z.ZodString;
|
|
315
|
+
version: z.ZodLiteral<1>;
|
|
316
|
+
anchor: z.ZodNullable<z.ZodISODateTime>;
|
|
317
|
+
origin: z.ZodLiteral<"operator-future-start">;
|
|
318
|
+
}, z.core.$strict>]>]>;
|
|
159
319
|
}, z.core.$strip>;
|
|
160
320
|
export declare const profileSchema: z.ZodObject<{
|
|
161
321
|
asks: z.ZodOptional<z.ZodString>;
|
|
@@ -171,6 +331,7 @@ export declare const programPatch: z.ZodObject<{
|
|
|
171
331
|
channelId: z.ZodOptional<z.ZodString>;
|
|
172
332
|
channelName: z.ZodOptional<z.ZodString>;
|
|
173
333
|
includeBios: z.ZodOptional<z.ZodBoolean>;
|
|
334
|
+
includeSchedulingLink: z.ZodOptional<z.ZodBoolean>;
|
|
174
335
|
intro: z.ZodOptional<z.ZodString>;
|
|
175
336
|
match: z.ZodOptional<z.ZodObject<{
|
|
176
337
|
fineTune: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
package/dist/schemas.js
CHANGED
|
@@ -14,7 +14,7 @@ export const strategySchema = z.enum([
|
|
|
14
14
|
'unit-weighted-multiple-hosts-per-group',
|
|
15
15
|
'unit-lottery-weighted',
|
|
16
16
|
]);
|
|
17
|
-
export const
|
|
17
|
+
export const nativeScheduleSchema = z
|
|
18
18
|
.object({
|
|
19
19
|
anchor: z.string().max(100),
|
|
20
20
|
frequency: z.enum([
|
|
@@ -40,6 +40,61 @@ export const scheduleSchema = z
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
|
+
export const legacyMonthlyCalendarSchema = z
|
|
44
|
+
.strictObject({
|
|
45
|
+
boundary: z.enum(['start', 'end']),
|
|
46
|
+
hour: z.number().int().min(0).max(527),
|
|
47
|
+
minute: z.number().int().min(0).max(59),
|
|
48
|
+
second: z.number().int().min(0).max(59),
|
|
49
|
+
timezone: z.string().min(1).max(100),
|
|
50
|
+
weekday: z.number().int().min(0).max(6),
|
|
51
|
+
})
|
|
52
|
+
.superRefine((rule, ctx) => {
|
|
53
|
+
try {
|
|
54
|
+
if (rule.boundary === 'end' && rule.hour > 23)
|
|
55
|
+
throw new Error();
|
|
56
|
+
if (/^[+-]/.test(rule.timezone))
|
|
57
|
+
throw new Error();
|
|
58
|
+
Temporal.Instant.fromEpochMilliseconds(0).toZonedDateTimeISO(rule.timezone);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
ctx.addIssue({
|
|
62
|
+
code: 'custom',
|
|
63
|
+
message: 'Invalid preserved monthly calendar',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const monthlyScheduleFields = {
|
|
68
|
+
calendar: legacyMonthlyCalendarSchema,
|
|
69
|
+
frequency: z.literal('legacy-monthly'),
|
|
70
|
+
insufficientOptInsNotice: z.strictObject({
|
|
71
|
+
future: z.boolean().nullable(),
|
|
72
|
+
pending: z.boolean().nullable(),
|
|
73
|
+
}),
|
|
74
|
+
interval: z.literal(1),
|
|
75
|
+
timezone: z.string().min(1).max(100),
|
|
76
|
+
version: z.literal(1),
|
|
77
|
+
};
|
|
78
|
+
export const legacyMonthlyScheduleSchema = z
|
|
79
|
+
.union([
|
|
80
|
+
z.strictObject({
|
|
81
|
+
...monthlyScheduleFields,
|
|
82
|
+
anchor: z.iso.datetime({ offset: true }),
|
|
83
|
+
origin: z.literal('source-pending').optional(),
|
|
84
|
+
}),
|
|
85
|
+
z.strictObject({
|
|
86
|
+
...monthlyScheduleFields,
|
|
87
|
+
anchor: z.iso.datetime({ offset: true }).nullable(),
|
|
88
|
+
origin: z.literal('operator-future-start'),
|
|
89
|
+
}),
|
|
90
|
+
])
|
|
91
|
+
.refine((rule) => rule.timezone === rule.calendar.timezone, 'Monthly timezone differs');
|
|
92
|
+
// Parsing a persisted rule does not authorize creation; only the bound importer
|
|
93
|
+
// can create legacy schedules. Ordinary writes may retain an existing rule.
|
|
94
|
+
export const scheduleSchema = z.union([
|
|
95
|
+
nativeScheduleSchema,
|
|
96
|
+
legacyMonthlyScheduleSchema,
|
|
97
|
+
]);
|
|
43
98
|
export const keepApartRelationshipSchema = z.enum([
|
|
44
99
|
'reporting-line',
|
|
45
100
|
'team',
|
|
@@ -73,17 +128,19 @@ export const fineTuneSchema = z.strictObject({
|
|
|
73
128
|
.enum(['any', 'fof', 'far'])
|
|
74
129
|
.describe('any: anyone new. fof: friends of friends first (people in common). far: far corners first (nobody in common).'),
|
|
75
130
|
});
|
|
131
|
+
export const maximumMatchingIterations = 1000;
|
|
76
132
|
export const matchSchema = z
|
|
77
133
|
.strictObject({
|
|
78
134
|
fineTune: fineTuneSchema.optional(),
|
|
79
135
|
groupSize: z.number().int().min(2).max(8),
|
|
80
|
-
iterations: z.number().int().min(1).max(
|
|
136
|
+
iterations: z.number().int().min(1).max(maximumMatchingIterations),
|
|
81
137
|
keepApart: keepApartSchema.optional(),
|
|
82
138
|
maxGroupSize: z.number().int().min(2).max(8),
|
|
83
139
|
strategy: strategySchema,
|
|
84
140
|
unitIds: z.array(z.string().trim().min(1).max(200)).max(20).optional(),
|
|
85
141
|
unitKind: z.enum(['department', 'team']).optional(),
|
|
86
142
|
})
|
|
143
|
+
.refine((x) => !currentStrategies.includes(x.strategy) || x.iterations <= 50, 'Purpose matching supports at most 50 search attempts')
|
|
87
144
|
.refine((x) => x.maxGroupSize >= x.groupSize, 'Maximum group size must be at least the target size')
|
|
88
145
|
.refine((x) => (!x.strategy.startsWith('unit-') && x.strategy !== 'hosted') ||
|
|
89
146
|
!!x.unitIds?.length, 'Choose at least one team or department')
|
|
@@ -102,6 +159,8 @@ export const programInput = z.object({
|
|
|
102
159
|
channelId: z.string().regex(/^[CG][A-Z0-9]+$/),
|
|
103
160
|
channelName: z.string().trim().min(1).max(100),
|
|
104
161
|
includeBios: z.boolean(),
|
|
162
|
+
// Omission preserves an existing program's choice; new programs default on.
|
|
163
|
+
includeSchedulingLink: z.boolean().optional(),
|
|
105
164
|
intro: z.string().trim().min(1).max(2000),
|
|
106
165
|
match: matchSchema,
|
|
107
166
|
name: z.string().trim().min(1).max(120),
|
|
@@ -125,12 +184,18 @@ export const programPatch = z.strictObject({
|
|
|
125
184
|
channelId: programInput.shape.channelId.optional(),
|
|
126
185
|
channelName: programInput.shape.channelName.optional(),
|
|
127
186
|
includeBios: z.boolean().optional(),
|
|
187
|
+
includeSchedulingLink: z.boolean().optional(),
|
|
128
188
|
intro: programInput.shape.intro.optional(),
|
|
129
189
|
match: z
|
|
130
190
|
.strictObject({
|
|
131
191
|
fineTune: fineTuneSchema.partial().nullable().optional(),
|
|
132
192
|
groupSize: z.number().int().min(2).max(8).optional(),
|
|
133
|
-
iterations: z
|
|
193
|
+
iterations: z
|
|
194
|
+
.number()
|
|
195
|
+
.int()
|
|
196
|
+
.min(1)
|
|
197
|
+
.max(maximumMatchingIterations)
|
|
198
|
+
.optional(),
|
|
134
199
|
keepApart: keepApartSchema.nullable().optional(),
|
|
135
200
|
maxGroupSize: z.number().int().min(2).max(8).optional(),
|
|
136
201
|
strategy: strategySchema.optional(),
|