@fedify/vocab-runtime 2.5.0-dev.2448 → 2.5.0-dev.2494

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 (120) hide show
  1. package/deno.json +5 -2
  2. package/dist/{docloader-BBnBpbsG.d.cts → docloader-DD6binpN.d.cts} +4 -8
  3. package/dist/{docloader-BBnBpbsG.d.ts → docloader-DD6binpN.d.ts} +4 -8
  4. package/dist/internal/jsonld-cache.cjs +1 -1
  5. package/dist/internal/jsonld-cache.d.cts +1 -1
  6. package/dist/internal/jsonld-cache.d.ts +1 -1
  7. package/dist/internal/jsonld-cache.js +1 -1
  8. package/dist/internal/portable-dereference.cjs +1 -1
  9. package/dist/internal/portable-dereference.d.cts +2 -2
  10. package/dist/internal/portable-dereference.d.ts +2 -2
  11. package/dist/internal/portable-dereference.js +1 -1
  12. package/dist/internal.cjs +6 -0
  13. package/dist/internal.d.cts +108 -0
  14. package/dist/internal.d.ts +108 -0
  15. package/dist/internal.js +3 -0
  16. package/dist/mod.cjs +104 -38
  17. package/dist/mod.d.cts +3 -336
  18. package/dist/mod.d.ts +4 -336
  19. package/dist/mod.js +104 -38
  20. package/dist/{portable-BDydjp5H.d.ts → portable-1VSnSJFj.d.ts} +1 -1
  21. package/dist/{portable-Dfvcg_Wy.d.cts → portable-Cr7NwZwb.d.cts} +1 -1
  22. package/dist/public-fetch-Dvfvawei.cjs +527 -0
  23. package/dist/public-fetch-ziZ39L5I.js +510 -0
  24. package/dist/tests/{body-DP2u2wUX.cjs → body-0Or8U7lA.cjs} +1 -1
  25. package/dist/tests/{body-oV99TVdA.mjs → body-DfH6VTh9.mjs} +1 -1
  26. package/dist/tests/body.test.cjs +1 -1
  27. package/dist/tests/body.test.mjs +1 -1
  28. package/dist/tests/decimal.test.cjs +4 -4
  29. package/dist/tests/decimal.test.mjs +4 -4
  30. package/dist/tests/{docloader-D3mA7aPJ.mjs → docloader-BZvFOza_.mjs} +106 -40
  31. package/dist/tests/{docloader-CwxZguqN.cjs → docloader-W_RHEh5W.cjs} +111 -39
  32. package/dist/tests/docloader.test.cjs +531 -1293
  33. package/dist/tests/docloader.test.mjs +472 -1252
  34. package/dist/tests/esm-Ds-eHTfY.mjs +1229 -0
  35. package/dist/tests/esm-Q2tdh5f3.cjs +1215 -0
  36. package/dist/tests/internal/portable-dereference.test.cjs +3 -3
  37. package/dist/tests/internal/portable-dereference.test.mjs +3 -3
  38. package/dist/tests/{jsonld-cache-BEXiiM-V.cjs → jsonld-cache-DKIWKxEX.cjs} +1 -1
  39. package/dist/tests/{jsonld-cache-DFnWsLuZ.mjs → jsonld-cache-KH40z9_7.mjs} +1 -1
  40. package/dist/tests/jsonld-cache.test.cjs +2 -2
  41. package/dist/tests/jsonld-cache.test.mjs +2 -2
  42. package/dist/tests/node-CbBR-th-.mjs +84 -0
  43. package/dist/tests/node-Ch29PDXR.cjs +89 -0
  44. package/dist/tests/pin-grant-CPrpk1l4.cjs +70 -0
  45. package/dist/tests/pin-grant-H5XhM1Xs.mjs +53 -0
  46. package/dist/tests/pin-grant.test.cjs +87 -0
  47. package/dist/tests/pin-grant.test.d.cts +1 -0
  48. package/dist/tests/pin-grant.test.d.mts +1 -0
  49. package/dist/tests/pin-grant.test.mjs +88 -0
  50. package/dist/tests/pinners/bun.test.cjs +166 -0
  51. package/dist/tests/pinners/bun.test.d.cts +1 -0
  52. package/dist/tests/pinners/bun.test.d.mts +1 -0
  53. package/dist/tests/pinners/bun.test.mjs +167 -0
  54. package/dist/tests/pinners/node.test.cjs +269 -0
  55. package/dist/tests/pinners/node.test.d.cts +1 -0
  56. package/dist/tests/pinners/node.test.d.mts +1 -0
  57. package/dist/tests/pinners/node.test.mjs +268 -0
  58. package/dist/tests/pinners/pinners.test.cjs +196 -0
  59. package/dist/tests/pinners/pinners.test.d.cts +1 -0
  60. package/dist/tests/pinners/pinners.test.d.mts +1 -0
  61. package/dist/tests/pinners/pinners.test.mjs +197 -0
  62. package/dist/tests/pinners/testdata/node-child.cjs +135 -0
  63. package/dist/tests/pinners/testdata/node-child.d.cts +1 -0
  64. package/dist/tests/pinners/testdata/node-child.d.mts +1 -0
  65. package/dist/tests/pinners/testdata/node-child.mjs +134 -0
  66. package/dist/tests/pinners-DLH_npLf.cjs +220 -0
  67. package/dist/tests/pinners-tGcl49uF.mjs +203 -0
  68. package/dist/tests/{portable-dereference-DaTBBvCj.cjs → portable-dereference-BUoDzMxk.cjs} +2 -2
  69. package/dist/tests/{portable-dereference-sGRs_qdT.mjs → portable-dereference-Du2k3ngE.mjs} +2 -2
  70. package/dist/tests/{portable-media-B-oCeotd.mjs → portable-media-CqCq3wqj.mjs} +3 -3
  71. package/dist/tests/{portable-media-D7ClSjFO.cjs → portable-media-Ls_EOkMN.cjs} +3 -3
  72. package/dist/tests/portable-media.test.cjs +1 -1
  73. package/dist/tests/portable-media.test.mjs +1 -1
  74. package/dist/tests/portable-workers.test.cjs +2 -2
  75. package/dist/tests/portable-workers.test.mjs +2 -2
  76. package/dist/tests/public-fetch-BGj5pzUD.cjs +186 -0
  77. package/dist/tests/public-fetch-Cxo3Yzdf.mjs +170 -0
  78. package/dist/tests/public-fetch.test.cjs +1073 -0
  79. package/dist/tests/public-fetch.test.d.cts +1 -0
  80. package/dist/tests/public-fetch.test.d.mts +1 -0
  81. package/dist/tests/public-fetch.test.mjs +1074 -0
  82. package/dist/tests/{request-BlLYI4x3.mjs → request-Bo8cJJhV.mjs} +2 -2
  83. package/dist/tests/{request-B77_oZnk.cjs → request-Cb6h3X72.cjs} +2 -2
  84. package/dist/tests/request.test.cjs +2 -2
  85. package/dist/tests/request.test.mjs +2 -2
  86. package/dist/tests/support-Bj44xlQu.mjs +272 -0
  87. package/dist/tests/support-BrdlbLJk.cjs +363 -0
  88. package/dist/tests/{url-BNakuZ8k.cjs → url-Bdj-rTcC.cjs} +88 -12
  89. package/dist/tests/{url-DMxmp7ZG.mjs → url-COj5Q8mb.mjs} +65 -13
  90. package/dist/tests/url.test.cjs +4 -1
  91. package/dist/tests/url.test.mjs +4 -1
  92. package/dist/url-4EE56sNP.d.cts +352 -0
  93. package/dist/url-4EE56sNP.d.ts +352 -0
  94. package/dist/{url-DrGTR8yv.cjs → url-DIKZnaJv.cjs} +88 -12
  95. package/dist/{url-Dzyp-NsC.js → url-mWveR5GA.js} +65 -13
  96. package/package.json +14 -3
  97. package/src/docloader.test.ts +696 -48
  98. package/src/docloader.ts +186 -75
  99. package/src/internal.ts +13 -0
  100. package/src/pin-grant.test.ts +118 -0
  101. package/src/pin-grant.ts +99 -0
  102. package/src/pinners/bun.test.ts +278 -0
  103. package/src/pinners/bun.ts +223 -0
  104. package/src/pinners/mod.ts +59 -0
  105. package/src/pinners/node.test.ts +415 -0
  106. package/src/pinners/node.ts +144 -0
  107. package/src/pinners/pinners.test.ts +316 -0
  108. package/src/pinners/testdata/node-child.ts +147 -0
  109. package/src/pinners/testdata/proxy-child.ts +82 -0
  110. package/src/pinners/testdata/support.ts +343 -0
  111. package/src/pinners/testdata/tls-cert.pem +12 -0
  112. package/src/pinners/testdata/tls-child.ts +88 -0
  113. package/src/pinners/testdata/tls-key.pem +5 -0
  114. package/src/pinners/types.ts +48 -0
  115. package/src/public-fetch.test.ts +1286 -0
  116. package/src/public-fetch.ts +338 -0
  117. package/src/testdata/public-fetch-deno-worker.ts +74 -0
  118. package/src/url.test.ts +11 -0
  119. package/src/url.ts +113 -22
  120. package/tsdown.config.ts +7 -2
