@automate.ax/integration-contracts 0.93.1 → 0.95.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.
@@ -16,49 +16,49 @@ const TRELLO_CARD_SCHEMA = TRELLO_RESOURCE_SCHEMA.extend({
16
16
  idShort: z.number().int().optional(),
17
17
  shortLink: z.string().optional(),
18
18
  })
19
- const TRELLO_LIST_SCHEMA = TRELLO_RESOURCE_SCHEMA
19
+ const TRELLO_LIST_SCHEMA = TRELLO_RESOURCE_SCHEMA.extend({
20
+ closed: z.boolean().optional(),
21
+ })
20
22
  const TRELLO_MEMBER_SCHEMA = TRELLO_RESOURCE_SCHEMA.extend({
21
23
  fullName: z.string().optional(),
22
24
  username: z.string().optional(),
23
25
  })
24
- const TRELLO_ACTION_DATA_SCHEMA = z
26
+ const TRELLO_ATTACHMENT_SCHEMA = TRELLO_RESOURCE_SCHEMA.extend({
27
+ url: z.string().optional(),
28
+ })
29
+ const TRELLO_CHECK_ITEM_SCHEMA = TRELLO_RESOURCE_SCHEMA.extend({
30
+ state: z.enum(["complete", "incomplete"]).optional(),
31
+ })
32
+ const TRELLO_COMMENT_SCHEMA = TRELLO_RESOURCE_SCHEMA.extend({
33
+ text: z.string().optional(),
34
+ })
35
+ const TRELLO_CUSTOM_FIELD_ITEM_SCHEMA = z
25
36
  .looseObject({
26
- /** Attachment affected by the action, when applicable. */
27
- attachment: TRELLO_RESOURCE_SCHEMA.extend({
28
- url: z.string().optional(),
29
- }).optional(),
37
+ id: z.string().optional(),
38
+ idCustomField: z.string().optional(),
39
+ idModel: z.string().optional(),
40
+ idValue: z.string().optional(),
41
+ value: TRELLO_JSON_OBJECT_SCHEMA.optional(),
42
+ })
43
+ .catchall(z.json())
30
44
 
31
- /** Board affected by the action. */
45
+ const TRELLO_ACTION_DATA_SCHEMA = z
46
+ .looseObject({
47
+ action: TRELLO_COMMENT_SCHEMA.optional(),
48
+ attachment: TRELLO_ATTACHMENT_SCHEMA.optional(),
32
49
  board: TRELLO_BOARD_SCHEMA.optional(),
33
-
34
- /** Card affected by the action, when applicable. */
35
50
  card: TRELLO_CARD_SCHEMA.optional(),
36
-
37
- /** Checklist affected by the action, when applicable. */
38
51
  checklist: TRELLO_RESOURCE_SCHEMA.optional(),
39
-
40
- /** Checklist item affected by the action, when applicable. */
41
- checkItem: TRELLO_RESOURCE_SCHEMA.optional(),
42
-
43
- /** List associated with the action. */
52
+ checkItem: TRELLO_CHECK_ITEM_SCHEMA.optional(),
53
+ customField: TRELLO_RESOURCE_SCHEMA.optional(),
54
+ customFieldItem: TRELLO_CUSTOM_FIELD_ITEM_SCHEMA.optional(),
55
+ label: TRELLO_RESOURCE_SCHEMA.optional(),
44
56
  list: TRELLO_LIST_SCHEMA.optional(),
45
-
46
- /** Destination list for a move action. */
47
57
  listAfter: TRELLO_LIST_SCHEMA.optional(),
48
-
49
- /** Source list for a move action. */
50
58
  listBefore: TRELLO_LIST_SCHEMA.optional(),
51
-
52
- /** Member affected by the action, when applicable. */
53
59
  member: TRELLO_MEMBER_SCHEMA.optional(),
54
-
55
- /** Previous field values supplied for an update action. */
56
60
  old: TRELLO_JSON_OBJECT_SCHEMA.optional(),
57
-
58
- /** Organization affected by the action, when applicable. */
59
61
  organization: TRELLO_RESOURCE_SCHEMA.optional(),
60
-
61
- /** Comment or other action text. */
62
62
  text: z.string().optional(),
63
63
  })
