@getstrata/core 2.2.38 → 2.2.40

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.
@@ -85,245 +85,8 @@ function sdkClientClassName() {
85
85
  return fromName ? `${fromName}Client` : "AppClient";
86
86
  }
87
87
 
88
- // ../../src/core/security/safeUrl.ts
89
- import { lookup as dnsLookupImpl } from "dns/promises";
90
- import { BadRequestError } from "@getstrata/core/errors/http";
91
- var dnsLookup = dnsLookupImpl;
92
- var BLOCKED_HOSTNAMES = new Set([
93
- "localhost",
94
- "127.0.0.1",
95
- "0.0.0.0",
96
- "::1",
97
- "metadata.google.internal"
98
- ]);
99
- function parseCanonicalDottedDecimal(hostname) {
100
- const parts = hostname.split(".");
101
- if (parts.length !== 4) {
102
- return null;
103
- }
104
- const octets = [];
105
- for (const part of parts) {
106
- if (!/^(0|[1-9]\d{0,2})$/u.test(part)) {
107
- return null;
108
- }
109
- const value = Number.parseInt(part, 10);
110
- if (!Number.isInteger(value) || value < 0 || value > 255) {
111
- return null;
112
- }
113
- octets.push(value);
114
- }
115
- return octets;
116
- }
117
- function parseDottedIpv4(hostname) {
118
- return parseCanonicalDottedDecimal(hostname);
119
- }
120
- function isBlockedIpv4Octets(octets) {
121
- const [a = 0, b = 0] = octets;
122
- if (a === 10 || a === 127 || a === 0) {
123
- return true;
124
- }
125
- if (a === 169 && b === 254) {
126
- return true;
127
- }
128
- if (a === 172 && b >= 16 && b <= 31) {
129
- return true;
130
- }
131
- if (a === 192 && b === 168) {
132
- return true;
133
- }
134
- if (a === 100 && b >= 64 && b <= 127) {
135
- return true;
136
- }
137
- if (a === 198 && (b === 18 || b === 19)) {
138
- return true;
139
- }
140
- return false;
141
- }
142
- function expandIpv6(hostname) {
143
- const trimmed = hostname.trim().toLowerCase();
144
- const unbracketed = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed;
145
- if (unbracketed.includes("%")) {
146
- return null;
147
- }
148
- const mapped = unbracketed.match(/^::ffff:([0-9a-fx.]+)$/iu);
149
- if (mapped?.[1]) {
150
- const octets = parseDottedIpv4(mapped[1]);
151
- if (!octets) {
152
- return null;
153
- }
154
- return [
155
- 0,
156
- 0,
157
- 0,
158
- 0,
159
- 0,
160
- 65535,
161
- octets[0] << 8 | octets[1],
162
- octets[2] << 8 | octets[3]
163
- ];
164
- }
165
- const halves = unbracketed.split("::");
166
- if (halves.length > 2) {
167
- return null;
168
- }
169
- const parseGroup = (part) => {
170
- if (!/^[0-9a-f]{1,4}$/u.test(part)) {
171
- return null;
172
- }
173
- return Number.parseInt(part, 16);
174
- };
175
- if (halves.length === 1) {
176
- const groups = unbracketed.split(":");
177
- if (groups.length !== 8) {
178
- return null;
179
- }
180
- const values = groups.map(parseGroup);
181
- return values.every((value) => value !== null) ? values : null;
182
- }
183
- const head = halves[0] ? halves[0].split(":").filter(Boolean) : [];
184
- const tail = halves[1] ? halves[1].split(":").filter(Boolean) : [];
185
- if (head.length + tail.length > 8) {
186
- return null;
187
- }
188
- const values = [];
189
- for (const part of head) {
190
- const value = parseGroup(part);
191
- if (value === null) {
192
- return null;
193
- }
194
- values.push(value);
195
- }
196
- while (values.length < 8 - tail.length) {
197
- values.push(0);
198
- }
199
- for (const part of tail) {
200
- const value = parseGroup(part);
201
- if (value === null) {
202
- return null;
203
- }
204
- values.push(value);
205
- }
206
- return values.length === 8 ? values : null;
207
- }
208
- function isBlockedIpv6(hostname) {
209
- const groups = expandIpv6(hostname);
210
- if (!groups) {
211
- return hostname.includes(":");
212
- }
213
- const first = groups[0] ?? 0;
214
- const second = groups[1] ?? 0;
215
- if (groups.every((group) => group === 0)) {
216
- return true;
217
- }
218
- if (groups.slice(0, 7).every((group) => group === 0) && groups[7] === 1) {
219
- return true;
220
- }
221
- if ((first & 65472) === 65152) {
222
- return true;
223
- }
224
- if ((first & 65024) === 64512) {
225
- return true;
226
- }
227
- if ((first & 65280) === 65280) {
228
- return true;
229
- }
230
- if (first === 0 && second === 0 && groups[2] === 0 && groups[3] === 0 && groups[4] === 0 && groups[5] === 65535) {
231
- const a = (groups[6] ?? 0) >> 8;
232
- const b = (groups[6] ?? 0) & 255;
233
- const c = (groups[7] ?? 0) >> 8;
234
- const d = (groups[7] ?? 0) & 255;
235
- return isBlockedIpv4Octets([a, b, c, d]);
236
- }
237
- return false;
238
- }
239
- function isBlockedHostname(hostname) {
240
- const normalized = hostname.trim().toLowerCase();
241
- if (normalized.length === 0) {
242
- return true;
243
- }
244
- if (BLOCKED_HOSTNAMES.has(normalized)) {
245
- return true;
246
- }
247
- if (normalized.endsWith(".local") || normalized.endsWith(".internal")) {
248
- return true;
249
- }
250
- if (normalized.includes(":")) {
251
- return isBlockedIpv6(normalized);
252
- }
253
- if (/^\d+$/u.test(normalized)) {
254
- return true;
255
- }
256
- if (normalized.includes(".")) {
257
- const canonical = parseCanonicalDottedDecimal(normalized);
258
- if (canonical) {
259
- return isBlockedIpv4Octets(canonical);
260
- }
261
- if (/^[0-9a-fx.]+$/iu.test(normalized)) {
262
- return true;
263
- }
264
- }
265
- return false;
266
- }
267
- function productionSafeOutboundOptions(options) {
268
- if (!isProductionEnv()) {
269
- return options;
270
- }
271
- return { ...options, allowPrivate: false, resolveDns: true };
272
- }
273
- function assertSafeOutboundUrl(rawUrl, options = {}) {
274
- const resolved = productionSafeOutboundOptions(options);
275
- let parsed;
276
- try {
277
- parsed = new URL(rawUrl);
278
- } catch {
279
- throw new BadRequestError("Webhook URL is invalid.");
280
- }
281
- if (parsed.protocol !== "https:" && !(resolved.allowHttp && parsed.protocol === "http:")) {
282
- throw new BadRequestError("Webhook URL must use HTTPS.");
283
- }
284
- if (parsed.username || parsed.password) {
285
- throw new BadRequestError("Webhook URL must not include credentials.");
286
- }
287
- if (!resolved.allowPrivate && isBlockedHostname(parsed.hostname)) {
288
- throw new BadRequestError("Webhook URL targets a blocked host.");
289
- }
290
- return parsed;
291
- }
292
- function isBlockedIpAddress(address) {
293
- return isBlockedHostname(address.trim().toLowerCase());
294
- }
295
- function isLiteralIpHostname(hostname) {
296
- return parseCanonicalDottedDecimal(hostname) !== null || expandIpv6(hostname) !== null;
297
- }
298
- function pinUrlToAddress(url, address) {
299
- const pinned = new URL(url.toString());
300
- const ipv6 = expandIpv6(address.replace(/^\[|\]$/gu, ""));
301
- pinned.hostname = ipv6 ? `[${address.replace(/^\[|\]$/gu, "")}]` : address;
302
- return pinned;
303
- }
304
- async function resolveSafeOutboundTarget(rawUrl, options = {}) {
305
- const resolvedOptions = productionSafeOutboundOptions(options);
306
- const parsed = assertSafeOutboundUrl(rawUrl, resolvedOptions);
307
- if (resolvedOptions.resolveDns === false || resolvedOptions.allowPrivate) {
308
- return { url: parsed, addresses: [] };
309
- }
310
- const hostname = parsed.hostname.trim().toLowerCase();
311
- if (isLiteralIpHostname(hostname)) {
312
- return { url: parsed, addresses: [hostname] };
313
- }
314
- let results;
315
- try {
316
- results = await dnsLookup(hostname, { all: true, verbatim: true });
317
- } catch {
318
- throw new BadRequestError("Webhook URL targets a blocked host.");
319
- }
320
- if (results.length === 0 || results.some((result) => isBlockedIpAddress(result.address))) {
321
- throw new BadRequestError("Webhook URL targets a blocked host.");
322
- }
323
- return { url: parsed, addresses: results.map((result) => result.address) };
324
- }
325
-
326
88
  // ../../src/core/security/safeFetch.ts
