@hookflo/tern 4.3.1-beta → 4.4.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/README.md +28 -19
- package/dist/adapters/cloudflare.d.ts +1 -2
- package/dist/adapters/cloudflare.js +5 -1
- package/dist/adapters/express.d.ts +1 -2
- package/dist/adapters/express.js +5 -1
- package/dist/adapters/hono.d.ts +1 -2
- package/dist/adapters/hono.js +5 -1
- package/dist/adapters/nextjs.d.ts +1 -2
- package/dist/adapters/nextjs.js +5 -1
- package/dist/adapters/shared.js +5 -2
- package/dist/index.d.ts +4 -5
- package/dist/index.js +18 -24
- package/dist/platforms/algorithms.d.ts +20 -0
- package/dist/platforms/algorithms.js +65 -51
- package/dist/types.d.ts +7 -72
- package/dist/types.js +1 -2
- package/dist/verifiers/algorithms.d.ts +5 -1
- package/dist/verifiers/algorithms.js +46 -36
- package/package.json +1 -1
- package/dist/normalization/index.d.ts +0 -20
- package/dist/normalization/index.js +0 -78
- package/dist/normalization/providers/payment/paypal.d.ts +0 -2
- package/dist/normalization/providers/payment/paypal.js +0 -12
- package/dist/normalization/providers/payment/razorpay.d.ts +0 -2
- package/dist/normalization/providers/payment/razorpay.js +0 -13
- package/dist/normalization/providers/payment/stripe.d.ts +0 -2
- package/dist/normalization/providers/payment/stripe.js +0 -13
- package/dist/normalization/providers/registry.d.ts +0 -5
- package/dist/normalization/providers/registry.js +0 -21
- package/dist/normalization/simple.d.ts +0 -4
- package/dist/normalization/simple.js +0 -126
- package/dist/normalization/storage/interface.d.ts +0 -13
- package/dist/normalization/storage/interface.js +0 -2
- package/dist/normalization/storage/memory.d.ts +0 -12
- package/dist/normalization/storage/memory.js +0 -39
- package/dist/normalization/templates/base/auth.d.ts +0 -2
- package/dist/normalization/templates/base/auth.js +0 -22
- package/dist/normalization/templates/base/ecommerce.d.ts +0 -2
- package/dist/normalization/templates/base/ecommerce.js +0 -25
- package/dist/normalization/templates/base/payment.d.ts +0 -2
- package/dist/normalization/templates/base/payment.js +0 -25
- package/dist/normalization/templates/registry.d.ts +0 -6
- package/dist/normalization/templates/registry.js +0 -22
- package/dist/normalization/transformer/engine.d.ts +0 -11
- package/dist/normalization/transformer/engine.js +0 -86
- package/dist/normalization/transformer/validator.d.ts +0 -12
- package/dist/normalization/transformer/validator.js +0 -56
- package/dist/normalization/types.d.ts +0 -79
- package/dist/normalization/types.js +0 -2
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type WebhookPlatform =
|
|
1
|
+
export type WebhookPlatform = "custom" | "clerk" | "svix" | "github" | "stripe" | "shopify" | "vercel" | "polar" | "dodopayments" | "gitlab" | "paddle" | "razorpay" | "lemonsqueezy" | "workos" | "woocommerce" | "replicateai" | "falai" | "sentry" | "grafana" | "doppler" | "sanity" | "linear" | "standardwebhooks" | "unknown";
|
|
2
2
|
export declare enum WebhookPlatformKeys {
|
|
3
3
|
GitHub = "github",
|
|
4
4
|
Stripe = "stripe",
|
|
@@ -21,87 +21,23 @@ export declare enum WebhookPlatformKeys {
|
|
|
21
21
|
Doppler = "doppler",
|
|
22
22
|
Sanity = "sanity",
|
|
23
23
|
Linear = "linear",
|
|
24
|
-
|
|
25
|
-
Twilio = "twilio",
|
|
24
|
+
StandardWebhooks = "standardwebhooks",
|
|
26
25
|
Custom = "custom",
|
|
27
26
|
Unknown = "unknown"
|
|
28
27
|
}
|
|
29
|
-
export type SignatureAlgorithm =
|
|
28
|
+
export type SignatureAlgorithm = "hmac-sha256" | "hmac-sha1" | "hmac-sha512" | "rsa-sha256" | "ed25519" | "custom";
|
|
30
29
|
export interface SignatureConfig {
|
|
31
30
|
algorithm: SignatureAlgorithm;
|
|
32
31
|
headerName: string;
|
|
33
|
-
headerFormat?:
|
|
32
|
+
headerFormat?: "raw" | "prefixed" | "comma-separated";
|
|
34
33
|
prefix?: string;
|
|
35
34
|
timestampHeader?: string;
|
|
36
|
-
timestampFormat?:
|
|
37
|
-
payloadFormat?:
|
|
35
|
+
timestampFormat?: "unix" | "iso" | "custom";
|
|
36
|
+
payloadFormat?: "raw" | "timestamped" | "json-stringified" | "custom";
|
|
38
37
|
idHeader?: string;
|
|
39
38
|
customConfig?: Record<string, any>;
|
|
40
39
|
}
|
|
41
|
-
export type WebhookErrorCode =
|
|
42
|
-
export type NormalizationCategory = 'payment' | 'auth' | 'ecommerce' | 'infrastructure';
|
|
43
|
-
export interface BaseNormalizedWebhook {
|
|
44
|
-
category: NormalizationCategory;
|
|
45
|
-
event: string;
|
|
46
|
-
_platform: WebhookPlatform | string;
|
|
47
|
-
_raw: unknown;
|
|
48
|
-
occurred_at?: string;
|
|
49
|
-
}
|
|
50
|
-
export type PaymentWebhookEvent = 'payment.succeeded' | 'payment.failed' | 'payment.refunded' | 'subscription.created' | 'subscription.cancelled' | 'payment.unknown';
|
|
51
|
-
export interface PaymentWebhookNormalized extends BaseNormalizedWebhook {
|
|
52
|
-
category: 'payment';
|
|
53
|
-
event: PaymentWebhookEvent;
|
|
54
|
-
amount?: number;
|
|
55
|
-
currency?: string;
|
|
56
|
-
customer_id?: string;
|
|
57
|
-
transaction_id?: string;
|
|
58
|
-
subscription_id?: string;
|
|
59
|
-
refund_amount?: number;
|
|
60
|
-
failure_reason?: string;
|
|
61
|
-
metadata?: Record<string, string>;
|
|
62
|
-
}
|
|
63
|
-
export type AuthWebhookEvent = 'user.created' | 'user.updated' | 'user.deleted' | 'session.started' | 'session.ended' | 'auth.unknown';
|
|
64
|
-
export interface AuthWebhookNormalized extends BaseNormalizedWebhook {
|
|
65
|
-
category: 'auth';
|
|
66
|
-
event: AuthWebhookEvent;
|
|
67
|
-
user_id?: string;
|
|
68
|
-
email?: string;
|
|
69
|
-
phone?: string;
|
|
70
|
-
metadata?: Record<string, string>;
|
|
71
|
-
}
|
|
72
|
-
export interface EcommerceWebhookNormalized extends BaseNormalizedWebhook {
|
|
73
|
-
category: 'ecommerce';
|
|
74
|
-
event: string;
|
|
75
|
-
order_id?: string;
|
|
76
|
-
customer_id?: string;
|
|
77
|
-
amount?: number;
|
|
78
|
-
currency?: string;
|
|
79
|
-
metadata?: Record<string, string>;
|
|
80
|
-
}
|
|
81
|
-
export interface InfrastructureWebhookNormalized extends BaseNormalizedWebhook {
|
|
82
|
-
category: 'infrastructure';
|
|
83
|
-
event: string;
|
|
84
|
-
project_id?: string;
|
|
85
|
-
deployment_id?: string;
|
|
86
|
-
status?: 'queued' | 'building' | 'ready' | 'error' | 'unknown';
|
|
87
|
-
metadata?: Record<string, string>;
|
|
88
|
-
}
|
|
89
|
-
export interface UnknownNormalizedWebhook extends BaseNormalizedWebhook {
|
|
90
|
-
event: string;
|
|
91
|
-
warning?: string;
|
|
92
|
-
}
|
|
93
|
-
export type NormalizedPayloadByCategory = {
|
|
94
|
-
payment: PaymentWebhookNormalized;
|
|
95
|
-
auth: AuthWebhookNormalized;
|
|
96
|
-
ecommerce: EcommerceWebhookNormalized;
|
|
97
|
-
infrastructure: InfrastructureWebhookNormalized;
|
|
98
|
-
};
|
|
99
|
-
export type AnyNormalizedWebhook = PaymentWebhookNormalized | AuthWebhookNormalized | EcommerceWebhookNormalized | InfrastructureWebhookNormalized | UnknownNormalizedWebhook;
|
|
100
|
-
export interface NormalizeOptions {
|
|
101
|
-
enabled?: boolean;
|
|
102
|
-
category?: NormalizationCategory;
|
|
103
|
-
includeRaw?: boolean;
|
|
104
|
-
}
|
|
40
|
+
export type WebhookErrorCode = "MISSING_SIGNATURE" | "INVALID_SIGNATURE" | "TIMESTAMP_EXPIRED" | "MISSING_TOKEN" | "INVALID_TOKEN" | "PLATFORM_NOT_SUPPORTED" | "NORMALIZATION_ERROR" | "VERIFICATION_ERROR";
|
|
105
41
|
export interface WebhookVerificationResult<TPayload = unknown> {
|
|
106
42
|
isValid: boolean;
|
|
107
43
|
error?: string;
|
|
@@ -120,7 +56,6 @@ export interface WebhookConfig {
|
|
|
120
56
|
secret: string;
|
|
121
57
|
toleranceInSeconds?: number;
|
|
122
58
|
signatureConfig?: SignatureConfig;
|
|
123
|
-
normalize?: boolean | NormalizeOptions;
|
|
124
59
|
}
|
|
125
60
|
export interface MultiPlatformSecrets {
|
|
126
61
|
[platform: string]: string | undefined;
|
package/dist/types.js
CHANGED
|
@@ -24,8 +24,7 @@ var WebhookPlatformKeys;
|
|
|
24
24
|
WebhookPlatformKeys["Doppler"] = "doppler";
|
|
25
25
|
WebhookPlatformKeys["Sanity"] = "sanity";
|
|
26
26
|
WebhookPlatformKeys["Linear"] = "linear";
|
|
27
|
-
WebhookPlatformKeys["
|
|
28
|
-
WebhookPlatformKeys["Twilio"] = "twilio";
|
|
27
|
+
WebhookPlatformKeys["StandardWebhooks"] = "standardwebhooks";
|
|
29
28
|
WebhookPlatformKeys["Custom"] = "custom";
|
|
30
29
|
WebhookPlatformKeys["Unknown"] = "unknown";
|
|
31
30
|
})(WebhookPlatformKeys || (exports.WebhookPlatformKeys = WebhookPlatformKeys = {}));
|
|
@@ -5,6 +5,11 @@ export declare abstract class AlgorithmBasedVerifier extends WebhookVerifier {
|
|
|
5
5
|
protected platform: WebhookPlatform;
|
|
6
6
|
constructor(secret: string, config: SignatureConfig, platform: WebhookPlatform, toleranceInSeconds?: number);
|
|
7
7
|
abstract verify(request: Request): Promise<WebhookVerificationResult>;
|
|
8
|
+
protected getMissingSignatureMessage(): string;
|
|
9
|
+
protected getMissingTimestampMessage(): string;
|
|
10
|
+
protected getTimestampExpiredMessage(): string;
|
|
11
|
+
protected getInvalidSignatureMessage(): string;
|
|
12
|
+
protected getVerificationErrorMessage(error: Error): string;
|
|
8
13
|
protected parseDelimitedHeader(headerValue: string): Record<string, string>;
|
|
9
14
|
protected extractSignatures(request: Request): string[];
|
|
10
15
|
protected extractTimestamp(request: Request): number | null;
|
|
@@ -19,7 +24,6 @@ export declare abstract class AlgorithmBasedVerifier extends WebhookVerifier {
|
|
|
19
24
|
}
|
|
20
25
|
export declare class GenericHMACVerifier extends AlgorithmBasedVerifier {
|
|
21
26
|
private validateLinearReplayWindow;
|
|
22
|
-
private validateTwilioBodyHash;
|
|
23
27
|
private resolveSentryPayloadCandidates;
|
|
24
28
|
verify(request: Request): Promise<WebhookVerificationResult>;
|
|
25
29
|
}
|
|
@@ -13,6 +13,37 @@ class AlgorithmBasedVerifier extends base_1.WebhookVerifier {
|
|
|
13
13
|
this.config = config;
|
|
14
14
|
this.platform = platform;
|
|
15
15
|
}
|
|
16
|
+
getMissingSignatureMessage() {
|
|
17
|
+
return `Missing signature header: ${this.config.headerName}. Ensure your webhook provider sends this header and your adapter forwards it unchanged.`;
|
|
18
|
+
}
|
|
19
|
+
getMissingTimestampMessage() {
|
|
20
|
+
const timestampHeader = this.config.timestampHeader || this.config.customConfig?.timestampHeader || 'timestamp';
|
|
21
|
+
return `Missing required timestamp for webhook verification. Verify header '${timestampHeader}' is present and passed through by your framework/proxy.`;
|
|
22
|
+
}
|
|
23
|
+
getTimestampExpiredMessage() {
|
|
24
|
+
return 'Webhook timestamp expired. Check server clock drift and increase tolerance only if your provider allows it.';
|
|
25
|
+
}
|
|
26
|
+
getInvalidSignatureMessage() {
|
|
27
|
+
const genericHint = `Invalid signature for ${this.platform}. Confirm webhook secret, raw request body handling, and signature header formatting.`;
|
|
28
|
+
switch (this.platform) {
|
|
29
|
+
case 'stripe':
|
|
30
|
+
return `${genericHint} Stripe signatures require the exact raw body and Stripe-Signature timestamp/value pair.`;
|
|
31
|
+
case 'github':
|
|
32
|
+
return `${genericHint} GitHub signatures must include the sha256= prefix from x-hub-signature-256.`;
|
|
33
|
+
case 'svix':
|
|
34
|
+
case 'standardwebhooks':
|
|
35
|
+
case 'clerk':
|
|
36
|
+
case 'dodopayments':
|
|
37
|
+
case 'replicateai':
|
|
38
|
+
case 'polar':
|
|
39
|
+
return `${genericHint} Standard Webhooks payload must be signed as id.timestamp.body and secrets may need whsec_ base64 decoding.`;
|
|
40
|
+
default:
|
|
41
|
+
return genericHint;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
getVerificationErrorMessage(error) {
|
|
45
|
+
return `${this.platform} verification error: ${error.message}. Check webhook secret configuration and ensure your framework preserves raw body + headers.`;
|
|
46
|
+
}
|
|
16
47
|
parseDelimitedHeader(headerValue) {
|
|
17
48
|
const parts = headerValue.split(/[;,]/);
|
|
18
49
|
const values = {};
|
|
@@ -32,7 +63,9 @@ class AlgorithmBasedVerifier extends base_1.WebhookVerifier {
|
|
|
32
63
|
return values;
|
|
33
64
|
}
|
|
34
65
|
extractSignatures(request) {
|
|
35
|
-
const headerValue = request.headers.get(this.config.headerName)
|
|
66
|
+
const headerValue = request.headers.get(this.config.headerName)
|
|
67
|
+
|| this.config.customConfig?.signatureHeaderAliases?.map((alias) => request.headers.get(alias)).find(Boolean)
|
|
68
|
+
|| null;
|
|
36
69
|
if (!headerValue)
|
|
37
70
|
return [];
|
|
38
71
|
switch (this.config.headerFormat) {
|
|
@@ -135,7 +168,7 @@ class AlgorithmBasedVerifier extends base_1.WebhookVerifier {
|
|
|
135
168
|
return true;
|
|
136
169
|
// These platforms have timestampHeader in config but timestamp
|
|
137
170
|
// is optional in their spec — validate only if present, never mandate
|
|
138
|
-
const optionalTimestampPlatforms = ['vercel', 'sentry', 'grafana'
|
|
171
|
+
const optionalTimestampPlatforms = ['vercel', 'sentry', 'grafana'];
|
|
139
172
|
if (optionalTimestampPlatforms.includes(this.platform))
|
|
140
173
|
return false;
|
|
141
174
|
// For all other platforms: infer from config
|
|
@@ -182,7 +215,7 @@ class AlgorithmBasedVerifier extends base_1.WebhookVerifier {
|
|
|
182
215
|
const id = request.headers.get(this.config.customConfig.idHeader || "x-webhook-id") || this.config.customConfig?.idHeaderAliases?.map((alias) => request.headers.get(alias)).find(Boolean);
|
|
183
216
|
const timestamp = request.headers.get(this.config.timestampHeader ||
|
|
184
217
|
this.config.customConfig?.timestampHeader ||
|
|
185
|
-
"x-webhook-timestamp");
|
|
218
|
+
"x-webhook-timestamp") || this.config.customConfig?.timestampHeaderAliases?.map((alias) => request.headers.get(alias)).find(Boolean);
|
|
186
219
|
// if either is missing payload will be malformed — fail explicitly
|
|
187
220
|
if (!id || !timestamp) {
|
|
188
221
|
throw new Error(`Missing required headers for payload construction: ${!id ? this.config.customConfig.idHeader || "x-webhook-id" : ""} ${!timestamp
|
|
@@ -302,20 +335,6 @@ class GenericHMACVerifier extends AlgorithmBasedVerifier {
|
|
|
302
335
|
}
|
|
303
336
|
return null;
|
|
304
337
|
}
|
|
305
|
-
validateTwilioBodyHash(rawBody, request) {
|
|
306
|
-
if (this.platform !== 'twilio' || !this.config.customConfig?.validateBodySHA256) {
|
|
307
|
-
return null;
|
|
308
|
-
}
|
|
309
|
-
const url = new URL(request.url);
|
|
310
|
-
const bodySha = url.searchParams.get('bodySHA256');
|
|
311
|
-
if (!bodySha)
|
|
312
|
-
return null;
|
|
313
|
-
const computed = (0, crypto_1.createHash)('sha256').update(rawBody).digest('hex');
|
|
314
|
-
if (!this.safeCompare(computed, bodySha)) {
|
|
315
|
-
return 'Twilio bodySHA256 query param does not match payload hash';
|
|
316
|
-
}
|
|
317
|
-
return null;
|
|
318
|
-
}
|
|
319
338
|
resolveSentryPayloadCandidates(rawBody, request) {
|
|
320
339
|
const candidates = [
|
|
321
340
|
this.formatPayload(rawBody, request),
|
|
@@ -349,7 +368,7 @@ class GenericHMACVerifier extends AlgorithmBasedVerifier {
|
|
|
349
368
|
if (signatures.length === 0) {
|
|
350
369
|
return {
|
|
351
370
|
isValid: false,
|
|
352
|
-
error:
|
|
371
|
+
error: this.getMissingSignatureMessage(),
|
|
353
372
|
errorCode: "MISSING_SIGNATURE",
|
|
354
373
|
platform: this.platform,
|
|
355
374
|
};
|
|
@@ -364,15 +383,6 @@ class GenericHMACVerifier extends AlgorithmBasedVerifier {
|
|
|
364
383
|
platform: this.platform,
|
|
365
384
|
};
|
|
366
385
|
}
|
|
367
|
-
const twilioBodyHashError = this.validateTwilioBodyHash(rawBody, request);
|
|
368
|
-
if (twilioBodyHashError) {
|
|
369
|
-
return {
|
|
370
|
-
isValid: false,
|
|
371
|
-
error: twilioBodyHashError,
|
|
372
|
-
errorCode: 'INVALID_SIGNATURE',
|
|
373
|
-
platform: this.platform,
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
386
|
let timestamp = null;
|
|
377
387
|
if (this.config.headerFormat === "comma-separated") {
|
|
378
388
|
timestamp = this.extractTimestampFromSignature(request);
|
|
@@ -383,7 +393,7 @@ class GenericHMACVerifier extends AlgorithmBasedVerifier {
|
|
|
383
393
|
if (this.requiresTimestamp() && !timestamp) {
|
|
384
394
|
return {
|
|
385
395
|
isValid: false,
|
|
386
|
-
error:
|
|
396
|
+
error: this.getMissingTimestampMessage(),
|
|
387
397
|
errorCode: 'MISSING_SIGNATURE',
|
|
388
398
|
platform: this.platform,
|
|
389
399
|
};
|
|
@@ -391,7 +401,7 @@ class GenericHMACVerifier extends AlgorithmBasedVerifier {
|
|
|
391
401
|
if (timestamp && !this.isTimestampValid(timestamp)) {
|
|
392
402
|
return {
|
|
393
403
|
isValid: false,
|
|
394
|
-
error:
|
|
404
|
+
error: this.getTimestampExpiredMessage(),
|
|
395
405
|
errorCode: "TIMESTAMP_EXPIRED",
|
|
396
406
|
platform: this.platform,
|
|
397
407
|
};
|
|
@@ -423,7 +433,7 @@ class GenericHMACVerifier extends AlgorithmBasedVerifier {
|
|
|
423
433
|
if (!isValid) {
|
|
424
434
|
return {
|
|
425
435
|
isValid: false,
|
|
426
|
-
error:
|
|
436
|
+
error: this.getInvalidSignatureMessage(),
|
|
427
437
|
errorCode: "INVALID_SIGNATURE",
|
|
428
438
|
platform: this.platform,
|
|
429
439
|
};
|
|
@@ -452,7 +462,7 @@ class GenericHMACVerifier extends AlgorithmBasedVerifier {
|
|
|
452
462
|
catch (error) {
|
|
453
463
|
return {
|
|
454
464
|
isValid: false,
|
|
455
|
-
error:
|
|
465
|
+
error: this.getVerificationErrorMessage(error),
|
|
456
466
|
errorCode: "VERIFICATION_ERROR",
|
|
457
467
|
platform: this.platform,
|
|
458
468
|
};
|
|
@@ -561,7 +571,7 @@ class Ed25519Verifier extends AlgorithmBasedVerifier {
|
|
|
561
571
|
if (signatures.length === 0) {
|
|
562
572
|
return {
|
|
563
573
|
isValid: false,
|
|
564
|
-
error:
|
|
574
|
+
error: this.getMissingSignatureMessage(),
|
|
565
575
|
errorCode: "MISSING_SIGNATURE",
|
|
566
576
|
platform: this.platform,
|
|
567
577
|
};
|
|
@@ -575,7 +585,7 @@ class Ed25519Verifier extends AlgorithmBasedVerifier {
|
|
|
575
585
|
if (!timestampStr) {
|
|
576
586
|
return {
|
|
577
587
|
isValid: false,
|
|
578
|
-
error:
|
|
588
|
+
error: this.getMissingTimestampMessage(),
|
|
579
589
|
errorCode: 'MISSING_SIGNATURE',
|
|
580
590
|
platform: this.platform,
|
|
581
591
|
};
|
|
@@ -584,7 +594,7 @@ class Ed25519Verifier extends AlgorithmBasedVerifier {
|
|
|
584
594
|
if (!this.isTimestampValid(timestamp)) {
|
|
585
595
|
return {
|
|
586
596
|
isValid: false,
|
|
587
|
-
error:
|
|
597
|
+
error: this.getTimestampExpiredMessage(),
|
|
588
598
|
errorCode: "TIMESTAMP_EXPIRED",
|
|
589
599
|
platform: this.platform,
|
|
590
600
|
};
|
|
@@ -644,7 +654,7 @@ class Ed25519Verifier extends AlgorithmBasedVerifier {
|
|
|
644
654
|
if (!isValid) {
|
|
645
655
|
return {
|
|
646
656
|
isValid: false,
|
|
647
|
-
error:
|
|
657
|
+
error: this.getInvalidSignatureMessage(),
|
|
648
658
|
errorCode: "INVALID_SIGNATURE",
|
|
649
659
|
platform: this.platform,
|
|
650
660
|
};
|
|
@@ -673,7 +683,7 @@ class Ed25519Verifier extends AlgorithmBasedVerifier {
|
|
|
673
683
|
catch (error) {
|
|
674
684
|
return {
|
|
675
685
|
isValid: false,
|
|
676
|
-
error:
|
|
686
|
+
error: this.getVerificationErrorMessage(error),
|
|
677
687
|
errorCode: "VERIFICATION_ERROR",
|
|
678
688
|
platform: this.platform,
|
|
679
689
|
};
|
package/package.json
CHANGED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { BaseTemplate, CreateSchemaInput, NormalizedResult, ProviderInfo, TemplateCategory, TransformParams, UpdateSchemaInput, UserSchema } from './types';
|
|
2
|
-
import { StorageAdapter } from './storage/interface';
|
|
3
|
-
export declare class Normalizer {
|
|
4
|
-
private readonly storage;
|
|
5
|
-
private engine;
|
|
6
|
-
constructor(storage?: StorageAdapter);
|
|
7
|
-
getBaseTemplates(): Promise<BaseTemplate[]>;
|
|
8
|
-
getProviders(category?: TemplateCategory): Promise<ProviderInfo[]>;
|
|
9
|
-
createSchema(input: CreateSchemaInput): Promise<UserSchema>;
|
|
10
|
-
updateSchema(schemaId: string, updates: UpdateSchemaInput): Promise<void>;
|
|
11
|
-
getSchema(id: string): Promise<UserSchema | null>;
|
|
12
|
-
transform(params: TransformParams): Promise<NormalizedResult>;
|
|
13
|
-
validateSchema(schema: UserSchema): Promise<{
|
|
14
|
-
valid: boolean;
|
|
15
|
-
errors: string[];
|
|
16
|
-
}>;
|
|
17
|
-
}
|
|
18
|
-
export * from './types';
|
|
19
|
-
export * from './storage/interface';
|
|
20
|
-
export { InMemoryStorageAdapter } from './storage/memory';
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.InMemoryStorageAdapter = exports.Normalizer = void 0;
|
|
18
|
-
const registry_1 = require("./providers/registry");
|
|
19
|
-
const registry_2 = require("./templates/registry");
|
|
20
|
-
const memory_1 = require("./storage/memory");
|
|
21
|
-
const engine_1 = require("./transformer/engine");
|
|
22
|
-
const validator_1 = require("./transformer/validator");
|
|
23
|
-
class Normalizer {
|
|
24
|
-
constructor(storage = new memory_1.InMemoryStorageAdapter()) {
|
|
25
|
-
this.storage = storage;
|
|
26
|
-
this.engine = new engine_1.NormalizationEngine(storage, new validator_1.SchemaValidator());
|
|
27
|
-
}
|
|
28
|
-
async getBaseTemplates() {
|
|
29
|
-
return this.storage.listBaseTemplates();
|
|
30
|
-
}
|
|
31
|
-
async getProviders(category) {
|
|
32
|
-
return registry_1.providerRegistry.list(category);
|
|
33
|
-
}
|
|
34
|
-
async createSchema(input) {
|
|
35
|
-
const schema = {
|
|
36
|
-
id: generateId(),
|
|
37
|
-
userId: input.userId,
|
|
38
|
-
baseTemplateId: input.baseTemplateId,
|
|
39
|
-
category: input.category,
|
|
40
|
-
fields: input.fields,
|
|
41
|
-
providerMappings: input.providerMappings,
|
|
42
|
-
createdAt: new Date(),
|
|
43
|
-
updatedAt: new Date(),
|
|
44
|
-
};
|
|
45
|
-
await this.storage.saveSchema(schema);
|
|
46
|
-
return schema;
|
|
47
|
-
}
|
|
48
|
-
async updateSchema(schemaId, updates) {
|
|
49
|
-
await this.storage.updateSchema(schemaId, updates);
|
|
50
|
-
}
|
|
51
|
-
async getSchema(id) {
|
|
52
|
-
return this.storage.getSchema(id);
|
|
53
|
-
}
|
|
54
|
-
async transform(params) {
|
|
55
|
-
return this.engine.transform(params);
|
|
56
|
-
}
|
|
57
|
-
async validateSchema(schema) {
|
|
58
|
-
const base = (await this.storage.getBaseTemplate(schema.baseTemplateId))
|
|
59
|
-
?? registry_2.templateRegistry.getById(schema.baseTemplateId);
|
|
60
|
-
if (!base) {
|
|
61
|
-
return {
|
|
62
|
-
valid: false,
|
|
63
|
-
errors: [`Base template not found: ${schema.baseTemplateId}`],
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
const validator = new validator_1.SchemaValidator();
|
|
67
|
-
return validator.validateSchema(schema, base);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
exports.Normalizer = Normalizer;
|
|
71
|
-
function generateId() {
|
|
72
|
-
// Simple non-crypto unique ID generator for framework default
|
|
73
|
-
return (`sch_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`);
|
|
74
|
-
}
|
|
75
|
-
__exportStar(require("./types"), exports);
|
|
76
|
-
__exportStar(require("./storage/interface"), exports);
|
|
77
|
-
var memory_2 = require("./storage/memory");
|
|
78
|
-
Object.defineProperty(exports, "InMemoryStorageAdapter", { enumerable: true, get: function () { return memory_2.InMemoryStorageAdapter; } });
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.paypalDefaultMapping = void 0;
|
|
4
|
-
exports.paypalDefaultMapping = {
|
|
5
|
-
provider: 'paypal',
|
|
6
|
-
fieldMappings: [
|
|
7
|
-
{ schemaFieldId: 'event_type', providerPath: 'event_type' },
|
|
8
|
-
{ schemaFieldId: 'amount', providerPath: 'resource.amount.value', transform: 'toNumber' },
|
|
9
|
-
{ schemaFieldId: 'currency', providerPath: 'resource.amount.currency_code', transform: 'toUpperCase' },
|
|
10
|
-
{ schemaFieldId: 'transaction_id', providerPath: 'resource.id' },
|
|
11
|
-
],
|
|
12
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.razorpayDefaultMapping = void 0;
|
|
4
|
-
exports.razorpayDefaultMapping = {
|
|
5
|
-
provider: 'razorpay',
|
|
6
|
-
fieldMappings: [
|
|
7
|
-
{ schemaFieldId: 'event_type', providerPath: 'event' },
|
|
8
|
-
{ schemaFieldId: 'amount', providerPath: 'payload.payment.entity.amount' },
|
|
9
|
-
{ schemaFieldId: 'currency', providerPath: 'payload.payment.entity.currency', transform: 'toUpperCase' },
|
|
10
|
-
{ schemaFieldId: 'transaction_id', providerPath: 'payload.payment.entity.id' },
|
|
11
|
-
{ schemaFieldId: 'customer_id', providerPath: 'payload.payment.entity.contact' },
|
|
12
|
-
],
|
|
13
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.stripeDefaultMapping = void 0;
|
|
4
|
-
exports.stripeDefaultMapping = {
|
|
5
|
-
provider: 'stripe',
|
|
6
|
-
fieldMappings: [
|
|
7
|
-
{ schemaFieldId: 'event_type', providerPath: 'type' },
|
|
8
|
-
{ schemaFieldId: 'amount', providerPath: 'data.object.amount_received' },
|
|
9
|
-
{ schemaFieldId: 'currency', providerPath: 'data.object.currency', transform: 'toUpperCase' },
|
|
10
|
-
{ schemaFieldId: 'transaction_id', providerPath: 'data.object.id' },
|
|
11
|
-
{ schemaFieldId: 'customer_id', providerPath: 'data.object.customer' },
|
|
12
|
-
],
|
|
13
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.providerRegistry = void 0;
|
|
4
|
-
const providers = [
|
|
5
|
-
{ id: 'stripe', name: 'Stripe', category: 'payment' },
|
|
6
|
-
{ id: 'razorpay', name: 'Razorpay', category: 'payment' },
|
|
7
|
-
{ id: 'paypal', name: 'PayPal', category: 'payment' },
|
|
8
|
-
{ id: 'clerk', name: 'Clerk', category: 'auth' },
|
|
9
|
-
{ id: 'shopify', name: 'Shopify', category: 'ecommerce' },
|
|
10
|
-
{ id: 'woocommerce', name: 'WooCommerce', category: 'ecommerce' },
|
|
11
|
-
];
|
|
12
|
-
exports.providerRegistry = {
|
|
13
|
-
list(category) {
|
|
14
|
-
if (!category)
|
|
15
|
-
return providers;
|
|
16
|
-
return providers.filter((p) => p.category === category);
|
|
17
|
-
},
|
|
18
|
-
getById(id) {
|
|
19
|
-
return providers.find((p) => p.id === id);
|
|
20
|
-
},
|
|
21
|
-
};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { AnyNormalizedWebhook, NormalizeOptions, NormalizationCategory, WebhookPlatform } from '../types';
|
|
2
|
-
export declare function getPlatformNormalizationCategory(platform: WebhookPlatform): NormalizationCategory | null;
|
|
3
|
-
export declare function getPlatformsByCategory(category: NormalizationCategory): WebhookPlatform[];
|
|
4
|
-
export declare function normalizePayload(platform: WebhookPlatform, payload: any, normalize?: boolean | NormalizeOptions): AnyNormalizedWebhook | unknown;
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPlatformNormalizationCategory = getPlatformNormalizationCategory;
|
|
4
|
-
exports.getPlatformsByCategory = getPlatformsByCategory;
|
|
5
|
-
exports.normalizePayload = normalizePayload;
|
|
6
|
-
function readPath(payload, path) {
|
|
7
|
-
return path.split('.').reduce((acc, key) => {
|
|
8
|
-
if (acc === undefined || acc === null) {
|
|
9
|
-
return undefined;
|
|
10
|
-
}
|
|
11
|
-
return acc[key];
|
|
12
|
-
}, payload);
|
|
13
|
-
}
|
|
14
|
-
const platformNormalizers = {
|
|
15
|
-
stripe: {
|
|
16
|
-
platform: 'stripe',
|
|
17
|
-
category: 'payment',
|
|
18
|
-
normalize: (payload) => ({
|
|
19
|
-
category: 'payment',
|
|
20
|
-
event: readPath(payload, 'type') === 'payment_intent.succeeded'
|
|
21
|
-
? 'payment.succeeded'
|
|
22
|
-
: 'payment.unknown',
|
|
23
|
-
amount: readPath(payload, 'data.object.amount_received')
|
|
24
|
-
?? readPath(payload, 'data.object.amount'),
|
|
25
|
-
currency: String(readPath(payload, 'data.object.currency') ?? '').toUpperCase() || undefined,
|
|
26
|
-
customer_id: readPath(payload, 'data.object.customer'),
|
|
27
|
-
transaction_id: readPath(payload, 'data.object.id'),
|
|
28
|
-
metadata: {},
|
|
29
|
-
occurred_at: new Date().toISOString(),
|
|
30
|
-
}),
|
|
31
|
-
},
|
|
32
|
-
polar: {
|
|
33
|
-
platform: 'polar',
|
|
34
|
-
category: 'payment',
|
|
35
|
-
normalize: (payload) => ({
|
|
36
|
-
category: 'payment',
|
|
37
|
-
event: readPath(payload, 'event') === 'payment.completed'
|
|
38
|
-
? 'payment.succeeded'
|
|
39
|
-
: 'payment.unknown',
|
|
40
|
-
amount: readPath(payload, 'payload.amount_cents'),
|
|
41
|
-
currency: String(readPath(payload, 'payload.currency_code') ?? '').toUpperCase() || undefined,
|
|
42
|
-
customer_id: readPath(payload, 'payload.customer_id'),
|
|
43
|
-
transaction_id: readPath(payload, 'payload.transaction_id'),
|
|
44
|
-
metadata: {},
|
|
45
|
-
occurred_at: new Date().toISOString(),
|
|
46
|
-
}),
|
|
47
|
-
},
|
|
48
|
-
clerk: {
|
|
49
|
-
platform: 'clerk',
|
|
50
|
-
category: 'auth',
|
|
51
|
-
normalize: (payload) => ({
|
|
52
|
-
category: 'auth',
|
|
53
|
-
event: readPath(payload, 'type') || 'auth.unknown',
|
|
54
|
-
user_id: readPath(payload, 'data.id'),
|
|
55
|
-
email: readPath(payload, 'data.email_addresses.0.email_address'),
|
|
56
|
-
metadata: {},
|
|
57
|
-
occurred_at: new Date().toISOString(),
|
|
58
|
-
}),
|
|
59
|
-
},
|
|
60
|
-
vercel: {
|
|
61
|
-
platform: 'vercel',
|
|
62
|
-
category: 'infrastructure',
|
|
63
|
-
normalize: (payload) => ({
|
|
64
|
-
category: 'infrastructure',
|
|
65
|
-
event: readPath(payload, 'type') || 'deployment.unknown',
|
|
66
|
-
project_id: readPath(payload, 'payload.project.id'),
|
|
67
|
-
deployment_id: readPath(payload, 'payload.deployment.id'),
|
|
68
|
-
status: 'unknown',
|
|
69
|
-
metadata: {},
|
|
70
|
-
occurred_at: new Date().toISOString(),
|
|
71
|
-
}),
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
function getPlatformNormalizationCategory(platform) {
|
|
75
|
-
return platformNormalizers[platform]?.category || null;
|
|
76
|
-
}
|
|
77
|
-
function getPlatformsByCategory(category) {
|
|
78
|
-
return Object.values(platformNormalizers)
|
|
79
|
-
.filter((spec) => !!spec)
|
|
80
|
-
.filter((spec) => spec.category === category)
|
|
81
|
-
.map((spec) => spec.platform);
|
|
82
|
-
}
|
|
83
|
-
function resolveNormalizeOptions(normalize) {
|
|
84
|
-
if (typeof normalize === 'boolean') {
|
|
85
|
-
return {
|
|
86
|
-
enabled: normalize,
|
|
87
|
-
category: undefined,
|
|
88
|
-
includeRaw: true,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
enabled: normalize?.enabled ?? true,
|
|
93
|
-
category: normalize?.category,
|
|
94
|
-
includeRaw: normalize?.includeRaw ?? true,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
function buildUnknownNormalizedPayload(platform, payload, category, includeRaw, warning) {
|
|
98
|
-
return {
|
|
99
|
-
category: category || 'infrastructure',
|
|
100
|
-
event: payload?.type ?? payload?.event ?? 'unknown',
|
|
101
|
-
_platform: platform,
|
|
102
|
-
_raw: includeRaw ? payload : undefined,
|
|
103
|
-
warning,
|
|
104
|
-
occurred_at: new Date().toISOString(),
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
function normalizePayload(platform, payload, normalize) {
|
|
108
|
-
const options = resolveNormalizeOptions(normalize);
|
|
109
|
-
if (!options.enabled) {
|
|
110
|
-
return payload;
|
|
111
|
-
}
|
|
112
|
-
const spec = platformNormalizers[platform];
|
|
113
|
-
const inferredCategory = spec?.category;
|
|
114
|
-
if (!spec) {
|
|
115
|
-
return buildUnknownNormalizedPayload(platform, payload, options.category, options.includeRaw);
|
|
116
|
-
}
|
|
117
|
-
if (options.category && options.category !== inferredCategory) {
|
|
118
|
-
return buildUnknownNormalizedPayload(platform, payload, inferredCategory, options.includeRaw, `Requested normalization category '${options.category}' does not match platform category '${inferredCategory}'`);
|
|
119
|
-
}
|
|
120
|
-
const normalized = spec.normalize(payload);
|
|
121
|
-
return {
|
|
122
|
-
...normalized,
|
|
123
|
-
_platform: platform,
|
|
124
|
-
_raw: options.includeRaw ? payload : undefined,
|
|
125
|
-
};
|
|
126
|
-
}
|