64
64
  .catchall(z.json())
@@ -68,6 +68,12 @@ const TRELLO_CARD_ACTION_DATA_SCHEMA = TRELLO_ACTION_DATA_SCHEMA.extend({
68
68
  .catchall(z.json())
69
69
  .optional(),
70
70
  })
71
+ const TRELLO_LIST_ACTION_DATA_SCHEMA = TRELLO_ACTION_DATA_SCHEMA.extend({
72
+ old: z
73
+ .looseObject({ closed: z.boolean().optional() })
74
+ .catchall(z.json())
75
+ .optional(),
76
+ })
71
77
 
72
78
  const TRELLO_WEBHOOK_EVENT_SCHEMA = z.object({
73
79
  action: z
@@ -75,19 +81,35 @@ const TRELLO_WEBHOOK_EVENT_SCHEMA = z.object({
75
81
  data: TRELLO_ACTION_DATA_SCHEMA,
76
82
  date: z.iso.datetime({ offset: true }),
77
83
  id: z.string(),
84
+ idMemberCreator: z.string().optional(),
78
85
  memberCreator: TRELLO_MEMBER_SCHEMA.optional(),
79
86
  type: z.string(),
80
87
  })
81
88
  .catchall(z.json()),
82
89
  model: TRELLO_BOARD_SCHEMA,
83
- webhook: z.looseObject({ id: z.string() }).catchall(z.json()),
90
+ webhook: z
91
+ .looseObject({
92
+ active: z.boolean().optional(),
93
+ callbackURL: z.string().optional(),
94
+ consecutiveFailures: z.number().int().nonnegative().optional(),
95
+ firstConsecutiveFailDate: z.string().nullable().optional(),
96
+ id: z.string(),
97
+ idModel: z.string().optional(),
98
+ })
99
+ .catchall(z.json()),
84
100
  })
101
+
85
102
  const TRELLO_CARD_CREATED_ACTION_SCHEMA = z.enum([
86
103
  "copyCard",
87
104
  "convertToCardFromCheckItem",
88
105
  "createCard",
89
106
  "emailCard",
90
107
  ])
108
+ const TRELLO_CARD_MOVED_ACTION_SCHEMA = z.enum([
109
+ "moveCardFromBoard",
110
+ "moveCardToBoard",
111
+ "updateCard",
112
+ ])
91
113
  const TRELLO_CARD_UPDATED_ACTION_SCHEMA = z.literal("updateCard")
92
114
  const TRELLO_CARD_COMMENTED_ACTION_SCHEMA = z.literal("commentCard")
93
115
 
@@ -96,66 +118,94 @@ export const TRELLO_CARD_EVENT_KINDS = [
96
118
  "moved",
97
119
  "updated",
98
120
  "archived",
121
+ "restored",
122
+ "deleted",
99
123
  "commented",
100
124
  ] as const
101
125
 
102
126
  export type TrelloCardEventKind = (typeof TRELLO_CARD_EVENT_KINDS)[number]
103
127
 