89
+ import { pinUrlToAddress, resolveSafeOutboundTarget } from "@getstrata/core/security/safeUrl";
327
90
  var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
328
91
  function untilAborted(work, signal) {
329
92
  signal.throwIfAborted();
@@ -26,130 +26,8 @@ function createAsyncContextStore(key) {
26
26
  }
27
27
  var init_asyncContextStore = () => {};
28
28
 
29
- // ../../src/core/metrics/prometheus.ts
30
- var HTTP_DURATION_BUCKETS_MS = [5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 1e4];
31
- function emptyHistogram() {
32
- return {
33
- cumulative: HTTP_DURATION_BUCKETS_MS.map(() => 0),
34
- sum: 0,
35
- count: 0
36
- };
37
- }
38
-
39
- class PrometheusRegistry {
40
- maxSeries;
41
- seriesLabels = new Map;
42
- constructor(maxSeries = 4096) {
43
- this.maxSeries = maxSeries;
44
- if (!Number.isSafeInteger(maxSeries) || maxSeries < 1) {
45
- throw new Error("Metrics maxSeries must be a positive safe integer.");
46
- }
47
- }
48
- httpRequestsTotal = new Map;
49
- httpRequestDurationMs = new Map;
50
- incrementHttpRequest(labels) {
51
- const key = this.metricKey(labels);
52
- this.httpRequestsTotal.set(key, (this.httpRequestsTotal.get(key) ?? 0) + 1);
53
- }
54
- observeHttpDuration(labels, durationMs) {
55
- if (!Number.isFinite(durationMs) || durationMs < 0)
56
- return;
57
- const key = this.metricKey(labels);
58
- const histogram = this.httpRequestDurationMs.get(key) ?? emptyHistogram();
59
- histogram.count += 1;
60
- histogram.sum += durationMs;
61
- for (let index = 0;index < HTTP_DURATION_BUCKETS_MS.length; index += 1) {
62
- const bound = HTTP_DURATION_BUCKETS_MS[index];
63
- if (bound !== undefined && durationMs <= bound) {
64
- const count = histogram.cumulative[index] ?? 0;
65
- histogram.cumulative[index] = count + 1;
66
- }
67
- }
68
- this.httpRequestDurationMs.set(key, histogram);
69
- }
70
- renderMetrics() {
71
- const lines = [
72
- "# HELP http_requests_total Total HTTP requests processed.",
73
- "# TYPE http_requests_total counter"
74
- ];
75
- for (const [key, value] of this.httpRequestsTotal) {
76
- lines.push(`http_requests_total{${key}} ${value}`);
77
- }
78
- lines.push("# HELP http_request_duration_ms HTTP request duration in milliseconds.", "# TYPE http_request_duration_ms histogram");
79
- for (const [key, histogram] of this.httpRequestDurationMs) {
80
- for (let index = 0;index < HTTP_DURATION_BUCKETS_MS.length; index += 1) {
81
- const bound = HTTP_DURATION_BUCKETS_MS[index];
82
- lines.push(`http_request_duration_ms_bucket{${key},le="${bound}"} ${histogram.cumulative[index] ?? 0}`);
83
- }
84
- lines.push(`http_request_duration_ms_bucket{${key},le="+Inf"} ${histogram.count}`);
85
- lines.push(`http_request_duration_ms_sum{${key}} ${histogram.sum}`);
86
- lines.push(`http_request_duration_ms_count{${key}} ${histogram.count}`);
87
- }
88
- return `${lines.join(`
89
- `)}
90
- `;
91
- }
92
- resetForTests() {
93
- this.seriesLabels.clear();
94
- this.httpRequestsTotal.clear();
95
- this.httpRequestDurationMs.clear();
96
- }
97
- getHttpRequestSummary() {
98
- const byStatus = {};
99
- const pathCounts = new Map;
100
- let totalRequests = 0;
101
- for (const [key, count] of this.httpRequestsTotal) {
102
- totalRequests += count;
103
- const labels = this.seriesLabels.get(key);
104
- if (!labels)
105
- continue;
106
- const { method, path, status } = labels;
107
- byStatus[status] = (byStatus[status] ?? 0) + count;
108
- const pathKey = `${method} ${path}`;
109
- const existing = pathCounts.get(pathKey);
110
- if (existing) {
111
- existing.count += count;
112
- } else {
113
- pathCounts.set(pathKey, { method, path, count });
114
- }
115
- }
116
- const topPaths = Array.from(pathCounts.values()).sort((left, right) => right.count - left.count).slice(0, 10);
117
- return {
118
- totalRequests,
119
- byStatus,
120
- topPaths
121
- };
122
- }
123
- getStorageStats() {
124
- return {
125
- series: this.seriesLabels.size,
126
- histograms: this.httpRequestDurationMs.size,
127
- bucketCounters: this.httpRequestDurationMs.size * HTTP_DURATION_BUCKETS_MS.length
128
- };
129
- }
130
- metricKey(labels) {
131
- const normalized = {
132
- method: /^(GET|HEAD|POST|PUT|PATCH|DELETE|CONNECT|OPTIONS|TRACE)$/.test(labels.method) ? labels.method : "OTHER",
133
- path: labels.path.length <= 512 ? labels.path : "__overflow__",
134
- status: /^[1-5][0-9]{2}$/.test(labels.status) ? labels.status : "000"
135
- };
136
- const escapeLabel = (value) => value.replaceAll("\\", "\\\\").replaceAll(`
137
- `, "\\n").replaceAll('"', "\\\"");
138
- const encode = (value) => `method="${value.method}",path="${escapeLabel(value.path)}",status="${value.status}"`;
139
- let key = encode(normalized);
140
- if (!this.seriesLabels.has(key)) {
141
- if (this.seriesLabels.size >= this.maxSeries) {
142
- const overflow = { method: "OTHER", path: "__overflow__", status: "000" };
143
- key = encode(overflow);
144
- this.seriesLabels.set(key, overflow);
145
- } else {
146
- this.seriesLabels.set(key, normalized);
147
- }
148
- }
149
- return key;
150
- }
151
- }
152
- var prometheusRegistry = new PrometheusRegistry;
29
+ // ../../src/core/http/metricsMiddleware.ts
30
+ import { prometheusRegistry } from "@getstrata/core/metrics/prometheus";
153
31
 
154
32
  // ../../src/core/http/requestMetaContext.ts
155
33
  init_asyncContextStore();
@@ -995,10 +995,141 @@ function sdkClientClassName() {
995
995
  return fromName ? `${fromName}Client` : "AppClient";
996
996
  }
997
997
 
998
+ // ../../src/core/security/safeFetch.ts
999
+ import { pinUrlToAddress, resolveSafeOutboundTarget } from "@getstrata/core/security/safeUrl";
1000
+ var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
1001
+ function untilAborted(work, signal) {
1002
+ signal.throwIfAborted();
1003
+ return new Promise((resolve, reject) => {
1004
+ const onAbort = () => reject(signal.reason);
1005
+ signal.addEventListener("abort", onAbort, { once: true });
1006
+ work.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
1007
+ });
1008
+ }
1009
+ function preserveResponseMetadata(response, original) {
1010
+ Object.defineProperties(response, {
1011
+ url: { value: original.url },
1012
+ redirected: { value: original.redirected },
1013
+ type: { value: original.type }
1014
+ });
1015
+ const clone = response.clone.bind(response);
1016
+ response.clone = () => preserveResponseMetadata(clone(), original);
1017
+ return response;
1018
+ }
1019
+ function withBodyDeadline(response, signal, release) {
1020
+ if (!response.body) {
1021
+ release();
1022
+ return response;
1023
+ }
1024
+ const reader = response.body.getReader();
1025
+ let stopped = false;
1026
+ let bodyController;
1027
+ const stop = (reason, errorBody) => {
1028
+ if (stopped)
1029
+ return;
1030
+ stopped = true;
1031
+ release();
1032
+ signal.removeEventListener("abort", onAbort);
1033
+ if (errorBody)
1034
+ bodyController.error(reason);
1035
+ reader.cancel(reason).catch(() => {}).finally(() => reader.releaseLock());
1036
+ };
1037
+ const onAbort = () => stop(signal.reason, true);
1038
+ const body = new ReadableStream({
1039
+ start(controller) {
1040
+ bodyController = controller;
1041
+ signal.addEventListener("abort", onAbort, { once: true });
1042
+ if (signal.aborted)
1043
+ onAbort();
1044
+ },
1045
+ async pull(controller) {
1046
+ try {
1047
+ const result = await untilAborted(reader.read(), signal);
1048
+ if (stopped)
1049
+ return;
1050
+ if (result.done) {
1051
+ stopped = true;
1052
+ release();
1053
+ signal.removeEventListener("abort", onAbort);
1054
+ reader.releaseLock();
1055
+ controller.close();
1056
+ } else {
1057
+ controller.enqueue(result.value);
1058
+ }
1059
+ } catch (error) {
1060
+ stop(error, true);
1061
+ }
1062
+ },
1063
+ cancel(reason) {
1064
+ stop(reason, false);
1065
+ }
1066
+ }, { highWaterMark: 0 });
1067
+ return preserveResponseMetadata(new Response(body, {
1068
+ status: response.status,
1069
+ statusText: response.statusText,
1070
+ headers: response.headers
1071
+ }), response);
1072
+ }
1073
+ async function safeFetch(input, init = {}, options = {}) {
1074
+ init.signal?.throwIfAborted();
1075
+ const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
1076
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
1077
+ throw new RangeError("safeFetch timeoutMs must be a positive finite number.");
1078
+ }
1079
+ const maxRedirects = options.maxRedirects ?? 0;
1080
+ const urlOptions = {
1081
+ allowHttp: options.allowHttp,
1082
+ resolveDns: options.resolveDns ?? true,
1083
+ allowPrivate: options.allowPrivate
1084
+ };
1085
+ const controller = new AbortController;
1086
+ const forwardAbort = () => controller.abort(init.signal?.reason);
1087
+ init.signal?.addEventListener("abort", forwardAbort, { once: true });
1088
+ const timeout = setTimeout(() => controller.abort(new DOMException("The operation timed out.", "TimeoutError")), timeoutMs);
1089
+ const release = () => {
1090
+ clearTimeout(timeout);
1091
+ init.signal?.removeEventListener("abort", forwardAbort);
1092
+ };
1093
+ try {
1094
+ let current = await untilAborted(resolveSafeOutboundTarget(input, urlOptions), controller.signal);
1095
+ let redirectCount = 0;
1096
+ while (true) {
1097
+ controller.signal.throwIfAborted();
1098
+ const address = current.addresses[0];
1099
+ const fetchUrl = address ? pinUrlToAddress(current.url, address).toString() : current.url.toString();
1100
+ const headers = new Headers(init.headers);
1101
+ if (address && !headers.has("host"))
1102
+ headers.set("Host", current.url.host);
1103
+ const fetchInit = {
1104
+ ...init,
1105
+ headers,
1106
+ signal: controller.signal,
1107
+ redirect: "manual"
1108
+ };
1109
+ if (address)
1110
+ fetchInit.tls = { serverName: current.url.hostname };
1111
+ const response = await untilAborted(fetch(fetchUrl, fetchInit), controller.signal);
1112
+ if (response.status >= 300 && response.status < 400) {
1113
+ const location = response.headers.get("location");
1114
+ if (location && redirectCount < maxRedirects) {
1115
+ if (response.body)
1116
+ await untilAborted(response.body.cancel(), controller.signal);
1117
+ current = await untilAborted(resolveSafeOutboundTarget(new URL(location, current.url).toString(), urlOptions), controller.signal);
1118
+ redirectCount++;
1119
+ continue;
1120
+ }
1121
+ }
1122
+ return withBodyDeadline(response, controller.signal, release);
1123
+ }
1124
+ } catch (error) {
1125
+ controller.abort(error);
1126
+ release();
1127
+ throw error;
1128
+ }
1129
+ }
1130
+
998
1131
  // ../../src/core/security/safeUrl.ts
