@automate.ax/integration-contracts 0.141.0 → 0.143.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.
@@ -33,11 +33,11 @@ export declare const TEAMS_CHANGE_EVENT_SCHEMAS: {
33
33
  channelId: z.ZodString;
34
34
  isArchived: z.ZodPrefault<z.ZodBoolean>;
35
35
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
36
- standard: "standard";
37
36
  private: "private";
37
+ standard: "standard";
38
38
  shared: "shared";
39
39
  unknownFutureValue: "unknownFutureValue";
40
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
40
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
41
41
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
42
42
  }, z.core.$strip>;
43
43
  resourceId: z.ZodString;
@@ -53,11 +53,11 @@ export declare const TEAMS_CHANGE_EVENT_SCHEMAS: {
53
53
  channelId: z.ZodString;
54
54
  isArchived: z.ZodPrefault<z.ZodBoolean>;
55
55
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
56
- standard: "standard";
57
56
  private: "private";
57
+ standard: "standard";
58
58
  shared: "shared";
59
59
  unknownFutureValue: "unknownFutureValue";
60
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
60
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
61
61
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
62
62
  }, z.core.$strip>;
63
63
  resourceId: z.ZodString;
@@ -79,11 +79,11 @@ export declare const TEAMS_CHANGE_EVENT_SCHEMAS: {
79
79
  channelId: z.ZodString;
80
80
  isArchived: z.ZodPrefault<z.ZodBoolean>;
81
81
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
82
- standard: "standard";
83
82
  private: "private";
83
+ standard: "standard";
84
84
  shared: "shared";
85
85
  unknownFutureValue: "unknownFutureValue";
86
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
86
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
87
87
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
88
88
  }, z.core.$strip>;
89
89
  resourceId: z.ZodString;
@@ -100,11 +100,11 @@ export declare const TEAMS_CHANGE_EVENT_SCHEMAS: {
100
100
  channelId: z.ZodString;
101
101
  isArchived: z.ZodPrefault<z.ZodBoolean>;
102
102
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
103
- standard: "standard";
104
103
  private: "private";
104
+ standard: "standard";
105
105
  shared: "shared";
106
106
  unknownFutureValue: "unknownFutureValue";
107
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
107
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
108
108
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
109
109
  }, z.core.$strip>;
110
110
  resourceId: z.ZodString;
@@ -883,11 +883,11 @@ export declare const TEAMS_CHANGE_EVENT_SCHEMA: z.ZodUnion<readonly [z.ZodDiscri
883
883
  channelId: z.ZodString;
884
884
  isArchived: z.ZodPrefault<z.ZodBoolean>;
885
885
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
886
- standard: "standard";
887
886
  private: "private";
887
+ standard: "standard";
888
888
  shared: "shared";
889
889
  unknownFutureValue: "unknownFutureValue";
890
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
890
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
891
891
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
892
892
  }, z.core.$strip>;
893
893
  resourceId: z.ZodString;
@@ -903,11 +903,11 @@ export declare const TEAMS_CHANGE_EVENT_SCHEMA: z.ZodUnion<readonly [z.ZodDiscri
903
903
  channelId: z.ZodString;
904
904
  isArchived: z.ZodPrefault<z.ZodBoolean>;
905
905
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
906
- standard: "standard";
907
906
  private: "private";
907
+ standard: "standard";
908
908
  shared: "shared";
909
909
  unknownFutureValue: "unknownFutureValue";
910
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
910
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
911
911
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
912
912
  }, z.core.$strip>;
913
913
  resourceId: z.ZodString;
@@ -1527,11 +1527,11 @@ export declare const teamsTriggerContracts: {
1527
1527
  channelId: z.ZodString;
1528
1528
  isArchived: z.ZodPrefault<z.ZodBoolean>;
1529
1529
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
1530
- standard: "standard";
1531
1530
  private: "private";
1531
+ standard: "standard";
1532
1532
  shared: "shared";
1533
1533
  unknownFutureValue: "unknownFutureValue";
1534
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
1534
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
1535
1535
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
1536
1536
  }, z.core.$strip>;
