@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.
Files changed (49) hide show
  1. package/README.md +28 -19
  2. package/dist/adapters/cloudflare.d.ts +1 -2
  3. package/dist/adapters/cloudflare.js +5 -1
  4. package/dist/adapters/express.d.ts +1 -2
  5. package/dist/adapters/express.js +5 -1
  6. package/dist/adapters/hono.d.ts +1 -2
  7. package/dist/adapters/hono.js +5 -1
  8. package/dist/adapters/nextjs.d.ts +1 -2
  9. package/dist/adapters/nextjs.js +5 -1
  10. package/dist/adapters/shared.js +5 -2
  11. package/dist/index.d.ts +4 -5
  12. package/dist/index.js +18 -24
  13. package/dist/platforms/algorithms.d.ts +20 -0
  14. package/dist/platforms/algorithms.js +65 -51
  15. package/dist/types.d.ts +7 -72
  16. package/dist/types.js +1 -2
  17. package/dist/verifiers/algorithms.d.ts +5 -1
  18. package/dist/verifiers/algorithms.js +46 -36
  19. package/package.json +1 -1
  20. package/dist/normalization/index.d.ts +0 -20
  21. package/dist/normalization/index.js +0 -78
  22. package/dist/normalization/providers/payment/paypal.d.ts +0 -2
  23. package/dist/normalization/providers/payment/paypal.js +0 -12
  24. package/dist/normalization/providers/payment/razorpay.d.ts +0 -2
  25. package/dist/normalization/providers/payment/razorpay.js +0 -13
  26. package/dist/normalization/providers/payment/stripe.d.ts +0 -2
  27. package/dist/normalization/providers/payment/stripe.js +0 -13
  28. package/dist/normalization/providers/registry.d.ts +0 -5
  29. package/dist/normalization/providers/registry.js +0 -21
  30. package/dist/normalization/simple.d.ts +0 -4
  31. package/dist/normalization/simple.js +0 -126
  32. package/dist/normalization/storage/interface.d.ts +0 -13
  33. package/dist/normalization/storage/interface.js +0 -2
  34. package/dist/normalization/storage/memory.d.ts +0 -12
  35. package/dist/normalization/storage/memory.js +0 -39
  36. package/dist/normalization/templates/base/auth.d.ts +0 -2
  37. package/dist/normalization/templates/base/auth.js +0 -22
  38. package/dist/normalization/templates/base/ecommerce.d.ts +0 -2
  39. package/dist/normalization/templates/base/ecommerce.js +0 -25
  40. package/dist/normalization/templates/base/payment.d.ts +0 -2
  41. package/dist/normalization/templates/base/payment.js +0 -25
  42. package/dist/normalization/templates/registry.d.ts +0 -6
  43. package/dist/normalization/templates/registry.js +0 -22
  44. package/dist/normalization/transformer/engine.d.ts +0 -11
  45. package/dist/normalization/transformer/engine.js +0 -86
  46. package/dist/normalization/transformer/validator.d.ts +0 -12
  47. package/dist/normalization/transformer/validator.js +0 -56
  48. package/dist/normalization/types.d.ts +0 -79
  49. package/dist/normalization/types.js +0 -2
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
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' | 'pagerduty' | 'twilio' | 'unknown';
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
- PagerDuty = "pagerduty",
25
- Twilio = "twilio",
24
+ StandardWebhooks = "standardwebhooks",
26
25
  Custom = "custom",
27
26
  Unknown = "unknown"
28
27
  }
29
- export type SignatureAlgorithm = 'hmac-sha256' | 'hmac-sha1' | 'hmac-sha512' | 'rsa-sha256' | 'ed25519' | 'custom';
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?: 'raw' | 'prefixed' | 'comma-separated';
32
+ headerFormat?: "raw" | "prefixed" | "comma-separated";
34
33
  prefix?: string;
35
34
  timestampHeader?: string;
36
- timestampFormat?: 'unix' | 'iso' | 'custom';
37
- payloadFormat?: 'raw' | 'timestamped' | 'json-stringified' | 'custom';
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 = 'MISSING_SIGNATURE' | 'INVALID_SIGNATURE' | 'TIMESTAMP_EXPIRED' | 'MISSING_TOKEN' | 'INVALID_TOKEN' | 'PLATFORM_NOT_SUPPORTED' | 'NORMALIZATION_ERROR' | 'VERIFICATION_ERROR';
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["PagerDuty"] = "pagerduty";
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', 'twilio'];
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: `Missing signature header: ${this.config.headerName}`,
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: 'Missing required timestamp for webhook verification',
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: "Webhook timestamp expired",
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: "Invalid signature",
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: `${this.platform} verification error: ${error.message}`,
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: `Missing signature header: ${this.config.headerName}`,
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: 'Missing required timestamp for webhook verification',
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: "Webhook timestamp expired",
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: "Invalid signature",
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: `${this.platform} verification error: ${error.message}`,
686
+ error: this.getVerificationErrorMessage(error),
677
687
  errorCode: "VERIFICATION_ERROR",
678
688
  platform: this.platform,
679
689
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hookflo/tern",
3
- "version": "4.3.1-beta",
3
+ "version": "4.4.0",
4
4
  "description": "A robust, scalable webhook verification framework supporting multiple platforms and signature algorithms",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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,2 +0,0 @@
1
- import { ProviderMapping } from '../../types';
2
- export declare const paypalDefaultMapping: ProviderMapping;
@@ -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,2 +0,0 @@
1
- import { ProviderMapping } from '../../types';
2
- export declare const razorpayDefaultMapping: ProviderMapping;
@@ -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,2 +0,0 @@
1
- import { ProviderMapping } from '../../types';
2
- export declare const stripeDefaultMapping: ProviderMapping;
@@ -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,5 +0,0 @@
1
- import { ProviderInfo } from '../types';
2
- export declare const providerRegistry: {
3
- list(category?: ProviderInfo["category"]): ProviderInfo[];
4
- getById(id: string): ProviderInfo | undefined;
5
- };
@@ -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
- }