@automate.ax/integration-contracts 0.110.0 → 0.111.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.
@@ -1,20 +1,14 @@
1
1
  import * as z from "zod"
2
-
3
- export const ASANA_RESOURCE_SCHEMA = z.object({
4
- /** Stable Asana globally unique identifier. */
5
- id: z.string(),
6
-
7
- /** Human-readable resource name, when available. */
8
- name: z.string().optional(),
9
-
10
- /** Provider resource type. */
11
- type: z.string().optional(),
12
- })
13
-
14
- export const ASANA_PAGE_INFO_SCHEMA = z.object({
15
- /** Opaque token for the next page, when more results are available. */
16
- nextOffset: z.string().optional(),
17
- })
2
+ import {
3
+ ASANA_PROVIDER_RESOURCE_SCHEMA,
4
+ ASANA_RESOURCE_SCHEMA,
5
+ toAsanaResource,
6
+ } from "./base"
7
+ import {
8
+ ASANA_CUSTOM_FIELD_VALUE_SCHEMA,
9
+ ASANA_PROVIDER_CUSTOM_FIELD_VALUE_SCHEMA,
10
+ toAsanaCustomFieldValue,
11
+ } from "./resources"
18
12
 
19
13
  export const ASANA_WORKSPACE_SCHEMA = ASANA_RESOURCE_SCHEMA.extend({
20
14
  /** Domains associated with this workspace. */
@@ -114,8 +108,8 @@ export const ASANA_TASK_SCHEMA = ASANA_RESOURCE_SCHEMA.extend({
114
108
  /** User who created the task. */
115
109
  createdBy: ASANA_RESOURCE_SCHEMA.optional(),
116
110
 
117
- /** Provider-native custom field values. */
118
- customFields: z.json().array(),
111
+ /** Typed custom field values with camelCase nested fields. */
112
+ customFields: ASANA_CUSTOM_FIELD_VALUE_SCHEMA.array(),
119
113
 
120
114
  /** Task due timestamp, for tasks with a due time. */
121
115
  dueAt: z.string().optional(),
@@ -325,22 +319,6 @@ export const ASANA_COMMENT_ADDED_EVENT_SCHEMA = ASANA_STORY_SCHEMA.extend({
325
319
  type: z.literal("story"),
326
320
  })
327
321
 
328
- export const ASANA_PROVIDER_RESOURCE_SCHEMA = z.looseObject({
329
- gid: z.string(),
330
- name: z.string().optional(),
331
- resource_subtype: z.string().optional(),
332
- resource_type: z.string().optional(),
333
- })
334
-
335
- export const ASANA_PROVIDER_PAGE_SCHEMA = z.looseObject({
336
- next_page: z
337
- .looseObject({
338
- offset: z.string(),
339
- })
340
- .nullable()
341
- .optional(),
342
- })
343
-
344
322
  const ASANA_PROVIDER_MEMBERSHIP_SCHEMA = z.looseObject({
345
323
  project: ASANA_PROVIDER_RESOURCE_SCHEMA,
346
324
  section: ASANA_PROVIDER_RESOURCE_SCHEMA.nullish(),
@@ -348,8 +326,8 @@ const ASANA_PROVIDER_MEMBERSHIP_SCHEMA = z.looseObject({
348
326
 
349
327
  export const ASANA_PROVIDER_WORKSPACE_SCHEMA =
350
328
  ASANA_PROVIDER_RESOURCE_SCHEMA.extend({
351
- email_domains: z.string().array().prefault([]),
352
- is_organization: z.boolean().prefault(false),
329
+ email_domains: z.string().array(),
330
+ is_organization: z.boolean(),
353
331
  })
354
332
 
355
333
  export const ASANA_PROVIDER_USER_SCHEMA = ASANA_PROVIDER_RESOURCE_SCHEMA.extend(
@@ -360,13 +338,13 @@ export const ASANA_PROVIDER_USER_SCHEMA = ASANA_PROVIDER_RESOURCE_SCHEMA.extend(
360
338
  image_128x128: z.string().optional(),
361
339
  })
362
340
  .nullish(),
363
- workspaces: ASANA_PROVIDER_RESOURCE_SCHEMA.array().prefault([]),
341
+ workspaces: ASANA_PROVIDER_RESOURCE_SCHEMA.array(),
364
342
  },
365
343
  )
366
344
 
367
345
  export const ASANA_PROVIDER_PROJECT_SCHEMA =
368
346
  ASANA_PROVIDER_RESOURCE_SCHEMA.extend({
369
- archived: z.boolean().prefault(false),
347
+ archived: z.boolean(),
370
348
  color: z.string().nullish(),
371
349
  created_at: z.string().optional(),
372
350
  default_view: z.string().optional(),
@@ -375,7 +353,7 @@ export const ASANA_PROVIDER_PROJECT_SCHEMA =
375
353
  modified_at: z.string().optional(),
376
354
  notes: z.string().optional(),
377
355
  permalink_url: z.string().optional(),
378
- public: z.boolean().prefault(false),
356
+ public: z.boolean(),
379
357
  start_on: z.string().nullish(),
380
358
  team: ASANA_PROVIDER_RESOURCE_SCHEMA.nullish(),
381
359
  workspace: ASANA_PROVIDER_RESOURCE_SCHEMA.nullish(),
@@ -391,26 +369,26 @@ export const ASANA_PROVIDER_TASK_SCHEMA = ASANA_PROVIDER_RESOURCE_SCHEMA.extend(
391
369
  {
392
370
  assignee: ASANA_PROVIDER_RESOURCE_SCHEMA.nullish(),
393
371
  assignee_status: z.string().optional(),
394
- completed: z.boolean().prefault(false),
372
+ completed: z.boolean(),
395
373
  completed_at: z.string().nullish(),
396
374
  completed_by: ASANA_PROVIDER_RESOURCE_SCHEMA.nullish(),
397
375
  created_at: z.string().optional(),
398
376
  created_by: ASANA_PROVIDER_RESOURCE_SCHEMA.nullish(),
399
- custom_fields: z.json().array().prefault([]),
377
+ custom_fields: ASANA_PROVIDER_CUSTOM_FIELD_VALUE_SCHEMA.array().optional(),
400
378
  due_at: z.string().nullish(),
401
379
  due_on: z.string().nullish(),
402
- followers: ASANA_PROVIDER_RESOURCE_SCHEMA.array().prefault([]),
380
+ followers: ASANA_PROVIDER_RESOURCE_SCHEMA.array(),
403
381
  html_notes: z.string().optional(),
404
- memberships: ASANA_PROVIDER_MEMBERSHIP_SCHEMA.array().prefault([]),
382
+ memberships: ASANA_PROVIDER_MEMBERSHIP_SCHEMA.array(),
405
383
  modified_at: z.string().optional(),
406
384
  notes: z.string().optional(),
407
385
  parent: ASANA_PROVIDER_RESOURCE_SCHEMA.nullish(),
408
386
  permalink_url: z.string().optional(),
409
- projects: ASANA_PROVIDER_RESOURCE_SCHEMA.array().prefault([]),
387
+ projects: ASANA_PROVIDER_RESOURCE_SCHEMA.array(),
410
388
  resource_subtype: z.string().optional(),
411
389
  start_at: z.string().nullish(),
412
390
  start_on: z.string().nullish(),
413
- tags: ASANA_PROVIDER_RESOURCE_SCHEMA.array().prefault([]),
391
+ tags: ASANA_PROVIDER_RESOURCE_SCHEMA.array(),
414
392
  workspace: ASANA_PROVIDER_RESOURCE_SCHEMA.nullish(),
415
393
  },
416
394
  )
@@ -420,10 +398,10 @@ export const ASANA_PROVIDER_STORY_SCHEMA =
420
398
  created_at: z.string().optional(),
421
399
  created_by: ASANA_PROVIDER_RESOURCE_SCHEMA.nullish(),
422
400
  html_text: z.string().optional(),
423
- is_edited: z.boolean().prefault(false),
424
- is_pinned: z.boolean().prefault(false),
401
+ is_edited: z.boolean(),
402
+ is_pinned: z.boolean(),
425
403
  resource_subtype: z.string().optional(),
426
- text: z.string().prefault(""),
404
+ text: z.string(),
427
405
  })
428
406
 
429
407
  const ASANA_PROVIDER_EVENT_CHANGE_SCHEMA = z.looseObject({
@@ -444,32 +422,6 @@ export const ASANA_PROVIDER_EVENT_SCHEMA = z.looseObject({
444
422
  user: ASANA_PROVIDER_RESOURCE_SCHEMA.nullish(),
445
423
  })
446
424
 
447
- /**
448
- * Converts an Asana compact resource to the public camel-cased shape.
449
- *
450
- * @param value - Provider-native compact resource.
451
- */
452
- export function toAsanaResource(
453
- value: z.output<typeof ASANA_PROVIDER_RESOURCE_SCHEMA>,
454
- ) {
455
- return {
456
- id: value.gid,
457
- name: value.name,
458
- type: value.resource_type,
459
- }
460
- }
461
-
462
- /**
463
- * Converts Asana offset pagination metadata.
464
- *
465
- * @param value - Provider-native response page.
466
- */
467
- export function toAsanaPageInfo(
468
- value: z.output<typeof ASANA_PROVIDER_PAGE_SCHEMA>,
469
- ) {
470
- return { nextOffset: value.next_page?.offset }
471
- }
472
-
473
425
  /**
474
426
  * Converts an Asana workspace to the public camel-cased shape.
475
427
  *
@@ -561,7 +513,7 @@ export function toAsanaTask(
561
513
  : undefined,
562
514
  createdAt: value.created_at,
563
515
  createdBy: value.created_by ? toAsanaResource(value.created_by) : undefined,
564
- customFields: value.custom_fields,
516
+ customFields: (value.custom_fields ?? []).map(toAsanaCustomFieldValue),
565
517
  dueAt: value.due_at ?? undefined,
566
518
  dueOn: value.due_on ?? undefined,
567
519
  followers: value.followers.map(toAsanaResource),
@@ -616,10 +568,10 @@ export function normalizeAsanaEvent(value: unknown, watchedResourceId: string) {
616
568
  change: event.change
617
569
  ? {
618
570
  action: event.change.action,
619
- addedValue: event.change.added_value,
620
- field: event.change.field,
621
- newValue: event.change.new_value,
622
- removedValue: event.change.removed_value,
571
+ addedValue: camelCaseAsanaValue(event.change.added_value),
572
+ field: camelCaseAsanaKey(event.change.field),
573
+ newValue: camelCaseAsanaValue(event.change.new_value),
574
+ removedValue: camelCaseAsanaValue(event.change.removed_value),
623
575
  }
624
576
  : undefined,
625
577
  createdAt: event.created_at,
@@ -653,3 +605,49 @@ export function normalizeAsanaTask(value: unknown) {
653
605
  toAsanaTask(ASANA_PROVIDER_TASK_SCHEMA.parse(value)),
654
606
  )
655
607
  }
608
+
609
+ /**
610
+ * Converts arbitrary provider JSON keys to camelCase recursively.
611
+ *
612
+ * @param value - Provider value to normalize.
613
+ */
614
+ export function camelCaseAsanaValue(value: unknown): unknown {
615
+ if (Array.isArray(value)) return value.map(camelCaseAsanaValue)
616
+ if (value === null || typeof value !== "object") return value
617
+ const gid = "gid" in value ? value.gid : undefined
618
+ const name = "name" in value ? value.name : undefined
619
+ const resourceType =
620
+ "resource_type" in value ? value.resource_type : undefined
621
+ if (typeof gid === "string" && "resource_type" in value) {
622
+ return {
623
+ ...Object.fromEntries(
624
+ Object.entries(value)
625
+ .filter(([key]) => !["gid", "name", "resource_type"].includes(key))
626
+ .map(([key, item]) => [
627
+ camelCaseAsanaKey(key),
628
+ camelCaseAsanaValue(item),
629
+ ]),
630
+ ),
631
+ id: gid,
632
+ ...(typeof name === "string" && { name }),
633
+ ...(typeof resourceType === "string" && { type: resourceType }),
634
+ }
635
+ }
636
+ return Object.fromEntries(
637
+ Object.entries(value).map(([key, item]) => [
638
+ camelCaseAsanaKey(key),
639
+ camelCaseAsanaValue(item),
640
+ ]),
641
+ )
642
+ }
643
+
644
+ /**
645
+ * Converts one snake_case provider key to camelCase.
646
+ *
647
+ * @param value - Provider key.
648
+ */
649
+ function camelCaseAsanaKey(value: string) {
650
+ return value.replace(/_([a-z0-9])/g, (_, character: string) =>
651
+ character.toUpperCase(),
652
+ )
653
+ }
@@ -0,0 +1,201 @@
1
+ import * as z from "zod"
2
+ import { ASANA_RESOURCE_SCHEMA } from "./base"
3
+ import {
4
+ ASANA_EVENT_CHANGE_SCHEMA,
5
+ ASANA_EVENT_SCHEMA,
6
+ ASANA_TASK_SCHEMA,
7
+ } from "./schemas"
8
+
9
+ /**
10
+ * Builds a lifecycle-event schema for one resource and action.
11
+ *
12
+ * @param resourceType - Provider resource category.
13
+ * @param action - Provider lifecycle action.
14
+ */
15
+ function resourceEventSchema<
16
+ TResourceType extends string,
17
+ TAction extends string,
18
+ >(resourceType: TResourceType, action: TAction) {
19
+ return ASANA_RESOURCE_SCHEMA.extend({
20
+ event: ASANA_EVENT_SCHEMA.extend({
21
+ action: z.literal(action),
22
+ resourceType: z.literal(resourceType),
23
+ }),
24
+ type: z.literal(resourceType),
25
+ })
26
+ }
27
+
28
+ /**
29
+ * Builds a field-change event schema for one resource field.
30
+ *
31
+ * @param resourceType - Provider resource category.
32
+ * @param field - Normalized changed field.
33
+ * @param changeAction - Provider field-change action.
34
+ */
35
+ function changedFieldEventSchema<
36
+ TResourceType extends string,
37
+ TField extends string,
38
+ TChangeAction extends "added" | "changed" | "removed",
39
+ >(resourceType: TResourceType, field: TField, changeAction: TChangeAction) {
40
+ return ASANA_RESOURCE_SCHEMA.extend({
41
+ event: ASANA_EVENT_SCHEMA.extend({
42
+ action: z.literal("changed"),
43
+ change: ASANA_EVENT_CHANGE_SCHEMA.extend({
44
+ action: z.literal(changeAction),
45
+ field: z.literal(field),
46
+ }),
47
+ resourceType: z.literal(resourceType),
48
+ }),
49
+ type: z.literal(resourceType),
50
+ })
51
+ }
52
+
53
+ export const ASANA_TASK_RESTORED_EVENT_SCHEMA = resourceEventSchema(
54
+ "task",
55
+ "undeleted",
56
+ )
57
+ const ASANA_TASK_COMPLETION_CHANGED_EVENT_SCHEMA = ASANA_EVENT_SCHEMA.extend({
58
+ action: z.literal("changed"),
59
+ change: ASANA_EVENT_CHANGE_SCHEMA.extend({
60
+ action: z.literal("changed"),
61
+ field: z.literal("completed"),
62
+ }),
63
+ resourceType: z.literal("task"),
64
+ })
65
+ export const ASANA_TASK_INCOMPLETE_EVENT_SCHEMA = ASANA_TASK_SCHEMA.extend({
66
+ completed: z.literal(false),
67
+ event: ASANA_TASK_COMPLETION_CHANGED_EVENT_SCHEMA,
68
+ type: z.literal("task"),
69
+ })
70
+ const ASANA_TASK_ASSIGNEE_CHANGED_EVENT_SCHEMA = ASANA_EVENT_SCHEMA.extend({
71
+ action: z.literal("changed"),
72
+ change: ASANA_EVENT_CHANGE_SCHEMA.extend({
73
+ action: z.literal("changed"),
74
+ field: z.literal("assignee"),
75
+ }),
76
+ resourceType: z.literal("task"),
77
+ })
78
+ export const ASANA_TASK_ASSIGNED_EVENT_SCHEMA = ASANA_RESOURCE_SCHEMA.extend({
79
+ event: ASANA_TASK_ASSIGNEE_CHANGED_EVENT_SCHEMA.extend({
80
+ change: ASANA_TASK_ASSIGNEE_CHANGED_EVENT_SCHEMA.shape.change.extend({
81
+ newValue: ASANA_RESOURCE_SCHEMA,
82
+ }),
83
+ }),
84
+ type: z.literal("task"),
85
+ })
86
+ export const ASANA_TASK_UNASSIGNED_EVENT_SCHEMA = ASANA_RESOURCE_SCHEMA.extend({
87
+ event: ASANA_TASK_ASSIGNEE_CHANGED_EVENT_SCHEMA.extend({
88
+ change: ASANA_TASK_ASSIGNEE_CHANGED_EVENT_SCHEMA.shape.change.extend({
89
+ newValue: z.null().optional(),
90
+ }),
91
+ }),
92
+ type: z.literal("task"),
93
+ })
94
+ export const ASANA_TASK_DUE_DATE_CHANGED_EVENT_SCHEMA = z.union([
95
+ changedFieldEventSchema("task", "dueAt", "changed"),
96
+ changedFieldEventSchema("task", "dueOn", "changed"),
97
+ ])
98
+ export const ASANA_TASK_FOLLOWER_ADDED_EVENT_SCHEMA = changedFieldEventSchema(
99
+ "task",
100
+ "followers",
101
+ "added",
102
+ )
103
+ export const ASANA_TASK_FOLLOWER_REMOVED_EVENT_SCHEMA = changedFieldEventSchema(
104
+ "task",
105
+ "followers",
106
+ "removed",
107
+ )
108
+ export const ASANA_TASK_PROJECT_ADDED_EVENT_SCHEMA = changedFieldEventSchema(
109
+ "task",
110
+ "projects",
111
+ "added",
112
+ )
113
+ export const ASANA_TASK_PROJECT_REMOVED_EVENT_SCHEMA = changedFieldEventSchema(
114
+ "task",
115
+ "projects",
116
+ "removed",
117
+ )
118
+ export const ASANA_TASK_TAG_ADDED_EVENT_SCHEMA = changedFieldEventSchema(
119
+ "task",
120
+ "tags",
121
+ "added",
122
+ )
123
+ export const ASANA_TASK_TAG_REMOVED_EVENT_SCHEMA = changedFieldEventSchema(
124
+ "task",
125
+ "tags",
126
+ "removed",
127
+ )
128
+
129
+ export const ASANA_COMMENT_UPDATED_EVENT_SCHEMA = resourceEventSchema(
130
+ "story",
131
+ "changed",
132
+ )
133
+ export const ASANA_COMMENT_DELETED_EVENT_SCHEMA = resourceEventSchema(
134
+ "story",
135
+ "removed",
136
+ )
137
+ export const ASANA_COMMENT_RESTORED_EVENT_SCHEMA = resourceEventSchema(
138
+ "story",
139
+ "undeleted",
140
+ )
141
+
142
+ export const ASANA_SECTION_ADDED_EVENT_SCHEMA = resourceEventSchema(
143
+ "section",
144
+ "added",
145
+ )
146
+ export const ASANA_SECTION_CHANGED_EVENT_SCHEMA = resourceEventSchema(
147
+ "section",
148
+ "changed",
149
+ )
150
+ export const ASANA_SECTION_DELETED_EVENT_SCHEMA = resourceEventSchema(
151
+ "section",
152
+ "deleted",
153
+ )
154
+ export const ASANA_SECTION_RESTORED_EVENT_SCHEMA = resourceEventSchema(
155
+ "section",
156
+ "undeleted",
157
+ )
158
+
159
+ export const ASANA_PROJECT_ADDED_EVENT_SCHEMA = resourceEventSchema(
160
+ "project",
161
+ "added",
162
+ )
163
+ export const ASANA_PROJECT_CHANGED_EVENT_SCHEMA = resourceEventSchema(
164
+ "project",
165
+ "changed",
166
+ )
167
+ export const ASANA_PROJECT_REMOVED_EVENT_SCHEMA = resourceEventSchema(
168
+ "project",
169
+ "removed",
170
+ )
171
+ export const ASANA_PROJECT_DELETED_EVENT_SCHEMA = resourceEventSchema(
172
+ "project",
173
+ "deleted",
174
+ )
175
+ export const ASANA_PROJECT_RESTORED_EVENT_SCHEMA = resourceEventSchema(
176
+ "project",
177
+ "undeleted",
178
+ )
179
+
180
+ export const ASANA_ATTACHMENT_DELETED_EVENT_SCHEMA = resourceEventSchema(
181
+ "attachment",
182
+ "deleted",
183
+ )
184
+ export const ASANA_ATTACHMENT_RESTORED_EVENT_SCHEMA = resourceEventSchema(
185
+ "attachment",
186
+ "undeleted",
187
+ )
188
+
189
+ export const ASANA_TAG_ADDED_EVENT_SCHEMA = resourceEventSchema("tag", "added")
190
+ export const ASANA_TAG_CHANGED_EVENT_SCHEMA = resourceEventSchema(
191
+ "tag",
192
+ "changed",
193
+ )
194
+ export const ASANA_TAG_DELETED_EVENT_SCHEMA = resourceEventSchema(
195
+ "tag",
196
+ "deleted",
197
+ )
198
+ export const ASANA_TAG_RESTORED_EVENT_SCHEMA = resourceEventSchema(
199
+ "tag",
200
+ "undeleted",
201
+ )