1537
1537
  resourceId: z.ZodString;
@@ -1547,11 +1547,11 @@ export declare const teamsTriggerContracts: {
1547
1547
  channelId: z.ZodString;
1548
1548
  isArchived: z.ZodPrefault<z.ZodBoolean>;
1549
1549
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
1550
- standard: "standard";
1551
1550
  private: "private";
1551
+ standard: "standard";
1552
1552
  shared: "shared";
1553
1553
  unknownFutureValue: "unknownFutureValue";
1554
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
1554
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
1555
1555
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
1556
1556
  }, z.core.$strip>;
1557
1557
  resourceId: z.ZodString;
@@ -1578,11 +1578,11 @@ export declare const teamsTriggerContracts: {
1578
1578
  channelId: z.ZodString;
1579
1579
  isArchived: z.ZodPrefault<z.ZodBoolean>;
1580
1580
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
1581
- standard: "standard";
1582
1581
  private: "private";
1582
+ standard: "standard";
1583
1583
  shared: "shared";
1584
1584
  unknownFutureValue: "unknownFutureValue";
1585
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
1585
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
1586
1586
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
1587
1587
  }, z.core.$strip>;
1588
1588
  resourceId: z.ZodString;
@@ -1604,11 +1604,11 @@ export declare const teamsTriggerContracts: {
1604
1604
  channelId: z.ZodString;
1605
1605
  isArchived: z.ZodPrefault<z.ZodBoolean>;
1606
1606
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
1607
- standard: "standard";
1608
1607
  private: "private";
1608
+ standard: "standard";
1609
1609
  shared: "shared";
1610
1610
  unknownFutureValue: "unknownFutureValue";
1611
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
1611
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
1612
1612
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
1613
1613
  }, z.core.$strip>;
1614
1614
  resourceId: z.ZodString;
@@ -56,11 +56,11 @@ export declare const TEAMS_CHANNEL_SCHEMA: z.ZodObject<{
56
56
  channelId: z.ZodString;
57
57
  isArchived: z.ZodPrefault<z.ZodBoolean>;
58
58
  membershipType: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
59
- standard: "standard";
60
59
  private: "private";
60
+ standard: "standard";
61
61
  shared: "shared";
62
62
  unknownFutureValue: "unknownFutureValue";
63
- }>>>, z.ZodTransform<"standard" | "private" | "shared" | "unknownFutureValue" | undefined, "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined>>;
63
+ }>>>, z.ZodTransform<"private" | "standard" | "shared" | "unknownFutureValue" | undefined, "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined>>;
64
64
  webUrl: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>;
65
65
  }, z.core.$strip>;