128
+ export const TRELLO_EVENT_KINDS = [
129
+ "card.created",
130
+ "card.moved",
131
+ "card.updated",
132
+ "card.archived",
133
+ "card.restored",
134
+ "card.deleted",
135
+ "card.commented",
136
+ "comment.updated",
137
+ "comment.deleted",
138
+ "attachment.added",
139
+ "attachment.deleted",
140
+ "card.member.added",
141
+ "card.member.removed",
142
+ "card.label.added",
143
+ "card.label.removed",
144
+ "checklist.created",
145
+ "checklist.updated",
146
+ "checklist.deleted",
147
+ "checklist-item.created",
148
+ "checklist-item.updated",
149
+ "checklist-item.completed",
150
+ "checklist-item.deleted",
151
+ "custom-field.created",
152
+ "custom-field.updated",
153
+ "custom-field.deleted",
154
+ "custom-field.value.updated",
155
+ "list.created",
156
+ "list.updated",
157
+ "list.archived",
158
+ "list.restored",
159
+ "list.moved",
160
+ ] as const
161
+
162
+ export type TrelloEventKind = (typeof TRELLO_EVENT_KINDS)[number]
163
+
104
164
  export const TRELLO_BOARD_EVENT_SCHEMA = TRELLO_ACTION_DATA_SCHEMA.extend({
105
- /** Stable ID of the Trello action that caused this event. */
106
165
  actionId: z.string(),
107
-
108
- /** Trello action category, such as `createCard` or `updateCard`. */
109
166
  actionType: z.string(),
110
-
111
- /** Board affected by the action, falling back to the watched board. */
112
167
  board: TRELLO_BOARD_SCHEMA,
113
-
114
- /** Complete provider webhook envelope. */
115
168
  event: TRELLO_WEBHOOK_EVENT_SCHEMA,
116
-
117
- /** Member responsible for the action, when Trello includes one. */
118
169
  memberCreator: TRELLO_MEMBER_SCHEMA.optional(),
119
-
120
- /** When the underlying Trello action occurred. */
121
170
  occurredAt: z.iso.datetime({ offset: true }),
122
-
123
- /** Provider webhook that delivered the event. */
124
171
  webhookId: z.string(),
125
172
  })
126
173
 
127
174
  export const TRELLO_CARD_EVENT_SCHEMA = z.object({
128
- /** Stable ID of the Trello action that caused this event. */
129
175
  actionId: z.string(),
130
-
131
- /** Provider-native Trello action category. */
132
176
  actionType: z.string(),
133
-
134
- /** Board containing the affected card. */
135
177
  board: TRELLO_BOARD_EVENT_SCHEMA.shape.board,
136
-
137
- /** Card affected by the action. */
138
178
  card: TRELLO_CARD_SCHEMA,
139
-
140
- /** Comment text for card-comment events. */
141
179
  commentText: z.string().optional(),
142
-
143
- /** Complete provider webhook envelope. */
144
180
  event: TRELLO_WEBHOOK_EVENT_SCHEMA,
145
-
146
- /** List containing the card after the action. */
147
181
  list: TRELLO_LIST_SCHEMA.optional(),
148
-
149
- /** Member responsible for the action, when supplied by Trello. */
150
182
  memberCreator: TRELLO_BOARD_EVENT_SCHEMA.shape.memberCreator,
151
-
152
- /** When the underlying Trello action occurred. */
153
183
  occurredAt: z.iso.datetime({ offset: true }),
154
-
155
- /** List containing the card before a move. */
156
184
  previousList: TRELLO_LIST_SCHEMA.optional(),
157
185
  })
158
186
 
