@automate.ax/integration-contracts 0.139.2 → 0.142.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 (50) hide show
  1. package/dist/clickup/api.d.ts +76 -0
  2. package/dist/clickup/api.js +195 -0
  3. package/dist/clickup/index.d.ts +1921 -0
  4. package/dist/clickup/index.js +91 -0
  5. package/dist/clickup/schemas.d.ts +477 -0
  6. package/dist/clickup/schemas.js +286 -0
  7. package/dist/closebot/api.d.ts +39 -0
  8. package/dist/closebot/api.js +117 -0
  9. package/dist/closebot/index.d.ts +2 -0
  10. package/dist/closebot/index.js +2 -0
  11. package/dist/closebot/schemas.d.ts +775 -0
  12. package/dist/closebot/schemas.js +469 -0
  13. package/dist/google-ads/api.d.ts +40 -0
  14. package/dist/google-ads/api.js +109 -0
  15. package/dist/google-ads/events.d.ts +1155 -0
  16. package/dist/google-ads/events.js +96 -0
  17. package/dist/google-ads/index.d.ts +3 -0
  18. package/dist/google-ads/index.js +3 -0
  19. package/dist/google-ads/schemas.d.ts +756 -0
  20. package/dist/google-ads/schemas.js +249 -0
  21. package/dist/google-drive/index.d.ts +6 -6
  22. package/dist/linear/schemas.d.ts +3 -3
  23. package/dist/meta-ads/api.d.ts +90 -0
  24. package/dist/meta-ads/api.js +260 -0
  25. package/dist/meta-ads/events.d.ts +391 -0
  26. package/dist/meta-ads/events.js +193 -0
  27. package/dist/meta-ads/index.d.ts +3 -0
  28. package/dist/meta-ads/index.js +3 -0
  29. package/dist/meta-ads/schemas.d.ts +184 -0
  30. package/dist/meta-ads/schemas.js +181 -0
  31. package/dist/notion/schemas.d.ts +84 -84
  32. package/dist/teams/index.d.ts +20 -20
  33. package/dist/teams/schemas.d.ts +5 -5
  34. package/dist/triggers.d.ts +4 -1
  35. package/package.json +27 -3
  36. package/src/clickup/api.ts +270 -0
  37. package/src/clickup/index.ts +108 -0
  38. package/src/clickup/schemas.ts +324 -0
  39. package/src/closebot/api.ts +157 -0
  40. package/src/closebot/index.ts +2 -0
  41. package/src/closebot/schemas.ts +521 -0
  42. package/src/google-ads/api.ts +151 -0
  43. package/src/google-ads/events.ts +131 -0
  44. package/src/google-ads/index.ts +3 -0
  45. package/src/google-ads/schemas.ts +270 -0
  46. package/src/meta-ads/api.ts +327 -0
  47. package/src/meta-ads/events.ts +218 -0
  48. package/src/meta-ads/index.ts +3 -0
  49. package/src/meta-ads/schemas.ts +206 -0
  50. package/src/triggers.ts +6 -0
