@hraness/kb 0.17.1

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.
Files changed (200) hide show
  1. package/DISCLOSURE +43 -0
  2. package/LICENSE +21 -0
  3. package/README.md +545 -0
  4. package/dist/agent-context.js +35 -0
  5. package/dist/agent-guide-audit.js +18 -0
  6. package/dist/attachments.js +19 -0
  7. package/dist/authoring.js +33 -0
  8. package/dist/benchmark.js +19 -0
  9. package/dist/capture.js +49 -0
  10. package/dist/cli.js +3646 -0
  11. package/dist/clip/acquire.js +57 -0
  12. package/dist/clip/args.js +21 -0
  13. package/dist/clip/bounded-byte-buffer.js +9 -0
  14. package/dist/clip/browser-profiles.js +17 -0
  15. package/dist/clip/bundle-reader.js +11 -0
  16. package/dist/clip/cli.js +27 -0
  17. package/dist/clip/cookies.js +21 -0
  18. package/dist/clip/defuddle-worker.js +84 -0
  19. package/dist/clip/doctor.js +32 -0
  20. package/dist/clip/jobs.js +784 -0
  21. package/dist/clip/network-proxy.js +9 -0
  22. package/dist/clip/network.js +28 -0
  23. package/dist/clip/persist.js +30 -0
  24. package/dist/clip/refresh.js +11 -0
  25. package/dist/clip/terminal.js +11 -0
  26. package/dist/evaluation-builder.js +12238 -0
  27. package/dist/evaluation-kb.js +26 -0
  28. package/dist/evaluation.js +35 -0
  29. package/dist/git.js +39 -0
  30. package/dist/graph.js +39 -0
  31. package/dist/index-01jj6rbv.js +1103 -0
  32. package/dist/index-06c9ctr6.js +738 -0
  33. package/dist/index-0kavxzqj.js +2480 -0
  34. package/dist/index-1gwbassd.js +743 -0
  35. package/dist/index-1n418kb9.js +827 -0
  36. package/dist/index-1vrd1rmn.js +56 -0
  37. package/dist/index-1xxnjn0d.js +171 -0
  38. package/dist/index-3rm7cz6h.js +373 -0
  39. package/dist/index-3v2z4f0q.js +328 -0
  40. package/dist/index-48pz4jpc.js +392 -0
  41. package/dist/index-4j3tt0c3.js +234 -0
  42. package/dist/index-5n05se68.js +307 -0
  43. package/dist/index-5vwpzb5a.js +764 -0
  44. package/dist/index-6g2pv9d2.js +370 -0
  45. package/dist/index-7fzc32gf.js +712 -0
  46. package/dist/index-84x0vjjp.js +432 -0
  47. package/dist/index-adx6khj5.js +488 -0
  48. package/dist/index-b88v3vtm.js +805 -0
  49. package/dist/index-cv6fh7z5.js +462 -0
  50. package/dist/index-cxfrakt7.js +1067 -0
  51. package/dist/index-d13v9ckt.js +150 -0
  52. package/dist/index-dycjbhw2.js +1943 -0
  53. package/dist/index-dyqwejk5.js +531 -0
  54. package/dist/index-e5fbsywq.js +658 -0
  55. package/dist/index-f984hw45.js +927 -0
  56. package/dist/index-gh719d91.js +63 -0
  57. package/dist/index-hgve9rh2.js +2046 -0
  58. package/dist/index-hya40gb2.js +474 -0
  59. package/dist/index-j4zgmzjr.js +152 -0
  60. package/dist/index-jsmvyyvf.js +1166 -0
  61. package/dist/index-kh0pwvdd.js +1974 -0
  62. package/dist/index-mqx4nd6v.js +119 -0
  63. package/dist/index-mxxxytys.js +1254 -0
  64. package/dist/index-n5dd7r0v.js +711 -0
  65. package/dist/index-npg9z1a4.js +431 -0
  66. package/dist/index-pj501bh1.js +140 -0
  67. package/dist/index-qry4vhxk.js +1041 -0
  68. package/dist/index-s2gw5aw9.js +199 -0
  69. package/dist/index-vvdj6kca.js +70 -0
  70. package/dist/index-w2zc0vwa.js +571 -0
  71. package/dist/index-x3fthpsc.js +585 -0
  72. package/dist/index-zr53sf63.js +44 -0
  73. package/dist/index-zxdy5pby.js +2728 -0
  74. package/dist/index-zzhgcwyt.js +966 -0
  75. package/dist/index.js +453 -0
  76. package/dist/navigation.js +13 -0
  77. package/dist/pdf.js +85 -0
  78. package/dist/percolate.js +24 -0
  79. package/dist/portfolio.js +77 -0
  80. package/dist/query.js +29 -0
  81. package/dist/repository-memory.js +60 -0
  82. package/dist/sdk.js +36 -0
  83. package/dist/search-rules.js +34 -0
  84. package/dist/search.js +22 -0
  85. package/dist/semantic.js +39 -0
  86. package/dist/source-inbox.js +17 -0
  87. package/dist/untrusted-content.js +32 -0
  88. package/dist/url-intelligence.js +74 -0
  89. package/dist/workflow.js +23 -0
  90. package/dist/workflows/decision-context.js +20 -0
  91. package/dist/workflows/explain-change.js +8 -0
  92. package/dist/workflows/index.js +28 -0
  93. package/dist/workflows/plan-radar.js +10 -0
  94. package/package.json +409 -0
  95. package/skills/kb/AGENTS.md +19 -0
  96. package/skills/kb/SKILL.md +82 -0
  97. package/skills/kb/agents/openai.yaml +4 -0
  98. package/skills/kb/references/pdf-review.md +51 -0
  99. package/skills/kb/references/percolate.md +144 -0
  100. package/skills/kb/references/plan-structure.md +105 -0
  101. package/skills/kb/references/plan.md +115 -0
  102. package/skills/kb/references/query.md +231 -0
  103. package/skills/kb/references/refresh.md +133 -0
  104. package/skills/kb/references/save-pdf.md +125 -0
  105. package/skills/kb/references/save-url.md +178 -0
  106. package/skills/kb/references/url-authentication.md +75 -0
  107. package/skills/kb/references/url-platforms.md +25 -0
  108. package/src/agent-context.ts +1241 -0
  109. package/src/agent-guide-audit.ts +750 -0
  110. package/src/attachments.ts +723 -0
  111. package/src/authoring.ts +1725 -0
  112. package/src/benchmark.ts +362 -0
  113. package/src/capture.ts +68 -0
  114. package/src/cli.ts +3660 -0
  115. package/src/clip/acquire.ts +1273 -0
  116. package/src/clip/archive-today.ts +402 -0
  117. package/src/clip/args.ts +423 -0
  118. package/src/clip/assets.ts +262 -0
  119. package/src/clip/bounded-byte-buffer.ts +75 -0
  120. package/src/clip/browser-profiles.ts +372 -0
  121. package/src/clip/bundle-reader.ts +608 -0
  122. package/src/clip/capture.ts +1054 -0
  123. package/src/clip/cli.ts +181 -0
  124. package/src/clip/cookies.ts +512 -0
  125. package/src/clip/doctor.ts +1110 -0
  126. package/src/clip/extract.ts +868 -0
  127. package/src/clip/jobs.ts +959 -0
  128. package/src/clip/lib.ts +861 -0
  129. package/src/clip/media.ts +1474 -0
  130. package/src/clip/metadata-search-tool/Cargo.lock +2498 -0
  131. package/src/clip/metadata-search-tool/Cargo.toml +18 -0
  132. package/src/clip/metadata-search-tool/runner.ts +685 -0
  133. package/src/clip/metadata-search-tool/src/main.rs +389 -0
  134. package/src/clip/metadata-search.ts +687 -0
  135. package/src/clip/network-proxy.ts +627 -0
  136. package/src/clip/network.ts +950 -0
  137. package/src/clip/package-root.ts +54 -0
  138. package/src/clip/persist.ts +1472 -0
  139. package/src/clip/platforms.ts +1824 -0
  140. package/src/clip/refresh.ts +197 -0
  141. package/src/clip/structured.ts +432 -0
  142. package/src/clip/terminal.ts +197 -0
  143. package/src/clip/url-intelligence.ts +581 -0
  144. package/src/clip/url-metadata-backfill.ts +616 -0
  145. package/src/clip/url-metadata-cli.ts +251 -0
  146. package/src/clip/url-metadata.ts +1397 -0
  147. package/src/evaluation-analysis-v2.ts +1440 -0
  148. package/src/evaluation-builder.ts +2539 -0
  149. package/src/evaluation-corpus-authoring.ts +2052 -0
  150. package/src/evaluation-evidence.ts +1147 -0
  151. package/src/evaluation-execution-v2.ts +426 -0
  152. package/src/evaluation-implementation.ts +131 -0
  153. package/src/evaluation-kb-closure.ts +1430 -0
  154. package/src/evaluation-kb-runner-v2.ts +735 -0
  155. package/src/evaluation-kb-v2.ts +2567 -0
  156. package/src/evaluation-kb.ts +1018 -0
  157. package/src/evaluation-measurement-v2.ts +1462 -0
  158. package/src/evaluation-packing-v2.ts +452 -0
  159. package/src/evaluation-redaction.ts +28 -0
  160. package/src/evaluation-v2.ts +3857 -0
  161. package/src/evaluation.ts +1220 -0
  162. package/src/git.ts +1276 -0
  163. package/src/graph.ts +1487 -0
  164. package/src/index.ts +20 -0
  165. package/src/init.ts +123 -0
  166. package/src/navigation.ts +277 -0
  167. package/src/note-lock.ts +529 -0
  168. package/src/pdf/args.ts +202 -0
  169. package/src/pdf/capture.ts +298 -0
  170. package/src/pdf/cli.ts +159 -0
  171. package/src/pdf/extract.ts +303 -0
  172. package/src/pdf/layout.ts +618 -0
  173. package/src/pdf/markdown.ts +154 -0
  174. package/src/pdf/model.ts +234 -0
  175. package/src/pdf/ocr.ts +190 -0
  176. package/src/pdf/persist.ts +314 -0
  177. package/src/pdf/source.ts +135 -0
  178. package/src/pdf/tools.ts +146 -0
  179. package/src/pdf.ts +92 -0
  180. package/src/percolate.ts +877 -0
  181. package/src/portfolio-audit.ts +574 -0
  182. package/src/portfolio-identity.ts +162 -0
  183. package/src/portfolio-registry.ts +473 -0
  184. package/src/portfolio.ts +765 -0
  185. package/src/query.ts +646 -0
  186. package/src/repository-memory.ts +1150 -0
  187. package/src/sdk.ts +1452 -0
  188. package/src/search-rules.ts +712 -0
  189. package/src/search.ts +710 -0
  190. package/src/semantic-runtime.ts +1139 -0
  191. package/src/semantic.ts +2480 -0
  192. package/src/source-inbox.ts +232 -0
  193. package/src/untrusted-content.ts +401 -0
  194. package/src/url-intelligence.ts +104 -0
  195. package/src/vault.ts +636 -0
  196. package/src/workflow.ts +653 -0
  197. package/src/workflows/decision-context.ts +81 -0
  198. package/src/workflows/explain-change.ts +73 -0
  199. package/src/workflows/index.ts +3 -0
  200. package/src/workflows/plan-radar.ts +101 -0
