@tanstack/ai 0.46.0 → 0.47.2

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.
Files changed (66) hide show
  1. package/dist/esm/activities/chat/index.d.ts +36 -11
  2. package/dist/esm/activities/chat/index.js +440 -79
  3. package/dist/esm/activities/chat/index.js.map +1 -1
  4. package/dist/esm/activities/chat/messages.d.ts +1 -0
  5. package/dist/esm/activities/chat/messages.js +12 -7
  6. package/dist/esm/activities/chat/messages.js.map +1 -1
  7. package/dist/esm/activities/chat/middleware/builder.d.ts +7 -2
  8. package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
  9. package/dist/esm/activities/chat/middleware/compose.d.ts +10 -3
  10. package/dist/esm/activities/chat/middleware/compose.js +55 -0
  11. package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
  12. package/dist/esm/activities/chat/middleware/define.d.ts +6 -3
  13. package/dist/esm/activities/chat/middleware/define.js.map +1 -1
  14. package/dist/esm/activities/chat/middleware/generic-interrupts.d.ts +13 -0
  15. package/dist/esm/activities/chat/middleware/generic-interrupts.js +8 -0
  16. package/dist/esm/activities/chat/middleware/generic-interrupts.js.map +1 -0
  17. package/dist/esm/activities/chat/middleware/index.d.ts +4 -1
  18. package/dist/esm/activities/chat/middleware/types.d.ts +54 -3
  19. package/dist/esm/activities/chat/middleware/types.js +16 -0
  20. package/dist/esm/activities/chat/middleware/types.js.map +1 -0
  21. package/dist/esm/activities/chat/stream/processor.js +18 -5
  22. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  23. package/dist/esm/adapter-internals.d.ts +6 -0
  24. package/dist/esm/adapter-internals.js +4 -1
  25. package/dist/esm/client.d.ts +4 -0
  26. package/dist/esm/client.js +3 -1
  27. package/dist/esm/client.js.map +1 -1
  28. package/dist/esm/generic-interrupt-continuation.d.ts +45 -0
  29. package/dist/esm/generic-interrupt-continuation.js +80 -0
  30. package/dist/esm/generic-interrupt-continuation.js.map +1 -0
  31. package/dist/esm/index.d.ts +6 -1
  32. package/dist/esm/index.js +4 -1
  33. package/dist/esm/interrupt-definition.d.ts +113 -0
  34. package/dist/esm/interrupt-definition.js +169 -0
  35. package/dist/esm/interrupt-definition.js.map +1 -0
  36. package/dist/esm/interrupt-resume.d.ts +3 -0
  37. package/dist/esm/interrupt-resume.js +77 -16
  38. package/dist/esm/interrupt-resume.js.map +1 -1
  39. package/dist/esm/interrupts.d.ts +12 -3
  40. package/dist/esm/interrupts.js.map +1 -1
  41. package/dist/esm/types.d.ts +11 -3
  42. package/dist/esm/utilities/chat-params.js +10 -1
  43. package/dist/esm/utilities/chat-params.js.map +1 -1
  44. package/package.json +3 -3
  45. package/skills/ai-core/media-generation/SKILL.md +4 -1
  46. package/skills/ai-core/middleware/SKILL.md +53 -44
  47. package/skills/ai-core/structured-outputs/SKILL.md +59 -55
  48. package/skills/ai-core/tool-calling/SKILL.md +54 -1
  49. package/src/activities/chat/index.ts +1030 -211
  50. package/src/activities/chat/messages.ts +11 -3
  51. package/src/activities/chat/middleware/builder.ts +29 -4
  52. package/src/activities/chat/middleware/compose.ts +95 -5
  53. package/src/activities/chat/middleware/define.ts +13 -3
  54. package/src/activities/chat/middleware/generic-interrupts.ts +26 -0
  55. package/src/activities/chat/middleware/index.ts +15 -0
  56. package/src/activities/chat/middleware/types.ts +127 -2
  57. package/src/activities/chat/stream/processor.ts +21 -0
  58. package/src/adapter-internals.ts +20 -0
  59. package/src/client.ts +20 -0
  60. package/src/generic-interrupt-continuation.ts +162 -0
  61. package/src/index.ts +34 -0
  62. package/src/interrupt-definition.ts +581 -0
  63. package/src/interrupt-resume.ts +156 -25
  64. package/src/interrupts.ts +13 -3
  65. package/src/types.ts +11 -3
  66. package/src/utilities/chat-params.ts +16 -3