@@ -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
@@ -0,0 +1,324 @@
1
+ import * as z from "zod"
2
+
3
+ export const CLICKUP_USER_SCHEMA = z.object({
4
+ color: z.string().nullable().optional(),
5
+ email: z.email().optional(),
6
+ id: z.number().int(),
7
+ initials: z.string().optional(),
8
+ profilePicture: z.url().nullable().optional(),
9
+ role: z.number().int().optional(),
10
+ username: z.string(),
11
+ })
12
+
13
+ export const CLICKUP_WORKSPACE_SCHEMA = z.object({
14
+ avatar: z.url().nullable().optional(),
15
+ color: z.string().nullable().optional(),
16
+ id: z.string(),
17
+ members: z.object({ user: CLICKUP_USER_SCHEMA }).array().prefault([]),
18
+ name: z.string(),
19
+ })
20
+
21
+ export const CLICKUP_STATUS_SCHEMA = z.object({
22
+ color: z.string().optional(),
23
+ orderindex: z.number().optional(),
24
+ status: z.string(),
25
+ type: z.string().optional(),
26
+ })
27
+
28
+ export const CLICKUP_SPACE_SCHEMA = z.object({
29
+ archived: z.boolean().prefault(false),
30
+ color: z.string().nullable().optional(),
31
+ id: z.string(),
32
+ multipleAssignees: z.boolean().optional(),
33
+ name: z.string(),
34
+ private: z.boolean().optional(),
35
+ statuses: CLICKUP_STATUS_SCHEMA.array().prefault([]),
36
+ })
37
+
38
+ export const CLICKUP_FOLDER_SCHEMA: z.ZodType<{
39
+ folders: z.output<typeof CLICKUP_FOLDER_SCHEMA>[]
40
+ hidden?: boolean
41
+ id: string
42
+ lists: z.output<typeof CLICKUP_LIST_SCHEMA>[]
43
+ name: string
44
+ orderindex?: number
45
+ overrideStatuses?: boolean
46
+ parentFolder?: string
47
+ space?: { id: string; name?: string }
48
+ taskCount?: string
49
+ }> = z.object({
50
+ folders: z.lazy(() => CLICKUP_FOLDER_SCHEMA.array()).prefault([]),
51
+ hidden: z.boolean().optional(),
52
+ id: z.string(),
53
+ lists: z.lazy(() => CLICKUP_LIST_SCHEMA.array()).prefault([]),
54
+ name: z.string(),
55
+ orderindex: z.number().optional(),
56
+ overrideStatuses: z.boolean().optional(),
57
+ parentFolder: z.string().optional(),
58
+ space: z.object({ id: z.string(), name: z.string().optional() }).optional(),
59
+ taskCount: z
60
+ .string()
61
+ .nullable()
62
+ .optional()
63
+ .transform((value) => value ?? undefined),
64
+ })
65
+
66
+ export const CLICKUP_LIST_SCHEMA = z.object({
67
+ archived: z.boolean().optional(),
68
+ content: z.string().optional(),
69
+ dueDate: z.string().nullable().optional(),
70
+ dueDateTime: z.boolean().optional(),
71
+ folder: z
72
+ .object({
73
+ hidden: z.boolean().optional(),
74
+ id: z.string(),
75
+ name: z.string().optional(),
76
+ })
77
+ .optional(),
78
+ id: z.string(),
79
+ name: z.string(),
80
+ orderindex: z.number().optional(),
81
+ space: z.object({ id: z.string(), name: z.string().optional() }).optional(),
82
+ statuses: CLICKUP_STATUS_SCHEMA.array().prefault([]),
83
+ taskCount: z.string().nullable().optional(),
84
+ })
85
+
86
+ export const CLICKUP_CUSTOM_FIELD_SCHEMA = z.object({
87
+ id: z.string(),
88
+ name: z.string(),
89
+ required: z.boolean().optional(),
90
+ type: z.string(),
91
+ typeConfig: z.json().optional(),
92
+ value: z.json().optional(),
93
+ })
94
+
95
+ const CLICKUP_TASK_BASE_SCHEMA = z.object({
96
+ archived: z.boolean().prefault(false),
97
+ assignees: CLICKUP_USER_SCHEMA.array().prefault([]),
98
+ creator: CLICKUP_USER_SCHEMA.optional(),
99
+ customFields: CLICKUP_CUSTOM_FIELD_SCHEMA.array().prefault([]),
100
+ dateClosed: z.string().nullable().optional(),
101
+ dateCreated: z.string().optional(),
102
+ dateDone: z.string().nullable().optional(),
103
+ dateUpdated: z.string().optional(),
104
+ description: z.string().optional(),
105
+ dueDate: z.string().nullable().optional(),
106
+ id: z.string(),
107
+ list: z.object({ id: z.string(), name: z.string().optional() }).optional(),
108
+ name: z.string(),
109
+ parent: z.string().nullable().optional(),
110
+ priority: z
111
+ .object({
112
+ color: z.string().optional(),
113
+ id: z.string().optional(),
114
+ orderindex: z.string().optional(),
115
+ priority: z.string(),
116
+ })
117
+ .nullable()
118
+ .optional(),
119
+ space: z.object({ id: z.string() }).optional(),
120
+ startDate: z.string().nullable().optional(),
121
+ status: CLICKUP_STATUS_SCHEMA.optional(),
122
+ tags: z
123
+ .object({
124
+ name: z.string(),
125
+ tagBg: z.string().optional(),
126
+ tagFg: z.string().optional(),
127
+ })
128
+ .array()
129
+ .prefault([]),
130
+ textContent: z.string().optional(),
131
+ timeEstimate: z.coerce.number().nullable().optional(),
132
+ timeSpent: z.number().optional(),
133
+ url: z.url().optional(),
134
+ watchers: CLICKUP_USER_SCHEMA.array().prefault([]),
135
+ })
136
+
137
+ export type ClickUpTask = z.output<typeof CLICKUP_TASK_BASE_SCHEMA> & {
138
+ subtasks: ClickUpTask[]
139
+ }
140
+
141
+ export const CLICKUP_TASK_SCHEMA: z.ZodType<ClickUpTask> =
142
+ CLICKUP_TASK_BASE_SCHEMA.extend({
143
+ subtasks: z.lazy(() => CLICKUP_TASK_SCHEMA.array()).prefault([]),
144
+ })
145
+
146
+ export const CLICKUP_COMMENT_SCHEMA = z.object({
147
+ assignee: CLICKUP_USER_SCHEMA.optional(),
148
+ comment: z.json().array().prefault([]),
149
+ commentText: z.string().optional(),
150
+ date: z.string(),
151
+ id: z.string(),
152
+ resolved: z.boolean().optional(),
153
+ user: CLICKUP_USER_SCHEMA,
154
+ })
155
+
156
+ export const CLICKUP_TIME_ENTRY_SCHEMA = z.object({
157
+ at: z.coerce.string().optional(),
158
+ billable: z.boolean().optional(),
159
+ description: z.string().optional(),
160
+ duration: z.coerce.string(),
161
+ end: z.coerce.string().optional(),
162
+ id: z.string(),
163
+ start: z.coerce.string(),
164
+ task: z
165
+ .object({ id: z.string(), name: z.string().optional() })
166
+ .nullable()
167
+ .optional(),
168
+ source: z.string().optional(),
169
+ tags: z.json().array().prefault([]),
170
+ taskLocation: z
171
+ .object({
172
+ folderId: z.union([z.number(), z.string()]).transform(String),
173
+ folderName: z.string(),
174
+ listId: z.union([z.number(), z.string()]).transform(String),
175
+ listName: z.string(),
176
+ spaceId: z.union([z.number(), z.string()]).transform(String),
177
+ spaceName: z.string(),
178
+ })
179
+ .optional(),
180
+ taskTags: z
181
+ .object({
182
+ creator: z.number().int().optional(),
183
+ name: z.string(),
184
+ tagBg: z.string().optional(),
185
+ tagFg: z.string().optional(),
186
+ })
187
+ .array()
188
+ .prefault([]),
189
+ taskUrl: z.url().optional(),
190
+ user: CLICKUP_USER_SCHEMA.optional(),
191
+ wid: z.string().optional(),
192
+ })
193
+
194
+ export const CLICKUP_KEY_RESULT_SCHEMA = z.object({
195
+ completed: z.boolean().optional(),
196
+ creator: z.number().int().optional(),
197
+ current: z.number().optional(),
198
+ dateCreated: z.string().optional(),
199
+ goalId: z.string().optional(),
200
+ goalPrettyId: z.string().optional(),
201
+ id: z.string(),
202
+ lastAction: z.json().optional(),
203
+ name: z.string(),
204
+ owners: CLICKUP_USER_SCHEMA.array().prefault([]),
205
+ percentCompleted: z.coerce.number().nullable().optional(),
206
+ subcategoryIds: z.string().array().prefault([]),
207
+ stepsCurrent: z.number().nullable().optional(),
208
+ stepsEnd: z.number().optional(),
209
+ stepsStart: z.number().optional(),
210
+ taskIds: z.string().array().prefault([]),
211
+ target: z.number().optional(),
212
+ type: z.string().optional(),
213
+ unit: z.string().optional(),
214
+ })
215
+
216
+ const CLICKUP_GOAL_BASE_SCHEMA = z.object({
217
+ archived: z.boolean().optional(),
218
+ color: z.string().optional(),
219
+ dateCreated: z.string().optional(),
220
+ description: z.string().optional(),
221
+ dueDate: z.string().nullable().optional(),
222
+ id: z.string(),
223
+ multipleOwners: z.boolean().optional(),
224
+ name: z.string(),
225
+ percentCompleted: z.number().optional(),
226
+ private: z.boolean().optional(),
227
+ teamId: z.string().optional(),
228
+ })
229
+
230
+ /** Goal shape returned by the Workspace Goal listing endpoint. */
231
+ export const CLICKUP_GOAL_LIST_ITEM_SCHEMA = CLICKUP_GOAL_BASE_SCHEMA.extend({
232
+ keyResults: z.string().array().prefault([]),
233
+ owners: z.string().array().prefault([]),
234
+ })
235
+
236
+ /** Goal shape returned by get, create, and update endpoints. */
237
+ export const CLICKUP_GOAL_SCHEMA = CLICKUP_GOAL_BASE_SCHEMA.extend({
238
+ keyResults: z.string().array().prefault([]),
239
+ owners: CLICKUP_USER_SCHEMA.array().prefault([]),
240
+ })
241
+
242
+ export const CLICKUP_HISTORY_ITEM_SCHEMA = z.object({
243
+ after: z.json().optional(),
244
+ before: z.json().optional(),
245
+ comment: z
246
+ .object({
247
+ comment: z.json().array().prefault([]),
248
+ date: z.string(),
249
+ id: z.string(),
250
+ parent: z.string().optional(),
251
+ textContent: z.string().optional(),
252
+ type: z.number().int().optional(),
253
+ user: CLICKUP_USER_SCHEMA,
254
+ })
255
+ .catchall(z.json())
256
+ .optional(),
257
+ customField: CLICKUP_CUSTOM_FIELD_SCHEMA.optional(),
258
+ data: z.json().optional(),
259
+ date: z.string(),
260
+ field: z.string().optional(),
261
+ id: z.string(),
262
+ parentId: z.string().optional(),
263
+ source: z.json().optional(),
264
+ type: z.number().int(),
265
+ user: CLICKUP_USER_SCHEMA.optional(),
266
+ })
267
+
268
+ export const CLICKUP_WEBHOOK_EVENT_NAMES = [
269
+ "taskCreated",
270
+ "taskUpdated",
271
+ "taskDeleted",
272
+ "taskPriorityUpdated",
273
+ "taskStatusUpdated",
274
+ "taskAssigneeUpdated",
275
+ "taskDueDateUpdated",
276
+ "taskTagUpdated",
277
+ "taskMoved",
278
+ "taskCommentPosted",
279
+ "taskCommentUpdated",
280
+ "taskTimeEstimateUpdated",
281
+ "taskTimeTrackedUpdated",
282
+ "listCreated",
283
+ "listUpdated",
284
+ "listDeleted",
285
+ "folderCreated",
286
+ "folderUpdated",
287
+ "folderDeleted",
288
+ "spaceCreated",
289
+ "spaceUpdated",
290
+ "spaceDeleted",
291
+ "goalCreated",
292
+ "goalUpdated",
293
+ "goalDeleted",
294
+ "keyResultCreated",
295
+ "keyResultUpdated",
296
+ "keyResultDeleted",
297
+ ] as const
298
+
299
+ export const CLICKUP_WEBHOOK_EVENT_NAME_SCHEMA = z.enum(
300
+ CLICKUP_WEBHOOK_EVENT_NAMES,
301
+ )
302
+
303
+ export const CLICKUP_WEBHOOK_EVENT_SCHEMA = z.object({
304
+ data: z
305
+ .object({
306
+ description: z.string().optional(),
307
+ intervalId: z.string().optional(),
308
+ })
309
+ .catchall(z.json())
310
+ .optional(),
311
+ event: CLICKUP_WEBHOOK_EVENT_NAME_SCHEMA,
312
+ folderId: z.string().optional(),
313
+ goalId: z.string().optional(),
314
+ historyItems: CLICKUP_HISTORY_ITEM_SCHEMA.array().prefault([]),
315
+ keyResultId: z.string().optional(),
316
+ listId: z.string().optional(),
317
+ spaceId: z.string().optional(),
318
+ taskId: z.string().optional(),
319
+ webhookId: z.string(),
320
+ })
321
+
322
+ export const CLICKUP_TRIGGER_CONFIG_SCHEMA = z.object({
323
+ workspaceId: z.string().min(1),
324
+ })
@@ -0,0 +1,157 @@
1
+ import { isEncodable, type Encodable } from "@automate.ax/codec"
2
+ import * as z from "zod"
3
+
4
+ const CLOSEBOT_API_ORIGIN = "https://api.closebot.com/"
5
+
6
+ const CLOSEBOT_SECRET_SCHEMA = z.object({
7
+ apiKey: z.string().trim().min(1),
8
+ })
9
+
10
+ export const CLOSEBOT_VALUE_SCHEMA = z.custom<Encodable>(isEncodable, {
11
+ message: "Expected a codec-safe CloseBot value.",
12
+ })
13
+
14
+ export const CLOSEBOT_RESPONSE_SCHEMA = z.union([
15
+ CLOSEBOT_VALUE_SCHEMA,
16
+ z.undefined().transform(() => null),
17
+ ])
18
+
19
+ interface CloseBotRequestOptions<TSchema extends z.ZodType> {
20
+ body?: Encodable | FormData
21
+ method?: "DELETE" | "GET" | "PATCH" | "POST" | "PUT"
22
+ query?: Record<
23
+ string,
24
+ | boolean
25
+ | Date
26
+ | number
27
+ | string
28
+ | (boolean | Date | number | string)[]
29
+ | null
30
+ | undefined
31
+ >
32
+ responseSchema: TSchema
33
+ }
34
+
35
+ /** Error returned by a rejected CloseBot API request. */
36
+ export class CloseBotApiError extends Error {
37
+ readonly details: Encodable
38
+ readonly retryAfter?: string
39
+ readonly status: number
40
+
41
+ /**
42
+ * Creates an error from a rejected CloseBot response.
43
+ *
44
+ * @param status - HTTP response status.
45
+ * @param details - Codec-safe response details.
46
+ * @param retryAfter - Raw provider retry timing, when present.
47
+ */
48
+ constructor(status: number, details: Encodable, retryAfter?: string) {
49
+ super(`CloseBot API request failed (${status}).`)
50
+ this.name = "CloseBotApiError"
51
+ this.details = details
52
+ this.retryAfter = retryAfter
53
+ this.status = status
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Creates an authenticated CloseBot REST API client.
59
+ *
60
+ * @param secret - Stored CloseBot API key.
61
+ */
62
+ export function getCloseBotApi(secret: unknown) {
63
+ const { apiKey } = CLOSEBOT_SECRET_SCHEMA.parse(secret)
64
+ const root = new URL(CLOSEBOT_API_ORIGIN)
65
+
66
+ return {
67
+ /**
68
+ * Sends one JSON request to a CloseBot API path.
69
+ *
70
+ * @param path - Relative path below the CloseBot API origin.
71
+ * @param options - Request method, query, body, and response schema.
72
+ */
73
+ async request<TSchema extends z.ZodType>(
74
+ path: string,
75
+ options: CloseBotRequestOptions<TSchema>,
76
+ ): Promise<z.output<TSchema>> {
77
+ const normalizedPath = path.replace(/^\/+/, "")
78
+ if (
79
+ !normalizedPath ||
80
+ normalizedPath.includes("://") ||
81
+ normalizedPath.includes("\\")
82
+ ) {
83
+ throw new TypeError("CloseBot API paths must be relative.")
84
+ }
85
+
86
+ const url = new URL(normalizedPath, root)
87
+ if (url.origin !== root.origin) {
88
+ throw new TypeError(
89
+ "CloseBot API paths must remain on api.closebot.com.",
90
+ )
91
+ }
92
+ for (const [key, value] of Object.entries(options.query ?? {})) {
93
+ if (value == null) continue
94
+ if (Array.isArray(value)) {
95
+ for (const item of value)
96
+ url.searchParams.append(key, queryValue(item))
97
+ } else {
98
+ url.searchParams.set(key, queryValue(value))
99
+ }
100
+ }
101
+
102
+ const headers = new Headers({
103
+ Accept: "application/json",
104
+ "X-CB-KEY": apiKey,
105
+ })
106
+ if (options.body !== undefined && !(options.body instanceof FormData)) {
107
+ headers.set("Content-Type", "application/json")
108
+ }
109
+
110
+ const response = await fetch(url, {
111
+ body:
112
+ options.body === undefined
113
+ ? undefined
114
+ : options.body instanceof FormData
115
+ ? options.body
116
+ : JSON.stringify(options.body),
117
+ headers,
118
+ method: options.method ?? "GET",
119
+ redirect: "error",
120
+ })
121
+ const text = await response.text()
122
+ const parsed = CLOSEBOT_RESPONSE_SCHEMA.safeParse(
123
+ text ? parseJson(text) : null,
124
+ )
125
+ if (!response.ok || !parsed.success) {
126
+ throw new CloseBotApiError(
127
+ response.status,
128
+ parsed.success ? parsed.data : { response: text },
129
+ response.headers.get("Retry-After") ?? undefined,
130
+ )
131
+ }
132
+ return options.responseSchema.parse(parsed.data)
133
+ },
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Parses a response while retaining non-JSON provider diagnostics.
139
+ *
140
+ * @param value - Raw response body.
141
+ */
142
+ function parseJson(value: string): unknown {
143
+ try {
144
+ return JSON.parse(value)
145
+ } catch {
146
+ return value
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Serializes one supported query value.
152
+ *
153
+ * @param value - Public query value.
154
+ */
155
+ function queryValue(value: boolean | Date | number | string) {
156
+ return value instanceof Date ? value.toISOString() : String(value)
157
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./api"
2
+ export * from "./schemas"