@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/dist/node.js CHANGED
@@ -1,874 +1,1218 @@
1
- import { Buffer } from 'buffer';
2
- import { request } from 'https';
3
- import { Resolver } from 'dns/promises';
4
-
5
- // src/node/transport.ts
6
-
7
- // src/snapshot.ts
8
- var reflectApply = Reflect.apply;
9
- var reflectOwnKeys = Reflect.ownKeys;
10
- var reflectGetOwnPropertyDescriptor = Reflect.getOwnPropertyDescriptor;
11
- var reflectGetPrototypeOf = Reflect.getPrototypeOf;
12
- var reflectDefineProperty = Reflect.defineProperty;
13
- var objectCreate = Object.create;
14
- var objectFreeze = Object.freeze;
15
- var objectHasOwn = Object.hasOwn;
16
- var objectPrototype = Object.prototype;
17
- var arrayIsArray = Array.isArray;
18
- var dateGetTime = Date.prototype.getTime;
19
- var regExpExec = RegExp.prototype.exec;
20
- var apply = reflectApply;
21
- var numberIsFinite = Number.isFinite;
22
- var numberIsSafeInteger = Number.isSafeInteger;
23
- var freeze = objectFreeze;
24
- var hasOwn = objectHasOwn;
25
- var isArray = arrayIsArray;
26
- var NOT_PRIMITIVE = /* @__PURE__ */ Symbol("webhook-delivery.not-primitive");
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
- const descriptor = objectCreate(null);
29
- descriptor.value = value;
30
- descriptor.enumerable = true;
31
- descriptor.writable = false;
32
- descriptor.configurable = false;
33
- return descriptor;
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
- reflectDefineProperty(target, key, dataDescriptor(value));
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
- const list = [];
40
- for (let index = 0; index < values.length; index += 1) define(list, index, values[index]);
41
- return objectFreeze(list);
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
- const out = objectCreate(null);
45
- const keys = reflectOwnKeys(fields);
46
- for (let index = 0; index < keys.length; index += 1) {
47
- const key = keys[index];
48
- const descriptor = reflectGetOwnPropertyDescriptor(fields, key);
49
- if (descriptor !== void 0 && objectHasOwn(descriptor, "value") && descriptor.value !== void 0) define(out, key, descriptor.value);
50
- }
51
- return objectFreeze(out);
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
- return objectCreate(null);
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
- return reflectApply(regExpExec, re, [value]) !== null;
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
- if (value === null || typeof value !== "object" || arrayIsArray(value)) return false;
61
- const prototype = reflectGetPrototypeOf(value);
62
- return prototype === objectPrototype || prototype === null;
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
- const descriptor = reflectGetOwnPropertyDescriptor(target, key);
66
- if (descriptor === void 0) return void 0;
67
- if (!objectHasOwn(descriptor, "value")) return fail("accessor", field);
68
- return descriptor.value;
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
- if (value === null || typeof value !== "object" && typeof value !== "function") return value;
72
- return NOT_PRIMITIVE;
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
- if (value === null || typeof value !== "object") return NaN;
76
- try {
77
- return reflectApply(dateGetTime, value, []);
78
- } catch {
79
- return NaN;
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
- // src/address.ts
84
- var stringCharCodeAt = String.prototype.charCodeAt;
85
- var stringIndexOf = String.prototype.indexOf;
86
- var stringSlice = String.prototype.slice;
87
- var stringSplit = String.prototype.split;
88
- var MAX_ADDRESS_LENGTH = 45;
89
- var MAX_TEXT_LENGTH = 64;
90
- var IPV4_OCTET = "(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)";
91
- var IPV4_DOTTED = new RegExp(`^${IPV4_OCTET}(?:\\.${IPV4_OCTET}){3}$`);
92
- var HEX_GROUP = /^[0-9A-Fa-f]{1,4}$/;
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
- return freeze({ base: (a << 24 | b << 16 | c << 8 | d) >>> 0, prefix });
95
- }
96
- var REFUSED_IPV4_BLOCKS = freeze([
97
- ipv4Block(0, 0, 0, 0, 8),
98
- // "this network", including the unspecified 0.0.0.0
99
- ipv4Block(10, 0, 0, 0, 8),
100
- // private
101
- ipv4Block(100, 64, 0, 0, 10),
102
- // shared address space (CGNAT)
103
- ipv4Block(127, 0, 0, 0, 8),
104
- // loopback
105
- ipv4Block(169, 254, 0, 0, 16),
106
- // link-local, including cloud metadata endpoints
107
- ipv4Block(172, 16, 0, 0, 12),
108
- // private
109
- ipv4Block(192, 0, 0, 0, 24),
110
- // IETF protocol assignments
111
- ipv4Block(192, 0, 2, 0, 24),
112
- // documentation (TEST-NET-1)
113
- ipv4Block(192, 88, 99, 0, 24),
114
- // deprecated 6to4 relay anycast
115
- ipv4Block(192, 168, 0, 0, 16),
116
- // private
117
- ipv4Block(198, 18, 0, 0, 15),
118
- // benchmarking
119
- ipv4Block(198, 51, 100, 0, 24),
120
- // documentation (TEST-NET-2)
121
- ipv4Block(203, 0, 113, 0, 24),
122
- // documentation (TEST-NET-3)
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
- const mask = block.prefix === 0 ? 0 : 4294967295 << 32 - block.prefix >>> 0;
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
- for (let index = 0; index < REFUSED_IPV4_BLOCKS.length; index += 1) {
134
- if (inIpv4Block(value, REFUSED_IPV4_BLOCKS[index])) return false;
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
- if (!matches(IPV4_DOTTED, text)) return void 0;
140
- const parts = apply(stringSplit, text, ["."]);
141
- let value = 0;
142
- for (let index = 0; index < 4; index += 1) value = value * 256 + decimal(parts[index]);
143
- return value >>> 0;
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
- let value = 0;
147
- for (let index = 0; index < text.length; index += 1) value = value * 10 + (apply(stringCharCodeAt, text, [index]) - 48);
148
- return value;
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
- let value = 0;
152
- for (let index = 0; index < text.length; index += 1) {
153
- const code = apply(stringCharCodeAt, text, [index]);
154
- const digit = code <= 57 ? code - 48 : code <= 70 ? code - 55 : code - 87;
155
- value = value * 16 + digit;
156
- }
157
- return value;
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
- const words = [];
161
- if (text === "") return words;
162
- const groups = apply(stringSplit, text, [":"]);
163
- for (let index = 0; index < groups.length; index += 1) {
164
- const group = groups[index];
165
- if (tailIpv4 && index === groups.length - 1 && apply(stringIndexOf, group, ["."]) !== -1) {
166
- const v4 = parseIpv4(group);
167
- if (v4 === void 0) return void 0;
168
- words[words.length] = v4 >>> 16;
169
- words[words.length] = v4 & 65535;
170
- continue;
171
- }
172
- if (!matches(HEX_GROUP, group)) return void 0;
173
- words[words.length] = hex(group);
174
- }
175
- return words;
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
- const gap = apply(stringIndexOf, text, ["::"]);
179
- if (gap === -1) {
180
- const words2 = parseIpv6Groups(text, true);
181
- return words2 !== void 0 && words2.length === 8 ? words2 : void 0;
182
- }
183
- if (apply(stringIndexOf, text, ["::", gap + 1]) !== -1) return void 0;
184
- const head = parseIpv6Groups(apply(stringSlice, text, [0, gap]), false);
185
- const tail = parseIpv6Groups(apply(stringSlice, text, [gap + 2]), true);
186
- if (head === void 0 || tail === void 0 || head.length + tail.length > 7) return void 0;
187
- const words = [];
188
- for (let index = 0; index < head.length; index += 1) words[words.length] = head[index];
189
- for (let index = head.length + tail.length; index < 8; index += 1) words[words.length] = 0;
190
- for (let index = 0; index < tail.length; index += 1) words[words.length] = tail[index];
191
- return words;
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
- return (high << 16 | low) >>> 0;
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
- if (w[0] === 100 && w[1] === 65435 && w[2] === 0 && w[3] === 0 && w[4] === 0 && w[5] === 0) {
198
- return isPublicIpv4Value(embeddedIpv4(w[6], w[7]));
199
- }
200
- if ((w[0] & 57344) !== 8192) return false;
201
- if (w[0] === 8193 && (w[1] & 65024) === 0) {
202
- if (w[1] !== 0) return false;
203
- const server = embeddedIpv4(w[2], w[3]);
204
- const client = ~embeddedIpv4(w[6], w[7]) >>> 0;
205
- return isPublicIpv4Value(server) && isPublicIpv4Value(client);
206
- }
207
- if (w[0] === 8193 && w[1] === 3512) return false;
208
- if (w[0] === 16383 && (w[1] & 61440) === 0) return false;
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
- let bestStart = -1;
214
- let bestLength = 0;
215
- for (let index = 0; index < 8; ) {
216
- if (w[index] !== 0) {
217
- index += 1;
218
- continue;
219
- }
220
- let end = index;
221
- while (end < 8 && w[end] === 0) end += 1;
222
- if (end - index > bestLength) {
223
- bestStart = index;
224
- bestLength = end - index;
225
- }
226
- index = end;
227
- }
228
- if (bestLength < 2) bestStart = -1;
229
- let text = "";
230
- for (let index = 0; index < 8; index += 1) {
231
- if (index === bestStart) {
232
- text += "::";
233
- index += bestLength - 1;
234
- continue;
235
- }
236
- if (text !== "" && !endsWithColons(text)) text += ":";
237
- text += hexText(w[index]);
238
- }
239
- return text;
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
- return text.length >= 2 && apply(stringSlice, text, [-2]) === "::";
263
+ return text.length >= 2 && apply(stringSlice$1, text, [-2]) === "::";
243
264
  }
244
- var HEX_DIGITS = "0123456789abcdef";
265
+ const HEX_DIGITS = "0123456789abcdef";
245
266
  function hexText(value) {
246
- if (value === 0) return "0";
247
- let text = "";
248
- for (let rest = value; rest > 0; rest = (rest - rest % 16) / 16) text = HEX_DIGITS[rest % 16] + text;
249
- return text;
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
- return `${value >>> 24}.${value >>> 16 & 255}.${value >>> 8 & 255}.${value & 255}`;
273
+ return `${value >>> 24}.${value >>> 16 & 255}.${value >>> 8 & 255}.${value & 255}`;
253
274
  }
254
- var NON_PUBLIC_V4 = freeze({ kind: "non_public", family: 4 });
255
- var NON_PUBLIC_V6 = freeze({ kind: "non_public", family: 6 });
256
- var MALFORMED = freeze({ kind: "malformed" });
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
- if (typeof text !== "string" || text.length === 0 || text.length > MAX_TEXT_LENGTH) return MALFORMED;
259
- const zone = apply(stringIndexOf, text, ["%"]);
260
- if (zone !== -1) return parseIpv6(apply(stringSlice, text, [0, zone])) === void 0 ? MALFORMED : NON_PUBLIC_V6;
261
- if (text.length > MAX_ADDRESS_LENGTH) return MALFORMED;
262
- const v4 = parseIpv4(text);
263
- if (v4 !== void 0) {
264
- return isPublicIpv4Value(v4) ? freeze({ kind: "public", address: formatIpv4(v4), family: 4 }) : NON_PUBLIC_V4;
265
- }
266
- const v6 = parseIpv6(text);
267
- if (v6 === void 0) return MALFORMED;
268
- return isPublicIpv6Words(v6) ? freeze({ kind: "public", address: formatIpv6(v6), family: 6 }) : NON_PUBLIC_V6;
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
- return matches(IPV4_DOTTED, text);
272
- }
273
- var CONFIG_MESSAGES = freeze({
274
- limit_not_positive_finite: "Webhook delivery limit must be a positive, finite number.",
275
- limit_not_integer: "Webhook delivery limit must be a whole number.",
276
- limit_out_of_range: "Webhook delivery limit is outside its allowed range.",
277
- retry_schedule_empty: "Webhook delivery retry schedule must list at least one delay.",
278
- limit_unreadable: "Webhook delivery limits could not be read."
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
- var INPUT_MESSAGES = freeze({
281
- input_unreadable: "Webhook delivery input could not be read.",
282
- observation_unknown: "Webhook delivery observation is not one decision 10 classifies.",
283
- status_out_of_range: "Webhook response status must be a whole number from 100 to 599.",
284
- signature_malformed: "Webhook signature must be a v1=<64 lowercase hex> value.",
285
- timestamp_invalid: "Webhook timestamp must be a non-negative whole number of seconds.",
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
- return typeof code === "string" && hasOwn(table, code) ? table[code] : fallback;
365
+ return typeof code === "string" && hasOwn(table, code) ? table[code] : fallback;
297
366
  }
298
- var reflectDefineProperty2 = Reflect.defineProperty;
299
- var objectCreate2 = Object.create;
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
- const descriptor = objectCreate2(null);
302
- descriptor.value = value;
303
- descriptor.enumerable = true;
304
- descriptor.writable = true;
305
- descriptor.configurable = true;
306
- reflectDefineProperty2(target, key, descriptor);
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
- constructor(code, field) {
310
- super(messageFor(CONFIG_MESSAGES, code, "Webhook delivery limit was rejected."));
311
- defineField(this, "name", "WebhookDeliveryConfigError");
312
- defineField(this, "code", code);
313
- defineField(this, "field", field);
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
- var created = /* @__PURE__ */ new WeakSet();
317
- var weakSetAdd = WeakSet.prototype.add;
318
- var weakSetHas = WeakSet.prototype.has;
319
- var TypeErrorConstructor = TypeError;
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
- const stack = objectCreate2(null);
322
- stack.value = `${name}: ${code}`;
323
- stack.enumerable = false;
324
- stack.writable = false;
325
- stack.configurable = false;
326
- if (!reflectDefineProperty2(error, "stack", stack)) throw new TypeErrorConstructor("Webhook delivery could not seal an error.");
327
- apply(weakSetAdd, created, [error]);
328
- return freeze(error);
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
- return (typeof value === "object" || typeof value === "function") && value !== null && apply(weakSetHas, created, [value]) === true;
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
- return mark(new WebhookDeliveryConfigError(code, field), "WebhookDeliveryConfigError", code);
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
- const message = INPUT_MESSAGES[code];
338
- const error = new TypeErrorConstructor(message) ;
339
- define(error, "code", code);
340
- return mark(error, "TypeError" , code);
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
- try {
344
- return run();
345
- } catch (thrown) {
346
- if (isPackageError(thrown)) throw thrown;
347
- throw replacement();
348
- }
349
- }
350
- var WEBHOOK_MAX_TIMER_MS = 2147483647;
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
- if (typeof value !== "number" || !numberIsFinite(value) || value <= 0) {
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
- assertPositiveFinite(value, field);
358
- if (!numberIsSafeInteger(value)) {
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
- // src/call-bounds.ts
364
- var reflectGetOwnPropertyDescriptor2 = Reflect.getOwnPropertyDescriptor;
365
- var abortedGetter = typeof AbortSignal === "function" ? reflectGetOwnPropertyDescriptor2(AbortSignal.prototype, "aborted")?.get : void 0;
366
- var eventTargetAdd = typeof EventTarget === "function" ? EventTarget.prototype.addEventListener : void 0;
367
- var eventTargetRemove = typeof EventTarget === "function" ? EventTarget.prototype.removeEventListener : void 0;
368
- var setTimer = setTimeout;
369
- var clearTimer = clearTimeout;
370
- var dateNow = Date.now;
371
- var mathMax = Math.max;
372
- var mathMin = Math.min;
373
- var unreadable = () => inputError("type", "input_unreadable");
374
- var unreadableFail = () => {
375
- throw unreadable();
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
- if (options === null || typeof options !== "object") throw unreadable();
379
- const deadlineMs = timeOf(readOwn(options, "deadline", "deadline", unreadableFail));
380
- if (!(deadlineMs === deadlineMs) || deadlineMs === Infinity || deadlineMs === -Infinity) throw unreadable();
381
- const signal = readOwn(options, "signal", "signal", unreadableFail);
382
- if (signal === null || typeof signal !== "object") throw unreadable();
383
- try {
384
- isAborted(signal);
385
- } catch {
386
- throw unreadable();
387
- }
388
- return { deadlineMs, signal };
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
- if (abortedGetter === void 0) throw unreadable();
392
- return apply(abortedGetter, signal, []) === true;
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
- if (eventTargetAdd === void 0 || eventTargetRemove === void 0) throw unreadable();
396
- apply(eventTargetAdd, signal, ["abort", listener, { once: true }]);
397
- return () => {
398
- apply(eventTargetRemove, signal, ["abort", listener]);
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
- if (clock === void 0) return { target: void 0, now: void 0 };
403
- if (clock === null || typeof clock !== "object") throw unreadable();
404
- const now = clock.now;
405
- if (typeof now !== "function") throw unreadable();
406
- return { target: clock, now };
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
- if (clock.now === void 0) return dateNow();
410
- try {
411
- const ms = timeOf(apply(clock.now, clock.target, []));
412
- return ms === Infinity || ms === -Infinity ? NaN : ms;
413
- } catch {
414
- return NaN;
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
- const handle = setTimer(fire, mathMax(0, mathMin(ms, WEBHOOK_MAX_TIMER_MS)));
419
- return () => {
420
- clearTimer(handle);
421
- };
590
+ const handle = setTimer(fire, mathMax(0, mathMin(ms, WEBHOOK_MAX_TIMER_MS)));
591
+ return () => {
592
+ clearTimer(handle);
593
+ };
422
594
  }
423
-
424
- // src/destination.ts
425
- var WEBHOOK_DESTINATION_MAX_URL_LENGTH = 2048;
426
- var URLConstructor = URL;
427
- var urlGetter = (name) => {
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
- var urlProtocol = urlGetter("protocol");
433
- var urlUsername = urlGetter("username");
434
- var urlPassword = urlGetter("password");
435
- var urlPort = urlGetter("port");
436
- var urlHostname = urlGetter("hostname");
437
- var urlPathname = urlGetter("pathname");
438
- var urlSearch = urlGetter("search");
439
- var stringSlice2 = String.prototype.slice;
440
- var stringEndsWith = String.prototype.endsWith;
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
- var URL_TEXT = /^[^\x00-\x20\x7f]+$/;
443
- var URL_PREFIX = /^https:\/\/[^/\\?#@]/i;
444
- var INVALID_URL = freeze({ ok: false, code: "invalid_url" });
445
- var PRIVATE_URL = freeze({ ok: false, code: "private_address" });
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
- try {
448
- return checkUrl(primitive(url), allowNonPublic);
449
- } catch {
450
- return INVALID_URL;
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
- if (typeof text !== "string" || text.length === 0 || text.length > WEBHOOK_DESTINATION_MAX_URL_LENGTH) return INVALID_URL;
455
- if (!matches(URL_TEXT, text) || !matches(URL_PREFIX, text)) return INVALID_URL;
456
- let parsed;
457
- try {
458
- parsed = new URLConstructor(text);
459
- } catch {
460
- return INVALID_URL;
461
- }
462
- if (apply(urlProtocol, parsed, []) !== "https:") return INVALID_URL;
463
- if (apply(urlUsername, parsed, []) !== "" || apply(urlPassword, parsed, []) !== "") return INVALID_URL;
464
- if (apply(urlPort, parsed, []) !== "") return INVALID_URL;
465
- const host = apply(urlHostname, parsed, []);
466
- if (host === "" || apply(stringEndsWith, host, ["."])) return INVALID_URL;
467
- const url = `https://${host}${apply(urlPathname, parsed, [])}${apply(urlSearch, parsed, [])}`;
468
- const v6 = host[0] === "[";
469
- if (v6 || isIpv4Literal(host)) {
470
- const text2 = v6 ? apply(stringSlice2, host, [1, -1]) : host;
471
- const literal = classifyAddress(text2);
472
- if (literal.kind === "malformed") return INVALID_URL;
473
- if (literal.kind === "non_public" && !allowNonPublic) return PRIVATE_URL;
474
- const family = v6 ? 6 : 4;
475
- return record({ ok: true, url, hostname: text2, literal: record({ address: text2, family }) });
476
- }
477
- if (!allowNonPublic && (host === "localhost" || apply(stringEndsWith, host, [".localhost"]))) return PRIVATE_URL;
478
- return record({ ok: true, url, hostname: host });
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
- // src/node/transport.ts
482
- var DEFAULT_NODE_WEBHOOK_MAX_RESPONSE_BYTES = 65536;
483
- var DEFAULT_NODE_WEBHOOK_MAX_CONCURRENT_REQUESTS = 16;
484
- var MAX_CONCURRENT_REQUESTS_LIMIT = 1024;
485
- var MAX_HEADERS = 64;
486
- var MAX_RETRY_AFTER_LENGTH = 128;
487
- var MAX_RESPONSE_HEADER_BYTES = 16384;
488
- var unreadable2 = () => inputError("type", "input_unreadable");
489
- var unreadableFail2 = () => {
490
- throw unreadable2();
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
- var PromiseConstructor = Promise;
493
- var bufferFrom = Buffer.from;
494
- var NumberConstructor = Number;
495
- var HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
496
- var HEADER_VALUE = /^[\t\x20-\x7e]*$/;
497
- var RESERVED_HEADER = /^(?:host|content-length|connection|transfer-encoding|keep-alive|upgrade|te|expect|proxy-.*)$/i;
498
- var DECIMAL = /^\d+$/;
499
- var NOT_SENT_DEADLINE = freeze({ kind: "not_sent", reason: "deadline_expired" });
500
- var NOT_SENT_ABORTED = freeze({ kind: "not_sent", reason: "aborted" });
501
- var TIMEOUT = freeze({ kind: "timeout" });
502
- var NETWORK_ERROR = freeze({ kind: "network_error" });
503
- var INVALID_URL2 = freeze({ kind: "invalid_url" });
504
- var PRIVATE_ADDRESS2 = freeze({ kind: "private_address" });
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
- return buildTransport(guard(() => readTransportOptions(options, PRODUCTION_SWITCHES), unreadable2));
769
+ return buildTransport(guard(() => readTransportOptions(options, PRODUCTION_SWITCHES), unreadable$1));
507
770
  }
508
- var PRODUCTION_SWITCHES = freeze({ allowNonPublicAddresses: false, port: 443 });
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
- const slots = { active: 0, pumping: false, head: void 0, tail: void 0 };
511
- const transport = {
512
- send(request, callOptions) {
513
- let bounds;
514
- let prepared;
515
- try {
516
- bounds = readCallBounds(callOptions);
517
- prepared = prepareRequest(request, settings);
518
- } catch {
519
- return PromiseConstructor.reject(unreadable2());
520
- }
521
- if ("kind" in prepared) return PromiseConstructor.resolve(prepared);
522
- const refused = notWanted(bounds, settings.clock);
523
- if (refused !== void 0) return PromiseConstructor.resolve(refused);
524
- return dispatch(slots, settings, prepared, bounds);
525
- }
526
- };
527
- return freeze(transport);
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
- if (!isPlainRecord(options)) throw unreadable2();
531
- const rawMax = primitive(readOwn(options, "maxResponseBytes", "maxResponseBytes", unreadableFail2));
532
- const maxResponseBytes = rawMax === void 0 ? DEFAULT_NODE_WEBHOOK_MAX_RESPONSE_BYTES : rawMax;
533
- assertPositiveInteger(maxResponseBytes, "maxResponseBytes");
534
- const rawConcurrency = primitive(readOwn(options, "maxConcurrentRequests", "maxConcurrentRequests", unreadableFail2));
535
- const maxConcurrentRequests = rawConcurrency === void 0 ? DEFAULT_NODE_WEBHOOK_MAX_CONCURRENT_REQUESTS : rawConcurrency;
536
- assertPositiveInteger(maxConcurrentRequests, "maxConcurrentRequests");
537
- if (maxConcurrentRequests > MAX_CONCURRENT_REQUESTS_LIMIT) throw configError("limit_out_of_range", "maxConcurrentRequests");
538
- const ca = readCa(readOwn(options, "ca", "ca", unreadableFail2));
539
- const clock = captureOptionalClock(readOwn(options, "clock", "clock", unreadableFail2));
540
- return freeze({
541
- maxResponseBytes,
542
- maxConcurrentRequests,
543
- ca,
544
- clock,
545
- allowNonPublic: switches.allowNonPublicAddresses === true,
546
- port: switches.port
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
- if (value === void 0) return void 0;
551
- if (typeof value === "string") return value === "" ? unreadableFail2() : frozenList([value]);
552
- if (!isArray(value)) throw unreadable2();
553
- const length = readOwn(value, "length", "ca", unreadableFail2);
554
- if (typeof length !== "number" || !numberIsSafeInteger(length) || length < 1 || length > 1024) throw unreadable2();
555
- const copy = [];
556
- for (let index = 0; index < length; index += 1) {
557
- const item = readOwn(value, `${index}`, "ca", unreadableFail2);
558
- if (typeof item !== "string" || item === "") throw unreadable2();
559
- copy[copy.length] = item;
560
- }
561
- return frozenList(copy);
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
- if (!isPlainRecord(request)) throw unreadable2();
565
- const destination = readOwn(request, "destination", "destination", unreadableFail2);
566
- const method = primitive(readOwn(request, "method", "method", unreadableFail2));
567
- const body = primitive(readOwn(request, "body", "body", unreadableFail2));
568
- const headers = readOwn(request, "headers", "headers", unreadableFail2);
569
- if (!isPlainRecord(destination) || method !== "POST" || typeof body !== "string") throw unreadable2();
570
- const outgoing = readHeaders(headers);
571
- const url = primitive(readOwn(destination, "url", "destination.url", unreadableFail2));
572
- const hostname = primitive(readOwn(destination, "hostname", "destination.hostname", unreadableFail2));
573
- const port = primitive(readOwn(destination, "port", "destination.port", unreadableFail2));
574
- const rawAddresses = readOwn(destination, "addresses", "destination.addresses", unreadableFail2);
575
- const check = checkDestinationUrl(url, settings.allowNonPublic);
576
- if (!check.ok) return check.code === "private_address" ? PRIVATE_ADDRESS2 : INVALID_URL2;
577
- if (port !== 443 || hostname !== check.hostname) return INVALID_URL2;
578
- const addresses = readAddresses(rawAddresses, settings.allowNonPublic);
579
- if (addresses === void 0) return PRIVATE_ADDRESS2;
580
- if (check.literal !== void 0) {
581
- for (let index = 0; index < addresses.length; index += 1) {
582
- if (addresses[index].address !== check.literal.address) return INVALID_URL2;
583
- }
584
- }
585
- const hostHeader = check.literal !== void 0 && check.literal.family === 6 ? `[${check.hostname}]` : check.hostname;
586
- const path = check.url.slice(`https://${hostHeader}`.length);
587
- const encoded = bufferFrom(body, "utf8");
588
- outgoing["Host"] = hostHeader;
589
- outgoing["Content-Length"] = `${encoded.length}`;
590
- outgoing["Connection"] = "close";
591
- return freeze({ hostname: check.hostname, path, headers: outgoing, body: encoded, addresses, literal: check.literal !== void 0 });
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
- if (!isPlainRecord(value)) throw unreadable2();
595
- const out = emptyMap();
596
- const keys = Reflect.ownKeys(value);
597
- if (keys.length > MAX_HEADERS) throw unreadable2();
598
- for (let index = 0; index < keys.length; index += 1) {
599
- const name = keys[index];
600
- if (typeof name !== "string") throw unreadable2();
601
- const headerValue = primitive(readOwn(value, name, "headers", unreadableFail2));
602
- if (typeof headerValue !== "string" || !matches(HEADER_NAME, name) || !matches(HEADER_VALUE, headerValue)) throw unreadable2();
603
- if (matches(RESERVED_HEADER, name)) continue;
604
- out[name] = headerValue;
605
- }
606
- return out;
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
- if (!isArray(value)) return void 0;
610
- const length = readOwn(value, "length", "destination.addresses", unreadableFail2);
611
- if (typeof length !== "number" || !numberIsSafeInteger(length) || length < 1 || length > 32) return void 0;
612
- const out = [];
613
- for (let index = 0; index < length; index += 1) {
614
- const entry = readOwn(value, `${index}`, "destination.addresses", unreadableFail2);
615
- if (!isPlainRecord(entry)) return void 0;
616
- const address = primitive(readOwn(entry, "address", "destination.addresses", unreadableFail2));
617
- const family = primitive(readOwn(entry, "family", "destination.addresses", unreadableFail2));
618
- const judged = classifyAddress(address);
619
- if (judged.kind === "public" && judged.family === family) {
620
- out[out.length] = record({ address: judged.address, family: judged.family });
621
- continue;
622
- }
623
- if (allowNonPublic && judged.kind === "non_public" && judged.family === family && typeof address === "string" && !address.includes("%")) {
624
- out[out.length] = record({ address, family: judged.family });
625
- continue;
626
- }
627
- return void 0;
628
- }
629
- return frozenList(out);
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
- if (isAborted(bounds.signal)) return NOT_SENT_ABORTED;
633
- if (!(bounds.deadlineMs > clockNowMs(clock))) return NOT_SENT_DEADLINE;
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
- return new PromiseConstructor((resolve) => {
638
- const release = () => {
639
- slots.active -= 1;
640
- pump(slots, settings);
641
- };
642
- const begin = () => {
643
- perform(settings, prepared, bounds, (result) => {
644
- release();
645
- resolve(result);
646
- });
647
- };
648
- if (slots.active < settings.maxConcurrentRequests) {
649
- slots.active += 1;
650
- begin();
651
- return;
652
- }
653
- enqueue(slots, settings, bounds, begin, resolve);
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
- let cancelTimer = () => {
658
- };
659
- let removeAbort = () => {
660
- };
661
- const call = {
662
- start: () => {
663
- cancelTimer();
664
- removeAbort();
665
- begin();
666
- },
667
- next: void 0,
668
- prev: void 0
669
- };
670
- const drop = (result) => {
671
- unlink(slots, call);
672
- cancelTimer();
673
- removeAbort();
674
- resolve(result);
675
- };
676
- if (slots.tail === void 0) {
677
- slots.head = call;
678
- slots.tail = call;
679
- } else {
680
- call.prev = slots.tail;
681
- slots.tail.next = call;
682
- slots.tail = call;
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
- if (call.prev !== void 0) call.prev.next = call.next;
689
- else if (slots.head === call) slots.head = call.next;
690
- if (call.next !== void 0) call.next.prev = call.prev;
691
- else if (slots.tail === call) slots.tail = call.prev;
692
- call.next = void 0;
693
- call.prev = void 0;
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
- if (slots.pumping) return;
697
- slots.pumping = true;
698
- try {
699
- while (slots.head !== void 0 && slots.active < settings.maxConcurrentRequests) {
700
- const call = slots.head;
701
- unlink(slots, call);
702
- slots.active += 1;
703
- call.start();
704
- }
705
- } finally {
706
- slots.pumping = false;
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
- return (host, options, callback) => {
711
- try {
712
- if (host !== hostname) {
713
- const error = new Error("Webhook transport refused a lookup for a name other than the vetted one.");
714
- error.code = "ENOTFOUND";
715
- callback(error, "", 4);
716
- return;
717
- }
718
- if (options.all === true) {
719
- const all = [];
720
- for (let index = 0; index < addresses.length; index += 1) all[all.length] = { address: addresses[index].address, family: addresses[index].family };
721
- callback(null, all);
722
- return;
723
- }
724
- callback(null, addresses[0].address, addresses[0].family);
725
- } catch {
726
- onFault();
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
- let settled = false;
732
- let status;
733
- let retryAfter = null;
734
- let request$1;
735
- let response;
736
- let cancelTimer = () => {
737
- };
738
- let removeAbort = () => {
739
- };
740
- const responseResult = () => record({ kind: "response", status, retryAfter });
741
- const finish = (result) => {
742
- if (settled) return;
743
- settled = true;
744
- cancelTimer();
745
- removeAbort();
746
- response?.destroy();
747
- request$1?.destroy();
748
- done(result);
749
- };
750
- const stop = () => finish(status === void 0 ? TIMEOUT : responseResult());
751
- if (isAborted(bounds.signal)) {
752
- finish(NOT_SENT_ABORTED);
753
- return;
754
- }
755
- const remaining = bounds.deadlineMs - clockNowMs(settings.clock);
756
- if (!(remaining > 0)) {
757
- finish(NOT_SENT_DEADLINE);
758
- return;
759
- }
760
- cancelTimer = startTimer(remaining, stop);
761
- removeAbort = onAbort(bounds.signal, stop);
762
- try {
763
- request$1 = request({
764
- host: prepared.hostname,
765
- port: settings.port,
766
- method: "POST",
767
- path: prepared.path,
768
- headers: prepared.headers,
769
- setHost: false,
770
- agent: false,
771
- lookup: pinnedLookup(prepared.hostname, prepared.addresses, () => finish(status === void 0 ? NETWORK_ERROR : responseResult())),
772
- // RFC 6066 forbids an IP address as SNI; the certificate is then checked against the IP.
773
- servername: prepared.literal ? "" : prepared.hostname,
774
- rejectUnauthorized: true,
775
- minVersion: "TLSv1.2",
776
- maxHeaderSize: MAX_RESPONSE_HEADER_BYTES,
777
- ...settings.ca === void 0 ? {} : { ca: settings.ca }
778
- });
779
- } catch {
780
- finish(NETWORK_ERROR);
781
- return;
782
- }
783
- request$1.on("error", () => finish(status === void 0 ? NETWORK_ERROR : responseResult()));
784
- request$1.on("response", (incoming) => {
785
- response = incoming;
786
- incoming.on("error", () => finish(responseResult()));
787
- const code = incoming.statusCode;
788
- if (typeof code !== "number" || !numberIsSafeInteger(code) || code < 100 || code > 599) {
789
- finish(NETWORK_ERROR);
790
- return;
791
- }
792
- status = code;
793
- const header = incoming.headers["retry-after"];
794
- retryAfter = typeof header === "string" && header.length <= MAX_RETRY_AFTER_LENGTH ? header : null;
795
- const declared = incoming.headers["content-length"];
796
- if (typeof declared === "string" && matches(DECIMAL, declared) && NumberConstructor(declared) > settings.maxResponseBytes) {
797
- finish(responseResult());
798
- return;
799
- }
800
- let received = 0;
801
- incoming.on("data", (chunk) => {
802
- received += chunk.length;
803
- if (received > settings.maxResponseBytes) finish(responseResult());
804
- });
805
- incoming.on("end", () => finish(responseResult()));
806
- incoming.on("close", () => finish(responseResult()));
807
- });
808
- if (settled) return;
809
- request$1.end(prepared.body);
810
- }
811
- var DEFAULT_NODE_WEBHOOK_DNS_TIMEOUT_MS = 5e3;
812
- var unreadable3 = () => inputError("type", "input_unreadable");
813
- var unreadableFail3 = () => {
814
- throw unreadable3();
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
- return error?.code === "ENODATA";
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
- const settings = guard(() => readLookupOptions(options), unreadable3);
821
- const lookup = async (hostname, callOptions) => {
822
- const signal = callOptions.signal;
823
- if (isAborted(signal)) throw unreadable3();
824
- const resolver = new Resolver({ timeout: settings.timeoutMs, tries: 1 });
825
- if (settings.servers !== void 0) resolver.setServers(settings.servers);
826
- const removeAbort = onAbort(signal, () => resolver.cancel());
827
- try {
828
- const [v4, v6] = await Promise.all([records(resolver.resolve4(hostname)), records(resolver.resolve6(hostname))]);
829
- const answer = [];
830
- for (const address of v4) answer[answer.length] = record({ address, family: 4 });
831
- for (const address of v6) answer[answer.length] = record({ address, family: 6 });
832
- return frozenList(answer);
833
- } finally {
834
- removeAbort();
835
- resolver.cancel();
836
- }
837
- };
838
- return lookup;
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
- try {
842
- return await query;
843
- } catch (error) {
844
- if (isNoRecords(error)) return [];
845
- throw error;
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
- if (!isPlainRecord(options)) throw unreadable3();
850
- const rawTimeout = primitive(readOwn(options, "timeoutMs", "timeoutMs", unreadableFail3));
851
- const timeoutMs = rawTimeout === void 0 ? DEFAULT_NODE_WEBHOOK_DNS_TIMEOUT_MS : rawTimeout;
852
- assertPositiveInteger(timeoutMs, "timeoutMs");
853
- if (timeoutMs > WEBHOOK_MAX_TIMER_MS) throw configError("limit_out_of_range", "timeoutMs");
854
- const rawServers = readOwn(options, "servers", "servers", unreadableFail3);
855
- let servers;
856
- if (rawServers !== void 0) {
857
- if (!isArray(rawServers)) throw unreadable3();
858
- const length = readOwn(rawServers, "length", "servers", unreadableFail3);
859
- if (typeof length !== "number" || !numberIsSafeInteger(length) || length < 1 || length > 16) throw unreadable3();
860
- const copy = [];
861
- for (let index = 0; index < length; index += 1) {
862
- const item = primitive(readOwn(rawServers, `${index}`, "servers", unreadableFail3));
863
- if (typeof item !== "string") throw unreadable3();
864
- copy[copy.length] = item;
865
- }
866
- new Resolver().setServers(copy);
867
- servers = frozenList(copy);
868
- }
869
- return { servers, timeoutMs };
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
- //# sourceMappingURL=node.js.map
1217
+
874
1218
  //# sourceMappingURL=node.js.map