@automate.ax/integration-contracts 0.55.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.
Files changed (136) hide show
  1. package/dist/airtable/api.d.ts +19 -0
  2. package/dist/airtable/api.js +44 -0
  3. package/dist/airtable/index.d.ts +353 -0
  4. package/dist/airtable/index.js +32 -0
  5. package/dist/airtable/schemas.d.ts +716 -0
  6. package/dist/airtable/schemas.js +184 -0
  7. package/dist/asana/api.d.ts +70 -0
  8. package/dist/asana/api.js +119 -0
  9. package/dist/asana/index.d.ts +384 -0
  10. package/dist/asana/index.js +38 -0
  11. package/dist/asana/schemas.d.ts +1060 -0
  12. package/dist/asana/schemas.js +512 -0
  13. package/dist/brevo/api.d.ts +31 -0
  14. package/dist/brevo/api.js +64 -0
  15. package/dist/brevo/index.d.ts +822 -0
  16. package/dist/brevo/index.js +51 -0
  17. package/dist/brevo/schemas.d.ts +1217 -0
  18. package/dist/brevo/schemas.js +332 -0
  19. package/dist/github/index.d.ts +37 -0
  20. package/dist/github/index.js +106 -0
  21. package/dist/github/schemas.d.ts +8014 -0
  22. package/dist/github/schemas.js +113 -0
  23. package/dist/gmail/gmail.d.ts +172 -0
  24. package/dist/gmail/gmail.js +405 -0
  25. package/dist/gmail/index.d.ts +262 -0
  26. package/dist/gmail/index.js +23 -0
  27. package/dist/gmail/schemas.d.ts +444 -0
  28. package/dist/gmail/schemas.js +242 -0
  29. package/dist/google-calendar/event-schemas.d.ts +117 -0
  30. package/dist/google-calendar/event-schemas.js +13 -0
  31. package/dist/google-calendar/google-calendar.d.ts +147 -0
  32. package/dist/google-calendar/google-calendar.js +583 -0
  33. package/dist/google-calendar/index.d.ts +365 -0
  34. package/dist/google-calendar/index.js +24 -0
  35. package/dist/google-calendar/schemas.d.ts +592 -0
  36. package/dist/google-calendar/schemas.js +379 -0
  37. package/dist/google-forms/event-schemas.d.ts +105 -0
  38. package/dist/google-forms/event-schemas.js +20 -0
  39. package/dist/google-forms/google-forms.d.ts +243 -0
  40. package/dist/google-forms/google-forms.js +581 -0
  41. package/dist/google-forms/index.d.ts +141 -0
  42. package/dist/google-forms/index.js +20 -0
  43. package/dist/google-forms/schemas.d.ts +618 -0
  44. package/dist/google-forms/schemas.js +383 -0
  45. package/dist/linear/api.d.ts +63 -0
  46. package/dist/linear/api.js +88 -0
  47. package/dist/linear/index.d.ts +1338 -0
  48. package/dist/linear/index.js +55 -0
  49. package/dist/linear/schemas.d.ts +1795 -0
  50. package/dist/linear/schemas.js +760 -0
  51. package/dist/outlook/graph.d.ts +34 -0
  52. package/dist/outlook/graph.js +98 -0
  53. package/dist/outlook/index.d.ts +115 -0
  54. package/dist/outlook/index.js +11 -0
  55. package/dist/outlook/schemas.d.ts +529 -0
  56. package/dist/outlook/schemas.js +312 -0
  57. package/dist/resend/index.d.ts +707 -0
  58. package/dist/resend/index.js +54 -0
  59. package/dist/resend/schemas.d.ts +1033 -0
  60. package/dist/resend/schemas.js +377 -0
  61. package/dist/slack/index.d.ts +151 -0
  62. package/dist/slack/index.js +22 -0
  63. package/dist/slack/schemas.d.ts +641 -0
  64. package/dist/slack/schemas.js +581 -0
  65. package/dist/teams/graph.d.ts +33 -0
  66. package/dist/teams/graph.js +95 -0
  67. package/dist/teams/index.d.ts +164 -0
  68. package/dist/teams/index.js +21 -0
  69. package/dist/teams/schemas.d.ts +675 -0
  70. package/dist/teams/schemas.js +365 -0
  71. package/dist/trello/api.d.ts +69 -0
  72. package/dist/trello/api.js +140 -0
  73. package/dist/trello/event-schemas.d.ts +1101 -0
  74. package/dist/trello/event-schemas.js +233 -0
  75. package/dist/trello/index.d.ts +721 -0
  76. package/dist/trello/index.js +35 -0
  77. package/dist/trello/schemas.d.ts +283 -0
  78. package/dist/trello/schemas.js +401 -0
  79. package/dist/triggers.d.ts +26 -0
  80. package/dist/triggers.js +33 -0
  81. package/dist/vercel/index.d.ts +1065 -0
  82. package/dist/vercel/index.js +114 -0
  83. package/dist/vercel/schemas.d.ts +1518 -0
  84. package/dist/vercel/schemas.js +335 -0
  85. package/dist/whatsapp/api.d.ts +62 -0
  86. package/dist/whatsapp/api.js +121 -0
  87. package/dist/whatsapp/index.d.ts +614 -0
  88. package/dist/whatsapp/index.js +35 -0
  89. package/dist/whatsapp/schemas.d.ts +926 -0
  90. package/dist/whatsapp/schemas.js +326 -0
  91. package/package.json +169 -0
  92. package/src/airtable/api.ts +66 -0
  93. package/src/airtable/index.ts +41 -0
  94. package/src/airtable/schemas.ts +231 -0
  95. package/src/asana/api.ts +171 -0
  96. package/src/asana/index.ts +49 -0
  97. package/src/asana/schemas.ts +655 -0
  98. package/src/brevo/api.ts +89 -0
  99. package/src/brevo/index.ts +66 -0
  100. package/src/brevo/schemas.ts +449 -0
  101. package/src/github/index.ts +217 -0
  102. package/src/github/schemas.ts +435 -0
  103. package/src/gmail/gmail.ts +577 -0
  104. package/src/gmail/index.ts +26 -0
  105. package/src/gmail/schemas.ts +327 -0
  106. package/src/google-calendar/event-schemas.ts +16 -0
  107. package/src/google-calendar/google-calendar.ts +727 -0
  108. package/src/google-calendar/index.ts +27 -0
  109. package/src/google-calendar/schemas.ts +529 -0
  110. package/src/google-forms/event-schemas.ts +26 -0
  111. package/src/google-forms/google-forms.ts +683 -0
  112. package/src/google-forms/index.ts +23 -0
  113. package/src/google-forms/schemas.ts +499 -0
  114. package/src/linear/api.ts +141 -0
  115. package/src/linear/index.ts +71 -0
  116. package/src/linear/schemas.ts +1060 -0
  117. package/src/outlook/graph.ts +132 -0
  118. package/src/outlook/index.ts +14 -0
  119. package/src/outlook/schemas.ts +388 -0
  120. package/src/resend/index.ts +70 -0
  121. package/src/resend/schemas.ts +462 -0
  122. package/src/slack/index.ts +29 -0
  123. package/src/slack/schemas.ts +737 -0
  124. package/src/teams/graph.ts +130 -0
  125. package/src/teams/index.ts +27 -0
  126. package/src/teams/schemas.ts +463 -0
  127. package/src/trello/api.ts +190 -0
  128. package/src/trello/event-schemas.ts +290 -0
  129. package/src/trello/index.ts +45 -0
  130. package/src/trello/schemas.ts +507 -0
  131. package/src/triggers.ts +68 -0
  132. package/src/vercel/index.ts +147 -0
  133. package/src/vercel/schemas.ts +594 -0
  134. package/src/whatsapp/api.ts +171 -0
  135. package/src/whatsapp/index.ts +46 -0
  136. package/src/whatsapp/schemas.ts +341 -0
