@atribu/node 0.1.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 (46) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +21 -0
  3. package/README.md +423 -0
  4. package/dist/admin/index.cjs +326 -0
  5. package/dist/admin/index.cjs.map +1 -0
  6. package/dist/admin/index.d.cts +46 -0
  7. package/dist/admin/index.d.ts +46 -0
  8. package/dist/admin/index.js +323 -0
  9. package/dist/admin/index.js.map +1 -0
  10. package/dist/api.d-BXINTQo6.d.cts +3547 -0
  11. package/dist/api.d-BXINTQo6.d.ts +3547 -0
  12. package/dist/errors-D3ApBz8J.d.cts +86 -0
  13. package/dist/errors-D3ApBz8J.d.ts +86 -0
  14. package/dist/index.cjs +549 -0
  15. package/dist/index.cjs.map +1 -0
  16. package/dist/index.d.cts +198 -0
  17. package/dist/index.d.ts +198 -0
  18. package/dist/index.js +536 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/next/index.cjs +153 -0
  21. package/dist/next/index.cjs.map +1 -0
  22. package/dist/next/index.d.cts +43 -0
  23. package/dist/next/index.d.ts +43 -0
  24. package/dist/next/index.js +151 -0
  25. package/dist/next/index.js.map +1 -0
  26. package/dist/oauth/index.cjs +299 -0
  27. package/dist/oauth/index.cjs.map +1 -0
  28. package/dist/oauth/index.d.cts +117 -0
  29. package/dist/oauth/index.d.ts +117 -0
  30. package/dist/oauth/index.js +291 -0
  31. package/dist/oauth/index.js.map +1 -0
  32. package/dist/test/index.cjs +443 -0
  33. package/dist/test/index.cjs.map +1 -0
  34. package/dist/test/index.d.cts +321 -0
  35. package/dist/test/index.d.ts +321 -0
  36. package/dist/test/index.js +437 -0
  37. package/dist/test/index.js.map +1 -0
  38. package/dist/types-Dc6tIN_V.d.cts +101 -0
  39. package/dist/types-Dc6tIN_V.d.ts +101 -0
  40. package/dist/webhooks/index.cjs +97 -0
  41. package/dist/webhooks/index.cjs.map +1 -0
  42. package/dist/webhooks/index.d.cts +35 -0
  43. package/dist/webhooks/index.d.ts +35 -0
  44. package/dist/webhooks/index.js +94 -0
  45. package/dist/webhooks/index.js.map +1 -0
  46. package/package.json +101 -0