66
66
  export declare const GRAPH_CHANNEL_SCHEMA: z.ZodPipe<z.ZodObject<{
@@ -71,8 +71,8 @@ export declare const GRAPH_CHANNEL_SCHEMA: z.ZodPipe<z.ZodObject<{
71
71
  id: z.ZodString;
72
72
  isArchived: z.ZodOptional<z.ZodBoolean>;
73
73
  membershipType: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
74
- standard: "standard";
75
74
  private: "private";
75
+ standard: "standard";
76
76
  shared: "shared";
77
77
  unknownFutureValue: "unknownFutureValue";
78
78
  }>>>;
@@ -84,7 +84,7 @@ export declare const GRAPH_CHANNEL_SCHEMA: z.ZodPipe<z.ZodObject<{
84
84
  email: string | undefined;
85
85
  channelId: string;
86
86
  isArchived: boolean;
87
- membershipType: "standard" | "private" | "shared" | "unknownFutureValue" | undefined;
87
+ membershipType: "private" | "standard" | "shared" | "unknownFutureValue" | undefined;
88
88
  webUrl: string | undefined;
89
89
  }, {
90
90
  [x: string]: unknown;
@@ -94,7 +94,7 @@ export declare const GRAPH_CHANNEL_SCHEMA: z.ZodPipe<z.ZodObject<{
94
94
  description?: string | null | undefined;
95
95
  email?: string | null | undefined;
96
96
  isArchived?: boolean | undefined;
97
- membershipType?: "standard" | "private" | "shared" | "unknownFutureValue" | null | undefined;
97
+ membershipType?: "private" | "standard" | "shared" | "unknownFutureValue" | null | undefined;
98
98
  webUrl?: string | null | undefined;
99
99
  }>>;
100
100
  export declare const TEAMS_MEMBER_SCHEMA: z.ZodObject<{
@@ -9,12 +9,14 @@ import type { convexTriggerContracts } from "./convex/index.js";
9
9
  import type { discordTriggerContracts } from "./discord/index.js";
10
10
  import type { closeTriggerContracts } from "./close/index.js";
11
11
  import type { cloudflareTriggerContracts } from "./cloudflare/index.js";
12
+ import type { clickUpTriggerContracts } from "./clickup/index.js";
12
13
  import type { githubTriggerContracts } from "./github/index.js";
13
14
  import type { gmailTriggerContracts } from "./gmail/index.js";
14
15
  import type { googleCalendarTriggerContracts } from "./google-calendar/index.js";
15
16
  import type { googleDriveTriggerContracts } from "./google-drive/index.js";
16
17
  import type { googleFormsTriggerContracts } from "./google-forms/index.js";
17
18
  import type { googleMeetTriggerContracts } from "./google-meet/index.js";
19
+ import type { googleAdsTriggerContracts } from "./google-ads/index.js";
18
20
  import type { googleSheetsTriggerContracts } from "./google-sheets/index.js";
19
21
  import type { hubspotTriggerContracts } from "./hubspot/index.js";
20
22
  import type { linearTriggerContracts } from "./linear/index.js";
@@ -32,7 +34,7 @@ import type { vercelTriggerContracts } from "./vercel/index.js";
32
34
  import type { webflowTriggerContracts } from "./webflow/index.js";
33
35
  import type { whatsappTriggerContracts } from "./whatsapp/index.js";
34
36
  import type { z } from "zod";
35
- export type TriggerContractMap = typeof airtableTriggerContracts & typeof anthropicTriggerContracts & typeof apifyTriggerContracts & typeof axiomTriggerContracts & typeof automateTriggerContracts & typeof asanaTriggerContracts & typeof brevoTriggerContracts & typeof convexTriggerContracts & typeof discordTriggerContracts & typeof closeTriggerContracts & typeof cloudflareTriggerContracts & typeof githubTriggerContracts & typeof gmailTriggerContracts & typeof googleCalendarTriggerContracts & typeof googleDriveTriggerContracts & typeof googleFormsTriggerContracts & typeof googleMeetTriggerContracts & typeof googleSheetsTriggerContracts & typeof hubspotTriggerContracts & typeof linearTriggerContracts & typeof millionVerifierTriggerContracts & typeof metaAdsTriggerContracts & typeof notionTriggerContracts & typeof outlookTriggerContracts & typeof redditTriggerContracts & typeof resendTriggerContracts & typeof slackTriggerContracts & typeof stripeTriggerContracts & typeof teamsTriggerContracts & typeof trelloTriggerContracts & typeof vercelTriggerContracts & typeof webflowTriggerContracts & typeof whatsappTriggerContracts;
37
+ export type TriggerContractMap = typeof airtableTriggerContracts & typeof anthropicTriggerContracts & typeof apifyTriggerContracts & typeof axiomTriggerContracts & typeof automateTriggerContracts & typeof asanaTriggerContracts & typeof brevoTriggerContracts & typeof convexTriggerContracts & typeof discordTriggerContracts & typeof closeTriggerContracts & typeof clickUpTriggerContracts & typeof cloudflareTriggerContracts & typeof githubTriggerContracts & typeof gmailTriggerContracts & typeof googleCalendarTriggerContracts & typeof googleDriveTriggerContracts & typeof googleFormsTriggerContracts & typeof googleMeetTriggerContracts & typeof googleAdsTriggerContracts & typeof googleSheetsTriggerContracts & typeof hubspotTriggerContracts & typeof linearTriggerContracts & typeof millionVerifierTriggerContracts & typeof metaAdsTriggerContracts & typeof notionTriggerContracts & typeof outlookTriggerContracts & typeof redditTriggerContracts & typeof resendTriggerContracts & typeof slackTriggerContracts & typeof stripeTriggerContracts & typeof teamsTriggerContracts & typeof trelloTriggerContracts & typeof vercelTriggerContracts & typeof webflowTriggerContracts & typeof whatsappTriggerContracts;
36
38
  export type IntegrationTriggerType = keyof TriggerContractMap;
37
39
  /** Canonical authoring configuration for one integration trigger type. */
38
40
  export type TriggerConfig<TType extends IntegrationTriggerType> = z.input<TriggerContractMap[TType]["configSchema"]> extends Record<string, never> ? object : z.input<TriggerContractMap[TType]["configSchema"]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automate.ax/integration-contracts",
3
- "version": "0.141.0",
3
+ "version": "0.143.0",
4
4
  "description": "Shared integration payload contracts and provider primitives for Automate.ax.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,6 +24,7 @@
24
24
  "./automate": "./src/automate/index.ts",
25
25
  "./axiom": "./src/axiom/index.ts",
26
26
  "./asana": "./src/asana/index.ts",
27
+ "./clickup": "./src/clickup/index.ts",
27
28
  "./brevo": "./src/brevo/index.ts",
28
29
  "./cloudflare": "./src/cloudflare/index.ts",
29
30
  "./meta-ads": "./src/meta-ads/index.ts",
@@ -36,6 +37,7 @@
36
37
  "./google-drive": "./src/google-drive/index.ts",
37
38
  "./google-forms": "./src/google-forms/index.ts",
38
39
  "./google-meet": "./src/google-meet/index.ts",
40
+ "./google-ads": "./src/google-ads/index.ts",
39
41
  "./google-sheets": "./src/google-sheets/index.ts",
40
42
  "./github": "./src/github/index.ts",
41
43
  "./hubspot": "./src/hubspot/index.ts",
@@ -63,7 +65,7 @@
63
65
  },
64
66
  "dependencies": {
65
67
  "@anthropic-ai/sdk": "0.123.0",
66
- "@automate.ax/codec": "0.141.0",
68
+ "@automate.ax/codec": "0.143.0",
67
69
  "@cfworker/json-schema": "^4.1.1",
68
70
  "@googleapis/calendar": "^16.0.0",
69
71
  "@googleapis/forms": "^6.0.1",
@@ -84,7 +86,7 @@
84
86
  "zod": "^4.3.6"
85
87
  },
86
88
  "devDependencies": {
87
- "@internal/config": "0.2.0",
89
+ "@internal/config": "0.3.0",
88
90
  "@types/bun": "latest",
89
91
  "@types/html-to-text": "^9.0.4",
90
92
  "@types/nodemailer": "^8.0.1",
@@ -146,6 +148,11 @@
146
148
  "types": "./dist/asana/index.d.ts",
147
149
  "default": "./dist/asana/index.js"
148
150
  },
151
+ "./clickup": {
152
+ "bun": "./src/clickup/index.ts",
153
+ "types": "./dist/clickup/index.d.ts",
154
+ "default": "./dist/clickup/index.js"
155
+ },
149
156
  "./brevo": {
150
157
  "bun": "./src/brevo/index.ts",
151
158
  "types": "./dist/brevo/index.d.ts",
@@ -206,6 +213,11 @@
206
213
  "types": "./dist/google-meet/index.d.ts",
207
214
  "default": "./dist/google-meet/index.js"
208
215
  },
216
+ "./google-ads": {
217
+ "bun": "./src/google-ads/index.ts",
218
+ "types": "./dist/google-ads/index.d.ts",
219
+ "default": "./dist/google-ads/index.js"
220
+ },
209
221
  "./google-sheets": {
210
222
  "bun": "./src/google-sheets/index.ts",
211
223
  "types": "./dist/google-sheets/index.d.ts",
@@ -0,0 +1,270 @@
1
+ import { encodableSchema, type Encodable } from "@automate.ax/codec"
2
+ import * as z from "zod"
3
+
4
+ const CLICKUP_API_BASE_URL = "https://api.clickup.com/api/v2/"
5
+ const CLICKUP_API_ORIGIN = new URL(CLICKUP_API_BASE_URL).origin
6
+ const CLICKUP_OAUTH_SECRET_SCHEMA = z.object({ accessToken: z.string().min(1) })
7
+ const CLICKUP_TOKEN_SECRET_SCHEMA = z.object({ apiKey: z.string().min(1) })
8
+
9
+ /** Resolved ClickUp account accepted by the shared API client. */
10
+ export interface ClickUpResolvedAccount {
11
+ connectionMethodId: string
12
+ secret: Record<string, unknown>
13
+ serviceId: "clickup"
14
+ }
15
+
16
+ /** Scalar value serialized into one ClickUp query parameter. */
17
+ export type ClickUpQueryValue =
18
+ | boolean
19
+ | number
20
+ | readonly (number | string)[]
21
+ | string
22
+ | undefined
23
+
24
+ /** Options for one authenticated ClickUp REST request. */
25
+ export interface ClickUpRequestOptions<TSchema extends z.ZodType> {
26
+ /** Public camelCase JSON body. */
27
+ body?: Encodable
28
+
29
+ /** HTTP verb. Defaults to `GET`. */
30
+ method?: "DELETE" | "GET" | "POST" | "PUT"
31
+
32
+ /** Public camelCase query parameters. */
33
+ query?: Record<string, ClickUpQueryValue>
34
+
35
+ /** Schema for the normalized provider response. */
36
+ responseSchema: TSchema
37
+ }
38
+
39
+ /** Authenticated ClickUp REST API helper for packaged actions. */
40
+ export interface ClickUpApi {
41
+ /**
42
+ * Calls a ClickUp API v2 endpoint and validates its normalized response.
43
+ *
44
+ * @param path - API path relative to `/api/v2/`.
45
+ * @param options - Request data and response schema.
46
+ */
47
+ request<TSchema extends z.ZodType>(
48
+ path: string,
49
+ options: ClickUpRequestOptions<TSchema>,
50
+ ): Promise<z.output<TSchema>>
51
+ }
52
+
53
+ /** Structured ClickUp REST error. */
54
+ export class ClickUpApiError extends Error {
55
+ /** Normalized provider error payload, when it was valid JSON. */
56
+ readonly body?: Encodable
57
+
58
+ /** API path that failed. */
59
+ readonly path: string
60
+
61
+ /** Unix timestamp in seconds when the current rate-limit window resets. */
62
+ readonly rateLimitReset?: number
63
+
64
+ /** HTTP status returned by ClickUp. */
65
+ readonly status: number
66
+
67
+ /**
68
+ * Creates a structured ClickUp API error.
69
+ *
70
+ * @param options - Provider and transport error details.
71
+ * @param options.body - Normalized provider error payload.
72
+ * @param options.path - API path that failed.
73
+ * @param options.rateLimitReset - Unix reset timestamp in seconds.
74
+ * @param options.status - HTTP response status.
75
+ */
76
+ constructor(options: {
77
+ body?: Encodable
78
+ path: string
79
+ rateLimitReset?: number
80
+ status: number
81
+ }) {
82
+ super(
83
+ getErrorMessage(options.body) ??
84
+ `ClickUp API request failed with status ${options.status}.`,
85
+ )
86
+ this.name = "ClickUpApiError"
87
+ this.body = options.body
88
+ this.path = options.path
89
+ this.rateLimitReset = options.rateLimitReset
90
+ this.status = options.status
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Creates an authenticated ClickUp API v2 client.
96
+ *
97
+ * @param account - Resolved ClickUp integration account.
98
+ * @throws When the account uses an unsupported connection method.
99
+ */
100
+ export function getClickUpApi(account: ClickUpResolvedAccount): ClickUpApi {
101
+ const accessToken: string =
102
+ account.connectionMethodId === "oauth"
103
+ ? CLICKUP_OAUTH_SECRET_SCHEMA.parse(account.secret).accessToken
104
+ : account.connectionMethodId === "personal-token"
105
+ ? CLICKUP_TOKEN_SECRET_SCHEMA.parse(account.secret).apiKey
106
+ : ""
107
+ if (!accessToken) {
108
+ throw new Error(
109
+ `Unsupported ClickUp connection method: ${account.connectionMethodId}`,
110
+ )
111
+ }
112
+
113
+ return {
114
+ request: async <TSchema extends z.ZodType>(
115
+ path: string,
116
+ options: ClickUpRequestOptions<TSchema>,
117
+ ): Promise<z.output<TSchema>> => {
118
+ const normalizedPath = path.replace(/^\/+/, "")
119
+ const url = new URL(normalizedPath, CLICKUP_API_BASE_URL)
120
+ const expectedPathname = `/api/v2/${normalizedPath}`
121
+ if (
122
+ url.origin !== CLICKUP_API_ORIGIN ||
123
+ url.pathname !== expectedPathname
124
+ ) {
125
+ throw new Error("ClickUp API paths must remain under /api/v2.")
126
+ }
127
+ for (const [name, value] of Object.entries(options.query ?? {})) {
128
+ if (value === undefined) continue
129
+ const wireName = toSnakeCase(name)
130
+ if (Array.isArray(value)) {
131
+ for (const item of value)
132
+ url.searchParams.append(`${wireName}[]`, String(item))
133
+ } else {
134
+ url.searchParams.set(wireName, String(value))
135
+ }
136
+ }
137
+
138
+ const response = await fetch(url, {
139
+ body:
140
+ options.body === undefined
141
+ ? undefined
142
+ : JSON.stringify(toClickUp(options.body)),
143
+ headers: {
144
+ Accept: "application/json",
145
+ Authorization:
146
+ account.connectionMethodId === "oauth"
147
+ ? `Bearer ${accessToken}`
148
+ : accessToken,
149
+ ...(options.body === undefined
150
+ ? {}
151
+ : { "Content-Type": "application/json" }),
152
+ },
153
+ method: options.method ?? "GET",
154
+ })
155
+ const responseText = await response.text()
156
+ const normalized =
157
+ responseText === "" ? undefined : fromClickUp(parseJson(responseText))
158
+ if (!response.ok) {
159
+ const body = encodableSchema.safeParse(normalized)
160
+ throw new ClickUpApiError({
161
+ ...(body.success && { body: body.data }),
162
+ path: `/${normalizedPath}`,
163
+ rateLimitReset: parseFiniteNumber(
164
+ response.headers.get("X-RateLimit-Reset"),
165
+ ),
166
+ status: response.status,
167
+ })
168
+ }
169
+ return options.responseSchema.parse(normalized)
170
+ },
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Converts public camelCase JSON to ClickUp wire keys.
176
+ *
177
+ * @param value - Public JSON value.
178
+ */
179
+ export function toClickUp(value: Encodable): Encodable {
180
+ if (value instanceof Date) return value.toISOString()
181
+ if (Array.isArray(value)) return value.map(toClickUp)
182
+ if (!isPlainObject(value)) return value
183
+ return Object.fromEntries(
184
+ Object.entries(value).map(([key, item]) => [
185
+ toSnakeCase(key),
186
+ toClickUp(item),
187
+ ]),
188
+ )
189
+ }
190
+
191
+ /**
192
+ * Converts ClickUp response keys to public camelCase recursively.
193
+ *
194
+ * @param value - Provider response value.
195
+ */
196
+ export function fromClickUp(value: unknown): unknown {
197
+ if (Array.isArray(value)) return value.map(fromClickUp)
198
+ if (!isPlainObject(value)) return value
199
+ return Object.fromEntries(
200
+ Object.entries(value).map(([key, item]) => [
201
+ toCamelCase(key),
202
+ fromClickUp(item),
203
+ ]),
204
+ )
205
+ }
206
+
207
+ /**
208
+ * Returns whether a value is a non-array object.
209
+ *
210
+ * @param value - Candidate value.
211
+ */
212
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
213
+ return typeof value === "object" && value !== null && !Array.isArray(value)
214
+ }
215
+
216
+ /**
217
+ * Parses JSON without masking the provider's response status.
218
+ *
219
+ * @param value - Raw response text.
220
+ */
221
+ function parseJson(value: string): unknown {
222
+ try {
223
+ return JSON.parse(value)
224
+ } catch {
225
+ return undefined
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Parses a finite number from an optional response header.
231
+ *
232
+ * @param value - Raw response header.
233
+ */
234
+ function parseFiniteNumber(value: string | null) {
235
+ if (value === null) return undefined
236
+ const number = Number(value)
237
+ return Number.isFinite(number) ? number : undefined
238
+ }
239
+
240
+ /**
241
+ * Reads ClickUp's human-readable error message from a response body.
242
+ *
243
+ * @param body - Normalized provider response body.
244
+ */
245
+ function getErrorMessage(body: Encodable | undefined) {
246
+ if (!isPlainObject(body)) return undefined
247
+ if (typeof body.err === "string") return body.err
248
+ if (typeof body.message === "string") return body.message
249
+ return undefined
250
+ }
251
+
252
+ /**
253
+ * Converts one camelCase key to ClickUp's snake_case wire format.
254
+ *
255
+ * @param value - Public key.
256
+ */
257
+ function toSnakeCase(value: string) {
258
+ return value.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
259
+ }
260
+
261
+ /**
262
+ * Converts one ClickUp snake_case key to camelCase.
263
+ *
264
+ * @param value - Provider wire key.
265
+ */
266
+ function toCamelCase(value: string) {
267
+ return value.replace(/_([a-z0-9])/g, (_match, letter: string) =>
268
+ letter.toUpperCase(),
269
+ )
270
+ }
@@ -0,0 +1,108 @@
1
+ export * from "./api"
2
+ export * from "./schemas"
3
+
4
+ import * as z from "zod"
5
+ import {
6
+ CLICKUP_TRIGGER_CONFIG_SCHEMA,
7
+ CLICKUP_WEBHOOK_EVENT_SCHEMA,
8
+ } from "./schemas"
9
+
10
+ export const CLICKUP_EVENT_TYPE_BY_PROVIDER_EVENT = {
11
+ folderCreated: "clickup.folder.created",
12
+ folderDeleted: "clickup.folder.deleted",
13
+ folderUpdated: "clickup.folder.updated",
14
+ goalCreated: "clickup.goal.created",
15
+ goalDeleted: "clickup.goal.deleted",
16
+ goalUpdated: "clickup.goal.updated",
17
+ keyResultCreated: "clickup.target.created",
18
+ keyResultDeleted: "clickup.target.deleted",
19
+ keyResultUpdated: "clickup.target.updated",
20
+ listCreated: "clickup.list.created",
21
+ listDeleted: "clickup.list.deleted",
22
+ listUpdated: "clickup.list.updated",
23
+ spaceCreated: "clickup.space.created",
24
+ spaceDeleted: "clickup.space.deleted",
25
+ spaceUpdated: "clickup.space.updated",
26
+ taskAssigneeUpdated: "clickup.task.assignee.updated",
27
+ taskCommentPosted: "clickup.task.comment.posted",
28
+ taskCommentUpdated: "clickup.task.comment.updated",
29
+ taskCreated: "clickup.task.created",
30
+ taskDeleted: "clickup.task.deleted",
31
+ taskDueDateUpdated: "clickup.task.due-date.updated",
32
+ taskMoved: "clickup.task.moved",
33
+ taskPriorityUpdated: "clickup.task.priority.updated",
34
+ taskStatusUpdated: "clickup.task.status.updated",
35
+ taskTagUpdated: "clickup.task.tag.updated",
36
+ taskTimeEstimateUpdated: "clickup.task.time-estimate.updated",
37
+ taskTimeTrackedUpdated: "clickup.task.time-tracked.updated",
38
+ taskUpdated: "clickup.task.updated",
39
+ } as const
40
+
41
+ /**
42
+ * Narrows the shared webhook payload to one provider event name.
43
+ *
44
+ * @param event - ClickUp provider event name.
45
+ */
46
+ function semanticEventSchema<
47
+ const TEvent extends keyof typeof CLICKUP_EVENT_TYPE_BY_PROVIDER_EVENT,
48
+ >(event: TEvent) {
49
+ return CLICKUP_WEBHOOK_EVENT_SCHEMA.extend({ event: z.literal(event) })
50
+ }
51
+
52
+ /**
53
+ * Creates one semantic ClickUp trigger contract.
54
+ *
55
+ * @param event - ClickUp provider event name.
56
+ */
57
+ function semanticContract<
58
+ const TEvent extends keyof typeof CLICKUP_EVENT_TYPE_BY_PROVIDER_EVENT,
59
+ >(event: TEvent) {
60
+ return {
61
+ configSchema: CLICKUP_TRIGGER_CONFIG_SCHEMA,
62
+ eventSchema: semanticEventSchema(event),
63
+ }
64
+ }
65
+
66
+ export const clickUpTriggerContracts = {
67
+ "clickup.workspace.event": {
68
+ configSchema: CLICKUP_TRIGGER_CONFIG_SCHEMA,
69
+ eventSchema: CLICKUP_WEBHOOK_EVENT_SCHEMA,
70
+ },
71
+ "clickup.folder.created": semanticContract("folderCreated"),
72
+ "clickup.folder.deleted": semanticContract("folderDeleted"),
73
+ "clickup.folder.updated": semanticContract("folderUpdated"),
74
+ "clickup.goal.created": semanticContract("goalCreated"),
75
+ "clickup.goal.deleted": semanticContract("goalDeleted"),
76
+ "clickup.goal.updated": semanticContract("goalUpdated"),
77
+ "clickup.list.created": semanticContract("listCreated"),
78
+ "clickup.list.deleted": semanticContract("listDeleted"),
79
+ "clickup.list.updated": semanticContract("listUpdated"),
80
+ "clickup.space.created": semanticContract("spaceCreated"),
81
+ "clickup.space.deleted": semanticContract("spaceDeleted"),
82
+ "clickup.space.updated": semanticContract("spaceUpdated"),
83
+ "clickup.target.created": semanticContract("keyResultCreated"),
84
+ "clickup.target.deleted": semanticContract("keyResultDeleted"),
85
+ "clickup.target.updated": semanticContract("keyResultUpdated"),
86
+ "clickup.task.assignee.updated": semanticContract("taskAssigneeUpdated"),
87
+ "clickup.task.comment.posted": semanticContract("taskCommentPosted"),
88
+ "clickup.task.comment.updated": semanticContract("taskCommentUpdated"),
89
+ "clickup.task.created": semanticContract("taskCreated"),
90
+ "clickup.task.deleted": semanticContract("taskDeleted"),
91
+ "clickup.task.due-date.updated": semanticContract("taskDueDateUpdated"),
92
+ "clickup.task.moved": semanticContract("taskMoved"),
93
+ "clickup.task.priority.updated": semanticContract("taskPriorityUpdated"),
94
+ "clickup.task.status.updated": semanticContract("taskStatusUpdated"),
95
+ "clickup.task.tag.updated": semanticContract("taskTagUpdated"),
96
+ "clickup.task.time-estimate.updated": semanticContract(
97
+ "taskTimeEstimateUpdated",
98
+ ),
99
+ "clickup.task.time-tracked.updated": semanticContract(
100
+ "taskTimeTrackedUpdated",
101
+ ),
102
+ "clickup.task.updated": semanticContract("taskUpdated"),
103
+ } as const
104
+
105
+ export const CLICKUP_EVENT_TYPES = [
106
+ "clickup.workspace.event",
107
+ ...Object.values(CLICKUP_EVENT_TYPE_BY_PROVIDER_EVENT),
108
+ ] as const