@@ -0,0 +1,658 @@
1
+ // @bun
2
+ import {
3
+ BoundedByteBuffer
4
+ } from "./index-gh719d91.js";
5
+
6
+ // src/clip/network.ts
7
+ import { lookup } from "dns/promises";
8
+ import {
9
+ Agent as HttpAgent,
10
+ request as requestHttp
11
+ } from "http";
12
+ import {
13
+ Agent as HttpsAgent,
14
+ request as requestHttps
15
+ } from "https";
16
+ import {
17
+ isIP
18
+ } from "net";
19
+ import { networkInterfaces } from "os";
20
+ class FetchFailure extends Error {
21
+ code;
22
+ status;
23
+ constructor(code, message, options) {
24
+ super(message, options);
25
+ this.name = "FetchFailure";
26
+ this.code = code;
27
+ this.status = options?.status ?? null;
28
+ }
29
+ }
30
+ var privateHostnameSuffixes = [".localhost", ".local", ".internal", ".home", ".lan"];
31
+ function normalizeHostname(hostname) {
32
+ const withoutBrackets = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
33
+ return withoutBrackets.toLowerCase().replace(/\.$/, "");
34
+ }
35
+ function parseIpv4(address) {
36
+ const pieces = address.split(".");
37
+ if (pieces.length !== 4)
38
+ return null;
39
+ const numbers = pieces.map((piece) => Number(piece));
40
+ return numbers.every((piece) => Number.isInteger(piece) && piece >= 0 && piece <= 255) ? numbers : null;
41
+ }
42
+ function parseIpv6(address) {
43
+ let value = address;
44
+ const ipv4Separator = value.lastIndexOf(":");
45
+ const ipv4Tail = ipv4Separator >= 0 ? value.slice(ipv4Separator + 1) : value;
46
+ if (ipv4Tail.includes(".")) {
47
+ const ipv4 = parseIpv4(ipv4Tail);
48
+ if (ipv4 === null || ipv4Separator < 0)
49
+ return null;
50
+ const high = (ipv4[0] ?? 0) << 8 | (ipv4[1] ?? 0);
51
+ const low = (ipv4[2] ?? 0) << 8 | (ipv4[3] ?? 0);
52
+ value = `${value.slice(0, ipv4Separator)}:${high.toString(16)}:${low.toString(16)}`;
53
+ }
54
+ const compression = value.indexOf("::");
55
+ if (compression !== -1 && compression !== value.lastIndexOf("::"))
56
+ return null;
57
+ const leftText = compression === -1 ? value : value.slice(0, compression);
58
+ const rightText = compression === -1 ? "" : value.slice(compression + 2);
59
+ const left = leftText === "" ? [] : leftText.split(":");
60
+ const right = rightText === "" ? [] : rightText.split(":");
61
+ if ([...left, ...right].some((part) => !/^[0-9a-f]{1,4}$/i.test(part)))
62
+ return null;
63
+ const missing = 8 - left.length - right.length;
64
+ if (compression === -1 && missing !== 0 || compression !== -1 && missing < 1)
65
+ return null;
66
+ const groups = [
67
+ ...left.map((part) => Number.parseInt(part, 16)),
68
+ ...Array.from({ length: Math.max(0, missing) }, () => 0),
69
+ ...right.map((part) => Number.parseInt(part, 16))
70
+ ];
71
+ return groups.length === 8 ? groups : null;
72
+ }
73
+ function addressWithoutScope(address) {
74
+ const normalized = normalizeHostname(address);
75
+ const scope = normalized.indexOf("%");
76
+ return scope === -1 ? normalized : normalized.slice(0, scope);
77
+ }
78
+ function comparableAddressKeys(address) {
79
+ const normalized = addressWithoutScope(address);
80
+ const version = isIP(normalized);
81
+ if (version === 4) {
82
+ const pieces = parseIpv4(normalized);
83
+ return pieces === null ? [] : [`4:${pieces.join(".")}`];
84
+ }
85
+ if (version !== 6)
86
+ return [];
87
+ const groups = parseIpv6(normalized);
88
+ if (groups === null)
89
+ return [];
90
+ const keys = [`6:${groups.map((group) => group.toString(16).padStart(4, "0")).join(":")}`];
91
+ const ipv4Compatible = groups.slice(0, 6).every((group) => group === 0);
92
+ const ipv4Mapped = groups.slice(0, 5).every((group) => group === 0) && groups[5] === 65535;
93
+ if (ipv4Compatible || ipv4Mapped) {
94
+ const high = groups[6] ?? 0;
95
+ const low = groups[7] ?? 0;
96
+ keys.push(`4:${high >>> 8}.${high & 255}.${low >>> 8}.${low & 255}`);
97
+ }
98
+ return keys;
99
+ }
100
+ function systemLocalNetworkAddresses() {
101
+ let interfaces;
102
+ try {
103
+ interfaces = networkInterfaces();
104
+ } catch (error) {
105
+ throw new FetchFailure("network", "could not inspect local network interfaces", { cause: error });
106
+ }
107
+ const addresses = [];
108
+ for (const records of Object.values(interfaces)) {
109
+ if (!Array.isArray(records))
110
+ continue;
111
+ for (const record of records) {
112
+ if (typeof record?.address === "string")
113
+ addresses.push(record.address);
114
+ }
115
+ }
116
+ return addresses;
117
+ }
118
+ function localAddressKeys(provider) {
119
+ let addresses;
120
+ try {
121
+ addresses = provider();
122
+ } catch (error) {
123
+ if (error instanceof FetchFailure)
124
+ throw error;
125
+ throw new FetchFailure("network", "could not inspect local network interfaces", { cause: error });
126
+ }
127
+ const keys = new Set;
128
+ for (const address of addresses) {
129
+ for (const key of comparableAddressKeys(address))
130
+ keys.add(key);
131
+ }
132
+ return keys;
133
+ }
134
+ function isAssignedLocalAddress(address, localKeys) {
135
+ return comparableAddressKeys(address).some((key) => localKeys.has(key));
136
+ }
137
+ function isPrivateAddress(address) {
138
+ const normalized = addressWithoutScope(address);
139
+ const version = isIP(normalized);
140
+ if (version === 4) {
141
+ const pieces = parseIpv4(normalized);
142
+ if (pieces === null)
143
+ return true;
144
+ const a = pieces[0] ?? 0;
145
+ const b = pieces[1] ?? 0;
146
+ return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && (b === 0 || b === 168) || a === 192 && b === 0 && (pieces[2] ?? 0) === 2 || a === 198 && (b === 18 || b === 19) || a === 198 && b === 51 && (pieces[2] ?? 0) === 100 || a === 203 && b === 0 && (pieces[2] ?? 0) === 113 || a >= 224;
147
+ }
148
+ if (version === 6) {
149
+ const groups = parseIpv6(normalized);
150
+ if (groups === null)
151
+ return true;
152
+ const first = groups[0] ?? 0;
153
+ const second = groups[1] ?? 0;
154
+ const firstSixAreZero = groups.slice(0, 6).every((group) => group === 0);
155
+ const ipv4Compatible = firstSixAreZero;
156
+ const ipv4Mapped = groups.slice(0, 5).every((group) => group === 0) && groups[5] === 65535;
157
+ if (ipv4Compatible || ipv4Mapped) {
158
+ const high = groups[6] ?? 0;
159
+ const low = groups[7] ?? 0;
160
+ return isPrivateAddress(`${high >>> 8}.${high & 255}.${low >>> 8}.${low & 255}`);
161
+ }
162
+ return (first & 65024) === 64512 || (first & 65472) === 65152 || (first & 65472) === 65216 || (first & 65280) === 65280 || first === 100 || first === 256 || first === 8193 && !Number.isNaN(second) && second <= 511 || first === 8193 && second === 3512 || first === 8194 || first === 16382 || first === 16383 && !Number.isNaN(second) && (second & 61440) === 0 || first === 24320;
163
+ }
164
+ return true;
165
+ }
166
+ function isPrivateHostname(hostname) {
167
+ const normalized = normalizeHostname(hostname);
168
+ if (normalized === "localhost" || normalized === "localhost.localdomain")
169
+ return true;
170
+ if (privateHostnameSuffixes.some((suffix) => normalized.endsWith(suffix)))
171
+ return true;
172
+ return isIP(normalized) !== 0 && isPrivateAddress(normalized);
173
+ }
174
+ async function systemResolveHostname(hostname) {
175
+ const answers = await lookup(hostname, { all: true, verbatim: true });
176
+ return answers.flatMap((answer) => answer.family === 4 || answer.family === 6 ? [{ address: answer.address, family: answer.family }] : []);
177
+ }
178
+ async function beforeDeadline(promise, deadline, timeoutMessage) {
179
+ const remainingMs = deadline - Date.now();
180
+ if (remainingMs <= 0)
181
+ throw new FetchFailure("timeout", timeoutMessage);
182
+ let timeout;
183
+ try {
184
+ return await Promise.race([
185
+ promise,
186
+ new Promise((_resolve, reject) => {
187
+ timeout = setTimeout(() => reject(new FetchFailure("timeout", timeoutMessage)), remainingMs);
188
+ })
189
+ ]);
190
+ } finally {
191
+ if (timeout !== undefined)
192
+ clearTimeout(timeout);
193
+ }
194
+ }
195
+ async function resolveNetworkTarget(url, allowPrivateNetwork, resolveHostname, getLocalNetworkAddresses, deadline, timeoutMs) {
196
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
197
+ throw new FetchFailure("invalid-url", `unsupported URL protocol: ${url.protocol}`);
198
+ }
199
+ if (url.username !== "" || url.password !== "") {
200
+ throw new FetchFailure("invalid-url", "credential-bearing URLs are not accepted; use a local cookie file or browser session");
201
+ }
202
+ const hostname = normalizeHostname(url.hostname);
203
+ if (!allowPrivateNetwork && isPrivateHostname(hostname)) {
204
+ throw new FetchFailure("private-network", `private-network URL requires --allow-private-network: ${url.origin}`);
205
+ }
206
+ const assignedLocalAddresses = allowPrivateNetwork ? new Set : localAddressKeys(getLocalNetworkAddresses);
207
+ const literalFamily = isIP(hostname);
208
+ if (literalFamily === 4 || literalFamily === 6) {
209
+ if (isAssignedLocalAddress(hostname, assignedLocalAddresses)) {
210
+ throw new FetchFailure("private-network", `address ${hostname} is assigned to a local interface; use --allow-private-network only when intended`);
211
+ }
212
+ return [{ address: hostname, family: literalFamily }];
213
+ }
214
+ let answers;
215
+ try {
216
+ answers = await beforeDeadline(resolveHostname(hostname), deadline, `request timed out after ${timeoutMs}ms while resolving ${hostname}`);
217
+ } catch (error) {
218
+ if (error instanceof FetchFailure)
219
+ throw error;
220
+ throw new FetchFailure("dns", `could not resolve ${hostname}`, { cause: error });
221
+ }
222
+ if (answers.length === 0)
223
+ throw new FetchFailure("dns", `could not resolve ${hostname}`);
224
+ if (!allowPrivateNetwork) {
225
+ for (const key of localAddressKeys(getLocalNetworkAddresses))
226
+ assignedLocalAddresses.add(key);
227
+ }
228
+ const unique = new Map;
229
+ for (const answer of answers) {
230
+ const address = normalizeHostname(answer.address);
231
+ const actualFamily = isIP(address);
232
+ if (actualFamily !== 4 && actualFamily !== 6 || actualFamily !== answer.family) {
233
+ throw new FetchFailure("dns", `${hostname} returned an invalid DNS answer`);
234
+ }
235
+ if (!allowPrivateNetwork && isPrivateAddress(address)) {
236
+ throw new FetchFailure("private-network", `${hostname} resolves to private or reserved address ${address}; use --allow-private-network only when intended`);
237
+ }
238
+ if (!allowPrivateNetwork && isAssignedLocalAddress(address, assignedLocalAddresses)) {
239
+ throw new FetchFailure("private-network", `${hostname} resolves to an address assigned to a local interface; use --allow-private-network only when intended`);
240
+ }
241
+ unique.set(`${answer.family}:${address}`, { address, family: answer.family });
242
+ }
243
+ return [...unique.values()];
244
+ }
245
+ async function resolveSafeNetworkTarget(url, options) {
246
+ const timeoutMs = options.timeoutMs ?? 30000;
247
+ return await resolveNetworkTarget(url, options.allowPrivateNetwork, options.resolveHostname ?? systemResolveHostname, options.getLocalNetworkAddresses ?? systemLocalNetworkAddresses, Date.now() + timeoutMs, timeoutMs);
248
+ }
249
+ async function assertSafeNetworkUrl(url, allowPrivateNetwork, timeoutMs = 30000) {
250
+ await resolveSafeNetworkTarget(url, { allowPrivateNetwork, timeoutMs });
251
+ }
252
+ function createPinnedLookup(pinned) {
253
+ const address = pinned.address;
254
+ const family = pinned.family;
255
+ return (_hostname, options, callback) => {
256
+ queueMicrotask(() => {
257
+ if (options.all === true) {
258
+ callback(null, [{ address, family }]);
259
+ return;
260
+ }
261
+ callback(null, address, family);
262
+ });
263
+ };
264
+ }
265
+ function responseHeaders(headers) {
266
+ const result = new Headers;
267
+ for (const [name, value] of Object.entries(headers)) {
268
+ if (typeof value === "string")
269
+ result.append(name, value);
270
+ else if (Array.isArray(value)) {
271
+ for (const item of value)
272
+ result.append(name, item);
273
+ }
274
+ }
275
+ return result;
276
+ }
277
+ function requestHeaders(headers) {
278
+ const result = {};
279
+ headers.forEach((value, name) => {
280
+ result[name] = value;
281
+ });
282
+ return result;
283
+ }
284
+ function preparePinnedNetworkRequest(request) {
285
+ const hostname = normalizeHostname(request.url.hostname);
286
+ if (request.url.protocol !== "http:" && request.url.protocol !== "https:") {
287
+ throw new Error(`unsupported pinned request protocol: ${request.url.protocol}`);
288
+ }
289
+ if (isIP(request.address.address) !== request.address.family) {
290
+ throw new Error("pinned request address does not match its declared family");
291
+ }
292
+ const hostnameFamily = isIP(hostname);
293
+ if (hostnameFamily !== 0) {
294
+ const hostnameKey = comparableAddressKeys(hostname)[0];
295
+ const pinnedKey = comparableAddressKeys(request.address.address)[0];
296
+ const pinnedAddressHasScope = normalizeHostname(request.address.address) !== addressWithoutScope(request.address.address);
297
+ if (hostnameFamily !== request.address.family || pinnedAddressHasScope || hostnameKey === undefined || hostnameKey !== pinnedKey) {
298
+ throw new Error("IP-literal request hostname does not match the pinned address");
299
+ }
300
+ }
301
+ return {
302
+ hostname,
303
+ options: {
304
+ protocol: request.url.protocol,
305
+ hostname,
306
+ method: request.method,
307
+ path: `${request.url.pathname}${request.url.search}`,
308
+ headers: requestHeaders(request.headers),
309
+ lookup: createPinnedLookup(request.address),
310
+ family: request.address.family,
311
+ signal: request.signal,
312
+ ...request.url.port === "" ? {} : { port: request.url.port }
313
+ }
314
+ };
315
+ }
316
+ function sendPreparedPinnedNetworkRequest(request, prepared, agent, activeRequests, activeSockets) {
317
+ return new Promise((resolve, reject) => {
318
+ const onResponse = (response) => {
319
+ if (response.statusCode === undefined) {
320
+ response.destroy();
321
+ reject(new Error("HTTP response omitted a status code"));
322
+ return;
323
+ }
324
+ resolve({
325
+ status: response.statusCode,
326
+ headers: responseHeaders(response.headers),
327
+ body: response,
328
+ cancel: () => response.destroy()
329
+ });
330
+ };
331
+ const requestOptions = {
332
+ ...prepared.options,
333
+ agent
334
+ };
335
+ const clientRequest = request.url.protocol === "https:" ? requestHttps({
336
+ ...requestOptions,
337
+ ...isIP(prepared.hostname) === 0 ? { servername: prepared.hostname } : {}
338
+ }, onResponse) : requestHttp(requestOptions, onResponse);
339
+ activeRequests?.add(clientRequest);
340
+ clientRequest.once("close", () => activeRequests?.delete(clientRequest));
341
+ clientRequest.once("socket", (socket) => {
342
+ activeSockets?.add(socket);
343
+ socket.once("close", () => activeSockets?.delete(socket));
344
+ });
345
+ clientRequest.on("error", reject);
346
+ clientRequest.end(request.body ?? undefined);
347
+ });
348
+ }
349
+ function requestPinnedNetworkAddress(request) {
350
+ try {
351
+ return sendPreparedPinnedNetworkRequest(request, preparePinnedNetworkRequest(request), false);
352
+ } catch (error) {
353
+ return Promise.reject(error instanceof Error ? error : new Error("pinned network request preparation failed", { cause: error }));
354
+ }
355
+ }
356
+ function createPinnedNetworkAgent(request, options) {
357
+ return request.url.protocol === "https:" ? new HttpsAgent({
358
+ keepAlive: true,
359
+ maxFreeSockets: 1,
360
+ maxSockets: 1,
361
+ ...options.certificateAuthorities === undefined ? {} : { ca: options.certificateAuthorities }
362
+ }) : new HttpAgent({ keepAlive: true, maxFreeSockets: 1, maxSockets: 1 });
363
+ }
364
+ function createPinnedNetworkConnectionPool(options = {}) {
365
+ const supportsIsolatedAgents = process.versions.bun === undefined;
366
+ const agents = new Map;
367
+ const activeRequests = new Set;
368
+ const activeSockets = new Set;
369
+ let closed = false;
370
+ const close = () => {
371
+ if (closed)
372
+ return;
373
+ closed = true;
374
+ for (const request of activeRequests) {
375
+ request.destroy();
376
+ }
377
+ activeRequests.clear();
378
+ for (const socket of activeSockets) {
379
+ socket.destroy();
380
+ }
381
+ activeSockets.clear();
382
+ for (const agent of agents.values()) {
383
+ agent.destroy();
384
+ }
385
+ agents.clear();
386
+ };
387
+ const pooledRequest = async (request) => {
388
+ if (closed) {
389
+ throw new Error("pinned network connection pool is closed");
390
+ }
391
+ const prepared = preparePinnedNetworkRequest(request);
392
+ const addressKey = comparableAddressKeys(request.address.address)[0];
393
+ if (addressKey === undefined) {
394
+ throw new Error("pinned request address is invalid");
395
+ }
396
+ const key = [
397
+ request.url.origin,
398
+ request.address.family,
399
+ addressKey
400
+ ].join("\x00");
401
+ if (closed)
402
+ throw new Error("pinned network connection pool is closed");
403
+ if (request.signal.aborted) {
404
+ const reason = request.signal.reason;
405
+ throw reason instanceof Error ? reason : new Error("pinned network request aborted");
406
+ }
407
+ if (!supportsIsolatedAgents) {
408
+ return await sendPreparedPinnedNetworkRequest(request, {
409
+ ...prepared,
410
+ options: {
411
+ ...prepared.options,
412
+ headers: {
413
+ ...requestHeaders(request.headers),
414
+ connection: "close"
415
+ },
416
+ ...options.certificateAuthorities === undefined ? {} : { ca: options.certificateAuthorities }
417
+ }
418
+ }, false, activeRequests, activeSockets);
419
+ }
420
+ let agent = agents.get(key);
421
+ if (agent === undefined) {
422
+ agent = createPinnedNetworkAgent(request, options);
423
+ agents.set(key, agent);
424
+ }
425
+ return await sendPreparedPinnedNetworkRequest(request, prepared, agent, activeRequests, activeSockets);
426
+ };
427
+ return Object.freeze({
428
+ request: pooledRequest,
429
+ close
430
+ });
431
+ }
432
+ function retryDelay(response, attempt) {
433
+ const header = response?.headers.get("retry-after")?.trim();
434
+ if (header !== undefined && header !== null) {
435
+ const seconds = Number(header);
436
+ if (Number.isFinite(seconds) && seconds >= 0)
437
+ return Math.min(seconds * 1000, 5000);
438
+ const date = Date.parse(header);
439
+ if (!Number.isNaN(date))
440
+ return Math.min(Math.max(date - Date.now(), 0), 5000);
441
+ }
442
+ return Math.min(250 * 2 ** attempt, 2000);
443
+ }
444
+ var retryableStatuses = new Set([408, 425, 429, 500, 502, 503, 504]);
445
+ async function readBounded(response, maxBytes) {
446
+ const declared = response.headers.get("content-length");
447
+ if (declared !== null) {
448
+ const length = Number(declared);
449
+ if (Number.isFinite(length) && length > maxBytes) {
450
+ response.cancel();
451
+ throw new FetchFailure("too-large", `response declares ${length} bytes; limit is ${maxBytes}`);
452
+ }
453
+ }
454
+ if (response.body === null)
455
+ return new Uint8Array;
456
+ const bytes = new BoundedByteBuffer(maxBytes);
457
+ for await (const value of response.body) {
458
+ if (!(value instanceof Uint8Array)) {
459
+ response.cancel();
460
+ throw new FetchFailure("network", "response body yielded a non-byte chunk");
461
+ }
462
+ if (!bytes.append(value)) {
463
+ response.cancel();
464
+ throw new FetchFailure("too-large", `response exceeded ${maxBytes} bytes`);
465
+ }
466
+ }
467
+ return bytes.toUint8Array();
468
+ }
469
+ function buildHeaders(current, originalUrl, options) {
470
+ const headers = new Headers({
471
+ Accept: options.accept ?? "text/html,application/xhtml+xml;q=0.9,*/*;q=0.5",
472
+ "Accept-Encoding": "identity",
473
+ "User-Agent": options.userAgent
474
+ });
475
+ if (options.referer !== undefined) {
476
+ try {
477
+ const referer = new URL(options.referer);
478
+ headers.set("Referer", referer.origin === current.origin ? referer.href : `${referer.origin}/`);
479
+ } catch {}
480
+ }
481
+ if (options.cookieHeader !== undefined && current.href === originalUrl.href) {
482
+ headers.set("Cookie", options.cookieHeader);
483
+ }
484
+ return headers;
485
+ }
486
+ function createSafeFetch(dependencies = {}) {
487
+ const resolveHostname = dependencies.resolveHostname ?? systemResolveHostname;
488
+ const transport = dependencies.transport ?? requestPinnedNetworkAddress;
489
+ const getLocalNetworkAddresses = dependencies.getLocalNetworkAddresses ?? systemLocalNetworkAddresses;
490
+ return async (url, options) => {
491
+ const maxRedirects = options.maxRedirects ?? 8;
492
+ const retries = options.retries ?? 2;
493
+ const acceptedStatuses = new Set(options.acceptStatuses ?? []);
494
+ if (acceptedStatuses.size > 8 || [...acceptedStatuses].some((status) => !Number.isInteger(status) || status < 400 || status > 599)) {
495
+ throw new FetchFailure("invalid-url", "accepted response statuses must contain at most eight HTTP 4xx/5xx integers");
496
+ }
497
+ const allowedRedirectOrigins = options.allowedRedirectOrigins === undefined ? null : new Set(options.allowedRedirectOrigins.map((origin) => {
498
+ let parsed;
499
+ try {
500
+ parsed = new URL(origin);
501
+ } catch (error) {
502
+ throw new FetchFailure("invalid-url", "allowed redirect origins must be absolute HTTP(S) origins", { cause: error });
503
+ }
504
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || parsed.pathname !== "/" || parsed.search !== "" || parsed.hash !== "" || parsed.href !== `${parsed.origin}/`) {
505
+ throw new FetchFailure("invalid-url", "allowed redirect origins must be canonical HTTP(S) origins");
506
+ }
507
+ return parsed.origin;
508
+ }));
509
+ const deadline = Date.now() + options.timeoutMs;
510
+ const originalUrl = new URL(url);
511
+ let current = new URL(url);
512
+ for (let redirects = 0;redirects <= maxRedirects; redirects += 1) {
513
+ const addresses = await resolveNetworkTarget(current, options.allowPrivateNetwork, resolveHostname, getLocalNetworkAddresses, deadline, options.timeoutMs);
514
+ let response = null;
515
+ let lastError;
516
+ let finalController = null;
517
+ let finalTimeout = null;
518
+ for (let attempt = 0;attempt <= retries; attempt += 1) {
519
+ const remainingMs = deadline - Date.now();
520
+ if (remainingMs <= 0) {
521
+ throw new FetchFailure("timeout", `request timed out after ${options.timeoutMs}ms: ${current.href}`);
522
+ }
523
+ const controller = new AbortController;
524
+ const timeout = setTimeout(() => controller.abort(), remainingMs);
525
+ try {
526
+ const address = addresses[attempt % addresses.length];
527
+ if (address === undefined)
528
+ throw new FetchFailure("dns", `could not resolve ${current.hostname}`);
529
+ response = await beforeDeadline(transport({
530
+ url: new URL(current),
531
+ address,
532
+ method: "GET",
533
+ headers: buildHeaders(current, originalUrl, options),
534
+ body: null,
535
+ signal: controller.signal
536
+ }), deadline, `request timed out after ${options.timeoutMs}ms: ${current.href}`);
537
+ if (!retryableStatuses.has(response.status) || attempt === retries) {
538
+ finalController = controller;
539
+ finalTimeout = timeout;
540
+ break;
541
+ }
542
+ controller.abort();
543
+ response.cancel();
544
+ clearTimeout(timeout);
545
+ } catch (error) {
546
+ lastError = error;
547
+ controller.abort();
548
+ clearTimeout(timeout);
549
+ if (error instanceof FetchFailure && error.code === "timeout")
550
+ throw error;
551
+ if (attempt === retries) {
552
+ if (controller.signal.aborted) {
553
+ throw new FetchFailure("timeout", `request timed out after ${options.timeoutMs}ms: ${current.href}`, {
554
+ cause: error
555
+ });
556
+ }
557
+ if (error instanceof FetchFailure)
558
+ throw error;
559
+ throw new FetchFailure("network", `request failed: ${current.href}`, { cause: error });
560
+ }
561
+ }
562
+ const delay = Math.min(retryDelay(response, attempt), Math.max(0, deadline - Date.now()));
563
+ if (delay > 0)
564
+ await Bun.sleep(delay);
565
+ }
566
+ if (response === null) {
567
+ throw new FetchFailure("network", `request failed: ${current.href}`, { cause: lastError });
568
+ }
569
+ if (response.status >= 300 && response.status < 400) {
570
+ if (options.redirect === "manual") {
571
+ try {
572
+ const location = response.headers.get("location");
573
+ response.cancel();
574
+ return {
575
+ bytes: new Uint8Array,
576
+ finalUrl: current,
577
+ status: response.status,
578
+ contentType: response.headers.get("content-type"),
579
+ etag: response.headers.get("etag"),
580
+ lastModified: response.headers.get("last-modified"),
581
+ location
582
+ };
583
+ } finally {
584
+ finalController?.abort();
585
+ if (finalTimeout !== null)
586
+ clearTimeout(finalTimeout);
587
+ }
588
+ }
589
+ try {
590
+ const location = response.headers.get("location");
591
+ if (location === null)
592
+ throw new FetchFailure("redirect", `HTTP ${response.status} omitted Location`);
593
+ if (redirects === maxRedirects)
594
+ throw new FetchFailure("redirect", `more than ${maxRedirects} redirects`);
595
+ let next;
596
+ try {
597
+ next = new URL(location, current);
598
+ } catch (error) {
599
+ throw new FetchFailure("redirect", `invalid redirect target: ${location}`, { cause: error });
600
+ }
601
+ if (allowedRedirectOrigins !== null && !allowedRedirectOrigins.has(next.origin)) {
602
+ throw new FetchFailure("redirect", `redirect target origin is not allowed: ${next.origin}`);
603
+ }
604
+ current = next;
605
+ continue;
606
+ } finally {
607
+ finalController?.abort();
608
+ response.cancel();
609
+ if (finalTimeout !== null)
610
+ clearTimeout(finalTimeout);
611
+ }
612
+ }
613
+ if ((response.status < 200 || response.status >= 300) && !acceptedStatuses.has(response.status)) {
614
+ if (finalTimeout !== null)
615
+ clearTimeout(finalTimeout);
616
+ finalController?.abort();
617
+ response.cancel();
618
+ throw new FetchFailure("http", `HTTP ${response.status} for ${current.href}`, { status: response.status });
619
+ }
620
+ try {
621
+ const bytes = await beforeDeadline(readBounded(response, options.maxBytes), deadline, `response body timed out after ${options.timeoutMs}ms: ${current.href}`);
622
+ return {
623
+ bytes,
624
+ finalUrl: current,
625
+ status: response.status,
626
+ contentType: response.headers.get("content-type"),
627
+ etag: response.headers.get("etag"),
628
+ lastModified: response.headers.get("last-modified"),
629
+ location: null
630
+ };
631
+ } catch (error) {
632
+ if (finalController?.signal.aborted === true || error instanceof FetchFailure && error.code === "timeout") {
633
+ response.cancel();
634
+ throw new FetchFailure("timeout", `response body timed out after ${options.timeoutMs}ms: ${current.href}`, {
635
+ cause: error
636
+ });
637
+ }
638
+ if (error instanceof FetchFailure)
639
+ throw error;
640
+ throw new FetchFailure("network", `response body failed: ${current.href}`, { cause: error });
641
+ } finally {
642
+ if (finalTimeout !== null)
643
+ clearTimeout(finalTimeout);
644
+ }
645
+ }
646
+ throw new FetchFailure("redirect", `more than ${maxRedirects} redirects`);
647
+ };
648
+ }
649
+ async function safeFetch(url, options) {
650
+ return await createSafeFetch()(url, options);
651
+ }
652
+ function decodeBytes(bytes, contentType) {
653
+ const charset = /charset\s*=\s*["']?([^;"'\s]+)/i.exec(contentType ?? "")?.[1]?.toLowerCase();
654
+ const supported = charset === "iso-8859-1" || charset === "windows-1252" ? "windows-1252" : "utf-8";
655
+ return new TextDecoder(supported, { fatal: false }).decode(bytes);
656
+ }
657
+
658
+ export { FetchFailure, isPrivateAddress, isPrivateHostname, resolveSafeNetworkTarget, assertSafeNetworkUrl, createPinnedLookup, requestPinnedNetworkAddress, createPinnedNetworkConnectionPool, createSafeFetch, safeFetch, decodeBytes };