@hookflo/tern 2.0.0 → 2.0.2

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/README.md CHANGED
@@ -3,6 +3,10 @@
3
3
  A robust, algorithm-agnostic webhook verification framework that supports multiple platforms with accurate signature verification and payload retrieval.
4
4
  The same framework that secures webhook verification at [Hookflo](https://hookflo.com).
5
5
 
6
+ ⭐ Star this repo to support the project and help others discover it!
7
+
8
+ 💬 Join the discussion & contribute in our Discord: [Hookflo Community](https://discord.com/invite/SNmCjU97nr)
9
+
6
10
  ```bash
7
11
  npm install @hookflo/tern
8
12
  ```
@@ -24,6 +28,12 @@ Supports HMAC-SHA256, HMAC-SHA1, HMAC-SHA512, and custom algorithms
24
28
  - **Flexible Configuration**: Custom signature configurations for any webhook format
25
29
  - **Type Safe**: Full TypeScript support with comprehensive type definitions
26
30
  - **Framework Agnostic**: Works with Express.js, Next.js, Cloudflare Workers, and more
31
+ - **Body-Parser Safe Adapters**: Read raw request bodies correctly to avoid signature mismatch issues
32
+ - **Multi-Provider Verification**: Verify and auto-detect across multiple providers with one API
33
+ - **Payload Normalization**: Opt-in normalized event shape to reduce provider lock-in
34
+ - **Category-aware Migration**: Normalize within provider categories (payment/auth/infrastructure) for safe platform switching
35
+ - **Strong Typed Normalized Schemas**: Category types like `PaymentWebhookNormalized` and `AuthWebhookNormalized` for safe migrations
36
+ - **Foundational Error Taxonomy**: Stable `errorCode` values (`INVALID_SIGNATURE`, `MISSING_SIGNATURE`, etc.)
27
37
 
28
38
  ## Why Tern?
29
39
 
@@ -65,6 +75,78 @@ if (result.isValid) {
65
75
  }
66
76
  ```
67
77
 
78
+ ### Universal Verification (auto-detect platform)
79
+
80
+ ```typescript
81
+ import { WebhookVerificationService } from '@hookflo/tern';
82
+
83
+ const result = await WebhookVerificationService.verifyAny(request, {
84
+ stripe: process.env.STRIPE_WEBHOOK_SECRET,
85
+ github: process.env.GITHUB_WEBHOOK_SECRET,
86
+ clerk: process.env.CLERK_WEBHOOK_SECRET,
87
+ });
88
+
89
+ if (result.isValid) {
90
+ console.log(`Verified ${result.platform} webhook`);
91
+ }
92
+ ```
93
+
94
+ ### Category-aware Payload Normalization
95
+
96
+ ### Strongly-Typed Normalized Payloads
97
+
98
+ ```typescript
99
+ import {
100
+ WebhookVerificationService,
101
+ PaymentWebhookNormalized,
102
+ } from '@hookflo/tern';
103
+
104
+ const result = await WebhookVerificationService.verifyWithPlatformConfig<PaymentWebhookNormalized>(
105
+ request,
106
+ 'stripe',
107
+ process.env.STRIPE_WEBHOOK_SECRET!,
108
+ 300,
109
+ { enabled: true, category: 'payment' },
110
+ );
111
+
112
+ if (result.isValid && result.payload?.event === 'payment.succeeded') {
113
+ // result.payload is strongly typed
114
+ console.log(result.payload.amount, result.payload.customer_id);
115
+ }
116
+ ```
117
+
118
+ ```typescript
119
+ import { WebhookVerificationService, getPlatformsByCategory } from '@hookflo/tern';
120
+
121
+ // Discover migration-compatible providers in the same category
122
+ const paymentPlatforms = getPlatformsByCategory('payment');
123
+ // ['stripe', 'polar', ...]
124
+
125
+ const result = await WebhookVerificationService.verifyWithPlatformConfig(
126
+ request,
127
+ 'stripe',
128
+ process.env.STRIPE_WEBHOOK_SECRET!,
129
+ 300,
130
+ {
131
+ enabled: true,
132
+ category: 'payment',
133
+ includeRaw: true,
134
+ },
135
+ );
136
+
137
+ console.log(result.payload);
138
+ // {
139
+ // event: 'payment.succeeded',
140
+ // amount: 5000,
141
+ // currency: 'USD',
142
+ // customer_id: 'cus_123',
143
+ // transaction_id: 'pi_123',
144
+ // provider: 'stripe',
145
+ // category: 'payment',
146
+ // _raw: {...}
147
+ // }
148
+ ```
149
+
68
150
  ### Platform-Specific Usage
69
151
 
70
152
  ```typescript
@@ -132,6 +214,7 @@ const result = await WebhookVerificationService.verify(request, stripeConfig);
132
214
  - **Vercel**: HMAC-SHA256
133
215
  - **Polar**: HMAC-SHA256
134
216
  - **Supabase**: Token-based authentication
217
+ - **GitLab**: Token-based authentication
135
218
 
136
219
  ## Custom Platform Configuration
137
220
 
@@ -231,80 +314,59 @@ const timestampedConfig = {
231
314
 
232
315
  ## Framework Integration
233
316
 
234
- ### Express.js
317
+ ### Express.js middleware (body-parser safe)
235
318
 
236
319
  ```typescript
237
- app.post('/webhooks/stripe', async (req, res) => {
238
- const result = await WebhookVerificationService.verifyWithPlatformConfig(
239
- req,
240
- 'stripe',
241
- process.env.STRIPE_WEBHOOK_SECRET
242
- );
243
-
244
- if (!result.isValid) {
245
- return res.status(400).json({ error: result.error });
246
- }
247
-
248
- // Process the webhook
249
- console.log('Stripe event:', result.payload.type);
250
- res.json({ received: true });
251
- });
320
+ import express from 'express';
321
+ import { createWebhookMiddleware } from '@hookflo/tern/express';
322
+
323
+ const app = express();
324
+
325
+ app.post(
326
+ '/webhooks/stripe',
327
+ createWebhookMiddleware({
328
+ platform: 'stripe',
329
+ secret: process.env.STRIPE_WEBHOOK_SECRET!,
330
+ normalize: true,
331
+ }),
332
+ (req, res) => {
333
+ const event = (req as any).webhook.payload;
334
+ res.json({ received: true, event: event.event });
335
+ },
336
+ );
252
337
  ```
253
338
 
254
- ### Next.js API Route
339
+ ### Next.js App Router
255
340
 
256
341
  ```typescript
257
- // pages/api/webhooks/github.js
258
- export default async function handler(req, res) {
259
- if (req.method !== 'POST') {
260
- return res.status(405).json({ error: 'Method not allowed' });
261
- }
262
-
263
- const result = await WebhookVerificationService.verifyWithPlatformConfig(
264
- req,
265
- 'github',
266
- process.env.GITHUB_WEBHOOK_SECRET
267
- );
342
+ import { createWebhookHandler } from '@hookflo/tern/nextjs';
268
343
 
269
- if (!result.isValid) {
270
- return res.status(400).json({ error: result.error });
271
- }
272
-
273
- // Handle GitHub webhook
274
- const event = req.headers['x-github-event'];
275
- console.log('GitHub event:', event);
276
-
277
- res.json({ received: true });
278
- }
344
+ export const POST = createWebhookHandler({
345
+ platform: 'github',
346
+ secret: process.env.GITHUB_WEBHOOK_SECRET!,
347
+ handler: async (payload) => ({ received: true, event: payload.event ?? payload.type }),
348
+ });
279
349
  ```
280
350
 
281
351
  ### Cloudflare Workers
282
352
 
283
353
  ```typescript
284
- addEventListener('fetch', event => {
285
- event.respondWith(handleRequest(event.request));
354
+ import { createWebhookHandler } from '@hookflo/tern/cloudflare';
355
+
356
+ const handleStripe = createWebhookHandler({
357
+ platform: 'stripe',
358
+ secretEnv: 'STRIPE_WEBHOOK_SECRET',
359
+ handler: async (payload) => ({ received: true, event: payload.event ?? payload.type }),
286
360
  });
287
361
 
288
- async function handleRequest(request) {
289
- if (request.url.includes('/webhooks/clerk')) {
290
- const result = await WebhookVerificationService.verifyWithPlatformConfig(
291
- request,
292
- 'clerk',
293
- CLERK_WEBHOOK_SECRET
294
- );
295
-
296
- if (!result.isValid) {
297
- return new Response(JSON.stringify({ error: result.error }), {
298
- status: 400,
299
- headers: { 'Content-Type': 'application/json' }
300
- });
362
+ export default {
363
+ async fetch(request: Request, env: Record<string, string>) {
364
+ if (new URL(request.url).pathname === '/webhooks/stripe') {
365
+ return handleStripe(request, env);
301
366
  }
302
-
303
- // Process Clerk webhook
304
- console.log('Clerk event:', result.payload.type);
305
- return new Response(JSON.stringify({ received: true }));
306
- }
307
- }
367
+ return new Response('Not Found', { status: 404 });
368
+ },
369
+ };
308
370
  ```
309
371
 
310
372
  ## API Reference
@@ -315,14 +377,22 @@ async function handleRequest(request) {
315
377
 
316
378
  Verifies a webhook using the provided configuration.
317
379
 
318
- #### `verifyWithPlatformConfig(request: Request, platform: WebhookPlatform, secret: string, toleranceInSeconds?: number): Promise<WebhookVerificationResult>`
380
+ #### `verifyWithPlatformConfig(request: Request, platform: WebhookPlatform, secret: string, toleranceInSeconds?: number, normalize?: boolean | NormalizeOptions): Promise<WebhookVerificationResult>`
381
+
382
+ Simplified verification using platform-specific configurations with optional payload normalization.
383
+
384
+ #### `verifyAny(request: Request, secrets: Record<string, string>, toleranceInSeconds?: number, normalize?: boolean | NormalizeOptions): Promise<WebhookVerificationResult>`
319
385
 
320
- Simplified verification using platform-specific configurations.
386
+ Auto-detects platform from headers and verifies against one or more provider secrets.
321
387
 
322
388
  #### `verifyTokenBased(request: Request, webhookId: string, webhookToken: string): Promise<WebhookVerificationResult>`
323
389
 
324
390
  Verifies token-based webhooks (like Supabase).
325
391
 
392
+ #### `getPlatformsByCategory(category: 'payment' | 'auth' | 'ecommerce' | 'infrastructure'): WebhookPlatform[]`
393
+
394
+ Returns built-in providers that normalize into a shared schema for the given migration category.
395
+
326
396
  ### Types
327
397
 
328
398
  #### `WebhookVerificationResult`
@@ -331,6 +401,7 @@ Verifies token-based webhooks (like Supabase).
331
401
  interface WebhookVerificationResult {
332
402
  isValid: boolean;
333
403
  error?: string;
404
+ errorCode?: WebhookErrorCode;
334
405
  platform: WebhookPlatform;
335
406
  payload?: any;
336
407
  metadata?: {
@@ -349,6 +420,7 @@ interface WebhookConfig {
349
420
  secret: string;
350
421
  toleranceInSeconds?: number;
351
422
  signatureConfig?: SignatureConfig;
423
+ normalize?: boolean | NormalizeOptions;
352
424
  }
353
425
  ```
354
426
 
@@ -422,4 +494,55 @@ MIT License - see [LICENSE](./LICENSE) for details.
422
494
 
423
495
  - [Documentation](./USAGE.md)
424
496
  - [Framework Summary](./FRAMEWORK_SUMMARY.md)
497
+ - [Architecture Guide](./ARCHITECTURE.md)
425
498
  - [Issues](https://github.com/Hookflo/tern/issues)
499
+
500
+
501
+ ## Troubleshooting
502
+
503
+ ### `Module not found: Can't resolve "@hookflo/tern/nextjs"`
504
+
505
+ If this happens in a Next.js project, it usually means one of these:
506
+
507
+ 1. You installed an older published package version that does not include subpath exports yet.
508
+ 2. Lockfile still points to an old tarball/version.
509
+ 3. `node_modules` cache is stale after upgrading.
510
+
511
+ Fix steps:
512
+
513
+ ```bash
514
+ # in your Next.js app
515
+ npm i @hookflo/tern@latest
516
+ rm -rf node_modules package-lock.json .next
517
+ npm i
518
+ ```
519
+
520
+ Then verify resolution:
521
+
522
+ ```bash
523
+ node -e "console.log(require.resolve('@hookflo/tern/nextjs'))"
524
+ ```
525
+
526
+ If you are testing this repo locally before publish:
527
+
528
+ ```bash
529
+ # inside /workspace/tern
530
+ npm run build
531
+ npm pack
532
+
533
+ # inside your other project
534
+ npm i /path/to/hookflo-tern-<version>.tgz
535
+ ```
536
+
537
+ Minimal Next.js App Router usage:
538
+
539
+ ```ts
540
+ import { createWebhookHandler } from '@hookflo/tern/nextjs';
541
+
542
+ export const POST = createWebhookHandler({
543
+ platform: 'stripe',
544
+ secret: process.env.STRIPE_WEBHOOK_SECRET!,
545
+ handler: async (payload) => ({ received: true, event: payload.event ?? payload.type }),
546
+ });
547
+ ```
548
+
@@ -0,0 +1,11 @@
1
+ import { WebhookPlatform, NormalizeOptions } from '../types';
2
+ export interface CloudflareWebhookHandlerOptions<TEnv = Record<string, unknown>, TResponse = unknown> {
3
+ platform: WebhookPlatform;
4
+ secret?: string;
5
+ secretEnv?: string;
6
+ toleranceInSeconds?: number;
7
+ normalize?: boolean | NormalizeOptions;
8
+ onError?: (error: Error) => void;
9
+ handler: (payload: any, env: TEnv, metadata: Record<string, any>) => Promise<TResponse> | TResponse;
10
+ }
11
+ export declare function createWebhookHandler<TEnv = Record<string, unknown>, TResponse = unknown>(options: CloudflareWebhookHandlerOptions<TEnv, TResponse>): (request: Request, env: TEnv) => Promise<Response>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWebhookHandler = createWebhookHandler;
4
+ const index_1 = require("../index");
5
+ function createWebhookHandler(options) {
6
+ return async (request, env) => {
7
+ try {
8
+ const secret = options.secret
9
+ || (options.secretEnv ? env[options.secretEnv] : undefined);
10
+ if (!secret) {
11
+ return Response.json({ error: 'Webhook secret is not configured' }, { status: 500 });
12
+ }
13
+ const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(request, options.platform, secret, options.toleranceInSeconds, options.normalize);
14
+ if (!result.isValid) {
15
+ return Response.json({ error: result.error, platform: result.platform }, { status: 400 });
16
+ }
17
+ const data = await options.handler(result.payload, env, result.metadata || {});
18
+ return Response.json(data);
19
+ }
20
+ catch (error) {
21
+ options.onError?.(error);
22
+ return Response.json({ error: error.message }, { status: 500 });
23
+ }
24
+ };
25
+ }
@@ -0,0 +1,18 @@
1
+ import { WebhookPlatform, WebhookVerificationResult, NormalizeOptions } from '../types';
2
+ import { MinimalNodeRequest } from './shared';
3
+ export interface ExpressLikeResponse {
4
+ status: (code: number) => ExpressLikeResponse;
5
+ json: (payload: unknown) => unknown;
6
+ }
7
+ export interface ExpressLikeRequest extends MinimalNodeRequest {
8
+ webhook?: WebhookVerificationResult;
9
+ }
10
+ export type ExpressLikeNext = () => void;
11
+ export interface ExpressWebhookMiddlewareOptions {
12
+ platform: WebhookPlatform;
13
+ secret: string;
14
+ toleranceInSeconds?: number;
15
+ normalize?: boolean | NormalizeOptions;
16
+ onError?: (error: Error) => void;
17
+ }
18
+ export declare function createWebhookMiddleware(options: ExpressWebhookMiddlewareOptions): (req: ExpressLikeRequest, res: ExpressLikeResponse, next: ExpressLikeNext) => Promise<void>;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWebhookMiddleware = createWebhookMiddleware;
4
+ const index_1 = require("../index");
5
+ const shared_1 = require("./shared");
6
+ function createWebhookMiddleware(options) {
7
+ return async (req, res, next) => {
8
+ try {
9
+ const webRequest = await (0, shared_1.toWebRequest)(req);
10
+ const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(webRequest, options.platform, options.secret, options.toleranceInSeconds, options.normalize);
11
+ if (!result.isValid) {
12
+ res.status(400).json({ error: result.error, platform: result.platform });
13
+ return;
14
+ }
15
+ req.webhook = result;
16
+ next();
17
+ }
18
+ catch (error) {
19
+ options.onError?.(error);
20
+ res.status(500).json({ error: error.message });
21
+ }
22
+ };
23
+ }
@@ -0,0 +1,4 @@
1
+ export { createWebhookMiddleware, ExpressWebhookMiddlewareOptions, ExpressLikeRequest, ExpressLikeResponse, } from './express';
2
+ export { createWebhookHandler as createNextjsWebhookHandler, NextWebhookHandlerOptions, } from './nextjs';
3
+ export { createWebhookHandler as createCloudflareWebhookHandler, CloudflareWebhookHandlerOptions, } from './cloudflare';
4
+ export { toWebRequest, extractRawBody } from './shared';
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractRawBody = exports.toWebRequest = exports.createCloudflareWebhookHandler = exports.createNextjsWebhookHandler = exports.createWebhookMiddleware = void 0;
4
+ var express_1 = require("./express");
5
+ Object.defineProperty(exports, "createWebhookMiddleware", { enumerable: true, get: function () { return express_1.createWebhookMiddleware; } });
6
+ var nextjs_1 = require("./nextjs");
7
+ Object.defineProperty(exports, "createNextjsWebhookHandler", { enumerable: true, get: function () { return nextjs_1.createWebhookHandler; } });
8
+ var cloudflare_1 = require("./cloudflare");
9
+ Object.defineProperty(exports, "createCloudflareWebhookHandler", { enumerable: true, get: function () { return cloudflare_1.createWebhookHandler; } });
10
+ var shared_1 = require("./shared");
11
+ Object.defineProperty(exports, "toWebRequest", { enumerable: true, get: function () { return shared_1.toWebRequest; } });
12
+ Object.defineProperty(exports, "extractRawBody", { enumerable: true, get: function () { return shared_1.extractRawBody; } });
@@ -0,0 +1,10 @@
1
+ import { WebhookPlatform, NormalizeOptions } from '../types';
2
+ export interface NextWebhookHandlerOptions<TResponse = unknown> {
3
+ platform: WebhookPlatform;
4
+ secret: string;
5
+ toleranceInSeconds?: number;
6
+ normalize?: boolean | NormalizeOptions;
7
+ onError?: (error: Error) => void;
8
+ handler: (payload: any, metadata: Record<string, any>) => Promise<TResponse> | TResponse;
9
+ }
10
+ export declare function createWebhookHandler<TResponse = unknown>(options: NextWebhookHandlerOptions<TResponse>): (request: Request) => Promise<Response>;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWebhookHandler = createWebhookHandler;
4
+ const index_1 = require("../index");
5
+ function createWebhookHandler(options) {
6
+ return async (request) => {
7
+ try {
8
+ const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(request, options.platform, options.secret, options.toleranceInSeconds, options.normalize);
9
+ if (!result.isValid) {
10
+ return Response.json({ error: result.error, platform: result.platform }, { status: 400 });
11
+ }
12
+ const data = await options.handler(result.payload, result.metadata || {});
13
+ return Response.json(data);
14
+ }
15
+ catch (error) {
16
+ options.onError?.(error);
17
+ return Response.json({ error: error.message }, { status: 500 });
18
+ }
19
+ };
20
+ }
@@ -0,0 +1,13 @@
1
+ export interface MinimalNodeRequest {
2
+ method?: string;
3
+ headers: Record<string, string | string[] | undefined>;
4
+ body?: unknown;
5
+ protocol?: string;
6
+ get?: (name: string) => string | undefined;
7
+ originalUrl?: string;
8
+ url?: string;
9
+ on?: (event: string, cb: (chunk?: any) => void) => void;
10
+ }
11
+ export declare function extractRawBody(request: MinimalNodeRequest): Promise<string>;
12
+ export declare function toHeadersInit(headers: Record<string, string | string[] | undefined>): HeadersInit;
13
+ export declare function toWebRequest(request: MinimalNodeRequest): Promise<Request>;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractRawBody = extractRawBody;
4
+ exports.toHeadersInit = toHeadersInit;
5
+ exports.toWebRequest = toWebRequest;
6
+ function getHeaderValue(headers, name) {
7
+ const value = headers[name.toLowerCase()] ?? headers[name];
8
+ if (Array.isArray(value)) {
9
+ return value.join(',');
10
+ }
11
+ return value;
12
+ }
13
+ async function readIncomingMessageBody(request) {
14
+ if (!request.on) {
15
+ return '';
16
+ }
17
+ const chunks = [];
18
+ return new Promise((resolve, reject) => {
19
+ request.on?.('data', (chunk) => {
20
+ if (typeof chunk === 'string') {
21
+ chunks.push(chunk);
22
+ return;
23
+ }
24
+ chunks.push(Buffer.from(chunk ?? '').toString('utf8'));
25
+ });
26
+ request.on?.('end', () => resolve(chunks.join('')));
27
+ request.on?.('error', reject);
28
+ });
29
+ }
30
+ async function extractRawBody(request) {
31
+ const body = request.body;
32
+ if (typeof body === 'string') {
33
+ return body;
34
+ }
35
+ if (Buffer.isBuffer(body)) {
36
+ return body.toString('utf8');
37
+ }
38
+ if (body && typeof body === 'object') {
39
+ return JSON.stringify(body);
40
+ }
41
+ return readIncomingMessageBody(request);
42
+ }
43
+ function toHeadersInit(headers) {
44
+ const normalized = new Headers();
45
+ for (const [key, value] of Object.entries(headers)) {
46
+ if (!value) {
47
+ continue;
48
+ }
49
+ if (Array.isArray(value)) {
50
+ normalized.set(key, value.join(','));
51
+ continue;
52
+ }
53
+ normalized.set(key, value);
54
+ }
55
+ return normalized;
56
+ }
57
+ async function toWebRequest(request) {
58
+ const protocol = request.protocol || 'https';
59
+ const host = request.get?.('host') || getHeaderValue(request.headers, 'host') || 'localhost';
60
+ const path = request.originalUrl || request.url || '/';
61
+ const rawBody = await extractRawBody(request);
62
+ return new Request(`${protocol}://${host}${path}`, {
63
+ method: request.method || 'POST',
64
+ headers: toHeadersInit(request.headers),
65
+ body: rawBody,
66
+ });
67
+ }
@@ -0,0 +1,2 @@
1
+ export { createWebhookHandler } from './adapters/cloudflare';
2
+ export type { CloudflareWebhookHandlerOptions } from './adapters/cloudflare';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWebhookHandler = void 0;
4
+ var cloudflare_1 = require("./adapters/cloudflare");
5
+ Object.defineProperty(exports, "createWebhookHandler", { enumerable: true, get: function () { return cloudflare_1.createWebhookHandler; } });
@@ -0,0 +1,2 @@
1
+ export { createWebhookMiddleware } from './adapters/express';
2
+ export type { ExpressWebhookMiddlewareOptions, ExpressLikeRequest, ExpressLikeResponse, } from './adapters/express';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWebhookMiddleware = void 0;
4
+ var express_1 = require("./adapters/express");
5
+ Object.defineProperty(exports, "createWebhookMiddleware", { enumerable: true, get: function () { return express_1.createWebhookMiddleware; } });
package/dist/index.d.ts CHANGED
@@ -1,17 +1,21 @@
1
- import { WebhookConfig, WebhookVerificationResult, WebhookPlatform, SignatureConfig } from './types';
1
+ import { WebhookConfig, WebhookVerificationResult, WebhookPlatform, SignatureConfig, MultiPlatformSecrets, NormalizeOptions } from './types';
2
2
  export declare class WebhookVerificationService {
3
- static verify(request: Request, config: WebhookConfig): Promise<WebhookVerificationResult>;
3
+ static verify<TPayload = unknown>(request: Request, config: WebhookConfig): Promise<WebhookVerificationResult<TPayload>>;
4
4
  private static getVerifier;
5
5
  private static createAlgorithmBasedVerifier;
6
6
  private static getLegacyVerifier;
7
- static verifyWithPlatformConfig(request: Request, platform: WebhookPlatform, secret: string, toleranceInSeconds?: number): Promise<WebhookVerificationResult>;
7
+ static verifyWithPlatformConfig<TPayload = unknown>(request: Request, platform: WebhookPlatform, secret: string, toleranceInSeconds?: number, normalize?: boolean | NormalizeOptions): Promise<WebhookVerificationResult<TPayload>>;
8
+ static verifyAny<TPayload = unknown>(request: Request, secrets: MultiPlatformSecrets, toleranceInSeconds?: number, normalize?: boolean | NormalizeOptions): Promise<WebhookVerificationResult<TPayload>>;
9
+ static detectPlatform(request: Request): WebhookPlatform;
8
10
  static getPlatformsUsingAlgorithm(algorithm: string): WebhookPlatform[];
9
11
  static platformUsesAlgorithm(platform: WebhookPlatform, algorithm: string): boolean;
10
12
  static validateSignatureConfig(config: SignatureConfig): boolean;
11
- static verifyTokenBased(request: Request, webhookId: string, webhookToken: string): Promise<WebhookVerificationResult>;
13
+ static verifyTokenBased<TPayload = unknown>(request: Request, webhookId: string, webhookToken: string): Promise<WebhookVerificationResult<TPayload>>;
12
14
  }
13
15
  export * from './types';
14
16
  export { getPlatformAlgorithmConfig, platformUsesAlgorithm, getPlatformsUsingAlgorithm, validateSignatureConfig, } from './platforms/algorithms';
15
17
  export { createAlgorithmVerifier } from './verifiers/algorithms';
16
18
  export { createCustomVerifier } from './verifiers/custom-algorithms';
19
+ export { normalizePayload, getPlatformNormalizationCategory, getPlatformsByCategory, } from './normalization/simple';
20
+ export * from './adapters';
17
21
  export default WebhookVerificationService;