187
+ const TRELLO_RESOURCE_EVENT_SCHEMA = z.object({
188
+ actionId: z.string(),
189
+ actionType: z.string(),
190
+ attachment: TRELLO_ATTACHMENT_SCHEMA.optional(),
191
+ board: TRELLO_BOARD_SCHEMA,
192
+ card: TRELLO_CARD_SCHEMA.optional(),
193
+ checklist: TRELLO_RESOURCE_SCHEMA.optional(),
194
+ checkItem: TRELLO_CHECK_ITEM_SCHEMA.optional(),
195
+ comment: TRELLO_COMMENT_SCHEMA.optional(),
196
+ customField: TRELLO_RESOURCE_SCHEMA.optional(),
197
+ customFieldItem: TRELLO_CUSTOM_FIELD_ITEM_SCHEMA.optional(),
198
+ event: TRELLO_WEBHOOK_EVENT_SCHEMA,
199
+ label: TRELLO_RESOURCE_SCHEMA.optional(),
200
+ list: TRELLO_LIST_SCHEMA.optional(),
201
+ listAfter: TRELLO_LIST_SCHEMA.optional(),
202
+ listBefore: TRELLO_LIST_SCHEMA.optional(),
203
+ member: TRELLO_MEMBER_SCHEMA.optional(),
204
+ memberCreator: TRELLO_MEMBER_SCHEMA.optional(),
205
+ occurredAt: z.iso.datetime({ offset: true }),
206
+ old: TRELLO_JSON_OBJECT_SCHEMA.optional(),
207
+ })
208
+
159
209
  export const TRELLO_CARD_CREATED_EVENT_SCHEMA = TRELLO_CARD_EVENT_SCHEMA.extend(
160
210
  {
161
211
  actionType: TRELLO_CARD_CREATED_ACTION_SCHEMA,
@@ -164,10 +214,8 @@ export const TRELLO_CARD_CREATED_EVENT_SCHEMA = TRELLO_CARD_EVENT_SCHEMA.extend(
164
214
  )
165
215
 
166
216
  export const TRELLO_CARD_MOVED_EVENT_SCHEMA = TRELLO_CARD_EVENT_SCHEMA.extend({
167
- actionType: TRELLO_CARD_UPDATED_ACTION_SCHEMA,
168
- event: trelloWebhookEventSchema(TRELLO_CARD_UPDATED_ACTION_SCHEMA),
169
- list: TRELLO_LIST_SCHEMA,
170
- previousList: TRELLO_LIST_SCHEMA,
217
+ actionType: TRELLO_CARD_MOVED_ACTION_SCHEMA,
218
+ event: trelloWebhookEventSchema(TRELLO_CARD_MOVED_ACTION_SCHEMA),
171
219
  })
172
220
 
173
221
  export const TRELLO_CARD_UPDATED_EVENT_SCHEMA = TRELLO_CARD_EVENT_SCHEMA.extend(
@@ -182,6 +230,18 @@ export const TRELLO_CARD_ARCHIVED_EVENT_SCHEMA =
182
230
  card: TRELLO_CARD_SCHEMA.extend({ closed: z.literal(true) }),
183
231
  })
184
232
 
233
+ export const TRELLO_CARD_RESTORED_EVENT_SCHEMA =
234
+ TRELLO_CARD_UPDATED_EVENT_SCHEMA.extend({
235
+ card: TRELLO_CARD_SCHEMA.extend({ closed: z.literal(false) }),
236
+ })
237
+
238
+ export const TRELLO_CARD_DELETED_EVENT_SCHEMA = TRELLO_CARD_EVENT_SCHEMA.extend(
239
+ {
240
+ actionType: z.literal("deleteCard"),
241
+ event: trelloWebhookEventSchema(z.literal("deleteCard")),
242
+ },
243
+ )
244
+
185
245
  export const TRELLO_CARD_COMMENTED_EVENT_SCHEMA =
186
246
  TRELLO_CARD_EVENT_SCHEMA.extend({
187
247
  actionType: TRELLO_CARD_COMMENTED_ACTION_SCHEMA,
@@ -189,10 +249,99 @@ export const TRELLO_CARD_COMMENTED_EVENT_SCHEMA =
189
249
  event: trelloWebhookEventSchema(TRELLO_CARD_COMMENTED_ACTION_SCHEMA),
190
250
  })
191
251
 
252
+ export const TRELLO_COMMENT_UPDATED_EVENT_SCHEMA = resourceEventSchema(
253
+ z.literal("updateComment"),
254
+ )
255
+ export const TRELLO_COMMENT_DELETED_EVENT_SCHEMA = resourceEventSchema(
256
+ z.literal("deleteComment"),
257
+ )
258
+ export const TRELLO_ATTACHMENT_ADDED_EVENT_SCHEMA = resourceEventSchema(
259
+ z.literal("addAttachmentToCard"),
260
+ ).extend({ attachment: TRELLO_ATTACHMENT_SCHEMA, card: TRELLO_CARD_SCHEMA })
261
+ export const TRELLO_ATTACHMENT_DELETED_EVENT_SCHEMA = resourceEventSchema(
262
+ z.literal("deleteAttachmentFromCard"),
263
+ ).extend({ attachment: TRELLO_ATTACHMENT_SCHEMA, card: TRELLO_CARD_SCHEMA })
264
+ export const TRELLO_CARD_MEMBER_ADDED_EVENT_SCHEMA = resourceEventSchema(
265
+ z.literal("addMemberToCard"),
266
+ ).extend({ card: TRELLO_CARD_SCHEMA, member: TRELLO_MEMBER_SCHEMA })
267
+ export const TRELLO_CARD_MEMBER_REMOVED_EVENT_SCHEMA = resourceEventSchema(
268
+ z.literal("removeMemberFromCard"),
269
+ ).extend({ card: TRELLO_CARD_SCHEMA, member: TRELLO_MEMBER_SCHEMA })
270
+ export const TRELLO_CARD_LABEL_ADDED_EVENT_SCHEMA = resourceEventSchema(
271
+ z.literal("addLabelToCard"),
272
+ ).extend({ card: TRELLO_CARD_SCHEMA, label: TRELLO_RESOURCE_SCHEMA })
273
+ export const TRELLO_CARD_LABEL_REMOVED_EVENT_SCHEMA = resourceEventSchema(
274
+ z.literal("removeLabelFromCard"),
275
+ ).extend({ card: TRELLO_CARD_SCHEMA, label: TRELLO_RESOURCE_SCHEMA })
276
+ export const TRELLO_CHECKLIST_CREATED_EVENT_SCHEMA = resourceEventSchema(
277
+ z.literal("addChecklistToCard"),
278
+ ).extend({ card: TRELLO_CARD_SCHEMA, checklist: TRELLO_RESOURCE_SCHEMA })
279
+ export const TRELLO_CHECKLIST_UPDATED_EVENT_SCHEMA = resourceEventSchema(
280
+ z.literal("updateChecklist"),
281
+ ).extend({ checklist: TRELLO_RESOURCE_SCHEMA })
282
+ export const TRELLO_CHECKLIST_DELETED_EVENT_SCHEMA = resourceEventSchema(
283
+ z.literal("removeChecklistFromCard"),
284
+ ).extend({ card: TRELLO_CARD_SCHEMA, checklist: TRELLO_RESOURCE_SCHEMA })
285
+ export const TRELLO_CHECKLIST_ITEM_CREATED_EVENT_SCHEMA = resourceEventSchema(
286
+ z.literal("createCheckItem"),
287
+ ).extend({
288
+ checkItem: TRELLO_CHECK_ITEM_SCHEMA,
289
+ checklist: TRELLO_RESOURCE_SCHEMA,
290
+ })
291
+ export const TRELLO_CHECKLIST_ITEM_UPDATED_EVENT_SCHEMA = resourceEventSchema(
292
+ z.enum(["updateCheckItem", "updateCheckItemStateOnCard"]),
293
+ ).extend({ checkItem: TRELLO_CHECK_ITEM_SCHEMA })
294
+ export const TRELLO_CHECKLIST_ITEM_COMPLETED_EVENT_SCHEMA =
295
+ TRELLO_CHECKLIST_ITEM_UPDATED_EVENT_SCHEMA.extend({
296
+ actionType: z.literal("updateCheckItemStateOnCard"),
297
+ checkItem: TRELLO_CHECK_ITEM_SCHEMA.extend({
298
+ state: z.literal("complete"),
299
+ }),
300
+ event: trelloWebhookEventSchema(z.literal("updateCheckItemStateOnCard")),
301
+ })
302
+ export const TRELLO_CHECKLIST_ITEM_DELETED_EVENT_SCHEMA = resourceEventSchema(
303
+ z.literal("deleteCheckItem"),
304
+ ).extend({
305
+ checkItem: TRELLO_CHECK_ITEM_SCHEMA,
306
+ checklist: TRELLO_RESOURCE_SCHEMA,
307
+ })
308
+ export const TRELLO_CUSTOM_FIELD_CREATED_EVENT_SCHEMA = resourceEventSchema(
309
+ z.literal("addCustomField"),
310
+ ).extend({ customField: TRELLO_RESOURCE_SCHEMA })
311
+ export const TRELLO_CUSTOM_FIELD_UPDATED_EVENT_SCHEMA = resourceEventSchema(
312
+ z.literal("updateCustomField"),
313
+ ).extend({ customField: TRELLO_RESOURCE_SCHEMA })
314
+ export const TRELLO_CUSTOM_FIELD_DELETED_EVENT_SCHEMA = resourceEventSchema(
315
+ z.literal("deleteCustomField"),
316
+ ).extend({ customField: TRELLO_RESOURCE_SCHEMA })
317
+ export const TRELLO_CUSTOM_FIELD_VALUE_UPDATED_EVENT_SCHEMA =
318
+ resourceEventSchema(z.literal("updateCustomFieldItem")).extend({
319
+ card: TRELLO_CARD_SCHEMA,
320
+ customField: TRELLO_RESOURCE_SCHEMA,
321
+ customFieldItem: TRELLO_CUSTOM_FIELD_ITEM_SCHEMA,
322
+ })
323
+ export const TRELLO_LIST_CREATED_EVENT_SCHEMA = resourceEventSchema(
324
+ z.literal("createList"),
325
+ ).extend({ list: TRELLO_LIST_SCHEMA })
326
+ export const TRELLO_LIST_UPDATED_EVENT_SCHEMA = resourceEventSchema(
327
+ z.literal("updateList"),
328
+ ).extend({ list: TRELLO_LIST_SCHEMA })
329
+ export const TRELLO_LIST_ARCHIVED_EVENT_SCHEMA =
330
+ TRELLO_LIST_UPDATED_EVENT_SCHEMA.extend({
331
+ list: TRELLO_LIST_SCHEMA.extend({ closed: z.literal(true) }),
332
+ })
333
+ export const TRELLO_LIST_RESTORED_EVENT_SCHEMA =
334
+ TRELLO_LIST_UPDATED_EVENT_SCHEMA.extend({
335
+ list: TRELLO_LIST_SCHEMA.extend({ closed: z.literal(false) }),
336
+ })
337
+ export const TRELLO_LIST_MOVED_EVENT_SCHEMA = resourceEventSchema(
338
+ z.enum(["moveListFromBoard", "moveListToBoard"]),
339
+ ).extend({ list: TRELLO_LIST_SCHEMA })
340
+
192
341
  /**
193
342
  * Normalizes one provider-native Trello board webhook payload.
194
343
  *
195
- * @param value - Untrusted Trello webhook body.
344
+ * @param value - Untrusted provider webhook payload.
196
345
  */
197
346
  export function normalizeTrelloBoardEvent(value: unknown) {
198
347
  const event = TRELLO_WEBHOOK_EVENT_SCHEMA.parse(value)
@@ -210,17 +359,17 @@ export function normalizeTrelloBoardEvent(value: unknown) {
210
359
  }
211
360
 
212
361
  /**
213
- * Classifies one normalized board action into semantic card event categories.
362
+ * Classifies one board action into every supported semantic event.
214
363
  *
215
- * A single Trello action can intentionally belong to more than one category;
216
- * for example, moving a card is also a card update.
217
- *
218
- * @param event - Normalized board event received from Trello.
364
+ * @param event - Normalized board event.
219
365
  */
220
- export function getTrelloCardEventKinds(
366
+ export function getTrelloEventKinds(
221
367
  event: z.output<typeof TRELLO_BOARD_EVENT_SCHEMA>,
222
- ): TrelloCardEventKind[] {
223
- const data = TRELLO_CARD_ACTION_DATA_SCHEMA.parse(event.event.action.data)
368
+ ): TrelloEventKind[] {
369
+ const data = TRELLO_ACTION_DATA_SCHEMA.parse(event.event.action.data)
370
+ const cardData = TRELLO_CARD_ACTION_DATA_SCHEMA.parse(data)
371
+ const listData = TRELLO_LIST_ACTION_DATA_SCHEMA.parse(data)
372
+ const actionType = event.actionType
224
373
 
225
374
  return [
226
375
  ...([
@@ -228,29 +377,133 @@ export function getTrelloCardEventKinds(
228
377
  "copyCard",
229
378
  "convertToCardFromCheckItem",
230
379
  "emailCard",
231
- ].includes(event.actionType)
232
- ? (["created"] as const)
380
+ ].includes(actionType)
381
+ ? (["card.created"] as const)
382
+ : []),
383
+ ...(actionType === "moveCardFromBoard" ||
384
+ actionType === "moveCardToBoard" ||
385
+ (actionType === "updateCard" && data.listBefore && data.listAfter)
386
+ ? (["card.moved"] as const)
387
+ : []),
388
+ ...(actionType === "updateCard" ? (["card.updated"] as const) : []),
389
+ ...(actionType === "updateCard" &&
390
+ cardData.old?.closed === false &&
391
+ cardData.card?.closed === true
392
+ ? (["card.archived"] as const)
393
+ : []),
394
+ ...(actionType === "updateCard" &&
395
+ cardData.old?.closed === true &&
396
+ cardData.card?.closed === false
397
+ ? (["card.restored"] as const)
398
+ : []),
399
+ ...(actionType === "deleteCard" ? (["card.deleted"] as const) : []),
400
+ ...(actionType === "commentCard" ? (["card.commented"] as const) : []),
401
+ ...(actionType === "updateComment" ? (["comment.updated"] as const) : []),
402
+ ...(actionType === "deleteComment" ? (["comment.deleted"] as const) : []),
403
+ ...(actionType === "addAttachmentToCard"
404
+ ? (["attachment.added"] as const)
233
405
  : []),
234
- ...(event.actionType === "updateCard" &&
235
- data.listBefore !== undefined &&
236
- data.listAfter !== undefined
237
- ? (["moved"] as const)
406
+ ...(actionType === "deleteAttachmentFromCard"
407
+ ? (["attachment.deleted"] as const)
238
408
  : []),
239
- ...(event.actionType === "updateCard" ? (["updated"] as const) : []),
240
- ...(event.actionType === "updateCard" &&
241
- data.old?.closed === false &&
242
- data.card?.closed === true
243
- ? (["archived"] as const)
409
+ ...(actionType === "addMemberToCard"
410
+ ? (["card.member.added"] as const)
411
+ : []),
412
+ ...(actionType === "removeMemberFromCard"
413
+ ? (["card.member.removed"] as const)
414
+ : []),
415
+ ...(actionType === "addLabelToCard" ? (["card.label.added"] as const) : []),
416
+ ...(actionType === "removeLabelFromCard"
417
+ ? (["card.label.removed"] as const)
418
+ : []),
419
+ ...(actionType === "addChecklistToCard"
420
+ ? (["checklist.created"] as const)
421
+ : []),
422
+ ...(actionType === "updateChecklist"
423
+ ? (["checklist.updated"] as const)
424
+ : []),
425
+ ...(actionType === "removeChecklistFromCard"
426
+ ? (["checklist.deleted"] as const)
427
+ : []),
428
+ ...(actionType === "createCheckItem"
429
+ ? (["checklist-item.created"] as const)
430
+ : []),
431
+ ...(actionType === "updateCheckItem" ||
432
+ actionType === "updateCheckItemStateOnCard"
433
+ ? (["checklist-item.updated"] as const)
434
+ : []),
435
+ ...(actionType === "updateCheckItemStateOnCard" &&
436
+ data.checkItem?.state === "complete"
437
+ ? (["checklist-item.completed"] as const)
438
+ : []),
439
+ ...(actionType === "deleteCheckItem"
440
+ ? (["checklist-item.deleted"] as const)
441
+ : []),
442
+ ...(actionType === "addCustomField"
443
+ ? (["custom-field.created"] as const)
444
+ : []),
445
+ ...(actionType === "updateCustomField"
446
+ ? (["custom-field.updated"] as const)
447
+ : []),
448
+ ...(actionType === "deleteCustomField"
449
+ ? (["custom-field.deleted"] as const)
450
+ : []),
451
+ ...(actionType === "updateCustomFieldItem"
452
+ ? (["custom-field.value.updated"] as const)
453
+ : []),
454
+ ...(actionType === "createList" ? (["list.created"] as const) : []),
455
+ ...(actionType === "updateList" ? (["list.updated"] as const) : []),
456
+ ...(actionType === "updateList" &&
457
+ listData.old?.closed === false &&
458
+ listData.list?.closed === true
459
+ ? (["list.archived"] as const)
460
+ : []),
461
+ ...(actionType === "updateList" &&
462
+ listData.old?.closed === true &&
463
+ listData.list?.closed === false
464
+ ? (["list.restored"] as const)
465
+ : []),
466
+ ...(actionType === "moveListFromBoard" || actionType === "moveListToBoard"
467
+ ? (["list.moved"] as const)
244
468
  : []),
245
- ...(event.actionType === "commentCard" ? (["commented"] as const) : []),
246
469
  ]
247
470
  }
248
471
 
249
472
  /**
250
- * Promotes provider-native card action data into the stable semantic payload.
473
+ * Classifies one board action into the card-oriented event categories.
251
474
  *
252
- * @param event - Normalized board event classified as a card event.
253
- * @throws When the classified Trello action does not identify a card.
475
+ * @param event - Normalized board event.
476
+ */
477
+ export function getTrelloCardEventKinds(
478
+ event: z.output<typeof TRELLO_BOARD_EVENT_SCHEMA>,
479
+ ): TrelloCardEventKind[] {
480
+ return getTrelloEventKinds(event).flatMap((kind) => {
481
+ switch (kind) {
482
+ case "card.created":
483
+ return ["created"]
484
+ case "card.moved":
485
+ return ["moved"]
486
+ case "card.updated":
487
+ return ["updated"]
488
+ case "card.archived":
489
+ return ["archived"]
490
+ case "card.restored":
491
+ return ["restored"]
492
+ case "card.deleted":
493
+ return ["deleted"]
494
+ case "card.commented":
495
+ return ["commented"]
496
+ default:
497
+ return []
498
+ }
499
+ })
500
+ }
501
+
502
+ /**
503
+ * Promotes provider-native card action data into the stable card payload.
504
+ *
505
+ * @param event - Normalized board event.
506
+ * @throws When the provider action contains no card data.
254
507
  */
255
508
  export function normalizeTrelloCardEvent(
256
509
  event: z.output<typeof TRELLO_BOARD_EVENT_SCHEMA>,
@@ -275,9 +528,56 @@ export function normalizeTrelloCardEvent(
275
528
  }
276
529
 
277
530
  /**
278
- * Narrows the provider action type retained under a semantic event.
531
+ * Promotes provider action data into a shared resource-event payload.
532
+ *
533
+ * @param event - Normalized board event.
534
+ */
535
+ export function normalizeTrelloResourceEvent(
536
+ event: z.output<typeof TRELLO_BOARD_EVENT_SCHEMA>,
537
+ ) {
538
+ const data = TRELLO_ACTION_DATA_SCHEMA.parse(event.event.action.data)
539
+
540
+ return TRELLO_RESOURCE_EVENT_SCHEMA.parse({
541
+ actionId: event.actionId,
542
+ actionType: event.actionType,
543
+ attachment: data.attachment,
544
+ board: event.board,
545
+ card: data.card,
546
+ checklist: data.checklist,
547
+ checkItem: data.checkItem,
548
+ comment: data.action,
549
+ customField: data.customField,
550
+ customFieldItem: data.customFieldItem,
551
+ event: event.event,
552
+ label: data.label,
553
+ list: data.listAfter ?? data.list,
554
+ listAfter: data.listAfter,
555
+ listBefore: data.listBefore,
556
+ member: data.member,
557
+ memberCreator: event.memberCreator,
558
+ occurredAt: event.occurredAt,
559
+ old: data.old,
560
+ })
561
+ }
562
+
563
+ /**
564
+ * Narrows the shared resource schema to one provider action type.
565
+ *
566
+ * @param actionType - Supported provider action schema.
567
+ */
568
+ function resourceEventSchema<TAction extends z.ZodType<string>>(
569
+ actionType: TAction,
570
+ ) {
571
+ return TRELLO_RESOURCE_EVENT_SCHEMA.extend({
572
+ actionType,
573
+ event: trelloWebhookEventSchema(actionType),
574
+ })
575
+ }
576
+
577
+ /**
578
+ * Narrows the raw webhook schema to one provider action type.
279
579
  *
280
- * @param actionType - Provider action-type schema for the semantic event.
580
+ * @param actionType - Supported provider action schema.
281
581
  */
282
582
  function trelloWebhookEventSchema<TAction extends z.ZodType<string>>(
283
583
  actionType: TAction,