@railhook/node 2.12.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.
@@ -0,0 +1,241 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.verifySignature = verifySignature;
37
+ exports.verifyStandardWebhook = verifyStandardWebhook;
38
+ exports.constructEvent = constructEvent;
39
+ exports.generateSignature = generateSignature;
40
+ const crypto = __importStar(require("crypto"));
41
+ const errors_1 = require("./errors");
42
+ const SIGNATURE_HEADER = 'x-signature';
43
+ const TIMESTAMP_HEADER = 'x-timestamp';
44
+ const EVENT_ID_HEADER = 'x-event-id';
45
+ const DELIVERY_ID_HEADER = 'x-delivery-id';
46
+ const DEFAULT_TOLERANCE = 300000; // 5 minutes in milliseconds
47
+ /**
48
+ * Verifies the webhook signature using HMAC-SHA256.
49
+ *
50
+ * The header is `t=<unix-ms>,v1=<hex>`, and it may carry **more than one** `v1`.
51
+ * After you rotate an endpoint's secret, Railhook signs each delivery with both
52
+ * the new secret and the retired one for the endpoint's grace window (24 hours
53
+ * by default), so you can deploy the new secret whenever you like instead of at
54
+ * the instant you press rotate. The delivery is authentic if *any* `v1` matches,
55
+ * which is what this checks.
56
+ *
57
+ * @param payload - Raw request body as string
58
+ * @param signature - X-Signature header value (format: t=timestamp,v1=signature[,v1=...])
59
+ * @param secret - Endpoint webhook secret
60
+ * @param options - Verification options
61
+ * @returns true if signature is valid
62
+ * @throws RailhookError if signature is invalid
63
+ */
64
+ function verifySignature(payload, signature, secret, options = {}) {
65
+ const tolerance = options.tolerance ?? DEFAULT_TOLERANCE;
66
+ if (!signature) {
67
+ throw new errors_1.RailhookError('Missing signature header', 400, 'invalid_signature');
68
+ }
69
+ const parts = signature.split(',');
70
+ let timestamp;
71
+ const signatures = [];
72
+ for (const part of parts) {
73
+ const [key, value] = part.split('=');
74
+ if (key === 't')
75
+ timestamp = value?.trim();
76
+ // Collected, not overwritten: a header sent during a secret rotation carries
77
+ // one v1 per valid secret, and keeping only the last would reject whichever
78
+ // of the pair you are currently holding.
79
+ if (key === 'v1' && value)
80
+ signatures.push(value.trim());
81
+ }
82
+ if (!timestamp || signatures.length === 0) {
83
+ throw new errors_1.RailhookError('Invalid signature format. Expected: t=timestamp,v1=signature', 400, 'invalid_signature');
84
+ }
85
+ const timestampMs = parseInt(timestamp, 10);
86
+ const now = Date.now();
87
+ if (Math.abs(now - timestampMs) > tolerance) {
88
+ throw new errors_1.RailhookError('Webhook timestamp is outside tolerance window', 400, 'timestamp_expired');
89
+ }
90
+ const signedPayload = `${timestamp}.${payload}`;
91
+ const expectedSignature = crypto
92
+ .createHmac('sha256', secret)
93
+ .update(signedPayload)
94
+ .digest('hex');
95
+ const expectedBuffer = Buffer.from(expectedSignature);
96
+ // Every candidate is compared — no early exit — so the time taken does not
97
+ // depend on which position matched.
98
+ let matched = false;
99
+ for (const candidate of signatures) {
100
+ const sigBuffer = Buffer.from(candidate);
101
+ if (sigBuffer.length === expectedBuffer.length && crypto.timingSafeEqual(sigBuffer, expectedBuffer)) {
102
+ matched = true;
103
+ }
104
+ }
105
+ if (!matched) {
106
+ throw new errors_1.RailhookError('Invalid signature', 400, 'invalid_signature');
107
+ }
108
+ return true;
109
+ }
110
+ const STANDARD_ID_HEADER = 'webhook-id';
111
+ const STANDARD_TIMESTAMP_HEADER = 'webhook-timestamp';
112
+ const STANDARD_SIGNATURE_HEADER = 'webhook-signature';
113
+ /** Tolerance for the Standard Webhooks headers, whose timestamp is in **seconds**. */
114
+ const DEFAULT_STANDARD_TOLERANCE_SECONDS = 300;
115
+ /**
116
+ * Verifies the [Standard Webhooks](https://www.standardwebhooks.com) headers.
117
+ *
118
+ * Endpoints receive both header sets by default (`signatureScheme: 'BOTH'`), so use
119
+ * whichever you prefer — this one if you would rather your verification match what other
120
+ * providers send, `verifySignature` if you are already verifying `X-Signature`.
121
+ *
122
+ * Two things differ from Railhook's own scheme beyond the header names: the message id is
123
+ * part of what is signed, and the digest is base64 rather than hex. Rotation works the same
124
+ * way — during the grace window the header carries a space-separated signature per valid
125
+ * secret, and any one matching is enough.
126
+ *
127
+ * @param payload - Raw request body as string
128
+ * @param headers - Request headers, including the three `webhook-*` ones
129
+ * @param secret - The endpoint's `standardWebhooksSecret` (`whsec_…`), not the raw secret.
130
+ * A plain secret is accepted too and used as-is.
131
+ * @returns true if the signature is valid
132
+ * @throws RailhookError if it is not
133
+ */
134
+ function verifyStandardWebhook(payload, headers, secret, options = {}) {
135
+ const tolerance = options.toleranceSeconds ?? DEFAULT_STANDARD_TOLERANCE_SECONDS;
136
+ const id = headers[STANDARD_ID_HEADER] || headers['Webhook-Id'];
137
+ const timestamp = headers[STANDARD_TIMESTAMP_HEADER] || headers['Webhook-Timestamp'];
138
+ const signature = headers[STANDARD_SIGNATURE_HEADER] || headers['Webhook-Signature'];
139
+ if (!id || !timestamp || !signature) {
140
+ throw new errors_1.RailhookError('Missing webhook-id, webhook-timestamp or webhook-signature header', 400, 'invalid_signature');
141
+ }
142
+ const timestampSeconds = parseInt(timestamp, 10);
143
+ if (Number.isNaN(timestampSeconds)) {
144
+ throw new errors_1.RailhookError('Invalid webhook-timestamp header', 400, 'invalid_signature');
145
+ }
146
+ const nowSeconds = Math.floor(Date.now() / 1000);
147
+ if (Math.abs(nowSeconds - timestampSeconds) > tolerance) {
148
+ throw new errors_1.RailhookError('Webhook timestamp is outside tolerance window', 400, 'timestamp_expired');
149
+ }
150
+ // `whsec_<base64>` is the conventional form and is what the endpoint's
151
+ // standardWebhooksSecret gives you; the base64 body decodes to the key bytes. Anything
152
+ // else is taken literally, so a raw secret still works.
153
+ const key = secret.startsWith('whsec_')
154
+ ? Buffer.from(secret.slice('whsec_'.length), 'base64')
155
+ : Buffer.from(secret, 'utf8');
156
+ const expected = crypto
157
+ .createHmac('sha256', key)
158
+ .update(`${id}.${timestampSeconds}.${payload}`)
159
+ .digest('base64');
160
+ const expectedBuffer = Buffer.from(expected);
161
+ // Space-separated, one per valid secret during a rotation window. Every candidate is
162
+ // compared with no early exit, so the time taken does not reveal which one matched.
163
+ let matched = false;
164
+ for (const part of signature.trim().split(/\s+/)) {
165
+ const comma = part.indexOf(',');
166
+ if (comma < 1 || part.slice(0, comma) !== 'v1')
167
+ continue;
168
+ const candidate = Buffer.from(part.slice(comma + 1));
169
+ if (candidate.length === expectedBuffer.length && crypto.timingSafeEqual(candidate, expectedBuffer)) {
170
+ matched = true;
171
+ }
172
+ }
173
+ if (!matched) {
174
+ throw new errors_1.RailhookError('Invalid signature', 400, 'invalid_signature');
175
+ }
176
+ return true;
177
+ }
178
+ /**
179
+ * Constructs a webhook event from the request.
180
+ *
181
+ * What Railhook actually PUTs on the wire is the event's **payload**, not an
182
+ * envelope: a `client.events.send({ type: 'order.completed', data: {...} })`
183
+ * arrives at your endpoint as the `data` object alone, with the identifiers
184
+ * carried in headers (`X-Event-Id`, `X-Delivery-Id`, `X-Timestamp`,
185
+ * `X-Sequence-Number`). So:
186
+ *
187
+ * - `eventId` / `deliveryId` / `timestamp` come from the headers and are
188
+ * always populated for a real delivery.
189
+ * - `data` is the parsed body.
190
+ * - `type` is only populated when the body happens to carry a `type` key —
191
+ * which for a default subscription it does not. Route on the payload, or
192
+ * configure the subscription's `payloadTemplate` to wrap the event so that
193
+ * `type` is part of the body.
194
+ *
195
+ * @param payload - Raw request body as string
196
+ * @param headers - Request headers
197
+ * @param secret - Endpoint webhook secret
198
+ * @param options - Verification options
199
+ * @returns Parsed and verified webhook event
200
+ */
201
+ function constructEvent(payload, headers, secret, options = {}) {
202
+ const signature = headers[SIGNATURE_HEADER] || headers['X-Signature'];
203
+ const timestamp = headers[TIMESTAMP_HEADER] || headers['X-Timestamp'];
204
+ const eventId = headers[EVENT_ID_HEADER] || headers['X-Event-Id'];
205
+ const deliveryId = headers[DELIVERY_ID_HEADER] || headers['X-Delivery-Id'];
206
+ if (!signature) {
207
+ throw new errors_1.RailhookError('Missing X-Signature header', 400, 'missing_header');
208
+ }
209
+ verifySignature(payload, signature, secret, options);
210
+ let data;
211
+ try {
212
+ data = JSON.parse(payload);
213
+ }
214
+ catch {
215
+ throw new errors_1.RailhookError('Invalid JSON payload', 400, 'invalid_payload');
216
+ }
217
+ return {
218
+ eventId: eventId || '',
219
+ deliveryId: deliveryId || '',
220
+ timestamp: timestamp ? parseInt(timestamp, 10) : Date.now(),
221
+ type: data.type || '',
222
+ data: data.data || data,
223
+ };
224
+ }
225
+ /**
226
+ * Generates a signature for testing purposes
227
+ * @param payload - Request body as string
228
+ * @param secret - Webhook secret
229
+ * @param timestamp - Optional timestamp (defaults to now)
230
+ * @returns Signature string in format t=timestamp,v1=signature
231
+ */
232
+ function generateSignature(payload, secret, timestamp) {
233
+ const ts = timestamp ?? Date.now();
234
+ const signedPayload = `${ts}.${payload}`;
235
+ const signature = crypto
236
+ .createHmac('sha256', secret)
237
+ .update(signedPayload)
238
+ .digest('hex');
239
+ return `t=${ts},v1=${signature}`;
240
+ }
241
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2ViaG9va3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvd2ViaG9va3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF3Q0EsMENBa0VDO0FBNEJELHNEQWdFQztBQXlCRCx3Q0ErQkM7QUFTRCw4Q0FhQztBQXBSRCwrQ0FBaUM7QUFFakMscUNBQXlDO0FBRXpDLE1BQU0sZ0JBQWdCLEdBQUcsYUFBYSxDQUFDO0FBQ3ZDLE1BQU0sZ0JBQWdCLEdBQUcsYUFBYSxDQUFDO0FBQ3ZDLE1BQU0sZUFBZSxHQUFHLFlBQVksQ0FBQztBQUNyQyxNQUFNLGtCQUFrQixHQUFHLGVBQWUsQ0FBQztBQUUzQyxNQUFNLGlCQUFpQixHQUFHLE1BQU0sQ0FBQyxDQUFDLDRCQUE0QjtBQWM5RDs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILFNBQWdCLGVBQWUsQ0FDN0IsT0FBZSxFQUNmLFNBQWlCLEVBQ2pCLE1BQWMsRUFDZCxVQUF5QixFQUFFO0lBRTNCLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxTQUFTLElBQUksaUJBQWlCLENBQUM7SUFFekQsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ2YsTUFBTSxJQUFJLHNCQUFhLENBQUMsMEJBQTBCLEVBQUUsR0FBRyxFQUFFLG1CQUFtQixDQUFDLENBQUM7SUFDaEYsQ0FBQztJQUVELE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDbkMsSUFBSSxTQUE2QixDQUFDO0lBQ2xDLE1BQU0sVUFBVSxHQUFhLEVBQUUsQ0FBQztJQUVoQyxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQ3pCLE1BQU0sQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNyQyxJQUFJLEdBQUcsS0FBSyxHQUFHO1lBQUUs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LFNBQWtCO0lBRWxCLE1BQU0sRUFBRSxHQUFHLFNBQVMsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDbkMsTUFBTSxhQUFhLEdBQUcsR0FBRyxFQUFFLElBQUksT0FBTyxFQUFFLENBQUM7SUFDekMsTUFBTSxTQUFTLEdBQUcsTUFBTTtTQUNyQixVQUFVLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQztTQUM1QixNQUFNLENBQUMsYUFBYSxDQUFDO1NBQ3JCLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUVqQixPQUFPLEtBQUssRUFBRSxPQUFPLFNBQVMsRUFBRSxDQUFDO0FBQ25DLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjcnlwdG8gZnJvbSAnY3J5cHRvJztcbmltcG9ydCB7IFdlYmhvb2tFdmVudCB9IGZyb20gJy4vdHlwZXMnO1xuaW1wb3J0IHsgUmFpbGhvb2tFcnJvciB9IGZyb20gJy4vZXJyb3JzJztcblxuY29uc3QgU0lHTkFUVVJFX0hFQURFUiA9ICd4LXNpZ25hdHVyZSc7XG5jb25zdCBUSU1FU1RBTVBfSEVBREVSID0gJ3gtdGltZXN0YW1wJztcbmNvbnN0IEVWRU5UX0lEX0hFQURFUiA9ICd4LWV2ZW50LWlkJztcbmNvbnN0IERFTElWRVJZX0lEX0hFQURFUiA9ICd4LWRlbGl2ZXJ5LWlkJztcblxuY29uc3QgREVGQVVMVF9UT0xFUkFOQ0UgPSAzMDAwMDA7IC8vIDUgbWludXRlcyBpbiBtaWxsaXNlY29uZHNcblxuZXhwb3J0IGludGVyZmFjZSBXZWJob29rSGVhZGVycyB7XG4gICd4LXNpZ25hdHVyZSc/OiBzdHJpbmc7XG4gICd4LXRpbWVzdGFtcCc/OiBzdHJpbmc7XG4gICd4LWV2ZW50LWlkJz86IHN0cmluZztcbiAgJ3gtZGVsaXZlcnktaWQnPzogc3RyaW5nO1xuICBba2V5OiBzdHJpbmddOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVmVyaWZ5T3B0aW9ucyB7XG4gIHRvbGVyYW5jZT86IG51bWJlcjtcbn1cblxuLyoqXG4gKiBWZXJpZmllcyB0aGUgd2ViaG9vayBzaWduYXR1cmUgdXNpbmcgSE1BQy1TSEEyNTYuXG4gKlxuICogVGhlIGhlYWRlciBpcyBgdD08dW5peC1tcz4sdjE9PGhleD5gLCBhbmQgaXQgbWF5IGNhcnJ5ICoqbW9yZSB0aGFuIG9uZSoqIGB2MWAuXG4gKiBBZnRlciB5b3Ugcm90YXRlIGFuIGVuZHBvaW50J3Mgc2VjcmV0LCBSYWlsaG9vayBzaWducyBlYWNoIGRlbGl2ZXJ5IHdpdGggYm90aFxuICogdGhlIG5ldyBzZWNyZXQgYW5kIHRoZSByZXRpcmVkIG9uZSBmb3IgdGhlIGVuZHBvaW50J3MgZ3JhY2Ugd2luZG93ICgyNCBob3Vyc1xuICogYnkgZGVmYXVsdCksIHNvIHlvdSBjYW4gZGVwbG95IHRoZSBuZXcgc2VjcmV0IHdoZW5ldmVyIHlvdSBsaWtlIGluc3RlYWQgb2YgYXRcbiAqIHRoZSBpbnN0YW50IHlvdSBwcmVzcyByb3RhdGUuIFRoZSBkZWxpdmVyeSBpcyBhdXRoZW50aWMgaWYgKmFueSogYHYxYCBtYXRjaGVzLFxuICogd2hpY2ggaXMgd2hhdCB0aGlzIGNoZWNrcy5cbiAqXG4gKiBAcGFyYW0gcGF5bG9hZCAtIFJhdyByZXF1ZXN0IGJvZHkgYXMgc3RyaW5nXG4gKiBAcGFyYW0gc2lnbmF0dXJlIC0gWC1TaWduYXR1cmUgaGVhZGVyIHZhbHVlIChmb3JtYXQ6IHQ9dGltZXN0YW1wLHYxPXNpZ25hdHVyZVssdjE9Li4uXSlcbiAqIEBwYXJhbSBzZWNyZXQgLSBFbmRwb2ludCB3ZWJob29rIHNlY3JldFxuICogQHBhcmFtIG9wdGlvbnMgLSBWZXJpZmljYXRpb24gb3B0aW9uc1xuICogQHJldHVybnMgdHJ1ZSBpZiBzaWduYXR1cmUgaXMgdmFsaWRcbiAqIEB0aHJvd3MgUmFpbGhvb2tFcnJvciBpZiBzaWduYXR1cmUgaXMgaW52YWxpZFxuICovXG5leHBvcnQgZnVuY3Rpb24gdmVyaWZ5U2lnbmF0dXJlKFxuICBwYXlsb2FkOiBzdHJpbmcsXG4gIHNpZ25hdHVyZTogc3RyaW5nLFxuICBzZWNyZXQ6IHN0cmluZyxcbiAgb3B0aW9uczogVmVyaWZ5T3B0aW9ucyA9IHt9XG4pOiBib29sZWFuIHtcbiAgY29uc3QgdG9sZXJhbmNlID0gb3B0aW9ucy50b2xlcmFuY2UgPz8gREVGQVVMVF9UT0xFUkFOQ0U7XG5cbiAgaWYgKCFzaWduYXR1cmUpIHtcbiAgICB0aHJvdyBuZXcgUmFpbGhvb2tFcnJvcignTWlzc2luZyBzaWduYXR1cmUgaGVhZGVyJywgNDAwLCAnaW52YWxpZF9zaWduYXR1cmUnKTtcbiAgfVxuXG4gIGNvbnN0IHBhcnRzID0gc2lnbmF0dXJlLnNwbGl0KCcsJyk7XG4gIGxldCB0aW1lc3RhbXA6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgY29uc3Qgc2lnbmF0dXJlczogc3RyaW5nW10gPSBbXTtcblxuICBmb3IgKGNvbnN0IHBhcnQgb2YgcGFydHMpIHtcbiAgICBjb25zdCBba2V5LCB2YWx1ZV0gPSBwYXJ0LnNwbGl0KCc9Jyk7XG4gICAgaWYgKGtleSA9PT0gJ3QnKSB0aW1lc3RhbXAgPSB2YWx1ZT8udHJpbSgpO1xuICAgIC8vIENvbGxlY3RlZCwgbm90IG92ZXJ3cml0dGVuOiBhIGhlYWRlciBzZW50IGR1cmluZyBhIHNlY3JldCByb3RhdGlvbiBjYXJyaWVzXG4gICAgLy8gb25lIHYxIHBlciB2YWxpZCBzZWNyZXQsIGFuZCBrZWVwaW5nIG9ubHkgdGhlIGxhc3Qgd291bGQgcmVqZWN0IHdoaWNoZXZlclxuICAgIC8vIG9mIHRoZSBwYWlyIHlvdSBhcmUgY3VycmVudGx5IGhvbGRpbmcuXG4gICAgaWYgKGtleSA9PT0gJ3YxJyAmJiB2YWx1ZSkgc2lnbmF0dXJlcy5wdXNoKHZhbHVlLnRyaW0oKSk7XG4gIH1cblxuICBpZiAoIXRpbWVzdGFtcCB8fCBzaWduYXR1cmVzLmxlbmd0aCA9PT0gMCkge1xuICAgIHRocm93IG5ldyBSYWlsaG9va0Vycm9yKFxuICAgICAgJ0ludmFsaWQgc2lnbmF0dXJlIGZvcm1hdC4gRXhwZWN0ZWQ6IHQ9dGltZXN0YW1wLHYxPXNpZ25hdHVyZScsXG4gICAgICA0MDAsXG4gICAgICAnaW52YWxpZF9zaWduYXR1cmUnXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IHRpbWVzdGFtcE1zID0gcGFyc2VJbnQodGltZXN0YW1wLCAxMCk7XG4gIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG5cbiAgaWYgKE1hdGguYWJzKG5vdyAtIHRpbWVzdGFtcE1zKSA+IHRvbGVyYW5jZSkge1xuICAgIHRocm93IG5ldyBSYWlsaG9va0Vycm9yKFxuICAgICAgJ1dlYmhvb2sgdGltZXN0YW1wIGlzIG91dHNpZGUgdG9sZXJhbmNlIHdpbmRvdycsXG4gICAgICA0MDAsXG4gICAgICAndGltZXN0YW1wX2V4cGlyZWQnXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IHNpZ25lZFBheWxvYWQgPSBgJHt0aW1lc3RhbXB9LiR7cGF5bG9hZH1gO1xuICBjb25zdCBleHBlY3RlZFNpZ25hdHVyZSA9IGNyeXB0b1xuICAgIC5jcmVhdGVIbWFjKCdzaGEyNTYnLCBzZWNyZXQpXG4gICAgLnVwZGF0ZShzaWduZWRQYXlsb2FkKVxuICAgIC5kaWdlc3QoJ2hleCcpO1xuXG4gIGNvbnN0IGV4cGVjdGVkQnVmZmVyID0gQnVmZmVyLmZyb20oZXhwZWN0ZWRTaWduYXR1cmUpO1xuICAvLyBFdmVyeSBjYW5kaWRhdGUgaXMgY29tcGFyZWQg4oCUIG5vIGVhcmx5IGV4aXQg4oCUIHNvIHRoZSB0aW1lIHRha2VuIGRvZXMgbm90XG4gIC8vIGRlcGVuZCBvbiB3aGljaCBwb3NpdGlvbiBtYXRjaGVkLlxuICBsZXQgbWF0Y2hlZCA9IGZhbHNlO1xuICBmb3IgKGNvbnN0IGNhbmRpZGF0ZSBvZiBzaWduYXR1cmVzKSB7XG4gICAgY29uc3Qgc2lnQnVmZmVyID0gQnVmZmVyLmZyb20oY2FuZGlkYXRlKTtcbiAgICBpZiAoc2lnQnVmZmVyLmxlbmd0aCA9PT0gZXhwZWN0ZWRCdWZmZXIubGVuZ3RoICYmIGNyeXB0by50aW1pbmdTYWZlRXF1YWwoc2lnQnVmZmVyLCBleHBlY3RlZEJ1ZmZlcikpIHtcbiAgICAgIG1hdGNoZWQgPSB0cnVlO1xuICAgIH1cbiAgfVxuXG4gIGlmICghbWF0Y2hlZCkge1xuICAgIHRocm93IG5ldyBSYWlsaG9va0Vycm9yKCdJbnZhbGlkIHNpZ25hdHVyZScsIDQwMCwgJ2ludmFsaWRfc2lnbmF0dXJlJyk7XG4gIH1cblxuICByZXR1cm4gdHJ1ZTtcbn1cblxuY29uc3QgU1RBTkRBUkRfSURfSEVBREVSID0gJ3dlYmhvb2staWQnO1xuY29uc3QgU1RBTkRBUkRfVElNRVNUQU1QX0hFQURFUiA9ICd3ZWJob29rLXRpbWVzdGFtcCc7XG5jb25zdCBTVEFOREFSRF9TSUdOQVRVUkVfSEVBREVSID0gJ3dlYmhvb2stc2lnbmF0dXJlJztcblxuLyoqIFRvbGVyYW5jZSBmb3IgdGhlIFN0YW5kYXJkIFdlYmhvb2tzIGhlYWRlcnMsIHdob3NlIHRpbWVzdGFtcCBpcyBpbiAqKnNlY29uZHMqKi4gKi9cbmNvbnN0IERFRkFVTFRfU1RBTkRBUkRfVE9MRVJBTkNFX1NFQ09ORFMgPSAzMDA7XG5cbi8qKlxuICogVmVyaWZpZXMgdGhlIFtTdGFuZGFyZCBXZWJob29rc10oaHR0cHM6Ly93d3cuc3RhbmRhcmR3ZWJob29rcy5jb20pIGhlYWRlcnMuXG4gKlxuICogRW5kcG9pbnRzIHJlY2VpdmUgYm90aCBoZWFkZXIgc2V0cyBieSBkZWZhdWx0IChgc2lnbmF0dXJlU2NoZW1lOiAnQk9USCdgKSwgc28gdXNlXG4gKiB3aGljaGV2ZXIgeW91IHByZWZlciDigJQgdGhpcyBvbmUgaWYgeW91IHdvdWxkIHJhdGhlciB5b3VyIHZlcmlmaWNhdGlvbiBtYXRjaCB3aGF0IG90aGVyXG4gKiBwcm92aWRlcnMgc2VuZCwgYHZlcmlmeVNpZ25hdHVyZWAgaWYgeW91IGFyZSBhbHJlYWR5IHZlcmlmeWluZyBgWC1TaWduYXR1cmVgLlxuICpcbiAqIFR3byB0aGluZ3MgZGlmZmVyIGZyb20gUmFpbGhvb2sncyBvd24gc2NoZW1lIGJleW9uZCB0aGUgaGVhZGVyIG5hbWVzOiB0aGUgbWVzc2FnZSBpZCBpc1xuICogcGFydCBvZiB3aGF0IGlzIHNpZ25lZCwgYW5kIHRoZSBkaWdlc3QgaXMgYmFzZTY0IHJhdGhlciB0aGFuIGhleC4gUm90YXRpb24gd29ya3MgdGhlIHNhbWVcbiAqIHdheSDigJQgZHVyaW5nIHRoZSBncmFjZSB3aW5kb3cgdGhlIGhlYWRlciBjYXJyaWVzIGEgc3BhY2Utc2VwYXJhdGVkIHNpZ25hdHVyZSBwZXIgdmFsaWRcbiAqIHNlY3JldCwgYW5kIGFueSBvbmUgbWF0Y2hpbmcgaXMgZW5vdWdoLlxuICpcbiAqIEBwYXJhbSBwYXlsb2FkIC0gUmF3IHJlcXVlc3QgYm9keSBhcyBzdHJpbmdcbiAqIEBwYXJhbSBoZWFkZXJzIC0gUmVxdWVzdCBoZWFkZXJzLCBpbmNsdWRpbmcgdGhlIHRocmVlIGB3ZWJob29rLSpgIG9uZXNcbiAqIEBwYXJhbSBzZWNyZXQgLSBUaGUgZW5kcG9pbnQncyBgc3RhbmRhcmRXZWJob29rc1NlY3JldGAgKGB3aHNlY1/igKZgKSwgbm90IHRoZSByYXcgc2VjcmV0LlxuICogICAgICAgICAgICAgICAgIEEgcGxhaW4gc2VjcmV0IGlzIGFjY2VwdGVkIHRvbyBhbmQgdXNlZCBhcy1pcy5cbiAqIEByZXR1cm5zIHRydWUgaWYgdGhlIHNpZ25hdHVyZSBpcyB2YWxpZFxuICogQHRocm93cyBSYWlsaG9va0Vycm9yIGlmIGl0IGlzIG5vdFxuICovXG5leHBvcnQgZnVuY3Rpb24gdmVyaWZ5U3RhbmRhcmRXZWJob29rKFxuICBwYXlsb2FkOiBzdHJpbmcsXG4gIGhlYWRlcnM6IFdlYmhvb2tIZWFkZXJzLFxuICBzZWNyZXQ6IHN0cmluZyxcbiAgb3B0aW9uczogeyB0b2xlcmFuY2VTZWNvbmRzPzogbnVtYmVyIH0gPSB7fVxuKTogYm9vbGVhbiB7XG4gIGNvbnN0IHRvbGVyYW5jZSA9IG9wdGlvbnMudG9sZXJhbmNlU2Vjb25kcyA/PyBERUZBVUxUX1NUQU5EQVJEX1RPTEVSQU5DRV9TRUNPTkRTO1xuXG4gIGNvbnN0IGlkID0gaGVhZGVyc1tTVEFOREFSRF9JRF9IRUFERVJdIHx8IGhlYWRlcnNbJ1dlYmhvb2stSWQnXTtcbiAgY29uc3QgdGltZXN0YW1wID0gaGVhZGVyc1tTVEFOREFSRF9USU1FU1RBTVBfSEVBREVSXSB8fCBoZWFkZXJzWydXZWJob29rLVRpbWVzdGFtcCddO1xuICBjb25zdCBzaWduYXR1cmUgPSBoZWFkZXJzW1NUQU5EQVJEX1NJR05BVFVSRV9IRUFERVJdIHx8IGhlYWRlcnNbJ1dlYmhvb2stU2lnbmF0dXJlJ107XG5cbiAgaWYgKCFpZCB8fCAhdGltZXN0YW1wIHx8ICFzaWduYXR1cmUpIHtcbiAgICB0aHJvdyBuZXcgUmFpbGhvb2tFcnJvcihcbiAgICAgICdNaXNzaW5nIHdlYmhvb2staWQsIHdlYmhvb2stdGltZXN0YW1wIG9yIHdlYmhvb2stc2lnbmF0dXJlIGhlYWRlcicsXG4gICAgICA0MDAsXG4gICAgICAnaW52YWxpZF9zaWduYXR1cmUnXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IHRpbWVzdGFtcFNlY29uZHMgPSBwYXJzZUludCh0aW1lc3RhbXAsIDEwKTtcbiAgaWYgKE51bWJlci5pc05hTih0aW1lc3RhbXBTZWNvbmRzKSkge1xuICAgIHRocm93IG5ldyBSYWlsaG9va0Vycm9yKCdJbnZhbGlkIHdlYmhvb2stdGltZXN0YW1wIGhlYWRlcicsIDQwMCwgJ2ludmFsaWRfc2lnbmF0dXJlJyk7XG4gIH1cblxuICBjb25zdCBub3dTZWNvbmRzID0gTWF0aC5mbG9vcihEYXRlLm5vdygpIC8gMTAwMCk7XG4gIGlmIChNYXRoLmFicyhub3dTZWNvbmRzIC0gdGltZXN0YW1wU2Vjb25kcykgPiB0b2xlcmFuY2UpIHtcbiAgICB0aHJvdyBuZXcgUmFpbGhvb2tFcnJvcihcbiAgICAgICdXZWJob29rIHRpbWVzdGFtcCBpcyBvdXRzaWRlIHRvbGVyYW5jZSB3aW5kb3cnLFxuICAgICAgNDAwLFxuICAgICAgJ3RpbWVzdGFtcF9leHBpcmVkJ1xuICAgICk7XG4gIH1cblxuICAvLyBgd2hzZWNfPGJhc2U2ND5gIGlzIHRoZSBjb252ZW50aW9uYWwgZm9ybSBhbmQgaXMgd2hhdCB0aGUgZW5kcG9pbnQnc1xuICAvLyBzdGFuZGFyZFdlYmhvb2tzU2VjcmV0IGdpdmVzIHlvdTsgdGhlIGJhc2U2NCBib2R5IGRlY29kZXMgdG8gdGhlIGtleSBieXRlcy4gQW55dGhpbmdcbiAgLy8gZWxzZSBpcyB0YWtlbiBsaXRlcmFsbHksIHNvIGEgcmF3IHNlY3JldCBzdGlsbCB3b3Jrcy5cbiAgY29uc3Qga2V5ID0gc2VjcmV0LnN0YXJ0c1dpdGgoJ3doc2VjXycpXG4gICAgPyBCdWZmZXIuZnJvbShzZWNyZXQuc2xpY2UoJ3doc2VjXycubGVuZ3RoKSwgJ2Jhc2U2NCcpXG4gICAgOiBCdWZmZXIuZnJvbShzZWNyZXQsICd1dGY4Jyk7XG5cbiAgY29uc3QgZXhwZWN0ZWQgPSBjcnlwdG9cbiAgICAuY3JlYXRlSG1hYygnc2hhMjU2Jywga2V5KVxuICAgIC51cGRhdGUoYCR7aWR9LiR7dGltZXN0YW1wU2Vjb25kc30uJHtwYXlsb2FkfWApXG4gICAgLmRpZ2VzdCgnYmFzZTY0Jyk7XG4gIGNvbnN0IGV4cGVjdGVkQnVmZmVyID0gQnVmZmVyLmZyb20oZXhwZWN0ZWQpO1xuXG4gIC8vIFNwYWNlLXNlcGFyYXRlZCwgb25lIHBlciB2YWxpZCBzZWNyZXQgZHVyaW5nIGEgcm90YXRpb24gd2luZG93LiBFdmVyeSBjYW5kaWRhdGUgaXNcbiAgLy8gY29tcGFyZWQgd2l0aCBubyBlYXJseSBleGl0LCBzbyB0aGUgdGltZSB0YWtlbiBkb2VzIG5vdCByZXZlYWwgd2hpY2ggb25lIG1hdGNoZWQuXG4gIGxldCBtYXRjaGVkID0gZmFsc2U7XG4gIGZvciAoY29uc3QgcGFydCBvZiBzaWduYXR1cmUudHJpbSgpLnNwbGl0KC9cXHMrLykpIHtcbiAgICBjb25zdCBjb21tYSA9IHBhcnQuaW5kZXhPZignLCcpO1xuICAgIGlmIChjb21tYSA8IDEgfHwgcGFydC5zbGljZSgwLCBjb21tYSkgIT09ICd2MScpIGNvbnRpbnVlO1xuICAgIGNvbnN0IGNhbmRpZGF0ZSA9IEJ1ZmZlci5mcm9tKHBhcnQuc2xpY2UoY29tbWEgKyAxKSk7XG4gICAgaWYgKGNhbmRpZGF0ZS5sZW5ndGggPT09IGV4cGVjdGVkQnVmZmVyLmxlbmd0aCAmJiBjcnlwdG8udGltaW5nU2FmZUVxdWFsKGNhbmRpZGF0ZSwgZXhwZWN0ZWRCdWZmZXIpKSB7XG4gICAgICBtYXRjaGVkID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICBpZiAoIW1hdGNoZWQpIHtcbiAgICB0aHJvdyBuZXcgUmFpbGhvb2tFcnJvcignSW52YWxpZCBzaWduYXR1cmUnLCA0MDAsICdpbnZhbGlkX3NpZ25hdHVyZScpO1xuICB9XG5cbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKlxuICogQ29uc3RydWN0cyBhIHdlYmhvb2sgZXZlbnQgZnJvbSB0aGUgcmVxdWVzdC5cbiAqXG4gKiBXaGF0IFJhaWxob29rIGFjdHVhbGx5IFBVVHMgb24gdGhlIHdpcmUgaXMgdGhlIGV2ZW50J3MgKipwYXlsb2FkKiosIG5vdCBhblxuICogZW52ZWxvcGU6IGEgYGNsaWVudC5ldmVudHMuc2VuZCh7IHR5cGU6ICdvcmRlci5jb21wbGV0ZWQnLCBkYXRhOiB7Li4ufSB9KWBcbiAqIGFycml2ZXMgYXQgeW91ciBlbmRwb2ludCBhcyB0aGUgYGRhdGFgIG9iamVjdCBhbG9uZSwgd2l0aCB0aGUgaWRlbnRpZmllcnNcbiAqIGNhcnJpZWQgaW4gaGVhZGVycyAoYFgtRXZlbnQtSWRgLCBgWC1EZWxpdmVyeS1JZGAsIGBYLVRpbWVzdGFtcGAsXG4gKiBgWC1TZXF1ZW5jZS1OdW1iZXJgKS4gU286XG4gKlxuICogLSBgZXZlbnRJZGAgLyBgZGVsaXZlcnlJZGAgLyBgdGltZXN0YW1wYCBjb21lIGZyb20gdGhlIGhlYWRlcnMgYW5kIGFyZVxuICogICBhbHdheXMgcG9wdWxhdGVkIGZvciBhIHJlYWwgZGVsaXZlcnkuXG4gKiAtIGBkYXRhYCBpcyB0aGUgcGFyc2VkIGJvZHkuXG4gKiAtIGB0eXBlYCBpcyBvbmx5IHBvcHVsYXRlZCB3aGVuIHRoZSBib2R5IGhhcHBlbnMgdG8gY2FycnkgYSBgdHlwZWAga2V5IOKAlFxuICogICB3aGljaCBmb3IgYSBkZWZhdWx0IHN1YnNjcmlwdGlvbiBpdCBkb2VzIG5vdC4gUm91dGUgb24gdGhlIHBheWxvYWQsIG9yXG4gKiAgIGNvbmZpZ3VyZSB0aGUgc3Vic2NyaXB0aW9uJ3MgYHBheWxvYWRUZW1wbGF0ZWAgdG8gd3JhcCB0aGUgZXZlbnQgc28gdGhhdFxuICogICBgdHlwZWAgaXMgcGFydCBvZiB0aGUgYm9keS5cbiAqXG4gKiBAcGFyYW0gcGF5bG9hZCAtIFJhdyByZXF1ZXN0IGJvZHkgYXMgc3RyaW5nXG4gKiBAcGFyYW0gaGVhZGVycyAtIFJlcXVlc3QgaGVhZGVyc1xuICogQHBhcmFtIHNlY3JldCAtIEVuZHBvaW50IHdlYmhvb2sgc2VjcmV0XG4gKiBAcGFyYW0gb3B0aW9ucyAtIFZlcmlmaWNhdGlvbiBvcHRpb25zXG4gKiBAcmV0dXJucyBQYXJzZWQgYW5kIHZlcmlmaWVkIHdlYmhvb2sgZXZlbnRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbnN0cnVjdEV2ZW50KFxuICBwYXlsb2FkOiBzdHJpbmcsXG4gIGhlYWRlcnM6IFdlYmhvb2tIZWFkZXJzLFxuICBzZWNyZXQ6IHN0cmluZyxcbiAgb3B0aW9uczogVmVyaWZ5T3B0aW9ucyA9IHt9XG4pOiBXZWJob29rRXZlbnQge1xuICBjb25zdCBzaWduYXR1cmUgPSBoZWFkZXJzW1NJR05BVFVSRV9IRUFERVJdIHx8IGhlYWRlcnNbJ1gtU2lnbmF0dXJlJ107XG4gIGNvbnN0IHRpbWVzdGFtcCA9IGhlYWRlcnNbVElNRVNUQU1QX0hFQURFUl0gfHwgaGVhZGVyc1snWC1UaW1lc3RhbXAnXTtcbiAgY29uc3QgZXZlbnRJZCA9IGhlYWRlcnNbRVZFTlRfSURfSEVBREVSXSB8fCBoZWFkZXJzWydYLUV2ZW50LUlkJ107XG4gIGNvbnN0IGRlbGl2ZXJ5SWQgPSBoZWFkZXJzW0RFTElWRVJZX0lEX0hFQURFUl0gfHwgaGVhZGVyc1snWC1EZWxpdmVyeS1JZCddO1xuXG4gIGlmICghc2lnbmF0dXJlKSB7XG4gICAgdGhyb3cgbmV3IFJhaWxob29rRXJyb3IoJ01pc3NpbmcgWC1TaWduYXR1cmUgaGVhZGVyJywgNDAwLCAnbWlzc2luZ19oZWFkZXInKTtcbiAgfVxuXG4gIHZlcmlmeVNpZ25hdHVyZShwYXlsb2FkLCBzaWduYXR1cmUsIHNlY3JldCwgb3B0aW9ucyk7XG5cbiAgbGV0IGRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICB0cnkge1xuICAgIGRhdGEgPSBKU09OLnBhcnNlKHBheWxvYWQpO1xuICB9IGNhdGNoIHtcbiAgICB0aHJvdyBuZXcgUmFpbGhvb2tFcnJvcignSW52YWxpZCBKU09OIHBheWxvYWQnLCA0MDAsICdpbnZhbGlkX3BheWxvYWQnKTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgZXZlbnRJZDogZXZlbnRJZCB8fCAnJyxcbiAgICBkZWxpdmVyeUlkOiBkZWxpdmVyeUlkIHx8ICcnLFxuICAgIHRpbWVzdGFtcDogdGltZXN0YW1wID8gcGFyc2VJbnQodGltZXN0YW1wLCAxMCkgOiBEYXRlLm5vdygpLFxuICAgIHR5cGU6IChkYXRhLnR5cGUgYXMgc3RyaW5nKSB8fCAnJyxcbiAgICBkYXRhOiAoZGF0YS5kYXRhIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KSB8fCBkYXRhLFxuICB9O1xufVxuXG4vKipcbiAqIEdlbmVyYXRlcyBhIHNpZ25hdHVyZSBmb3IgdGVzdGluZyBwdXJwb3Nlc1xuICogQHBhcmFtIHBheWxvYWQgLSBSZXF1ZXN0IGJvZHkgYXMgc3RyaW5nXG4gKiBAcGFyYW0gc2VjcmV0IC0gV2ViaG9vayBzZWNyZXRcbiAqIEBwYXJhbSB0aW1lc3RhbXAgLSBPcHRpb25hbCB0aW1lc3RhbXAgKGRlZmF1bHRzIHRvIG5vdylcbiAqIEByZXR1cm5zIFNpZ25hdHVyZSBzdHJpbmcgaW4gZm9ybWF0IHQ9dGltZXN0YW1wLHYxPXNpZ25hdHVyZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVTaWduYXR1cmUoXG4gIHBheWxvYWQ6IHN0cmluZyxcbiAgc2VjcmV0OiBzdHJpbmcsXG4gIHRpbWVzdGFtcD86IG51bWJlclxuKTogc3RyaW5nIHtcbiAgY29uc3QgdHMgPSB0aW1lc3RhbXAgPz8gRGF0ZS5ub3coKTtcbiAgY29uc3Qgc2lnbmVkUGF5bG9hZCA9IGAke3RzfS4ke3BheWxvYWR9YDtcbiAgY29uc3Qgc2lnbmF0dXJlID0gY3J5cHRvXG4gICAgLmNyZWF0ZUhtYWMoJ3NoYTI1NicsIHNlY3JldClcbiAgICAudXBkYXRlKHNpZ25lZFBheWxvYWQpXG4gICAgLmRpZ2VzdCgnaGV4Jyk7XG5cbiAgcmV0dXJuIGB0PSR7dHN9LHYxPSR7c2lnbmF0dXJlfWA7XG59XG4iXX0=
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@railhook/node",
3
+ "version": "2.12.0",
4
+ "description": "Official Node.js SDK for Railhook — reliable webhook infrastructure",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "test": "jest",
16
+ "test:contract": "jest --config jest.contract.config.js",
17
+ "smoke:live": "npm run build && node scripts/live-api-smoke.mjs",
18
+ "lint": "eslint src --ext .ts",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "webhook",
23
+ "webhooks",
24
+ "api",
25
+ "events",
26
+ "delivery"
27
+ ],
28
+ "author": "Vadym Kykalo <vadymkykalo@gmail.com>",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/vadymkykalo/railhook.git",
33
+ "directory": "sdks/node"
34
+ },
35
+ "engines": {
36
+ "node": ">=16.0.0"
37
+ },
38
+ "dependencies": {},
39
+ "devDependencies": {
40
+ "@types/jest": "^29.5.0",
41
+ "@types/node": "^26.3.0",
42
+ "jest": "^29.7.0",
43
+ "ts-jest": "^29.4.12",
44
+ "typescript": "^5.3.0"
45
+ },
46
+ "peerDependencies": {}
47
+ }