@@ -1,7 +1,7 @@
1
- const require_url = require("./url-BNakuZ8k.cjs");
1
+ const require_url = require("./url-Bdj-rTcC.cjs");
2
2
  const require_contexts = require("./contexts-C9Lj11SC.cjs");
3
3
  require("./jsonld-CmDhHNTx.cjs");
4
- const require_jsonld_cache = require("./jsonld-cache-BEXiiM-V.cjs");
4
+ const require_jsonld_cache = require("./jsonld-cache-DKIWKxEX.cjs");
5
5
  let _logtape_logtape = require("@logtape/logtape");
6
6
  require("@opentelemetry/api");
7
7
  //#region src/internal/portable-dereference.ts
@@ -1,7 +1,7 @@
1
- import { h as parseGatewayOrigin, o as formatIri, s as fromCompatibleEf61Id, t as GATEWAY_HINT_PARAMETER, u as haveSameFe34Origin } from "./url-DMxmp7ZG.mjs";
1
+ import { c as fromCompatibleEf61Id, d as haveSameFe34Origin, s as formatIri, t as GATEWAY_HINT_PARAMETER, v as parseGatewayOrigin } from "./url-COj5Q8mb.mjs";
2
2
  import { t as preloadedContexts } from "./contexts-duRA_dDC.mjs";