@@ -0,0 +1,437 @@
1
+ import { http, HttpResponse } from 'msw';
2
+
3
+ // src/test/handlers.ts
4
+
5
+ // src/test/fixtures.ts
6
+ function deepMerge(base, override) {
7
+ if (!override || typeof override !== "object") return base;
8
+ const out = { ...base };
9
+ for (const [k, v] of Object.entries(override)) {
10
+ const baseVal = base[k];
11
+ if (v && typeof v === "object" && !Array.isArray(v) && baseVal && typeof baseVal === "object" && !Array.isArray(baseVal)) {
12
+ out[k] = deepMerge(baseVal, v);
13
+ } else if (v !== void 0) {
14
+ out[k] = v;
15
+ }
16
+ }
17
+ return out;
18
+ }
19
+ var SAMPLE_APP_ID = "00000000-0000-0000-0000-000000000aaa";
20
+ var SAMPLE_CONNECTION_ID = "00000000-0000-0000-0000-000000000bbb";
21
+ var SAMPLE_PROFILE_ID = "00000000-0000-0000-0000-000000000ccc";
22
+ var SAMPLE_WORKSPACE_ID = "00000000-0000-0000-0000-000000000ddd";
23
+ var SAMPLE_SUBSCRIPTION_ID = "00000000-0000-0000-0000-000000000eee";
24
+ var sampleIds = {
25
+ appId: SAMPLE_APP_ID,
26
+ connectionId: SAMPLE_CONNECTION_ID,
27
+ profileId: SAMPLE_PROFILE_ID,
28
+ workspaceId: SAMPLE_WORKSPACE_ID,
29
+ subscriptionId: SAMPLE_SUBSCRIPTION_ID
30
+ };
31
+ var eventFixtures = {
32
+ whatsappMessageReceived(overrides) {
33
+ return deepMerge(
34
+ {
35
+ id: "evt_wa_received_01",
36
+ type: "message.received",
37
+ occurred_at: "2026-05-15T10:00:00Z",
38
+ app_id: SAMPLE_APP_ID,
39
+ provider: "whatsapp",
40
+ connection_id: SAMPLE_CONNECTION_ID,
41
+ data: {
42
+ wa_message_id: "wamid.HBgM",
43
+ from: "+15551234567",
44
+ to: "+15559876543",
45
+ contact_name: "Alice",
46
+ type: "text",
47
+ text: "Hi! Is this still available?",
48
+ raw: {}
49
+ }
50
+ },
51
+ overrides
52
+ );
53
+ },
54
+ whatsappMessageDelivery(overrides) {
55
+ return deepMerge(
56
+ {
57
+ id: "evt_wa_delivery_01",
58
+ type: "message.delivery",
59
+ occurred_at: "2026-05-15T10:00:01Z",
60
+ app_id: SAMPLE_APP_ID,
61
+ provider: "whatsapp",
62
+ connection_id: SAMPLE_CONNECTION_ID,
63
+ data: {
64
+ wa_message_id: "wamid.HBgM",
65
+ recipient_id: "15551234567",
66
+ status: "delivered",
67
+ raw: {}
68
+ }
69
+ },
70
+ overrides
71
+ );
72
+ },
73
+ instagramFbLoginMessage(overrides) {
74
+ return deepMerge(
75
+ {
76
+ id: "evt_ig_msg_01",
77
+ type: "message.received",
78
+ occurred_at: "2026-05-15T10:00:00Z",
79
+ app_id: SAMPLE_APP_ID,
80
+ provider: "instagram",
81
+ connection_id: SAMPLE_CONNECTION_ID,
82
+ data: {
83
+ sender_id: "17841400000000001",
84
+ recipient_id: "17841400000000002",
85
+ mid: "m_BNvxxxx",
86
+ text: "\u{1F44B}",
87
+ is_echo: false,
88
+ attachments: null,
89
+ referral: null,
90
+ raw: {}
91
+ }
92
+ },
93
+ overrides
94
+ );
95
+ },
96
+ instagramPostback(overrides) {
97
+ return deepMerge(
98
+ {
99
+ id: "evt_ig_pb_01",
100
+ type: "message.received",
101
+ occurred_at: "2026-05-15T10:00:00Z",
102
+ app_id: SAMPLE_APP_ID,
103
+ provider: "instagram",
104
+ connection_id: SAMPLE_CONNECTION_ID,
105
+ data: {
106
+ sender_id: "17841400000000001",
107
+ recipient_id: "17841400000000002",
108
+ kind: "postback",
109
+ title: "Get Started",
110
+ payload: "GET_STARTED",
111
+ raw: {}
112
+ }
113
+ },
114
+ overrides
115
+ );
116
+ },
117
+ instagramIgLoginChange(overrides) {
118
+ return deepMerge(
119
+ {
120
+ id: "evt_ig_change_01",
121
+ type: "message.received",
122
+ occurred_at: "2026-05-15T10:00:00Z",
123
+ app_id: SAMPLE_APP_ID,
124
+ provider: "instagram",
125
+ connection_id: SAMPLE_CONNECTION_ID,
126
+ data: {
127
+ sender_id: "17841400000000001",
128
+ recipient_id: "17841400000000002",
129
+ from_username: "alice.example",
130
+ mid: "m_BNvxxxx",
131
+ text: "Hello!",
132
+ raw: {}
133
+ }
134
+ },
135
+ overrides
136
+ );
137
+ },
138
+ instagramMessageDelivery(overrides) {
139
+ return deepMerge(
140
+ {
141
+ id: "evt_ig_delivery_01",
142
+ type: "message.delivery",
143
+ occurred_at: "2026-05-15T10:00:01Z",
144
+ app_id: SAMPLE_APP_ID,
145
+ provider: "instagram",
146
+ connection_id: SAMPLE_CONNECTION_ID,
147
+ data: {
148
+ sender_id: "17841400000000001",
149
+ recipient_id: "17841400000000002",
150
+ mids: ["m_BNvxxxx"],
151
+ watermark: 17157696e5
152
+ }
153
+ },
154
+ overrides
155
+ );
156
+ }
157
+ };
158
+ var responseFixtures = {
159
+ messageSent(overrides) {
160
+ return {
161
+ data: {
162
+ connection_id: SAMPLE_CONNECTION_ID,
163
+ channel: "whatsapp",
164
+ to: "+15551234567",
165
+ provider_message_id: overrides?.provider_message_id ?? "wamid.HBgM",
166
+ sent_at: overrides?.sent_at ?? (/* @__PURE__ */ new Date()).toISOString()
167
+ }
168
+ };
169
+ },
170
+ commentReply(overrides) {
171
+ return {
172
+ data: {
173
+ comment_id: "ig_comment_01",
174
+ kind: "private_reply",
175
+ connection_id: SAMPLE_CONNECTION_ID,
176
+ provider_message_id: overrides?.provider_message_id ?? "wamid.reply.x",
177
+ sent_at: (/* @__PURE__ */ new Date()).toISOString()
178
+ },
179
+ meta: { profile_id: SAMPLE_PROFILE_ID }
180
+ };
181
+ },
182
+ subscriptionCreated(overrides) {
183
+ return {
184
+ data: {
185
+ id: SAMPLE_SUBSCRIPTION_ID,
186
+ app_id: SAMPLE_APP_ID,
187
+ profile_id: SAMPLE_PROFILE_ID,
188
+ url: overrides?.url ?? "https://example.com/webhook",
189
+ events: ["message.received", "message.delivery"],
190
+ providers: ["whatsapp", "instagram"],
191
+ status: "active",
192
+ last_delivery_at: null,
193
+ consecutive_failures: 0,
194
+ previous_secret_expires_at: null,
195
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
196
+ updated_at: (/* @__PURE__ */ new Date()).toISOString(),
197
+ secret: overrides?.secret ?? "whsec_fixture_test_secret_value"
198
+ }
199
+ };
200
+ },
201
+ subscriptionList(overrides) {
202
+ const items = overrides?.items ?? 1;
203
+ return {
204
+ data: Array.from({ length: items }, (_, i) => ({
205
+ id: `00000000-0000-0000-0000-${String(i).padStart(12, "0")}`,
206
+ app_id: SAMPLE_APP_ID,
207
+ profile_id: SAMPLE_PROFILE_ID,
208
+ url: `https://example.com/webhook/${i}`,
209
+ events: ["message.received", "message.delivery"],
210
+ providers: ["whatsapp", "instagram"],
211
+ status: "active",
212
+ last_delivery_at: null,
213
+ consecutive_failures: 0,
214
+ previous_secret_expires_at: null,
215
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
216
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
217
+ })),
218
+ meta: { profile_id: SAMPLE_PROFILE_ID }
219
+ };
220
+ },
221
+ rotatedSecret(overrides) {
222
+ const graceDays = overrides?.graceDays ?? 7;
223
+ return {
224
+ data: {
225
+ subscription_id: SAMPLE_SUBSCRIPTION_ID,
226
+ secret: overrides?.secret ?? "whsec_rotated_test_secret",
227
+ grace_days: graceDays,
228
+ previous_secret_expires_at: new Date(
229
+ Date.now() + graceDays * 864e5
230
+ ).toISOString()
231
+ },
232
+ meta: { profile_id: SAMPLE_PROFILE_ID }
233
+ };
234
+ },
235
+ oauthTokenResponse(overrides) {
236
+ return {
237
+ access_token: overrides?.accessToken ?? "atb_live_test_fixture_key",
238
+ token_type: "bearer",
239
+ scope: overrides?.scope ?? "whatsapp",
240
+ connection_id: SAMPLE_CONNECTION_ID,
241
+ profile_id: SAMPLE_PROFILE_ID,
242
+ workspace_id: SAMPLE_WORKSPACE_ID
243
+ };
244
+ },
245
+ oauthError(error = "invalid_grant", description = "code expired") {
246
+ return { error, error_description: description };
247
+ },
248
+ apiError(code, message, status) {
249
+ return { error: { code, message, status, request_id: "req_fixture" } };
250
+ }
251
+ };
252
+ var fixtures = {
253
+ ids: sampleIds,
254
+ events: eventFixtures,
255
+ responses: responseFixtures
256
+ };
257
+
258
+ // src/test/handlers.ts
259
+ var DEFAULT_BASE_URL = "https://www.atribu.app";
260
+ function resolve(override, fallbackStatus, fallbackBody) {
261
+ const status = override?.status ?? fallbackStatus;
262
+ const body = override?.body ?? fallbackBody;
263
+ if (status === 204) return new HttpResponse(null, { status: 204 });
264
+ return HttpResponse.json(body, {
265
+ status,
266
+ headers: { "x-request-id": "req_mock" }
267
+ });
268
+ }
269
+ function atribuMockHandlers(overrides = {}) {
270
+ const baseUrl = (overrides.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
271
+ const u = (p) => `${baseUrl}${p}`;
272
+ return [
273
+ // ----- Messaging -----
274
+ http.post(
275
+ u("/api/v1/messages"),
276
+ () => resolve(overrides.messages?.send, 200, responseFixtures.messageSent())
277
+ ),
278
+ // ----- IG Comments -----
279
+ http.post(
280
+ u("/api/v1/comments/:commentId/reply"),
281
+ () => resolve(overrides.comments?.reply, 200, responseFixtures.commentReply())
282
+ ),
283
+ http.post(
284
+ u("/api/v1/comments/:commentId/private-reply"),
285
+ () => resolve(
286
+ overrides.comments?.privateReply,
287
+ 200,
288
+ responseFixtures.commentReply()
289
+ )
290
+ ),
291
+ // ----- Webhook subscriptions -----
292
+ http.get(
293
+ u("/api/v1/webhooks/subscriptions"),
294
+ () => resolve(
295
+ overrides.webhooks?.subscriptions?.list,
296
+ 200,
297
+ responseFixtures.subscriptionList()
298
+ )
299
+ ),
300
+ http.post(
301
+ u("/api/v1/webhooks/subscriptions"),
302
+ () => resolve(
303
+ overrides.webhooks?.subscriptions?.create,
304
+ 201,
305
+ responseFixtures.subscriptionCreated()
306
+ )
307
+ ),
308
+ http.patch(
309
+ u("/api/v1/webhooks/subscriptions/:id"),
310
+ () => resolve(
311
+ overrides.webhooks?.subscriptions?.update,
312
+ 200,
313
+ responseFixtures.subscriptionList({ items: 1 })
314
+ )
315
+ ),
316
+ http.delete(
317
+ u("/api/v1/webhooks/subscriptions/:id"),
318
+ () => resolve(overrides.webhooks?.subscriptions?.delete, 204, null)
319
+ ),
320
+ http.post(
321
+ u("/api/v1/webhooks/subscriptions/:id/rotate-secret"),
322
+ () => resolve(
323
+ overrides.webhooks?.subscriptions?.rotateSecret,
324
+ 200,
325
+ responseFixtures.rotatedSecret()
326
+ )
327
+ ),
328
+ http.post(
329
+ u("/api/v1/webhooks/test/:id"),
330
+ () => resolve(overrides.webhooks?.subscriptions?.test, 200, {
331
+ data: {
332
+ enqueued: true,
333
+ event_id: "00000000-0000-0000-0000-000000000fff",
334
+ subscription_id: "00000000-0000-0000-0000-000000000eee"
335
+ },
336
+ meta: { profile_id: "00000000-0000-0000-0000-000000000ccc" }
337
+ })
338
+ ),
339
+ // ----- Webhook deliveries -----
340
+ http.post(
341
+ u("/api/v1/webhooks/deliveries/:id/replay"),
342
+ () => resolve(overrides.webhooks?.deliveries?.replay, 200, {
343
+ data: {
344
+ enqueued: true,
345
+ delivery_id: "00000000-0000-0000-0000-000000000fff",
346
+ event_id: "00000000-0000-0000-0000-000000000fff",
347
+ subscription_id: "00000000-0000-0000-0000-000000000eee"
348
+ },
349
+ meta: { profile_id: "00000000-0000-0000-0000-000000000ccc" }
350
+ })
351
+ ),
352
+ // ----- OAuth -----
353
+ http.post(
354
+ u("/oauth/token"),
355
+ () => resolve(overrides.oauth?.token, 200, responseFixtures.oauthTokenResponse())
356
+ ),
357
+ http.post(
358
+ u("/oauth/revoke"),
359
+ () => overrides.oauth?.revoke ? resolve(overrides.oauth.revoke, 200, null) : new HttpResponse(null, { status: 200 })
360
+ ),
361
+ // ----- Admin -----
362
+ http.post(
363
+ u("/api/v1/admin/oauth-apps"),
364
+ () => resolve(overrides.admin?.create, 201, {
365
+ data: {
366
+ id: "00000000-0000-0000-0000-000000000aaa",
367
+ client_id: "test-app",
368
+ name: "Test App",
369
+ description: null,
370
+ logo_url: null,
371
+ redirect_uris: ["https://example.com/cb"],
372
+ allowed_scopes: ["whatsapp"],
373
+ status: "active",
374
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
375
+ client_secret: "test_client_secret_shown_once",
376
+ jwt_signing_secret: "test_jwt_signing_secret_shown_once"
377
+ }
378
+ })
379
+ ),
380
+ http.patch(
381
+ u("/api/v1/admin/oauth-apps/:id"),
382
+ () => resolve(overrides.admin?.update, 200, {
383
+ data: {
384
+ id: "00000000-0000-0000-0000-000000000aaa",
385
+ client_id: "test-app",
386
+ name: "Updated Name",
387
+ description: null,
388
+ logo_url: null,
389
+ redirect_uris: ["https://example.com/cb"],
390
+ allowed_scopes: ["whatsapp", "instagram"],
391
+ status: "active",
392
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
393
+ }
394
+ })
395
+ ),
396
+ http.delete(
397
+ u("/api/v1/admin/oauth-apps/:id"),
398
+ () => resolve(overrides.admin?.suspend, 200, {
399
+ data: {
400
+ id: "00000000-0000-0000-0000-000000000aaa",
401
+ status: "suspended",
402
+ keys_revoked: 0
403
+ }
404
+ })
405
+ ),
406
+ http.post(
407
+ u("/api/v1/admin/oauth-apps/:id/rotate-client-secret"),
408
+ () => resolve(overrides.admin?.rotateClientSecret, 200, {
409
+ data: {
410
+ oauth_app_id: "00000000-0000-0000-0000-000000000aaa",
411
+ client_secret: "rotated_client_secret",
412
+ grace_days: 7,
413
+ previous_client_secret_expires_at: new Date(
414
+ Date.now() + 7 * 864e5
415
+ ).toISOString()
416
+ }
417
+ })
418
+ ),
419
+ http.post(
420
+ u("/api/v1/admin/oauth-apps/:id/rotate-jwt-secret"),
421
+ () => resolve(overrides.admin?.rotateJwtSecret, 200, {
422
+ data: {
423
+ oauth_app_id: "00000000-0000-0000-0000-000000000aaa",
424
+ jwt_signing_secret: "rotated_jwt_secret",
425
+ grace_days: 7,
426
+ previous_jwt_signing_secret_expires_at: new Date(
427
+ Date.now() + 7 * 864e5
428
+ ).toISOString()
429
+ }
430
+ })
431
+ )
432
+ ];
433
+ }
434
+
435
+ export { atribuMockHandlers, eventFixtures, fixtures, responseFixtures, sampleIds };
436
+ //# sourceMappingURL=index.js.map
437
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/test/fixtures.ts","../../src/test/handlers.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Canned fixtures for testing against the Atribu SDK.\n *\n * Use these to drive your test cases with realistic event shapes, or pass\n * `body` overrides into `atribuMockHandlers({...})` to customize responses.\n */\n\nimport type {\n AtribuWebhookEvent,\n WhatsAppMessageReceivedEvent,\n WhatsAppMessageDeliveryEvent,\n InstagramMessageReceivedEvent,\n InstagramMessageDeliveryEvent,\n} from \"../webhooks/types\";\n\ntype DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;\nfunction deepMerge<T>(base: T, override?: DeepPartial<T>): T {\n if (!override || typeof override !== \"object\") return base;\n const out: Record<string, unknown> = { ...(base as Record<string, unknown>) };\n for (const [k, v] of Object.entries(override as Record<string, unknown>)) {\n const baseVal = (base as Record<string, unknown>)[k];\n if (v && typeof v === \"object\" && !Array.isArray(v) && baseVal && typeof baseVal === \"object\" && !Array.isArray(baseVal)) {\n out[k] = deepMerge(baseVal, v as DeepPartial<typeof baseVal>);\n } else if (v !== undefined) {\n out[k] = v;\n }\n }\n return out as T;\n}\n\nconst SAMPLE_APP_ID = \"00000000-0000-0000-0000-000000000aaa\";\nconst SAMPLE_CONNECTION_ID = \"00000000-0000-0000-0000-000000000bbb\";\nconst SAMPLE_PROFILE_ID = \"00000000-0000-0000-0000-000000000ccc\";\nconst SAMPLE_WORKSPACE_ID = \"00000000-0000-0000-0000-000000000ddd\";\nconst SAMPLE_SUBSCRIPTION_ID = \"00000000-0000-0000-0000-000000000eee\";\n\nexport const sampleIds = {\n appId: SAMPLE_APP_ID,\n connectionId: SAMPLE_CONNECTION_ID,\n profileId: SAMPLE_PROFILE_ID,\n workspaceId: SAMPLE_WORKSPACE_ID,\n subscriptionId: SAMPLE_SUBSCRIPTION_ID,\n} as const;\n\n/** Canned webhook event fixtures — deep-merge any field you care about. */\nexport const eventFixtures = {\n whatsappMessageReceived(\n overrides?: DeepPartial<WhatsAppMessageReceivedEvent>,\n ): WhatsAppMessageReceivedEvent {\n return deepMerge<WhatsAppMessageReceivedEvent>(\n {\n id: \"evt_wa_received_01\",\n type: \"message.received\",\n occurred_at: \"2026-05-15T10:00:00Z\",\n app_id: SAMPLE_APP_ID,\n provider: \"whatsapp\",\n connection_id: SAMPLE_CONNECTION_ID,\n data: {\n wa_message_id: \"wamid.HBgM\",\n from: \"+15551234567\",\n to: \"+15559876543\",\n contact_name: \"Alice\",\n type: \"text\",\n text: \"Hi! Is this still available?\",\n raw: {},\n },\n },\n overrides,\n );\n },\n\n whatsappMessageDelivery(\n overrides?: DeepPartial<WhatsAppMessageDeliveryEvent>,\n ): WhatsAppMessageDeliveryEvent {\n return deepMerge<WhatsAppMessageDeliveryEvent>(\n {\n id: \"evt_wa_delivery_01\",\n type: \"message.delivery\",\n occurred_at: \"2026-05-15T10:00:01Z\",\n app_id: SAMPLE_APP_ID,\n provider: \"whatsapp\",\n connection_id: SAMPLE_CONNECTION_ID,\n data: {\n wa_message_id: \"wamid.HBgM\",\n recipient_id: \"15551234567\",\n status: \"delivered\",\n raw: {},\n },\n },\n overrides,\n );\n },\n\n instagramFbLoginMessage(\n overrides?: DeepPartial<InstagramMessageReceivedEvent>,\n ): InstagramMessageReceivedEvent {\n return deepMerge<InstagramMessageReceivedEvent>(\n {\n id: \"evt_ig_msg_01\",\n type: \"message.received\",\n occurred_at: \"2026-05-15T10:00:00Z\",\n app_id: SAMPLE_APP_ID,\n provider: \"instagram\",\n connection_id: SAMPLE_CONNECTION_ID,\n data: {\n sender_id: \"17841400000000001\",\n recipient_id: \"17841400000000002\",\n mid: \"m_BNvxxxx\",\n text: \"👋\",\n is_echo: false,\n attachments: null,\n referral: null,\n raw: {},\n },\n },\n overrides,\n );\n },\n\n instagramPostback(\n overrides?: DeepPartial<InstagramMessageReceivedEvent>,\n ): InstagramMessageReceivedEvent {\n return deepMerge<InstagramMessageReceivedEvent>(\n {\n id: \"evt_ig_pb_01\",\n type: \"message.received\",\n occurred_at: \"2026-05-15T10:00:00Z\",\n app_id: SAMPLE_APP_ID,\n provider: \"instagram\",\n connection_id: SAMPLE_CONNECTION_ID,\n data: {\n sender_id: \"17841400000000001\",\n recipient_id: \"17841400000000002\",\n kind: \"postback\",\n title: \"Get Started\",\n payload: \"GET_STARTED\",\n raw: {},\n },\n },\n overrides,\n );\n },\n\n instagramIgLoginChange(\n overrides?: DeepPartial<InstagramMessageReceivedEvent>,\n ): InstagramMessageReceivedEvent {\n return deepMerge<InstagramMessageReceivedEvent>(\n {\n id: \"evt_ig_change_01\",\n type: \"message.received\",\n occurred_at: \"2026-05-15T10:00:00Z\",\n app_id: SAMPLE_APP_ID,\n provider: \"instagram\",\n connection_id: SAMPLE_CONNECTION_ID,\n data: {\n sender_id: \"17841400000000001\",\n recipient_id: \"17841400000000002\",\n from_username: \"alice.example\",\n mid: \"m_BNvxxxx\",\n text: \"Hello!\",\n raw: {},\n },\n },\n overrides,\n );\n },\n\n instagramMessageDelivery(\n overrides?: DeepPartial<InstagramMessageDeliveryEvent>,\n ): InstagramMessageDeliveryEvent {\n return deepMerge<InstagramMessageDeliveryEvent>(\n {\n id: \"evt_ig_delivery_01\",\n type: \"message.delivery\",\n occurred_at: \"2026-05-15T10:00:01Z\",\n app_id: SAMPLE_APP_ID,\n provider: \"instagram\",\n connection_id: SAMPLE_CONNECTION_ID,\n data: {\n sender_id: \"17841400000000001\",\n recipient_id: \"17841400000000002\",\n mids: [\"m_BNvxxxx\"],\n watermark: 1715769600000,\n },\n },\n overrides,\n );\n },\n};\n\n/** Canned API response envelopes (matches what the server returns on 200). */\nexport const responseFixtures = {\n messageSent(overrides?: { provider_message_id?: string; sent_at?: string }) {\n return {\n data: {\n connection_id: SAMPLE_CONNECTION_ID,\n channel: \"whatsapp\" as const,\n to: \"+15551234567\",\n provider_message_id: overrides?.provider_message_id ?? \"wamid.HBgM\",\n sent_at: overrides?.sent_at ?? new Date().toISOString(),\n },\n };\n },\n\n commentReply(overrides?: { provider_message_id?: string }) {\n return {\n data: {\n comment_id: \"ig_comment_01\",\n kind: \"private_reply\" as const,\n connection_id: SAMPLE_CONNECTION_ID,\n provider_message_id: overrides?.provider_message_id ?? \"wamid.reply.x\",\n sent_at: new Date().toISOString(),\n },\n meta: { profile_id: SAMPLE_PROFILE_ID },\n };\n },\n\n subscriptionCreated(overrides?: { secret?: string; url?: string }) {\n return {\n data: {\n id: SAMPLE_SUBSCRIPTION_ID,\n app_id: SAMPLE_APP_ID,\n profile_id: SAMPLE_PROFILE_ID,\n url: overrides?.url ?? \"https://example.com/webhook\",\n events: [\"message.received\", \"message.delivery\"],\n providers: [\"whatsapp\", \"instagram\"],\n status: \"active\" as const,\n last_delivery_at: null,\n consecutive_failures: 0,\n previous_secret_expires_at: null,\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n secret: overrides?.secret ?? \"whsec_fixture_test_secret_value\",\n },\n };\n },\n\n subscriptionList(overrides?: { items?: number }) {\n const items = overrides?.items ?? 1;\n return {\n data: Array.from({ length: items }, (_, i) => ({\n id: `00000000-0000-0000-0000-${String(i).padStart(12, \"0\")}`,\n app_id: SAMPLE_APP_ID,\n profile_id: SAMPLE_PROFILE_ID,\n url: `https://example.com/webhook/${i}`,\n events: [\"message.received\", \"message.delivery\"],\n providers: [\"whatsapp\", \"instagram\"],\n status: \"active\" as const,\n last_delivery_at: null,\n consecutive_failures: 0,\n previous_secret_expires_at: null,\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n })),\n meta: { profile_id: SAMPLE_PROFILE_ID },\n };\n },\n\n rotatedSecret(overrides?: { secret?: string; graceDays?: number }) {\n const graceDays = overrides?.graceDays ?? 7;\n return {\n data: {\n subscription_id: SAMPLE_SUBSCRIPTION_ID,\n secret: overrides?.secret ?? \"whsec_rotated_test_secret\",\n grace_days: graceDays,\n previous_secret_expires_at: new Date(\n Date.now() + graceDays * 86400_000,\n ).toISOString(),\n },\n meta: { profile_id: SAMPLE_PROFILE_ID },\n };\n },\n\n oauthTokenResponse(overrides?: { accessToken?: string; scope?: string }) {\n return {\n access_token: overrides?.accessToken ?? \"atb_live_test_fixture_key\",\n token_type: \"bearer\" as const,\n scope: overrides?.scope ?? \"whatsapp\",\n connection_id: SAMPLE_CONNECTION_ID,\n profile_id: SAMPLE_PROFILE_ID,\n workspace_id: SAMPLE_WORKSPACE_ID,\n };\n },\n\n oauthError(error = \"invalid_grant\", description = \"code expired\") {\n return { error, error_description: description };\n },\n\n apiError(code: string, message: string, status: number) {\n return { error: { code, message, status, request_id: \"req_fixture\" } };\n },\n};\n\n/** Combined export so consumers can `import { fixtures } from \"@atribu/sdk/test\"`. */\nexport const fixtures = {\n ids: sampleIds,\n events: eventFixtures,\n responses: responseFixtures,\n};\n\nexport type FixtureWebhookEvent = AtribuWebhookEvent;\n","/**\n * Drop-in MSW v2 handlers for testing against `@atribu/sdk`.\n *\n * Consumers add these to their MSW server setup:\n *\n * import { setupServer } from \"msw/node\";\n * import { atribuMockHandlers } from \"@atribu/sdk/test\";\n *\n * const server = setupServer(...atribuMockHandlers());\n *\n * Per-endpoint defaults reflect the wire shape the real Atribu API returns.\n * Override any response by passing `MockOverrides` — the handler returns\n * the override directly if `body` is provided; otherwise the default\n * envelope from `responseFixtures`.\n *\n * MSW v2 is a peer dependency. Consumers must install it.\n */\n\nimport { http, HttpResponse, type HttpHandler } from \"msw\";\nimport { responseFixtures } from \"./fixtures\";\n\ninterface EndpointOverride {\n status?: number;\n body?: unknown;\n}\n\nexport interface MockOverrides {\n baseUrl?: string;\n messages?: { send?: EndpointOverride };\n comments?: {\n reply?: EndpointOverride;\n privateReply?: EndpointOverride;\n };\n webhooks?: {\n subscriptions?: {\n list?: EndpointOverride;\n create?: EndpointOverride;\n update?: EndpointOverride;\n delete?: EndpointOverride;\n rotateSecret?: EndpointOverride;\n test?: EndpointOverride;\n };\n deliveries?: {\n replay?: EndpointOverride;\n };\n };\n oauth?: {\n token?: EndpointOverride;\n revoke?: EndpointOverride;\n };\n admin?: {\n create?: EndpointOverride;\n update?: EndpointOverride;\n suspend?: EndpointOverride;\n rotateClientSecret?: EndpointOverride;\n rotateJwtSecret?: EndpointOverride;\n };\n}\n\nconst DEFAULT_BASE_URL = \"https://www.atribu.app\";\n\nfunction resolve(\n override: EndpointOverride | undefined,\n fallbackStatus: number,\n fallbackBody: unknown,\n): Response {\n const status = override?.status ?? fallbackStatus;\n const body = override?.body ?? fallbackBody;\n if (status === 204) return new HttpResponse(null, { status: 204 });\n return HttpResponse.json(body as Record<string, unknown> | unknown[] | null, {\n status,\n headers: { \"x-request-id\": \"req_mock\" },\n });\n}\n\nexport function atribuMockHandlers(overrides: MockOverrides = {}): HttpHandler[] {\n const baseUrl = (overrides.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n const u = (p: string): string => `${baseUrl}${p}`;\n\n return [\n // ----- Messaging -----\n http.post(u(\"/api/v1/messages\"), () =>\n resolve(overrides.messages?.send, 200, responseFixtures.messageSent()),\n ),\n\n // ----- IG Comments -----\n http.post(u(\"/api/v1/comments/:commentId/reply\"), () =>\n resolve(overrides.comments?.reply, 200, responseFixtures.commentReply()),\n ),\n http.post(u(\"/api/v1/comments/:commentId/private-reply\"), () =>\n resolve(\n overrides.comments?.privateReply,\n 200,\n responseFixtures.commentReply(),\n ),\n ),\n\n // ----- Webhook subscriptions -----\n http.get(u(\"/api/v1/webhooks/subscriptions\"), () =>\n resolve(\n overrides.webhooks?.subscriptions?.list,\n 200,\n responseFixtures.subscriptionList(),\n ),\n ),\n http.post(u(\"/api/v1/webhooks/subscriptions\"), () =>\n resolve(\n overrides.webhooks?.subscriptions?.create,\n 201,\n responseFixtures.subscriptionCreated(),\n ),\n ),\n http.patch(u(\"/api/v1/webhooks/subscriptions/:id\"), () =>\n resolve(\n overrides.webhooks?.subscriptions?.update,\n 200,\n responseFixtures.subscriptionList({ items: 1 }),\n ),\n ),\n http.delete(u(\"/api/v1/webhooks/subscriptions/:id\"), () =>\n resolve(overrides.webhooks?.subscriptions?.delete, 204, null),\n ),\n http.post(u(\"/api/v1/webhooks/subscriptions/:id/rotate-secret\"), () =>\n resolve(\n overrides.webhooks?.subscriptions?.rotateSecret,\n 200,\n responseFixtures.rotatedSecret(),\n ),\n ),\n http.post(u(\"/api/v1/webhooks/test/:id\"), () =>\n resolve(overrides.webhooks?.subscriptions?.test, 200, {\n data: {\n enqueued: true,\n event_id: \"00000000-0000-0000-0000-000000000fff\",\n subscription_id: \"00000000-0000-0000-0000-000000000eee\",\n },\n meta: { profile_id: \"00000000-0000-0000-0000-000000000ccc\" },\n }),\n ),\n\n // ----- Webhook deliveries -----\n http.post(u(\"/api/v1/webhooks/deliveries/:id/replay\"), () =>\n resolve(overrides.webhooks?.deliveries?.replay, 200, {\n data: {\n enqueued: true,\n delivery_id: \"00000000-0000-0000-0000-000000000fff\",\n event_id: \"00000000-0000-0000-0000-000000000fff\",\n subscription_id: \"00000000-0000-0000-0000-000000000eee\",\n },\n meta: { profile_id: \"00000000-0000-0000-0000-000000000ccc\" },\n }),\n ),\n\n // ----- OAuth -----\n http.post(u(\"/oauth/token\"), () =>\n resolve(overrides.oauth?.token, 200, responseFixtures.oauthTokenResponse()),\n ),\n http.post(u(\"/oauth/revoke\"), () =>\n overrides.oauth?.revoke\n ? resolve(overrides.oauth.revoke, 200, null)\n : new HttpResponse(null, { status: 200 }),\n ),\n\n // ----- Admin -----\n http.post(u(\"/api/v1/admin/oauth-apps\"), () =>\n resolve(overrides.admin?.create, 201, {\n data: {\n id: \"00000000-0000-0000-0000-000000000aaa\",\n client_id: \"test-app\",\n name: \"Test App\",\n description: null,\n logo_url: null,\n redirect_uris: [\"https://example.com/cb\"],\n allowed_scopes: [\"whatsapp\"],\n status: \"active\",\n created_at: new Date().toISOString(),\n client_secret: \"test_client_secret_shown_once\",\n jwt_signing_secret: \"test_jwt_signing_secret_shown_once\",\n },\n }),\n ),\n http.patch(u(\"/api/v1/admin/oauth-apps/:id\"), () =>\n resolve(overrides.admin?.update, 200, {\n data: {\n id: \"00000000-0000-0000-0000-000000000aaa\",\n client_id: \"test-app\",\n name: \"Updated Name\",\n description: null,\n logo_url: null,\n redirect_uris: [\"https://example.com/cb\"],\n allowed_scopes: [\"whatsapp\", \"instagram\"],\n status: \"active\",\n created_at: new Date().toISOString(),\n },\n }),\n ),\n http.delete(u(\"/api/v1/admin/oauth-apps/:id\"), () =>\n resolve(overrides.admin?.suspend, 200, {\n data: {\n id: \"00000000-0000-0000-0000-000000000aaa\",\n status: \"suspended\",\n keys_revoked: 0,\n },\n }),\n ),\n http.post(u(\"/api/v1/admin/oauth-apps/:id/rotate-client-secret\"), () =>\n resolve(overrides.admin?.rotateClientSecret, 200, {\n data: {\n oauth_app_id: \"00000000-0000-0000-0000-000000000aaa\",\n client_secret: \"rotated_client_secret\",\n grace_days: 7,\n previous_client_secret_expires_at: new Date(\n Date.now() + 7 * 86400_000,\n ).toISOString(),\n },\n }),\n ),\n http.post(u(\"/api/v1/admin/oauth-apps/:id/rotate-jwt-secret\"), () =>\n resolve(overrides.admin?.rotateJwtSecret, 200, {\n data: {\n oauth_app_id: \"00000000-0000-0000-0000-000000000aaa\",\n jwt_signing_secret: \"rotated_jwt_secret\",\n grace_days: 7,\n previous_jwt_signing_secret_expires_at: new Date(\n Date.now() + 7 * 86400_000,\n ).toISOString(),\n },\n }),\n ),\n ];\n}\n"]}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Typed webhook event union. Mirrors `src/lib/webhooks/fan-out.ts` and the
3
+ * fan-out call sites in the WA + IG webhook processors.
4
+ *
5
+ * Wire format is snake_case to match the server. Consumers that prefer
6
+ * camelCase can convert in their handler.
7
+ */
8
+ type WebhookProvider = "whatsapp" | "instagram";
9
+ type WebhookEventType = "message.received" | "message.delivery" | "conversation.started";
10
+ interface BaseEvent {
11
+ /** Stable event id for de-dup (also surfaced in `X-Atribu-Delivery-Id`). */
12
+ id: string;
13
+ /** ISO 8601 timestamp of the underlying provider event. */
14
+ occurred_at: string;
15
+ /** The consumer OAuth app id (`oauth_apps.id`). */
16
+ app_id: string;
17
+ /** The `data_connections.id` the event came from. */
18
+ connection_id: string;
19
+ }
20
+ interface WhatsAppMessageReceivedEvent extends BaseEvent {
21
+ type: "message.received";
22
+ provider: "whatsapp";
23
+ data: {
24
+ wa_message_id: string;
25
+ from: string;
26
+ to: string | null;
27
+ contact_name: string | null;
28
+ /** Meta's message type (text, image, video, audio, document, button, interactive, …). */
29
+ type: string;
30
+ text: string | null;
31
+ /** Full Meta message envelope. */
32
+ raw: Record<string, unknown>;
33
+ };
34
+ }
35
+ interface WhatsAppMessageDeliveryEvent extends BaseEvent {
36
+ type: "message.delivery";
37
+ provider: "whatsapp";
38
+ data: {
39
+ wa_message_id: string;
40
+ recipient_id: string;
41
+ status: "sent" | "delivered" | "read" | "failed" | (string & {});
42
+ raw: Record<string, unknown>;
43
+ };
44
+ }
45
+ interface InstagramFbLoginMessageData {
46
+ sender_id: string;
47
+ recipient_id: string;
48
+ mid: string;
49
+ text: string | null;
50
+ is_echo: boolean;
51
+ attachments: unknown[] | null;
52
+ referral: unknown | null;
53
+ raw: Record<string, unknown>;
54
+ }
55
+ interface InstagramFbLoginPostbackData {
56
+ sender_id: string;
57
+ recipient_id: string;
58
+ kind: "postback";
59
+ title: string | null;
60
+ payload: string | null;
61
+ raw: Record<string, unknown>;
62
+ }
63
+ interface InstagramIgLoginChangeData {
64
+ sender_id: string | null;
65
+ recipient_id: string | null;
66
+ from_username: string | null;
67
+ mid: string | null;
68
+ text: string | null;
69
+ raw: Record<string, unknown>;
70
+ }
71
+ /**
72
+ * IG `message.received` has three shapes (fb_login message, fb_login
73
+ * postback, ig_login change). Narrow on `"kind" in data` for postback,
74
+ * `"is_echo" in data` for fb_login message, otherwise it's an ig_login
75
+ * change.
76
+ */
77
+ type InstagramMessageReceivedData = InstagramFbLoginMessageData | InstagramFbLoginPostbackData | InstagramIgLoginChangeData;
78
+ interface InstagramMessageReceivedEvent extends BaseEvent {
79
+ type: "message.received";
80
+ provider: "instagram";
81
+ data: InstagramMessageReceivedData;
82
+ }
83
+ interface InstagramMessageDeliveryEvent extends BaseEvent {
84
+ type: "message.delivery";
85
+ provider: "instagram";
86
+ data: {
87
+ sender_id: string;
88
+ recipient_id: string;
89
+ mids: string[];
90
+ watermark: number;
91
+ };
92
+ }
93
+ /** Reserved — the server doesn't emit this today but the type is in the union. */
94
+ interface ConversationStartedEvent extends BaseEvent {
95
+ type: "conversation.started";
96
+ provider: WebhookProvider;
97
+ data: Record<string, unknown>;
98
+ }
99
+ type AtribuWebhookEvent = WhatsAppMessageReceivedEvent | WhatsAppMessageDeliveryEvent | InstagramMessageReceivedEvent | InstagramMessageDeliveryEvent | ConversationStartedEvent;
100
+
101
+ export type { AtribuWebhookEvent as A, ConversationStartedEvent as C, InstagramFbLoginMessageData as I, WebhookEventType as W, InstagramFbLoginPostbackData as a, InstagramIgLoginChangeData as b, InstagramMessageDeliveryEvent as c, InstagramMessageReceivedData as d, InstagramMessageReceivedEvent as e, WebhookProvider as f, WhatsAppMessageDeliveryEvent as g, WhatsAppMessageReceivedEvent as h };