@@ -0,0 +1,581 @@
1
+ import type {
2
+ StandardJSONSchemaV1,
3
+ StandardSchemaV1,
4
+ } from '@standard-schema/spec'
5
+ import {
6
+ canonicalInterruptJson,
7
+ cloneAndDeepFreezeJson,
8
+ digestInterruptJson,
9
+ } from './interrupt-serialization'
10
+ import {
11
+ isStandardSchema,
12
+ isStandardJSONSchema,
13
+ } from './activities/chat/tools/schema-converter'
14
+ import { INTERRUPT_BINDING_VERSION } from './interrupts'
15
+
16
+ export const INTERRUPT_PAYLOAD_METADATA_KEY =
17
+ 'tanstack:interruptPayload' as const
18
+ export const INTERRUPT_BINDING_KIND = 'generic' as const
19
+
20
+ type PortableSchema =
21
+ | StandardJSONSchemaV1<any, any>
22
+ | StandardSchemaV1<any, any>
23
+
24
+ type InferSchemaOutput<TSchema> =
25
+ TSchema extends StandardSchemaV1<any, infer TOutput>
26
+ ? TOutput
27
+ : TSchema extends StandardJSONSchemaV1<any, infer TOutput>
28
+ ? TOutput
29
+ : never
30
+ type InferSchemaInput<TSchema> =
31
+ TSchema extends StandardSchemaV1<infer TInput, any>
32
+ ? TInput
33
+ : TSchema extends StandardJSONSchemaV1<infer TInput, any>
34
+ ? TInput
35
+ : never
36
+ type DefinitionSchemaState = {
37
+ responseSchemaCanonicalJson?: string
38
+ responseSchemaHash?: string
39
+ payloadSchemaCanonicalJson?: string
40
+ payloadSchemaHash?: string
41
+ }
42
+ const definitionSchemaState = new WeakMap<object, DefinitionSchemaState>()
43
+
44
+ export interface InterruptDefinitionOptions<
45
+ TId extends string,
46
+ TPayloadSchema extends PortableSchema | undefined,
47
+ TResponseSchema extends PortableSchema | undefined,
48
+ > {
49
+ id: TId
50
+ payloadSchema?: TPayloadSchema
51
+ responseSchema?: TResponseSchema
52
+ }
53
+
54
+ export interface InterruptBindingDescriptor {
55
+ v: typeof INTERRUPT_BINDING_VERSION
56
+ kind: typeof INTERRUPT_BINDING_KIND
57
+ definitionId: string
58
+ key: string
59
+ threadId?: string
60
+ interruptedRunId?: string
61
+ generation?: number
62
+ batchIndex?: number
63
+ responseSchemaCanonicalJson?: string
64
+ payloadSchemaCanonicalJson?: string
65
+ payloadSchemaHash?: string
66
+ responseSchemaHash?: string
67
+ }
68
+
69
+ export interface InterruptPreEmissionData {
70
+ descriptor: InterruptBindingDescriptor
71
+ payload?: unknown
72
+ }
73
+
74
+ type InterruptInput<
75
+ TPayloadSchema extends PortableSchema | undefined,
76
+ TPayload = unknown,
77
+ > = {
78
+ key: string
79
+ reason: string
80
+ message: string
81
+ expiresAt?: string
82
+ } & ([TPayloadSchema] extends [undefined] ? {} : { payload?: TPayload })
83
+
84
+ type GenericInterruptRequestBase<
85
+ TDefinition extends InterruptDefinition<any, any, any, any>,
86
+ > = {
87
+ readonly definition: TDefinition
88
+ readonly key: string
89
+ readonly reason: string
90
+ readonly message: string
91
+ readonly expiresAt?: string
92
+ }
93
+
94
+ type GenericInterruptRequestFor<
95
+ TDefinition extends InterruptDefinition<any, any, any, any>,
96
+ TPayloadSchema extends PortableSchema | undefined,
97
+ TPayload,
98
+ > = GenericInterruptRequestBase<TDefinition> &
99
+ ([TPayloadSchema] extends [undefined]
100
+ ? {}
101
+ : { readonly payload: TPayload | undefined })
102
+
103
+ export type GenericInterruptRequest<
104
+ TDefinition extends InterruptDefinition<any, any, any, any>,
105
+ > = [TDefinition] extends [never]
106
+ ? never
107
+ : TDefinition extends InterruptDefinition<
108
+ any,
109
+ infer TPayloadSchema,
110
+ any,
111
+ infer TPayload
112
+ >
113
+ ? GenericInterruptRequestFor<TDefinition, TPayloadSchema, TPayload>
114
+ : GenericInterruptRequestBase<TDefinition>
115
+
116
+ type InterruptInputKey = 'key' | 'reason' | 'message' | 'expiresAt' | 'payload'
117
+ type RejectUnexpectedInputKeys<TInput> =
118
+ Exclude<keyof TInput, InterruptInputKey> extends never
119
+ ? unknown
120
+ : { [K in Exclude<keyof TInput, InterruptInputKey>]: never }
121
+ type RejectUnexpectedPayload<TInput> = 'payload' extends keyof TInput
122
+ ? { payload: never }
123
+ : unknown
124
+ type ValidInterruptInput<
125
+ TInput,
126
+ TPayloadSchema extends PortableSchema | undefined,
127
+ TPayload = unknown,
128
+ > =
129
+ TInput extends InterruptInput<TPayloadSchema, TPayload>
130
+ ? RejectUnexpectedInputKeys<TInput> &
131
+ ([TPayloadSchema] extends [undefined]
132
+ ? RejectUnexpectedPayload<TInput>
133
+ : unknown)
134
+ : never
135
+
136
+ /**
137
+ * Extracting a class method preserves the intentional bivariant assignment
138
+ * behavior of the public `interrupt` callback without exposing a method
139
+ * signature in an interface.
140
+ */
141
+ declare abstract class InterruptRequestMethodSignature<
142
+ TId extends string,
143
+ TPayloadSchema extends PortableSchema | undefined,
144
+ TResponseSchema extends PortableSchema | undefined,
145
+ TPayload,
146
+ TPayloadInput,
147
+ > {
148
+ abstract call<TInput>(
149
+ input: TInput & ValidInterruptInput<TInput, TPayloadSchema, TPayloadInput>,
150
+ ): GenericInterruptRequestFor<
151
+ InterruptDefinition<
152
+ TId,
153
+ TPayloadSchema,
154
+ TResponseSchema,
155
+ TPayload,
156
+ TPayloadInput
157
+ >,
158
+ TPayloadSchema,
159
+ TPayload
160
+ >
161
+ }
162
+
163
+ type InterruptRequestMethod<
164
+ TId extends string,
165
+ TPayloadSchema extends PortableSchema | undefined,
166
+ TResponseSchema extends PortableSchema | undefined,
167
+ TPayload,
168
+ TPayloadInput,
169
+ > = InterruptRequestMethodSignature<
170
+ TId,
171
+ TPayloadSchema,
172
+ TResponseSchema,
173
+ TPayload,
174
+ TPayloadInput
175
+ >['call']
176
+
177
+ type DefinedInterruptDefinition<
178
+ TId extends string,
179
+ TPayloadSchema extends PortableSchema | undefined,
180
+ TResponseSchema extends PortableSchema | undefined,
181
+ TPayload = unknown,
182
+ TPayloadInput = TPayload,
183
+ > = Omit<
184
+ InterruptDefinition<
185
+ TId,
186
+ TPayloadSchema,
187
+ TResponseSchema,
188
+ TPayload,
189
+ TPayloadInput
190
+ >,
191
+ 'interrupt'
192
+ > & {
193
+ interrupt: InterruptRequestMethod<
194
+ TId,
195
+ TPayloadSchema,
196
+ TResponseSchema,
197
+ TPayload,
198
+ TPayloadInput
199
+ >
200
+ }
201
+
202
+ export interface InterruptDefinition<
203
+ TId extends string,
204
+ TPayloadSchema extends PortableSchema | undefined,
205
+ TResponseSchema extends PortableSchema | undefined,
206
+ TPayload = unknown,
207
+ TPayloadInput = TPayload,
208
+ > {
209
+ readonly id: TId
210
+ readonly payloadSchema: TPayloadSchema
211
+ readonly responseSchema: TResponseSchema
212
+ interrupt: InterruptRequestMethod<
213
+ TId,
214
+ TPayloadSchema,
215
+ TResponseSchema,
216
+ TPayload,
217
+ TPayloadInput
218
+ >
219
+ }
220
+
221
+ export function createInterruptBinding(
222
+ request: GenericInterruptRequest<InterruptDefinition<any, any, any, any>>,
223
+ fields: Pick<
224
+ InterruptBindingDescriptor,
225
+ 'threadId' | 'interruptedRunId' | 'generation' | 'batchIndex'
226
+ > = {},
227
+ ): InterruptPreEmissionData {
228
+ const schemaState = definitionSchemaState.get(request.definition)
229
+ if (!schemaState) {
230
+ throw new TypeError('Interrupt definition schema state is unavailable.')
231
+ }
232
+ const { threadId, interruptedRunId, generation, batchIndex } = fields
233
+ return {
234
+ descriptor: {
235
+ v: INTERRUPT_BINDING_VERSION,
236
+ kind: INTERRUPT_BINDING_KIND,
237
+ definitionId: request.definition.id,
238
+ key: request.key,
239
+ ...(threadId !== undefined ? { threadId } : {}),
240
+ ...(interruptedRunId !== undefined ? { interruptedRunId } : {}),
241
+ ...(generation !== undefined ? { generation } : {}),
242
+ ...(batchIndex !== undefined ? { batchIndex } : {}),
243
+ ...(schemaState.responseSchemaCanonicalJson
244
+ ? {
245
+ responseSchemaCanonicalJson:
246
+ schemaState.responseSchemaCanonicalJson,
247
+ }
248
+ : {}),
249
+ ...(schemaState.payloadSchemaCanonicalJson
250
+ ? { payloadSchemaCanonicalJson: schemaState.payloadSchemaCanonicalJson }
251
+ : {}),
252
+ ...(schemaState.payloadSchemaHash
253
+ ? { payloadSchemaHash: schemaState.payloadSchemaHash }
254
+ : {}),
255
+ ...(schemaState.responseSchemaHash
256
+ ? { responseSchemaHash: schemaState.responseSchemaHash }
257
+ : {}),
258
+ },
259
+ ...('payload' in request && request.payload !== undefined
260
+ ? { payload: request.payload }
261
+ : {}),
262
+ }
263
+ }
264
+
265
+ type ParsedInterruptInput = {
266
+ key: string
267
+ reason: string
268
+ message: string
269
+ expiresAt?: string
270
+ payload?: unknown
271
+ }
272
+
273
+ type InterruptRequestFactory = (
274
+ input: ParsedInterruptInput,
275
+ payloadIsParsed: boolean,
276
+ ) => GenericInterruptRequest<InterruptDefinition<any, any, any, any>>
277
+
278
+ const interruptRequestFactories = new WeakMap<object, InterruptRequestFactory>()
279
+ const interruptRequestInputs = new WeakMap<
280
+ object,
281
+ Readonly<ParsedInterruptInput>
282
+ >()
283
+
284
+ /**
285
+ * Returns the schema input captured for a newly emitted request. This is
286
+ * internal because continuation state can cross a client boundary and must be
287
+ * parsed again when it returns to the server.
288
+ */
289
+ export function getInterruptRequestInput(
290
+ request: GenericInterruptRequest<InterruptDefinition<any, any, any, any>>,
291
+ ): Readonly<ParsedInterruptInput> {
292
+ const input = interruptRequestInputs.get(request)
293
+ if (!input) {
294
+ throw new TypeError('Interrupt request input is unavailable.')
295
+ }
296
+ return input
297
+ }
298
+
299
+ /**
300
+ * Rebuild a request from a persisted display payload that has already passed
301
+ * the definition's payload schema. This is internal because callers must not
302
+ * bypass public input validation for new requests.
303
+ */
304
+ export function rehydrateInterruptRequest(
305
+ definition: InterruptDefinition<any, any, any, any>,
306
+ input: ParsedInterruptInput,
307
+ ): GenericInterruptRequest<InterruptDefinition<any, any, any, any>> {
308
+ const factory = interruptRequestFactories.get(definition)
309
+ if (!factory) {
310
+ throw new TypeError('Interrupt definition request factory is unavailable.')
311
+ }
312
+ return factory(input, true)
313
+ }
314
+
315
+ interface CanonicalSchemaJson {
316
+ json: Record<string, unknown>
317
+ canonicalJson: string
318
+ }
319
+
320
+ function schemaJson(schema: unknown, name: string): CanonicalSchemaJson {
321
+ if (!isStandardJSONSchema(schema)) {
322
+ throw new TypeError(
323
+ `${name} must be a Standard Schema with a JSON Schema converter.`,
324
+ )
325
+ }
326
+ try {
327
+ const exported = schema['~standard'].jsonSchema.input({
328
+ target: 'draft-07',
329
+ })
330
+ if (exported === undefined) {
331
+ throw new TypeError('The exported schema is undefined.')
332
+ }
333
+ if (typeof exported === 'function') {
334
+ throw new TypeError('The exported schema must not be a function.')
335
+ }
336
+ if (Array.isArray(exported)) {
337
+ throw new TypeError('The exported schema must be a plain JSON object.')
338
+ }
339
+ if (
340
+ !exported ||
341
+ typeof exported !== 'object' ||
342
+ ![Object.prototype, null].includes(Object.getPrototypeOf(exported))
343
+ ) {
344
+ throw new TypeError('The exported schema must be a plain JSON object.')
345
+ }
346
+ const converted: Record<string, unknown> = {}
347
+ for (const [key, value] of Object.entries(exported)) {
348
+ if (key !== '$schema') converted[key] = value
349
+ }
350
+ const canonicalJson = canonicalInterruptJson(converted)
351
+ return { json: converted, canonicalJson }
352
+ } catch (error) {
353
+ throw new TypeError(
354
+ `${name} could not export compatible JSON Schema: ${error instanceof Error ? error.message : String(error)}`,
355
+ )
356
+ }
357
+ }
358
+
359
+ /** Same hash the producer stamps on a first-party generic binding. */
360
+ export function hashInterruptDefinitionSchema(schema: unknown): string {
361
+ return digestInterruptJson(
362
+ schemaJson(schema, 'Interrupt schema').canonicalJson,
363
+ )
364
+ }
365
+
366
+ function validateJson(value: unknown, label: string): void {
367
+ try {
368
+ canonicalInterruptJson(value)
369
+ } catch (error) {
370
+ throw new TypeError(
371
+ `${label} must be JSON-compatible: ${error instanceof Error ? error.message : String(error)}`,
372
+ )
373
+ }
374
+ }
375
+
376
+ function validateNonEmptyString(value: unknown, label: string): string {
377
+ if (typeof value !== 'string' || value.trim() === '') {
378
+ throw new TypeError(`${label} must be a non-empty string.`)
379
+ }
380
+ return value
381
+ }
382
+
383
+ function validateExpiresAt(value: unknown): string {
384
+ if (typeof value !== 'string' || !Number.isFinite(Date.parse(value))) {
385
+ throw new TypeError('Interrupt expiresAt must be a valid date string.')
386
+ }
387
+ return value
388
+ }
389
+
390
+ function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
391
+ return (
392
+ value !== null &&
393
+ (typeof value === 'object' || typeof value === 'function') &&
394
+ 'then' in value &&
395
+ typeof value.then === 'function'
396
+ )
397
+ }
398
+
399
+ function parseInterruptPayload(
400
+ schema: PortableSchema,
401
+ value: unknown,
402
+ ): unknown {
403
+ if (!isStandardSchema(schema)) return value
404
+ const result = schema['~standard'].validate(value)
405
+ if (isPromiseLike(result)) {
406
+ throw new TypeError(
407
+ 'Interrupt payloadSchema validation must be synchronous.',
408
+ )
409
+ }
410
+ if (result.issues !== undefined) {
411
+ throw new TypeError(
412
+ `Interrupt payload is invalid: ${result.issues.map((issue) => issue.message).join(' ')}`,
413
+ )
414
+ }
415
+ return result.value
416
+ }
417
+
418
+ export function defineInterrupt<
419
+ const TId extends string,
420
+ const TPayloadSchema extends PortableSchema,
421
+ const TResponseSchema extends PortableSchema,
422
+ >(options: {
423
+ id: TId
424
+ payloadSchema: TPayloadSchema
425
+ responseSchema: TResponseSchema
426
+ }): DefinedInterruptDefinition<
427
+ TId,
428
+ TPayloadSchema,
429
+ TResponseSchema,
430
+ InferSchemaOutput<TPayloadSchema>,
431
+ InferSchemaInput<TPayloadSchema>
432
+ >
433
+ export function defineInterrupt<
434
+ const TId extends string,
435
+ const TPayloadSchema extends PortableSchema,
436
+ >(options: {
437
+ id: TId
438
+ payloadSchema: TPayloadSchema
439
+ responseSchema?: never
440
+ }): DefinedInterruptDefinition<
441
+ TId,
442
+ TPayloadSchema,
443
+ undefined,
444
+ InferSchemaOutput<TPayloadSchema>,
445
+ InferSchemaInput<TPayloadSchema>
446
+ >
447
+ export function defineInterrupt<
448
+ const TId extends string,
449
+ const TResponseSchema extends PortableSchema,
450
+ >(options: {
451
+ id: TId
452
+ responseSchema: TResponseSchema
453
+ payloadSchema?: never
454
+ }): DefinedInterruptDefinition<
455
+ TId,
456
+ undefined,
457
+ TResponseSchema,
458
+ undefined,
459
+ undefined
460
+ >
461
+ export function defineInterrupt<
462
+ const TId extends string,
463
+ const TPayloadSchema extends PortableSchema | undefined,
464
+ const TResponseSchema extends PortableSchema | undefined,
465
+ >(
466
+ options: InterruptDefinitionOptions<TId, TPayloadSchema, TResponseSchema>,
467
+ ): InterruptDefinition<
468
+ TId,
469
+ TPayloadSchema,
470
+ TResponseSchema,
471
+ InferSchemaOutput<TPayloadSchema>,
472
+ InferSchemaInput<TPayloadSchema>
473
+ > {
474
+ validateNonEmptyString(options.id, 'Interrupt definition id')
475
+ const hasResponseSchema = options.responseSchema !== undefined
476
+ const responseJson = hasResponseSchema
477
+ ? schemaJson(options.responseSchema, 'responseSchema')
478
+ : undefined
479
+ const hasPayloadSchema = Object.prototype.hasOwnProperty.call(
480
+ options,
481
+ 'payloadSchema',
482
+ )
483
+ const payloadJson = hasPayloadSchema
484
+ ? schemaJson(options.payloadSchema, 'payloadSchema')
485
+ : undefined
486
+ const schemaState: DefinitionSchemaState = {
487
+ ...(responseJson
488
+ ? {
489
+ responseSchemaCanonicalJson: responseJson.canonicalJson,
490
+ responseSchemaHash: digestInterruptJson(responseJson.canonicalJson),
491
+ }
492
+ : {}),
493
+ ...(payloadJson
494
+ ? {
495
+ payloadSchemaCanonicalJson: payloadJson.canonicalJson,
496
+ payloadSchemaHash: digestInterruptJson(payloadJson.canonicalJson),
497
+ }
498
+ : {}),
499
+ }
500
+ const definition = {
501
+ id: options.id,
502
+ payloadSchema: options.payloadSchema,
503
+ responseSchema: options.responseSchema,
504
+ interrupt(input: InterruptInput<TPayloadSchema>) {
505
+ return createRequest(input, false)
506
+ },
507
+ } as InterruptDefinition<
508
+ TId,
509
+ TPayloadSchema,
510
+ TResponseSchema,
511
+ InferSchemaOutput<TPayloadSchema>,
512
+ InferSchemaInput<TPayloadSchema>
513
+ >
514
+ const parsePayload = (payload: unknown): unknown => {
515
+ const payloadSchema = options.payloadSchema
516
+ if (payloadSchema === undefined) {
517
+ throw new TypeError(
518
+ 'This interrupt definition does not accept a payload.',
519
+ )
520
+ }
521
+ return parseInterruptPayload(payloadSchema, payload)
522
+ }
523
+ const createRequest: InterruptRequestFactory = (input, payloadIsParsed) => {
524
+ for (const key of Object.keys(input)) {
525
+ if (!['key', 'payload', 'reason', 'message', 'expiresAt'].includes(key)) {
526
+ throw new TypeError(`Interrupt input field ${key} is not allowed.`)
527
+ }
528
+ }
529
+ const key = validateNonEmptyString(input.key, 'Interrupt key')
530
+ const reason = validateNonEmptyString(input.reason, 'Interrupt reason')
531
+ const message = validateNonEmptyString(input.message, 'Interrupt message')
532
+ if ('payload' in input) {
533
+ if (!hasPayloadSchema) {
534
+ throw new TypeError(
535
+ 'This interrupt definition does not accept a payload.',
536
+ )
537
+ }
538
+ if (input.payload !== undefined) {
539
+ validateJson(input.payload, 'Interrupt payload')
540
+ }
541
+ }
542
+ const parsedPayload =
543
+ 'payload' in input
544
+ ? payloadIsParsed
545
+ ? input.payload
546
+ : parsePayload(input.payload)
547
+ : undefined
548
+ const payload =
549
+ parsedPayload === undefined
550
+ ? undefined
551
+ : cloneAndDeepFreezeJson(parsedPayload)
552
+ const expiresAt =
553
+ input.expiresAt === undefined
554
+ ? undefined
555
+ : validateExpiresAt(input.expiresAt)
556
+ const request = Object.freeze({
557
+ definition,
558
+ key,
559
+ ...(hasPayloadSchema && payload !== undefined ? { payload } : {}),
560
+ reason,
561
+ message,
562
+ ...(expiresAt !== undefined ? { expiresAt } : {}),
563
+ })
564
+ if (!payloadIsParsed) {
565
+ interruptRequestInputs.set(
566
+ request,
567
+ cloneAndDeepFreezeJson({
568
+ key,
569
+ reason,
570
+ message,
571
+ ...(expiresAt !== undefined ? { expiresAt } : {}),
572
+ ...(parsedPayload !== undefined ? { payload: parsedPayload } : {}),
573
+ }),
574
+ )
575
+ }
576
+ return request
577
+ }
578
+ definitionSchemaState.set(definition, schemaState)
579
+ interruptRequestFactories.set(definition, createRequest)
580
+ return Object.freeze(definition)
581
+ }