3
3
  import "./jsonld-DweIJ07b.mjs";
4
- import { c as unwrapReleasedDocumentLoader, s as registerDocumentLoaderWrapper } from "./jsonld-cache-DFnWsLuZ.mjs";
4
+ import { c as unwrapReleasedDocumentLoader, s as registerDocumentLoaderWrapper } from "./jsonld-cache-KH40z9_7.mjs";
5
5
  import { getLogger } from "@logtape/logtape";
6
6
  import "@opentelemetry/api";
7
7
  //#region src/internal/portable-dereference.ts
@@ -1,6 +1,6 @@
1
- import { r as getUserAgent } from "./request-BlLYI4x3.mjs";
2
- import { g as parseGatewayUrl, x as validatePublicUrl } from "./url-DMxmp7ZG.mjs";
3
- import { r as readBoundedBytes } from "./body-oV99TVdA.mjs";
1
+ import { r as getUserAgent } from "./request-Bo8cJJhV.mjs";
2
+ import { T as validatePublicUrl, y as parseGatewayUrl } from "./url-COj5Q8mb.mjs";
3
+ import { r as readBoundedBytes } from "./body-DfH6VTh9.mjs";
4
4
  import { a as verifyDigestMultibase, i as parseHashlink, r as parseDigestMultibase } from "./digest-COC7xDiQ.mjs";
