@norbix.ai/ts 1.1.0 → 1.2.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.
@@ -44,6 +44,31 @@ var NORBIX_WEBHOOK_EVENT_NAMES = [
44
44
  "files.file.uploaded",
45
45
  "files.file.deleted"
46
46
  ];
47
+ var NorbixWebhookEvents = {
48
+ Database: {
49
+ RecordInserted: "database.record.inserted",
50
+ RecordUpdated: "database.record.updated",
51
+ RecordDeleted: "database.record.deleted",
52
+ RecordReplaced: "database.record.replaced",
53
+ RecordResponsibilityChanged: "database.record.responsibilityChanged",
54
+ RecordsInserted: "database.records.inserted",
55
+ RecordsUpdated: "database.records.updated",
56
+ RecordsDeleted: "database.records.deleted"
57
+ },
58
+ Membership: {
59
+ UserRegistered: "membership.user.registered",
60
+ UserInvited: "membership.user.invited",
61
+ UserVerified: "membership.user.verified",
62
+ UserUpdated: "membership.user.updated",
63
+ UserDeleted: "membership.user.deleted",
64
+ UserBlocked: "membership.user.blocked",
65
+ UserReactivated: "membership.user.reactivated"
66
+ },
67
+ Files: {
68
+ FileUploaded: "files.file.uploaded",
69
+ FileDeleted: "files.file.deleted"
70
+ }
71
+ };
47
72
  var NORBIX_WEBHOOK_EVENT_GROUPS = [
48
73
  {
49
74
  group: "database",
@@ -91,6 +116,71 @@ var NORBIX_WEBHOOK_HEADERS = {
91
116
  signature: "X-Norbix-Signature",
92
117
  timestamp: "X-Norbix-Timestamp"
93
118
  };
119
+
120
+ // src/webhooks/normalize.ts
121
+ function isObject(value) {
122
+ return typeof value === "object" && value !== null;
123
+ }
124
+ function normalizeNorbixWebhook(envelope) {
125
+ const event = envelope.event;
126
+ const data = envelope.data;
127
+ const d = isObject(data) ? data : {};
128
+ if (event.startsWith("database.")) {
129
+ const metadata = {};
130
+ if (typeof d.schemaName === "string") {
131
+ const schema = isObject(d.schema) ? d.schema : null;
132
+ const schemaId = schema && typeof schema.id === "string" ? schema.id : null;
133
+ metadata.schema = { id: schemaId, name: d.schemaName };
134
+ }
135
+ if (typeof d.integrationId === "string") metadata.integrationId = d.integrationId;
136
+ if (typeof d.id === "string") metadata.record = { id: d.id };
137
+ if (Array.isArray(d.ids)) metadata.records = { ids: d.ids };
138
+ switch (event) {
139
+ case "database.record.inserted":
140
+ case "database.record.deleted":
141
+ return { payload: d.document, metadata };
142
+ case "database.record.updated":
143
+ case "database.record.replaced":
144
+ return { payload: { from: d.from, to: d.to }, metadata };
145
+ case "database.records.inserted":
146
+ return { payload: d.documents ?? [], metadata };
147
+ default:
148
+ return { payload: data, metadata };
149
+ }
150
+ }
151
+ if (event.startsWith("membership.")) {
152
+ const metadata = {};
153
+ if (typeof d.id === "string") metadata.user = { id: d.id };
154
+ switch (event) {
155
+ case "membership.user.registered":
156
+ case "membership.user.verified":
157
+ case "membership.user.blocked":
158
+ case "membership.user.reactivated":
159
+ return { payload: d.to, metadata };
160
+ case "membership.user.deleted":
161
+ return { payload: d.from, metadata };
162
+ case "membership.user.updated":
163
+ return { payload: { from: d.from, to: d.to }, metadata };
164
+ case "membership.user.invited":
165
+ return { payload: { email: d.email }, metadata };
166
+ default:
167
+ return { payload: data, metadata };
168
+ }
169
+ }
170
+ if (event.startsWith("files.")) {
171
+ const metadata = {};
172
+ if (typeof d.integrationId === "string") metadata.integrationId = d.integrationId;
173
+ switch (event) {
174
+ case "files.file.uploaded":
175
+ return { payload: d.file, metadata };
176
+ case "files.file.deleted":
177
+ return { payload: { path: d.path }, metadata };
178
+ default:
179
+ return { payload: data, metadata };
180
+ }
181
+ }
182
+ return { payload: data, metadata: {} };
183
+ }
94
184
  function headerValue(headers, name) {
95
185
  if (headers instanceof Headers) {
96
186
  return headers.get(name);
@@ -185,38 +275,77 @@ function timingSafeEqualUtf8(a, b) {
185
275
  }
186
276
 
187
277
  // src/webhooks/receiver.ts
278
+ function readEnv() {
279
+ return typeof process !== "undefined" && process.env ? process.env : {};
280
+ }
281
+ function toNumber(value) {
282
+ if (value == null || value === "") return void 0;
283
+ const n = Number(value);
284
+ return Number.isFinite(n) ? n : void 0;
285
+ }
286
+ function resolveConfig(options) {
287
+ const env = readEnv();
288
+ const tolerance = options.toleranceSeconds ?? toNumber(env.NORBIX_WEBHOOK_TOLERANCE_SECONDS);
289
+ return {
290
+ secret: options.secret ?? env.NORBIX_WEBHOOK_SIGNING_SECRET,
291
+ projectId: options.projectId ?? env.NORBIX_PROJECT_ID,
292
+ accountId: options.accountId ?? env.NORBIX_ACCOUNT_ID,
293
+ toleranceSeconds: Number.isFinite(tolerance) ? tolerance : 300
294
+ };
295
+ }
188
296
  var NorbixWebhookReceiver = class {
189
- constructor(options = {}) {
190
- this.options = options;
191
- }
192
- options;
193
297
  handlers = /* @__PURE__ */ new Map();
194
298
  defaultHandler;
195
- /** Handle a specific event name (e.g. membership.user.registered). */
299
+ config;
300
+ constructor(options = {}) {
301
+ this.config = resolveConfig(options);
302
+ }
303
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- overloads pass narrower handlers
196
304
  on(event, handler) {
197
- this.handlers.set(event, handler);
305
+ this.handlers.set(event, { raw: false, fn: handler });
306
+ return this;
307
+ }
308
+ /** Register the typed handler for many events at once (skips already-registered). */
309
+ onEach(events, handler) {
310
+ for (const event of events) {
311
+ if (!this.handlers.has(event)) this.handlers.set(event, { raw: false, fn: handler });
312
+ }
198
313
  return this;
199
314
  }
200
- /** Fallback when no event-specific handler is registered. */
315
+ /* ---- Raw handlers: (envelope, ctx) ---- */
316
+ /** Raw handler for one event — receives the envelope and delivery context. */
317
+ onRaw(event, handler) {
318
+ this.handlers.set(event, { raw: true, fn: handler });
319
+ return this;
320
+ }
321
+ /** Register a raw handler for many events at once (skips already-registered). */
322
+ onEachRaw(events, handler) {
323
+ for (const event of events) {
324
+ if (!this.handlers.has(event)) this.handlers.set(event, { raw: true, fn: handler });
325
+ }
326
+ return this;
327
+ }
328
+ /** Fallback (raw) when no event-specific handler is registered. */
201
329
  onDefault(handler) {
202
- this.defaultHandler = handler;
330
+ this.defaultHandler = { raw: true, fn: handler };
203
331
  return this;
204
332
  }
205
333
  /**
206
- * Verify (when secret configured), parse, and dispatch the delivery.
207
- * Returns 200-worthy result throw NorbixWebhookSignatureError for 401.
334
+ * Verify (when secret configured), parse, normalise, and dispatch the delivery.
335
+ * Throws NorbixWebhookSignatureError (401-worthy) on bad signature or guard
336
+ * mismatch; otherwise returns a 200-worthy result.
208
337
  */
209
338
  async handle(input) {
210
339
  const deliveryHeaders = parseNorbixWebhookHeaders(input.headers);
211
- const shouldVerify = input.verify !== false && !!this.options.secret;
340
+ const shouldVerify = input.verify !== false && !!this.config.secret;
212
341
  let verified = null;
213
- if (shouldVerify && this.options.secret) {
342
+ if (shouldVerify && this.config.secret) {
214
343
  const result = verifyNorbixWebhookSignature({
215
- secret: this.options.secret,
344
+ secret: this.config.secret,
216
345
  rawBody: input.rawBody,
217
346
  signature: deliveryHeaders.signature,
218
347
  timestamp: deliveryHeaders.timestamp,
219
- toleranceSeconds: this.options.toleranceSeconds ?? 300
348
+ toleranceSeconds: this.config.toleranceSeconds
220
349
  });
221
350
  if (!result.ok) {
222
351
  throw new NorbixWebhookSignatureError(result.reason ?? "Invalid signature");
@@ -224,6 +353,16 @@ var NorbixWebhookReceiver = class {
224
353
  verified = true;
225
354
  }
226
355
  const envelope = parseNorbixWebhookEnvelope(input.rawBody);
356
+ if (this.config.projectId && envelope.projectId !== this.config.projectId) {
357
+ throw new NorbixWebhookSignatureError(
358
+ `delivery projectId ${envelope.projectId} does not match configured ${this.config.projectId}`
359
+ );
360
+ }
361
+ if (this.config.accountId && envelope.accountId !== this.config.accountId) {
362
+ throw new NorbixWebhookSignatureError(
363
+ `delivery accountId ${envelope.accountId} does not match configured ${this.config.accountId}`
364
+ );
365
+ }
227
366
  const ctx = {
228
367
  path: input.path,
229
368
  headers: {
@@ -235,10 +374,29 @@ var NorbixWebhookReceiver = class {
235
374
  },
236
375
  verified
237
376
  };
238
- const handler = this.handlers.get(envelope.event) ?? this.defaultHandler;
377
+ const registration = this.handlers.get(envelope.event) ?? this.defaultHandler;
239
378
  let handled = false;
240
- if (handler) {
241
- await handler(envelope, ctx);
379
+ if (registration) {
380
+ if (registration.raw) {
381
+ await registration.fn(envelope, ctx);
382
+ } else {
383
+ const { payload, metadata } = normalizeNorbixWebhook(envelope);
384
+ const event = {
385
+ name: envelope.event,
386
+ deliveryId: envelope.id,
387
+ createdOn: envelope.createdOn,
388
+ triggerId: envelope.triggerId ?? null,
389
+ correlationId: null,
390
+ accountId: ctx.headers.accountId ?? envelope.accountId,
391
+ projectId: ctx.headers.projectId ?? envelope.projectId,
392
+ integrationId: ctx.headers.integrationId,
393
+ destinationId: ctx.headers.destinationId,
394
+ verified,
395
+ metadata,
396
+ raw: envelope
397
+ };
398
+ await registration.fn(payload, event);
399
+ }
242
400
  handled = true;
243
401
  }
244
402
  return {
@@ -256,10 +414,12 @@ exports.NORBIX_WEBHOOK_EVENT_GROUPS = NORBIX_WEBHOOK_EVENT_GROUPS;
256
414
  exports.NORBIX_WEBHOOK_EVENT_NAMES = NORBIX_WEBHOOK_EVENT_NAMES;
257
415
  exports.NORBIX_WEBHOOK_HEADERS = NORBIX_WEBHOOK_HEADERS;
258
416
  exports.NorbixWebhookError = NorbixWebhookError;
417
+ exports.NorbixWebhookEvents = NorbixWebhookEvents;
259
418
  exports.NorbixWebhookParseError = NorbixWebhookParseError;
260
419
  exports.NorbixWebhookReceiver = NorbixWebhookReceiver;
261
420
  exports.NorbixWebhookSignatureError = NorbixWebhookSignatureError;
262
421
  exports.computeNorbixWebhookSignature = computeNorbixWebhookSignature;
422
+ exports.normalizeNorbixWebhook = normalizeNorbixWebhook;
263
423
  exports.parseNorbixWebhookEnvelope = parseNorbixWebhookEnvelope;
264
424
  exports.parseNorbixWebhookHeaders = parseNorbixWebhookHeaders;
265
425
  exports.verifyNorbixWebhookSignature = verifyNorbixWebhookSignature;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/webhooks/errors.ts","../../src/webhooks/events.ts","../../src/webhooks/headers.ts","../../src/webhooks/parse.ts","../../src/webhooks/receiver.ts"],"names":["createHmac","timingSafeEqual"],"mappings":"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","file":"index.cjs","sourcesContent":["export class NorbixWebhookError extends Error {\n readonly code: string;\n\n constructor(message: string, code: string) {\n super(message);\n this.name = 'NorbixWebhookError';\n this.code = code;\n }\n}\n\nexport class NorbixWebhookSignatureError extends NorbixWebhookError {\n constructor(message: string) {\n super(message, 'WEBHOOK_SIGNATURE_INVALID');\n this.name = 'NorbixWebhookSignatureError';\n }\n}\n\nexport class NorbixWebhookParseError extends NorbixWebhookError {\n constructor(message: string) {\n super(message, 'WEBHOOK_PARSE_INVALID');\n this.name = 'NorbixWebhookParseError';\n }\n}\n","/**\n * Closed catalog of event names a destination may subscribe to.\n * Source: gateway Domain trigger event name value objects.\n */\nexport const NORBIX_WEBHOOK_EVENT_NAMES = [\n 'database.record.inserted',\n 'database.record.updated',\n 'database.record.deleted',\n 'database.record.replaced',\n 'database.record.responsibilityChanged',\n 'database.records.inserted',\n 'database.records.updated',\n 'database.records.deleted',\n 'membership.user.registered',\n 'membership.user.invited',\n 'membership.user.verified',\n 'membership.user.updated',\n 'membership.user.deleted',\n 'membership.user.blocked',\n 'membership.user.reactivated',\n 'files.file.uploaded',\n 'files.file.deleted',\n] as const;\n\nexport type NorbixWebhookEventName = (typeof NORBIX_WEBHOOK_EVENT_NAMES)[number];\n\nexport interface NorbixWebhookEventGroup {\n group: string;\n label: string;\n events: readonly string[];\n}\n\nexport const NORBIX_WEBHOOK_EVENT_GROUPS: NorbixWebhookEventGroup[] = [\n {\n group: 'database',\n label: 'Database',\n events: [\n 'database.record.inserted',\n 'database.record.updated',\n 'database.record.deleted',\n 'database.record.replaced',\n 'database.record.responsibilityChanged',\n 'database.records.inserted',\n 'database.records.updated',\n 'database.records.deleted',\n ],\n },\n {\n group: 'membership',\n label: 'Membership',\n events: [\n 'membership.user.registered',\n 'membership.user.invited',\n 'membership.user.verified',\n 'membership.user.updated',\n 'membership.user.deleted',\n 'membership.user.blocked',\n 'membership.user.reactivated',\n ],\n },\n {\n group: 'files',\n label: 'Files',\n events: ['files.file.uploaded', 'files.file.deleted'],\n },\n];\n","/**\n * Outbound Norbix webhook delivery headers.\n *\n * Source of truth: gateway `WebhookDeliveryClient` (NOT `AuthStatics` /\n * `ConfigureCors`, which define **inbound API** headers like\n * `norbix-account-id` / `norbix-project-id` for studio → gateway calls).\n *\n * @see gateway/src/Isidos.CodeMash.Services.Webhook/Dispatching/WebhookDeliveryClient.cs\n */\nexport const NORBIX_WEBHOOK_HEADERS = {\n event: 'X-Norbix-Event',\n delivery: 'X-Norbix-Delivery',\n idempotencyKey: 'Idempotency-Key',\n account: 'X-Norbix-Account',\n project: 'X-Norbix-Project',\n integration: 'X-Norbix-Integration',\n destination: 'X-Norbix-Destination',\n signature: 'X-Norbix-Signature',\n timestamp: 'X-Norbix-Timestamp',\n} as const;\n\nexport type NorbixWebhookHeaderName =\n (typeof NORBIX_WEBHOOK_HEADERS)[keyof typeof NORBIX_WEBHOOK_HEADERS];\n","import { createHmac, timingSafeEqual } from 'node:crypto';\n\nimport { NorbixWebhookParseError } from './errors.js';\nimport { NORBIX_WEBHOOK_HEADERS } from './headers.js';\nimport type {\n NorbixWebhookDeliveryHeaders,\n NorbixWebhookEnvelope,\n NorbixWebhookHeaderBag,\n NorbixWebhookVerifyOptions,\n} from './types.js';\n\nfunction headerValue(headers: NorbixWebhookHeaderBag, name: string): string | null {\n if (headers instanceof Headers) {\n return headers.get(name);\n }\n\n const direct = headers[name];\n if (direct !== undefined) {\n return Array.isArray(direct) ? (direct[0] ?? null) : direct;\n }\n\n const lower = name.toLowerCase();\n const lowerDirect = headers[lower];\n if (lowerDirect !== undefined) {\n return Array.isArray(lowerDirect) ? (lowerDirect[0] ?? null) : lowerDirect;\n }\n\n // Some frameworks preserve original casing on header keys.\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lower) {\n return Array.isArray(value) ? (value[0] ?? null) : (value ?? null);\n }\n }\n\n return null;\n}\n\n/** Read Norbix delivery headers from an incoming HTTP request. */\nexport function parseNorbixWebhookHeaders(\n headers: NorbixWebhookHeaderBag,\n): NorbixWebhookDeliveryHeaders {\n const deliveryId =\n headerValue(headers, NORBIX_WEBHOOK_HEADERS.delivery) ??\n headerValue(headers, NORBIX_WEBHOOK_HEADERS.idempotencyKey);\n\n return {\n event: headerValue(headers, NORBIX_WEBHOOK_HEADERS.event),\n deliveryId,\n idempotencyKey: headerValue(headers, NORBIX_WEBHOOK_HEADERS.idempotencyKey),\n accountId: headerValue(headers, NORBIX_WEBHOOK_HEADERS.account),\n projectId: headerValue(headers, NORBIX_WEBHOOK_HEADERS.project),\n integrationId: headerValue(headers, NORBIX_WEBHOOK_HEADERS.integration),\n destinationId: headerValue(headers, NORBIX_WEBHOOK_HEADERS.destination),\n signature: headerValue(headers, NORBIX_WEBHOOK_HEADERS.signature),\n timestamp: headerValue(headers, NORBIX_WEBHOOK_HEADERS.timestamp),\n };\n}\n\n/** Parse the JSON envelope from the raw POST body. */\nexport function parseNorbixWebhookEnvelope<TData = unknown>(\n rawBody: string,\n): NorbixWebhookEnvelope<TData> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawBody);\n } catch {\n throw new NorbixWebhookParseError('Webhook body is not valid JSON');\n }\n\n if (!parsed || typeof parsed !== 'object') {\n throw new NorbixWebhookParseError('Webhook body must be a JSON object');\n }\n\n const envelope = parsed as Partial<NorbixWebhookEnvelope<TData>>;\n if (typeof envelope.id !== 'string' || !envelope.id) {\n throw new NorbixWebhookParseError('Webhook envelope missing id');\n }\n if (typeof envelope.event !== 'string' || !envelope.event) {\n throw new NorbixWebhookParseError('Webhook envelope missing event');\n }\n\n return envelope as NorbixWebhookEnvelope<TData>;\n}\n\nexport interface NorbixWebhookSignatureVerification {\n ok: boolean;\n reason?: string;\n}\n\n/**\n * Verify X-Norbix-Signature against the raw body.\n * Algorithm (gateway WebhookDeliveryClient.Sign):\n * sha256=<hex> HMAC-SHA256(secret, \"<timestamp>.<rawBody>\")\n */\nexport function verifyNorbixWebhookSignature(\n input: {\n rawBody: string;\n signature?: string | null;\n timestamp?: string | null;\n } & NorbixWebhookVerifyOptions,\n): NorbixWebhookSignatureVerification {\n const { secret, rawBody, signature, timestamp, toleranceSeconds = 300 } = input;\n\n if (!signature) {\n return { ok: false, reason: 'missing X-Norbix-Signature header' };\n }\n if (!timestamp) {\n return { ok: false, reason: 'missing X-Norbix-Timestamp header' };\n }\n\n if (toleranceSeconds > 0) {\n const sent = Number(timestamp);\n if (!Number.isFinite(sent)) {\n return { ok: false, reason: 'X-Norbix-Timestamp is not a number' };\n }\n const ageSeconds = Math.abs(Date.now() / 1000 - sent);\n if (ageSeconds > toleranceSeconds) {\n return {\n ok: false,\n reason: `timestamp outside ${toleranceSeconds}s tolerance (age ${Math.round(ageSeconds)}s)`,\n };\n }\n }\n\n const expected = computeNorbixWebhookSignature(secret, timestamp, rawBody);\n if (!timingSafeEqualUtf8(expected, signature)) {\n return { ok: false, reason: 'signature mismatch' };\n }\n\n return { ok: true };\n}\n\nexport function computeNorbixWebhookSignature(\n secret: string,\n timestamp: string,\n rawBody: string,\n): string {\n return `sha256=${hmacSha256Hex(secret, `${timestamp}.${rawBody}`)}`;\n}\n\nfunction hmacSha256Hex(secret: string, payload: string): string {\n return createHmac('sha256', secret).update(payload, 'utf8').digest('hex');\n}\n\nfunction timingSafeEqualUtf8(a: string, b: string): boolean {\n const bufA = Buffer.from(a, 'utf8');\n const bufB = Buffer.from(b, 'utf8');\n if (bufA.length !== bufB.length) return false;\n return timingSafeEqual(bufA, bufB);\n}\n","import { NorbixWebhookSignatureError } from './errors.js';\nimport {\n parseNorbixWebhookEnvelope,\n parseNorbixWebhookHeaders,\n verifyNorbixWebhookSignature,\n} from './parse.js';\nimport type {\n NorbixWebhookContext,\n NorbixWebhookHandleInput,\n NorbixWebhookHandleResult,\n NorbixWebhookHandler,\n NorbixWebhookReceiverOptions,\n} from './types.js';\n\n/**\n * Register handlers for inbound Norbix webhook deliveries (trigger → destination POST).\n *\n * Triggers with a `WebhookCall` action publish events to configured destinations.\n * This receiver verifies the HMAC signature, parses the envelope, and dispatches\n * to per-event handlers (similar to Stripe's webhook pattern).\n *\n * @example\n * ```ts\n * const receiver = new NorbixWebhookReceiver({\n * secret: process.env.NORBIX_WEBHOOK_SECRET,\n * });\n *\n * receiver.on('database.record.inserted', async (event) => {\n * console.log('inserted', event.data);\n * });\n *\n * receiver.onDefault(async (event) => {\n * console.log('unhandled', event.event);\n * });\n *\n * await receiver.handle({ rawBody, headers: req.headers });\n * ```\n */\nexport class NorbixWebhookReceiver {\n private readonly handlers = new Map<string, NorbixWebhookHandler>();\n private defaultHandler?: NorbixWebhookHandler;\n\n constructor(private readonly options: NorbixWebhookReceiverOptions = {}) {}\n\n /** Handle a specific event name (e.g. membership.user.registered). */\n on(event: string, handler: NorbixWebhookHandler): this {\n this.handlers.set(event, handler);\n return this;\n }\n\n /** Fallback when no event-specific handler is registered. */\n onDefault(handler: NorbixWebhookHandler): this {\n this.defaultHandler = handler;\n return this;\n }\n\n /**\n * Verify (when secret configured), parse, and dispatch the delivery.\n * Returns 200-worthy result — throw NorbixWebhookSignatureError for 401.\n */\n async handle(input: NorbixWebhookHandleInput): Promise<NorbixWebhookHandleResult> {\n const deliveryHeaders = parseNorbixWebhookHeaders(input.headers);\n const shouldVerify = input.verify !== false && !!this.options.secret;\n let verified: boolean | null = null;\n\n if (shouldVerify && this.options.secret) {\n const result = verifyNorbixWebhookSignature({\n secret: this.options.secret,\n rawBody: input.rawBody,\n signature: deliveryHeaders.signature,\n timestamp: deliveryHeaders.timestamp,\n toleranceSeconds: this.options.toleranceSeconds ?? 300,\n });\n if (!result.ok) {\n throw new NorbixWebhookSignatureError(result.reason ?? 'Invalid signature');\n }\n verified = true;\n }\n\n const envelope = parseNorbixWebhookEnvelope(input.rawBody);\n const ctx: NorbixWebhookContext = {\n path: input.path,\n headers: {\n ...deliveryHeaders,\n event: deliveryHeaders.event ?? envelope.event,\n deliveryId: deliveryHeaders.deliveryId ?? envelope.id,\n accountId: deliveryHeaders.accountId ?? envelope.accountId,\n projectId: deliveryHeaders.projectId ?? envelope.projectId,\n },\n verified,\n };\n\n const handler = this.handlers.get(envelope.event) ?? this.defaultHandler;\n let handled = false;\n if (handler) {\n await handler(envelope, ctx);\n handled = true;\n }\n\n return {\n received: true,\n event: envelope.event,\n deliveryId: envelope.id,\n verified,\n handled,\n triggerId: envelope.triggerId,\n };\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/webhooks/errors.ts","../../src/webhooks/events.ts","../../src/webhooks/headers.ts","../../src/webhooks/normalize.ts","../../src/webhooks/parse.ts","../../src/webhooks/receiver.ts"],"names":["createHmac","timingSafeEqual"],"mappings":"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","file":"index.cjs","sourcesContent":["export class NorbixWebhookError extends Error {\n readonly code: string;\n\n constructor(message: string, code: string) {\n super(message);\n this.name = 'NorbixWebhookError';\n this.code = code;\n }\n}\n\nexport class NorbixWebhookSignatureError extends NorbixWebhookError {\n constructor(message: string) {\n super(message, 'WEBHOOK_SIGNATURE_INVALID');\n this.name = 'NorbixWebhookSignatureError';\n }\n}\n\nexport class NorbixWebhookParseError extends NorbixWebhookError {\n constructor(message: string) {\n super(message, 'WEBHOOK_PARSE_INVALID');\n this.name = 'NorbixWebhookParseError';\n }\n}\n","/**\n * Closed catalog of event names a destination may subscribe to.\n * Source: gateway Domain trigger event name value objects.\n */\nexport const NORBIX_WEBHOOK_EVENT_NAMES = [\n 'database.record.inserted',\n 'database.record.updated',\n 'database.record.deleted',\n 'database.record.replaced',\n 'database.record.responsibilityChanged',\n 'database.records.inserted',\n 'database.records.updated',\n 'database.records.deleted',\n 'membership.user.registered',\n 'membership.user.invited',\n 'membership.user.verified',\n 'membership.user.updated',\n 'membership.user.deleted',\n 'membership.user.blocked',\n 'membership.user.reactivated',\n 'files.file.uploaded',\n 'files.file.deleted',\n] as const;\n\nexport type NorbixWebhookEventName = (typeof NORBIX_WEBHOOK_EVENT_NAMES)[number];\n\n/**\n * Named event constants — use these instead of raw strings so app code gets\n * autocomplete and is typo-safe.\n *\n * @example\n * receiver.on(NorbixWebhookEvents.Membership.UserRegistered, (user, event) => {});\n */\nexport const NorbixWebhookEvents = {\n Database: {\n RecordInserted: 'database.record.inserted',\n RecordUpdated: 'database.record.updated',\n RecordDeleted: 'database.record.deleted',\n RecordReplaced: 'database.record.replaced',\n RecordResponsibilityChanged: 'database.record.responsibilityChanged',\n RecordsInserted: 'database.records.inserted',\n RecordsUpdated: 'database.records.updated',\n RecordsDeleted: 'database.records.deleted',\n },\n Membership: {\n UserRegistered: 'membership.user.registered',\n UserInvited: 'membership.user.invited',\n UserVerified: 'membership.user.verified',\n UserUpdated: 'membership.user.updated',\n UserDeleted: 'membership.user.deleted',\n UserBlocked: 'membership.user.blocked',\n UserReactivated: 'membership.user.reactivated',\n },\n Files: {\n FileUploaded: 'files.file.uploaded',\n FileDeleted: 'files.file.deleted',\n },\n} as const satisfies Record<string, Record<string, NorbixWebhookEventName>>;\n\nexport interface NorbixWebhookEventGroup {\n group: string;\n label: string;\n events: readonly string[];\n}\n\nexport const NORBIX_WEBHOOK_EVENT_GROUPS: NorbixWebhookEventGroup[] = [\n {\n group: 'database',\n label: 'Database',\n events: [\n 'database.record.inserted',\n 'database.record.updated',\n 'database.record.deleted',\n 'database.record.replaced',\n 'database.record.responsibilityChanged',\n 'database.records.inserted',\n 'database.records.updated',\n 'database.records.deleted',\n ],\n },\n {\n group: 'membership',\n label: 'Membership',\n events: [\n 'membership.user.registered',\n 'membership.user.invited',\n 'membership.user.verified',\n 'membership.user.updated',\n 'membership.user.deleted',\n 'membership.user.blocked',\n 'membership.user.reactivated',\n ],\n },\n {\n group: 'files',\n label: 'Files',\n events: ['files.file.uploaded', 'files.file.deleted'],\n },\n];\n","/**\n * Outbound Norbix webhook delivery headers.\n *\n * Source of truth: gateway `WebhookDeliveryClient` (NOT `AuthStatics` /\n * `ConfigureCors`, which define **inbound API** headers like\n * `norbix-account-id` / `norbix-project-id` for studio → gateway calls).\n *\n * @see gateway/src/Isidos.CodeMash.Services.Webhook/Dispatching/WebhookDeliveryClient.cs\n */\nexport const NORBIX_WEBHOOK_HEADERS = {\n event: 'X-Norbix-Event',\n delivery: 'X-Norbix-Delivery',\n idempotencyKey: 'Idempotency-Key',\n account: 'X-Norbix-Account',\n project: 'X-Norbix-Project',\n integration: 'X-Norbix-Integration',\n destination: 'X-Norbix-Destination',\n signature: 'X-Norbix-Signature',\n timestamp: 'X-Norbix-Timestamp',\n} as const;\n\nexport type NorbixWebhookHeaderName =\n (typeof NORBIX_WEBHOOK_HEADERS)[keyof typeof NORBIX_WEBHOOK_HEADERS];\n","import type { NorbixWebhookEventMetadata } from './event-data.js';\nimport type { NorbixWebhookEnvelope } from './types.js';\n\n/** Result of normalising a wire envelope for a typed handler. */\nexport interface NorbixWebhookNormalized {\n /** The payload-first value handed to a typed handler. */\n payload: unknown;\n /** Identifiers lifted off the wire payload. */\n metadata: NorbixWebhookEventMetadata;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Turn a raw wire envelope into `{ payload, metadata }`.\n *\n * - Entity events → `payload` is the entity (user / document / file).\n * - Mutation events → `payload` is `{ from, to }`.\n * - Batch events → `payload` is the array.\n *\n * Wrapper ids (record id, schema, user id, …) are moved onto `metadata`.\n * Unknown events fall back to `payload = envelope.data`, `metadata = {}`.\n */\nexport function normalizeNorbixWebhook(envelope: NorbixWebhookEnvelope): NorbixWebhookNormalized {\n const event = envelope.event;\n const data: unknown = envelope.data;\n const d = isObject(data) ? data : {};\n\n // Database events share schema/record metadata.\n if (event.startsWith('database.')) {\n const metadata: NorbixWebhookEventMetadata = {};\n if (typeof d.schemaName === 'string') {\n const schema = isObject(d.schema) ? d.schema : null;\n const schemaId = schema && typeof schema.id === 'string' ? schema.id : null;\n metadata.schema = { id: schemaId, name: d.schemaName };\n }\n if (typeof d.integrationId === 'string') metadata.integrationId = d.integrationId;\n if (typeof d.id === 'string') metadata.record = { id: d.id };\n if (Array.isArray(d.ids)) metadata.records = { ids: d.ids as string[] };\n\n switch (event) {\n case 'database.record.inserted':\n case 'database.record.deleted':\n return { payload: d.document, metadata };\n case 'database.record.updated':\n case 'database.record.replaced':\n return { payload: { from: d.from, to: d.to }, metadata };\n case 'database.records.inserted':\n return { payload: d.documents ?? [], metadata };\n default:\n // records.updated / records.deleted / responsibilityChanged — pass data through.\n return { payload: data, metadata };\n }\n }\n\n // Membership events: entity directly, except updated (mutation).\n if (event.startsWith('membership.')) {\n const metadata: NorbixWebhookEventMetadata = {};\n if (typeof d.id === 'string') metadata.user = { id: d.id };\n\n switch (event) {\n case 'membership.user.registered':\n case 'membership.user.verified':\n case 'membership.user.blocked':\n case 'membership.user.reactivated':\n // wire sends { id, to } / { id, from?, to } — the entity is `to`.\n return { payload: d.to, metadata };\n case 'membership.user.deleted':\n // wire sends { id, from } — the entity is `from`.\n return { payload: d.from, metadata };\n case 'membership.user.updated':\n return { payload: { from: d.from, to: d.to }, metadata };\n case 'membership.user.invited':\n return { payload: { email: d.email }, metadata };\n default:\n return { payload: data, metadata };\n }\n }\n\n // Files events.\n if (event.startsWith('files.')) {\n const metadata: NorbixWebhookEventMetadata = {};\n if (typeof d.integrationId === 'string') metadata.integrationId = d.integrationId;\n\n switch (event) {\n case 'files.file.uploaded':\n return { payload: d.file, metadata };\n case 'files.file.deleted':\n return { payload: { path: d.path }, metadata };\n default:\n return { payload: data, metadata };\n }\n }\n\n return { payload: data, metadata: {} };\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto';\n\nimport { NorbixWebhookParseError } from './errors.js';\nimport { NORBIX_WEBHOOK_HEADERS } from './headers.js';\nimport type {\n NorbixWebhookDeliveryHeaders,\n NorbixWebhookEnvelope,\n NorbixWebhookHeaderBag,\n NorbixWebhookVerifyOptions,\n} from './types.js';\n\nfunction headerValue(headers: NorbixWebhookHeaderBag, name: string): string | null {\n if (headers instanceof Headers) {\n return headers.get(name);\n }\n\n const direct = headers[name];\n if (direct !== undefined) {\n return Array.isArray(direct) ? (direct[0] ?? null) : direct;\n }\n\n const lower = name.toLowerCase();\n const lowerDirect = headers[lower];\n if (lowerDirect !== undefined) {\n return Array.isArray(lowerDirect) ? (lowerDirect[0] ?? null) : lowerDirect;\n }\n\n // Some frameworks preserve original casing on header keys.\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === lower) {\n return Array.isArray(value) ? (value[0] ?? null) : (value ?? null);\n }\n }\n\n return null;\n}\n\n/** Read Norbix delivery headers from an incoming HTTP request. */\nexport function parseNorbixWebhookHeaders(\n headers: NorbixWebhookHeaderBag,\n): NorbixWebhookDeliveryHeaders {\n const deliveryId =\n headerValue(headers, NORBIX_WEBHOOK_HEADERS.delivery) ??\n headerValue(headers, NORBIX_WEBHOOK_HEADERS.idempotencyKey);\n\n return {\n event: headerValue(headers, NORBIX_WEBHOOK_HEADERS.event),\n deliveryId,\n idempotencyKey: headerValue(headers, NORBIX_WEBHOOK_HEADERS.idempotencyKey),\n accountId: headerValue(headers, NORBIX_WEBHOOK_HEADERS.account),\n projectId: headerValue(headers, NORBIX_WEBHOOK_HEADERS.project),\n integrationId: headerValue(headers, NORBIX_WEBHOOK_HEADERS.integration),\n destinationId: headerValue(headers, NORBIX_WEBHOOK_HEADERS.destination),\n signature: headerValue(headers, NORBIX_WEBHOOK_HEADERS.signature),\n timestamp: headerValue(headers, NORBIX_WEBHOOK_HEADERS.timestamp),\n };\n}\n\n/** Parse the JSON envelope from the raw POST body. */\nexport function parseNorbixWebhookEnvelope<TData = unknown>(\n rawBody: string,\n): NorbixWebhookEnvelope<TData> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawBody);\n } catch {\n throw new NorbixWebhookParseError('Webhook body is not valid JSON');\n }\n\n if (!parsed || typeof parsed !== 'object') {\n throw new NorbixWebhookParseError('Webhook body must be a JSON object');\n }\n\n const envelope = parsed as Partial<NorbixWebhookEnvelope<TData>>;\n if (typeof envelope.id !== 'string' || !envelope.id) {\n throw new NorbixWebhookParseError('Webhook envelope missing id');\n }\n if (typeof envelope.event !== 'string' || !envelope.event) {\n throw new NorbixWebhookParseError('Webhook envelope missing event');\n }\n\n return envelope as NorbixWebhookEnvelope<TData>;\n}\n\nexport interface NorbixWebhookSignatureVerification {\n ok: boolean;\n reason?: string;\n}\n\n/**\n * Verify X-Norbix-Signature against the raw body.\n * Algorithm (gateway WebhookDeliveryClient.Sign):\n * sha256=<hex> HMAC-SHA256(secret, \"<timestamp>.<rawBody>\")\n */\nexport function verifyNorbixWebhookSignature(\n input: {\n rawBody: string;\n signature?: string | null;\n timestamp?: string | null;\n } & NorbixWebhookVerifyOptions,\n): NorbixWebhookSignatureVerification {\n const { secret, rawBody, signature, timestamp, toleranceSeconds = 300 } = input;\n\n if (!signature) {\n return { ok: false, reason: 'missing X-Norbix-Signature header' };\n }\n if (!timestamp) {\n return { ok: false, reason: 'missing X-Norbix-Timestamp header' };\n }\n\n if (toleranceSeconds > 0) {\n const sent = Number(timestamp);\n if (!Number.isFinite(sent)) {\n return { ok: false, reason: 'X-Norbix-Timestamp is not a number' };\n }\n const ageSeconds = Math.abs(Date.now() / 1000 - sent);\n if (ageSeconds > toleranceSeconds) {\n return {\n ok: false,\n reason: `timestamp outside ${toleranceSeconds}s tolerance (age ${Math.round(ageSeconds)}s)`,\n };\n }\n }\n\n const expected = computeNorbixWebhookSignature(secret, timestamp, rawBody);\n if (!timingSafeEqualUtf8(expected, signature)) {\n return { ok: false, reason: 'signature mismatch' };\n }\n\n return { ok: true };\n}\n\nexport function computeNorbixWebhookSignature(\n secret: string,\n timestamp: string,\n rawBody: string,\n): string {\n return `sha256=${hmacSha256Hex(secret, `${timestamp}.${rawBody}`)}`;\n}\n\nfunction hmacSha256Hex(secret: string, payload: string): string {\n return createHmac('sha256', secret).update(payload, 'utf8').digest('hex');\n}\n\nfunction timingSafeEqualUtf8(a: string, b: string): boolean {\n const bufA = Buffer.from(a, 'utf8');\n const bufB = Buffer.from(b, 'utf8');\n if (bufA.length !== bufB.length) return false;\n return timingSafeEqual(bufA, bufB);\n}\n","import type { CodeMashHub2 } from '../types/hub2.dtos.js';\n\nimport { NorbixWebhookSignatureError } from './errors.js';\nimport type { NorbixWebhookPayload } from './event-data.js';\nimport type { NorbixWebhookEventName } from './events.js';\nimport { normalizeNorbixWebhook } from './normalize.js';\nimport {\n parseNorbixWebhookEnvelope,\n parseNorbixWebhookHeaders,\n verifyNorbixWebhookSignature,\n} from './parse.js';\nimport type { NorbixWebhookMutation } from './payloads.js';\nimport type {\n NorbixWebhookContext,\n NorbixWebhookEvent,\n NorbixWebhookHandleInput,\n NorbixWebhookHandleResult,\n NorbixWebhookHandler,\n NorbixWebhookRawHandler,\n NorbixWebhookReceiverOptions,\n} from './types.js';\n\ninterface ResolvedConfig {\n secret?: string;\n toleranceSeconds: number;\n projectId?: string;\n accountId?: string;\n}\n\nfunction readEnv(): Record<string, string | undefined> {\n return typeof process !== 'undefined' && process.env ? process.env : {};\n}\n\nfunction toNumber(value: string | undefined): number | undefined {\n if (value == null || value === '') return undefined;\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\nfunction resolveConfig(options: NorbixWebhookReceiverOptions): ResolvedConfig {\n const env = readEnv();\n const tolerance = options.toleranceSeconds ?? toNumber(env.NORBIX_WEBHOOK_TOLERANCE_SECONDS);\n return {\n secret: options.secret ?? env.NORBIX_WEBHOOK_SIGNING_SECRET,\n projectId: options.projectId ?? env.NORBIX_PROJECT_ID,\n accountId: options.accountId ?? env.NORBIX_ACCOUNT_ID,\n toleranceSeconds: Number.isFinite(tolerance) ? (tolerance as number) : 300,\n };\n}\n\n/** Internal record of a registered handler and how to invoke it. */\ninterface Registration {\n raw: boolean;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- holds either handler shape\n fn: (...args: any[]) => void | Promise<void>;\n}\n\n/**\n * Register handlers for inbound Norbix webhook deliveries (trigger → destination POST).\n *\n * Verifies the HMAC signature, parses the envelope, normalises the payload, and\n * dispatches to per-event handlers.\n *\n * @example\n * ```ts\n * import { NorbixWebhookReceiver, NorbixWebhookEvents } from '@norbix.ai/ts/webhooks';\n * import type { CodeMashHub2 } from '@norbix.ai/ts/types/hub';\n *\n * const receiver = new NorbixWebhookReceiver(); // reads env\n *\n * // Typed: first arg IS the payload, second is metadata.\n * receiver.on<CodeMashHub2.UserDto>(\n * NorbixWebhookEvents.Membership.UserRegistered,\n * (user, event) => {\n * user.email ?? user.userName;\n * event.metadata.user?.id;\n * },\n * );\n *\n * // Mutation: payload is { from, to }.\n * receiver.on(NorbixWebhookEvents.Membership.UserUpdated, (user, event) => {\n * if (user.from.email !== user.to.email) { ... }\n * });\n *\n * // Raw escape hatch: (envelope, ctx).\n * receiver.onRaw(NorbixWebhookEvents.Files.FileUploaded, (envelope, ctx) => {});\n *\n * await receiver.handle({ rawBody, headers: req.headers });\n * ```\n */\nexport class NorbixWebhookReceiver {\n private readonly handlers = new Map<string, Registration>();\n private defaultHandler?: Registration;\n private readonly config: ResolvedConfig;\n\n constructor(options: NorbixWebhookReceiverOptions = {}) {\n this.config = resolveConfig(options);\n }\n\n /* ---- Typed handlers: (payload, event) ---- */\n\n /**\n * Membership user entity events (create / delete / state flip) — `payload`\n * is the user directly. Pass the entity type as the generic, e.g.\n * `on<CodeMashHub2.UserDto>(NorbixWebhookEvents.Membership.UserRegistered, ...)`.\n */\n on<TUser = CodeMashHub2.UserDto>(\n event:\n | 'membership.user.registered'\n | 'membership.user.verified'\n | 'membership.user.blocked'\n | 'membership.user.reactivated'\n | 'membership.user.deleted',\n handler: NorbixWebhookHandler<TUser>,\n ): this;\n /** Membership user-updated mutation — `payload` is `{ from, to }`. */\n on<TUser = CodeMashHub2.UserDto>(\n event: 'membership.user.updated',\n handler: NorbixWebhookHandler<NorbixWebhookMutation<TUser>>,\n ): this;\n /** Database record mutation — pass the document type; `payload` is `{ from, to }`. */\n on<TDocument>(\n event: 'database.record.updated' | 'database.record.replaced',\n handler: NorbixWebhookHandler<NorbixWebhookMutation<TDocument>>,\n ): this;\n /** Database single-record entity — pass the document type; `payload` is the document. */\n on<TDocument>(\n event: 'database.record.inserted' | 'database.record.deleted',\n handler: NorbixWebhookHandler<TDocument>,\n ): this;\n /** Database batch insert — pass the document type; `payload` is the array. */\n on<TDocument>(\n event: 'database.records.inserted',\n handler: NorbixWebhookHandler<TDocument[]>,\n ): this;\n /** Any known catalog event — `payload` typed from the payload map. */\n on<E extends NorbixWebhookEventName>(\n event: E,\n handler: NorbixWebhookHandler<NorbixWebhookPayload<E>>,\n ): this;\n /** Any event name — untyped payload. */\n on(event: string, handler: NorbixWebhookHandler): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- overloads pass narrower handlers\n on(event: string, handler: NorbixWebhookHandler<any>): this {\n this.handlers.set(event, { raw: false, fn: handler });\n return this;\n }\n\n /** Register the typed handler for many events at once (skips already-registered). */\n onEach(events: readonly string[], handler: NorbixWebhookHandler): this {\n for (const event of events) {\n if (!this.handlers.has(event)) this.handlers.set(event, { raw: false, fn: handler });\n }\n return this;\n }\n\n /* ---- Raw handlers: (envelope, ctx) ---- */\n\n /** Raw handler for one event — receives the envelope and delivery context. */\n onRaw<TData = unknown>(event: string, handler: NorbixWebhookRawHandler<TData>): this {\n this.handlers.set(event, { raw: true, fn: handler as NorbixWebhookRawHandler });\n return this;\n }\n\n /** Register a raw handler for many events at once (skips already-registered). */\n onEachRaw(events: readonly string[], handler: NorbixWebhookRawHandler): this {\n for (const event of events) {\n if (!this.handlers.has(event)) this.handlers.set(event, { raw: true, fn: handler });\n }\n return this;\n }\n\n /** Fallback (raw) when no event-specific handler is registered. */\n onDefault(handler: NorbixWebhookRawHandler): this {\n this.defaultHandler = { raw: true, fn: handler };\n return this;\n }\n\n /**\n * Verify (when secret configured), parse, normalise, and dispatch the delivery.\n * Throws NorbixWebhookSignatureError (401-worthy) on bad signature or guard\n * mismatch; otherwise returns a 200-worthy result.\n */\n async handle(input: NorbixWebhookHandleInput): Promise<NorbixWebhookHandleResult> {\n const deliveryHeaders = parseNorbixWebhookHeaders(input.headers);\n const shouldVerify = input.verify !== false && !!this.config.secret;\n let verified: boolean | null = null;\n\n if (shouldVerify && this.config.secret) {\n const result = verifyNorbixWebhookSignature({\n secret: this.config.secret,\n rawBody: input.rawBody,\n signature: deliveryHeaders.signature,\n timestamp: deliveryHeaders.timestamp,\n toleranceSeconds: this.config.toleranceSeconds,\n });\n if (!result.ok) {\n throw new NorbixWebhookSignatureError(result.reason ?? 'Invalid signature');\n }\n verified = true;\n }\n\n const envelope = parseNorbixWebhookEnvelope(input.rawBody);\n\n if (this.config.projectId && envelope.projectId !== this.config.projectId) {\n throw new NorbixWebhookSignatureError(\n `delivery projectId ${envelope.projectId} does not match configured ${this.config.projectId}`,\n );\n }\n if (this.config.accountId && envelope.accountId !== this.config.accountId) {\n throw new NorbixWebhookSignatureError(\n `delivery accountId ${envelope.accountId} does not match configured ${this.config.accountId}`,\n );\n }\n\n const ctx: NorbixWebhookContext = {\n path: input.path,\n headers: {\n ...deliveryHeaders,\n event: deliveryHeaders.event ?? envelope.event,\n deliveryId: deliveryHeaders.deliveryId ?? envelope.id,\n accountId: deliveryHeaders.accountId ?? envelope.accountId,\n projectId: deliveryHeaders.projectId ?? envelope.projectId,\n },\n verified,\n };\n\n const registration = this.handlers.get(envelope.event) ?? this.defaultHandler;\n let handled = false;\n\n if (registration) {\n if (registration.raw) {\n await registration.fn(envelope, ctx);\n } else {\n const { payload, metadata } = normalizeNorbixWebhook(envelope);\n const event: NorbixWebhookEvent = {\n name: envelope.event,\n deliveryId: envelope.id,\n createdOn: envelope.createdOn,\n triggerId: envelope.triggerId ?? null,\n correlationId: null,\n accountId: ctx.headers.accountId ?? envelope.accountId,\n projectId: ctx.headers.projectId ?? envelope.projectId,\n integrationId: ctx.headers.integrationId,\n destinationId: ctx.headers.destinationId,\n verified,\n metadata,\n raw: envelope,\n };\n await registration.fn(payload, event);\n }\n handled = true;\n }\n\n return {\n received: true,\n event: envelope.event,\n deliveryId: envelope.id,\n verified,\n handled,\n triggerId: envelope.triggerId,\n };\n }\n}\n"]}