@@ -0,0 +1,499 @@
1
+ import type { forms_v1 } from "@googleapis/forms"
2
+ import { z } from "zod"
3
+
4
+ export const FORM_REFERENCE_SCHEMA = z.string().trim().min(1)
5
+ export const ITEM_REFERENCE_SCHEMA = z.union([
6
+ z.string().trim().min(1),
7
+ z.number().int().positive(),
8
+ ])
9
+
10
+ export const EMAIL_COLLECTION_TYPE_SCHEMA = z.enum([
11
+ "none",
12
+ "verified",
13
+ "respondentInput",
14
+ ])
15
+
16
+ export const FORM_SETTINGS_INPUT_SCHEMA = z
17
+ .object({
18
+ /** How respondent email addresses are collected. */
19
+ emailCollection: EMAIL_COLLECTION_TYPE_SCHEMA.optional(),
20
+
21
+ /** Whether the form is configured as a graded quiz. */
22
+ isQuiz: z.boolean().optional(),
23
+ })
24
+ .refine(
25
+ ({ emailCollection, isQuiz }) =>
26
+ emailCollection !== undefined || isQuiz !== undefined,
27
+ "Provide at least one form setting to update.",
28
+ )
29
+
30
+ export const FORM_SETTINGS_SCHEMA = z.object({
31
+ /** How respondent email addresses are collected. */
32
+ emailCollection: EMAIL_COLLECTION_TYPE_SCHEMA,
33
+
34
+ /** Whether the form is configured as a graded quiz. */
35
+ isQuiz: z.boolean(),
36
+ })
37
+
38
+ export const PUBLISH_SETTINGS_SCHEMA = z.object({
39
+ /** Whether respondents can currently submit responses. */
40
+ isAcceptingResponses: z.boolean(),
41
+
42
+ /** Whether the form is published and visible to respondents. */
43
+ isPublished: z.boolean(),
44
+ })
45
+
46
+ export const QUESTION_GRADING_INPUT_SCHEMA = z.object({
47
+ /** Accepted exact answer values used for automatic grading. */
48
+ correctAnswers: z.string().array().min(1),
49
+
50
+ /** Feedback shown for any answer. */
51
+ generalFeedback: z.string().optional(),
52
+
53
+ /** Maximum points awarded for a correct answer. */
54
+ points: z.number().nonnegative(),
55
+
56
+ /** Feedback shown for an incorrect answer. */
57
+ whenWrong: z.string().optional(),
58
+
59
+ /** Feedback shown for a correct answer. */
60
+ whenRight: z.string().optional(),
61
+ })
62
+
63
+ const QUESTION_BASE_INPUT_SCHEMA = z.object({
64
+ /** Optional stable question ID to preserve during replacement updates. */
65
+ questionId: z.string().trim().min(1).optional(),
66
+
67
+ /** Whether respondents must answer the question. */
68
+ required: z.boolean().optional(),
69
+ })
70
+
71
+ const CHOICE_OPTION_INPUT_SCHEMA = z.union([
72
+ z.string().min(1),
73
+ z.object({
74
+ /** Whether this is Forms' built-in free-text "Other" option. */
75
+ isOther: z.literal(true),
76
+
77
+ /** Optional display value. Google may ignore this for "Other". */
78
+ value: z.string().min(1).optional(),
79
+ }),
80
+ ])
81
+
82
+ export const FORM_QUESTION_INPUT_SCHEMA = z.discriminatedUnion("type", [
83
+ QUESTION_BASE_INPUT_SCHEMA.extend({
84
+ /** Question kind. */
85
+ type: z.literal("shortText"),
86
+ }),
87
+ QUESTION_BASE_INPUT_SCHEMA.extend({
88
+ /** Question kind. */
89
+ type: z.literal("paragraph"),
90
+ }),
91
+ QUESTION_BASE_INPUT_SCHEMA.extend({
92
+ /** Available choices in display order. */
93
+ options: CHOICE_OPTION_INPUT_SCHEMA.array().min(1),
94
+
95
+ /** Whether each respondent sees choices in randomized order. */
96
+ shuffle: z.boolean().optional(),
97
+
98
+ /** Question kind. */
99
+ type: z.literal("multipleChoice"),
100
+ }),
101
+ QUESTION_BASE_INPUT_SCHEMA.extend({
102
+ /** Available choices in display order. */
103
+ options: CHOICE_OPTION_INPUT_SCHEMA.array().min(1),
104
+
105
+ /** Whether each respondent sees choices in randomized order. */
106
+ shuffle: z.boolean().optional(),
107
+
108
+ /** Question kind. */
109
+ type: z.literal("checkbox"),
110
+ }),
111
+ QUESTION_BASE_INPUT_SCHEMA.extend({
112
+ /** Available choices in display order. */
113
+ options: z.string().min(1).array().min(1),
114
+
115
+ /** Whether each respondent sees choices in randomized order. */
116
+ shuffle: z.boolean().optional(),
117
+
118
+ /** Question kind. */
119
+ type: z.literal("dropdown"),
120
+ }),
121
+ QUESTION_BASE_INPUT_SCHEMA.extend({
122
+ /** Inclusive maximum scale value. */
123
+ high: z.number().int().min(2).max(10),
124
+
125
+ /** Label displayed beside the maximum value. */
126
+ highLabel: z.string().optional(),
127
+
128
+ /** Inclusive minimum scale value. */
129
+ low: z.union([z.literal(0), z.literal(1)]),
130
+
131
+ /** Label displayed beside the minimum value. */
132
+ lowLabel: z.string().optional(),
133
+
134
+ /** Question kind. */
135
+ type: z.literal("scale"),
136
+ }).refine(({ high, low }) => high > low, {
137
+ message: "Scale high must be greater than low.",
138
+ path: ["high"],
139
+ }),
140
+ QUESTION_BASE_INPUT_SCHEMA.extend({
141
+ /** Whether respondents also enter a time of day. */
142
+ includeTime: z.boolean().optional(),
143
+
144
+ /** Whether respondents enter a four-digit year. */
145
+ includeYear: z.boolean().optional(),
146
+
147
+ /** Question kind. */
148
+ type: z.literal("date"),
149
+ }),
150
+ QUESTION_BASE_INPUT_SCHEMA.extend({
151
+ /** Question kind. */
152
+ type: z.literal("time"),
153
+ }),
154
+ QUESTION_BASE_INPUT_SCHEMA.extend({
155
+ /** Question kind. */
156
+ type: z.literal("duration"),
157
+ }),
158
+ QUESTION_BASE_INPUT_SCHEMA.extend({
159
+ /** Icon displayed for each rating value. */
160
+ icon: z.enum(["star", "heart", "thumbUp"]),
161
+
162
+ /** Number of available rating levels. */
163
+ levels: z.number().int().min(3).max(10),
164
+
165
+ /** Question kind. */
166
+ type: z.literal("rating"),
167
+ }),
168
+ ])
169
+
170
+ const FORM_ITEM_BASE_INPUT_SCHEMA = z.object({
171
+ /** Optional item description displayed below its title. */
172
+ description: z.string().optional(),
173
+
174
+ /** Optional stable item ID to preserve during replacement updates. */
175
+ itemId: z.string().trim().min(1).optional(),
176
+
177
+ /** Item title visible to respondents. */
178
+ title: z.string().optional(),
179
+ })
180
+
181
+ export const FORM_ITEM_INPUT_SCHEMA = z.discriminatedUnion("type", [
182
+ FORM_ITEM_BASE_INPUT_SCHEMA.extend({
183
+ /** Optional quiz grading configuration. */
184
+ grading: QUESTION_GRADING_INPUT_SCHEMA.optional(),
185
+
186
+ /** Question configuration. */
187
+ question: FORM_QUESTION_INPUT_SCHEMA,
188
+
189
+ /** Item kind. */
190
+ type: z.literal("question"),
191
+ }),
192
+ FORM_ITEM_BASE_INPUT_SCHEMA.extend({
193
+ /** Choice columns shared by every grid row. */
194
+ columns: z.string().min(1).array().min(1),
195
+
196
+ /** Grid selection behavior. */
197
+ gridType: z.enum(["multipleChoice", "checkbox"]),
198
+
199
+ /** Row labels, one per generated question. */
200
+ rows: z
201
+ .object({
202
+ /** Optional stable question ID. */
203
+ questionId: z.string().trim().min(1).optional(),
204
+
205
+ /** Whether this row must be answered. */
206
+ required: z.boolean().optional(),
207
+
208
+ /** Row prompt. */
209
+ title: z.string().min(1),
210
+ })
211
+ .array()
212
+ .min(1),
213
+
214
+ /** Whether each respondent sees rows in randomized order. */
215
+ shuffleRows: z.boolean().optional(),
216
+
217
+ /** Item kind. */
218
+ type: z.literal("grid"),
219
+ }),
220
+ FORM_ITEM_BASE_INPUT_SCHEMA.extend({
221
+ /** Item kind. */
222
+ type: z.literal("pageBreak"),
223
+ }),
224
+ FORM_ITEM_BASE_INPUT_SCHEMA.extend({
225
+ /** Item kind. */
226
+ type: z.literal("text"),
227
+ }),
228
+ FORM_ITEM_BASE_INPUT_SCHEMA.extend({
229
+ /** Accessible image description. */
230
+ altText: z.string().optional(),
231
+
232
+ /** Horizontal image alignment. */
233
+ alignment: z.enum(["left", "center", "right"]).optional(),
234
+
235
+ /** Public source URL for the image. */
236
+ sourceUrl: z.url(),
237
+
238
+ /** Item kind. */
239
+ type: z.literal("image"),
240
+
241
+ /** Display width in pixels. */
242
+ width: z.number().int().min(1).max(740).optional(),
243
+ }),
244
+ FORM_ITEM_BASE_INPUT_SCHEMA.extend({
245
+ /** Caption displayed beneath the video. */
246
+ caption: z.string().optional(),
247
+
248
+ /** Horizontal video alignment. */
249
+ alignment: z.enum(["left", "center", "right"]).optional(),
250
+
251
+ /** Item kind. */
252
+ type: z.literal("video"),
253
+
254
+ /** Display width in pixels. */
255
+ width: z.number().int().min(1).max(740).optional(),
256
+
257
+ /** YouTube watch or share URL. */
258
+ youtubeUrl: z.url(),
259
+ }),
260
+ ])
261
+
262
+ export const NORMALIZED_GRADING_SCHEMA = z.object({
263
+ /** Whether the submitted answer was marked correct. */
264
+ correct: z.boolean().optional(),
265
+
266
+ /** Exact answer values accepted for automatic grading. */
267
+ correctAnswers: z.string().array(),
268
+
269
+ /** General or respondent-specific feedback text. */
270
+ feedback: z.string().optional(),
271
+
272
+ /** Maximum available points. */
273
+ points: z.number().nonnegative().optional(),
274
+
275
+ /** Points awarded to a submitted answer. */
276
+ score: z.number().optional(),
277
+
278
+ /** Feedback shown for incorrect answers. */
279
+ whenWrong: z.string().optional(),
280
+
281
+ /** Feedback shown for correct answers. */
282
+ whenRight: z.string().optional(),
283
+ })
284
+
285
+ export const NORMALIZED_QUESTION_SCHEMA = z.object({
286
+ /** Available choice values for choice questions. */
287
+ choices: z.string().array(),
288
+
289
+ /** Date-question configuration. */
290
+ date: z
291
+ .object({
292
+ includeTime: z.boolean(),
293
+ includeYear: z.boolean(),
294
+ })
295
+ .optional(),
296
+
297
+ /** File-upload configuration for existing upload questions. */
298
+ fileUpload: z
299
+ .object({
300
+ folderId: z.string().optional(),
301
+ maxFileSizeBytes: z.string().optional(),
302
+ maxFiles: z.number().int().nonnegative().optional(),
303
+ types: z.string().array(),
304
+ })
305
+ .optional(),
306
+
307
+ /** Quiz grading configuration. */
308
+ grading: NORMALIZED_GRADING_SCHEMA.optional(),
309
+
310
+ /** Immutable question ID. */
311
+ questionId: z.string(),
312
+
313
+ /** Rating-question configuration. */
314
+ rating: z
315
+ .object({
316
+ icon: z.string(),
317
+ levels: z.number().int().nonnegative(),
318
+ })
319
+ .optional(),
320
+
321
+ /** Whether the question must be answered. */
322
+ required: z.boolean(),
323
+
324
+ /** Grid row title for questions inside a grid. */
325
+ rowTitle: z.string().optional(),
326
+
327
+ /** Scale-question configuration. */
328
+ scale: z
329
+ .object({
330
+ high: z.number(),
331
+ highLabel: z.string().optional(),
332
+ low: z.number(),
333
+ lowLabel: z.string().optional(),
334
+ })
335
+ .optional(),
336
+
337
+ /** Normalized question kind. */
338
+ type: z.enum([
339
+ "shortText",
340
+ "paragraph",
341
+ "multipleChoice",
342
+ "checkbox",
343
+ "dropdown",
344
+ "scale",
345
+ "date",
346
+ "time",
347
+ "duration",
348
+ "rating",
349
+ "fileUpload",
350
+ "gridRow",
351
+ "unknown",
352
+ ]),
353
+ })
354
+
355
+ export const FORM_ITEM_SCHEMA = z.object({
356
+ /** Item description. */
357
+ description: z.string().optional(),
358
+
359
+ /** Zero-based item position. */
360
+ index: z.number().int().nonnegative(),
361
+
362
+ /** Immutable item ID. */
363
+ itemId: z.string(),
364
+
365
+ /** Questions contained by this item. */
366
+ questions: NORMALIZED_QUESTION_SCHEMA.array(),
367
+
368
+ /** Item title. */
369
+ title: z.string(),
370
+
371
+ /** Normalized item kind. */
372
+ type: z.enum([
373
+ "question",
374
+ "grid",
375
+ "pageBreak",
376
+ "text",
377
+ "image",
378
+ "video",
379
+ "unknown",
380
+ ]),
381
+ })
382
+
383
+ export const FORM_SCHEMA = z.object({
384
+ /** Browser URL for editing the form. */
385
+ editUrl: z.string(),
386
+
387
+ /** Immutable Google Form ID. */
388
+ formId: z.string(),
389
+
390
+ /** Form items in display order. */
391
+ items: FORM_ITEM_SCHEMA.array(),
392
+
393
+ /** Linked response spreadsheet ID, when configured. */
394
+ linkedSheetId: z.string().optional(),
395
+
396
+ /** Publishing state for modern forms. */
397
+ publishSettings: PUBLISH_SETTINGS_SCHEMA.optional(),
398
+
399
+ /** Browser URL respondents use to submit the form. */
400
+ responderUrl: z.string().optional(),
401
+
402
+ /** Opaque content revision ID valid for optimistic writes. */
403
+ revisionId: z.string().optional(),
404
+
405
+ /** Form-level behavior and quiz configuration. */
406
+ settings: FORM_SETTINGS_SCHEMA,
407
+
408
+ /** Description visible to respondents. */
409
+ description: z.string().optional(),
410
+
411
+ /** Drive document title. */
412
+ documentTitle: z.string().optional(),
413
+
414
+ /** Form title visible to respondents. */
415
+ title: z.string(),
416
+ })
417
+
418
+ export const FORM_FILE_ANSWER_SCHEMA = z.object({
419
+ /** Immutable Google Drive file ID. */
420
+ fileId: z.string(),
421
+
422
+ /** Uploaded file name. */
423
+ fileName: z.string(),
424
+
425
+ /** Uploaded file MIME type. */
426
+ mimeType: z.string(),
427
+ })
428
+
429
+ export const FORM_ANSWER_SCHEMA = z.object({
430
+ /** Files submitted to a file-upload question. */
431
+ files: FORM_FILE_ANSWER_SCHEMA.array(),
432
+
433
+ /** Quiz grade data for this answer. */
434
+ grade: NORMALIZED_GRADING_SCHEMA.optional(),
435
+
436
+ /** Immutable question ID. */
437
+ questionId: z.string(),
438
+
439
+ /** Question title or grid-row title, when form metadata was available. */
440
+ questionTitle: z.string().optional(),
441
+
442
+ /** Submitted values in provider display format. */
443
+ values: z.string().array(),
444
+ })
445
+
446
+ export const FORM_RESPONSE_SCHEMA = z.object({
447
+ /** Submitted answers in form display order. */
448
+ answers: FORM_ANSWER_SCHEMA.array(),
449
+
450
+ /** Timestamp when the response was first submitted. */
451
+ createTime: z.string().optional(),
452
+
453
+ /** Immutable Google Form ID. */
454
+ formId: z.string(),
455
+
456
+ /** Timestamp when the response was last submitted. */
457
+ lastSubmittedTime: z.string().optional(),
458
+
459
+ /** Respondent email when collection was enabled. */
460
+ respondentEmail: z.string().optional(),
461
+
462
+ /** Immutable response ID. */
463
+ responseId: z.string(),
464
+
465
+ /** Total quiz score when graded. */
466
+ totalScore: z.number().optional(),
467
+ })
468
+
469
+ export const FORM_RESPONSE_LIST_SCHEMA = z.object({
470
+ /** Token for retrieving the next result page. */
471
+ nextPageToken: z.string().optional(),
472
+
473
+ /** Normalized form responses. */
474
+ responses: FORM_RESPONSE_SCHEMA.array(),
475
+ })
476
+
477
+ export const FORM_BATCH_UPDATE_SCHEMA = z.object({
478
+ /** Updated form when requested. */
479
+ form: FORM_SCHEMA.optional(),
480
+
481
+ /** Normalized replies corresponding one-to-one with update requests. */
482
+ replies: z
483
+ .object({
484
+ /** Generated item ID for a create-item request. */
485
+ createdItemId: z.string().optional(),
486
+
487
+ /** Generated question IDs for a create-item request. */
488
+ createdQuestionIds: z.string().array(),
489
+ })
490
+ .array(),
491
+
492
+ /** Revision ID returned after the updates. */
493
+ revisionId: z.string().optional(),
494
+ })
495
+
496
+ export const RAW_FORM_UPDATE_REQUEST_SCHEMA = z.custom<forms_v1.Schema$Request>(
497
+ (value) => z.looseObject({}).safeParse(value).success,
498
+ "Expected a Google Forms batch update request object.",
499
+ )
@@ -0,0 +1,141 @@
1
+ import type { JsonObject } from "type-fest"
2
+ import { z } from "zod"
3
+
4
+ const LINEAR_GRAPHQL_URL = "https://api.linear.app/graphql"
5
+ const LINEAR_SECRET_SCHEMA = z.object({
6
+ accessToken: z.string().min(1),
7
+ })
8
+ const LINEAR_ERROR_SCHEMA = z.looseObject({
9
+ extensions: z.record(z.string(), z.json()).optional(),
10
+ message: z.string(),
11
+ path: z.union([z.string(), z.number()]).array().optional(),
12
+ })
13
+ const LINEAR_RESPONSE_SCHEMA = z.looseObject({
14
+ data: z.unknown().optional(),
15
+ errors: LINEAR_ERROR_SCHEMA.array().optional(),
16
+ })
17
+
18
+ /** Options for one authenticated Linear GraphQL operation. */
19
+ export interface LinearGraphqlOptions<TSchema extends z.ZodType> {
20
+ /** Schema that validates and types the operation's `data` payload. */
21
+ responseSchema: TSchema
22
+
23
+ /** GraphQL variables referenced by the operation. */
24
+ variables?: JsonObject
25
+ }
26
+
27
+ /** Authenticated Linear GraphQL helper for custom actions. */
28
+ export interface LinearApi {
29
+ /**
30
+ * Executes any Linear GraphQL query or mutation.
31
+ *
32
+ * GraphQL errors throw `LinearApiError`, including partial failures returned
33
+ * with HTTP 200. Successful `data` is validated with the supplied schema.
34
+ *
35
+ * @param document - GraphQL query or mutation document.
36
+ * @param options - Variables and successful response schema.
37
+ */
38
+ graphql<TSchema extends z.ZodType>(
39
+ document: string,
40
+ options: LinearGraphqlOptions<TSchema>,
41
+ ): Promise<z.output<TSchema>>
42
+ }
43
+
44
+ /** One provider error returned in Linear's GraphQL error envelope. */
45
+ export interface LinearGraphqlError {
46
+ /** Provider-specific error metadata, including an error code when supplied. */
47
+ extensions?: Record<string, unknown>
48
+
49
+ /** Human-readable provider error message. */
50
+ message: string
51
+
52
+ /** Response path whose resolution failed. */
53
+ path?: (number | string)[]
54
+ }
55
+
56
+ /** Structured error returned by an authenticated Linear GraphQL operation. */
57
+ export class LinearApiError extends Error {
58
+ /** GraphQL errors returned by Linear. */
59
+ readonly errors: LinearGraphqlError[]
60
+
61
+ /** Retry delay from Linear's `Retry-After` header, in seconds. */
62
+ readonly retryAfter?: number
63
+
64
+ /** HTTP status returned by Linear. */
65
+ readonly status: number
66
+
67
+ /**
68
+ * Creates a structured Linear API error.
69
+ *
70
+ * @param options - Provider and transport error details.
71
+ * @param options.errors - GraphQL errors returned by Linear.
72
+ * @param options.retryAfter - Retry delay in seconds.
73
+ * @param options.status - HTTP response status.
74
+ */
75
+ constructor(options: {
76
+ errors: LinearGraphqlError[]
77
+ retryAfter?: number
78
+ status: number
79
+ }) {
80
+ super(
81
+ `Linear GraphQL request failed: ${options.errors.map(({ message }) => message).join("; ")}`,
82
+ )
83
+ this.name = "LinearApiError"
84
+ this.errors = options.errors
85
+ this.retryAfter = options.retryAfter
86
+ this.status = options.status
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Creates a raw authenticated Linear GraphQL helper.
92
+ *
93
+ * Use this escape hatch for provider operations that do not yet have a packaged
94
+ * action. OAuth access tokens are resolved by the platform and never exposed to
95
+ * automation code.
96
+ *
97
+ * @param secret - Resolved Linear OAuth integration secret.
98
+ */
99
+ export function getLinearApi(secret: Record<string, unknown>): LinearApi {
100
+ const { accessToken } = LINEAR_SECRET_SCHEMA.parse(secret)
101
+
102
+ return {
103
+ graphql: async (document, options) => {
104
+ const response = await fetch(LINEAR_GRAPHQL_URL, {
105
+ body: JSON.stringify({
106
+ query: document,
107
+ ...(options.variables && { variables: options.variables }),
108
+ }),
109
+ headers: {
110
+ Accept: "application/json",
111
+ Authorization: `Bearer ${accessToken}`,
112
+ "Content-Type": "application/json",
113
+ },
114
+ method: "POST",
115
+ })
116
+ const envelope = LINEAR_RESPONSE_SCHEMA.parse(await response.json())
117
+ if (!response.ok || envelope.errors?.length) {
118
+ throw new LinearApiError({
119
+ errors: envelope.errors ?? [
120
+ { message: `HTTP ${response.status} ${response.statusText}` },
121
+ ],
122
+ retryAfter: parseRetryAfter(response.headers.get("Retry-After")),
123
+ status: response.status,
124
+ })
125
+ }
126
+
127
+ return options.responseSchema.parse(envelope.data)
128
+ },
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Parses Linear's integer retry delay header.
134
+ *
135
+ * @param value - Raw `Retry-After` header.
136
+ */
137
+ function parseRetryAfter(value: string | null) {
138
+ if (value === null) return undefined
139
+ const seconds = Number(value)
140
+ return Number.isFinite(seconds) ? seconds : undefined
141
+ }
@@ -0,0 +1,71 @@
1
+ import { z } from "zod"
2
+ import {
3
+ LINEAR_COMMENT_CREATED_EVENT_SCHEMA,
4
+ LINEAR_COMMENT_EVENT_SCHEMA,
5
+ LINEAR_COMMENT_REMOVED_EVENT_SCHEMA,
6
+ LINEAR_COMMENT_UPDATED_EVENT_SCHEMA,
7
+ LINEAR_ISSUE_CREATED_EVENT_SCHEMA,
8
+ LINEAR_ISSUE_EVENT_SCHEMA,
9
+ LINEAR_ISSUE_REMOVED_EVENT_SCHEMA,
10
+ LINEAR_ISSUE_UPDATED_EVENT_SCHEMA,
11
+ LINEAR_PROJECT_CREATED_EVENT_SCHEMA,
12
+ LINEAR_PROJECT_EVENT_SCHEMA,
13
+ LINEAR_PROJECT_REMOVED_EVENT_SCHEMA,
14
+ LINEAR_PROJECT_UPDATED_EVENT_SCHEMA,
15
+ } from "./schemas"
16
+
17
+ export * from "./api"
18
+ export * from "./schemas"
19
+
20
+ export const LINEAR_TRIGGER_CONFIG_SCHEMA = z.object({})
21
+
22
+ export const linearTriggerContracts = {
23
+ "linear.comment.created": {
24
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
25
+ eventSchema: LINEAR_COMMENT_CREATED_EVENT_SCHEMA,
26
+ },
27
+ "linear.comment.event": {
28
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
29
+ eventSchema: LINEAR_COMMENT_EVENT_SCHEMA,
30
+ },
31
+ "linear.comment.removed": {
32
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
33
+ eventSchema: LINEAR_COMMENT_REMOVED_EVENT_SCHEMA,
34
+ },
35
+ "linear.comment.updated": {
36
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
37
+ eventSchema: LINEAR_COMMENT_UPDATED_EVENT_SCHEMA,
38
+ },
39
+ "linear.issue.created": {
40
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
41
+ eventSchema: LINEAR_ISSUE_CREATED_EVENT_SCHEMA,
42
+ },
43
+ "linear.issue.event": {
44
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
45
+ eventSchema: LINEAR_ISSUE_EVENT_SCHEMA,
46
+ },
47
+ "linear.issue.removed": {
48
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
49
+ eventSchema: LINEAR_ISSUE_REMOVED_EVENT_SCHEMA,
50
+ },
51
+ "linear.issue.updated": {
52
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
53
+ eventSchema: LINEAR_ISSUE_UPDATED_EVENT_SCHEMA,
54
+ },
55
+ "linear.project.created": {
56
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
57
+ eventSchema: LINEAR_PROJECT_CREATED_EVENT_SCHEMA,
58
+ },
59
+ "linear.project.event": {
60
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
61
+ eventSchema: LINEAR_PROJECT_EVENT_SCHEMA,
62
+ },
63
+ "linear.project.removed": {
64
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
65
+ eventSchema: LINEAR_PROJECT_REMOVED_EVENT_SCHEMA,
66
+ },
67
+ "linear.project.updated": {
68
+ configSchema: LINEAR_TRIGGER_CONFIG_SCHEMA,
69
+ eventSchema: LINEAR_PROJECT_UPDATED_EVENT_SCHEMA,
70
+ },
71
+ } as const