@greatwait/contracts 0.2.2 → 0.3.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/dist/attempt-search.d.ts +235 -0
- package/dist/attempt-search.js +98 -0
- package/dist/billing.d.ts +4 -4
- package/dist/bulk-operations.d.ts +1832 -0
- package/dist/bulk-operations.js +610 -0
- package/dist/configuration-bounds.d.ts +12 -0
- package/dist/configuration-bounds.js +12 -0
- package/dist/configuration.d.ts +71 -0
- package/dist/configuration.js +204 -0
- package/dist/data-export.d.ts +3 -3
- package/dist/effective-limits.d.ts +4 -4
- package/dist/execution-selection.d.ts +48 -0
- package/dist/execution-selection.js +21 -0
- package/dist/http-request.d.ts +1 -1
- package/dist/identifiers.d.ts +2 -0
- package/dist/identifiers.js +2 -0
- package/dist/identity.d.ts +3 -3
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/occurrence-states.d.ts +14 -0
- package/dist/occurrence-states.js +13 -0
- package/dist/operations.d.ts +573 -89
- package/dist/operations.js +67 -0
- package/dist/protocol.d.ts +19 -5
- package/dist/protocol.js +14 -7
- package/dist/schedule-selection.d.ts +28 -0
- package/dist/schedule-selection.js +48 -0
- package/dist/scheduling-records.d.ts +20 -20
- package/dist/scheduling.d.ts +64 -63
- package/dist/scheduling.js +10 -37
- package/dist/test-sink.d.ts +6 -6
- package/dist/usage.d.ts +3 -3
- package/generated/v1/examples/attempts.search.response.json +42 -0
- package/generated/v1/examples/bulk.cancel.request.json +3 -0
- package/generated/v1/examples/bulk.cancel.response.json +59 -0
- package/generated/v1/examples/bulk.confirm.request.json +5 -0
- package/generated/v1/examples/bulk.confirm.response.json +59 -0
- package/generated/v1/examples/bulk.items.response.json +3 -0
- package/generated/v1/examples/bulk.list.response.json +63 -0
- package/generated/v1/examples/bulk.pause.request.json +3 -0
- package/generated/v1/examples/bulk.pause.response.json +59 -0
- package/generated/v1/examples/bulk.preview.request.json +12 -0
- package/generated/v1/examples/bulk.preview.response.json +59 -0
- package/generated/v1/examples/bulk.read.response.json +59 -0
- package/generated/v1/examples/bulk.resume.request.json +3 -0
- package/generated/v1/examples/bulk.resume.response.json +59 -0
- package/generated/v1/manifest.json +243 -1
- package/generated/v1/openapi.json +21569 -2418
- package/generated/v1/policy-matrix.json +63 -0
- package/generated/v1/schemas/attempts.search.query.json +96 -0
- package/generated/v1/schemas/attempts.search.response.json +271 -0
- package/generated/v1/schemas/bulk.cancel.request.json +13 -0
- package/generated/v1/schemas/bulk.cancel.response.json +1717 -0
- package/generated/v1/schemas/bulk.confirm.request-headers.json +13 -0
- package/generated/v1/schemas/bulk.confirm.request.json +19 -0
- package/generated/v1/schemas/bulk.confirm.response.json +1717 -0
- package/generated/v1/schemas/bulk.items.query.json +29 -0
- package/generated/v1/schemas/bulk.items.response.json +194 -0
- package/generated/v1/schemas/bulk.list.query.json +32 -0
- package/generated/v1/schemas/bulk.list.response.json +1789 -0
- package/generated/v1/schemas/bulk.pause.request.json +13 -0
- package/generated/v1/schemas/bulk.pause.response.json +1717 -0
- package/generated/v1/schemas/bulk.preview.request.json +1218 -0
- package/generated/v1/schemas/bulk.preview.response.json +1717 -0
- package/generated/v1/schemas/bulk.read.response.json +1717 -0
- package/generated/v1/schemas/bulk.resume.request.json +13 -0
- package/generated/v1/schemas/bulk.resume.response.json +1717 -0
- package/generated/v1/schemas/executions.list.query.json +33 -12
- package/generated/v1/schemas/schedules.list.query.json +32 -3
- package/generated/v1/snippets/attempts.search.json +6 -0
- package/generated/v1/snippets/bulk.cancel.json +6 -0
- package/generated/v1/snippets/bulk.confirm.json +6 -0
- package/generated/v1/snippets/bulk.items.json +6 -0
- package/generated/v1/snippets/bulk.list.json +6 -0
- package/generated/v1/snippets/bulk.pause.json +6 -0
- package/generated/v1/snippets/bulk.preview.json +6 -0
- package/generated/v1/snippets/bulk.read.json +6 -0
- package/generated/v1/snippets/bulk.resume.json +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
import { nonnegativeMicrodollarAmountSchema, BILLING_ACTIVATION_MODES, } from "./billing.js";
|
|
2
|
+
import { z } from "./zod.js";
|
|
3
|
+
import { authoritySchema } from "./authority.js";
|
|
4
|
+
import { bulkJobIdSchema, scheduleIdSchema, occurrenceIdSchema, attemptIdSchema, } from "./identifiers.js";
|
|
5
|
+
import { scheduleSelectionSchema, normalizeScheduleSelection, } from "./schedule-selection.js";
|
|
6
|
+
import { executionSelectionSchema, normalizeExecutionSelection, } from "./execution-selection.js";
|
|
7
|
+
import { timestampSchema, timeZoneSchema } from "./time.js";
|
|
8
|
+
import { latenessPolicySchema, deliveryOverlapPolicySchema, } from "./scheduling.js";
|
|
9
|
+
import { BULK_CONFIGURATION_MAXIMA, FIXED_INTERVAL_MIN_SECONDS, FIXED_INTERVAL_MAX_SECONDS, } from "./configuration-bounds.js";
|
|
10
|
+
import { CONFIGURATION_DEFAULTS } from "./configuration.js";
|
|
11
|
+
import { pageRequestSchema, requiredIdempotencyRequestHeaderSchema, } from "./protocol.js";
|
|
12
|
+
import { defineOperations, API_UNUSED_RESPONSE_SCHEMA, } from "./operation-definitions.js";
|
|
13
|
+
export const BULK_OPERATIONS = [
|
|
14
|
+
"retry",
|
|
15
|
+
"cancel",
|
|
16
|
+
"reschedule",
|
|
17
|
+
"pause",
|
|
18
|
+
"resume",
|
|
19
|
+
];
|
|
20
|
+
export const bulkOperationSchema = z.enum(BULK_OPERATIONS);
|
|
21
|
+
export const bulkTargetSchema = z.enum([
|
|
22
|
+
"schedules",
|
|
23
|
+
"parent_schedules",
|
|
24
|
+
"occurrences",
|
|
25
|
+
]);
|
|
26
|
+
/** The action registry owns targets, permissions and one canonical effect path. */
|
|
27
|
+
export const BULK_OPERATION_REGISTRY = Object.freeze({
|
|
28
|
+
retry: {
|
|
29
|
+
targets: ["occurrences"],
|
|
30
|
+
scopes: ["occurrences:read", "occurrences:redeliver"],
|
|
31
|
+
effect: "occurrences.redeliver",
|
|
32
|
+
duplicateEffectAcknowledgment: true,
|
|
33
|
+
},
|
|
34
|
+
cancel: {
|
|
35
|
+
targets: ["schedules", "parent_schedules"],
|
|
36
|
+
scopes: ["schedules:read", "schedules:write"],
|
|
37
|
+
effect: "schedules.cancel",
|
|
38
|
+
duplicateEffectAcknowledgment: false,
|
|
39
|
+
},
|
|
40
|
+
reschedule: {
|
|
41
|
+
targets: ["schedules", "parent_schedules"],
|
|
42
|
+
scopes: ["schedules:read", "schedules:write"],
|
|
43
|
+
effect: "schedules.replace",
|
|
44
|
+
duplicateEffectAcknowledgment: false,
|
|
45
|
+
},
|
|
46
|
+
pause: {
|
|
47
|
+
targets: ["schedules", "parent_schedules"],
|
|
48
|
+
scopes: ["schedules:read", "schedules:write"],
|
|
49
|
+
effect: "schedules.pause",
|
|
50
|
+
duplicateEffectAcknowledgment: false,
|
|
51
|
+
},
|
|
52
|
+
resume: {
|
|
53
|
+
targets: ["schedules", "parent_schedules"],
|
|
54
|
+
scopes: ["schedules:read", "schedules:write"],
|
|
55
|
+
effect: "schedules.resume",
|
|
56
|
+
duplicateEffectAcknowledgment: false,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
const scheduleFilter = scheduleSelectionSchema
|
|
60
|
+
.strict()
|
|
61
|
+
.refine((filter) => filter.dueAfter === undefined ||
|
|
62
|
+
filter.dueBefore === undefined ||
|
|
63
|
+
Date.parse(filter.dueAfter) < Date.parse(filter.dueBefore), { message: "dueBefore must be later than dueAfter", path: ["dueBefore"] });
|
|
64
|
+
const executionFilter = executionSelectionSchema
|
|
65
|
+
.strict()
|
|
66
|
+
.refine((filter) => filter.from === undefined ||
|
|
67
|
+
filter.to === undefined ||
|
|
68
|
+
Date.parse(filter.from) < Date.parse(filter.to), { message: "to must be later than from", path: ["to"] });
|
|
69
|
+
export const bulkSelectionSchema = z.union([
|
|
70
|
+
z
|
|
71
|
+
.object({
|
|
72
|
+
kind: z.literal("ids"),
|
|
73
|
+
resource: z.literal("schedules"),
|
|
74
|
+
ids: z
|
|
75
|
+
.array(scheduleIdSchema)
|
|
76
|
+
.max(BULK_CONFIGURATION_MAXIMA.bulkMaxTargets),
|
|
77
|
+
})
|
|
78
|
+
.strict(),
|
|
79
|
+
z
|
|
80
|
+
.object({
|
|
81
|
+
kind: z.literal("ids"),
|
|
82
|
+
resource: z.literal("executions"),
|
|
83
|
+
ids: z
|
|
84
|
+
.array(occurrenceIdSchema)
|
|
85
|
+
.max(BULK_CONFIGURATION_MAXIMA.bulkMaxTargets),
|
|
86
|
+
})
|
|
87
|
+
.strict(),
|
|
88
|
+
z
|
|
89
|
+
.object({
|
|
90
|
+
kind: z.literal("all_matching"),
|
|
91
|
+
resource: z.literal("schedules"),
|
|
92
|
+
filter: scheduleFilter,
|
|
93
|
+
})
|
|
94
|
+
.strict(),
|
|
95
|
+
z
|
|
96
|
+
.object({
|
|
97
|
+
kind: z.literal("all_matching"),
|
|
98
|
+
resource: z.literal("executions"),
|
|
99
|
+
filter: executionFilter,
|
|
100
|
+
})
|
|
101
|
+
.strict(),
|
|
102
|
+
]);
|
|
103
|
+
export function normalizeBulkSelection(selection) {
|
|
104
|
+
if (selection.kind === "ids")
|
|
105
|
+
return { ...selection, ids: [...new Set(selection.ids)].sort() };
|
|
106
|
+
return selection.resource === "schedules"
|
|
107
|
+
? { ...selection, filter: normalizeScheduleSelection(selection.filter) }
|
|
108
|
+
: { ...selection, filter: normalizeExecutionSelection(selection.filter) };
|
|
109
|
+
}
|
|
110
|
+
export const bulkRescheduleParametersSchema = z.discriminatedUnion("mode", [
|
|
111
|
+
z.object({ mode: z.literal("absolute"), at: timestampSchema }).strict(),
|
|
112
|
+
z
|
|
113
|
+
.object({
|
|
114
|
+
mode: z.literal("shift_existing"),
|
|
115
|
+
seconds: z
|
|
116
|
+
.number()
|
|
117
|
+
.int()
|
|
118
|
+
.min(-FIXED_INTERVAL_MAX_SECONDS)
|
|
119
|
+
.max(FIXED_INTERVAL_MAX_SECONDS)
|
|
120
|
+
.refine((seconds) => seconds !== 0, "shift must be nonzero"),
|
|
121
|
+
})
|
|
122
|
+
.strict(),
|
|
123
|
+
z
|
|
124
|
+
.object({
|
|
125
|
+
mode: z.literal("delay_from_reference"),
|
|
126
|
+
seconds: z.number().int().min(1).max(FIXED_INTERVAL_MAX_SECONDS),
|
|
127
|
+
})
|
|
128
|
+
.strict(),
|
|
129
|
+
z
|
|
130
|
+
.object({
|
|
131
|
+
mode: z.literal("definition"),
|
|
132
|
+
definition: z.union([
|
|
133
|
+
z
|
|
134
|
+
.object({
|
|
135
|
+
everySeconds: z
|
|
136
|
+
.number()
|
|
137
|
+
.int()
|
|
138
|
+
.min(FIXED_INTERVAL_MIN_SECONDS)
|
|
139
|
+
.max(FIXED_INTERVAL_MAX_SECONDS),
|
|
140
|
+
})
|
|
141
|
+
.strict(),
|
|
142
|
+
z
|
|
143
|
+
.object({
|
|
144
|
+
cron: z.string().min(1).max(256),
|
|
145
|
+
timeZone: timeZoneSchema,
|
|
146
|
+
})
|
|
147
|
+
.strict(),
|
|
148
|
+
]),
|
|
149
|
+
})
|
|
150
|
+
.strict(),
|
|
151
|
+
]);
|
|
152
|
+
const previewFields = {
|
|
153
|
+
target: bulkTargetSchema,
|
|
154
|
+
selection: bulkSelectionSchema,
|
|
155
|
+
};
|
|
156
|
+
export const bulkPreviewRequestSchema = z
|
|
157
|
+
.discriminatedUnion("operation", [
|
|
158
|
+
z
|
|
159
|
+
.object({
|
|
160
|
+
...previewFields,
|
|
161
|
+
operation: z.literal("retry"),
|
|
162
|
+
parameters: z
|
|
163
|
+
.object({ includeSucceeded: z.boolean().optional() })
|
|
164
|
+
.strict(),
|
|
165
|
+
})
|
|
166
|
+
.strict(),
|
|
167
|
+
z
|
|
168
|
+
.object({
|
|
169
|
+
...previewFields,
|
|
170
|
+
operation: z.literal("reschedule"),
|
|
171
|
+
parameters: bulkRescheduleParametersSchema,
|
|
172
|
+
})
|
|
173
|
+
.strict(),
|
|
174
|
+
...["cancel", "pause", "resume"].map((operation) => z
|
|
175
|
+
.object({
|
|
176
|
+
...previewFields,
|
|
177
|
+
operation: z.literal(operation),
|
|
178
|
+
parameters: z.object({}).strict(),
|
|
179
|
+
})
|
|
180
|
+
.strict()),
|
|
181
|
+
])
|
|
182
|
+
.superRefine((request, context) => {
|
|
183
|
+
const registry = BULK_OPERATION_REGISTRY[request.operation];
|
|
184
|
+
if (!registry.targets.includes(request.target))
|
|
185
|
+
context.addIssue({
|
|
186
|
+
code: "custom",
|
|
187
|
+
path: ["target"],
|
|
188
|
+
message: "unsupported target for this operation",
|
|
189
|
+
});
|
|
190
|
+
if ((request.target === "schedules" &&
|
|
191
|
+
request.selection.resource !== "schedules") ||
|
|
192
|
+
(request.target === "parent_schedules" &&
|
|
193
|
+
request.selection.resource !== "executions") ||
|
|
194
|
+
(request.target === "occurrences" &&
|
|
195
|
+
request.selection.resource !== "executions"))
|
|
196
|
+
context.addIssue({
|
|
197
|
+
code: "custom",
|
|
198
|
+
path: ["selection", "resource"],
|
|
199
|
+
message: "resource does not match the explicitly chosen target",
|
|
200
|
+
});
|
|
201
|
+
if (request.operation === "resume" &&
|
|
202
|
+
request.selection.kind === "all_matching" &&
|
|
203
|
+
request.selection.filter.status !== undefined &&
|
|
204
|
+
request.selection.filter.status !== "paused")
|
|
205
|
+
context.addIssue({
|
|
206
|
+
code: "custom",
|
|
207
|
+
path: ["selection", "filter", "status"],
|
|
208
|
+
message: "bulk Resume candidates must be paused",
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
export const bulkConfirmRequestSchema = z
|
|
212
|
+
.object({
|
|
213
|
+
intentDigest: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
214
|
+
confirmed: z.literal(true),
|
|
215
|
+
acknowledgeDuplicateEffects: z.boolean(),
|
|
216
|
+
})
|
|
217
|
+
.strict();
|
|
218
|
+
export const BULK_JOB_STATES = [
|
|
219
|
+
"preparing",
|
|
220
|
+
"awaiting_confirmation",
|
|
221
|
+
"queued",
|
|
222
|
+
"running",
|
|
223
|
+
"paused",
|
|
224
|
+
"blocked",
|
|
225
|
+
"completed",
|
|
226
|
+
"completed_with_failures",
|
|
227
|
+
"cancelled",
|
|
228
|
+
"expired",
|
|
229
|
+
"refused",
|
|
230
|
+
];
|
|
231
|
+
export const bulkJobStateSchema = z.enum(BULK_JOB_STATES);
|
|
232
|
+
export const BULK_ITEM_STATES = [
|
|
233
|
+
"pending",
|
|
234
|
+
"running",
|
|
235
|
+
"succeeded",
|
|
236
|
+
"no_op",
|
|
237
|
+
"skipped",
|
|
238
|
+
"conflicted",
|
|
239
|
+
"failed",
|
|
240
|
+
"cancelled",
|
|
241
|
+
];
|
|
242
|
+
export const bulkItemStateSchema = z.enum(BULK_ITEM_STATES);
|
|
243
|
+
export const BULK_REASON_CODES = [
|
|
244
|
+
"eligible",
|
|
245
|
+
"already_active",
|
|
246
|
+
"already_paused",
|
|
247
|
+
"already_cancelled",
|
|
248
|
+
"invalid_state",
|
|
249
|
+
"unsupported_target",
|
|
250
|
+
"expired_input",
|
|
251
|
+
"unauthorized",
|
|
252
|
+
"concurrently_changed",
|
|
253
|
+
"past_time",
|
|
254
|
+
"attempt_limit",
|
|
255
|
+
"operation_failed",
|
|
256
|
+
"transient_failure",
|
|
257
|
+
"selection_too_large",
|
|
258
|
+
"preparation_incomplete",
|
|
259
|
+
"preview_expired",
|
|
260
|
+
"job_expired",
|
|
261
|
+
"job_cancelled",
|
|
262
|
+
"quota_exceeded",
|
|
263
|
+
"spending_limit",
|
|
264
|
+
"in_flight",
|
|
265
|
+
];
|
|
266
|
+
export const bulkReasonSchema = z.enum(BULK_REASON_CODES);
|
|
267
|
+
export const bulkCountsSchema = z.object({
|
|
268
|
+
matched: z.number().int().nonnegative(),
|
|
269
|
+
eligible: z.number().int().nonnegative(),
|
|
270
|
+
ineligible: z.number().int().nonnegative(),
|
|
271
|
+
pending: z.number().int().nonnegative(),
|
|
272
|
+
running: z.number().int().nonnegative(),
|
|
273
|
+
succeeded: z.number().int().nonnegative(),
|
|
274
|
+
noOp: z.number().int().nonnegative(),
|
|
275
|
+
skipped: z.number().int().nonnegative(),
|
|
276
|
+
conflicted: z.number().int().nonnegative(),
|
|
277
|
+
failed: z.number().int().nonnegative(),
|
|
278
|
+
cancelled: z.number().int().nonnegative(),
|
|
279
|
+
});
|
|
280
|
+
export const bulkReadbackSchema = z
|
|
281
|
+
.object({
|
|
282
|
+
status: z.string().optional(),
|
|
283
|
+
plannedFor: timestampSchema.optional(),
|
|
284
|
+
recurrence: z.string().optional(),
|
|
285
|
+
timeZone: z.string().optional(),
|
|
286
|
+
affectedFutureRuns: z.number().int().nonnegative().optional(),
|
|
287
|
+
latenessPolicy: latenessPolicySchema.optional(),
|
|
288
|
+
maxStalenessSeconds: z
|
|
289
|
+
.number()
|
|
290
|
+
.int()
|
|
291
|
+
.nonnegative()
|
|
292
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
293
|
+
.optional(),
|
|
294
|
+
overlapPolicy: deliveryOverlapPolicySchema.optional(),
|
|
295
|
+
nextRunNote: z.string().min(1).max(1_000).optional(),
|
|
296
|
+
})
|
|
297
|
+
.strict();
|
|
298
|
+
export const bulkItemSchema = z
|
|
299
|
+
.object({
|
|
300
|
+
id: z.string().min(1),
|
|
301
|
+
jobId: bulkJobIdSchema,
|
|
302
|
+
scheduleId: scheduleIdSchema,
|
|
303
|
+
occurrenceId: occurrenceIdSchema.optional(),
|
|
304
|
+
state: bulkItemStateSchema,
|
|
305
|
+
reason: bulkReasonSchema,
|
|
306
|
+
eligible: z.boolean(),
|
|
307
|
+
retryable: z.boolean(),
|
|
308
|
+
fence: z.string().min(1),
|
|
309
|
+
before: bulkReadbackSchema,
|
|
310
|
+
after: bulkReadbackSchema.optional(),
|
|
311
|
+
attemptId: attemptIdSchema.optional(),
|
|
312
|
+
actionId: z.string().min(1).optional(),
|
|
313
|
+
updatedAt: timestampSchema,
|
|
314
|
+
expiresAt: timestampSchema.optional(),
|
|
315
|
+
})
|
|
316
|
+
.strict();
|
|
317
|
+
export const bulkJobSchema = z
|
|
318
|
+
.object({
|
|
319
|
+
id: bulkJobIdSchema,
|
|
320
|
+
authority: authoritySchema,
|
|
321
|
+
intent: bulkPreviewRequestSchema,
|
|
322
|
+
state: bulkJobStateSchema,
|
|
323
|
+
version: z.number().int().positive(),
|
|
324
|
+
createdAt: timestampSchema,
|
|
325
|
+
updatedAt: timestampSchema,
|
|
326
|
+
referenceAt: timestampSchema,
|
|
327
|
+
preparationExpiresAt: timestampSchema,
|
|
328
|
+
previewExpiresAt: timestampSchema.optional(),
|
|
329
|
+
executionExpiresAt: timestampSchema.optional(),
|
|
330
|
+
resultsExpireAt: timestampSchema.optional(),
|
|
331
|
+
intentDigest: z
|
|
332
|
+
.string()
|
|
333
|
+
.regex(/^[a-f0-9]{64}$/u)
|
|
334
|
+
.optional(),
|
|
335
|
+
complete: z.boolean(),
|
|
336
|
+
counts: bulkCountsSchema,
|
|
337
|
+
reasons: z.array(z.object({
|
|
338
|
+
reason: bulkReasonSchema,
|
|
339
|
+
count: z.number().int().nonnegative(),
|
|
340
|
+
})),
|
|
341
|
+
warnings: z.array(z.string()),
|
|
342
|
+
samples: z.array(bulkItemSchema).max(10),
|
|
343
|
+
limits: z.object({
|
|
344
|
+
maxTargets: z.number().int().positive(),
|
|
345
|
+
executionLifetimeSeconds: z.number().int().positive(),
|
|
346
|
+
previewLifetimeSeconds: z.number().int().positive(),
|
|
347
|
+
resultRetentionSeconds: z.number().int().nonnegative(),
|
|
348
|
+
batchSize: z.number().int().positive(),
|
|
349
|
+
workerConcurrency: z.number().int().positive(),
|
|
350
|
+
}),
|
|
351
|
+
billing: z.object({
|
|
352
|
+
additionalAttemptsMinimum: z.number().int().nonnegative(),
|
|
353
|
+
additionalAttemptsMaximum: z.number().int().nonnegative().optional(),
|
|
354
|
+
currentLimits: z
|
|
355
|
+
.object({
|
|
356
|
+
capturedAt: timestampSchema,
|
|
357
|
+
billingMode: z.enum(BILLING_ACTIVATION_MODES),
|
|
358
|
+
attemptsPerOccurrence: z.number().int().positive(),
|
|
359
|
+
dailyAttemptQuota: z.literal("none"),
|
|
360
|
+
availableCreditMicrodollars: nonnegativeMicrodollarAmountSchema.optional(),
|
|
361
|
+
spendingCapMicrodollars: nonnegativeMicrodollarAmountSchema
|
|
362
|
+
.nullable()
|
|
363
|
+
.optional(),
|
|
364
|
+
remainingToSpendingCapMicrodollars: nonnegativeMicrodollarAmountSchema.optional(),
|
|
365
|
+
})
|
|
366
|
+
.strict()
|
|
367
|
+
.optional(),
|
|
368
|
+
exactChargeKnown: z.literal(false),
|
|
369
|
+
note: z.string(),
|
|
370
|
+
}),
|
|
371
|
+
reason: bulkReasonSchema.optional(),
|
|
372
|
+
})
|
|
373
|
+
.strict();
|
|
374
|
+
export const bulkJobPageSchema = z.object({
|
|
375
|
+
data: z.array(bulkJobSchema),
|
|
376
|
+
nextCursor: z.string().min(1).optional(),
|
|
377
|
+
});
|
|
378
|
+
export const bulkItemPageSchema = z.object({
|
|
379
|
+
data: z.array(bulkItemSchema),
|
|
380
|
+
nextCursor: z.string().min(1).optional(),
|
|
381
|
+
});
|
|
382
|
+
export const bulkListRequestSchema = pageRequestSchema.extend({
|
|
383
|
+
state: bulkJobStateSchema.optional(),
|
|
384
|
+
});
|
|
385
|
+
export const bulkItemListRequestSchema = pageRequestSchema.extend({
|
|
386
|
+
state: bulkItemStateSchema.optional(),
|
|
387
|
+
});
|
|
388
|
+
export const bulkControlRequestSchema = z
|
|
389
|
+
.object({ expectedVersion: z.number().int().positive() })
|
|
390
|
+
.strict();
|
|
391
|
+
const exampleIntent = {
|
|
392
|
+
operation: "pause",
|
|
393
|
+
target: "schedules",
|
|
394
|
+
selection: {
|
|
395
|
+
kind: "all_matching",
|
|
396
|
+
resource: "schedules",
|
|
397
|
+
filter: { tag: "reminders" },
|
|
398
|
+
},
|
|
399
|
+
parameters: {},
|
|
400
|
+
};
|
|
401
|
+
const exampleJob = {
|
|
402
|
+
id: "bjo_01h8xyz9k2m4n6p8r0t2v4w6y8",
|
|
403
|
+
authority: {
|
|
404
|
+
organization: "org_01h8xyz9k2m4n6p8r0t2v4w6y8",
|
|
405
|
+
project: "proj_01h8xyz9k2m4n6p8r0t2v4w6y8",
|
|
406
|
+
environment: "env_01h8xyz9k2m4n6p8r0t2v4w6y8",
|
|
407
|
+
environmentKind: "test",
|
|
408
|
+
},
|
|
409
|
+
intent: exampleIntent,
|
|
410
|
+
state: "awaiting_confirmation",
|
|
411
|
+
version: 1,
|
|
412
|
+
createdAt: "2035-06-01T00:00:00.000Z",
|
|
413
|
+
updatedAt: "2035-06-01T00:00:00.000Z",
|
|
414
|
+
referenceAt: "2035-06-01T00:00:00.000Z",
|
|
415
|
+
preparationExpiresAt: "2035-06-08T00:00:00.000Z",
|
|
416
|
+
previewExpiresAt: "2035-06-01T00:05:00.000Z",
|
|
417
|
+
intentDigest: "a".repeat(64),
|
|
418
|
+
complete: true,
|
|
419
|
+
counts: {
|
|
420
|
+
matched: 0,
|
|
421
|
+
eligible: 0,
|
|
422
|
+
ineligible: 0,
|
|
423
|
+
pending: 0,
|
|
424
|
+
running: 0,
|
|
425
|
+
succeeded: 0,
|
|
426
|
+
noOp: 0,
|
|
427
|
+
skipped: 0,
|
|
428
|
+
conflicted: 0,
|
|
429
|
+
failed: 0,
|
|
430
|
+
cancelled: 0,
|
|
431
|
+
},
|
|
432
|
+
reasons: [],
|
|
433
|
+
warnings: [],
|
|
434
|
+
samples: [],
|
|
435
|
+
limits: {
|
|
436
|
+
maxTargets: CONFIGURATION_DEFAULTS.bulkMaxTargets,
|
|
437
|
+
executionLifetimeSeconds: CONFIGURATION_DEFAULTS.bulkExecutionLifetimeSeconds,
|
|
438
|
+
previewLifetimeSeconds: CONFIGURATION_DEFAULTS.bulkPreviewLifetimeSeconds,
|
|
439
|
+
resultRetentionSeconds: CONFIGURATION_DEFAULTS.bulkResultRetentionSeconds,
|
|
440
|
+
batchSize: CONFIGURATION_DEFAULTS.bulkBatchSize,
|
|
441
|
+
workerConcurrency: CONFIGURATION_DEFAULTS.bulkWorkerConcurrency,
|
|
442
|
+
},
|
|
443
|
+
billing: {
|
|
444
|
+
additionalAttemptsMinimum: 0,
|
|
445
|
+
exactChargeKnown: false,
|
|
446
|
+
note: "Ordinary per-attempt billing and quota admission apply; preview creates no delivery or charge.",
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
const runtime = globalThis;
|
|
450
|
+
const requestOnly = runtime.GREATWAIT_API_REQUEST_REGISTRY;
|
|
451
|
+
const contractsOnly = runtime.GREATWAIT_RUNTIME_CONTRACTS_ONLY;
|
|
452
|
+
const withoutExamples = runtime.GREATWAIT_RUNTIME_WITHOUT_EXAMPLES;
|
|
453
|
+
const shared = {
|
|
454
|
+
principals: ["api-credential", "oauth", "session"],
|
|
455
|
+
scopes: [],
|
|
456
|
+
sessionAuthority: "headers",
|
|
457
|
+
surfaces: ["api", "mock"],
|
|
458
|
+
problems: ["invalid_request", "not_found", "service_unavailable"],
|
|
459
|
+
mcp: { kind: "tool", destructive: false, openWorld: false },
|
|
460
|
+
};
|
|
461
|
+
/** Resolved action scopes from BULK_OPERATION_REGISTRY are mandatory on every endpoint. */
|
|
462
|
+
export const BULK_API_OPERATIONS = defineOperations([
|
|
463
|
+
{
|
|
464
|
+
...shared,
|
|
465
|
+
id: "bulk.preview",
|
|
466
|
+
problems: [...shared.problems, "plan_limit_reached"],
|
|
467
|
+
method: "POST",
|
|
468
|
+
path: "/v1/bulk/jobs",
|
|
469
|
+
creates: true,
|
|
470
|
+
mutates: true,
|
|
471
|
+
idempotent: false,
|
|
472
|
+
summary: requestOnly ? "" : "Preview a bulk operation",
|
|
473
|
+
description: contractsOnly
|
|
474
|
+
? ""
|
|
475
|
+
: "Prepares one bounded frozen selection for retry, cancel, reschedule, pause or resume. Requires the selected action's ordinary scopes and execution reads for parent mapping. Preview creates no deliveries, lifecycle mutations or billable attempts. Only complete awaiting_confirmation jobs may be confirmed.",
|
|
476
|
+
request: bulkPreviewRequestSchema,
|
|
477
|
+
requestExample: withoutExamples ? {} : exampleIntent,
|
|
478
|
+
response: runtime.GREATWAIT_API_REQUEST_REGISTRY
|
|
479
|
+
? API_UNUSED_RESPONSE_SCHEMA
|
|
480
|
+
: bulkJobSchema,
|
|
481
|
+
example: runtime.GREATWAIT_RUNTIME_WITHOUT_EXAMPLES ? {} : exampleJob,
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
...shared,
|
|
485
|
+
id: "bulk.confirm",
|
|
486
|
+
problems: [
|
|
487
|
+
...shared.problems,
|
|
488
|
+
"state_conflict",
|
|
489
|
+
"version_conflict",
|
|
490
|
+
"idempotency_key_reused",
|
|
491
|
+
"plan_limit_reached",
|
|
492
|
+
],
|
|
493
|
+
method: "POST",
|
|
494
|
+
path: "/v1/bulk/jobs/{jobId}/confirm",
|
|
495
|
+
creates: false,
|
|
496
|
+
mutates: true,
|
|
497
|
+
idempotent: true,
|
|
498
|
+
mcp: { kind: "tool", destructive: true, openWorld: true },
|
|
499
|
+
summary: requestOnly ? "" : "Confirm an exact bulk preview",
|
|
500
|
+
description: contractsOnly
|
|
501
|
+
? ""
|
|
502
|
+
: "Confirms the complete unexpired frozen target manifest by intent digest and explicit acknowledgment. Requires Idempotency-Key, the action's current scopes, and duplicate-effect acknowledgment for retry. Changed resources conflict; repeated identical submissions return the same job without applying work twice.",
|
|
503
|
+
request: bulkConfirmRequestSchema,
|
|
504
|
+
requestHeaders: requiredIdempotencyRequestHeaderSchema,
|
|
505
|
+
requestExample: withoutExamples
|
|
506
|
+
? {}
|
|
507
|
+
: {
|
|
508
|
+
intentDigest: "a".repeat(64),
|
|
509
|
+
confirmed: true,
|
|
510
|
+
acknowledgeDuplicateEffects: false,
|
|
511
|
+
},
|
|
512
|
+
response: runtime.GREATWAIT_API_REQUEST_REGISTRY
|
|
513
|
+
? API_UNUSED_RESPONSE_SCHEMA
|
|
514
|
+
: bulkJobSchema,
|
|
515
|
+
example: runtime.GREATWAIT_RUNTIME_WITHOUT_EXAMPLES
|
|
516
|
+
? {}
|
|
517
|
+
: { ...exampleJob, state: "queued" },
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
...shared,
|
|
521
|
+
id: "bulk.read",
|
|
522
|
+
method: "GET",
|
|
523
|
+
path: "/v1/bulk/jobs/{jobId}",
|
|
524
|
+
creates: false,
|
|
525
|
+
mutates: false,
|
|
526
|
+
idempotent: true,
|
|
527
|
+
summary: requestOnly ? "" : "Read bulk preview or job progress",
|
|
528
|
+
description: contractsOnly
|
|
529
|
+
? ""
|
|
530
|
+
: "Reads one scoped job, exact counts when preparation is complete, limits, warnings and reconciled durable outcomes. Requires the resolved action's current scopes; job records contain no callback payloads or secrets.",
|
|
531
|
+
response: runtime.GREATWAIT_API_REQUEST_REGISTRY
|
|
532
|
+
? API_UNUSED_RESPONSE_SCHEMA
|
|
533
|
+
: bulkJobSchema,
|
|
534
|
+
example: runtime.GREATWAIT_RUNTIME_WITHOUT_EXAMPLES ? {} : exampleJob,
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
...shared,
|
|
538
|
+
id: "bulk.list",
|
|
539
|
+
method: "GET",
|
|
540
|
+
path: "/v1/bulk/jobs",
|
|
541
|
+
creates: false,
|
|
542
|
+
mutates: false,
|
|
543
|
+
idempotent: true,
|
|
544
|
+
summary: requestOnly ? "" : "List authorized bulk jobs",
|
|
545
|
+
description: contractsOnly
|
|
546
|
+
? ""
|
|
547
|
+
: "Lists retained jobs in the current authority; only jobs whose action remains authorized are visible. Pagination is cursor based and bound to authority and state.",
|
|
548
|
+
query: bulkListRequestSchema,
|
|
549
|
+
response: runtime.GREATWAIT_API_REQUEST_REGISTRY
|
|
550
|
+
? API_UNUSED_RESPONSE_SCHEMA
|
|
551
|
+
: bulkJobPageSchema,
|
|
552
|
+
example: runtime.GREATWAIT_RUNTIME_WITHOUT_EXAMPLES
|
|
553
|
+
? {}
|
|
554
|
+
: { data: [exampleJob] },
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
...shared,
|
|
558
|
+
id: "bulk.items",
|
|
559
|
+
method: "GET",
|
|
560
|
+
path: "/v1/bulk/jobs/{jobId}/items",
|
|
561
|
+
creates: false,
|
|
562
|
+
mutates: false,
|
|
563
|
+
idempotent: true,
|
|
564
|
+
summary: requestOnly ? "" : "Inspect frozen bulk targets and results",
|
|
565
|
+
description: contractsOnly
|
|
566
|
+
? ""
|
|
567
|
+
: "Pages through the frozen manifest and secret-safe item outcomes. Requires the resolved action's current scopes. Cursors bind this job and item-state filter; retained history cutoffs still apply.",
|
|
568
|
+
query: bulkItemListRequestSchema,
|
|
569
|
+
response: runtime.GREATWAIT_API_REQUEST_REGISTRY
|
|
570
|
+
? API_UNUSED_RESPONSE_SCHEMA
|
|
571
|
+
: bulkItemPageSchema,
|
|
572
|
+
example: withoutExamples ? {} : { data: [] },
|
|
573
|
+
},
|
|
574
|
+
...["pause", "resume", "cancel"].map((action) => ({
|
|
575
|
+
...shared,
|
|
576
|
+
id: `bulk.${action}`,
|
|
577
|
+
problems: [
|
|
578
|
+
...shared.problems,
|
|
579
|
+
"state_conflict",
|
|
580
|
+
"version_conflict",
|
|
581
|
+
],
|
|
582
|
+
method: "POST",
|
|
583
|
+
path: `/v1/bulk/jobs/{jobId}/${action}`,
|
|
584
|
+
creates: false,
|
|
585
|
+
mutates: true,
|
|
586
|
+
idempotent: true,
|
|
587
|
+
mcp: { kind: "tool", destructive: true, openWorld: false },
|
|
588
|
+
summary: requestOnly
|
|
589
|
+
? ""
|
|
590
|
+
: `${action[0].toUpperCase()}${action.slice(1)} a bulk job`,
|
|
591
|
+
description: contractsOnly
|
|
592
|
+
? ""
|
|
593
|
+
: `Controls remaining bulk-job work with a mandatory expectedVersion fence and current resolved-action authorization. ${action === "resume" ? "Keeps the confirmed manifest and retries only eligible unfinished transient work." : "Already-started items may finish; completed callbacks and history remain."} This does not ${action} the selected schedules.`,
|
|
594
|
+
request: bulkControlRequestSchema,
|
|
595
|
+
requestExample: withoutExamples ? {} : { expectedVersion: 1 },
|
|
596
|
+
response: runtime.GREATWAIT_API_REQUEST_REGISTRY
|
|
597
|
+
? API_UNUSED_RESPONSE_SCHEMA
|
|
598
|
+
: bulkJobSchema,
|
|
599
|
+
example: runtime.GREATWAIT_RUNTIME_WITHOUT_EXAMPLES
|
|
600
|
+
? {}
|
|
601
|
+
: {
|
|
602
|
+
...exampleJob,
|
|
603
|
+
state: action === "resume"
|
|
604
|
+
? "queued"
|
|
605
|
+
: action === "pause"
|
|
606
|
+
? "paused"
|
|
607
|
+
: "cancelled",
|
|
608
|
+
},
|
|
609
|
+
})),
|
|
610
|
+
]);
|
|
@@ -11,3 +11,15 @@ export declare const PUBLIC_PAGE_SIZE: Readonly<{
|
|
|
11
11
|
max: 100;
|
|
12
12
|
min: 1;
|
|
13
13
|
}>;
|
|
14
|
+
/** Transport/engineering envelopes; effective bulk policy is layered configuration. */
|
|
15
|
+
export declare const BULK_CONFIGURATION_MAXIMA: Readonly<{
|
|
16
|
+
bulkMaxTargets: 1000000;
|
|
17
|
+
bulkExecutionLifetimeSeconds: 31536000;
|
|
18
|
+
bulkPreviewLifetimeSeconds: 86400;
|
|
19
|
+
bulkResultRetentionSeconds: 31536000;
|
|
20
|
+
bulkBatchSize: 100;
|
|
21
|
+
bulkWorkerConcurrency: 25;
|
|
22
|
+
bulkActiveJobLimit: 25;
|
|
23
|
+
bulkWorkBudgetSeconds: 30;
|
|
24
|
+
bulkCarrierBatchSize: 1000;
|
|
25
|
+
}>;
|
|
@@ -11,3 +11,15 @@ export const PUBLIC_PAGE_SIZE = Object.freeze({
|
|
|
11
11
|
max: 100,
|
|
12
12
|
min: 1,
|
|
13
13
|
});
|
|
14
|
+
/** Transport/engineering envelopes; effective bulk policy is layered configuration. */
|
|
15
|
+
export const BULK_CONFIGURATION_MAXIMA = Object.freeze({
|
|
16
|
+
bulkMaxTargets: 1_000_000,
|
|
17
|
+
bulkExecutionLifetimeSeconds: 31_536_000,
|
|
18
|
+
bulkPreviewLifetimeSeconds: 86_400,
|
|
19
|
+
bulkResultRetentionSeconds: 31_536_000,
|
|
20
|
+
bulkBatchSize: 100,
|
|
21
|
+
bulkWorkerConcurrency: 25,
|
|
22
|
+
bulkActiveJobLimit: 25,
|
|
23
|
+
bulkWorkBudgetSeconds: 30,
|
|
24
|
+
bulkCarrierBatchSize: 1_000,
|
|
25
|
+
});
|