@tangle-network/agent-integrations 0.39.0 → 0.40.1

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.
@@ -404,7 +404,7 @@ interface RestConnectorSpec {
404
404
  credentialPlacement?: RestCredentialPlacement;
405
405
  defaultHeaders?: Record<string, string>;
406
406
  capabilities: RestOperationSpec[];
407
- test?: RestRequestSpec;
407
+ test?: RestTestSpec;
408
408
  }
409
409
  interface RestOperationSpec {
410
410
  name: string;
@@ -423,6 +423,13 @@ interface RestRequestSpec {
423
423
  headers?: Record<string, string>;
424
424
  body?: 'args' | string | Record<string, unknown>;
425
425
  }
426
+ interface RestTestSpec extends RestRequestSpec {
427
+ expectResponse?: RestResponseExpectation | RestResponseExpectation[];
428
+ }
429
+ interface RestResponseExpectation {
430
+ path: string;
431
+ equals: unknown;
432
+ }
426
433
  declare function declarativeRestConnector(spec: RestConnectorSpec): ConnectorAdapter;
427
434
 
428
435
  /**
@@ -488,7 +488,7 @@ import {
488
488
  zohoCrmConnector,
489
489
  zohoDeskConnector,
490
490
  zuoraConnector
491
- } from "../../chunk-6MUBOVI4.js";
491
+ } from "../../chunk-JXXPAUCX.js";
492
492
  import "../../chunk-2TW2QKGZ.js";
493
493
  import {
494
494
  DEFAULT_TANGLE_PLATFORM_URL,
@@ -495,7 +495,7 @@ import {
495
495
  zohoCrmConnector,
496
496
  zohoDeskConnector,
497
497
  zuoraConnector
498
- } from "../chunk-6MUBOVI4.js";
498
+ } from "../chunk-JXXPAUCX.js";
499
499
  import {
500
500
  DEFAULT_SIGNATURE_TOLERANCE_SECONDS,
501
501
  firstHeader,
package/dist/index.js CHANGED
@@ -119,7 +119,7 @@ import {
119
119
  validateIntegrationManifest,
120
120
  validateProviderPassthroughRequest,
121
121
  verifyCapabilityToken
122
- } from "./chunk-P3RWMLK3.js";
122
+ } from "./chunk-5QU3YCGR.js";
123
123
  import {
124
124
  InMemoryConnectStateStore,
125
125
  finishConnectFlow,
@@ -663,7 +663,7 @@ import {
663
663
  zohoCrmConnector,
664
664
  zohoDeskConnector,
665
665
  zuoraConnector
666
- } from "./chunk-6MUBOVI4.js";
666
+ } from "./chunk-JXXPAUCX.js";
667
667
  import {
668
668
  DEFAULT_SIGNATURE_TOLERANCE_SECONDS,
669
669
  firstHeader,
package/dist/mcp.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  IntegrationError,
3
3
  createCatalogExecutorProvider,
4
4
  importMcpConnector
5
- } from "./chunk-P3RWMLK3.js";
5
+ } from "./chunk-5QU3YCGR.js";
6
6
  import "./chunk-TC5OMANJ.js";
7
7
  import "./chunk-IKCXDQVP.js";
8
8
  import "./chunk-H4XYLS7T.js";
@@ -10,7 +10,7 @@ import "./chunk-FAFQZJPZ.js";
10
10
  import "./chunk-IA5GCKM6.js";
11
11
  import "./chunk-7P2LN4VT.js";
12
12
  import "./chunk-376UBTNB.js";
13
- import "./chunk-6MUBOVI4.js";
13
+ import "./chunk-JXXPAUCX.js";
14
14
  import "./chunk-2TW2QKGZ.js";
15
15
  import "./chunk-OMLQCESJ.js";
16
16
  import "./chunk-PZ5AY32C.js";
package/dist/registry.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  composeIntegrationRegistry,
6
6
  inferIntegrationSupportTier,
7
7
  summarizeIntegrationRegistry
8
- } from "./chunk-P3RWMLK3.js";
8
+ } from "./chunk-5QU3YCGR.js";
9
9
  import "./chunk-TC5OMANJ.js";
10
10
  import "./chunk-IKCXDQVP.js";
11
11
  import "./chunk-H4XYLS7T.js";
@@ -13,7 +13,7 @@ import "./chunk-FAFQZJPZ.js";
13
13
  import "./chunk-IA5GCKM6.js";
14
14
  import "./chunk-7P2LN4VT.js";
15
15
  import "./chunk-376UBTNB.js";
16
- import "./chunk-6MUBOVI4.js";
16
+ import "./chunk-JXXPAUCX.js";
17
17
  import "./chunk-2TW2QKGZ.js";
18
18
  import "./chunk-OMLQCESJ.js";
19
19
  import "./chunk-PZ5AY32C.js";
@@ -83,6 +83,16 @@ interface WebhookProvider {
83
83
  headers: WebhookHeaders;
84
84
  now?: number;
85
85
  }): WebhookEnvelope[] | Promise<WebhookEnvelope[]>;
86
+ /** Optional provider-specific 200 ACK the router returns on a verified
87
+ * delivery, in place of the default `{ received, total }` JSON. Dropbox Sign
88
+ * acknowledges a delivery only when the body is exactly
89
+ * 'Hello API Event Received' and retries otherwise; this lets such a
90
+ * provider declare the body/headers it needs. The frozen status contract is
91
+ * unchanged — only the 200 body/headers are customized. */
92
+ successResponse?: {
93
+ body: string;
94
+ headers?: Record<string, string>;
95
+ };
86
96
  }
