@fabricorg/webhook-delivery 0.1.1 → 0.1.3
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/CHANGELOG.md +29 -0
- package/README.md +10 -4
- package/dist/index.cjs +8641 -6289
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2227 -779
- package/dist/index.d.ts +2227 -779
- package/dist/index.js +8640 -6287
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +1089 -746
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +102 -32
- package/dist/node.d.ts +102 -32
- package/dist/node.js +1088 -744
- package/dist/node.js.map +1 -1
- package/package.json +8 -7
- package/dist/destination-joCBtgvY.d.cts +0 -1444
- package/dist/destination-joCBtgvY.d.ts +0 -1444
package/dist/node.cjs
CHANGED
|
@@ -1,880 +1,1223 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let buffer = require("buffer");
|
|
3
|
+
let https = require("https");
|
|
4
|
+
let dns_promises = require("dns/promises");
|
|
5
|
+
//#region src/snapshot.ts
|
|
6
|
+
const reflectApply = Reflect.apply;
|
|
7
|
+
const reflectOwnKeys = Reflect.ownKeys;
|
|
8
|
+
const reflectGetOwnPropertyDescriptor$1 = Reflect.getOwnPropertyDescriptor;
|
|
9
|
+
const reflectGetPrototypeOf = Reflect.getPrototypeOf;
|
|
10
|
+
const reflectDefineProperty$1 = Reflect.defineProperty;
|
|
11
|
+
const objectCreate$1 = Object.create;
|
|
12
|
+
const objectFreeze = Object.freeze;
|
|
13
|
+
const objectHasOwn = Object.hasOwn;
|
|
14
|
+
const objectPrototype = Object.prototype;
|
|
15
|
+
const arrayIsArray = Array.isArray;
|
|
16
|
+
const dateGetTime = Date.prototype.getTime;
|
|
17
|
+
Date.prototype.toISOString;
|
|
18
|
+
const regExpExec = RegExp.prototype.exec;
|
|
19
|
+
String.prototype.trim;
|
|
20
|
+
/** Captured `Reflect.apply`, so a replaced `Function.prototype.call` is never used. */
|
|
21
|
+
const apply = reflectApply;
|
|
22
|
+
/** Captured numeric intrinsics. */
|
|
23
|
+
const numberIsFinite = Number.isFinite;
|
|
24
|
+
const numberIsSafeInteger = Number.isSafeInteger;
|
|
25
|
+
Date.parse;
|
|
26
|
+
const freeze = objectFreeze;
|
|
27
|
+
const hasOwn = objectHasOwn;
|
|
28
|
+
const isArray = arrayIsArray;
|
|
29
|
+
/** Stands in for a non-primitive where an option must be a primitive, so validation rejects it. */
|
|
30
|
+
const NOT_PRIMITIVE = Symbol("webhook-delivery.not-primitive");
|
|
31
|
+
/** A null-prototype data descriptor, so `defineProperty` reads nothing from `Object.prototype`. */
|
|
29
32
|
function dataDescriptor(value) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
const descriptor = objectCreate$1(null);
|
|
34
|
+
descriptor.value = value;
|
|
35
|
+
descriptor.enumerable = true;
|
|
36
|
+
descriptor.writable = false;
|
|
37
|
+
descriptor.configurable = false;
|
|
38
|
+
return descriptor;
|
|
36
39
|
}
|
|
40
|
+
/** Define an own, enumerable, read-only data property without consulting any prototype. */
|
|
37
41
|
function define(target, key, value) {
|
|
38
|
-
|
|
42
|
+
reflectDefineProperty$1(target, key, dataDescriptor(value));
|
|
39
43
|
}
|
|
44
|
+
/** A fresh array holding `values`' elements in order, frozen. `values` must be package-owned. */
|
|
40
45
|
function frozenList(values) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
const list = [];
|
|
47
|
+
for (let index = 0; index < values.length; index += 1) define(list, index, values[index]);
|
|
48
|
+
return objectFreeze(list);
|
|
44
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* A frozen null-prototype copy of a package-built object literal. Keys whose
|
|
52
|
+
* value is `undefined` are left out, so optional fields are absent rather
|
|
53
|
+
* than present-and-undefined.
|
|
54
|
+
*/
|
|
45
55
|
function record(fields) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
const out = objectCreate$1(null);
|
|
57
|
+
const keys = reflectOwnKeys(fields);
|
|
58
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
59
|
+
const key = keys[index];
|
|
60
|
+
const descriptor = reflectGetOwnPropertyDescriptor$1(fields, key);
|
|
61
|
+
if (descriptor !== void 0 && objectHasOwn(descriptor, "value") && descriptor.value !== void 0) define(out, key, descriptor.value);
|
|
62
|
+
}
|
|
63
|
+
return objectFreeze(out);
|
|
54
64
|
}
|
|
65
|
+
/** A mutable null-prototype map for package-internal bookkeeping. */
|
|
55
66
|
function emptyMap() {
|
|
56
|
-
|
|
67
|
+
return objectCreate$1(null);
|
|
57
68
|
}
|
|
69
|
+
/** `true` when `re` matches `value`, using the captured `RegExp.prototype.exec`. */
|
|
58
70
|
function matches(re, value) {
|
|
59
|
-
|
|
71
|
+
return reflectApply(regExpExec, re, [value]) !== null;
|
|
60
72
|
}
|
|
73
|
+
/** `true` for a non-array object whose prototype is `Object.prototype` or `null`. A Proxy's trap may run. */
|
|
61
74
|
function isPlainRecord(value) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
75
|
+
if (value === null || typeof value !== "object" || arrayIsArray(value)) return false;
|
|
76
|
+
const prototype = reflectGetPrototypeOf(value);
|
|
77
|
+
return prototype === objectPrototype || prototype === null;
|
|
65
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Read one own data property. Absent is `undefined`; an accessor is refused
|
|
81
|
+
* through `fail` without being called.
|
|
82
|
+
*/
|
|
66
83
|
function readOwn(target, key, field, fail) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
84
|
+
const descriptor = reflectGetOwnPropertyDescriptor$1(target, key);
|
|
85
|
+
if (descriptor === void 0) return void 0;
|
|
86
|
+
if (!objectHasOwn(descriptor, "value")) return fail("accessor", field);
|
|
87
|
+
return descriptor.value;
|
|
71
88
|
}
|
|
89
|
+
/** `undefined` for an absent value, the value for a primitive, {@link NOT_PRIMITIVE} otherwise. */
|
|
72
90
|
function primitive(value) {
|
|
73
|
-
|
|
74
|
-
|
|
91
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return value;
|
|
92
|
+
return NOT_PRIMITIVE;
|
|
75
93
|
}
|
|
94
|
+
/** The time value of a real `Date`, or `NaN` for anything else. */
|
|
76
95
|
function timeOf(value) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
96
|
+
if (value === null || typeof value !== "object") return NaN;
|
|
97
|
+
try {
|
|
98
|
+
return reflectApply(dateGetTime, value, []);
|
|
99
|
+
} catch {
|
|
100
|
+
return NaN;
|
|
101
|
+
}
|
|
83
102
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/address.ts
|
|
105
|
+
const stringCharCodeAt = String.prototype.charCodeAt;
|
|
106
|
+
const stringIndexOf = String.prototype.indexOf;
|
|
107
|
+
const stringSlice$1 = String.prototype.slice;
|
|
108
|
+
const stringSplit = String.prototype.split;
|
|
109
|
+
/** Longest address text parsed: a full IPv6 form with an embedded IPv4 tail is 45 characters. */
|
|
110
|
+
const MAX_ADDRESS_LENGTH = 45;
|
|
111
|
+
/** Longest text inspected at all, leaving room for a zone index. */
|
|
112
|
+
const MAX_TEXT_LENGTH = 64;
|
|
113
|
+
const IPV4_OCTET = "(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)";
|
|
114
|
+
/** Canonical dotted-quad only: no leading zeros, no shorthand, no hex. */
|
|
115
|
+
const IPV4_DOTTED = new RegExp(`^${IPV4_OCTET}(?:\\.${IPV4_OCTET}){3}$`);
|
|
116
|
+
const HEX_GROUP = /^[0-9A-Fa-f]{1,4}$/;
|
|
95
117
|
function ipv4Block(a, b, c, d, prefix) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
ipv4Block(224, 0, 0, 0, 4),
|
|
126
|
-
// multicast
|
|
127
|
-
ipv4Block(240, 0, 0, 0, 4)
|
|
128
|
-
// reserved, including 255.255.255.255
|
|
118
|
+
return freeze({
|
|
119
|
+
base: (a << 24 | b << 16 | c << 8 | d) >>> 0,
|
|
120
|
+
prefix
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* IPv4 blocks that are never a webhook destination. The IANA IPv4
|
|
125
|
+
* Special-Purpose Address Registry's not-globally-reachable entries, plus
|
|
126
|
+
* multicast (224.0.0.0/4) and reserved space (240.0.0.0/4, which holds the
|
|
127
|
+
* limited broadcast address). 192.0.0.0/24 is refused whole, including the
|
|
128
|
+
* two anycast /32s in it that IANA marks reachable; neither is a webhook
|
|
129
|
+
* receiver.
|
|
130
|
+
*/
|
|
131
|
+
const REFUSED_IPV4_BLOCKS = freeze([
|
|
132
|
+
ipv4Block(0, 0, 0, 0, 8),
|
|
133
|
+
ipv4Block(10, 0, 0, 0, 8),
|
|
134
|
+
ipv4Block(100, 64, 0, 0, 10),
|
|
135
|
+
ipv4Block(127, 0, 0, 0, 8),
|
|
136
|
+
ipv4Block(169, 254, 0, 0, 16),
|
|
137
|
+
ipv4Block(172, 16, 0, 0, 12),
|
|
138
|
+
ipv4Block(192, 0, 0, 0, 24),
|
|
139
|
+
ipv4Block(192, 0, 2, 0, 24),
|
|
140
|
+
ipv4Block(192, 88, 99, 0, 24),
|
|
141
|
+
ipv4Block(192, 168, 0, 0, 16),
|
|
142
|
+
ipv4Block(198, 18, 0, 0, 15),
|
|
143
|
+
ipv4Block(198, 51, 100, 0, 24),
|
|
144
|
+
ipv4Block(203, 0, 113, 0, 24),
|
|
145
|
+
ipv4Block(224, 0, 0, 0, 4),
|
|
146
|
+
ipv4Block(240, 0, 0, 0, 4)
|
|
129
147
|
]);
|
|
130
148
|
function inIpv4Block(value, block) {
|
|
131
|
-
|
|
132
|
-
return (value & mask) >>> 0 === block.base;
|
|
149
|
+
return (value & (block.prefix === 0 ? 0 : 4294967295 << 32 - block.prefix >>> 0)) >>> 0 === block.base;
|
|
133
150
|
}
|
|
151
|
+
/** `true` when an IPv4 address, as an unsigned 32-bit integer, is publicly routable. */
|
|
134
152
|
function isPublicIpv4Value(value) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
return true;
|
|
153
|
+
for (let index = 0; index < REFUSED_IPV4_BLOCKS.length; index += 1) if (inIpv4Block(value, REFUSED_IPV4_BLOCKS[index])) return false;
|
|
154
|
+
return true;
|
|
139
155
|
}
|
|
156
|
+
/** Parse a canonical dotted-quad to an unsigned 32-bit integer, or `undefined`. */
|
|
140
157
|
function parseIpv4(text) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
158
|
+
if (!matches(IPV4_DOTTED, text)) return void 0;
|
|
159
|
+
const parts = apply(stringSplit, text, ["."]);
|
|
160
|
+
let value = 0;
|
|
161
|
+
for (let index = 0; index < 4; index += 1) value = value * 256 + decimal(parts[index]);
|
|
162
|
+
return value >>> 0;
|
|
146
163
|
}
|
|
147
164
|
function decimal(text) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
165
|
+
let value = 0;
|
|
166
|
+
for (let index = 0; index < text.length; index += 1) value = value * 10 + (apply(stringCharCodeAt, text, [index]) - 48);
|
|
167
|
+
return value;
|
|
151
168
|
}
|
|
152
169
|
function hex(text) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
170
|
+
let value = 0;
|
|
171
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
172
|
+
const code = apply(stringCharCodeAt, text, [index]);
|
|
173
|
+
const digit = code <= 57 ? code - 48 : code <= 70 ? code - 55 : code - 87;
|
|
174
|
+
value = value * 16 + digit;
|
|
175
|
+
}
|
|
176
|
+
return value;
|
|
160
177
|
}
|
|
178
|
+
/** Parse one side of an IPv6 address (no `::`) into 16-bit words; `tailIpv4` allows a dotted last group. */
|
|
161
179
|
function parseIpv6Groups(text, tailIpv4) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
180
|
+
const words = [];
|
|
181
|
+
if (text === "") return words;
|
|
182
|
+
const groups = apply(stringSplit, text, [":"]);
|
|
183
|
+
for (let index = 0; index < groups.length; index += 1) {
|
|
184
|
+
const group = groups[index];
|
|
185
|
+
if (tailIpv4 && index === groups.length - 1 && apply(stringIndexOf, group, ["."]) !== -1) {
|
|
186
|
+
const v4 = parseIpv4(group);
|
|
187
|
+
if (v4 === void 0) return void 0;
|
|
188
|
+
words[words.length] = v4 >>> 16;
|
|
189
|
+
words[words.length] = v4 & 65535;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (!matches(HEX_GROUP, group)) return void 0;
|
|
193
|
+
words[words.length] = hex(group);
|
|
194
|
+
}
|
|
195
|
+
return words;
|
|
178
196
|
}
|
|
197
|
+
/** Parse an IPv6 address (no brackets, no zone) into eight 16-bit words, or `undefined`. */
|
|
179
198
|
function parseIpv6(text) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
199
|
+
const gap = apply(stringIndexOf, text, ["::"]);
|
|
200
|
+
if (gap === -1) {
|
|
201
|
+
const words = parseIpv6Groups(text, true);
|
|
202
|
+
return words !== void 0 && words.length === 8 ? words : void 0;
|
|
203
|
+
}
|
|
204
|
+
if (apply(stringIndexOf, text, ["::", gap + 1]) !== -1) return void 0;
|
|
205
|
+
const head = parseIpv6Groups(apply(stringSlice$1, text, [0, gap]), false);
|
|
206
|
+
const tail = parseIpv6Groups(apply(stringSlice$1, text, [gap + 2]), true);
|
|
207
|
+
if (head === void 0 || tail === void 0 || head.length + tail.length > 7) return void 0;
|
|
208
|
+
const words = [];
|
|
209
|
+
for (let index = 0; index < head.length; index += 1) words[words.length] = head[index];
|
|
210
|
+
for (let index = head.length + tail.length; index < 8; index += 1) words[words.length] = 0;
|
|
211
|
+
for (let index = 0; index < tail.length; index += 1) words[words.length] = tail[index];
|
|
212
|
+
return words;
|
|
194
213
|
}
|
|
214
|
+
/** The IPv4 address held in two consecutive 16-bit words. */
|
|
195
215
|
function embeddedIpv4(high, low) {
|
|
196
|
-
|
|
216
|
+
return (high << 16 | low) >>> 0;
|
|
197
217
|
}
|
|
218
|
+
/** `true` when an IPv6 address, as eight 16-bit words, is publicly routable. */
|
|
198
219
|
function isPublicIpv6Words(w) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (w[0] === 8194) return isPublicIpv4Value(embeddedIpv4(w[1], w[2]));
|
|
212
|
-
return true;
|
|
220
|
+
if (w[0] === 100 && w[1] === 65435 && w[2] === 0 && w[3] === 0 && w[4] === 0 && w[5] === 0) return isPublicIpv4Value(embeddedIpv4(w[6], w[7]));
|
|
221
|
+
if ((w[0] & 57344) !== 8192) return false;
|
|
222
|
+
if (w[0] === 8193 && (w[1] & 65024) === 0) {
|
|
223
|
+
if (w[1] !== 0) return false;
|
|
224
|
+
const server = embeddedIpv4(w[2], w[3]);
|
|
225
|
+
const client = ~embeddedIpv4(w[6], w[7]) >>> 0;
|
|
226
|
+
return isPublicIpv4Value(server) && isPublicIpv4Value(client);
|
|
227
|
+
}
|
|
228
|
+
if (w[0] === 8193 && w[1] === 3512) return false;
|
|
229
|
+
if (w[0] === 16383 && (w[1] & 61440) === 0) return false;
|
|
230
|
+
if (w[0] === 8194) return isPublicIpv4Value(embeddedIpv4(w[1], w[2]));
|
|
231
|
+
return true;
|
|
213
232
|
}
|
|
233
|
+
/** RFC 5952 text for eight 16-bit words: lowercase, no leading zeros, the longest zero run of two or more compressed. */
|
|
214
234
|
function formatIpv6(w) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
235
|
+
let bestStart = -1;
|
|
236
|
+
let bestLength = 0;
|
|
237
|
+
for (let index = 0; index < 8;) {
|
|
238
|
+
if (w[index] !== 0) {
|
|
239
|
+
index += 1;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
let end = index;
|
|
243
|
+
while (end < 8 && w[end] === 0) end += 1;
|
|
244
|
+
if (end - index > bestLength) {
|
|
245
|
+
bestStart = index;
|
|
246
|
+
bestLength = end - index;
|
|
247
|
+
}
|
|
248
|
+
index = end;
|
|
249
|
+
}
|
|
250
|
+
if (bestLength < 2) bestStart = -1;
|
|
251
|
+
let text = "";
|
|
252
|
+
for (let index = 0; index < 8; index += 1) {
|
|
253
|
+
if (index === bestStart) {
|
|
254
|
+
text += "::";
|
|
255
|
+
index += bestLength - 1;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (text !== "" && !endsWithColons(text)) text += ":";
|
|
259
|
+
text += hexText(w[index]);
|
|
260
|
+
}
|
|
261
|
+
return text;
|
|
242
262
|
}
|
|
243
263
|
function endsWithColons(text) {
|
|
244
|
-
|
|
264
|
+
return text.length >= 2 && apply(stringSlice$1, text, [-2]) === "::";
|
|
245
265
|
}
|
|
246
|
-
|
|
266
|
+
const HEX_DIGITS = "0123456789abcdef";
|
|
247
267
|
function hexText(value) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
268
|
+
if (value === 0) return "0";
|
|
269
|
+
let text = "";
|
|
270
|
+
for (let rest = value; rest > 0; rest = (rest - rest % 16) / 16) text = HEX_DIGITS[rest % 16] + text;
|
|
271
|
+
return text;
|
|
252
272
|
}
|
|
253
273
|
function formatIpv4(value) {
|
|
254
|
-
|
|
274
|
+
return `${value >>> 24}.${value >>> 16 & 255}.${value >>> 8 & 255}.${value & 255}`;
|
|
255
275
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
276
|
+
const NON_PUBLIC_V4 = freeze({
|
|
277
|
+
kind: "non_public",
|
|
278
|
+
family: 4
|
|
279
|
+
});
|
|
280
|
+
const NON_PUBLIC_V6 = freeze({
|
|
281
|
+
kind: "non_public",
|
|
282
|
+
family: 6
|
|
283
|
+
});
|
|
284
|
+
const MALFORMED = freeze({ kind: "malformed" });
|
|
285
|
+
/** @internal Classify a textual IP address (no brackets). The `address` of a public verdict is canonical. */
|
|
259
286
|
function classifyAddress(text) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
287
|
+
if (typeof text !== "string" || text.length === 0 || text.length > MAX_TEXT_LENGTH) return MALFORMED;
|
|
288
|
+
const zone = apply(stringIndexOf, text, ["%"]);
|
|
289
|
+
if (zone !== -1) return parseIpv6(apply(stringSlice$1, text, [0, zone])) === void 0 ? MALFORMED : NON_PUBLIC_V6;
|
|
290
|
+
if (text.length > MAX_ADDRESS_LENGTH) return MALFORMED;
|
|
291
|
+
const v4 = parseIpv4(text);
|
|
292
|
+
if (v4 !== void 0) return isPublicIpv4Value(v4) ? freeze({
|
|
293
|
+
kind: "public",
|
|
294
|
+
address: formatIpv4(v4),
|
|
295
|
+
family: 4
|
|
296
|
+
}) : NON_PUBLIC_V4;
|
|
297
|
+
const v6 = parseIpv6(text);
|
|
298
|
+
if (v6 === void 0) return MALFORMED;
|
|
299
|
+
return isPublicIpv6Words(v6) ? freeze({
|
|
300
|
+
kind: "public",
|
|
301
|
+
address: formatIpv6(v6),
|
|
302
|
+
family: 6
|
|
303
|
+
}) : NON_PUBLIC_V6;
|
|
271
304
|
}
|
|
305
|
+
/** @internal `true` for a canonical dotted-quad, the form WHATWG URL parsing gives an IPv4 host. */
|
|
272
306
|
function isIpv4Literal(text) {
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
307
|
+
return matches(IPV4_DOTTED, text);
|
|
308
|
+
}
|
|
309
|
+
freeze({
|
|
310
|
+
invalid_envelope: "Webhook projection failed: a required envelope or job field is missing or invalid.",
|
|
311
|
+
payload_field_not_json: "Webhook projection failed: an allowlisted payload field is not plain JSON.",
|
|
312
|
+
fact_field_not_json: "Webhook projection failed: a declared fact field is not plain JSON.",
|
|
313
|
+
accessor_property: "Webhook projection failed: accessor properties are not read.",
|
|
314
|
+
inspection_failed: "Webhook projection failed: an input could not be inspected.",
|
|
315
|
+
scope_mismatch: "Webhook projection failed: the subscription's scope does not match the event's tenant and space."
|
|
316
|
+
});
|
|
317
|
+
const CONFIG_MESSAGES = freeze({
|
|
318
|
+
limit_not_positive_finite: "Webhook delivery limit must be a positive, finite number.",
|
|
319
|
+
limit_not_integer: "Webhook delivery limit must be a whole number.",
|
|
320
|
+
limit_out_of_range: "Webhook delivery limit is outside its allowed range.",
|
|
321
|
+
retry_schedule_empty: "Webhook delivery retry schedule must list at least one delay.",
|
|
322
|
+
limit_unreadable: "Webhook delivery limits could not be read."
|
|
323
|
+
});
|
|
324
|
+
const INPUT_MESSAGES = freeze({
|
|
325
|
+
input_unreadable: "Webhook delivery input could not be read.",
|
|
326
|
+
observation_unknown: "Webhook delivery observation is not one decision 10 classifies.",
|
|
327
|
+
status_out_of_range: "Webhook response status must be a whole number from 100 to 599.",
|
|
328
|
+
signature_malformed: "Webhook signature must be a v1=<64 lowercase hex> value.",
|
|
329
|
+
timestamp_invalid: "Webhook timestamp must be a non-negative whole number of seconds.",
|
|
330
|
+
event_id_invalid: "Webhook eventId must be printable ASCII with no control characters or surrounding spaces.",
|
|
331
|
+
delivery_id_invalid: "Webhook deliveryId must be printable ASCII with no control characters or surrounding spaces.",
|
|
332
|
+
attempt_invalid: "Webhook attempt must be a positive whole number.",
|
|
333
|
+
raw_body_not_string: "Webhook raw body must be a string.",
|
|
334
|
+
random_source_missing: "createWebhookRetryDelay requires a random source.",
|
|
335
|
+
random_sample_invalid: "Webhook retry random source must return a number in [0, 1).",
|
|
336
|
+
random_source_failed: "Webhook retry random source failed.",
|
|
337
|
+
denial_code_unknown: "Webhook admission denial code is not in WEBHOOK_ADMISSION_DENIAL_CODES."
|
|
338
|
+
});
|
|
339
|
+
freeze({
|
|
340
|
+
input_invalid: "Webhook store input is missing, malformed or not plain data.",
|
|
341
|
+
record_mismatch: "Webhook store record does not match its parent's event, tenant, space or delivery.",
|
|
342
|
+
delivery_id_conflict: "Webhook store deliveryId is already used by another subscription and event.",
|
|
343
|
+
call_aborted: "Webhook store call was aborted before the store acted.",
|
|
344
|
+
deadline_passed: "Webhook store call deadline passed before the store acted.",
|
|
345
|
+
clock_invalid: "Webhook store clock failed or returned an invalid time.",
|
|
346
|
+
store_failed: "Webhook store failed."
|
|
281
347
|
});
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
raw_body_not_string: "Webhook raw body must be a string.",
|
|
292
|
-
random_source_missing: "createWebhookRetryDelay requires a random source.",
|
|
293
|
-
random_sample_invalid: "Webhook retry random source must return a number in [0, 1).",
|
|
294
|
-
random_source_failed: "Webhook retry random source failed.",
|
|
295
|
-
denial_code_unknown: "Webhook admission denial code is not in WEBHOOK_ADMISSION_DENIAL_CODES."
|
|
348
|
+
freeze({
|
|
349
|
+
event_past_replay_horizon: "Webhook fan-out refused the event: it is older than the replay horizon, so no fan-out job can be stored.",
|
|
350
|
+
fan_out_store_failed: "Webhook fan-out store call failed.",
|
|
351
|
+
fan_out_store_timeout: "Webhook fan-out store call did not settle in time.",
|
|
352
|
+
publish_aborted: "Webhook fan-out publish was aborted before the job was stored.",
|
|
353
|
+
sink_failed: "A required sink of the composite event publisher failed.",
|
|
354
|
+
sink_timeout: "A required sink of the composite event publisher did not settle in time.",
|
|
355
|
+
clock_invalid: "Webhook fan-out clock failed or returned an invalid time.",
|
|
356
|
+
options_invalid: "Webhook fan-out options are missing or malformed."
|
|
296
357
|
});
|
|
358
|
+
freeze({
|
|
359
|
+
delivery_store_failed: "Webhook delivery store call failed.",
|
|
360
|
+
delivery_store_timeout: "Webhook delivery store call did not settle in time.",
|
|
361
|
+
clock_invalid: "Webhook delivery clock failed or returned an invalid time.",
|
|
362
|
+
options_invalid: "Webhook delivery options are missing or malformed."
|
|
363
|
+
});
|
|
364
|
+
freeze({ options_invalid: "Webhook governed-action module options are missing or malformed." });
|
|
297
365
|
function messageFor(table, code, fallback) {
|
|
298
|
-
|
|
366
|
+
return typeof code === "string" && hasOwn(table, code) ? table[code] : fallback;
|
|
299
367
|
}
|
|
300
|
-
|
|
301
|
-
|
|
368
|
+
const reflectDefineProperty = Reflect.defineProperty;
|
|
369
|
+
const objectCreate = Object.create;
|
|
370
|
+
/**
|
|
371
|
+
* An own, enumerable, writable data property, defined with a null-prototype
|
|
372
|
+
* descriptor so neither `Error.prototype` nor `Object.prototype` is consulted.
|
|
373
|
+
* Errors the package throws are frozen afterwards.
|
|
374
|
+
*/
|
|
302
375
|
function defineField(target, key, value) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
376
|
+
const descriptor = objectCreate(null);
|
|
377
|
+
descriptor.value = value;
|
|
378
|
+
descriptor.enumerable = true;
|
|
379
|
+
descriptor.writable = true;
|
|
380
|
+
descriptor.configurable = true;
|
|
381
|
+
reflectDefineProperty(target, key, descriptor);
|
|
309
382
|
}
|
|
383
|
+
/**
|
|
384
|
+
* A configured limit was not usable: zero, negative, `NaN`, infinite, not an
|
|
385
|
+
* integer where a count is required, outside its documented range, or not
|
|
386
|
+
* readable as plain data. The message is constant for the code; `field`
|
|
387
|
+
* names the limit, for example `deliveryRetryDelaysMs[2]`.
|
|
388
|
+
*/
|
|
310
389
|
var WebhookDeliveryConfigError = class extends Error {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
390
|
+
constructor(code, field) {
|
|
391
|
+
super(messageFor(CONFIG_MESSAGES, code, "Webhook delivery limit was rejected."));
|
|
392
|
+
defineField(this, "name", "WebhookDeliveryConfigError");
|
|
393
|
+
defineField(this, "code", code);
|
|
394
|
+
defineField(this, "field", field);
|
|
395
|
+
}
|
|
317
396
|
};
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
397
|
+
const created = /* @__PURE__ */ new WeakSet();
|
|
398
|
+
const weakSetAdd = WeakSet.prototype.add;
|
|
399
|
+
const weakSetHas = WeakSet.prototype.has;
|
|
400
|
+
const TypeErrorConstructor = TypeError;
|
|
401
|
+
const RangeErrorConstructor = RangeError;
|
|
402
|
+
/**
|
|
403
|
+
* Register a factory-made error and freeze it. Its native `stack` is
|
|
404
|
+
* replaced first with fixed package text: V8 frames name the functions on
|
|
405
|
+
* the call stack, and a caller can name its own function (a Proxy trap, a
|
|
406
|
+
* getter, a callback that calls a package helper) after a secret. Replacing
|
|
407
|
+
* it before anything reads it also means `Error.prepareStackTrace` is never
|
|
408
|
+
* consulted for a package error.
|
|
409
|
+
*/
|
|
322
410
|
function mark(error, name, code) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
411
|
+
const stack = objectCreate(null);
|
|
412
|
+
stack.value = `${name}: ${code}`;
|
|
413
|
+
stack.enumerable = false;
|
|
414
|
+
stack.writable = false;
|
|
415
|
+
stack.configurable = false;
|
|
416
|
+
if (!reflectDefineProperty(error, "stack", stack)) throw new TypeErrorConstructor("Webhook delivery could not seal an error.");
|
|
417
|
+
apply(weakSetAdd, created, [error]);
|
|
418
|
+
return freeze(error);
|
|
331
419
|
}
|
|
420
|
+
/** `true` only for an error one of this module's factories created. Runs no code of `value`. */
|
|
332
421
|
function isPackageError(value) {
|
|
333
|
-
|
|
422
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && apply(weakSetHas, created, [value]) === true;
|
|
334
423
|
}
|
|
424
|
+
/** @internal A package-created, frozen limit error. */
|
|
335
425
|
function configError(code, field) {
|
|
336
|
-
|
|
426
|
+
return mark(new WebhookDeliveryConfigError(code, field), "WebhookDeliveryConfigError", code);
|
|
337
427
|
}
|
|
428
|
+
/** @internal A package-created, frozen `TypeError` or `RangeError` carrying `code`. */
|
|
338
429
|
function inputError(kind, code) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
430
|
+
const message = INPUT_MESSAGES[code];
|
|
431
|
+
const error = kind === "type" ? new TypeErrorConstructor(message) : new RangeErrorConstructor(message);
|
|
432
|
+
define(error, "code", code);
|
|
433
|
+
return mark(error, kind === "type" ? "TypeError" : "RangeError", code);
|
|
343
434
|
}
|
|
435
|
+
/**
|
|
436
|
+
* @internal Run an entry point. A package-created error passes through
|
|
437
|
+
* unchanged; anything else thrown (by a Proxy trap, an injected callback or
|
|
438
|
+
* the engine) is never inspected and is replaced by `replacement()`.
|
|
439
|
+
*/
|
|
344
440
|
function guard(run, replacement) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
|
|
441
|
+
try {
|
|
442
|
+
return run();
|
|
443
|
+
} catch (thrown) {
|
|
444
|
+
if (isPackageError(thrown)) throw thrown;
|
|
445
|
+
throw replacement();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
449
|
+
//#region src/limits.ts
|
|
450
|
+
const MINUTE = 6e4;
|
|
451
|
+
const HOUR = 60 * MINUTE;
|
|
452
|
+
const DAY = 24 * HOUR;
|
|
453
|
+
/** The largest delay a JavaScript timer accepts without overflowing. */
|
|
454
|
+
const WEBHOOK_MAX_TIMER_MS = 2147483647;
|
|
455
|
+
Object.freeze({
|
|
456
|
+
maxDeliveryAttempts: 8,
|
|
457
|
+
deliveryRetryDelaysMs: Object.freeze([
|
|
458
|
+
MINUTE,
|
|
459
|
+
5 * MINUTE,
|
|
460
|
+
15 * MINUTE,
|
|
461
|
+
HOUR,
|
|
462
|
+
6 * HOUR,
|
|
463
|
+
12 * HOUR,
|
|
464
|
+
24 * HOUR
|
|
465
|
+
]),
|
|
466
|
+
deliveryDeadlineMs: 72 * HOUR,
|
|
467
|
+
maxFanOutAttempts: 24,
|
|
468
|
+
fanOutRetryDelaysMs: Object.freeze([
|
|
469
|
+
1e3,
|
|
470
|
+
5e3,
|
|
471
|
+
3e4,
|
|
472
|
+
MINUTE,
|
|
473
|
+
5 * MINUTE,
|
|
474
|
+
15 * MINUTE
|
|
475
|
+
]),
|
|
476
|
+
fanOutDeadlineMs: 24 * HOUR,
|
|
477
|
+
attemptTimeoutMs: 15e3,
|
|
478
|
+
portTimeoutMs: 5e3,
|
|
479
|
+
retryJitterRatio: .1,
|
|
480
|
+
maxRetryAfterMs: HOUR,
|
|
481
|
+
replayHorizonMs: 30 * DAY
|
|
482
|
+
});
|
|
483
|
+
Object.freeze([
|
|
484
|
+
"maxDeliveryAttempts",
|
|
485
|
+
"deliveryRetryDelaysMs",
|
|
486
|
+
"deliveryDeadlineMs",
|
|
487
|
+
"maxFanOutAttempts",
|
|
488
|
+
"fanOutRetryDelaysMs",
|
|
489
|
+
"fanOutDeadlineMs",
|
|
490
|
+
"attemptTimeoutMs",
|
|
491
|
+
"portTimeoutMs",
|
|
492
|
+
"retryJitterRatio",
|
|
493
|
+
"maxRetryAfterMs",
|
|
494
|
+
"replayHorizonMs"
|
|
495
|
+
]);
|
|
496
|
+
/** @internal `value` must come from a snapshot (a primitive). */
|
|
353
497
|
function assertPositiveFinite(value, field) {
|
|
354
|
-
|
|
355
|
-
throw configError("limit_not_positive_finite", field);
|
|
356
|
-
}
|
|
498
|
+
if (typeof value !== "number" || !numberIsFinite(value) || value <= 0) throw configError("limit_not_positive_finite", field);
|
|
357
499
|
}
|
|
500
|
+
/** @internal */
|
|
358
501
|
function assertPositiveInteger(value, field) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
throw configError("limit_not_integer", field);
|
|
362
|
-
}
|
|
502
|
+
assertPositiveFinite(value, field);
|
|
503
|
+
if (!numberIsSafeInteger(value)) throw configError("limit_not_integer", field);
|
|
363
504
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region src/call-bounds.ts
|
|
507
|
+
const reflectGetOwnPropertyDescriptor = Reflect.getOwnPropertyDescriptor;
|
|
508
|
+
const abortedGetter = typeof AbortSignal === "function" ? reflectGetOwnPropertyDescriptor(AbortSignal.prototype, "aborted")?.get : void 0;
|
|
509
|
+
const eventTargetAdd = typeof EventTarget === "function" ? EventTarget.prototype.addEventListener : void 0;
|
|
510
|
+
const eventTargetRemove = typeof EventTarget === "function" ? EventTarget.prototype.removeEventListener : void 0;
|
|
511
|
+
const setTimer = setTimeout;
|
|
512
|
+
const clearTimer = clearTimeout;
|
|
513
|
+
const dateNow = Date.now;
|
|
514
|
+
const mathMax = Math.max;
|
|
515
|
+
const mathMin = Math.min;
|
|
516
|
+
const unreadable$2 = () => inputError("type", "input_unreadable");
|
|
517
|
+
const unreadableFail$2 = () => {
|
|
518
|
+
throw unreadable$2();
|
|
378
519
|
};
|
|
520
|
+
/**
|
|
521
|
+
* @internal Read `{ deadline, signal }` at entry, one descriptor read each.
|
|
522
|
+
* The deadline must be a real `Date` with a finite time and the signal a real
|
|
523
|
+
* `AbortSignal`; anything else throws `input_unreadable`.
|
|
524
|
+
*/
|
|
379
525
|
function readCallBounds(options) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
526
|
+
if (options === null || typeof options !== "object") throw unreadable$2();
|
|
527
|
+
const deadlineMs = timeOf(readOwn(options, "deadline", "deadline", unreadableFail$2));
|
|
528
|
+
if (!(deadlineMs === deadlineMs) || deadlineMs === Infinity || deadlineMs === -Infinity) throw unreadable$2();
|
|
529
|
+
const signal = readOwn(options, "signal", "signal", unreadableFail$2);
|
|
530
|
+
if (signal === null || typeof signal !== "object") throw unreadable$2();
|
|
531
|
+
try {
|
|
532
|
+
isAborted(signal);
|
|
533
|
+
} catch {
|
|
534
|
+
throw unreadable$2();
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
deadlineMs,
|
|
538
|
+
signal
|
|
539
|
+
};
|
|
391
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* @internal Whether a real `AbortSignal` has aborted, read through the
|
|
543
|
+
* intrinsic getter (it throws for anything else and runs none of its code).
|
|
544
|
+
*/
|
|
392
545
|
function isAborted(signal) {
|
|
393
|
-
|
|
394
|
-
|
|
546
|
+
if (abortedGetter === void 0) throw unreadable$2();
|
|
547
|
+
return apply(abortedGetter, signal, []) === true;
|
|
395
548
|
}
|
|
549
|
+
/** @internal Call `listener` once when `signal` aborts; the returned function removes it. */
|
|
396
550
|
function onAbort(signal, listener) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
551
|
+
if (eventTargetAdd === void 0 || eventTargetRemove === void 0) throw unreadable$2();
|
|
552
|
+
apply(eventTargetAdd, signal, [
|
|
553
|
+
"abort",
|
|
554
|
+
listener,
|
|
555
|
+
{ once: true }
|
|
556
|
+
]);
|
|
557
|
+
return () => {
|
|
558
|
+
apply(eventTargetRemove, signal, ["abort", listener]);
|
|
559
|
+
};
|
|
402
560
|
}
|
|
561
|
+
/** @internal Capture an optional {@link WebhookClock}; a non-object or a missing `now` throws `input_unreadable`. */
|
|
403
562
|
function captureOptionalClock(clock) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
563
|
+
if (clock === void 0) return {
|
|
564
|
+
target: void 0,
|
|
565
|
+
now: void 0
|
|
566
|
+
};
|
|
567
|
+
if (clock === null || typeof clock !== "object") throw unreadable$2();
|
|
568
|
+
const now = clock.now;
|
|
569
|
+
if (typeof now !== "function") throw unreadable$2();
|
|
570
|
+
return {
|
|
571
|
+
target: clock,
|
|
572
|
+
now
|
|
573
|
+
};
|
|
409
574
|
}
|
|
575
|
+
/** @internal Epoch milliseconds from the clock, or `NaN` when it throws or returns no valid `Date`. Never throws. */
|
|
410
576
|
function clockNowMs(clock) {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
577
|
+
if (clock.now === void 0) return dateNow();
|
|
578
|
+
try {
|
|
579
|
+
const ms = timeOf(apply(clock.now, clock.target, []));
|
|
580
|
+
return ms === Infinity || ms === -Infinity ? NaN : ms;
|
|
581
|
+
} catch {
|
|
582
|
+
return NaN;
|
|
583
|
+
}
|
|
418
584
|
}
|
|
585
|
+
/**
|
|
586
|
+
* @internal Run `fire` after `ms`, clamped to `[0, WEBHOOK_MAX_TIMER_MS]` so a
|
|
587
|
+
* far deadline cannot overflow into an immediate timer. Returns a cancel
|
|
588
|
+
* function.
|
|
589
|
+
*/
|
|
419
590
|
function startTimer(ms, fire) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
591
|
+
const handle = setTimer(fire, mathMax(0, mathMin(ms, WEBHOOK_MAX_TIMER_MS)));
|
|
592
|
+
return () => {
|
|
593
|
+
clearTimer(handle);
|
|
594
|
+
};
|
|
424
595
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
const getter = Reflect.getOwnPropertyDescriptor(URL.prototype, name)?.get;
|
|
431
|
-
if (getter === void 0) throw new TypeError(`URL.prototype.${name} is not an accessor.`);
|
|
432
|
-
return getter;
|
|
596
|
+
const URLConstructor = URL;
|
|
597
|
+
const urlGetter = (name) => {
|
|
598
|
+
const getter = Reflect.getOwnPropertyDescriptor(URL.prototype, name)?.get;
|
|
599
|
+
if (getter === void 0) throw new TypeError(`URL.prototype.${name} is not an accessor.`);
|
|
600
|
+
return getter;
|
|
433
601
|
};
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
602
|
+
const urlProtocol = urlGetter("protocol");
|
|
603
|
+
const urlUsername = urlGetter("username");
|
|
604
|
+
const urlPassword = urlGetter("password");
|
|
605
|
+
const urlPort = urlGetter("port");
|
|
606
|
+
const urlHostname = urlGetter("hostname");
|
|
607
|
+
const urlPathname = urlGetter("pathname");
|
|
608
|
+
const urlSearch = urlGetter("search");
|
|
609
|
+
const stringSlice = String.prototype.slice;
|
|
610
|
+
const stringEndsWith = String.prototype.endsWith;
|
|
611
|
+
Promise.prototype.then;
|
|
612
|
+
AbortController.prototype.abort;
|
|
443
613
|
Reflect.getOwnPropertyDescriptor(AbortController.prototype, "signal").get;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
614
|
+
/** No ASCII control character or space anywhere: WHATWG parsing would strip or drop them silently. */
|
|
615
|
+
const URL_TEXT = /^[^\x00-\x20\x7f]+$/;
|
|
616
|
+
/**
|
|
617
|
+
* `https://` then a host character. WHATWG parsing also accepts
|
|
618
|
+
* `https:host`, `https:///host` and backslashes for a special scheme and
|
|
619
|
+
* finds a host in them; requiring the plain form keeps the host an operator
|
|
620
|
+
* sees in the URL the one that is checked.
|
|
621
|
+
*/
|
|
622
|
+
const URL_PREFIX = /^https:\/\/[^/\\?#@]/i;
|
|
623
|
+
const INVALID_URL$1 = freeze({
|
|
624
|
+
ok: false,
|
|
625
|
+
code: "invalid_url"
|
|
626
|
+
});
|
|
627
|
+
const PRIVATE_URL = freeze({
|
|
628
|
+
ok: false,
|
|
629
|
+
code: "private_address"
|
|
630
|
+
});
|
|
631
|
+
/**
|
|
632
|
+
* @internal {@link checkWebhookDestinationUrl}, optionally accepting a
|
|
633
|
+
* non-public host (an IP literal or a `localhost` name) while every other
|
|
634
|
+
* rule still applies. `true` is passed only when the Node transport was built
|
|
635
|
+
* by the internal test factory with its public-address re-check off.
|
|
636
|
+
*/
|
|
448
637
|
function checkDestinationUrl(url, allowNonPublic) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
638
|
+
try {
|
|
639
|
+
return checkUrl(primitive(url), allowNonPublic);
|
|
640
|
+
} catch {
|
|
641
|
+
return INVALID_URL$1;
|
|
642
|
+
}
|
|
454
643
|
}
|
|
455
644
|
function checkUrl(text, allowNonPublic) {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
645
|
+
if (typeof text !== "string" || text.length === 0 || text.length > 2048) return INVALID_URL$1;
|
|
646
|
+
if (!matches(URL_TEXT, text) || !matches(URL_PREFIX, text)) return INVALID_URL$1;
|
|
647
|
+
let parsed;
|
|
648
|
+
try {
|
|
649
|
+
parsed = new URLConstructor(text);
|
|
650
|
+
} catch {
|
|
651
|
+
return INVALID_URL$1;
|
|
652
|
+
}
|
|
653
|
+
if (apply(urlProtocol, parsed, []) !== "https:") return INVALID_URL$1;
|
|
654
|
+
if (apply(urlUsername, parsed, []) !== "" || apply(urlPassword, parsed, []) !== "") return INVALID_URL$1;
|
|
655
|
+
if (apply(urlPort, parsed, []) !== "") return INVALID_URL$1;
|
|
656
|
+
const host = apply(urlHostname, parsed, []);
|
|
657
|
+
if (host === "" || apply(stringEndsWith, host, ["."])) return INVALID_URL$1;
|
|
658
|
+
const url = `https://${host}${apply(urlPathname, parsed, [])}${apply(urlSearch, parsed, [])}`;
|
|
659
|
+
const v6 = host[0] === "[";
|
|
660
|
+
if (v6 || isIpv4Literal(host)) {
|
|
661
|
+
const text = v6 ? apply(stringSlice, host, [1, -1]) : host;
|
|
662
|
+
const literal = classifyAddress(text);
|
|
663
|
+
if (literal.kind === "malformed") return INVALID_URL$1;
|
|
664
|
+
if (literal.kind === "non_public" && !allowNonPublic) return PRIVATE_URL;
|
|
665
|
+
return record({
|
|
666
|
+
ok: true,
|
|
667
|
+
url,
|
|
668
|
+
hostname: text,
|
|
669
|
+
literal: record({
|
|
670
|
+
address: text,
|
|
671
|
+
family: v6 ? 6 : 4
|
|
672
|
+
})
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
if (!allowNonPublic && (host === "localhost" || apply(stringEndsWith, host, [".localhost"]))) return PRIVATE_URL;
|
|
676
|
+
return record({
|
|
677
|
+
ok: true,
|
|
678
|
+
url,
|
|
679
|
+
hostname: host
|
|
680
|
+
});
|
|
481
681
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
682
|
+
freeze({
|
|
683
|
+
ok: false,
|
|
684
|
+
code: "dns_failure"
|
|
685
|
+
});
|
|
686
|
+
freeze({
|
|
687
|
+
ok: false,
|
|
688
|
+
code: "private_address"
|
|
689
|
+
});
|
|
690
|
+
//#endregion
|
|
691
|
+
//#region src/node/transport.ts
|
|
692
|
+
/** The default response body cap: past it the connection is closed (the crossing chunk is already read). */
|
|
693
|
+
const DEFAULT_NODE_WEBHOOK_MAX_RESPONSE_BYTES = 65536;
|
|
694
|
+
/** The default number of requests one transport runs at once; later calls wait in its queue. */
|
|
695
|
+
const DEFAULT_NODE_WEBHOOK_MAX_CONCURRENT_REQUESTS = 16;
|
|
696
|
+
const MAX_CONCURRENT_REQUESTS_LIMIT = 1024;
|
|
697
|
+
const MAX_HEADERS = 64;
|
|
698
|
+
const MAX_RETRY_AFTER_LENGTH = 128;
|
|
699
|
+
const MAX_RESPONSE_HEADER_BYTES = 16384;
|
|
700
|
+
const unreadable$1 = () => inputError("type", "input_unreadable");
|
|
701
|
+
const unreadableFail$1 = () => {
|
|
702
|
+
throw unreadable$1();
|
|
493
703
|
};
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
704
|
+
const PromiseConstructor = Promise;
|
|
705
|
+
const bufferFrom = buffer.Buffer.from;
|
|
706
|
+
const NumberConstructor = Number;
|
|
707
|
+
/** RFC 9110 token characters. */
|
|
708
|
+
const HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
709
|
+
/** Visible ASCII, space and tab: no CR, LF, NUL or other control characters. */
|
|
710
|
+
const HEADER_VALUE = /^[\t\x20-\x7e]*$/;
|
|
711
|
+
/** Header names the transport sets itself; a caller's copy is dropped. */
|
|
712
|
+
const RESERVED_HEADER = /^(?:host|content-length|connection|transfer-encoding|keep-alive|upgrade|te|expect|proxy-.*)$/i;
|
|
713
|
+
const DECIMAL = /^\d+$/;
|
|
714
|
+
const NOT_SENT_DEADLINE = freeze({
|
|
715
|
+
kind: "not_sent",
|
|
716
|
+
reason: "deadline_expired"
|
|
717
|
+
});
|
|
718
|
+
const NOT_SENT_ABORTED = freeze({
|
|
719
|
+
kind: "not_sent",
|
|
720
|
+
reason: "aborted"
|
|
721
|
+
});
|
|
722
|
+
const TIMEOUT = freeze({ kind: "timeout" });
|
|
723
|
+
const NETWORK_ERROR = freeze({ kind: "network_error" });
|
|
724
|
+
const INVALID_URL = freeze({ kind: "invalid_url" });
|
|
725
|
+
const PRIVATE_ADDRESS = freeze({ kind: "private_address" });
|
|
726
|
+
/**
|
|
727
|
+
* The Node `WebhookTransport` (ADR 0018 decision 6, R3, R8).
|
|
728
|
+
*
|
|
729
|
+
* - **Pinned addresses.** It connects only to `destination.addresses`,
|
|
730
|
+
* through a lookup function that answers with those addresses and nothing
|
|
731
|
+
* else; it never resolves the hostname. The hostname is kept for SNI (left
|
|
732
|
+
* out for an IP literal, as RFC 6066 requires), certificate verification and
|
|
733
|
+
* the `Host` header. Certificate and hostname verification are always on.
|
|
734
|
+
* - **Re-checked destination.** It refuses as `private_address` a destination with no addresses
|
|
735
|
+
* or any address that is not public, and as `invalid_url` one whose URL
|
|
736
|
+
* breaks decision 6's rules or whose hostname does not match it. Nothing is
|
|
737
|
+
* sent.
|
|
738
|
+
* - **No redirects.** A 3xx is returned as a response for the classifier;
|
|
739
|
+
* `Location` is never followed.
|
|
740
|
+
* - **Bounded response.** Body bytes are counted and discarded as they
|
|
741
|
+
* arrive. As soon as the count passes `maxResponseBytes`, or a declared
|
|
742
|
+
* `Content-Length` exceeds it, the connection is closed. The chunk that
|
|
743
|
+
* crosses the cap has already been read, so the bytes read can exceed the
|
|
744
|
+
* cap by up to one chunk as the socket delivered it. Only the status and a
|
|
745
|
+
* `Retry-After` of at most 128 characters are returned; once the status
|
|
746
|
+
* has arrived, the result is that response whatever happens to the body.
|
|
747
|
+
* - **Deadline and cancellation.** A call whose signal has already aborted,
|
|
748
|
+
* or whose deadline has passed, returns `not_sent` without connecting. A
|
|
749
|
+
* call waiting in the queue that is aborted, or whose deadline passes, is
|
|
750
|
+
* removed and returns `not_sent` without ever starting; the same check runs
|
|
751
|
+
* again immediately before the connection is opened. Once started, the
|
|
752
|
+
* request is destroyed at the deadline or on abort and returns `timeout`
|
|
753
|
+
* (or the response, if its status had arrived).
|
|
754
|
+
*
|
|
755
|
+
* Each request uses its own connection (`agent: false`, `Connection:
|
|
756
|
+
* close`), so no pooled socket opened for an earlier resolution is reused.
|
|
757
|
+
* Transport failures (refused connection, TLS or certificate failure, reset)
|
|
758
|
+
* are `network_error`; nothing is logged.
|
|
759
|
+
*
|
|
760
|
+
* @throws {TypeError} `input_unreadable` at construction for unreadable
|
|
761
|
+
* options or `ca`, and from `send` (as a rejected promise) when the request
|
|
762
|
+
* or `{ deadline, signal }` cannot be read: no destination record, a
|
|
763
|
+
* non-string body, a header name or value that is not header-safe, more than
|
|
764
|
+
* 64 headers, or a deadline and signal that are not a real `Date` and
|
|
765
|
+
* `AbortSignal`.
|
|
766
|
+
* @throws {WebhookDeliveryConfigError} when `maxResponseBytes` or
|
|
767
|
+
* `maxConcurrentRequests` (at most 1024) is not a positive whole number.
|
|
768
|
+
*/
|
|
507
769
|
function createNodeWebhookTransport(options = {}) {
|
|
508
|
-
|
|
770
|
+
return buildTransport(guard(() => readTransportOptions(options, PRODUCTION_SWITCHES), unreadable$1));
|
|
509
771
|
}
|
|
510
|
-
|
|
772
|
+
/** What the public constructor always uses: the public-address re-check on, port 443. */
|
|
773
|
+
const PRODUCTION_SWITCHES = freeze({
|
|
774
|
+
allowNonPublicAddresses: false,
|
|
775
|
+
port: 443
|
|
776
|
+
});
|
|
777
|
+
/** @internal Build a transport from validated settings. Not exported from any package entry. */
|
|
511
778
|
function buildTransport(settings) {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
779
|
+
const slots = {
|
|
780
|
+
active: 0,
|
|
781
|
+
pumping: false,
|
|
782
|
+
head: void 0,
|
|
783
|
+
tail: void 0
|
|
784
|
+
};
|
|
785
|
+
return freeze({ send(request, callOptions) {
|
|
786
|
+
let bounds;
|
|
787
|
+
let prepared;
|
|
788
|
+
try {
|
|
789
|
+
bounds = readCallBounds(callOptions);
|
|
790
|
+
prepared = prepareRequest(request, settings);
|
|
791
|
+
} catch {
|
|
792
|
+
return PromiseConstructor.reject(unreadable$1());
|
|
793
|
+
}
|
|
794
|
+
if ("kind" in prepared) return PromiseConstructor.resolve(prepared);
|
|
795
|
+
const refused = notWanted(bounds, settings.clock);
|
|
796
|
+
if (refused !== void 0) return PromiseConstructor.resolve(refused);
|
|
797
|
+
return dispatch(slots, settings, prepared, bounds);
|
|
798
|
+
} });
|
|
530
799
|
}
|
|
800
|
+
/**
|
|
801
|
+
* @internal Read the public options by name, one descriptor read each; no
|
|
802
|
+
* other key of `options` is read, so a test switch placed on it has no
|
|
803
|
+
* effect. The switches come from the caller as a separate argument.
|
|
804
|
+
*/
|
|
531
805
|
function readTransportOptions(options, switches) {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
806
|
+
if (!isPlainRecord(options)) throw unreadable$1();
|
|
807
|
+
const rawMax = primitive(readOwn(options, "maxResponseBytes", "maxResponseBytes", unreadableFail$1));
|
|
808
|
+
const maxResponseBytes = rawMax === void 0 ? DEFAULT_NODE_WEBHOOK_MAX_RESPONSE_BYTES : rawMax;
|
|
809
|
+
assertPositiveInteger(maxResponseBytes, "maxResponseBytes");
|
|
810
|
+
const rawConcurrency = primitive(readOwn(options, "maxConcurrentRequests", "maxConcurrentRequests", unreadableFail$1));
|
|
811
|
+
const maxConcurrentRequests = rawConcurrency === void 0 ? 16 : rawConcurrency;
|
|
812
|
+
assertPositiveInteger(maxConcurrentRequests, "maxConcurrentRequests");
|
|
813
|
+
if (maxConcurrentRequests > MAX_CONCURRENT_REQUESTS_LIMIT) throw configError("limit_out_of_range", "maxConcurrentRequests");
|
|
814
|
+
const ca = readCa(readOwn(options, "ca", "ca", unreadableFail$1));
|
|
815
|
+
const clock = captureOptionalClock(readOwn(options, "clock", "clock", unreadableFail$1));
|
|
816
|
+
return freeze({
|
|
817
|
+
maxResponseBytes,
|
|
818
|
+
maxConcurrentRequests,
|
|
819
|
+
ca,
|
|
820
|
+
clock,
|
|
821
|
+
allowNonPublic: switches.allowNonPublicAddresses === true,
|
|
822
|
+
port: switches.port
|
|
823
|
+
});
|
|
550
824
|
}
|
|
551
825
|
function readCa(value) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
826
|
+
if (value === void 0) return void 0;
|
|
827
|
+
if (typeof value === "string") return value === "" ? unreadableFail$1("not_plain", "ca") : frozenList([value]);
|
|
828
|
+
if (!isArray(value)) throw unreadable$1();
|
|
829
|
+
const length = readOwn(value, "length", "ca", unreadableFail$1);
|
|
830
|
+
if (typeof length !== "number" || !numberIsSafeInteger(length) || length < 1 || length > 1024) throw unreadable$1();
|
|
831
|
+
const copy = [];
|
|
832
|
+
for (let index = 0; index < length; index += 1) {
|
|
833
|
+
const item = readOwn(value, `${index}`, "ca", unreadableFail$1);
|
|
834
|
+
if (typeof item !== "string" || item === "") throw unreadable$1();
|
|
835
|
+
copy[copy.length] = item;
|
|
836
|
+
}
|
|
837
|
+
return frozenList(copy);
|
|
564
838
|
}
|
|
839
|
+
/** Read the request into a snapshot and re-check its destination. A refusal is returned as a result. */
|
|
565
840
|
function prepareRequest(request, settings) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
841
|
+
if (!isPlainRecord(request)) throw unreadable$1();
|
|
842
|
+
const destination = readOwn(request, "destination", "destination", unreadableFail$1);
|
|
843
|
+
const method = primitive(readOwn(request, "method", "method", unreadableFail$1));
|
|
844
|
+
const body = primitive(readOwn(request, "body", "body", unreadableFail$1));
|
|
845
|
+
const headers = readOwn(request, "headers", "headers", unreadableFail$1);
|
|
846
|
+
if (!isPlainRecord(destination) || method !== "POST" || typeof body !== "string") throw unreadable$1();
|
|
847
|
+
const outgoing = readHeaders(headers);
|
|
848
|
+
const url = primitive(readOwn(destination, "url", "destination.url", unreadableFail$1));
|
|
849
|
+
const hostname = primitive(readOwn(destination, "hostname", "destination.hostname", unreadableFail$1));
|
|
850
|
+
const port = primitive(readOwn(destination, "port", "destination.port", unreadableFail$1));
|
|
851
|
+
const rawAddresses = readOwn(destination, "addresses", "destination.addresses", unreadableFail$1);
|
|
852
|
+
const check = checkDestinationUrl(url, settings.allowNonPublic);
|
|
853
|
+
if (!check.ok) return check.code === "private_address" ? PRIVATE_ADDRESS : INVALID_URL;
|
|
854
|
+
if (port !== 443 || hostname !== check.hostname) return INVALID_URL;
|
|
855
|
+
const addresses = readAddresses(rawAddresses, settings.allowNonPublic);
|
|
856
|
+
if (addresses === void 0) return PRIVATE_ADDRESS;
|
|
857
|
+
if (check.literal !== void 0) {
|
|
858
|
+
for (let index = 0; index < addresses.length; index += 1) if (addresses[index].address !== check.literal.address) return INVALID_URL;
|
|
859
|
+
}
|
|
860
|
+
const hostHeader = check.literal !== void 0 && check.literal.family === 6 ? `[${check.hostname}]` : check.hostname;
|
|
861
|
+
const path = check.url.slice(`https://${hostHeader}`.length);
|
|
862
|
+
const encoded = bufferFrom(body, "utf8");
|
|
863
|
+
outgoing["Host"] = hostHeader;
|
|
864
|
+
outgoing["Content-Length"] = `${encoded.length}`;
|
|
865
|
+
outgoing["Connection"] = "close";
|
|
866
|
+
return freeze({
|
|
867
|
+
hostname: check.hostname,
|
|
868
|
+
path,
|
|
869
|
+
headers: outgoing,
|
|
870
|
+
body: encoded,
|
|
871
|
+
addresses,
|
|
872
|
+
literal: check.literal !== void 0
|
|
873
|
+
});
|
|
594
874
|
}
|
|
595
875
|
function readHeaders(value) {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
876
|
+
if (!isPlainRecord(value)) throw unreadable$1();
|
|
877
|
+
const out = emptyMap();
|
|
878
|
+
const keys = Reflect.ownKeys(value);
|
|
879
|
+
if (keys.length > MAX_HEADERS) throw unreadable$1();
|
|
880
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
881
|
+
const name = keys[index];
|
|
882
|
+
if (typeof name !== "string") throw unreadable$1();
|
|
883
|
+
const headerValue = primitive(readOwn(value, name, "headers", unreadableFail$1));
|
|
884
|
+
if (typeof headerValue !== "string" || !matches(HEADER_NAME, name) || !matches(HEADER_VALUE, headerValue)) throw unreadable$1();
|
|
885
|
+
if (matches(RESERVED_HEADER, name)) continue;
|
|
886
|
+
out[name] = headerValue;
|
|
887
|
+
}
|
|
888
|
+
return out;
|
|
609
889
|
}
|
|
890
|
+
/** The vetted addresses, canonical; `undefined` when there are none or one is not public (unless allowed). */
|
|
610
891
|
function readAddresses(value, allowNonPublic) {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
892
|
+
if (!isArray(value)) return void 0;
|
|
893
|
+
const length = readOwn(value, "length", "destination.addresses", unreadableFail$1);
|
|
894
|
+
if (typeof length !== "number" || !numberIsSafeInteger(length) || length < 1 || length > 32) return void 0;
|
|
895
|
+
const out = [];
|
|
896
|
+
for (let index = 0; index < length; index += 1) {
|
|
897
|
+
const entry = readOwn(value, `${index}`, "destination.addresses", unreadableFail$1);
|
|
898
|
+
if (!isPlainRecord(entry)) return void 0;
|
|
899
|
+
const address = primitive(readOwn(entry, "address", "destination.addresses", unreadableFail$1));
|
|
900
|
+
const family = primitive(readOwn(entry, "family", "destination.addresses", unreadableFail$1));
|
|
901
|
+
const judged = classifyAddress(address);
|
|
902
|
+
if (judged.kind === "public" && judged.family === family) {
|
|
903
|
+
out[out.length] = record({
|
|
904
|
+
address: judged.address,
|
|
905
|
+
family: judged.family
|
|
906
|
+
});
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
if (allowNonPublic && judged.kind === "non_public" && judged.family === family && typeof address === "string" && !address.includes("%")) {
|
|
910
|
+
out[out.length] = record({
|
|
911
|
+
address,
|
|
912
|
+
family: judged.family
|
|
913
|
+
});
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
return frozenList(out);
|
|
632
919
|
}
|
|
920
|
+
/** `not_sent` when the call is no longer wanted: aborted, past its deadline, or the clock cannot tell. */
|
|
633
921
|
function notWanted(bounds, clock) {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
return void 0;
|
|
922
|
+
if (isAborted(bounds.signal)) return NOT_SENT_ABORTED;
|
|
923
|
+
if (!(bounds.deadlineMs > clockNowMs(clock))) return NOT_SENT_DEADLINE;
|
|
637
924
|
}
|
|
638
925
|
function dispatch(slots, settings, prepared, bounds) {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
926
|
+
return new PromiseConstructor((resolve) => {
|
|
927
|
+
const release = () => {
|
|
928
|
+
slots.active -= 1;
|
|
929
|
+
pump(slots, settings);
|
|
930
|
+
};
|
|
931
|
+
const begin = () => {
|
|
932
|
+
perform(settings, prepared, bounds, (result) => {
|
|
933
|
+
release();
|
|
934
|
+
resolve(result);
|
|
935
|
+
});
|
|
936
|
+
};
|
|
937
|
+
if (slots.active < settings.maxConcurrentRequests) {
|
|
938
|
+
slots.active += 1;
|
|
939
|
+
begin();
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
enqueue(slots, settings, bounds, begin, resolve);
|
|
943
|
+
});
|
|
657
944
|
}
|
|
658
945
|
function enqueue(slots, settings, bounds, begin, resolve) {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
cancelTimer = startTimer(bounds.deadlineMs - clockNowMs(settings.clock), () => drop(NOT_SENT_DEADLINE));
|
|
687
|
-
removeAbort = onAbort(bounds.signal, () => drop(NOT_SENT_ABORTED));
|
|
946
|
+
let cancelTimer = () => {};
|
|
947
|
+
let removeAbort = () => {};
|
|
948
|
+
const call = {
|
|
949
|
+
start: () => {
|
|
950
|
+
cancelTimer();
|
|
951
|
+
removeAbort();
|
|
952
|
+
begin();
|
|
953
|
+
},
|
|
954
|
+
next: void 0,
|
|
955
|
+
prev: void 0
|
|
956
|
+
};
|
|
957
|
+
const drop = (result) => {
|
|
958
|
+
unlink(slots, call);
|
|
959
|
+
cancelTimer();
|
|
960
|
+
removeAbort();
|
|
961
|
+
resolve(result);
|
|
962
|
+
};
|
|
963
|
+
if (slots.tail === void 0) {
|
|
964
|
+
slots.head = call;
|
|
965
|
+
slots.tail = call;
|
|
966
|
+
} else {
|
|
967
|
+
call.prev = slots.tail;
|
|
968
|
+
slots.tail.next = call;
|
|
969
|
+
slots.tail = call;
|
|
970
|
+
}
|
|
971
|
+
cancelTimer = startTimer(bounds.deadlineMs - clockNowMs(settings.clock), () => drop(NOT_SENT_DEADLINE));
|
|
972
|
+
removeAbort = onAbort(bounds.signal, () => drop(NOT_SENT_ABORTED));
|
|
688
973
|
}
|
|
689
974
|
function unlink(slots, call) {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
975
|
+
if (call.prev !== void 0) call.prev.next = call.next;
|
|
976
|
+
else if (slots.head === call) slots.head = call.next;
|
|
977
|
+
if (call.next !== void 0) call.next.prev = call.prev;
|
|
978
|
+
else if (slots.tail === call) slots.tail = call.prev;
|
|
979
|
+
call.next = void 0;
|
|
980
|
+
call.prev = void 0;
|
|
696
981
|
}
|
|
982
|
+
/**
|
|
983
|
+
* Start queued calls, oldest first, while slots are free. A started call
|
|
984
|
+
* that fails its last check settles synchronously and releases its slot,
|
|
985
|
+
* which calls `pump` again; the `pumping` flag turns that re-entry into
|
|
986
|
+
* another turn of this loop, so draining any backlog uses constant stack
|
|
987
|
+
* depth.
|
|
988
|
+
*/
|
|
697
989
|
function pump(slots, settings) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
990
|
+
if (slots.pumping) return;
|
|
991
|
+
slots.pumping = true;
|
|
992
|
+
try {
|
|
993
|
+
while (slots.head !== void 0 && slots.active < settings.maxConcurrentRequests) {
|
|
994
|
+
const call = slots.head;
|
|
995
|
+
unlink(slots, call);
|
|
996
|
+
slots.active += 1;
|
|
997
|
+
call.start();
|
|
998
|
+
}
|
|
999
|
+
} finally {
|
|
1000
|
+
slots.pumping = false;
|
|
1001
|
+
}
|
|
710
1002
|
}
|
|
1003
|
+
/**
|
|
1004
|
+
* The lookup Node's socket uses: it answers with the vetted addresses for
|
|
1005
|
+
* the vetted name and refuses any other name. Node connects synchronously
|
|
1006
|
+
* from inside this callback, so a throw there (an address it cannot use)
|
|
1007
|
+
* would escape as an uncaught exception; it is caught and reported through
|
|
1008
|
+
* `onFault` instead.
|
|
1009
|
+
*/
|
|
711
1010
|
function pinnedLookup(hostname, addresses, onFault) {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
1011
|
+
return (host, options, callback) => {
|
|
1012
|
+
try {
|
|
1013
|
+
if (host !== hostname) {
|
|
1014
|
+
const error = /* @__PURE__ */ new Error("Webhook transport refused a lookup for a name other than the vetted one.");
|
|
1015
|
+
error.code = "ENOTFOUND";
|
|
1016
|
+
callback(error, "", 4);
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
if (options.all === true) {
|
|
1020
|
+
const all = [];
|
|
1021
|
+
for (let index = 0; index < addresses.length; index += 1) all[all.length] = {
|
|
1022
|
+
address: addresses[index].address,
|
|
1023
|
+
family: addresses[index].family
|
|
1024
|
+
};
|
|
1025
|
+
callback(null, all);
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
callback(null, addresses[0].address, addresses[0].family);
|
|
1029
|
+
} catch {
|
|
1030
|
+
onFault();
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
731
1033
|
}
|
|
732
1034
|
function perform(settings, prepared, bounds, done) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1035
|
+
let settled = false;
|
|
1036
|
+
let status;
|
|
1037
|
+
let retryAfter = null;
|
|
1038
|
+
let request;
|
|
1039
|
+
let response;
|
|
1040
|
+
let cancelTimer = () => {};
|
|
1041
|
+
let removeAbort = () => {};
|
|
1042
|
+
const responseResult = () => record({
|
|
1043
|
+
kind: "response",
|
|
1044
|
+
status,
|
|
1045
|
+
retryAfter
|
|
1046
|
+
});
|
|
1047
|
+
const finish = (result) => {
|
|
1048
|
+
if (settled) return;
|
|
1049
|
+
settled = true;
|
|
1050
|
+
cancelTimer();
|
|
1051
|
+
removeAbort();
|
|
1052
|
+
response?.destroy();
|
|
1053
|
+
request?.destroy();
|
|
1054
|
+
done(result);
|
|
1055
|
+
};
|
|
1056
|
+
const stop = () => finish(status === void 0 ? TIMEOUT : responseResult());
|
|
1057
|
+
if (isAborted(bounds.signal)) {
|
|
1058
|
+
finish(NOT_SENT_ABORTED);
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
const remaining = bounds.deadlineMs - clockNowMs(settings.clock);
|
|
1062
|
+
if (!(remaining > 0)) {
|
|
1063
|
+
finish(NOT_SENT_DEADLINE);
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
cancelTimer = startTimer(remaining, stop);
|
|
1067
|
+
removeAbort = onAbort(bounds.signal, stop);
|
|
1068
|
+
try {
|
|
1069
|
+
request = (0, https.request)({
|
|
1070
|
+
host: prepared.hostname,
|
|
1071
|
+
port: settings.port,
|
|
1072
|
+
method: "POST",
|
|
1073
|
+
path: prepared.path,
|
|
1074
|
+
headers: prepared.headers,
|
|
1075
|
+
setHost: false,
|
|
1076
|
+
agent: false,
|
|
1077
|
+
lookup: pinnedLookup(prepared.hostname, prepared.addresses, () => finish(status === void 0 ? NETWORK_ERROR : responseResult())),
|
|
1078
|
+
servername: prepared.literal ? "" : prepared.hostname,
|
|
1079
|
+
rejectUnauthorized: true,
|
|
1080
|
+
minVersion: "TLSv1.2",
|
|
1081
|
+
maxHeaderSize: MAX_RESPONSE_HEADER_BYTES,
|
|
1082
|
+
...settings.ca === void 0 ? {} : { ca: settings.ca }
|
|
1083
|
+
});
|
|
1084
|
+
} catch {
|
|
1085
|
+
finish(NETWORK_ERROR);
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
request.on("error", () => finish(status === void 0 ? NETWORK_ERROR : responseResult()));
|
|
1089
|
+
request.on("response", (incoming) => {
|
|
1090
|
+
response = incoming;
|
|
1091
|
+
incoming.on("error", () => finish(responseResult()));
|
|
1092
|
+
const code = incoming.statusCode;
|
|
1093
|
+
if (typeof code !== "number" || !numberIsSafeInteger(code) || code < 100 || code > 599) {
|
|
1094
|
+
finish(NETWORK_ERROR);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
status = code;
|
|
1098
|
+
const header = incoming.headers["retry-after"];
|
|
1099
|
+
retryAfter = typeof header === "string" && header.length <= MAX_RETRY_AFTER_LENGTH ? header : null;
|
|
1100
|
+
const declared = incoming.headers["content-length"];
|
|
1101
|
+
if (typeof declared === "string" && matches(DECIMAL, declared) && NumberConstructor(declared) > settings.maxResponseBytes) {
|
|
1102
|
+
finish(responseResult());
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
let received = 0;
|
|
1106
|
+
incoming.on("data", (chunk) => {
|
|
1107
|
+
received += chunk.length;
|
|
1108
|
+
if (received > settings.maxResponseBytes) finish(responseResult());
|
|
1109
|
+
});
|
|
1110
|
+
incoming.on("end", () => finish(responseResult()));
|
|
1111
|
+
incoming.on("close", () => finish(responseResult()));
|
|
1112
|
+
});
|
|
1113
|
+
if (settled) return;
|
|
1114
|
+
request.end(prepared.body);
|
|
1115
|
+
}
|
|
1116
|
+
//#endregion
|
|
1117
|
+
//#region src/node/lookup.ts
|
|
1118
|
+
/** The default per-query timeout of {@link createNodeWebhookDnsLookup}. */
|
|
1119
|
+
const DEFAULT_NODE_WEBHOOK_DNS_TIMEOUT_MS = 5e3;
|
|
1120
|
+
const unreadable = () => inputError("type", "input_unreadable");
|
|
1121
|
+
const unreadableFail = () => {
|
|
1122
|
+
throw unreadable();
|
|
817
1123
|
};
|
|
1124
|
+
/**
|
|
1125
|
+
* "The name exists but has no records of this type": an empty half of the
|
|
1126
|
+
* answer, not a failure. "No such name" (`ENOTFOUND`) is a failure: the two
|
|
1127
|
+
* queries disagreeing about whether the name exists is a partial answer.
|
|
1128
|
+
*/
|
|
818
1129
|
function isNoRecords(error) {
|
|
819
|
-
|
|
1130
|
+
return error?.code === "ENODATA";
|
|
820
1131
|
}
|
|
1132
|
+
/**
|
|
1133
|
+
* A {@link WebhookDnsLookup} for `createWebhookDestinationResolver()` built on
|
|
1134
|
+
* a fresh `node:dns` `Resolver` per call, asking for A and AAAA records
|
|
1135
|
+
* together. It uses c-ares rather than `dns.lookup()` because a c-ares query
|
|
1136
|
+
* can be cancelled: when the resolver aborts the call's signal, the queries
|
|
1137
|
+
* are cancelled instead of holding a libuv thread-pool thread until the
|
|
1138
|
+
* operating system gives up, so a slow authoritative server named in a
|
|
1139
|
+
* destination cannot starve the pool. It therefore does not consult
|
|
1140
|
+
* `/etc/hosts`.
|
|
1141
|
+
*
|
|
1142
|
+
* The answer is every A and AAAA address. A query that fails for any reason
|
|
1143
|
+
* other than "no records of this type" (`ENODATA`), "no such name"
|
|
1144
|
+
* (`ENOTFOUND`) included, rejects the whole lookup, so a half-answered name
|
|
1145
|
+
* is a `dns_failure` behind the resolver, never a partial pass.
|
|
1146
|
+
*
|
|
1147
|
+
* @throws {TypeError} `input_unreadable` when `servers` is not a list of
|
|
1148
|
+
* server strings `Resolver.setServers` accepts.
|
|
1149
|
+
* @throws {WebhookDeliveryConfigError} when `timeoutMs` is not a positive
|
|
1150
|
+
* whole number of at most `WEBHOOK_MAX_TIMER_MS`.
|
|
1151
|
+
*/
|
|
821
1152
|
function createNodeWebhookDnsLookup(options = {}) {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1153
|
+
const settings = guard(() => readLookupOptions(options), unreadable);
|
|
1154
|
+
const lookup = async (hostname, callOptions) => {
|
|
1155
|
+
const signal = callOptions.signal;
|
|
1156
|
+
if (isAborted(signal)) throw unreadable();
|
|
1157
|
+
const resolver = new dns_promises.Resolver({
|
|
1158
|
+
timeout: settings.timeoutMs,
|
|
1159
|
+
tries: 1
|
|
1160
|
+
});
|
|
1161
|
+
if (settings.servers !== void 0) resolver.setServers(settings.servers);
|
|
1162
|
+
const removeAbort = onAbort(signal, () => resolver.cancel());
|
|
1163
|
+
try {
|
|
1164
|
+
const [v4, v6] = await Promise.all([records(resolver.resolve4(hostname)), records(resolver.resolve6(hostname))]);
|
|
1165
|
+
const answer = [];
|
|
1166
|
+
for (const address of v4) answer[answer.length] = record({
|
|
1167
|
+
address,
|
|
1168
|
+
family: 4
|
|
1169
|
+
});
|
|
1170
|
+
for (const address of v6) answer[answer.length] = record({
|
|
1171
|
+
address,
|
|
1172
|
+
family: 6
|
|
1173
|
+
});
|
|
1174
|
+
return frozenList(answer);
|
|
1175
|
+
} finally {
|
|
1176
|
+
removeAbort();
|
|
1177
|
+
resolver.cancel();
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
return lookup;
|
|
841
1181
|
}
|
|
842
1182
|
async function records(query) {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1183
|
+
try {
|
|
1184
|
+
return await query;
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
if (isNoRecords(error)) return [];
|
|
1187
|
+
throw error;
|
|
1188
|
+
}
|
|
849
1189
|
}
|
|
850
1190
|
function readLookupOptions(options) {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1191
|
+
if (!isPlainRecord(options)) throw unreadable();
|
|
1192
|
+
const rawTimeout = primitive(readOwn(options, "timeoutMs", "timeoutMs", unreadableFail));
|
|
1193
|
+
const timeoutMs = rawTimeout === void 0 ? DEFAULT_NODE_WEBHOOK_DNS_TIMEOUT_MS : rawTimeout;
|
|
1194
|
+
assertPositiveInteger(timeoutMs, "timeoutMs");
|
|
1195
|
+
if (timeoutMs > 2147483647) throw configError("limit_out_of_range", "timeoutMs");
|
|
1196
|
+
const rawServers = readOwn(options, "servers", "servers", unreadableFail);
|
|
1197
|
+
let servers;
|
|
1198
|
+
if (rawServers !== void 0) {
|
|
1199
|
+
if (!isArray(rawServers)) throw unreadable();
|
|
1200
|
+
const length = readOwn(rawServers, "length", "servers", unreadableFail);
|
|
1201
|
+
if (typeof length !== "number" || !numberIsSafeInteger(length) || length < 1 || length > 16) throw unreadable();
|
|
1202
|
+
const copy = [];
|
|
1203
|
+
for (let index = 0; index < length; index += 1) {
|
|
1204
|
+
const item = primitive(readOwn(rawServers, `${index}`, "servers", unreadableFail));
|
|
1205
|
+
if (typeof item !== "string") throw unreadable();
|
|
1206
|
+
copy[copy.length] = item;
|
|
1207
|
+
}
|
|
1208
|
+
new dns_promises.Resolver().setServers(copy);
|
|
1209
|
+
servers = frozenList(copy);
|
|
1210
|
+
}
|
|
1211
|
+
return {
|
|
1212
|
+
servers,
|
|
1213
|
+
timeoutMs
|
|
1214
|
+
};
|
|
872
1215
|
}
|
|
873
|
-
|
|
1216
|
+
//#endregion
|
|
874
1217
|
exports.DEFAULT_NODE_WEBHOOK_DNS_TIMEOUT_MS = DEFAULT_NODE_WEBHOOK_DNS_TIMEOUT_MS;
|
|
875
1218
|
exports.DEFAULT_NODE_WEBHOOK_MAX_CONCURRENT_REQUESTS = DEFAULT_NODE_WEBHOOK_MAX_CONCURRENT_REQUESTS;
|
|
876
1219
|
exports.DEFAULT_NODE_WEBHOOK_MAX_RESPONSE_BYTES = DEFAULT_NODE_WEBHOOK_MAX_RESPONSE_BYTES;
|
|
877
1220
|
exports.createNodeWebhookDnsLookup = createNodeWebhookDnsLookup;
|
|
878
1221
|
exports.createNodeWebhookTransport = createNodeWebhookTransport;
|
|
879
|
-
|
|
1222
|
+
|
|
880
1223
|
//# sourceMappingURL=node.cjs.map
|