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