@automate.ax/integration-contracts 0.115.6 → 0.118.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 (36) hide show
  1. package/dist/gmail/index.d.ts +269 -0
  2. package/dist/gmail/index.js +13 -1
  3. package/dist/gmail/schemas.d.ts +204 -0
  4. package/dist/gmail/schemas.js +24 -0
  5. package/dist/google-calendar/event-schemas.d.ts +346 -0
  6. package/dist/google-calendar/event-schemas.js +14 -0
  7. package/dist/google-calendar/index.d.ts +350 -0
  8. package/dist/google-calendar/index.js +5 -1
  9. package/dist/millionverifier/api.d.ts +62 -0
  10. package/dist/millionverifier/api.js +114 -0
  11. package/dist/millionverifier/events.d.ts +126 -0
  12. package/dist/millionverifier/events.js +103 -0
  13. package/dist/millionverifier/index.d.ts +77 -0
  14. package/dist/millionverifier/index.js +17 -0
  15. package/dist/millionverifier/schemas.d.ts +226 -0
  16. package/dist/millionverifier/schemas.js +188 -0
  17. package/dist/tidycal/api.d.ts +62 -0
  18. package/dist/tidycal/api.js +192 -0
  19. package/dist/tidycal/index.d.ts +2 -0
  20. package/dist/tidycal/index.js +2 -0
  21. package/dist/tidycal/schemas.d.ts +121 -0
  22. package/dist/tidycal/schemas.js +95 -0
  23. package/dist/triggers.d.ts +2 -1
  24. package/package.json +14 -2
  25. package/src/gmail/index.ts +18 -1
  26. package/src/gmail/schemas.ts +26 -0
  27. package/src/google-calendar/event-schemas.ts +18 -0
  28. package/src/google-calendar/index.ts +8 -1
  29. package/src/millionverifier/api.ts +151 -0
  30. package/src/millionverifier/events.ts +110 -0
  31. package/src/millionverifier/index.ts +33 -0
  32. package/src/millionverifier/schemas.ts +207 -0
  33. package/src/tidycal/api.ts +239 -0
  34. package/src/tidycal/index.ts +2 -0
  35. package/src/tidycal/schemas.ts +105 -0
  36. package/src/triggers.ts +2 -0
