@norbix.ai/ts 1.1.1 → 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.
- package/dist/webhooks/index.cjs +177 -17
- package/dist/webhooks/index.cjs.map +1 -1
- package/dist/webhooks/index.d.cts +322 -22
- package/dist/webhooks/index.d.ts +322 -22
- package/dist/webhooks/index.js +176 -18
- package/dist/webhooks/index.js.map +1 -1
- package/package.json +11 -4
package/dist/webhooks/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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.
|
|
340
|
+
const shouldVerify = input.verify !== false && !!this.config.secret;
|
|
212
341
|
let verified = null;
|
|
213
|
-
if (shouldVerify && this.
|
|
342
|
+
if (shouldVerify && this.config.secret) {
|
|
214
343
|
const result = verifyNorbixWebhookSignature({
|
|
215
|
-
secret: this.
|
|
344
|
+
secret: this.config.secret,
|
|
216
345
|
rawBody: input.rawBody,
|
|
217
346
|
signature: deliveryHeaders.signature,
|
|
218
347
|
timestamp: deliveryHeaders.timestamp,
|
|
219
|
-
toleranceSeconds: this.
|
|
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
|
|
377
|
+
const registration = this.handlers.get(envelope.event) ?? this.defaultHandler;
|
|
239
378
|
let handled = false;
|
|
240
|
-
if (
|
|
241
|
-
|
|
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"]}
|