@hookflo/tern 1.0.0 โ 1.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 +191 -283
- package/dist/examples.d.ts +8 -28
- package/dist/examples.js +270 -128
- package/dist/index.js +8 -3
- package/dist/platforms/algorithms.js +2 -2
- package/dist/test-compiled.js +1 -1
- package/dist/test.d.ts +2 -6
- package/dist/test.js +160 -86
- package/dist/verifiers/algorithms.d.ts +17 -9
- package/dist/verifiers/algorithms.js +162 -139
- package/dist/verifiers/custom-algorithms.d.ts +0 -10
- package/dist/verifiers/custom-algorithms.js +7 -216
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,20 +1,38 @@
|
|
|
1
|
-
# Tern
|
|
1
|
+
# Tern - Algorithm Agnostic Webhook Verification Framework
|
|
2
2
|
|
|
3
|
-
A robust,
|
|
3
|
+
A robust, algorithm-agnostic webhook verification framework that supports multiple platforms with accurate signature verification and payload retrieval.
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/@hookflo/tern)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
Tern is a zero-dependency TypeScript framework for robust webhook verification across multiple platforms and algorithms.
|
|
10
|
+
|
|
11
|
+
<img width="1396" height="470" style="border-radius: 10px" alt="tern bird nature" src="https://github.com/user-attachments/assets/5f0da3e6-1aba-4f88-a9d7-9d8698845c39" />
|
|
5
12
|
## Features
|
|
6
13
|
|
|
7
|
-
- **Algorithm
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
14
|
+
- **Algorithm Agnostic**: Decouples platform logic from signature verification โ verify based on cryptographic algorithm, not hardcoded platform rules.
|
|
15
|
+
Supports HMAC-SHA256, HMAC-SHA1, HMAC-SHA512, and custom algorithms
|
|
16
|
+
|
|
17
|
+
- **Platform Specific**: Accurate implementations for Stripe, GitHub, Clerk, and other platforms
|
|
18
|
+
- **Flexible Configuration**: Custom signature configurations for any webhook format
|
|
19
|
+
- **Type Safe**: Full TypeScript support with comprehensive type definitions
|
|
20
|
+
- **Framework Agnostic**: Works with Express.js, Next.js, Cloudflare Workers, and more
|
|
21
|
+
|
|
22
|
+
## Why Tern?
|
|
23
|
+
|
|
24
|
+
Most webhook verifiers are tightly coupled to specific platforms or hardcoded logic. Tern introduces a flexible, scalable, algorithm-first approach that:
|
|
25
|
+
|
|
26
|
+
- Works across all major platforms
|
|
27
|
+
- Supports custom signing logic
|
|
28
|
+
- Keeps your code clean and modular
|
|
29
|
+
- Avoids unnecessary dependencies
|
|
30
|
+
- Is written in strict, modern TypeScript
|
|
13
31
|
|
|
14
32
|
## Installation
|
|
15
33
|
|
|
16
34
|
```bash
|
|
17
|
-
npm install tern
|
|
35
|
+
npm install @hookflo/tern
|
|
18
36
|
```
|
|
19
37
|
|
|
20
38
|
## Quick Start
|
|
@@ -22,375 +40,265 @@ npm install tern
|
|
|
22
40
|
### Basic Usage
|
|
23
41
|
|
|
24
42
|
```typescript
|
|
25
|
-
import { WebhookVerificationService } from 'tern';
|
|
43
|
+
import { WebhookVerificationService } from '@hookflo/tern';
|
|
26
44
|
|
|
27
|
-
// Verify a
|
|
45
|
+
// Verify a Stripe webhook
|
|
28
46
|
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
29
47
|
request,
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
300
|
|
48
|
+
'stripe',
|
|
49
|
+
'whsec_your_stripe_webhook_secret'
|
|
33
50
|
);
|
|
34
51
|
|
|
35
52
|
if (result.isValid) {
|
|
36
|
-
console.log('Webhook verified
|
|
53
|
+
console.log('Webhook verified!', result.payload);
|
|
37
54
|
} else {
|
|
38
|
-
console.
|
|
55
|
+
console.log('Verification failed:', result.error);
|
|
39
56
|
}
|
|
40
57
|
```
|
|
41
58
|
|
|
42
|
-
###
|
|
59
|
+
### Platform-Specific Configurations
|
|
43
60
|
|
|
44
61
|
```typescript
|
|
45
|
-
|
|
46
|
-
const result = await WebhookVerificationService.verifyTokenBased(
|
|
47
|
-
request,
|
|
48
|
-
'your-webhook-id',
|
|
49
|
-
'your-webhook-token'
|
|
50
|
-
);
|
|
51
|
-
```
|
|
62
|
+
import { WebhookVerificationService } from '@hookflo/tern';
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
// Stripe webhook
|
|
65
|
+
const stripeConfig = {
|
|
66
|
+
platform: 'stripe',
|
|
67
|
+
secret: 'whsec_your_stripe_webhook_secret',
|
|
68
|
+
toleranceInSeconds: 300,
|
|
69
|
+
};
|
|
54
70
|
|
|
55
|
-
|
|
56
|
-
|
|
71
|
+
// GitHub webhook
|
|
72
|
+
const githubConfig = {
|
|
73
|
+
platform: 'github',
|
|
74
|
+
secret: 'your_github_webhook_secret',
|
|
75
|
+
toleranceInSeconds: 300,
|
|
76
|
+
};
|
|
57
77
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
headerName: 'x-signature',
|
|
64
|
-
headerFormat: 'prefixed',
|
|
65
|
-
prefix: 'sha256=',
|
|
66
|
-
payloadFormat: 'raw'
|
|
67
|
-
}
|
|
78
|
+
// Clerk webhook
|
|
79
|
+
const clerkConfig = {
|
|
80
|
+
platform: 'clerk',
|
|
81
|
+
secret: 'whsec_your_clerk_webhook_secret',
|
|
82
|
+
toleranceInSeconds: 300,
|
|
68
83
|
};
|
|
69
84
|
|
|
70
|
-
const result = await WebhookVerificationService.verify(request,
|
|
85
|
+
const result = await WebhookVerificationService.verify(request, stripeConfig);
|
|
71
86
|
```
|
|
72
87
|
|
|
73
|
-
##
|
|
88
|
+
## Supported Platforms
|
|
74
89
|
|
|
75
|
-
###
|
|
76
|
-
- **
|
|
77
|
-
- **
|
|
78
|
-
- **
|
|
79
|
-
- **Dodo Payments**: `webhook-signature` with raw format
|
|
80
|
-
- **Shopify**: `x-shopify-hmac-sha256`
|
|
81
|
-
- **Vercel**: `x-vercel-signature`
|
|
82
|
-
- **Polar**: `x-polar-signature`
|
|
90
|
+
### Stripe
|
|
91
|
+
- **Signature Format**: `t={timestamp},v1={signature}`
|
|
92
|
+
- **Algorithm**: HMAC-SHA256
|
|
93
|
+
- **Payload Format**: `{timestamp}.{body}`
|
|
83
94
|
|
|
84
|
-
###
|
|
85
|
-
- **
|
|
86
|
-
- **
|
|
87
|
-
- **
|
|
95
|
+
### GitHub
|
|
96
|
+
- **Signature Format**: `sha256={signature}`
|
|
97
|
+
- **Algorithm**: HMAC-SHA256
|
|
98
|
+
- **Payload Format**: Raw body
|
|
88
99
|
|
|
89
|
-
|
|
100
|
+
### Clerk
|
|
101
|
+
- **Signature Format**: `v1,{signature}` (space-separated)
|
|
102
|
+
- **Algorithm**: HMAC-SHA256 with base64 encoding
|
|
103
|
+
- **Payload Format**: `{id}.{timestamp}.{body}`
|
|
90
104
|
|
|
91
|
-
###
|
|
105
|
+
### Other Platforms
|
|
106
|
+
- **Dodo Payments**: HMAC-SHA256
|
|
107
|
+
- **Shopify**: HMAC-SHA256
|
|
108
|
+
- **Vercel**: HMAC-SHA256
|
|
109
|
+
- **Polar**: HMAC-SHA256
|
|
110
|
+
- **Supabase**: Token-based authentication
|
|
92
111
|
|
|
93
|
-
|
|
112
|
+
## Custom Configurations
|
|
94
113
|
|
|
95
|
-
|
|
114
|
+
### Custom HMAC-SHA256
|
|
96
115
|
|
|
97
116
|
```typescript
|
|
98
|
-
const
|
|
99
|
-
platform: '
|
|
100
|
-
secret: '
|
|
101
|
-
|
|
117
|
+
const customConfig = {
|
|
118
|
+
platform: 'custom',
|
|
119
|
+
secret: 'your_custom_secret',
|
|
120
|
+
signatureConfig: {
|
|
121
|
+
algorithm: 'hmac-sha256',
|
|
122
|
+
headerName: 'x-custom-signature',
|
|
123
|
+
headerFormat: 'prefixed',
|
|
124
|
+
prefix: 'sha256=',
|
|
125
|
+
payloadFormat: 'raw',
|
|
126
|
+
},
|
|
102
127
|
};
|
|
103
|
-
|
|
104
|
-
const result = await WebhookVerificationService.verify(request, config);
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
#### `verifyWithPlatformConfig(request: Request, platform: WebhookPlatform, secret: string, toleranceInSeconds?: number): Promise<WebhookVerificationResult>`
|
|
108
|
-
|
|
109
|
-
Verify a webhook using platform-specific configuration.
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
113
|
-
request,
|
|
114
|
-
'stripe',
|
|
115
|
-
'your-stripe-secret',
|
|
116
|
-
300
|
|
117
|
-
);
|
|
118
128
|
```
|
|
119
129
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
Verify a webhook using simple token-based authentication.
|
|
130
|
+
### Custom Timestamped Payload
|
|
123
131
|
|
|
124
132
|
```typescript
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
'
|
|
128
|
-
|
|
129
|
-
|
|
133
|
+
const timestampedConfig = {
|
|
134
|
+
platform: 'custom',
|
|
135
|
+
secret: 'your_custom_secret',
|
|
136
|
+
signatureConfig: {
|
|
137
|
+
algorithm: 'hmac-sha256',
|
|
138
|
+
headerName: 'x-webhook-signature',
|
|
139
|
+
headerFormat: 'raw',
|
|
140
|
+
timestampHeader: 'x-webhook-timestamp',
|
|
141
|
+
timestampFormat: 'unix',
|
|
142
|
+
payloadFormat: 'timestamped',
|
|
143
|
+
},
|
|
144
|
+
};
|
|
130
145
|
```
|
|
131
146
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
#### `detectPlatformFromHeaders(headers: Headers): WebhookPlatform | null`
|
|
147
|
+
## Framework Integration
|
|
135
148
|
|
|
136
|
-
|
|
149
|
+
### Express.js
|
|
137
150
|
|
|
138
151
|
```typescript
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const platform = detectPlatformFromHeaders(request.headers);
|
|
142
|
-
if (platform) {
|
|
152
|
+
app.post('/webhooks/stripe', async (req, res) => {
|
|
143
153
|
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
144
|
-
|
|
154
|
+
req,
|
|
155
|
+
'stripe',
|
|
156
|
+
process.env.STRIPE_WEBHOOK_SECRET
|
|
145
157
|
);
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
#### `getPlatformsUsingAlgorithm(algorithm: string): WebhookPlatform[]`
|
|
150
|
-
|
|
151
|
-
Get all platforms that use a specific algorithm.
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
import { WebhookVerificationService } from 'tern';
|
|
155
|
-
|
|
156
|
-
const hmacPlatforms = WebhookVerificationService.getPlatformsUsingAlgorithm('hmac-sha256');
|
|
157
|
-
// Returns: ['github', 'stripe', 'clerk', 'dodopayments', ...]
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## ๐ฏ Usage Examples
|
|
161
|
-
|
|
162
|
-
### Express.js Integration
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
import express from 'express';
|
|
166
|
-
import { WebhookVerificationService } from 'tern';
|
|
167
|
-
|
|
168
|
-
const app = express();
|
|
169
|
-
|
|
170
|
-
app.post('/webhook', async (req, res) => {
|
|
171
|
-
try {
|
|
172
|
-
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
173
|
-
req,
|
|
174
|
-
'github',
|
|
175
|
-
process.env.GITHUB_WEBHOOK_SECRET,
|
|
176
|
-
300
|
|
177
|
-
);
|
|
178
158
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
console.log('Webhook received:', result.payload);
|
|
182
|
-
res.status(200).json({ success: true });
|
|
183
|
-
} else {
|
|
184
|
-
res.status(401).json({ error: result.error });
|
|
185
|
-
}
|
|
186
|
-
} catch (error) {
|
|
187
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
159
|
+
if (!result.isValid) {
|
|
160
|
+
return res.status(400).json({ error: result.error });
|
|
188
161
|
}
|
|
162
|
+
|
|
163
|
+
// Process the webhook
|
|
164
|
+
console.log('Stripe event:', result.payload.type);
|
|
165
|
+
res.json({ received: true });
|
|
189
166
|
});
|
|
190
167
|
```
|
|
191
168
|
|
|
192
169
|
### Next.js API Route
|
|
193
170
|
|
|
194
171
|
```typescript
|
|
195
|
-
// pages/api/
|
|
196
|
-
|
|
197
|
-
import { WebhookVerificationService } from 'tern';
|
|
198
|
-
|
|
199
|
-
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
172
|
+
// pages/api/webhooks/github.js
|
|
173
|
+
export default async function handler(req, res) {
|
|
200
174
|
if (req.method !== 'POST') {
|
|
201
175
|
return res.status(405).json({ error: 'Method not allowed' });
|
|
202
176
|
}
|
|
203
177
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
300
|
|
210
|
-
);
|
|
178
|
+
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
179
|
+
req,
|
|
180
|
+
'github',
|
|
181
|
+
process.env.GITHUB_WEBHOOK_SECRET
|
|
182
|
+
);
|
|
211
183
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
console.log('Stripe webhook:', result.payload);
|
|
215
|
-
res.status(200).json({ received: true });
|
|
216
|
-
} else {
|
|
217
|
-
res.status(401).json({ error: result.error });
|
|
218
|
-
}
|
|
219
|
-
} catch (error) {
|
|
220
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
184
|
+
if (!result.isValid) {
|
|
185
|
+
return res.status(400).json({ error: result.error });
|
|
221
186
|
}
|
|
187
|
+
|
|
188
|
+
// Handle GitHub webhook
|
|
189
|
+
const event = req.headers['x-github-event'];
|
|
190
|
+
console.log('GitHub event:', event);
|
|
191
|
+
|
|
192
|
+
res.json({ received: true });
|
|
222
193
|
}
|
|
223
194
|
```
|
|
224
195
|
|
|
225
|
-
###
|
|
196
|
+
### Cloudflare Workers
|
|
226
197
|
|
|
227
198
|
```typescript
|
|
228
|
-
|
|
199
|
+
addEventListener('fetch', event => {
|
|
200
|
+
event.respondWith(handleRequest(event.request));
|
|
201
|
+
});
|
|
229
202
|
|
|
230
|
-
async function
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
203
|
+
async function handleRequest(request) {
|
|
204
|
+
if (request.url.includes('/webhooks/clerk')) {
|
|
205
|
+
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
206
|
+
request,
|
|
207
|
+
'clerk',
|
|
208
|
+
CLERK_WEBHOOK_SECRET
|
|
209
|
+
);
|
|
236
210
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
);
|
|
211
|
+
if (!result.isValid) {
|
|
212
|
+
return new Response(JSON.stringify({ error: result.error }), {
|
|
213
|
+
status: 400,
|
|
214
|
+
headers: { 'Content-Type': 'application/json' }
|
|
215
|
+
});
|
|
216
|
+
}
|
|
244
217
|
|
|
245
|
-
|
|
218
|
+
// Process Clerk webhook
|
|
219
|
+
console.log('Clerk event:', result.payload.type);
|
|
220
|
+
return new Response(JSON.stringify({ received: true }));
|
|
221
|
+
}
|
|
246
222
|
}
|
|
247
223
|
```
|
|
248
224
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
```typescript
|
|
252
|
-
import { WebhookVerificationService } from 'tern';
|
|
253
|
-
|
|
254
|
-
const customConfig = {
|
|
255
|
-
platform: 'custom',
|
|
256
|
-
secret: 'your-custom-secret',
|
|
257
|
-
signatureConfig: {
|
|
258
|
-
algorithm: 'hmac-sha256',
|
|
259
|
-
headerName: 'x-custom-signature',
|
|
260
|
-
headerFormat: 'raw',
|
|
261
|
-
payloadFormat: 'raw'
|
|
262
|
-
}
|
|
263
|
-
};
|
|
225
|
+
## API Reference
|
|
264
226
|
|
|
265
|
-
|
|
266
|
-
```
|
|
227
|
+
### WebhookVerificationService
|
|
267
228
|
|
|
268
|
-
|
|
229
|
+
#### `verify(request: Request, config: WebhookConfig): Promise<WebhookVerificationResult>`
|
|
269
230
|
|
|
270
|
-
|
|
231
|
+
Verifies a webhook using the provided configuration.
|
|
271
232
|
|
|
272
|
-
|
|
273
|
-
// In your project, extend the types
|
|
274
|
-
declare module 'tern' {
|
|
275
|
-
interface WebhookPlatform {
|
|
276
|
-
'your-platform': 'your-platform';
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
```
|
|
233
|
+
#### `verifyWithPlatformConfig(request: Request, platform: WebhookPlatform, secret: string, toleranceInSeconds?: number): Promise<WebhookVerificationResult>`
|
|
280
234
|
|
|
281
|
-
|
|
235
|
+
Simplified verification using platform-specific configurations.
|
|
282
236
|
|
|
283
|
-
|
|
284
|
-
const config = {
|
|
285
|
-
platform: 'custom',
|
|
286
|
-
secret: 'your-secret',
|
|
287
|
-
signatureConfig: {
|
|
288
|
-
algorithm: 'hmac-sha256', // or 'custom'
|
|
289
|
-
headerName: 'x-your-signature',
|
|
290
|
-
headerFormat: 'raw',
|
|
291
|
-
payloadFormat: 'raw'
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
```
|
|
237
|
+
#### `verifyTokenBased(request: Request, webhookId: string, webhookToken: string): Promise<WebhookVerificationResult>`
|
|
295
238
|
|
|
296
|
-
|
|
239
|
+
Verifies token-based webhooks (like Supabase).
|
|
297
240
|
|
|
298
|
-
|
|
299
|
-
|----------|-----------|--------|--------|
|
|
300
|
-
| GitHub | HMAC-SHA256 | `x-hub-signature-256` | `sha256=...` |
|
|
301
|
-
| Stripe | HMAC-SHA256 | `stripe-signature` | `t=...,v1=...` |
|
|
302
|
-
| Clerk | Custom | `svix-signature` | Base64 |
|
|
303
|
-
| Supabase | Token-based | `x-webhook-token` | Simple |
|
|
304
|
-
| Shopify | HMAC-SHA256 | `x-shopify-hmac-sha256` | Raw |
|
|
305
|
-
| Vercel | HMAC-SHA256 | `x-vercel-signature` | Raw |
|
|
306
|
-
| Polar | HMAC-SHA256 | `x-polar-signature` | Raw |
|
|
241
|
+
### Types
|
|
307
242
|
|
|
308
|
-
|
|
243
|
+
#### `WebhookVerificationResult`
|
|
309
244
|
|
|
310
245
|
```typescript
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
return res.status(401).json({ error: result.error });
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Process webhook
|
|
325
|
-
console.log('Webhook verified:', result.payload);
|
|
326
|
-
} catch (error) {
|
|
327
|
-
console.error('Verification error:', error);
|
|
328
|
-
return res.status(500).json({ error: 'Internal server error' });
|
|
246
|
+
interface WebhookVerificationResult {
|
|
247
|
+
isValid: boolean;
|
|
248
|
+
error?: string;
|
|
249
|
+
platform: WebhookPlatform;
|
|
250
|
+
payload?: any;
|
|
251
|
+
metadata?: {
|
|
252
|
+
timestamp?: string;
|
|
253
|
+
id?: string | null;
|
|
254
|
+
[key: string]: any;
|
|
255
|
+
};
|
|
329
256
|
}
|
|
330
257
|
```
|
|
331
258
|
|
|
332
|
-
|
|
259
|
+
#### `WebhookConfig`
|
|
333
260
|
|
|
334
261
|
```typescript
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
'content-type': 'application/json'
|
|
343
|
-
},
|
|
344
|
-
body: JSON.stringify({ test: 'data' })
|
|
345
|
-
});
|
|
262
|
+
interface WebhookConfig {
|
|
263
|
+
platform: WebhookPlatform;
|
|
264
|
+
secret: string;
|
|
265
|
+
toleranceInSeconds?: number;
|
|
266
|
+
signatureConfig?: SignatureConfig;
|
|
267
|
+
}
|
|
268
|
+
```
|
|
346
269
|
|
|
347
|
-
|
|
348
|
-
mockRequest,
|
|
349
|
-
'github',
|
|
350
|
-
'test-secret',
|
|
351
|
-
300
|
|
352
|
-
);
|
|
270
|
+
## Testing
|
|
353
271
|
|
|
354
|
-
|
|
272
|
+
Run the test suite:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npm test
|
|
355
276
|
```
|
|
356
277
|
|
|
357
|
-
|
|
278
|
+
Run examples:
|
|
358
279
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
- **Minimal overhead**: Lightweight and fast
|
|
280
|
+
```bash
|
|
281
|
+
npm run examples
|
|
282
|
+
```
|
|
363
283
|
|
|
364
|
-
##
|
|
284
|
+
## Examples
|
|
365
285
|
|
|
366
|
-
|
|
367
|
-
- **Proper validation**: Comprehensive input validation
|
|
368
|
-
- **Secure defaults**: Secure by default configuration
|
|
369
|
-
- **Algorithm flexibility**: Support for multiple signature algorithms
|
|
286
|
+
See the [examples.ts](./src/examples.ts) file for comprehensive usage examples.
|
|
370
287
|
|
|
371
|
-
##
|
|
288
|
+
## Contributing
|
|
372
289
|
|
|
373
290
|
1. Fork the repository
|
|
374
291
|
2. Create a feature branch
|
|
375
292
|
3. Make your changes
|
|
376
|
-
4. Add tests
|
|
293
|
+
4. Add tests for new functionality
|
|
377
294
|
5. Submit a pull request
|
|
378
295
|
|
|
379
296
|
## ๐ License
|
|
380
297
|
|
|
381
|
-
MIT License - see LICENSE
|
|
382
|
-
|
|
383
|
-
## ๐ Support
|
|
384
|
-
|
|
385
|
-
- **Issues**: [GitHub Issues](https://github.com/yourusername/tern/issues)
|
|
386
|
-
- **Documentation**: [GitHub Wiki](https://github.com/yourusername/tern/wiki)
|
|
387
|
-
- **Discussions**: [GitHub Discussions](https://github.com/yourusername/tern/discussions)
|
|
298
|
+
MIT License - see [LICENSE](./LICENSE) for details.
|
|
388
299
|
|
|
389
|
-
##
|
|
300
|
+
## ๐ Links
|
|
390
301
|
|
|
391
|
-
- [
|
|
392
|
-
- [ ]
|
|
393
|
-
- [
|
|
394
|
-
- [ ] More platform integrations
|
|
395
|
-
- [ ] Built-in rate limiting
|
|
396
|
-
- [ ] Monitoring and metrics
|
|
302
|
+
- [Documentation](./USAGE.md)
|
|
303
|
+
- [Framework Summary](./FRAMEWORK_SUMMARY.md)
|
|
304
|
+
- [Issues](https://github.com/your-repo/tern/issues)
|
package/dist/examples.d.ts
CHANGED
|
@@ -1,28 +1,8 @@
|
|
|
1
|
-
export declare function
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function exampleErrorHandling(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} | {
|
|
10
|
-
success: boolean;
|
|
11
|
-
payload: any;
|
|
12
|
-
error?: undefined;
|
|
13
|
-
}>;
|
|
14
|
-
export declare function exampleBatchVerification(request: Request): Promise<({
|
|
15
|
-
platform: string;
|
|
16
|
-
success: boolean;
|
|
17
|
-
result: import("./types").WebhookVerificationResult;
|
|
18
|
-
error?: undefined;
|
|
19
|
-
} | {
|
|
20
|
-
platform: string;
|
|
21
|
-
success: boolean;
|
|
22
|
-
error: string;
|
|
23
|
-
result?: undefined;
|
|
24
|
-
})[]>;
|
|
25
|
-
export declare function exampleExpressIntegration(): any;
|
|
26
|
-
export declare function exampleNextJsApiRoute(): (req: any, res: any) => Promise<any>;
|
|
27
|
-
export declare function exampleCustomPlatform(request: Request): Promise<import("./types").WebhookVerificationResult>;
|
|
28
|
-
export declare function exampleHelperMethods(): void;
|
|
1
|
+
export declare function examplePlatformSpecific(): Promise<void>;
|
|
2
|
+
export declare function exampleCustomSignature(): Promise<void>;
|
|
3
|
+
export declare function exampleSimplifiedPlatform(): Promise<void>;
|
|
4
|
+
export declare function exampleTokenBased(): Promise<void>;
|
|
5
|
+
export declare function exampleErrorHandling(): Promise<void>;
|
|
6
|
+
export declare function examplePlatformInfo(): void;
|
|
7
|
+
export declare function exampleRealWorldUsage(): Promise<void>;
|
|
8
|
+
export declare function runAllExamples(): Promise<void>;
|