5
5
  //#region src/portable-media.ts
6
6
  function resolveTimeout(value, fallback) {
@@ -1,6 +1,6 @@
1
- const require_request = require("./request-B77_oZnk.cjs");
2
- const require_url = require("./url-BNakuZ8k.cjs");
3
- const require_body = require("./body-DP2u2wUX.cjs");
1
+ const require_request = require("./request-Cb6h3X72.cjs");
2
+ const require_url = require("./url-Bdj-rTcC.cjs");
3
+ const require_body = require("./body-0Or8U7lA.cjs");
4
4
  const require_digest = require("./digest-3FeH2Y-Q.cjs");
5
5
  //#region src/portable-media.ts
6
6
  function resolveTimeout(value, fallback) {
@@ -1,5 +1,5 @@
1
1
  const require_digest = require("./digest-3FeH2Y-Q.cjs");
2
- const require_portable_media = require("./portable-media-D7ClSjFO.cjs");
2
+ const require_portable_media = require("./portable-media-Ls_EOkMN.cjs");
3
3
  let node_assert_strict = require("node:assert/strict");
4
4
  let node_test = require("node:test");
5
5
  let node_http = require("node:http");
@@ -1,5 +1,5 @@
1
1
  import { n as createHashlink, t as computeDigestMultibase } from "./digest-COC7xDiQ.mjs";
2
- import { t as fetchPortableMedia } from "./portable-media-B-oCeotd.mjs";
2
+ import { t as fetchPortableMedia } from "./portable-media-CqCq3wqj.mjs";
3
3
  import { deepStrictEqual, equal, rejects } from "node:assert/strict";
4
4
  import { test } from "node:test";
5
5
  import { createServer } from "node:http";
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_url = require("./url-BNakuZ8k.cjs");
2
+ const require_url = require("./url-Bdj-rTcC.cjs");
3
3
  const require_key = require("./key-C-AYkdJJ.cjs");
4
4
  const require_digest = require("./digest-3FeH2Y-Q.cjs");
5
- const require_portable_dereference = require("./portable-dereference-DaTBBvCj.cjs");
5
+ const require_portable_dereference = require("./portable-dereference-BUoDzMxk.cjs");
6
6
  let node_assert_strict = require("node:assert/strict");
7
7
  let _fedify_fixture = require("@fedify/fixture");
8
8
  //#region src/portable-workers.test.ts
@@ -1,7 +1,7 @@
1
- import { _ as parseIri, i as canonicalizePortableUri, o as formatIri, s as fromCompatibleEf61Id, y as toCompatibleEf61Id } from "./url-DMxmp7ZG.mjs";
1
+ import { C as toCompatibleEf61Id, a as canonicalizePortableUri, b as parseIri, c as fromCompatibleEf61Id, s as formatIri } from "./url-COj5Q8mb.mjs";
2
2
  import { i as importDidKey, t as exportDidKey } from "./key-C2Db_TAJ.mjs";
3
3
  import { n as createHashlink, o as verifyHashlink, t as computeDigestMultibase } from "./digest-COC7xDiQ.mjs";
4
- import { n as getPortableGatewayCandidates } from "./portable-dereference-sGRs_qdT.mjs";
4
+ import { n as getPortableGatewayCandidates } from "./portable-dereference-Du2k3ngE.mjs";
5
5
  import { deepStrictEqual, equal } from "node:assert/strict";
6
6
  import { test, testDefinitions } from "@fedify/fixture";
7
7
  //#region src/portable-workers.test.ts
@@ -0,0 +1,186 @@
1
+ const require_url = require("./url-Bdj-rTcC.cjs");
2
+ const require_pin_grant = require("./pin-grant-CPrpk1l4.cjs");
3
+ const require_pinners = require("./pinners-DLH_npLf.cjs");
4
+ //#region src/public-fetch.ts
5
+ const logger = (0, require("@logtape/logtape").getLogger)([
6
+ "fedify",
7
+ "runtime",
8
+ "fetch"
9
+ ]);
10
+ /**
11
+ * Creates {@link PublicFetch.openPublicFetch} and
12
+ * {@link PublicFetch.withPublicFetch} bound to the given pinner and lookup.
13
+ * This is exported only for tests; it is not exported from any entry point.
14
+ * @param dependencies The pinner and the default lookup function.
15
+ * @returns The functions.
16
+ */
17
+ function createPublicFetch(dependencies) {
18
+ async function openPublicFetch(url, options = {}) {
19
+ const { url: parsed, hostname, family } = require_url.parsePublicUrl(url);
20
+ if (parsed.username !== "" || parsed.password !== "") throw new require_url.UrlError("URLs with credentials are not allowed");
21
+ const lookup = options.lookup ?? dependencies.lookup;
22
+ let addresses;
23
+ if (family !== 0) addresses = [{
24
+ address: hostname,
25
+ family
26
+ }];
27
+ else if (lookup === require_url.lookupAllAddresses && !await canResolve()) return createSession(parsed, globalFetch);
28
+ else {
29
+ options.signal?.throwIfAborted();
30
+ addresses = await require_url.lookupPublicAddresses(hostname, lookup);
31
+ options.signal?.throwIfAborted();
32
+ }
33
+ const pinner = await dependencies.pinner();
34
+ if (pinner == null || !pinner.supports(parsed)) {
35
+ logUnpinned(parsed);
36
+ return createSession(parsed, globalFetch);
37
+ }
38
+ const grant = require_pin_grant.issuePinGrant(parsed, addresses);
39
+ return createSession(parsed, (input, init) => pinner.fetch(grant, new Request(input, init), init.signal ?? void 0), {
40
+ grant,
41
+ pinner
42
+ });
43
+ }
44
+ async function withPublicFetch(url, fn, options) {
45
+ const session = await openPublicFetch(url, options);
46
+ try {
47
+ const result = await fn(session.fetch);
48
+ if (result instanceof Response) throw new TypeError("withPublicFetch() callbacks must not return a Response, since its body is cancelled when the session is closed.");
49
+ return result;
50
+ } catch (error) {
51
+ logger.debug("Failed while fetching {url}; closing the session: {error}", {
52
+ url: session.url.href,
53
+ error
54
+ });
55
+ throw error;
56
+ } finally {
57
+ await session.close();
58
+ }
59
+ }
60
+ return {
61
+ openPublicFetch,
62
+ withPublicFetch
63
+ };
64
+ }
65
+ const runtimePublicFetch = createPublicFetch({
66
+ pinner: require_pinners.getRuntimePinner,
67
+ lookup: require_url.lookupAllAddresses
68
+ });
69
+ /**
70
+ * Validates the URL, resolves its hostname, checks that every resolved
71
+ * address is public, and opens a session that connects only to those
72
+ * addresses. Connections are pinned on Node.js and Bun. On other runtimes,
73
+ * the session uses the global `fetch()` after the check.
74
+ * @param url The URL to open a session for.
75
+ * @param options The options.
76
+ * @returns The session. It must be closed.
77
+ * @throws {UrlError} If the URL is not allowed, or its hostname cannot be
78
+ * resolved (with `reason: "dns"`).
79
+ */
80
+ const openPublicFetch = runtimePublicFetch.openPublicFetch;
81
+ runtimePublicFetch.withPublicFetch;
82
+ /**
83
+ * Opens a session that neither validates nor pins addresses, for callers that
84
+ * allow private addresses. It has the same rules as {@link openPublicFetch}
85
+ * sessions otherwise: requests are limited to the URL's origin, redirects are
86
+ * never followed, and unconsumed response bodies are cancelled on close.
87
+ * It grants nothing beyond calling the global `fetch()` directly.
88
+ * @param url The URL to open a session for.
89
+ * @returns The session. It must be closed.
90
+ */
91
+ function openDirectFetch(url) {
92
+ return Promise.resolve(createSession(new URL(url), globalFetch));
93
+ }
94
+ /**
95
+ * Starts cancelling the body of a response without waiting for it to settle.
96
+ * The cancellation of a branch of a tee (e.g., a body cloned by a fetch mock)
97
+ * settles only after the other branch is cancelled too, per the Streams
98
+ * standard, so awaiting it could block forever. Failures are only logged.
99
+ */
100
+ function cancelResponseBody(response, url) {
101
+ response.body?.cancel().catch((error) => {
102
+ logger.debug("Failed to cancel a response body from {url}: {error}", {
103
+ url: url.href,
104
+ error
105
+ });
106
+ });
107
+ }
108
+ function globalFetch(input, init) {
109
+ return fetch(input, init);
110
+ }
111
+ function createSession(url, send, pin) {
112
+ const origin = url.origin;
113
+ const tracked = [];
114
+ let closed = false;
115
+ async function sessionFetch(input, init) {
116
+ if (closed) throw new TypeError("The fetch session is closed.");
117
+ const target = new URL(input instanceof Request ? input.url : input);
118
+ if (target.origin !== origin) throw new TypeError(`A fetch session for ${origin} cannot fetch ${target.origin}.`);
119
+ const response = await send(input, {
120
+ ...init,
121
+ redirect: "manual"
122
+ });
123
+ if (closed) {
124
+ cancelResponseBody(response, url);
125
+ throw new TypeError("The fetch session is closed.");
126
+ }
127
+ tracked.push(response);
128
+ return response;
129
+ }
130
+ async function close() {
131
+ if (closed) return;
132
+ closed = true;
133
+ for (const response of tracked) {
134
+ if (response.body == null || response.bodyUsed) continue;
135
+ if (response.body.locked) {
136
+ logger.debug("Cannot cancel a locked response body from {url}.", { url: url.href });
137
+ continue;
138
+ }
139
+ cancelResponseBody(response, url);
140
+ }
141
+ if (pin == null) return;
142
+ require_pin_grant.revokePinGrant(pin.grant);
143
+ await pin.pinner.release(pin.grant);
144
+ }
145
+ return {
146
+ get url() {
147
+ return new URL(url);
148
+ },
149
+ fetch: sessionFetch,
150
+ close
151
+ };
152
+ }
153
+ async function canResolve() {
154
+ if (!("Deno" in globalThis)) return true;
155
+ return (await Deno.permissions.query({ name: "net" })).state === "granted";
156
+ }
157
+ const loggedUnpinned = /* @__PURE__ */ new Set();
158
+ function logUnpinned(url) {
159
+ const runtime = require_pinners.detectRuntime();
160
+ const key = `${runtime} ${url.protocol}`;
161
+ if (loggedUnpinned.has(key)) return;
162
+ loggedUnpinned.add(key);
163
+ logger.debug("Outbound {protocol} connections are not pinned to validated addresses on {runtime}; the hostname is resolved again when connecting.", {
164
+ runtime,
165
+ protocol: url.protocol
166
+ });
167
+ }
168
+ //#endregion
169
+ Object.defineProperty(exports, "createPublicFetch", {
170
+ enumerable: true,
171
+ get: function() {
172
+ return createPublicFetch;
173
+ }
174
+ });
175
+ Object.defineProperty(exports, "openDirectFetch", {
176
+ enumerable: true,
177
+ get: function() {
178
+ return openDirectFetch;
179
+ }
180
+ });
181
+ Object.defineProperty(exports, "openPublicFetch", {
182
+ enumerable: true,
183
+ get: function() {
184
+ return openPublicFetch;
185
+ }
186
+ });
@@ -0,0 +1,170 @@
1
+ import { S as parsePublicUrl, _ as lookupPublicAddresses, g as lookupAllAddresses, n as UrlError } from "./url-COj5Q8mb.mjs";
2
+ import { r as revokePinGrant, t as issuePinGrant } from "./pin-grant-H5XhM1Xs.mjs";
3
+ import { n as getRuntimePinner, t as detectRuntime } from "./pinners-tGcl49uF.mjs";
4
+ import { getLogger } from "@logtape/logtape";
5
+ //#region src/public-fetch.ts
6
+ const logger = getLogger([
7
+ "fedify",
8
+ "runtime",
9
+ "fetch"
10
+ ]);
11
+ /**
12
+ * Creates {@link PublicFetch.openPublicFetch} and
13
+ * {@link PublicFetch.withPublicFetch} bound to the given pinner and lookup.
14
+ * This is exported only for tests; it is not exported from any entry point.
15
+ * @param dependencies The pinner and the default lookup function.
16
+ * @returns The functions.
17
+ */
18
+ function createPublicFetch(dependencies) {
19
+ async function openPublicFetch(url, options = {}) {
20
+ const { url: parsed, hostname, family } = parsePublicUrl(url);
21
+ if (parsed.username !== "" || parsed.password !== "") throw new UrlError("URLs with credentials are not allowed");
22
+ const lookup = options.lookup ?? dependencies.lookup;
23
+ let addresses;
24
+ if (family !== 0) addresses = [{
25
+ address: hostname,
26
+ family
27
+ }];
28
+ else if (lookup === lookupAllAddresses && !await canResolve()) return createSession(parsed, globalFetch);
29
+ else {
30
+ options.signal?.throwIfAborted();
31
+ addresses = await lookupPublicAddresses(hostname, lookup);
32
+ options.signal?.throwIfAborted();
33
+ }
34
+ const pinner = await dependencies.pinner();
35
+ if (pinner == null || !pinner.supports(parsed)) {
36
+ logUnpinned(parsed);
37
+ return createSession(parsed, globalFetch);
38
+ }
39
+ const grant = issuePinGrant(parsed, addresses);
40
+ return createSession(parsed, (input, init) => pinner.fetch(grant, new Request(input, init), init.signal ?? void 0), {
41
+ grant,
42
+ pinner
43
+ });
44
+ }
45
+ async function withPublicFetch(url, fn, options) {
46
+ const session = await openPublicFetch(url, options);
47
+ try {
48
+ const result = await fn(session.fetch);
49
+ if (result instanceof Response) throw new TypeError("withPublicFetch() callbacks must not return a Response, since its body is cancelled when the session is closed.");
50
+ return result;
51
+ } catch (error) {
52
+ logger.debug("Failed while fetching {url}; closing the session: {error}", {
53
+ url: session.url.href,
54
+ error
55
+ });
56
+ throw error;
57
+ } finally {
58
+ await session.close();
59
+ }
60
+ }
61
+ return {
62
+ openPublicFetch,
63
+ withPublicFetch
64
+ };
65
+ }
66
+ const runtimePublicFetch = createPublicFetch({
67
+ pinner: getRuntimePinner,
68
+ lookup: lookupAllAddresses
69
+ });
70
+ /**
71
+ * Validates the URL, resolves its hostname, checks that every resolved
72
+ * address is public, and opens a session that connects only to those
73
+ * addresses. Connections are pinned on Node.js and Bun. On other runtimes,
74
+ * the session uses the global `fetch()` after the check.
75
+ * @param url The URL to open a session for.
76
+ * @param options The options.
77
+ * @returns The session. It must be closed.
78
+ * @throws {UrlError} If the URL is not allowed, or its hostname cannot be
79
+ * resolved (with `reason: "dns"`).
80
+ */
81
+ const openPublicFetch = runtimePublicFetch.openPublicFetch;
82
+ runtimePublicFetch.withPublicFetch;
83
+ /**
84
+ * Opens a session that neither validates nor pins addresses, for callers that
85
+ * allow private addresses. It has the same rules as {@link openPublicFetch}
86
+ * sessions otherwise: requests are limited to the URL's origin, redirects are
87
+ * never followed, and unconsumed response bodies are cancelled on close.
88
+ * It grants nothing beyond calling the global `fetch()` directly.
89
+ * @param url The URL to open a session for.
90
+ * @returns The session. It must be closed.
91
+ */
92
+ function openDirectFetch(url) {
93
+ return Promise.resolve(createSession(new URL(url), globalFetch));
94
+ }
95
+ /**
96
+ * Starts cancelling the body of a response without waiting for it to settle.
97
+ * The cancellation of a branch of a tee (e.g., a body cloned by a fetch mock)
98
+ * settles only after the other branch is cancelled too, per the Streams
99
+ * standard, so awaiting it could block forever. Failures are only logged.
100
+ */
101
+ function cancelResponseBody(response, url) {
102
+ response.body?.cancel().catch((error) => {
103
+ logger.debug("Failed to cancel a response body from {url}: {error}", {
104
+ url: url.href,
105
+ error
106
+ });
107
+ });
108
+ }
109
+ function globalFetch(input, init) {
110
+ return fetch(input, init);
111
+ }
112
+ function createSession(url, send, pin) {
113
+ const origin = url.origin;
114
+ const tracked = [];
115
+ let closed = false;
116
+ async function sessionFetch(input, init) {
117
+ if (closed) throw new TypeError("The fetch session is closed.");
118
+ const target = new URL(input instanceof Request ? input.url : input);
119
+ if (target.origin !== origin) throw new TypeError(`A fetch session for ${origin} cannot fetch ${target.origin}.`);
120
+ const response = await send(input, {
121
+ ...init,
122
+ redirect: "manual"
123
+ });
124
+ if (closed) {
125
+ cancelResponseBody(response, url);
126
+ throw new TypeError("The fetch session is closed.");
127
+ }
128
+ tracked.push(response);
129
+ return response;
130
+ }
131
+ async function close() {
132
+ if (closed) return;
133
+ closed = true;
134
+ for (const response of tracked) {
135
+ if (response.body == null || response.bodyUsed) continue;
136
+ if (response.body.locked) {
137
+ logger.debug("Cannot cancel a locked response body from {url}.", { url: url.href });
138
+ continue;
139
+ }
140
+ cancelResponseBody(response, url);
141
+ }
142
+ if (pin == null) return;
143
+ revokePinGrant(pin.grant);
144
+ await pin.pinner.release(pin.grant);
145
+ }
146
+ return {
147
+ get url() {
148
+ return new URL(url);
149
+ },
150
+ fetch: sessionFetch,
151
+ close
152
+ };
153
+ }
154
+ async function canResolve() {
155
+ if (!("Deno" in globalThis)) return true;
156
+ return (await Deno.permissions.query({ name: "net" })).state === "granted";
157
+ }
158
+ const loggedUnpinned = /* @__PURE__ */ new Set();
159
+ function logUnpinned(url) {
160
+ const runtime = detectRuntime();
161
+ const key = `${runtime} ${url.protocol}`;
162
+ if (loggedUnpinned.has(key)) return;
163
+ loggedUnpinned.add(key);
164
+ logger.debug("Outbound {protocol} connections are not pinned to validated addresses on {runtime}; the hostname is resolved again when connecting.", {
165
+ runtime,
166
+ protocol: url.protocol
167
+ });
168
+ }
169
+ //#endregion
170
+ export { openDirectFetch as n, openPublicFetch as r, createPublicFetch as t };