87
97
  interface WebhookIdempotencyStore {
88
98
  /** Returns true if this providerEventId has been processed already.
package/dist/runtime.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  InMemoryIntegrationGrantStore,
3
3
  IntegrationRuntime,
4
4
  createIntegrationRuntime
5
- } from "./chunk-P3RWMLK3.js";
5
+ } from "./chunk-5QU3YCGR.js";
6
6
  import "./chunk-TC5OMANJ.js";
7
7
  import "./chunk-IKCXDQVP.js";
8
8
  import "./chunk-H4XYLS7T.js";
@@ -10,7 +10,7 @@ import "./chunk-FAFQZJPZ.js";
10
10
  import "./chunk-IA5GCKM6.js";
11
11
  import "./chunk-7P2LN4VT.js";
12
12
  import "./chunk-376UBTNB.js";
13
- import "./chunk-6MUBOVI4.js";
13
+ import "./chunk-JXXPAUCX.js";
14
14
  import "./chunk-2TW2QKGZ.js";
15
15
  import "./chunk-OMLQCESJ.js";
16
16
  import "./chunk-PZ5AY32C.js";
@@ -1,5 +1,5 @@
1
1
  import { I as IntegrationRuntimeError, a as IntegrationUserAction } from '../errors-Bg3_rxnQ.js';
2
- import { a as WebhookEnvelope } from '../router-BncoovUh.js';
2
+ import { a as WebhookEnvelope } from '../router-S0Dadv2x.js';
3
3
 
4
4
  /**
5
5
  * Subscription state machine.
@@ -1,5 +1,5 @@
1
- import { W as WebhookProvider } from '../router-BncoovUh.js';
2
- export { S as SignatureVerification, a as WebhookEnvelope, b as WebhookHeaders, c as WebhookIdempotencyStore, d as WebhookRouter, e as WebhookRouterOptions, f as WebhookRouterRequest, g as WebhookRouterResponse } from '../router-BncoovUh.js';
1
+ import { W as WebhookProvider } from '../router-S0Dadv2x.js';
2
+ export { S as SignatureVerification, a as WebhookEnvelope, b as WebhookHeaders, c as WebhookIdempotencyStore, d as WebhookRouter, e as WebhookRouterOptions, f as WebhookRouterRequest, g as WebhookRouterResponse } from '../router-S0Dadv2x.js';
3
3
 
4
4
  /**
5
5
  * Pre-built `WebhookProvider` implementations for the inbound surfaces
@@ -32,6 +32,17 @@ declare const gmailWebhookProvider: WebhookProvider;
32
32
  * the per-channel token (`X-Goog-Channel-Token`) as the shared secret.
33
33
  * The router compares it constant-time against the resolved secret. */
34
34
  declare const gdriveWebhookProvider: WebhookProvider;
35
+ /** Telegram Bot API webhook provider. Telegram does NOT sign the body — it
36
+ * authenticates by echoing the `secret_token` set at `setWebhook` time in the
37
+ * `X-Telegram-Bot-Api-Secret-Token` header. We compare it constant-time to the
38
+ * per-connection secret. `update_id` is the idempotency anchor. */
39
+ declare const telegramWebhookProvider: WebhookProvider;
40
+ /** Dropbox Sign (HelloSign) webhook provider. There is NO signature header:
41
+ * the body carries `event.event_hash = HMAC_SHA256(apiKey, event_time +
42
+ * event_type)` (hex), so the `secret` here is the account's API key (the
43
+ * caller resolves it from the connection credential). The body is JSON, or a
44
+ * `json=`/multipart `name="json"` form field on older apps. */
45
+ declare const hellosignWebhookProvider: WebhookProvider;
35
46
  /** Generic HMAC provider — for the long-tail webhook source where the
36
47
  * caller has standardised on a single sha256-of-body scheme. Header
37
48
  * `X-Signature` by default; override at provider-build time if needed. */
@@ -45,4 +56,4 @@ declare function genericHmacWebhookProvider(options: {
45
56
  parse?: WebhookProvider['parse'];
46
57
  }): WebhookProvider;
47
58
 
48
- export { WebhookProvider, docusealWebhookProvider, gdriveWebhookProvider, genericHmacWebhookProvider, gmailWebhookProvider, slackWebhookProvider, stripeWebhookProvider };
59
+ export { WebhookProvider, docusealWebhookProvider, gdriveWebhookProvider, genericHmacWebhookProvider, gmailWebhookProvider, hellosignWebhookProvider, slackWebhookProvider, stripeWebhookProvider, telegramWebhookProvider };
@@ -61,6 +61,13 @@ var WebhookRouter = class {
61
61
  queueMicrotask(() => {
62
62
  void this.deliverEach(accepted);
63
63
  });
64
+ if (provider.successResponse) {
65
+ return {
66
+ status: 200,
67
+ body: provider.successResponse.body,
68
+ headers: provider.successResponse.headers
69
+ };
70
+ }
64
71
  return { status: 200, body: { received: accepted.length, total: events.length } };
65
72
  }
66
73
  async deliverEach(events) {
@@ -88,7 +95,7 @@ function errMessage(err) {
88
95
  }
89
96
 
90
97
  // src/webhooks/providers.ts
91
- import { createHmac, timingSafeEqual } from "crypto";
98
+ import { createHash, createHmac, timingSafeEqual } from "crypto";
92
99
  var stripeWebhookProvider = {
93
100
  id: "stripe",
94
101
  verifySignature({ rawBody, headers, secret }) {
@@ -234,6 +241,123 @@ var gdriveWebhookProvider = {
234
241
  }];
235
242
  }
236
243
  };
244
+ var TELEGRAM_UPDATE_KEYS = [
245
+ "message",
246
+ "edited_message",
247
+ "channel_post",
248
+ "edited_channel_post",
249
+ "business_connection",
250
+ "business_message",
251
+ "edited_business_message",
252
+ "deleted_business_messages",
253
+ "message_reaction",
254
+ "message_reaction_count",
255
+ "inline_query",
256
+ "chosen_inline_result",
257
+ "callback_query",
258
+ "shipping_query",
259
+ "pre_checkout_query",
260
+ "purchased_paid_media",
261
+ "poll",
262
+ "poll_answer",
263
+ "my_chat_member",
264
+ "chat_member",
265
+ "chat_join_request",
266
+ "chat_boost",
267
+ "removed_chat_boost"
268
+ ];
269
+ var telegramWebhookProvider = {
270
+ id: "telegram",
271
+ verifySignature({ headers, secret }) {
272
+ const token = firstHeader(headers, "x-telegram-bot-api-secret-token");
273
+ if (!token) return { valid: false, reason: "missing_telegram_secret_token" };
274
+ const a = Buffer.from(token, "utf-8");
275
+ const b = Buffer.from(secret, "utf-8");
276
+ if (a.length !== b.length) return { valid: false, reason: "invalid_secret_token" };
277
+ return timingSafeEqual(a, b) ? { valid: true } : { valid: false, reason: "invalid_secret_token" };
278
+ },
279
+ parse({ rawBody, headers, now }) {
280
+ const evt = safeJson(rawBody);
281
+ if (!evt || typeof evt !== "object") return [];
282
+ const kind = TELEGRAM_UPDATE_KEYS.find((k) => k in evt) ?? "unknown";
283
+ return [{
284
+ provider: "telegram",
285
+ eventType: `telegram.${kind}`,
286
+ providerEventId: typeof evt.update_id === "number" ? String(evt.update_id) : void 0,
287
+ receivedAt: now ?? Date.now(),
288
+ payload: evt,
289
+ headers: normalizeHeaders(headers)
290
+ }];
291
+ }
292
+ };
293
+ var hellosignWebhookProvider = {
294
+ id: "hellosign",
295
+ // Dropbox Sign acknowledges a delivery only when the response body is exactly
296
+ // 'Hello API Event Received'; any other body makes it retry the event.
297
+ successResponse: { body: "Hello API Event Received", headers: { "content-type": "text/plain" } },
298
+ verifySignature({ rawBody, secret }) {
299
+ const body = parseHelloSignBody(rawBody);
300
+ const event = body?.event;
301
+ if (!event || typeof event.event_time !== "string" || typeof event.event_type !== "string" || typeof event.event_hash !== "string") {
302
+ return { valid: false, reason: "missing_event_fields" };
303
+ }
304
+ const expected = createHmac("sha256", secret).update(`${event.event_time}${event.event_type}`).digest("hex");
305
+ const a = Buffer.from(event.event_hash.toLowerCase(), "utf-8");
306
+ const b = Buffer.from(expected.toLowerCase(), "utf-8");
307
+ if (a.length !== b.length) return { valid: false, reason: "invalid_signature" };
308
+ return timingSafeEqual(a, b) ? { valid: true } : { valid: false, reason: "invalid_signature" };
309
+ },
310
+ parse({ rawBody, headers, now }) {
311
+ const body = parseHelloSignBody(rawBody);
312
+ const event = body?.event;
313
+ if (!event) return [];
314
+ return [{
315
+ provider: "hellosign",
316
+ eventType: `hellosign.${event.event_type ?? "unknown"}`,
317
+ // Prefer Dropbox Sign's canonical `event.event_id` (stable across
318
+ // redeliveries, correlates with the provider). Fall back to a digest of
319
+ // the parsed body when it's absent: `event_hash` = HMAC(apiKey,
320
+ // event_time + event_type), so two distinct same-type events in the same
321
+ // second share it — the body digest stays distinct across differing
322
+ // events and identical across redeliveries of one event.
323
+ providerEventId: typeof event.event_id === "string" && event.event_id.length > 0 ? event.event_id : createHash("sha256").update(JSON.stringify(body)).digest("hex"),
324
+ receivedAt: now ?? Date.now(),
325
+ payload: body,
326
+ headers: normalizeHeaders(headers)
327
+ }];
328
+ }
329
+ };
330
+ function parseHelloSignBody(rawBody) {
331
+ try {
332
+ const json = JSON.parse(rawBody);
333
+ if (json && typeof json === "object") return json;
334
+ } catch {
335
+ }
336
+ const formJson = new URLSearchParams(rawBody).get("json");
337
+ if (formJson) {
338
+ try {
339
+ return JSON.parse(formJson);
340
+ } catch {
341
+ return null;
342
+ }
343
+ }
344
+ return extractMultipartJsonField(rawBody);
345
+ }
346
+ function extractMultipartJsonField(rawBody) {
347
+ const nameIdx = rawBody.indexOf('name="json"');
348
+ if (nameIdx === -1) return null;
349
+ const afterName = rawBody.slice(nameIdx);
350
+ const sep = /\r?\n\r?\n/.exec(afterName);
351
+ if (!sep) return null;
352
+ const partBody = afterName.slice(sep.index + sep[0].length);
353
+ const end = /\r?\n--/.exec(partBody);
354
+ if (!end) return null;
355
+ try {
356
+ return JSON.parse(partBody.slice(0, end.index));
357
+ } catch {
358
+ return null;
359
+ }
360
+ }
237
361
  function genericHmacWebhookProvider(options) {
238
362
  const header = options.signatureHeader ?? "x-signature";
239
363
  return {
@@ -280,7 +404,9 @@ export {
280
404
  gdriveWebhookProvider,
281
405
  genericHmacWebhookProvider,
282
406
  gmailWebhookProvider,
407
+ hellosignWebhookProvider,
283
408
  slackWebhookProvider,
284
- stripeWebhookProvider
409
+ stripeWebhookProvider,
410
+ telegramWebhookProvider
285
411
  };
286
412
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/webhooks/router.ts","../../src/webhooks/providers.ts"],"sourcesContent":["/**\n * @stable Provider-agnostic inbound webhook router.\n *\n * Consumer hooks a single HTTP handler at `/webhook/:provider/:event`\n * (or whatever pathing they prefer) and forwards the request through\n * `WebhookRouter.handle()`. The router:\n *\n * 1. Resolves the registered provider entry.\n * 2. Calls the provider's `verifySignature(rawBody, headers, secrets)`.\n * Failure → 401 fast, no downstream work.\n * 3. Calls the provider's `parse(rawBody, headers)` to extract zero or\n * more normalized events.\n * 4. Enqueues each event for async processing via the consumer-supplied\n * `deliver(event)` callback (best-effort fire-and-forget — the\n * router does NOT block the HTTP response on the consumer's work).\n * 5. Returns 200 fast with `{received: events.length}`.\n *\n * Replay protection: providers that sign timestamps (Stripe, Slack)\n * already reject stale signatures inside `verifySignature`. For providers\n * that don't (DocuSeal, GDrive push), the router exposes a pluggable\n * `idempotency` hook: if `idempotency.seen(providerEventId)` returns\n * true, the router 200s without invoking `deliver()`. Consumers wire\n * this to a durable kv (D1 / Redis / Postgres unique-index).\n *\n * Why a router and not a per-provider express app: the runtime contract\n * a product cares about is \"an inbound event came in, here's the\n * normalized envelope\". Verification, parsing, and idempotency-dedup\n * are mechanical and provider-specific — the router owns them. The\n * consumer's `deliver()` is the only place product logic runs.\n *\n * Stability: `@stable` — additions to `WebhookEnvelope` must be\n * additive; the router's HTTP contract (paths, status codes) is frozen\n * at 200 (ok), 400 (bad request), 401 (bad signature), 404 (unknown\n * provider), 405 (provider has no inbound surface).\n */\n\nexport interface WebhookHeaders {\n [name: string]: string | string[] | undefined\n}\n\n/** Normalized inbound event the router emits after parsing. */\nexport interface WebhookEnvelope<TPayload = unknown> {\n /** Provider id (matches the `:provider` path segment). */\n provider: string\n /** Optional event class — e.g., 'customer.subscription.deleted'. The\n * provider's parser decides. Used for routing inside `deliver()`. */\n eventType: string\n /** Provider-emitted event id, when present. Used for the idempotency\n * short-circuit. */\n providerEventId?: string\n /** Wall-clock receive time. */\n receivedAt: number\n /** Provider payload, normalized to the provider's documented event\n * shape. The router does NOT reshape this — `parse()` is the contract. */\n payload: TPayload\n /** Headers passed through for downstream handlers that want them\n * (e.g., to extract custom routing metadata). Always lowercased keys. */\n headers: Record<string, string>\n}\n\nexport type SignatureVerification =\n | { valid: true }\n | { valid: false; reason: string }\n\n/** Per-provider plug-in. Stateless — the router calls `verifySignature`\n * then `parse` on every request. The provider's HTTP-shape concerns\n * (e.g., raw body required) are documented per provider. */\nexport interface WebhookProvider {\n /** Stable provider id (`stripe`, `docuseal`, `gdrive`, ...). */\n id: string\n /** Verify the inbound signature. Receives the EXACT raw body string —\n * consumers MUST preserve raw bytes through their HTTP server (do not\n * parse JSON before forwarding here). */\n verifySignature(input: {\n rawBody: string\n headers: WebhookHeaders\n secret: string\n }): SignatureVerification\n /** Parse the validated raw body into zero or more normalized events.\n * A single push payload may carry multiple events (e.g., Slack bulk\n * delivery). Return [] to ack the push as a no-op. */\n parse(input: {\n rawBody: string\n headers: WebhookHeaders\n now?: number\n }): WebhookEnvelope[] | Promise<WebhookEnvelope[]>\n}\n\nexport interface WebhookIdempotencyStore {\n /** Returns true if this providerEventId has been processed already.\n * Implementations should be O(1) (Redis SETNX, D1 UNIQUE constraint). */\n seen(providerEventId: string): Promise<boolean> | boolean\n /** Marks a providerEventId as processed. Called AFTER `deliver()` has\n * been invoked. */\n remember(providerEventId: string, ttlMs: number): Promise<void> | void\n}\n\nexport interface WebhookRouterOptions {\n /** Provider registry. Pass any number of providers; routing is by id. */\n providers: WebhookProvider[]\n /** Async callback invoked with every accepted event. Fire-and-forget\n * from the router's perspective — the HTTP response is sent before\n * this resolves. Throws are caught and reported via `onError`. */\n deliver(event: WebhookEnvelope): Promise<void> | void\n /** Resolve the signing secret for a provider id at request time. The\n * router never holds secrets — the consumer's vault resolves them. */\n resolveSecret(providerId: string, headers: WebhookHeaders): Promise<string | null> | string | null\n /** Optional idempotency-dedup hook. Required for providers that don't\n * sign timestamps in their signature scheme (DocuSeal, Drive push). */\n idempotency?: WebhookIdempotencyStore\n /** TTL on idempotency entries. Default 7 days — long enough that a\n * provider's normal retry-window can't re-deliver. */\n idempotencyTtlMs?: number\n /** Surface delivery errors. Default: console.error. */\n onError?(err: unknown, context: { provider: string; eventType?: string; providerEventId?: string }): void\n /** Override `now()` for tests. */\n now?(): number\n}\n\nexport interface WebhookRouterRequest {\n providerId: string\n rawBody: string\n headers: WebhookHeaders\n}\n\nexport interface WebhookRouterResponse {\n status: number\n body: unknown\n headers?: Record<string, string>\n}\n\n/**\n * Router instance. Stateless aside from the provider registry — safe to\n * share across requests; build once per process.\n */\nexport class WebhookRouter {\n private readonly providers: Map<string, WebhookProvider>\n private readonly deliver: WebhookRouterOptions['deliver']\n private readonly resolveSecret: WebhookRouterOptions['resolveSecret']\n private readonly idempotency?: WebhookIdempotencyStore\n private readonly idempotencyTtlMs: number\n private readonly onError: NonNullable<WebhookRouterOptions['onError']>\n private readonly nowFn: () => number\n\n constructor(opts: WebhookRouterOptions) {\n this.providers = new Map(opts.providers.map((p) => [p.id, p]))\n this.deliver = opts.deliver\n this.resolveSecret = opts.resolveSecret\n this.idempotency = opts.idempotency\n this.idempotencyTtlMs = opts.idempotencyTtlMs ?? 7 * 24 * 60 * 60 * 1000\n this.onError = opts.onError ?? defaultOnError\n this.nowFn = opts.now ?? Date.now\n }\n\n /** Process one inbound webhook request. Pure with respect to side-\n * effects on the router instance — safe to call concurrently. */\n async handle(request: WebhookRouterRequest): Promise<WebhookRouterResponse> {\n const provider = this.providers.get(request.providerId)\n if (!provider) {\n return { status: 404, body: { error: 'unknown_provider', provider: request.providerId } }\n }\n const secret = await this.resolveSecret(provider.id, request.headers)\n if (!secret) {\n return { status: 401, body: { error: 'missing_secret', provider: provider.id } }\n }\n const verification = provider.verifySignature({\n rawBody: request.rawBody,\n headers: request.headers,\n secret,\n })\n if (!verification.valid) {\n return { status: 401, body: { error: 'invalid_signature', reason: verification.reason } }\n }\n\n let events: WebhookEnvelope[]\n try {\n events = await provider.parse({ rawBody: request.rawBody, headers: request.headers, now: this.nowFn() })\n } catch (err) {\n this.onError(err, { provider: provider.id })\n return { status: 400, body: { error: 'parse_error', message: errMessage(err) } }\n }\n\n const accepted: WebhookEnvelope[] = []\n for (const event of events) {\n if (event.providerEventId && this.idempotency) {\n const already = await this.idempotency.seen(event.providerEventId)\n if (already) continue\n }\n accepted.push(event)\n }\n\n // Deliver async — do NOT block the HTTP response. Errors land in\n // `onError`; the provider already got its 200 by then so it will\n // not retry.\n queueMicrotask(() => {\n void this.deliverEach(accepted)\n })\n\n return { status: 200, body: { received: accepted.length, total: events.length } }\n }\n\n private async deliverEach(events: WebhookEnvelope[]): Promise<void> {\n for (const event of events) {\n try {\n await this.deliver(event)\n if (event.providerEventId && this.idempotency) {\n await this.idempotency.remember(event.providerEventId, this.idempotencyTtlMs)\n }\n } catch (err) {\n this.onError(err, {\n provider: event.provider,\n eventType: event.eventType,\n providerEventId: event.providerEventId,\n })\n }\n }\n }\n}\n\nfunction defaultOnError(err: unknown, context: { provider: string; eventType?: string; providerEventId?: string }): void {\n // eslint-disable-next-line no-console\n console.error('[WebhookRouter]', context, err)\n}\n\nfunction errMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n","/**\n * Pre-built `WebhookProvider` implementations for the inbound surfaces\n * the substrate ships first-party verifiers for.\n *\n * Each provider implementation is intentionally thin: it delegates\n * signature verification to the corresponding pure function in\n * `connectors/webhooks.ts` and parses the body into one or more\n * normalized `WebhookEnvelope` rows. Anything provider-specific that\n * doesn't fit cleanly (Slack URL-verification handshake, etc.) is\n * surfaced via the envelope `eventType` so the consumer's `deliver()`\n * can branch.\n */\n\nimport {\n firstHeader,\n verifyHmacSignature,\n verifySlackSignature,\n verifyStripeSignature,\n} from '../connectors/webhooks.js'\nimport type { WebhookEnvelope, WebhookHeaders, WebhookProvider, SignatureVerification } from './router.js'\nimport { createHmac, timingSafeEqual } from 'node:crypto'\n\n/** Stripe webhook provider. Signature header `Stripe-Signature`. */\nexport const stripeWebhookProvider: WebhookProvider = {\n id: 'stripe',\n verifySignature({ rawBody, headers, secret }): SignatureVerification {\n const sig = firstHeader(headers, 'stripe-signature')\n if (!sig) return { valid: false, reason: 'missing_stripe_signature' }\n return verifyStripeSignature(rawBody, sig, secret)\n ? { valid: true }\n : { valid: false, reason: 'invalid_signature' }\n },\n parse({ rawBody, headers, now }): WebhookEnvelope[] {\n const evt = safeJson(rawBody)\n if (!evt || typeof evt !== 'object') return []\n const e = evt as { id?: unknown; type?: unknown }\n return [\n {\n provider: 'stripe',\n eventType: typeof e.type === 'string' ? e.type : 'stripe.unknown',\n providerEventId: typeof e.id === 'string' ? e.id : undefined,\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n },\n ]\n },\n}\n\n/** Slack Events API provider. Handles the `url_verification` handshake\n * by emitting a synthetic event the consumer's `deliver()` can echo. */\nexport const slackWebhookProvider: WebhookProvider = {\n id: 'slack',\n verifySignature({ rawBody, headers, secret }): SignatureVerification {\n const sig = firstHeader(headers, 'x-slack-signature')\n const ts = firstHeader(headers, 'x-slack-request-timestamp')\n if (!sig || !ts) return { valid: false, reason: 'missing_slack_signature_or_timestamp' }\n return verifySlackSignature(rawBody, sig, ts, secret)\n ? { valid: true }\n : { valid: false, reason: 'invalid_signature' }\n },\n parse({ rawBody, headers, now }): WebhookEnvelope[] {\n const evt = safeJson(rawBody) as { type?: string; event_id?: string; event?: { type?: string } } | null\n if (!evt || typeof evt !== 'object') return []\n if (evt.type === 'url_verification') {\n return [{\n provider: 'slack',\n eventType: 'slack.url_verification',\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n }]\n }\n const eventType = `slack.${evt.event?.type ?? evt.type ?? 'unknown'}`\n return [{\n provider: 'slack',\n eventType,\n providerEventId: typeof evt.event_id === 'string' ? evt.event_id : undefined,\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n }]\n },\n}\n\n/** DocuSeal webhook provider. Signature header `X-Docuseal-Signature`. */\nexport const docusealWebhookProvider: WebhookProvider = {\n id: 'docuseal',\n verifySignature({ rawBody, headers, secret }): SignatureVerification {\n const sig = firstHeader(headers, 'x-docuseal-signature')\n if (!sig) return { valid: false, reason: 'missing_docuseal_signature' }\n const expected = createHmac('sha256', secret).update(rawBody).digest('hex')\n const a = Buffer.from(sig.toLowerCase(), 'utf-8')\n const b = Buffer.from(expected, 'utf-8')\n if (a.length !== b.length) return { valid: false, reason: 'invalid_signature' }\n return timingSafeEqual(a, b) ? { valid: true } : { valid: false, reason: 'invalid_signature' }\n },\n parse({ rawBody, headers, now }): WebhookEnvelope[] {\n const evt = safeJson(rawBody) as { event_type?: string; event_id?: string } | null\n if (!evt || typeof evt !== 'object') return []\n return [{\n provider: 'docuseal',\n eventType: `docuseal.${evt.event_type ?? 'unknown'}`,\n providerEventId: typeof evt.event_id === 'string' ? evt.event_id : undefined,\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n }]\n },\n}\n\n/** Gmail push provider. Cloud Pub/Sub posts a JWT-signed envelope; the\n * *payload* is base64 JSON describing the changed history range. The\n * signature scheme here is the Pub/Sub JWT auth header — when supplied,\n * consumers SHOULD verify the JWT against Google's well-known\n * certificates. We accept the simpler \"Bearer <pubsub-shared-secret>\"\n * variant by default (matching `verifyHmacSignature`). */\nexport const gmailWebhookProvider: WebhookProvider = {\n id: 'gmail',\n verifySignature({ headers, secret }): SignatureVerification {\n const auth = firstHeader(headers, 'authorization')\n if (!auth) return { valid: false, reason: 'missing_authorization' }\n // Accept either \"Bearer <secret>\" or \"Token <secret>\" formats. This\n // is the simple per-tenant shared-secret path; JWT verification is\n // left to the consumer's `deliver()` when they need full Google JWT\n // chain validation.\n const m = /^(?:Bearer|Token)\\s+(.+)$/i.exec(auth)\n if (!m) return { valid: false, reason: 'invalid_authorization_format' }\n const a = Buffer.from(m[1]!, 'utf-8')\n const b = Buffer.from(secret, 'utf-8')\n if (a.length !== b.length) return { valid: false, reason: 'invalid_signature' }\n return timingSafeEqual(a, b) ? { valid: true } : { valid: false, reason: 'invalid_signature' }\n },\n parse({ rawBody, headers, now }): WebhookEnvelope[] {\n const envelope = safeJson(rawBody) as { message?: { data?: string; messageId?: string; publishTime?: string } } | null\n if (!envelope?.message?.data) return []\n let payload: unknown\n try {\n payload = JSON.parse(Buffer.from(envelope.message.data, 'base64').toString('utf-8'))\n } catch {\n return []\n }\n const inner = payload as { historyId?: number | string; emailAddress?: string }\n return [{\n provider: 'gmail',\n eventType: 'gmail.history_changed',\n providerEventId: envelope.message.messageId,\n receivedAt: now ?? Date.now(),\n payload: { ...inner, publishTime: envelope.message.publishTime },\n headers: normalizeHeaders(headers),\n }]\n },\n}\n\n/** Google Drive push provider. Drive does NOT sign the body — it uses\n * the per-channel token (`X-Goog-Channel-Token`) as the shared secret.\n * The router compares it constant-time against the resolved secret. */\nexport const gdriveWebhookProvider: WebhookProvider = {\n id: 'gdrive',\n verifySignature({ headers, secret }): SignatureVerification {\n const token = firstHeader(headers, 'x-goog-channel-token')\n if (!token) return { valid: false, reason: 'missing_channel_token' }\n const a = Buffer.from(token, 'utf-8')\n const b = Buffer.from(secret, 'utf-8')\n if (a.length !== b.length) return { valid: false, reason: 'invalid_signature' }\n return timingSafeEqual(a, b) ? { valid: true } : { valid: false, reason: 'invalid_signature' }\n },\n parse({ headers, now }): WebhookEnvelope[] {\n const resourceId = firstHeader(headers, 'x-goog-resource-id')\n const resourceState = firstHeader(headers, 'x-goog-resource-state') ?? 'unknown'\n const channelId = firstHeader(headers, 'x-goog-channel-id')\n const messageNumber = firstHeader(headers, 'x-goog-message-number')\n if (resourceState === 'sync') {\n return [{\n provider: 'gdrive',\n eventType: 'gdrive.channel.sync',\n providerEventId: messageNumber ? `${channelId}-${messageNumber}` : undefined,\n receivedAt: now ?? Date.now(),\n payload: { channelId, resourceId, resourceState },\n headers: normalizeHeaders(headers),\n }]\n }\n return [{\n provider: 'gdrive',\n eventType: `gdrive.resource.${resourceState}`,\n providerEventId: messageNumber ? `${channelId}-${messageNumber}` : undefined,\n receivedAt: now ?? Date.now(),\n payload: { channelId, resourceId, resourceState },\n headers: normalizeHeaders(headers),\n }]\n },\n}\n\n/** Generic HMAC provider — for the long-tail webhook source where the\n * caller has standardised on a single sha256-of-body scheme. Header\n * `X-Signature` by default; override at provider-build time if needed. */\nexport function genericHmacWebhookProvider(options: {\n id: string\n signatureHeader?: string\n algorithm?: 'sha256' | 'sha1' | 'sha512'\n signaturePrefix?: string\n /** Parser to convert the raw body into envelopes. Defaults to\n * \"one event with eventType=<provider>.event and payload=JSON\". */\n parse?: WebhookProvider['parse']\n}): WebhookProvider {\n const header = options.signatureHeader ?? 'x-signature'\n return {\n id: options.id,\n verifySignature({ rawBody, headers, secret }) {\n const sig = firstHeader(headers, header)\n if (!sig) return { valid: false, reason: `missing_${header}` }\n return verifyHmacSignature(rawBody, sig, secret, {\n algorithm: options.algorithm ?? 'sha256',\n signaturePrefix: options.signaturePrefix,\n })\n ? { valid: true }\n : { valid: false, reason: 'invalid_signature' }\n },\n parse:\n options.parse ??\n (({ rawBody, headers, now }) => {\n const evt = safeJson(rawBody) ?? rawBody\n return [{\n provider: options.id,\n eventType: `${options.id}.event`,\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n }]\n }),\n }\n}\n\nfunction safeJson(s: string): unknown {\n try {\n return JSON.parse(s)\n } catch {\n return null\n }\n}\n\nfunction normalizeHeaders(headers: WebhookHeaders): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries(headers)) {\n if (v === undefined) continue\n const value = Array.isArray(v) ? v[0] : v\n if (typeof value === 'string') out[k.toLowerCase()] = value\n }\n return out\n}\n"],"mappings":";;;;;;;;;AAuIO,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,MAA4B;AACtC,SAAK,YAAY,IAAI,IAAI,KAAK,UAAU,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAC7D,SAAK,UAAU,KAAK;AACpB,SAAK,gBAAgB,KAAK;AAC1B,SAAK,cAAc,KAAK;AACxB,SAAK,mBAAmB,KAAK,oBAAoB,IAAI,KAAK,KAAK,KAAK;AACpE,SAAK,UAAU,KAAK,WAAW;AAC/B,SAAK,QAAQ,KAAK,OAAO,KAAK;AAAA,EAChC;AAAA;AAAA;AAAA,EAIA,MAAM,OAAO,SAA+D;AAC1E,UAAM,WAAW,KAAK,UAAU,IAAI,QAAQ,UAAU;AACtD,QAAI,CAAC,UAAU;AACb,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,oBAAoB,UAAU,QAAQ,WAAW,EAAE;AAAA,IAC1F;AACA,UAAM,SAAS,MAAM,KAAK,cAAc,SAAS,IAAI,QAAQ,OAAO;AACpE,QAAI,CAAC,QAAQ;AACX,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,kBAAkB,UAAU,SAAS,GAAG,EAAE;AAAA,IACjF;AACA,UAAM,eAAe,SAAS,gBAAgB;AAAA,MAC5C,SAAS,QAAQ;AAAA,MACjB,SAAS,QAAQ;AAAA,MACjB;AAAA,IACF,CAAC;AACD,QAAI,CAAC,aAAa,OAAO;AACvB,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,qBAAqB,QAAQ,aAAa,OAAO,EAAE;AAAA,IAC1F;AAEA,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,SAAS,MAAM,EAAE,SAAS,QAAQ,SAAS,SAAS,QAAQ,SAAS,KAAK,KAAK,MAAM,EAAE,CAAC;AAAA,IACzG,SAAS,KAAK;AACZ,WAAK,QAAQ,KAAK,EAAE,UAAU,SAAS,GAAG,CAAC;AAC3C,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,eAAe,SAAS,WAAW,GAAG,EAAE,EAAE;AAAA,IACjF;AAEA,UAAM,WAA8B,CAAC;AACrC,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,mBAAmB,KAAK,aAAa;AAC7C,cAAM,UAAU,MAAM,KAAK,YAAY,KAAK,MAAM,eAAe;AACjE,YAAI,QAAS;AAAA,MACf;AACA,eAAS,KAAK,KAAK;AAAA,IACrB;AAKA,mBAAe,MAAM;AACnB,WAAK,KAAK,YAAY,QAAQ;AAAA,IAChC,CAAC;AAED,WAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,UAAU,SAAS,QAAQ,OAAO,OAAO,OAAO,EAAE;AAAA,EAClF;AAAA,EAEA,MAAc,YAAY,QAA0C;AAClE,eAAW,SAAS,QAAQ;AAC1B,UAAI;AACF,cAAM,KAAK,QAAQ,KAAK;AACxB,YAAI,MAAM,mBAAmB,KAAK,aAAa;AAC7C,gBAAM,KAAK,YAAY,SAAS,MAAM,iBAAiB,KAAK,gBAAgB;AAAA,QAC9E;AAAA,MACF,SAAS,KAAK;AACZ,aAAK,QAAQ,KAAK;AAAA,UAChB,UAAU,MAAM;AAAA,UAChB,WAAW,MAAM;AAAA,UACjB,iBAAiB,MAAM;AAAA,QACzB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,eAAe,KAAc,SAAmF;AAEvH,UAAQ,MAAM,mBAAmB,SAAS,GAAG;AAC/C;AAEA,SAAS,WAAW,KAAsB;AACxC,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;;;AC9MA,SAAS,YAAY,uBAAuB;AAGrC,IAAM,wBAAyC;AAAA,EACpD,IAAI;AAAA,EACJ,gBAAgB,EAAE,SAAS,SAAS,OAAO,GAA0B;AACnE,UAAM,MAAM,YAAY,SAAS,kBAAkB;AACnD,QAAI,CAAC,IAAK,QAAO,EAAE,OAAO,OAAO,QAAQ,2BAA2B;AACpE,WAAO,sBAAsB,SAAS,KAAK,MAAM,IAC7C,EAAE,OAAO,KAAK,IACd,EAAE,OAAO,OAAO,QAAQ,oBAAoB;AAAA,EAClD;AAAA,EACA,MAAM,EAAE,SAAS,SAAS,IAAI,GAAsB;AAClD,UAAM,MAAM,SAAS,OAAO;AAC5B,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,CAAC;AAC7C,UAAM,IAAI;AACV,WAAO;AAAA,MACL;AAAA,QACE,UAAU;AAAA,QACV,WAAW,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;AAAA,QACjD,iBAAiB,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK;AAAA,QACnD,YAAY,OAAO,KAAK,IAAI;AAAA,QAC5B,SAAS;AAAA,QACT,SAAS,iBAAiB,OAAO;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AACF;AAIO,IAAM,uBAAwC;AAAA,EACnD,IAAI;AAAA,EACJ,gBAAgB,EAAE,SAAS,SAAS,OAAO,GAA0B;AACnE,UAAM,MAAM,YAAY,SAAS,mBAAmB;AACpD,UAAM,KAAK,YAAY,SAAS,2BAA2B;AAC3D,QAAI,CAAC,OAAO,CAAC,GAAI,QAAO,EAAE,OAAO,OAAO,QAAQ,uCAAuC;AACvF,WAAO,qBAAqB,SAAS,KAAK,IAAI,MAAM,IAChD,EAAE,OAAO,KAAK,IACd,EAAE,OAAO,OAAO,QAAQ,oBAAoB;AAAA,EAClD;AAAA,EACA,MAAM,EAAE,SAAS,SAAS,IAAI,GAAsB;AAClD,UAAM,MAAM,SAAS,OAAO;AAC5B,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,CAAC;AAC7C,QAAI,IAAI,SAAS,oBAAoB;AACnC,aAAO,CAAC;AAAA,QACN,UAAU;AAAA,QACV,WAAW;AAAA,QACX,YAAY,OAAO,KAAK,IAAI;AAAA,QAC5B,SAAS;AAAA,QACT,SAAS,iBAAiB,OAAO;AAAA,MACnC,CAAC;AAAA,IACH;AACA,UAAM,YAAY,SAAS,IAAI,OAAO,QAAQ,IAAI,QAAQ,SAAS;AACnE,WAAO,CAAC;AAAA,MACN,UAAU;AAAA,MACV;AAAA,MACA,iBAAiB,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;AAAA,MACnE,YAAY,OAAO,KAAK,IAAI;AAAA,MAC5B,SAAS;AAAA,MACT,SAAS,iBAAiB,OAAO;AAAA,IACnC,CAAC;AAAA,EACH;AACF;AAGO,IAAM,0BAA2C;AAAA,EACtD,IAAI;AAAA,EACJ,gBAAgB,EAAE,SAAS,SAAS,OAAO,GAA0B;AACnE,UAAM,MAAM,YAAY,SAAS,sBAAsB;AACvD,QAAI,CAAC,IAAK,QAAO,EAAE,OAAO,OAAO,QAAQ,6BAA6B;AACtE,UAAM,WAAW,WAAW,UAAU,MAAM,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC1E,UAAM,IAAI,OAAO,KAAK,IAAI,YAAY,GAAG,OAAO;AAChD,UAAM,IAAI,OAAO,KAAK,UAAU,OAAO;AACvC,QAAI,EAAE,WAAW,EAAE,OAAQ,QAAO,EAAE,OAAO,OAAO,QAAQ,oBAAoB;AAC9E,WAAO,gBAAgB,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,OAAO,QAAQ,oBAAoB;AAAA,EAC/F;AAAA,EACA,MAAM,EAAE,SAAS,SAAS,IAAI,GAAsB;AAClD,UAAM,MAAM,SAAS,OAAO;AAC5B,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,CAAC;AAC7C,WAAO,CAAC;AAAA,MACN,UAAU;AAAA,MACV,WAAW,YAAY,IAAI,cAAc,SAAS;AAAA,MAClD,iBAAiB,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;AAAA,MACnE,YAAY,OAAO,KAAK,IAAI;AAAA,MAC5B,SAAS;AAAA,MACT,SAAS,iBAAiB,OAAO;AAAA,IACnC,CAAC;AAAA,EACH;AACF;AAQO,IAAM,uBAAwC;AAAA,EACnD,IAAI;AAAA,EACJ,gBAAgB,EAAE,SAAS,OAAO,GAA0B;AAC1D,UAAM,OAAO,YAAY,SAAS,eAAe;AACjD,QAAI,CAAC,KAAM,QAAO,EAAE,OAAO,OAAO,QAAQ,wBAAwB;AAKlE,UAAM,IAAI,6BAA6B,KAAK,IAAI;AAChD,QAAI,CAAC,EAAG,QAAO,EAAE,OAAO,OAAO,QAAQ,+BAA+B;AACtE,UAAM,IAAI,OAAO,KAAK,EAAE,CAAC,GAAI,OAAO;AACpC,UAAM,IAAI,OAAO,KAAK,QAAQ,OAAO;AACrC,QAAI,EAAE,WAAW,EAAE,OAAQ,QAAO,EAAE,OAAO,OAAO,QAAQ,oBAAoB;AAC9E,WAAO,gBAAgB,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,OAAO,QAAQ,oBAAoB;AAAA,EAC/F;AAAA,EACA,MAAM,EAAE,SAAS,SAAS,IAAI,GAAsB;AAClD,UAAM,WAAW,SAAS,OAAO;AACjC,QAAI,CAAC,UAAU,SAAS,KAAM,QAAO,CAAC;AACtC,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,OAAO,KAAK,SAAS,QAAQ,MAAM,QAAQ,EAAE,SAAS,OAAO,CAAC;AAAA,IACrF,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AACA,UAAM,QAAQ;AACd,WAAO,CAAC;AAAA,MACN,UAAU;AAAA,MACV,WAAW;AAAA,MACX,iBAAiB,SAAS,QAAQ;AAAA,MAClC,YAAY,OAAO,KAAK,IAAI;AAAA,MAC5B,SAAS,EAAE,GAAG,OAAO,aAAa,SAAS,QAAQ,YAAY;AAAA,MAC/D,SAAS,iBAAiB,OAAO;AAAA,IACnC,CAAC;AAAA,EACH;AACF;AAKO,IAAM,wBAAyC;AAAA,EACpD,IAAI;AAAA,EACJ,gBAAgB,EAAE,SAAS,OAAO,GAA0B;AAC1D,UAAM,QAAQ,YAAY,SAAS,sBAAsB;AACzD,QAAI,CAAC,MAAO,QAAO,EAAE,OAAO,OAAO,QAAQ,wBAAwB;AACnE,UAAM,IAAI,OAAO,KAAK,OAAO,OAAO;AACpC,UAAM,IAAI,OAAO,KAAK,QAAQ,OAAO;AACrC,QAAI,EAAE,WAAW,EAAE,OAAQ,QAAO,EAAE,OAAO,OAAO,QAAQ,oBAAoB;AAC9E,WAAO,gBAAgB,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,OAAO,QAAQ,oBAAoB;AAAA,EAC/F;AAAA,EACA,MAAM,EAAE,SAAS,IAAI,GAAsB;AACzC,UAAM,aAAa,YAAY,SAAS,oBAAoB;AAC5D,UAAM,gBAAgB,YAAY,SAAS,uBAAuB,KAAK;AACvE,UAAM,YAAY,YAAY,SAAS,mBAAmB;AAC1D,UAAM,gBAAgB,YAAY,SAAS,uBAAuB;AAClE,QAAI,kBAAkB,QAAQ;AAC5B,aAAO,CAAC;AAAA,QACN,UAAU;AAAA,QACV,WAAW;AAAA,QACX,iBAAiB,gBAAgB,GAAG,SAAS,IAAI,aAAa,KAAK;AAAA,QACnE,YAAY,OAAO,KAAK,IAAI;AAAA,QAC5B,SAAS,EAAE,WAAW,YAAY,cAAc;AAAA,QAChD,SAAS,iBAAiB,OAAO;AAAA,MACnC,CAAC;AAAA,IACH;AACA,WAAO,CAAC;AAAA,MACN,UAAU;AAAA,MACV,WAAW,mBAAmB,aAAa;AAAA,MAC3C,iBAAiB,gBAAgB,GAAG,SAAS,IAAI,aAAa,KAAK;AAAA,MACnE,YAAY,OAAO,KAAK,IAAI;AAAA,MAC5B,SAAS,EAAE,WAAW,YAAY,cAAc;AAAA,MAChD,SAAS,iBAAiB,OAAO;AAAA,IACnC,CAAC;AAAA,EACH;AACF;AAKO,SAAS,2BAA2B,SAQvB;AAClB,QAAM,SAAS,QAAQ,mBAAmB;AAC1C,SAAO;AAAA,IACL,IAAI,QAAQ;AAAA,IACZ,gBAAgB,EAAE,SAAS,SAAS,OAAO,GAAG;AAC5C,YAAM,MAAM,YAAY,SAAS,MAAM;AACvC,UAAI,CAAC,IAAK,QAAO,EAAE,OAAO,OAAO,QAAQ,WAAW,MAAM,GAAG;AAC7D,aAAO,oBAAoB,SAAS,KAAK,QAAQ;AAAA,QAC/C,WAAW,QAAQ,aAAa;AAAA,QAChC,iBAAiB,QAAQ;AAAA,MAC3B,CAAC,IACG,EAAE,OAAO,KAAK,IACd,EAAE,OAAO,OAAO,QAAQ,oBAAoB;AAAA,IAClD;AAAA,IACA,OACE,QAAQ,UACP,CAAC,EAAE,SAAS,SAAS,IAAI,MAAM;AAC9B,YAAM,MAAM,SAAS,OAAO,KAAK;AACjC,aAAO,CAAC;AAAA,QACN,UAAU,QAAQ;AAAA,QAClB,WAAW,GAAG,QAAQ,EAAE;AAAA,QACxB,YAAY,OAAO,KAAK,IAAI;AAAA,QAC5B,SAAS;AAAA,QACT,SAAS,iBAAiB,OAAO;AAAA,MACnC,CAAC;AAAA,IACH;AAAA,EACJ;AACF;AAEA,SAAS,SAAS,GAAoB;AACpC,MAAI;AACF,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,iBAAiB,SAAiD;AACzE,QAAM,MAA8B,CAAC;AACrC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC5C,QAAI,MAAM,OAAW;AACrB,UAAM,QAAQ,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI;AACxC,QAAI,OAAO,UAAU,SAAU,KAAI,EAAE,YAAY,CAAC,IAAI;AAAA,EACxD;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/webhooks/router.ts","../../src/webhooks/providers.ts"],"sourcesContent":["/**\n * @stable Provider-agnostic inbound webhook router.\n *\n * Consumer hooks a single HTTP handler at `/webhook/:provider/:event`\n * (or whatever pathing they prefer) and forwards the request through\n * `WebhookRouter.handle()`. The router:\n *\n * 1. Resolves the registered provider entry.\n * 2. Calls the provider's `verifySignature(rawBody, headers, secrets)`.\n * Failure → 401 fast, no downstream work.\n * 3. Calls the provider's `parse(rawBody, headers)` to extract zero or\n * more normalized events.\n * 4. Enqueues each event for async processing via the consumer-supplied\n * `deliver(event)` callback (best-effort fire-and-forget — the\n * router does NOT block the HTTP response on the consumer's work).\n * 5. Returns 200 fast with `{received: events.length}`.\n *\n * Replay protection: providers that sign timestamps (Stripe, Slack)\n * already reject stale signatures inside `verifySignature`. For providers\n * that don't (DocuSeal, GDrive push), the router exposes a pluggable\n * `idempotency` hook: if `idempotency.seen(providerEventId)` returns\n * true, the router 200s without invoking `deliver()`. Consumers wire\n * this to a durable kv (D1 / Redis / Postgres unique-index).\n *\n * Why a router and not a per-provider express app: the runtime contract\n * a product cares about is \"an inbound event came in, here's the\n * normalized envelope\". Verification, parsing, and idempotency-dedup\n * are mechanical and provider-specific — the router owns them. The\n * consumer's `deliver()` is the only place product logic runs.\n *\n * Stability: `@stable` — additions to `WebhookEnvelope` must be\n * additive; the router's HTTP contract (paths, status codes) is frozen\n * at 200 (ok), 400 (bad request), 401 (bad signature), 404 (unknown\n * provider), 405 (provider has no inbound surface).\n */\n\nexport interface WebhookHeaders {\n [name: string]: string | string[] | undefined\n}\n\n/** Normalized inbound event the router emits after parsing. */\nexport interface WebhookEnvelope<TPayload = unknown> {\n /** Provider id (matches the `:provider` path segment). */\n provider: string\n /** Optional event class — e.g., 'customer.subscription.deleted'. The\n * provider's parser decides. Used for routing inside `deliver()`. */\n eventType: string\n /** Provider-emitted event id, when present. Used for the idempotency\n * short-circuit. */\n providerEventId?: string\n /** Wall-clock receive time. */\n receivedAt: number\n /** Provider payload, normalized to the provider's documented event\n * shape. The router does NOT reshape this — `parse()` is the contract. */\n payload: TPayload\n /** Headers passed through for downstream handlers that want them\n * (e.g., to extract custom routing metadata). Always lowercased keys. */\n headers: Record<string, string>\n}\n\nexport type SignatureVerification =\n | { valid: true }\n | { valid: false; reason: string }\n\n/** Per-provider plug-in. Stateless — the router calls `verifySignature`\n * then `parse` on every request. The provider's HTTP-shape concerns\n * (e.g., raw body required) are documented per provider. */\nexport interface WebhookProvider {\n /** Stable provider id (`stripe`, `docuseal`, `gdrive`, ...). */\n id: string\n /** Verify the inbound signature. Receives the EXACT raw body string —\n * consumers MUST preserve raw bytes through their HTTP server (do not\n * parse JSON before forwarding here). */\n verifySignature(input: {\n rawBody: string\n headers: WebhookHeaders\n secret: string\n }): SignatureVerification\n /** Parse the validated raw body into zero or more normalized events.\n * A single push payload may carry multiple events (e.g., Slack bulk\n * delivery). Return [] to ack the push as a no-op. */\n parse(input: {\n rawBody: string\n headers: WebhookHeaders\n now?: number\n }): WebhookEnvelope[] | Promise<WebhookEnvelope[]>\n /** Optional provider-specific 200 ACK the router returns on a verified\n * delivery, in place of the default `{ received, total }` JSON. Dropbox Sign\n * acknowledges a delivery only when the body is exactly\n * 'Hello API Event Received' and retries otherwise; this lets such a\n * provider declare the body/headers it needs. The frozen status contract is\n * unchanged — only the 200 body/headers are customized. */\n successResponse?: { body: string; headers?: Record<string, string> }\n}\n\nexport interface WebhookIdempotencyStore {\n /** Returns true if this providerEventId has been processed already.\n * Implementations should be O(1) (Redis SETNX, D1 UNIQUE constraint). */\n seen(providerEventId: string): Promise<boolean> | boolean\n /** Marks a providerEventId as processed. Called AFTER `deliver()` has\n * been invoked. */\n remember(providerEventId: string, ttlMs: number): Promise<void> | void\n}\n\nexport interface WebhookRouterOptions {\n /** Provider registry. Pass any number of providers; routing is by id. */\n providers: WebhookProvider[]\n /** Async callback invoked with every accepted event. Fire-and-forget\n * from the router's perspective — the HTTP response is sent before\n * this resolves. Throws are caught and reported via `onError`. */\n deliver(event: WebhookEnvelope): Promise<void> | void\n /** Resolve the signing secret for a provider id at request time. The\n * router never holds secrets — the consumer's vault resolves them. */\n resolveSecret(providerId: string, headers: WebhookHeaders): Promise<string | null> | string | null\n /** Optional idempotency-dedup hook. Required for providers that don't\n * sign timestamps in their signature scheme (DocuSeal, Drive push). */\n idempotency?: WebhookIdempotencyStore\n /** TTL on idempotency entries. Default 7 days — long enough that a\n * provider's normal retry-window can't re-deliver. */\n idempotencyTtlMs?: number\n /** Surface delivery errors. Default: console.error. */\n onError?(err: unknown, context: { provider: string; eventType?: string; providerEventId?: string }): void\n /** Override `now()` for tests. */\n now?(): number\n}\n\nexport interface WebhookRouterRequest {\n providerId: string\n rawBody: string\n headers: WebhookHeaders\n}\n\nexport interface WebhookRouterResponse {\n status: number\n body: unknown\n headers?: Record<string, string>\n}\n\n/**\n * Router instance. Stateless aside from the provider registry — safe to\n * share across requests; build once per process.\n */\nexport class WebhookRouter {\n private readonly providers: Map<string, WebhookProvider>\n private readonly deliver: WebhookRouterOptions['deliver']\n private readonly resolveSecret: WebhookRouterOptions['resolveSecret']\n private readonly idempotency?: WebhookIdempotencyStore\n private readonly idempotencyTtlMs: number\n private readonly onError: NonNullable<WebhookRouterOptions['onError']>\n private readonly nowFn: () => number\n\n constructor(opts: WebhookRouterOptions) {\n this.providers = new Map(opts.providers.map((p) => [p.id, p]))\n this.deliver = opts.deliver\n this.resolveSecret = opts.resolveSecret\n this.idempotency = opts.idempotency\n this.idempotencyTtlMs = opts.idempotencyTtlMs ?? 7 * 24 * 60 * 60 * 1000\n this.onError = opts.onError ?? defaultOnError\n this.nowFn = opts.now ?? Date.now\n }\n\n /** Process one inbound webhook request. Pure with respect to side-\n * effects on the router instance — safe to call concurrently. */\n async handle(request: WebhookRouterRequest): Promise<WebhookRouterResponse> {\n const provider = this.providers.get(request.providerId)\n if (!provider) {\n return { status: 404, body: { error: 'unknown_provider', provider: request.providerId } }\n }\n const secret = await this.resolveSecret(provider.id, request.headers)\n if (!secret) {\n return { status: 401, body: { error: 'missing_secret', provider: provider.id } }\n }\n const verification = provider.verifySignature({\n rawBody: request.rawBody,\n headers: request.headers,\n secret,\n })\n if (!verification.valid) {\n return { status: 401, body: { error: 'invalid_signature', reason: verification.reason } }\n }\n\n let events: WebhookEnvelope[]\n try {\n events = await provider.parse({ rawBody: request.rawBody, headers: request.headers, now: this.nowFn() })\n } catch (err) {\n this.onError(err, { provider: provider.id })\n return { status: 400, body: { error: 'parse_error', message: errMessage(err) } }\n }\n\n const accepted: WebhookEnvelope[] = []\n for (const event of events) {\n if (event.providerEventId && this.idempotency) {\n const already = await this.idempotency.seen(event.providerEventId)\n if (already) continue\n }\n accepted.push(event)\n }\n\n // Deliver async — do NOT block the HTTP response. Errors land in\n // `onError`; the provider already got its 200 by then so it will\n // not retry.\n queueMicrotask(() => {\n void this.deliverEach(accepted)\n })\n\n if (provider.successResponse) {\n return {\n status: 200,\n body: provider.successResponse.body,\n headers: provider.successResponse.headers,\n }\n }\n return { status: 200, body: { received: accepted.length, total: events.length } }\n }\n\n private async deliverEach(events: WebhookEnvelope[]): Promise<void> {\n for (const event of events) {\n try {\n await this.deliver(event)\n if (event.providerEventId && this.idempotency) {\n await this.idempotency.remember(event.providerEventId, this.idempotencyTtlMs)\n }\n } catch (err) {\n this.onError(err, {\n provider: event.provider,\n eventType: event.eventType,\n providerEventId: event.providerEventId,\n })\n }\n }\n }\n}\n\nfunction defaultOnError(err: unknown, context: { provider: string; eventType?: string; providerEventId?: string }): void {\n // eslint-disable-next-line no-console\n console.error('[WebhookRouter]', context, err)\n}\n\nfunction errMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n","/**\n * Pre-built `WebhookProvider` implementations for the inbound surfaces\n * the substrate ships first-party verifiers for.\n *\n * Each provider implementation is intentionally thin: it delegates\n * signature verification to the corresponding pure function in\n * `connectors/webhooks.ts` and parses the body into one or more\n * normalized `WebhookEnvelope` rows. Anything provider-specific that\n * doesn't fit cleanly (Slack URL-verification handshake, etc.) is\n * surfaced via the envelope `eventType` so the consumer's `deliver()`\n * can branch.\n */\n\nimport {\n firstHeader,\n verifyHmacSignature,\n verifySlackSignature,\n verifyStripeSignature,\n} from '../connectors/webhooks.js'\nimport type { WebhookEnvelope, WebhookHeaders, WebhookProvider, SignatureVerification } from './router.js'\nimport { createHash, createHmac, timingSafeEqual } from 'node:crypto'\n\n/** Stripe webhook provider. Signature header `Stripe-Signature`. */\nexport const stripeWebhookProvider: WebhookProvider = {\n id: 'stripe',\n verifySignature({ rawBody, headers, secret }): SignatureVerification {\n const sig = firstHeader(headers, 'stripe-signature')\n if (!sig) return { valid: false, reason: 'missing_stripe_signature' }\n return verifyStripeSignature(rawBody, sig, secret)\n ? { valid: true }\n : { valid: false, reason: 'invalid_signature' }\n },\n parse({ rawBody, headers, now }): WebhookEnvelope[] {\n const evt = safeJson(rawBody)\n if (!evt || typeof evt !== 'object') return []\n const e = evt as { id?: unknown; type?: unknown }\n return [\n {\n provider: 'stripe',\n eventType: typeof e.type === 'string' ? e.type : 'stripe.unknown',\n providerEventId: typeof e.id === 'string' ? e.id : undefined,\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n },\n ]\n },\n}\n\n/** Slack Events API provider. Handles the `url_verification` handshake\n * by emitting a synthetic event the consumer's `deliver()` can echo. */\nexport const slackWebhookProvider: WebhookProvider = {\n id: 'slack',\n verifySignature({ rawBody, headers, secret }): SignatureVerification {\n const sig = firstHeader(headers, 'x-slack-signature')\n const ts = firstHeader(headers, 'x-slack-request-timestamp')\n if (!sig || !ts) return { valid: false, reason: 'missing_slack_signature_or_timestamp' }\n return verifySlackSignature(rawBody, sig, ts, secret)\n ? { valid: true }\n : { valid: false, reason: 'invalid_signature' }\n },\n parse({ rawBody, headers, now }): WebhookEnvelope[] {\n const evt = safeJson(rawBody) as { type?: string; event_id?: string; event?: { type?: string } } | null\n if (!evt || typeof evt !== 'object') return []\n if (evt.type === 'url_verification') {\n return [{\n provider: 'slack',\n eventType: 'slack.url_verification',\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n }]\n }\n const eventType = `slack.${evt.event?.type ?? evt.type ?? 'unknown'}`\n return [{\n provider: 'slack',\n eventType,\n providerEventId: typeof evt.event_id === 'string' ? evt.event_id : undefined,\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n }]\n },\n}\n\n/** DocuSeal webhook provider. Signature header `X-Docuseal-Signature`. */\nexport const docusealWebhookProvider: WebhookProvider = {\n id: 'docuseal',\n verifySignature({ rawBody, headers, secret }): SignatureVerification {\n const sig = firstHeader(headers, 'x-docuseal-signature')\n if (!sig) return { valid: false, reason: 'missing_docuseal_signature' }\n const expected = createHmac('sha256', secret).update(rawBody).digest('hex')\n const a = Buffer.from(sig.toLowerCase(), 'utf-8')\n const b = Buffer.from(expected, 'utf-8')\n if (a.length !== b.length) return { valid: false, reason: 'invalid_signature' }\n return timingSafeEqual(a, b) ? { valid: true } : { valid: false, reason: 'invalid_signature' }\n },\n parse({ rawBody, headers, now }): WebhookEnvelope[] {\n const evt = safeJson(rawBody) as { event_type?: string; event_id?: string } | null\n if (!evt || typeof evt !== 'object') return []\n return [{\n provider: 'docuseal',\n eventType: `docuseal.${evt.event_type ?? 'unknown'}`,\n providerEventId: typeof evt.event_id === 'string' ? evt.event_id : undefined,\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n }]\n },\n}\n\n/** Gmail push provider. Cloud Pub/Sub posts a JWT-signed envelope; the\n * *payload* is base64 JSON describing the changed history range. The\n * signature scheme here is the Pub/Sub JWT auth header — when supplied,\n * consumers SHOULD verify the JWT against Google's well-known\n * certificates. We accept the simpler \"Bearer <pubsub-shared-secret>\"\n * variant by default (matching `verifyHmacSignature`). */\nexport const gmailWebhookProvider: WebhookProvider = {\n id: 'gmail',\n verifySignature({ headers, secret }): SignatureVerification {\n const auth = firstHeader(headers, 'authorization')\n if (!auth) return { valid: false, reason: 'missing_authorization' }\n // Accept either \"Bearer <secret>\" or \"Token <secret>\" formats. This\n // is the simple per-tenant shared-secret path; JWT verification is\n // left to the consumer's `deliver()` when they need full Google JWT\n // chain validation.\n const m = /^(?:Bearer|Token)\\s+(.+)$/i.exec(auth)\n if (!m) return { valid: false, reason: 'invalid_authorization_format' }\n const a = Buffer.from(m[1]!, 'utf-8')\n const b = Buffer.from(secret, 'utf-8')\n if (a.length !== b.length) return { valid: false, reason: 'invalid_signature' }\n return timingSafeEqual(a, b) ? { valid: true } : { valid: false, reason: 'invalid_signature' }\n },\n parse({ rawBody, headers, now }): WebhookEnvelope[] {\n const envelope = safeJson(rawBody) as { message?: { data?: string; messageId?: string; publishTime?: string } } | null\n if (!envelope?.message?.data) return []\n let payload: unknown\n try {\n payload = JSON.parse(Buffer.from(envelope.message.data, 'base64').toString('utf-8'))\n } catch {\n return []\n }\n const inner = payload as { historyId?: number | string; emailAddress?: string }\n return [{\n provider: 'gmail',\n eventType: 'gmail.history_changed',\n providerEventId: envelope.message.messageId,\n receivedAt: now ?? Date.now(),\n payload: { ...inner, publishTime: envelope.message.publishTime },\n headers: normalizeHeaders(headers),\n }]\n },\n}\n\n/** Google Drive push provider. Drive does NOT sign the body — it uses\n * the per-channel token (`X-Goog-Channel-Token`) as the shared secret.\n * The router compares it constant-time against the resolved secret. */\nexport const gdriveWebhookProvider: WebhookProvider = {\n id: 'gdrive',\n verifySignature({ headers, secret }): SignatureVerification {\n const token = firstHeader(headers, 'x-goog-channel-token')\n if (!token) return { valid: false, reason: 'missing_channel_token' }\n const a = Buffer.from(token, 'utf-8')\n const b = Buffer.from(secret, 'utf-8')\n if (a.length !== b.length) return { valid: false, reason: 'invalid_signature' }\n return timingSafeEqual(a, b) ? { valid: true } : { valid: false, reason: 'invalid_signature' }\n },\n parse({ headers, now }): WebhookEnvelope[] {\n const resourceId = firstHeader(headers, 'x-goog-resource-id')\n const resourceState = firstHeader(headers, 'x-goog-resource-state') ?? 'unknown'\n const channelId = firstHeader(headers, 'x-goog-channel-id')\n const messageNumber = firstHeader(headers, 'x-goog-message-number')\n if (resourceState === 'sync') {\n return [{\n provider: 'gdrive',\n eventType: 'gdrive.channel.sync',\n providerEventId: messageNumber ? `${channelId}-${messageNumber}` : undefined,\n receivedAt: now ?? Date.now(),\n payload: { channelId, resourceId, resourceState },\n headers: normalizeHeaders(headers),\n }]\n }\n return [{\n provider: 'gdrive',\n eventType: `gdrive.resource.${resourceState}`,\n providerEventId: messageNumber ? `${channelId}-${messageNumber}` : undefined,\n receivedAt: now ?? Date.now(),\n payload: { channelId, resourceId, resourceState },\n headers: normalizeHeaders(headers),\n }]\n },\n}\n\n/** The Telegram update keys, in resolution order — the first present key names\n * the update kind (`telegram.message`, `telegram.callback_query`, …). */\nconst TELEGRAM_UPDATE_KEYS = [\n 'message',\n 'edited_message',\n 'channel_post',\n 'edited_channel_post',\n 'business_connection',\n 'business_message',\n 'edited_business_message',\n 'deleted_business_messages',\n 'message_reaction',\n 'message_reaction_count',\n 'inline_query',\n 'chosen_inline_result',\n 'callback_query',\n 'shipping_query',\n 'pre_checkout_query',\n 'purchased_paid_media',\n 'poll',\n 'poll_answer',\n 'my_chat_member',\n 'chat_member',\n 'chat_join_request',\n 'chat_boost',\n 'removed_chat_boost',\n] as const\n\n/** Telegram Bot API webhook provider. Telegram does NOT sign the body — it\n * authenticates by echoing the `secret_token` set at `setWebhook` time in the\n * `X-Telegram-Bot-Api-Secret-Token` header. We compare it constant-time to the\n * per-connection secret. `update_id` is the idempotency anchor. */\nexport const telegramWebhookProvider: WebhookProvider = {\n id: 'telegram',\n verifySignature({ headers, secret }): SignatureVerification {\n const token = firstHeader(headers, 'x-telegram-bot-api-secret-token')\n if (!token) return { valid: false, reason: 'missing_telegram_secret_token' }\n const a = Buffer.from(token, 'utf-8')\n const b = Buffer.from(secret, 'utf-8')\n if (a.length !== b.length) return { valid: false, reason: 'invalid_secret_token' }\n return timingSafeEqual(a, b) ? { valid: true } : { valid: false, reason: 'invalid_secret_token' }\n },\n parse({ rawBody, headers, now }): WebhookEnvelope[] {\n const evt = safeJson(rawBody) as ({ update_id?: number } & Record<string, unknown>) | null\n if (!evt || typeof evt !== 'object') return []\n const kind = TELEGRAM_UPDATE_KEYS.find((k) => k in evt) ?? 'unknown'\n return [{\n provider: 'telegram',\n eventType: `telegram.${kind}`,\n providerEventId: typeof evt.update_id === 'number' ? String(evt.update_id) : undefined,\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n }]\n },\n}\n\n/** Dropbox Sign (HelloSign) webhook provider. There is NO signature header:\n * the body carries `event.event_hash = HMAC_SHA256(apiKey, event_time +\n * event_type)` (hex), so the `secret` here is the account's API key (the\n * caller resolves it from the connection credential). The body is JSON, or a\n * `json=`/multipart `name=\"json\"` form field on older apps. */\nexport const hellosignWebhookProvider: WebhookProvider = {\n id: 'hellosign',\n // Dropbox Sign acknowledges a delivery only when the response body is exactly\n // 'Hello API Event Received'; any other body makes it retry the event.\n successResponse: { body: 'Hello API Event Received', headers: { 'content-type': 'text/plain' } },\n verifySignature({ rawBody, secret }): SignatureVerification {\n const body = parseHelloSignBody(rawBody)\n const event = body?.event\n if (\n !event ||\n typeof event.event_time !== 'string' ||\n typeof event.event_type !== 'string' ||\n typeof event.event_hash !== 'string'\n ) {\n return { valid: false, reason: 'missing_event_fields' }\n }\n const expected = createHmac('sha256', secret)\n .update(`${event.event_time}${event.event_type}`)\n .digest('hex')\n const a = Buffer.from(event.event_hash.toLowerCase(), 'utf-8')\n const b = Buffer.from(expected.toLowerCase(), 'utf-8')\n if (a.length !== b.length) return { valid: false, reason: 'invalid_signature' }\n return timingSafeEqual(a, b) ? { valid: true } : { valid: false, reason: 'invalid_signature' }\n },\n parse({ rawBody, headers, now }): WebhookEnvelope[] {\n const body = parseHelloSignBody(rawBody)\n const event = body?.event\n if (!event) return []\n return [{\n provider: 'hellosign',\n eventType: `hellosign.${event.event_type ?? 'unknown'}`,\n // Prefer Dropbox Sign's canonical `event.event_id` (stable across\n // redeliveries, correlates with the provider). Fall back to a digest of\n // the parsed body when it's absent: `event_hash` = HMAC(apiKey,\n // event_time + event_type), so two distinct same-type events in the same\n // second share it — the body digest stays distinct across differing\n // events and identical across redeliveries of one event.\n providerEventId:\n typeof event.event_id === 'string' && event.event_id.length > 0\n ? event.event_id\n : createHash('sha256').update(JSON.stringify(body)).digest('hex'),\n receivedAt: now ?? Date.now(),\n payload: body,\n headers: normalizeHeaders(headers),\n }]\n },\n}\n\ninterface HelloSignInboundBody {\n event?: {\n event_time?: string\n event_type?: string\n event_hash?: string\n event_id?: string\n }\n}\n\n/** Dropbox Sign posts JSON, a form-urlencoded `json=` field, or a\n * multipart `name=\"json\"` part depending on app vintage. Mirrors the\n * connector adapter's body extraction. */\nfunction parseHelloSignBody(rawBody: string): HelloSignInboundBody | null {\n try {\n const json = JSON.parse(rawBody) as HelloSignInboundBody\n if (json && typeof json === 'object') return json\n } catch {\n // fall through to form-data extraction\n }\n // application/x-www-form-urlencoded `json=` field. URLSearchParams decodes\n // `+` as a space (decodeURIComponent leaves it literal) and resolves\n // percent-escapes, so space-bearing string fields aren't corrupted.\n const formJson = new URLSearchParams(rawBody).get('json')\n if (formJson) {\n try {\n return JSON.parse(formJson) as HelloSignInboundBody\n } catch {\n return null\n }\n }\n // multipart/form-data with a `name=\"json\"` part. Extract linearly (indexOf +\n // fixed-shape separator probes, no unbounded lazy regex) so a hostile body\n // can't drive quadratic backtracking on this public ingress.\n return extractMultipartJsonField(rawBody)\n}\n\n/** Pull the `json` part out of a multipart/form-data body without an\n * unbounded backtracking regex. Returns null if the part isn't present or\n * isn't valid JSON. The separator probes (`\\r?\\n\\r?\\n`, `\\r?\\n--`) are\n * fixed-shape (no `*`/`+`), so each runs in a single linear scan. */\nfunction extractMultipartJsonField(rawBody: string): HelloSignInboundBody | null {\n const nameIdx = rawBody.indexOf('name=\"json\"')\n if (nameIdx === -1) return null\n const afterName = rawBody.slice(nameIdx)\n const sep = /\\r?\\n\\r?\\n/.exec(afterName)\n if (!sep) return null\n const partBody = afterName.slice(sep.index + sep[0].length)\n const end = /\\r?\\n--/.exec(partBody)\n if (!end) return null\n try {\n return JSON.parse(partBody.slice(0, end.index)) as HelloSignInboundBody\n } catch {\n return null\n }\n}\n\n/** Generic HMAC provider — for the long-tail webhook source where the\n * caller has standardised on a single sha256-of-body scheme. Header\n * `X-Signature` by default; override at provider-build time if needed. */\nexport function genericHmacWebhookProvider(options: {\n id: string\n signatureHeader?: string\n algorithm?: 'sha256' | 'sha1' | 'sha512'\n signaturePrefix?: string\n /** Parser to convert the raw body into envelopes. Defaults to\n * \"one event with eventType=<provider>.event and payload=JSON\". */\n parse?: WebhookProvider['parse']\n}): WebhookProvider {\n const header = options.signatureHeader ?? 'x-signature'\n return {\n id: options.id,\n verifySignature({ rawBody, headers, secret }) {\n const sig = firstHeader(headers, header)\n if (!sig) return { valid: false, reason: `missing_${header}` }\n return verifyHmacSignature(rawBody, sig, secret, {\n algorithm: options.algorithm ?? 'sha256',\n signaturePrefix: options.signaturePrefix,\n })\n ? { valid: true }\n : { valid: false, reason: 'invalid_signature' }\n },\n parse:\n options.parse ??\n (({ rawBody, headers, now }) => {\n const evt = safeJson(rawBody) ?? rawBody\n return [{\n provider: options.id,\n eventType: `${options.id}.event`,\n receivedAt: now ?? Date.now(),\n payload: evt,\n headers: normalizeHeaders(headers),\n }]\n }),\n }\n}\n\nfunction safeJson(s: string): unknown {\n try {\n return JSON.parse(s)\n } catch {\n return null\n }\n}\n\nfunction normalizeHeaders(headers: WebhookHeaders): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries(headers)) {\n if (v === undefined) continue\n const value = Array.isArray(v) ? v[0] : v\n if (typeof value === 'string') out[k.toLowerCase()] = value\n }\n return out\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-integrations",
3
- "version": "0.39.0",
3
+ "version": "0.40.1",
4
4
  "description": "Vendor-neutral integration contracts and runtime helpers for sandbox and agent apps.",
5
5
  "homepage": "https://github.com/tangle-network/agent-integrations#readme",
6
6
  "repository": {