@@ -0,0 +1,239 @@
1
+ import type { Encodable } from "@automate.ax/codec"
2
+ import { encodableSchema } from "@automate.ax/codec"
3
+ import * as z from "zod"
4
+
5
+ const TIDYCAL_API_BASE_URL = "https://tidycal.com/api/"
6
+ const TIDYCAL_API_ORIGIN = new URL(TIDYCAL_API_BASE_URL).origin
7
+ const TIDYCAL_PERSONAL_ACCESS_TOKEN_SECRET_SCHEMA = z.object({
8
+ apiKey: z.string().min(1),
9
+ })
10
+ const TIDYCAL_OAUTH_SECRET_SCHEMA = z.object({
11
+ accessToken: z.string().min(1),
12
+ })
13
+
14
+ /** Resolved TidyCal account accepted by the shared API client. */
15
+ export interface TidyCalResolvedAccount {
16
+ connectionMethodId: string
17
+ secret: Record<string, unknown>
18
+ serviceId: "tidycal"
19
+ }
20
+
21
+ /** Options for one authenticated TidyCal REST request. */
22
+ export interface TidyCalRequestOptions<TSchema extends z.ZodType> {
23
+ /** Public camelCase JSON body. */
24
+ body?: Encodable
25
+
26
+ /** HTTP verb. Defaults to `GET`. */
27
+ method?: "DELETE" | "GET" | "PATCH" | "POST"
28
+
29
+ /** Public camelCase query parameters. */
30
+ query?: Record<string, boolean | number | string | undefined>
31
+
32
+ /** Schema for the normalized provider response. */
33
+ responseSchema: TSchema
34
+ }
35
+
36
+ /** Structured TidyCal REST error. */
37
+ export class TidyCalApiError extends Error {
38
+ /** Normalized provider error payload, when it was valid JSON. */
39
+ readonly body?: Encodable
40
+
41
+ /** Retry delay in seconds, when TidyCal returned one. */
42
+ readonly retryAfter?: number
43
+
44
+ /** HTTP status returned by TidyCal. */
45
+ readonly status: number
46
+
47
+ /**
48
+ * Creates a structured error from one TidyCal response.
49
+ *
50
+ * @param options - Response status, provider body, and retry metadata.
51
+ * @param options.body - Normalized provider error payload.
52
+ * @param options.retryAfter - Retry delay in seconds.
53
+ * @param options.status - HTTP response status.
54
+ */
55
+ constructor(options: {
56
+ body?: Encodable
57
+ retryAfter?: number
58
+ status: number
59
+ }) {
60
+ super(
61
+ getErrorMessage(options.body) ??
62
+ `TidyCal API request failed with status ${options.status}.`,
63
+ )
64
+ this.name = "TidyCalApiError"
65
+ this.body = options.body
66
+ this.retryAfter = options.retryAfter
67
+ this.status = options.status
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Creates an authenticated TidyCal REST client.
73
+ *
74
+ * @param account - Resolved OAuth or personal-access-token account.
75
+ * @throws When the connection method or secret shape is unsupported.
76
+ */
77
+ export function getTidyCalApi(account: TidyCalResolvedAccount) {
78
+ const accessToken =
79
+ account.connectionMethodId === "oauth"
80
+ ? TIDYCAL_OAUTH_SECRET_SCHEMA.parse(account.secret).accessToken
81
+ : account.connectionMethodId === "personal-access-token"
82
+ ? TIDYCAL_PERSONAL_ACCESS_TOKEN_SECRET_SCHEMA.parse(account.secret)
83
+ .apiKey
84
+ : undefined
85
+ if (!accessToken) {
86
+ throw new Error(
87
+ `Unsupported TidyCal connection method: ${account.connectionMethodId}`,
88
+ )
89
+ }
90
+
91
+ /**
92
+ * Runs one request and returns its normalized response.
93
+ *
94
+ * @param path - TidyCal API path relative to the API root.
95
+ * @param options - Method, parameters, and response schema.
96
+ */
97
+ async function request<TSchema extends z.ZodType>(
98
+ path: string,
99
+ options: TidyCalRequestOptions<TSchema>,
100
+ ): Promise<z.output<TSchema>> {
101
+ const url = new URL(path.replace(/^\//, ""), TIDYCAL_API_BASE_URL)
102
+ if (url.origin !== TIDYCAL_API_ORIGIN) {
103
+ throw new Error("TidyCal API paths must use the TidyCal API origin.")
104
+ }
105
+ for (const [name, value] of Object.entries(options.query ?? {})) {
106
+ if (value !== undefined)
107
+ url.searchParams.set(toSnakeCase(name), String(value))
108
+ }
109
+ const response = await fetch(url, {
110
+ body:
111
+ options.body === undefined
112
+ ? undefined
113
+ : JSON.stringify(toTidyCal(options.body)),
114
+ headers: {
115
+ Accept: "application/json",
116
+ Authorization: `Bearer ${accessToken}`,
117
+ ...(options.body === undefined
118
+ ? {}
119
+ : { "Content-Type": "application/json" }),
120
+ },
121
+ method: options.method ?? "GET",
122
+ })
123
+ const normalized = fromTidyCal(parseJson(await response.text()))
124
+ if (!response.ok) {
125
+ const body = encodableSchema.safeParse(normalized)
126
+ throw new TidyCalApiError({
127
+ ...(body.success && { body: body.data }),
128
+ retryAfter: parseFiniteNumber(response.headers.get("Retry-After")),
129
+ status: response.status,
130
+ })
131
+ }
132
+ return options.responseSchema.parse(normalized)
133
+ }
134
+
135
+ return { request }
136
+ }
137
+
138
+ /**
139
+ * Converts public camelCase JSON to TidyCal wire keys.
140
+ *
141
+ * @param value - Codec-safe public value.
142
+ */
143
+ export function toTidyCal(value: Encodable): Encodable {
144
+ if (value instanceof Date) return value.toISOString()
145
+ if (Array.isArray(value)) return value.map(toTidyCal)
146
+ if (!isPlainObject(value)) return value
147
+ return Object.fromEntries(
148
+ Object.entries(value).map(([key, item]) => [
149
+ toSnakeCase(key),
150
+ toTidyCal(item),
151
+ ]),
152
+ )
153
+ }
154
+
155
+ /**
156
+ * Converts TidyCal wire JSON to public camelCase keys.
157
+ *
158
+ * @param value - Untrusted provider JSON value.
159
+ */
160
+ export function fromTidyCal(value: unknown): unknown {
161
+ if (Array.isArray(value)) return value.map(fromTidyCal)
162
+ if (!isPlainObject(value)) return value
163
+ return Object.fromEntries(
164
+ Object.entries(value).map(([key, item]) => [
165
+ toCamelCase(key),
166
+ fromTidyCal(item),
167
+ ]),
168
+ )
169
+ }
170
+
171
+ /**
172
+ * Returns whether a value is a plain JSON object.
173
+ *
174
+ * @param value - Value to inspect.
175
+ */
176
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
177
+ return typeof value === "object" && value !== null && !Array.isArray(value)
178
+ }
179
+
180
+ /**
181
+ * Parses JSON without obscuring the eventual structured API error.
182
+ *
183
+ * @param value - Response text to parse.
184
+ */
185
+ function parseJson(value: string): unknown {
186
+ try {
187
+ return JSON.parse(value)
188
+ } catch {
189
+ return undefined
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Parses a finite numeric response header.
195
+ *
196
+ * @param value - Header value.
197
+ */
198
+ function parseFiniteNumber(value: string | null) {
199
+ if (value === null) return undefined
200
+ const number = Number(value)
201
+ return Number.isFinite(number) ? number : undefined
202
+ }
203
+
204
+ /**
205
+ * Extracts the provider's primary error text without weakening its payload.
206
+ *
207
+ * @param body - Normalized provider error body.
208
+ */
209
+ function getErrorMessage(body: Encodable | undefined) {
210
+ if (!isPlainObject(body)) return undefined
211
+ if (typeof body.message === "string") return body.message
212
+ const errors = body.errors
213
+ if (!isPlainObject(errors)) return undefined
214
+ const first = Object.values(errors)[0]
215
+ if (typeof first === "string") return first
216
+ return Array.isArray(first) && typeof first[0] === "string"
217
+ ? first[0]
218
+ : undefined
219
+ }
220
+
221
+ /**
222
+ * Converts one camelCase public key to snake_case.
223
+ *
224
+ * @param value - Public key.
225
+ */
226
+ function toSnakeCase(value: string) {
227
+ return value.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
228
+ }
229
+
230
+ /**
231
+ * Converts one snake_case provider key to camelCase.
232
+ *
233
+ * @param value - Provider key.
234
+ */
235
+ function toCamelCase(value: string) {
236
+ return value.replace(/_([a-z0-9])/g, (_, letter: string) =>
237
+ letter.toUpperCase(),
238
+ )
239
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./api"
2
+ export * from "./schemas"
@@ -0,0 +1,105 @@
1
+ import * as z from "zod"
2
+
3
+ const DATE_TIME_SCHEMA = z.iso.datetime({ offset: true })
4
+ const ID_SCHEMA = z.number().int().positive()
5
+
6
+ export const TIDYCAL_ACCOUNT_SCHEMA = z.object({
7
+ currencySymbol: z.string(),
8
+ email: z.email(),
9
+ language: z.string(),
10
+ lifetimeProAt: DATE_TIME_SCHEMA.nullable(),
11
+ name: z.string(),
12
+ profilePictureUrl: z.url().nullable(),
13
+ vanityPath: z.string(),
14
+ })
15
+
16
+ export const TIDYCAL_CONTACT_SCHEMA = z.object({
17
+ createdAt: DATE_TIME_SCHEMA,
18
+ email: z.email(),
19
+ id: ID_SCHEMA,
20
+ ipAddress: z.string().nullable().optional(),
21
+ name: z.string(),
22
+ phoneNumber: z.string().nullable().optional(),
23
+ timezone: z.string().nullable().optional(),
24
+ updatedAt: DATE_TIME_SCHEMA,
25
+ })
26
+
27
+ export const TIDYCAL_PAYMENT_SCHEMA = z.object({
28
+ amount: z.number().nonnegative(),
29
+ bookingId: ID_SCHEMA,
30
+ createdAt: DATE_TIME_SCHEMA,
31
+ currency: z.string(),
32
+ id: ID_SCHEMA,
33
+ paymentId: z.string(),
34
+ updatedAt: DATE_TIME_SCHEMA,
35
+ })
36
+
37
+ export const TIDYCAL_QUESTION_SCHEMA = z.object({
38
+ answer: z.string(),
39
+ bookingId: ID_SCHEMA,
40
+ createdAt: DATE_TIME_SCHEMA,
41
+ id: ID_SCHEMA,
42
+ question: z.string(),
43
+ updatedAt: DATE_TIME_SCHEMA,
44
+ })
45
+
46
+ export const TIDYCAL_BOOKING_SCHEMA = z.object({
47
+ bookingTypeId: ID_SCHEMA,
48
+ cancelledAt: DATE_TIME_SCHEMA.nullable(),
49
+ contact: TIDYCAL_CONTACT_SCHEMA,
50
+ contactId: ID_SCHEMA,
51
+ createdAt: DATE_TIME_SCHEMA,
52
+ endsAt: DATE_TIME_SCHEMA,
53
+ id: ID_SCHEMA,
54
+ meetingId: z.string().nullable().optional(),
55
+ meetingUrl: z.url().nullable().optional(),
56
+ payment: TIDYCAL_PAYMENT_SCHEMA.nullable().optional(),
57
+ questions: TIDYCAL_QUESTION_SCHEMA.array(),
58
+ startsAt: DATE_TIME_SCHEMA,
59
+ timezone: z.string(),
60
+ updatedAt: DATE_TIME_SCHEMA,
61
+ })
62
+
63
+ export const TIDYCAL_BOOKING_TYPE_SCHEMA = z.object({
64
+ bookingThresholdMinutes: z.number().int().min(0),
65
+ createdAt: DATE_TIME_SCHEMA,
66
+ currencyCode: z.string().nullable().optional(),
67
+ description: z.string(),
68
+ disabledAt: DATE_TIME_SCHEMA.nullable(),
69
+ durationMinutes: z.number().int().positive(),
70
+ id: ID_SCHEMA,
71
+ latestAvailabilityDays: z.number().int().min(0),
72
+ maxBookings: z.number().int().positive(),
73
+ paddingMinutes: z.number().int().min(0),
74
+ paymentPlatform: z.enum(["paypal", "stripe", "tidycal"]).nullable(),
75
+ paymentPlatformId: z.string().nullable().optional(),
76
+ price: z.number().nonnegative(),
77
+ private: z.boolean(),
78
+ redirectUrl: z.url().nullable(),
79
+ title: z.string(),
80
+ updatedAt: DATE_TIME_SCHEMA,
81
+ url: z.url(),
82
+ urlSlug: z.string(),
83
+ userId: ID_SCHEMA,
84
+ })
85
+
86
+ export const TIDYCAL_TIMESLOT_SCHEMA = z.object({
87
+ availableBookings: z.number().int().nonnegative(),
88
+ endsAt: DATE_TIME_SCHEMA,
89
+ startsAt: DATE_TIME_SCHEMA,
90
+ })
91
+
92
+ export const TIDYCAL_TEAM_SCHEMA = z.object({
93
+ createdAt: DATE_TIME_SCHEMA,
94
+ id: ID_SCHEMA,
95
+ name: z.string(),
96
+ updatedAt: DATE_TIME_SCHEMA,
97
+ })
98
+
99
+ export const TIDYCAL_TEAM_USER_SCHEMA = z.object({
100
+ createdAt: DATE_TIME_SCHEMA,
101
+ email: z.email(),
102
+ id: ID_SCHEMA,
103
+ name: z.string(),
104
+ updatedAt: DATE_TIME_SCHEMA,
105
+ })
package/src/triggers.ts CHANGED
@@ -12,6 +12,7 @@ import type { googleFormsTriggerContracts } from "./google-forms"
12
12
  import type { googleMeetTriggerContracts } from "./google-meet"
13
13
  import type { googleSheetsTriggerContracts } from "./google-sheets"
14
14
  import type { linearTriggerContracts } from "./linear"
15
+ import type { millionVerifierTriggerContracts } from "./millionverifier"
15
16
  import type { notionTriggerContracts } from "./notion"
16
17
  import type { outlookTriggerContracts } from "./outlook"
17
18
  import type { resendTriggerContracts } from "./resend"
@@ -38,6 +39,7 @@ export type TriggerContractMap = typeof airtableTriggerContracts &
38
39
  typeof googleMeetTriggerContracts &
39
40
  typeof googleSheetsTriggerContracts &
40
41
  typeof linearTriggerContracts &
42
+ typeof millionVerifierTriggerContracts &
41
43
  typeof notionTriggerContracts &
42
44
  typeof outlookTriggerContracts &
43
45
  typeof resendTriggerContracts &