@hookflo/tern 1.0.6 → 2.0.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 +70 -10
- package/dist/normalization/index.d.ts +20 -0
- package/dist/normalization/index.js +78 -0
- package/dist/normalization/providers/payment/paypal.d.ts +2 -0
- package/dist/normalization/providers/payment/paypal.js +12 -0
- package/dist/normalization/providers/payment/razorpay.d.ts +2 -0
- package/dist/normalization/providers/payment/razorpay.js +13 -0
- package/dist/normalization/providers/payment/stripe.d.ts +2 -0
- package/dist/normalization/providers/payment/stripe.js +13 -0
- package/dist/normalization/providers/registry.d.ts +5 -0
- package/dist/normalization/providers/registry.js +23 -0
- package/dist/normalization/storage/interface.d.ts +13 -0
- package/dist/normalization/storage/interface.js +2 -0
- package/dist/normalization/storage/memory.d.ts +12 -0
- package/dist/normalization/storage/memory.js +39 -0
- package/dist/normalization/templates/base/auth.d.ts +2 -0
- package/dist/normalization/templates/base/auth.js +22 -0
- package/dist/normalization/templates/base/ecommerce.d.ts +2 -0
- package/dist/normalization/templates/base/ecommerce.js +25 -0
- package/dist/normalization/templates/base/payment.d.ts +2 -0
- package/dist/normalization/templates/base/payment.js +25 -0
- package/dist/normalization/templates/registry.d.ts +6 -0
- package/dist/normalization/templates/registry.js +22 -0
- package/dist/normalization/transformer/engine.d.ts +11 -0
- package/dist/normalization/transformer/engine.js +86 -0
- package/dist/normalization/transformer/validator.d.ts +12 -0
- package/dist/normalization/transformer/validator.js +56 -0
- package/dist/normalization/types.d.ts +79 -0
- package/dist/normalization/types.js +2 -0
- package/dist/platforms/algorithms.d.ts +1 -1
- package/dist/platforms/algorithms.js +89 -89
- package/dist/verifiers/algorithms.d.ts +2 -2
- package/dist/verifiers/algorithms.js +62 -62
- package/dist/verifiers/base.d.ts +1 -1
- package/dist/verifiers/custom-algorithms.d.ts +2 -2
- package/dist/verifiers/custom-algorithms.js +8 -8
- package/package.json +1 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SchemaValidator = void 0;
|
|
4
|
+
class SchemaValidator {
|
|
5
|
+
validateSchema(userSchema, baseTemplate) {
|
|
6
|
+
const errors = [];
|
|
7
|
+
// Ensure required base fields exist and are enabled or have defaults
|
|
8
|
+
for (const baseField of baseTemplate.fields) {
|
|
9
|
+
if (!baseField.required)
|
|
10
|
+
continue;
|
|
11
|
+
const userField = userSchema.fields.find((f) => f.id === baseField.id);
|
|
12
|
+
if (!userField) {
|
|
13
|
+
errors.push(`Missing required field in schema: ${baseField.id}`);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (!userField.enabled && baseField.defaultValue === undefined) {
|
|
17
|
+
errors.push(`Required field disabled without default: ${baseField.id}`);
|
|
18
|
+
}
|
|
19
|
+
if (userField.type !== baseField.type) {
|
|
20
|
+
errors.push(`Type mismatch for field ${baseField.id}: expected ${baseField.type}, got ${userField.type}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return { valid: errors.length === 0, errors };
|
|
24
|
+
}
|
|
25
|
+
validateOutput(output, userSchema, baseTemplate) {
|
|
26
|
+
const errors = [];
|
|
27
|
+
for (const field of userSchema.fields) {
|
|
28
|
+
if (!field.enabled)
|
|
29
|
+
continue;
|
|
30
|
+
const value = output[field.name];
|
|
31
|
+
if (value === undefined) {
|
|
32
|
+
if (field.required)
|
|
33
|
+
errors.push(`Missing required field in output: ${field.name}`);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (!this.matchesType(value, field.type)) {
|
|
37
|
+
errors.push(`Type mismatch for output field ${field.name}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { valid: errors.length === 0, errors };
|
|
41
|
+
}
|
|
42
|
+
matchesType(value, type) {
|
|
43
|
+
if (type === 'number')
|
|
44
|
+
return typeof value === 'number' && !Number.isNaN(value);
|
|
45
|
+
if (type === 'string')
|
|
46
|
+
return typeof value === 'string';
|
|
47
|
+
if (type === 'boolean')
|
|
48
|
+
return typeof value === 'boolean';
|
|
49
|
+
if (type === 'object')
|
|
50
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
51
|
+
if (type === 'array')
|
|
52
|
+
return Array.isArray(value);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.SchemaValidator = SchemaValidator;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export type TemplateCategory = 'payment' | 'auth' | 'ecommerce';
|
|
2
|
+
export interface TemplateField {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
type: 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
6
|
+
required: boolean;
|
|
7
|
+
description?: string;
|
|
8
|
+
defaultValue?: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface BaseTemplate {
|
|
11
|
+
id: string;
|
|
12
|
+
category: TemplateCategory;
|
|
13
|
+
version: string;
|
|
14
|
+
fields: TemplateField[];
|
|
15
|
+
}
|
|
16
|
+
export interface UserSchemaField {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
type: TemplateField['type'];
|
|
20
|
+
required: boolean;
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
defaultValue?: unknown;
|
|
23
|
+
}
|
|
24
|
+
export interface FieldMapping {
|
|
25
|
+
schemaFieldId: string;
|
|
26
|
+
providerPath: string;
|
|
27
|
+
transform?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ProviderMapping {
|
|
30
|
+
provider: string;
|
|
31
|
+
fieldMappings: FieldMapping[];
|
|
32
|
+
}
|
|
33
|
+
export interface UserSchema {
|
|
34
|
+
id: string;
|
|
35
|
+
userId: string;
|
|
36
|
+
baseTemplateId: string;
|
|
37
|
+
category: TemplateCategory;
|
|
38
|
+
fields: UserSchemaField[];
|
|
39
|
+
providerMappings: ProviderMapping[];
|
|
40
|
+
createdAt: Date;
|
|
41
|
+
updatedAt: Date;
|
|
42
|
+
}
|
|
43
|
+
export interface NormalizedPayloadMeta {
|
|
44
|
+
provider: string;
|
|
45
|
+
schemaId: string;
|
|
46
|
+
schemaVersion: string;
|
|
47
|
+
transformedAt: Date;
|
|
48
|
+
}
|
|
49
|
+
export interface NormalizedResult {
|
|
50
|
+
normalized: Record<string, unknown>;
|
|
51
|
+
meta: NormalizedPayloadMeta;
|
|
52
|
+
}
|
|
53
|
+
export interface CreateSchemaInput {
|
|
54
|
+
userId: string;
|
|
55
|
+
baseTemplateId: string;
|
|
56
|
+
category: TemplateCategory;
|
|
57
|
+
fields: UserSchemaField[];
|
|
58
|
+
providerMappings: ProviderMapping[];
|
|
59
|
+
}
|
|
60
|
+
export interface UpdateSchemaInput {
|
|
61
|
+
fields?: UserSchemaField[];
|
|
62
|
+
providerMappings?: ProviderMapping[];
|
|
63
|
+
}
|
|
64
|
+
export interface ProviderInfoField {
|
|
65
|
+
path: string;
|
|
66
|
+
type?: TemplateField['type'];
|
|
67
|
+
description?: string;
|
|
68
|
+
}
|
|
69
|
+
export interface ProviderInfo {
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
category: TemplateCategory;
|
|
73
|
+
samplePaths?: ProviderInfoField[];
|
|
74
|
+
}
|
|
75
|
+
export interface TransformParams {
|
|
76
|
+
rawPayload: unknown;
|
|
77
|
+
provider: string;
|
|
78
|
+
schemaId: string;
|
|
79
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PlatformAlgorithmConfig, WebhookPlatform, SignatureConfig } from
|
|
1
|
+
import { PlatformAlgorithmConfig, WebhookPlatform, SignatureConfig } from '../types';
|
|
2
2
|
export declare const platformAlgorithmConfigs: Record<WebhookPlatform, PlatformAlgorithmConfig>;
|
|
3
3
|
export declare function getPlatformAlgorithmConfig(platform: WebhookPlatform): PlatformAlgorithmConfig;
|
|
4
4
|
export declare function platformUsesAlgorithm(platform: WebhookPlatform, algorithm: string): boolean;
|
|
@@ -7,139 +7,139 @@ exports.getPlatformsUsingAlgorithm = getPlatformsUsingAlgorithm;
|
|
|
7
7
|
exports.validateSignatureConfig = validateSignatureConfig;
|
|
8
8
|
exports.platformAlgorithmConfigs = {
|
|
9
9
|
github: {
|
|
10
|
-
platform:
|
|
10
|
+
platform: 'github',
|
|
11
11
|
signatureConfig: {
|
|
12
|
-
algorithm:
|
|
13
|
-
headerName:
|
|
14
|
-
headerFormat:
|
|
15
|
-
prefix:
|
|
12
|
+
algorithm: 'hmac-sha256',
|
|
13
|
+
headerName: 'x-hub-signature-256',
|
|
14
|
+
headerFormat: 'prefixed',
|
|
15
|
+
prefix: 'sha256=',
|
|
16
16
|
timestampHeader: undefined,
|
|
17
|
-
payloadFormat:
|
|
17
|
+
payloadFormat: 'raw',
|
|
18
18
|
},
|
|
19
|
-
description:
|
|
19
|
+
description: 'GitHub webhooks use HMAC-SHA256 with sha256= prefix',
|
|
20
20
|
},
|
|
21
21
|
stripe: {
|
|
22
|
-
platform:
|
|
22
|
+
platform: 'stripe',
|
|
23
23
|
signatureConfig: {
|
|
24
|
-
algorithm:
|
|
25
|
-
headerName:
|
|
26
|
-
headerFormat:
|
|
24
|
+
algorithm: 'hmac-sha256',
|
|
25
|
+
headerName: 'stripe-signature',
|
|
26
|
+
headerFormat: 'comma-separated',
|
|
27
27
|
timestampHeader: undefined,
|
|
28
|
-
payloadFormat:
|
|
28
|
+
payloadFormat: 'timestamped',
|
|
29
29
|
customConfig: {
|
|
30
|
-
signatureFormat:
|
|
30
|
+
signatureFormat: 't={timestamp},v1={signature}',
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
|
-
description:
|
|
33
|
+
description: 'Stripe webhooks use HMAC-SHA256 with comma-separated format',
|
|
34
34
|
},
|
|
35
35
|
clerk: {
|
|
36
|
-
platform:
|
|
36
|
+
platform: 'clerk',
|
|
37
37
|
signatureConfig: {
|
|
38
|
-
algorithm:
|
|
39
|
-
headerName:
|
|
40
|
-
headerFormat:
|
|
41
|
-
timestampHeader:
|
|
42
|
-
timestampFormat:
|
|
43
|
-
payloadFormat:
|
|
38
|
+
algorithm: 'hmac-sha256',
|
|
39
|
+
headerName: 'svix-signature',
|
|
40
|
+
headerFormat: 'raw',
|
|
41
|
+
timestampHeader: 'svix-timestamp',
|
|
42
|
+
timestampFormat: 'unix',
|
|
43
|
+
payloadFormat: 'custom',
|
|
44
44
|
customConfig: {
|
|
45
|
-
signatureFormat:
|
|
46
|
-
payloadFormat:
|
|
47
|
-
encoding:
|
|
48
|
-
idHeader:
|
|
45
|
+
signatureFormat: 'v1={signature}',
|
|
46
|
+
payloadFormat: '{id}.{timestamp}.{body}',
|
|
47
|
+
encoding: 'base64',
|
|
48
|
+
idHeader: 'svix-id',
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
|
-
description:
|
|
51
|
+
description: 'Clerk webhooks use HMAC-SHA256 with base64 encoding',
|
|
52
52
|
},
|
|
53
53
|
dodopayments: {
|
|
54
|
-
platform:
|
|
54
|
+
platform: 'dodopayments',
|
|
55
55
|
signatureConfig: {
|
|
56
|
-
algorithm:
|
|
57
|
-
headerName:
|
|
58
|
-
headerFormat:
|
|
59
|
-
timestampHeader:
|
|
60
|
-
timestampFormat:
|
|
61
|
-
payloadFormat:
|
|
56
|
+
algorithm: 'hmac-sha256',
|
|
57
|
+
headerName: 'webhook-signature',
|
|
58
|
+
headerFormat: 'raw',
|
|
59
|
+
timestampHeader: 'webhook-timestamp',
|
|
60
|
+
timestampFormat: 'unix',
|
|
61
|
+
payloadFormat: 'custom',
|
|
62
62
|
customConfig: {
|
|
63
|
-
signatureFormat:
|
|
64
|
-
payloadFormat:
|
|
65
|
-
encoding:
|
|
66
|
-
idHeader:
|
|
63
|
+
signatureFormat: 'v1={signature}',
|
|
64
|
+
payloadFormat: '{id}.{timestamp}.{body}',
|
|
65
|
+
encoding: 'base64',
|
|
66
|
+
idHeader: 'webhook-id',
|
|
67
67
|
},
|
|
68
68
|
},
|
|
69
|
-
description:
|
|
69
|
+
description: 'Dodo Payments webhooks use HMAC-SHA256 with svix-style format (Standard Webhooks)',
|
|
70
70
|
},
|
|
71
71
|
shopify: {
|
|
72
|
-
platform:
|
|
72
|
+
platform: 'shopify',
|
|
73
73
|
signatureConfig: {
|
|
74
|
-
algorithm:
|
|
75
|
-
headerName:
|
|
76
|
-
headerFormat:
|
|
77
|
-
timestampHeader:
|
|
78
|
-
payloadFormat:
|
|
74
|
+
algorithm: 'hmac-sha256',
|
|
75
|
+
headerName: 'x-shopify-hmac-sha256',
|
|
76
|
+
headerFormat: 'raw',
|
|
77
|
+
timestampHeader: 'x-shopify-shop-domain',
|
|
78
|
+
payloadFormat: 'raw',
|
|
79
79
|
},
|
|
80
|
-
description:
|
|
80
|
+
description: 'Shopify webhooks use HMAC-SHA256',
|
|
81
81
|
},
|
|
82
82
|
vercel: {
|
|
83
|
-
platform:
|
|
83
|
+
platform: 'vercel',
|
|
84
84
|
signatureConfig: {
|
|
85
|
-
algorithm:
|
|
86
|
-
headerName:
|
|
87
|
-
headerFormat:
|
|
88
|
-
timestampHeader:
|
|
89
|
-
timestampFormat:
|
|
90
|
-
payloadFormat:
|
|
85
|
+
algorithm: 'hmac-sha256',
|
|
86
|
+
headerName: 'x-vercel-signature',
|
|
87
|
+
headerFormat: 'raw',
|
|
88
|
+
timestampHeader: 'x-vercel-timestamp',
|
|
89
|
+
timestampFormat: 'unix',
|
|
90
|
+
payloadFormat: 'raw',
|
|
91
91
|
},
|
|
92
|
-
description:
|
|
92
|
+
description: 'Vercel webhooks use HMAC-SHA256',
|
|
93
93
|
},
|
|
94
94
|
polar: {
|
|
95
|
-
platform:
|
|
95
|
+
platform: 'polar',
|
|
96
96
|
signatureConfig: {
|
|
97
|
-
algorithm:
|
|
98
|
-
headerName:
|
|
99
|
-
headerFormat:
|
|
100
|
-
timestampHeader:
|
|
101
|
-
timestampFormat:
|
|
102
|
-
payloadFormat:
|
|
97
|
+
algorithm: 'hmac-sha256',
|
|
98
|
+
headerName: 'x-polar-signature',
|
|
99
|
+
headerFormat: 'raw',
|
|
100
|
+
timestampHeader: 'x-polar-timestamp',
|
|
101
|
+
timestampFormat: 'unix',
|
|
102
|
+
payloadFormat: 'raw',
|
|
103
103
|
},
|
|
104
|
-
description:
|
|
104
|
+
description: 'Polar webhooks use HMAC-SHA256',
|
|
105
105
|
},
|
|
106
106
|
supabase: {
|
|
107
|
-
platform:
|
|
107
|
+
platform: 'supabase',
|
|
108
108
|
signatureConfig: {
|
|
109
|
-
algorithm:
|
|
110
|
-
headerName:
|
|
111
|
-
headerFormat:
|
|
112
|
-
payloadFormat:
|
|
109
|
+
algorithm: 'custom',
|
|
110
|
+
headerName: 'x-webhook-token',
|
|
111
|
+
headerFormat: 'raw',
|
|
112
|
+
payloadFormat: 'raw',
|
|
113
113
|
customConfig: {
|
|
114
|
-
type:
|
|
115
|
-
idHeader:
|
|
114
|
+
type: 'token-based',
|
|
115
|
+
idHeader: 'x-webhook-id',
|
|
116
116
|
},
|
|
117
117
|
},
|
|
118
|
-
description:
|
|
118
|
+
description: 'Supabase webhooks use token-based authentication',
|
|
119
119
|
},
|
|
120
120
|
custom: {
|
|
121
|
-
platform:
|
|
121
|
+
platform: 'custom',
|
|
122
122
|
signatureConfig: {
|
|
123
|
-
algorithm:
|
|
124
|
-
headerName:
|
|
125
|
-
headerFormat:
|
|
126
|
-
payloadFormat:
|
|
123
|
+
algorithm: 'hmac-sha256',
|
|
124
|
+
headerName: 'x-webhook-token',
|
|
125
|
+
headerFormat: 'raw',
|
|
126
|
+
payloadFormat: 'raw',
|
|
127
127
|
customConfig: {
|
|
128
|
-
type:
|
|
129
|
-
idHeader:
|
|
128
|
+
type: 'token-based',
|
|
129
|
+
idHeader: 'x-webhook-id',
|
|
130
130
|
},
|
|
131
131
|
},
|
|
132
|
-
description:
|
|
132
|
+
description: 'Custom webhook configuration',
|
|
133
133
|
},
|
|
134
134
|
unknown: {
|
|
135
|
-
platform:
|
|
135
|
+
platform: 'unknown',
|
|
136
136
|
signatureConfig: {
|
|
137
|
-
algorithm:
|
|
138
|
-
headerName:
|
|
139
|
-
headerFormat:
|
|
140
|
-
payloadFormat:
|
|
137
|
+
algorithm: 'hmac-sha256',
|
|
138
|
+
headerName: 'x-webhook-signature',
|
|
139
|
+
headerFormat: 'raw',
|
|
140
|
+
payloadFormat: 'raw',
|
|
141
141
|
},
|
|
142
|
-
description:
|
|
142
|
+
description: 'Unknown platform - using default HMAC-SHA256',
|
|
143
143
|
},
|
|
144
144
|
};
|
|
145
145
|
function getPlatformAlgorithmConfig(platform) {
|
|
@@ -159,14 +159,14 @@ function validateSignatureConfig(config) {
|
|
|
159
159
|
return false;
|
|
160
160
|
}
|
|
161
161
|
switch (config.algorithm) {
|
|
162
|
-
case
|
|
163
|
-
case
|
|
164
|
-
case
|
|
162
|
+
case 'hmac-sha256':
|
|
163
|
+
case 'hmac-sha1':
|
|
164
|
+
case 'hmac-sha512':
|
|
165
165
|
return true;
|
|
166
|
-
case
|
|
167
|
-
case
|
|
166
|
+
case 'rsa-sha256':
|
|
167
|
+
case 'ed25519':
|
|
168
168
|
return !!config.customConfig?.publicKey;
|
|
169
|
-
case
|
|
169
|
+
case 'custom':
|
|
170
170
|
return !!config.customConfig;
|
|
171
171
|
default:
|
|
172
172
|
return false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { WebhookVerifier } from
|
|
2
|
-
import { WebhookVerificationResult, SignatureConfig, WebhookPlatform } from
|
|
1
|
+
import { WebhookVerifier } from './base';
|
|
2
|
+
import { WebhookVerificationResult, SignatureConfig, WebhookPlatform } from '../types';
|
|
3
3
|
export declare abstract class AlgorithmBasedVerifier extends WebhookVerifier {
|
|
4
4
|
protected config: SignatureConfig;
|
|
5
5
|
protected platform: WebhookPlatform;
|