@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.
- package/README.md +476 -0
- package/dist/__tests__/client.test.d.ts +1 -0
- package/dist/__tests__/client.test.js +105 -0
- package/dist/__tests__/incoming.test.d.ts +1 -0
- package/dist/__tests__/incoming.test.js +316 -0
- package/dist/__tests__/package-identity.test.d.ts +1 -0
- package/dist/__tests__/package-identity.test.js +19 -0
- package/dist/__tests__/standardWebhooks.test.d.ts +1 -0
- package/dist/__tests__/standardWebhooks.test.js +102 -0
- package/dist/__tests__/webhooks.test.d.ts +1 -0
- package/dist/__tests__/webhooks.test.js +190 -0
- package/dist/client.d.ts +103 -0
- package/dist/client.js +344 -0
- package/dist/errors.d.ts +28 -0
- package/dist/errors.js +53 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +37 -0
- package/dist/types.d.ts +293 -0
- package/dist/types.js +3 -0
- package/dist/webhooks.d.ts +83 -0
- package/dist/webhooks.js +241 -0
- package/package.json +47 -0
package/dist/webhooks.js
ADDED
|
@@ -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
|
+
}
|