999
- import { lookup as dnsLookupImpl } from "dns/promises";
1000
1132
  import { BadRequestError } from "@getstrata/core/errors/http";
1001
- var dnsLookup = dnsLookupImpl;
1002
1133
  var BLOCKED_HOSTNAMES = new Set([
1003
1134
  "localhost",
1004
1135
  "127.0.0.1",
@@ -1199,171 +1330,6 @@ function assertSafeOutboundUrl(rawUrl, options = {}) {
1199
1330
  }
1200
1331
  return parsed;
1201
1332
  }
1202
- function isBlockedIpAddress(address) {
1203
- return isBlockedHostname(address.trim().toLowerCase());
1204
- }
1205
- function isLiteralIpHostname(hostname) {
1206
- return parseCanonicalDottedDecimal(hostname) !== null || expandIpv6(hostname) !== null;
1207
- }
1208
- function pinUrlToAddress(url, address) {
1209
- const pinned = new URL(url.toString());
1210
- const ipv6 = expandIpv6(address.replace(/^\[|\]$/gu, ""));
1211
- pinned.hostname = ipv6 ? `[${address.replace(/^\[|\]$/gu, "")}]` : address;
1212
- return pinned;
1213
- }
1214
- async function resolveSafeOutboundTarget(rawUrl, options = {}) {
1215
- const resolvedOptions = productionSafeOutboundOptions(options);
1216
- const parsed = assertSafeOutboundUrl(rawUrl, resolvedOptions);
1217
- if (resolvedOptions.resolveDns === false || resolvedOptions.allowPrivate) {
1218
- return { url: parsed, addresses: [] };
1219
- }
1220
- const hostname = parsed.hostname.trim().toLowerCase();
1221
- if (isLiteralIpHostname(hostname)) {
1222
- return { url: parsed, addresses: [hostname] };
1223
- }
1224
- let results;
1225
- try {
1226
- results = await dnsLookup(hostname, { all: true, verbatim: true });
1227
- } catch {
1228
- throw new BadRequestError("Webhook URL targets a blocked host.");
1229
- }
1230
- if (results.length === 0 || results.some((result) => isBlockedIpAddress(result.address))) {
1231
- throw new BadRequestError("Webhook URL targets a blocked host.");
1232
- }
1233
- return { url: parsed, addresses: results.map((result) => result.address) };
1234
- }
1235
-
1236
- // ../../src/core/security/safeFetch.ts
1237
- var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
1238
- function untilAborted(work, signal) {
1239
- signal.throwIfAborted();
1240
- return new Promise((resolve, reject) => {
1241
- const onAbort = () => reject(signal.reason);
1242
- signal.addEventListener("abort", onAbort, { once: true });
1243
- work.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
1244
- });
1245
- }
1246
- function preserveResponseMetadata(response, original) {
1247
- Object.defineProperties(response, {
1248
- url: { value: original.url },
1249
- redirected: { value: original.redirected },
1250
- type: { value: original.type }
1251
- });
1252
- const clone = response.clone.bind(response);
1253
- response.clone = () => preserveResponseMetadata(clone(), original);
1254
- return response;
1255
- }
1256
- function withBodyDeadline(response, signal, release) {
1257
- if (!response.body) {
1258
- release();
1259
- return response;
1260
- }
1261
- const reader = response.body.getReader();
1262
- let stopped = false;
1263
- let bodyController;
1264
- const stop = (reason, errorBody) => {
1265
- if (stopped)
1266
- return;
1267
- stopped = true;
1268
- release();
1269
- signal.removeEventListener("abort", onAbort);
1270
- if (errorBody)
1271
- bodyController.error(reason);
1272
- reader.cancel(reason).catch(() => {}).finally(() => reader.releaseLock());
1273
- };
1274
- const onAbort = () => stop(signal.reason, true);
1275
- const body = new ReadableStream({
1276
- start(controller) {
1277
- bodyController = controller;
1278
- signal.addEventListener("abort", onAbort, { once: true });
1279
- if (signal.aborted)
1280
- onAbort();
1281
- },
1282
- async pull(controller) {
1283
- try {
1284
- const result = await untilAborted(reader.read(), signal);
1285
- if (stopped)
1286
- return;
1287
- if (result.done) {
1288
- stopped = true;
1289
- release();
1290
- signal.removeEventListener("abort", onAbort);
1291
- reader.releaseLock();
1292
- controller.close();
1293
- } else {
1294
- controller.enqueue(result.value);
1295
- }
1296
- } catch (error) {
1297
- stop(error, true);
1298
- }
1299
- },
1300
- cancel(reason) {
1301
- stop(reason, false);
1302
- }
1303
- }, { highWaterMark: 0 });
1304
- return preserveResponseMetadata(new Response(body, {
1305
- status: response.status,
1306
- statusText: response.statusText,
1307
- headers: response.headers
1308
- }), response);
1309
- }
1310
- async function safeFetch(input, init = {}, options = {}) {
1311
- init.signal?.throwIfAborted();
1312
- const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
1313
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
1314
- throw new RangeError("safeFetch timeoutMs must be a positive finite number.");
1315
- }
1316
- const maxRedirects = options.maxRedirects ?? 0;
1317
- const urlOptions = {
1318
- allowHttp: options.allowHttp,
1319
- resolveDns: options.resolveDns ?? true,
1320
- allowPrivate: options.allowPrivate
1321
- };
1322
- const controller = new AbortController;
1323
- const forwardAbort = () => controller.abort(init.signal?.reason);
1324
- init.signal?.addEventListener("abort", forwardAbort, { once: true });
1325
- const timeout = setTimeout(() => controller.abort(new DOMException("The operation timed out.", "TimeoutError")), timeoutMs);
1326
- const release = () => {
1327
- clearTimeout(timeout);
1328
- init.signal?.removeEventListener("abort", forwardAbort);
1329
- };
1330
- try {
1331
- let current = await untilAborted(resolveSafeOutboundTarget(input, urlOptions), controller.signal);
1332
- let redirectCount = 0;
1333
- while (true) {
1334
- controller.signal.throwIfAborted();
1335
- const address = current.addresses[0];
1336
- const fetchUrl = address ? pinUrlToAddress(current.url, address).toString() : current.url.toString();
1337
- const headers = new Headers(init.headers);
1338
- if (address && !headers.has("host"))
1339
- headers.set("Host", current.url.host);
1340
- const fetchInit = {
1341
- ...init,
1342
- headers,
1343
- signal: controller.signal,
1344
- redirect: "manual"
1345
- };
1346
- if (address)
1347
- fetchInit.tls = { serverName: current.url.hostname };
1348
- const response = await untilAborted(fetch(fetchUrl, fetchInit), controller.signal);
1349
- if (response.status >= 300 && response.status < 400) {
1350
- const location = response.headers.get("location");
1351
- if (location && redirectCount < maxRedirects) {
1352
- if (response.body)
1353
- await untilAborted(response.body.cancel(), controller.signal);
1354
- current = await untilAborted(resolveSafeOutboundTarget(new URL(location, current.url).toString(), urlOptions), controller.signal);
1355
- redirectCount++;
1356
- continue;
1357
- }
1358
- }
1359
- return withBodyDeadline(response, controller.signal, release);
1360
- }
1361
- } catch (error) {
1362
- controller.abort(error);
1363
- release();
1364
- throw error;
1365
- }
1366
- }
1367
1333
 
1368
1334
  // ../../src/core/audit/exportAuditLogs.ts
1369
1335
  init_databaseTenantContext();