@automate.ax/integration-contracts 0.89.2 → 0.91.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.
- package/dist/jobnimbus/api.d.ts +71 -0
- package/dist/jobnimbus/api.js +231 -0
- package/dist/jobnimbus/index.d.ts +2 -0
- package/dist/jobnimbus/index.js +2 -0
- package/dist/jobnimbus/schemas.d.ts +508 -0
- package/dist/jobnimbus/schemas.js +308 -0
- package/dist/notion/index.d.ts +5 -1
- package/dist/notion/index.js +6 -0
- package/dist/notion/schemas.d.ts +246 -0
- package/dist/notion/schemas.js +15 -0
- package/dist/whatsapp/index.d.ts +2 -2
- package/dist/whatsapp/schemas.d.ts +5 -5
- package/package.json +8 -2
- package/src/jobnimbus/api.ts +294 -0
- package/src/jobnimbus/index.ts +2 -0
- package/src/jobnimbus/schemas.ts +351 -0
- package/src/notion/index.ts +10 -0
- package/src/notion/schemas.ts +27 -0
package/dist/whatsapp/index.d.ts
CHANGED
|
@@ -330,11 +330,11 @@ export declare const whatsappTriggerContracts: {
|
|
|
330
330
|
video: "video";
|
|
331
331
|
location: "location";
|
|
332
332
|
system: "system";
|
|
333
|
+
order: "order";
|
|
333
334
|
reaction: "reaction";
|
|
334
335
|
button: "button";
|
|
335
336
|
contacts: "contacts";
|
|
336
337
|
interactive: "interactive";
|
|
337
|
-
order: "order";
|
|
338
338
|
sticker: "sticker";
|
|
339
339
|
unsupported: "unsupported";
|
|
340
340
|
}>;
|
|
@@ -425,11 +425,11 @@ export declare const whatsappTriggerContracts: {
|
|
|
425
425
|
video: "video";
|
|
426
426
|
location: "location";
|
|
427
427
|
system: "system";
|
|
428
|
+
order: "order";
|
|
428
429
|
reaction: "reaction";
|
|
429
430
|
button: "button";
|
|
430
431
|
contacts: "contacts";
|
|
431
432
|
interactive: "interactive";
|
|
432
|
-
order: "order";
|
|
433
433
|
sticker: "sticker";
|
|
434
434
|
unsupported: "unsupported";
|
|
435
435
|
}>;
|
|
@@ -96,11 +96,11 @@ export declare const WHATSAPP_WEBHOOK_SCHEMA: z.ZodObject<{
|
|
|
96
96
|
video: "video";
|
|
97
97
|
location: "location";
|
|
98
98
|
system: "system";
|
|
99
|
+
order: "order";
|
|
99
100
|
reaction: "reaction";
|
|
100
101
|
button: "button";
|
|
101
102
|
contacts: "contacts";
|
|
102
103
|
interactive: "interactive";
|
|
103
|
-
order: "order";
|
|
104
104
|
sticker: "sticker";
|
|
105
105
|
unsupported: "unsupported";
|
|
106
106
|
}>;
|
|
@@ -189,11 +189,11 @@ export declare const WHATSAPP_MESSAGE_RECEIVED_EVENT_SCHEMA: z.ZodObject<{
|
|
|
189
189
|
video: "video";
|
|
190
190
|
location: "location";
|
|
191
191
|
system: "system";
|
|
192
|
+
order: "order";
|
|
192
193
|
reaction: "reaction";
|
|
193
194
|
button: "button";
|
|
194
195
|
contacts: "contacts";
|
|
195
196
|
interactive: "interactive";
|
|
196
|
-
order: "order";
|
|
197
197
|
sticker: "sticker";
|
|
198
198
|
unsupported: "unsupported";
|
|
199
199
|
}>;
|
|
@@ -284,11 +284,11 @@ export declare const WHATSAPP_MESSAGE_RECEIVED_EVENT_SCHEMA: z.ZodObject<{
|
|
|
284
284
|
video: "video";
|
|
285
285
|
location: "location";
|
|
286
286
|
system: "system";
|
|
287
|
+
order: "order";
|
|
287
288
|
reaction: "reaction";
|
|
288
289
|
button: "button";
|
|
289
290
|
contacts: "contacts";
|
|
290
291
|
interactive: "interactive";
|
|
291
|
-
order: "order";
|
|
292
292
|
sticker: "sticker";
|
|
293
293
|
unsupported: "unsupported";
|
|
294
294
|
}>;
|
|
@@ -768,7 +768,7 @@ export declare function normalizeWhatsAppWebhook(input: unknown): {
|
|
|
768
768
|
message: {
|
|
769
769
|
id: string;
|
|
770
770
|
timestamp: string;
|
|
771
|
-
type: "unknown" | "text" | "document" | "audio" | "image" | "video" | "location" | "system" | "
|
|
771
|
+
type: "unknown" | "text" | "document" | "audio" | "image" | "video" | "location" | "system" | "order" | "reaction" | "button" | "contacts" | "interactive" | "sticker" | "unsupported";
|
|
772
772
|
content?: z.core.util.JSONType | undefined;
|
|
773
773
|
context?: {
|
|
774
774
|
messageId: string;
|
|
@@ -781,7 +781,7 @@ export declare function normalizeWhatsAppWebhook(input: unknown): {
|
|
|
781
781
|
from: string;
|
|
782
782
|
id: string;
|
|
783
783
|
timestamp: string;
|
|
784
|
-
type: "unknown" | "text" | "document" | "audio" | "image" | "video" | "location" | "system" | "
|
|
784
|
+
type: "unknown" | "text" | "document" | "audio" | "image" | "video" | "location" | "system" | "order" | "reaction" | "button" | "contacts" | "interactive" | "sticker" | "unsupported";
|
|
785
785
|
audio?: {
|
|
786
786
|
id: string;
|
|
787
787
|
mime_type?: string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automate.ax/integration-contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.91.0",
|
|
4
4
|
"description": "Shared integration payload contracts and provider primitives for Automate.ax.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"./google-forms": "./src/google-forms/index.ts",
|
|
29
29
|
"./google-meet": "./src/google-meet/index.ts",
|
|
30
30
|
"./github": "./src/github/index.ts",
|
|
31
|
+
"./jobnimbus": "./src/jobnimbus/index.ts",
|
|
31
32
|
"./linear": "./src/linear/index.ts",
|
|
32
33
|
"./notion": "./src/notion/index.ts",
|
|
33
34
|
"./outlook": "./src/outlook/index.ts",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
}
|
|
48
49
|
},
|
|
49
50
|
"dependencies": {
|
|
50
|
-
"@automate.ax/codec": "0.
|
|
51
|
+
"@automate.ax/codec": "0.91.0",
|
|
51
52
|
"@cfworker/json-schema": "^4.1.1",
|
|
52
53
|
"@googleapis/calendar": "^15.0.0",
|
|
53
54
|
"@googleapis/forms": "^6.0.1",
|
|
@@ -148,6 +149,11 @@
|
|
|
148
149
|
"types": "./dist/github/index.d.ts",
|
|
149
150
|
"default": "./dist/github/index.js"
|
|
150
151
|
},
|
|
152
|
+
"./jobnimbus": {
|
|
153
|
+
"bun": "./src/jobnimbus/index.ts",
|
|
154
|
+
"types": "./dist/jobnimbus/index.d.ts",
|
|
155
|
+
"default": "./dist/jobnimbus/index.js"
|
|
156
|
+
},
|
|
151
157
|
"./linear": {
|
|
152
158
|
"bun": "./src/linear/index.ts",
|
|
153
159
|
"types": "./dist/linear/index.d.ts",
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { isEncodable, type Encodable } from "@automate.ax/codec"
|
|
2
|
+
import * as z from "zod"
|
|
3
|
+
|
|
4
|
+
const JOB_NIMBUS_API_ORIGINS = {
|
|
5
|
+
accountsReceivable: "https://api.jobnimbus.com/accounts-receivable/v1/",
|
|
6
|
+
activities: "https://api.jobnimbus.com/activities/v1/",
|
|
7
|
+
files: "https://api.jobnimbus.com/files/v1/",
|
|
8
|
+
identity: "https://api.jobnimbus.com/identity/v1/",
|
|
9
|
+
public: "https://app.jobnimbus.com/api1/",
|
|
10
|
+
} as const
|
|
11
|
+
|
|
12
|
+
const JOB_NIMBUS_SECRET_SCHEMA = z.object({
|
|
13
|
+
apiKey: z.string().trim().min(1),
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export const JOB_NIMBUS_VALUE_SCHEMA = z.custom<Encodable>(isEncodable, {
|
|
17
|
+
message: "Expected a codec-safe JobNimbus value.",
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export const JOB_NIMBUS_RESPONSE_SCHEMA = z.union([
|
|
21
|
+
JOB_NIMBUS_VALUE_SCHEMA,
|
|
22
|
+
z.undefined().transform(() => null),
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
export type JobNimbusApi = keyof typeof JOB_NIMBUS_API_ORIGINS
|
|
26
|
+
|
|
27
|
+
interface JobNimbusRequestOptions<TSchema extends z.ZodType> {
|
|
28
|
+
body?: Encodable
|
|
29
|
+
contentType?: string
|
|
30
|
+
headers?: Record<string, string | undefined>
|
|
31
|
+
method?: "DELETE" | "GET" | "PATCH" | "POST" | "PUT"
|
|
32
|
+
query?: Record<
|
|
33
|
+
string,
|
|
34
|
+
| boolean
|
|
35
|
+
| number
|
|
36
|
+
| string
|
|
37
|
+
| readonly (boolean | number | string)[]
|
|
38
|
+
| null
|
|
39
|
+
| undefined
|
|
40
|
+
>
|
|
41
|
+
responseSchema: TSchema
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Error returned by a rejected JobNimbus API request. */
|
|
45
|
+
export class JobNimbusApiError extends Error {
|
|
46
|
+
readonly details: Encodable
|
|
47
|
+
readonly status: number
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates an error from a rejected JobNimbus response.
|
|
51
|
+
*
|
|
52
|
+
* @param status - HTTP response status.
|
|
53
|
+
* @param details - Codec-safe response details.
|
|
54
|
+
*/
|
|
55
|
+
constructor(status: number, details: Encodable) {
|
|
56
|
+
super(`JobNimbus API request failed (${status}).`)
|
|
57
|
+
this.name = "JobNimbusApiError"
|
|
58
|
+
this.details = details
|
|
59
|
+
this.status = status
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Creates an authenticated client for one documented JobNimbus API root.
|
|
65
|
+
*
|
|
66
|
+
* @param secret - Stored JobNimbus API key.
|
|
67
|
+
* @param api - Public API or one current Platform service.
|
|
68
|
+
*/
|
|
69
|
+
export function getJobNimbusApi(secret: unknown, api: JobNimbusApi) {
|
|
70
|
+
const { apiKey } = JOB_NIMBUS_SECRET_SCHEMA.parse(secret)
|
|
71
|
+
const origin = JOB_NIMBUS_API_ORIGINS[api]
|
|
72
|
+
const root = new URL(origin)
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
/**
|
|
76
|
+
* Sends one JSON request below the selected JobNimbus API root.
|
|
77
|
+
*
|
|
78
|
+
* @param path - Relative path below the selected root.
|
|
79
|
+
* @param options - Request method, query, body, headers, and response
|
|
80
|
+
* schema.
|
|
81
|
+
*/
|
|
82
|
+
async request<TSchema extends z.ZodType>(
|
|
83
|
+
path: string,
|
|
84
|
+
options: JobNimbusRequestOptions<TSchema>,
|
|
85
|
+
): Promise<z.output<TSchema>> {
|
|
86
|
+
const normalizedPath = path.replace(/^\/+/, "")
|
|
87
|
+
if (
|
|
88
|
+
!normalizedPath ||
|
|
89
|
+
normalizedPath.includes("://") ||
|
|
90
|
+
normalizedPath.includes("\\")
|
|
91
|
+
) {
|
|
92
|
+
throw new TypeError("JobNimbus API paths must be relative.")
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const url = new URL(normalizedPath, origin)
|
|
96
|
+
if (
|
|
97
|
+
url.origin !== root.origin ||
|
|
98
|
+
!url.pathname.startsWith(root.pathname)
|
|
99
|
+
) {
|
|
100
|
+
throw new TypeError(
|
|
101
|
+
"JobNimbus API paths must remain below the selected root.",
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
for (const [key, value] of Object.entries(options.query ?? {})) {
|
|
105
|
+
if (value == null) continue
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
for (const item of value) url.searchParams.append(key, String(item))
|
|
108
|
+
} else {
|
|
109
|
+
url.searchParams.set(key, String(value))
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const headers = new Headers({
|
|
114
|
+
Accept: "application/json",
|
|
115
|
+
Authorization: `Bearer ${apiKey}`,
|
|
116
|
+
})
|
|
117
|
+
for (const [key, value] of Object.entries(options.headers ?? {})) {
|
|
118
|
+
if (value !== undefined) headers.set(key, value)
|
|
119
|
+
}
|
|
120
|
+
if (options.body !== undefined) {
|
|
121
|
+
headers.set("Content-Type", options.contentType ?? "application/json")
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const response = await fetch(url, {
|
|
125
|
+
body:
|
|
126
|
+
options.body === undefined
|
|
127
|
+
? undefined
|
|
128
|
+
: JSON.stringify(
|
|
129
|
+
api === "public" ? toJobNimbus(options.body) : options.body,
|
|
130
|
+
),
|
|
131
|
+
headers,
|
|
132
|
+
method: options.method ?? "GET",
|
|
133
|
+
})
|
|
134
|
+
const text = await response.text()
|
|
135
|
+
const parsed = JOB_NIMBUS_RESPONSE_SCHEMA.safeParse(
|
|
136
|
+
fromJobNimbus(text ? parseJson(text) : null),
|
|
137
|
+
)
|
|
138
|
+
if (!response.ok || !parsed.success) {
|
|
139
|
+
throw new JobNimbusApiError(
|
|
140
|
+
response.status,
|
|
141
|
+
parsed.success ? parsed.data : { response: text },
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
return options.responseSchema.parse(parsed.data)
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Downloads one JobNimbus attachment from its documented non-API route.
|
|
151
|
+
*
|
|
152
|
+
* @param secret - Stored JobNimbus API key.
|
|
153
|
+
* @param fileId - Attachment JNID.
|
|
154
|
+
*/
|
|
155
|
+
export async function downloadJobNimbusFile(secret: unknown, fileId: string) {
|
|
156
|
+
const { apiKey } = JOB_NIMBUS_SECRET_SCHEMA.parse(secret)
|
|
157
|
+
const response = await fetch(
|
|
158
|
+
new URL(
|
|
159
|
+
encodeURIComponent(z.string().trim().min(1).parse(fileId)),
|
|
160
|
+
"https://app.jobnimbus.com/files/",
|
|
161
|
+
),
|
|
162
|
+
{ headers: { Authorization: `Bearer ${apiKey}` } },
|
|
163
|
+
)
|
|
164
|
+
if (!response.ok) {
|
|
165
|
+
throw new JobNimbusApiError(response.status, {
|
|
166
|
+
response: await response.text(),
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
return response.blob()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Converts public camelCase JSON into JobNimbus's provider-native keys.
|
|
174
|
+
*
|
|
175
|
+
* @param value - Codec-safe public value.
|
|
176
|
+
*/
|
|
177
|
+
export function toJobNimbus(value: Encodable): Encodable {
|
|
178
|
+
if (value instanceof Date) return value.toISOString()
|
|
179
|
+
if (Array.isArray(value)) return value.map(toJobNimbus)
|
|
180
|
+
if (!isPlainObject(value)) return value
|
|
181
|
+
|
|
182
|
+
return Object.fromEntries(
|
|
183
|
+
Object.entries(value).flatMap(([key, item]) =>
|
|
184
|
+
key === "customFields" && isPlainObject(item)
|
|
185
|
+
? Object.entries(item).map(([field, fieldValue]) => [
|
|
186
|
+
field,
|
|
187
|
+
toJobNimbus(fieldValue),
|
|
188
|
+
])
|
|
189
|
+
: [[toSnakeCase(key), toJobNimbus(item)]],
|
|
190
|
+
),
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Converts JobNimbus JSON into public camelCase values.
|
|
196
|
+
*
|
|
197
|
+
* Documented legacy Unix date fields become Date instances. Unknown fields are
|
|
198
|
+
* preserved so account-defined CRM fields survive normalization.
|
|
199
|
+
*
|
|
200
|
+
* @param value - Raw provider value.
|
|
201
|
+
*/
|
|
202
|
+
export function fromJobNimbus(value: unknown): Encodable {
|
|
203
|
+
return normalizeJobNimbusValue(value)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Recursively normalizes one raw provider value and its original key.
|
|
208
|
+
*
|
|
209
|
+
* @param value - Raw provider value.
|
|
210
|
+
* @param providerKey - Original provider field name, when nested in an object.
|
|
211
|
+
*/
|
|
212
|
+
function normalizeJobNimbusValue(
|
|
213
|
+
value: unknown,
|
|
214
|
+
providerKey?: string,
|
|
215
|
+
): Encodable {
|
|
216
|
+
if (typeof value === "number" && providerKey?.startsWith("date_")) {
|
|
217
|
+
return new Date(value * 1_000)
|
|
218
|
+
}
|
|
219
|
+
if (Array.isArray(value)) {
|
|
220
|
+
return value.map((item) => normalizeJobNimbusValue(item))
|
|
221
|
+
}
|
|
222
|
+
if (!isPlainObject(value)) return JOB_NIMBUS_VALUE_SCHEMA.parse(value)
|
|
223
|
+
|
|
224
|
+
const customFields: Record<string, Encodable> = {}
|
|
225
|
+
return Object.fromEntries([
|
|
226
|
+
...Object.entries(value).flatMap(([key, item]) => {
|
|
227
|
+
if (/^cf_(?:boolean|date|double|long|string)_\d+$/.test(key)) {
|
|
228
|
+
customFields[key] = normalizeJobNimbusValue(item, key)
|
|
229
|
+
return []
|
|
230
|
+
}
|
|
231
|
+
return [
|
|
232
|
+
[
|
|
233
|
+
key === "extermal_id"
|
|
234
|
+
? "externalId"
|
|
235
|
+
: key === "jnid"
|
|
236
|
+
? "id"
|
|
237
|
+
: key === "recid"
|
|
238
|
+
? "recordId"
|
|
239
|
+
: toCamelCase(key),
|
|
240
|
+
normalizeJobNimbusValue(item, key),
|
|
241
|
+
] as const,
|
|
242
|
+
]
|
|
243
|
+
}),
|
|
244
|
+
...(Object.keys(customFields).length > 0
|
|
245
|
+
? [["customFields", customFields] as const]
|
|
246
|
+
: []),
|
|
247
|
+
])
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Parses a response body while preserving non-JSON diagnostics.
|
|
252
|
+
*
|
|
253
|
+
* @param value - Raw response body.
|
|
254
|
+
*/
|
|
255
|
+
function parseJson(value: string): unknown {
|
|
256
|
+
try {
|
|
257
|
+
return JSON.parse(value)
|
|
258
|
+
} catch {
|
|
259
|
+
return { response: value }
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Checks whether a value can be traversed as a JSON-style object.
|
|
265
|
+
*
|
|
266
|
+
* @param value - Candidate object value.
|
|
267
|
+
*/
|
|
268
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
269
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Converts one provider snake-case key to camel case.
|
|
274
|
+
*
|
|
275
|
+
* @param value - Provider field name.
|
|
276
|
+
*/
|
|
277
|
+
function toCamelCase(value: string) {
|
|
278
|
+
return value
|
|
279
|
+
.replace(/_([a-z0-9])/g, (_match, character: string) =>
|
|
280
|
+
character.toUpperCase(),
|
|
281
|
+
)
|
|
282
|
+
.replace(/^[A-Z]/, (character) => character.toLowerCase())
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Converts one public camel-case key to provider snake case.
|
|
287
|
+
*
|
|
288
|
+
* @param value - Public field name.
|
|
289
|
+
*/
|
|
290
|
+
function toSnakeCase(value: string) {
|
|
291
|
+
return value
|
|
292
|
+
.replace(/([a-zA-Z])([0-9])/g, "$1_$2")
|
|
293
|
+
.replace(/[A-Z]/g, (character) => `_${character.toLowerCase()}`)
|
|
294
|
+
}
|