@fedify/vocab-runtime 2.3.15-dev.2482 → 2.3.15

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 (114) hide show
  1. package/deno.json +5 -2
  2. package/dist/internal.cjs +6 -0
  3. package/dist/internal.d.cts +3 -0
  4. package/dist/internal.d.ts +3 -0
  5. package/dist/internal.js +3 -0
  6. package/dist/mod.cjs +105 -253
  7. package/dist/mod.d.cts +5 -28
  8. package/dist/mod.d.ts +6 -28
  9. package/dist/mod.js +98 -245
  10. package/dist/public-fetch-BC-nWJNM.d.ts +143 -0
  11. package/dist/public-fetch-Bk32IaYB.cjs +821 -0
  12. package/dist/public-fetch-DBpt3xxf.d.cts +143 -0
  13. package/dist/public-fetch-Dfmc_Kfy.js +772 -0
  14. package/dist/tests/{body-BmS3KJE1.cjs → body-BtW9TZGZ.cjs} +1 -1
  15. package/dist/tests/{body-xrlsEUPK.mjs → body-CmdrGt1w.mjs} +1 -1
  16. package/dist/tests/body.test.cjs +1 -1
  17. package/dist/tests/body.test.mjs +1 -1
  18. package/dist/tests/decimal.test.cjs +6 -6
  19. package/dist/tests/decimal.test.mjs +6 -6
  20. package/dist/tests/{docloader-CETMi5su.cjs → docloader-D34pSxKd.cjs} +106 -35
  21. package/dist/tests/{docloader-BJXlwNXs.mjs → docloader-zoX3z1bj.mjs} +102 -37
  22. package/dist/tests/docloader.test.cjs +523 -1273
  23. package/dist/tests/docloader.test.mjs +469 -1238
  24. package/dist/tests/esm-C_ftyt-G.cjs +1208 -0
  25. package/dist/tests/esm-LaZ1p80h.mjs +1222 -0
  26. package/dist/tests/internal/multicodec.test.cjs +1 -1
  27. package/dist/tests/internal/multicodec.test.mjs +1 -1
  28. package/dist/tests/{key-Qj7u1qmH.mjs → key-2lZR0Cht.mjs} +2 -2
  29. package/dist/tests/{key-CWDxavIR.cjs → key-CnsHrQSU.cjs} +2 -2
  30. package/dist/tests/key.test.cjs +2 -2
  31. package/dist/tests/key.test.mjs +2 -2
  32. package/dist/tests/{langstr-BsqFJupv.cjs → langstr-CZpWmpo5.cjs} +1 -1
  33. package/dist/tests/{langstr-ABdAOKlF.mjs → langstr-DTB4sl0a.mjs} +1 -1
  34. package/dist/tests/langstr.test.cjs +1 -1
  35. package/dist/tests/langstr.test.mjs +1 -1
  36. package/dist/tests/langtag.test.cjs +1 -1
  37. package/dist/tests/langtag.test.mjs +1 -1
  38. package/dist/tests/link.test.mjs +1 -1
  39. package/dist/tests/multibase/multibase.test.cjs +1 -1
  40. package/dist/tests/multibase/multibase.test.mjs +1 -1
  41. package/dist/tests/node-C0iftNaR.cjs +90 -0
  42. package/dist/tests/node-C3bjFjL1.mjs +84 -0
  43. package/dist/tests/pin-grant-CU-o367k.cjs +70 -0
  44. package/dist/tests/pin-grant-HW3B6SKv.mjs +53 -0
  45. package/dist/tests/pin-grant.test.cjs +88 -0
  46. package/dist/tests/pin-grant.test.d.cts +1 -0
  47. package/dist/tests/pin-grant.test.d.mts +1 -0
  48. package/dist/tests/pin-grant.test.mjs +88 -0
  49. package/dist/tests/pinners/bun.test.cjs +167 -0
  50. package/dist/tests/pinners/bun.test.d.cts +1 -0
  51. package/dist/tests/pinners/bun.test.d.mts +1 -0
  52. package/dist/tests/pinners/bun.test.mjs +167 -0
  53. package/dist/tests/pinners/node.test.cjs +269 -0
  54. package/dist/tests/pinners/node.test.d.cts +1 -0
  55. package/dist/tests/pinners/node.test.d.mts +1 -0
  56. package/dist/tests/pinners/node.test.mjs +268 -0
  57. package/dist/tests/pinners/pinners.test.cjs +197 -0
  58. package/dist/tests/pinners/pinners.test.d.cts +1 -0
  59. package/dist/tests/pinners/pinners.test.d.mts +1 -0
  60. package/dist/tests/pinners/pinners.test.mjs +197 -0
  61. package/dist/tests/pinners/testdata/node-child.cjs +135 -0
  62. package/dist/tests/pinners/testdata/node-child.d.cts +1 -0
  63. package/dist/tests/pinners/testdata/node-child.d.mts +1 -0
  64. package/dist/tests/pinners/testdata/node-child.mjs +134 -0
  65. package/dist/tests/pinners-D4UQ-HXm.cjs +221 -0
  66. package/dist/tests/pinners-ZgZDx9y6.mjs +203 -0
  67. package/dist/tests/public-fetch-BtiBjQPL.cjs +187 -0
  68. package/dist/tests/public-fetch-Yi7Vks9s.mjs +170 -0
  69. package/dist/tests/public-fetch.test.cjs +1074 -0
  70. package/dist/tests/public-fetch.test.d.cts +1 -0
  71. package/dist/tests/public-fetch.test.d.mts +1 -0
  72. package/dist/tests/public-fetch.test.mjs +1074 -0
  73. package/dist/tests/{request-rtQTKXfB.mjs → request-BKFirRrO.mjs} +1 -1
  74. package/dist/tests/{request-pbEw07NE.cjs → request-CqKju-Ll.cjs} +1 -1
  75. package/dist/tests/request.test.cjs +1 -1
  76. package/dist/tests/request.test.mjs +1 -1
  77. package/dist/tests/support-Cj0T6TP_.cjs +363 -0
  78. package/dist/tests/support-M5ClN-no.mjs +272 -0
  79. package/dist/tests/{url-4PAkYhZC.cjs → url-BR7UVTkW.cjs} +88 -12
  80. package/dist/tests/{url-3fyJuqC0.mjs → url-DQWrMGhu.mjs} +65 -13
  81. package/dist/tests/url.test.cjs +4 -1
  82. package/dist/tests/url.test.mjs +4 -1
  83. package/package.json +14 -3
  84. package/src/docloader.test.ts +700 -32
  85. package/src/docloader.ts +174 -67
  86. package/src/internal.ts +13 -0
  87. package/src/pin-grant.test.ts +118 -0
  88. package/src/pin-grant.ts +99 -0
  89. package/src/pinners/bun.test.ts +278 -0
  90. package/src/pinners/bun.ts +223 -0
  91. package/src/pinners/mod.ts +59 -0
  92. package/src/pinners/node.test.ts +415 -0
  93. package/src/pinners/node.ts +144 -0
  94. package/src/pinners/pinners.test.ts +316 -0
  95. package/src/pinners/testdata/node-child.ts +147 -0
  96. package/src/pinners/testdata/proxy-child.ts +82 -0
  97. package/src/pinners/testdata/support.ts +343 -0
  98. package/src/pinners/testdata/tls-cert.pem +12 -0
  99. package/src/pinners/testdata/tls-child.ts +88 -0
  100. package/src/pinners/testdata/tls-key.pem +5 -0
  101. package/src/pinners/types.ts +48 -0
  102. package/src/public-fetch.test.ts +1286 -0
  103. package/src/public-fetch.ts +338 -0
  104. package/src/testdata/public-fetch-deno-worker.ts +74 -0
  105. package/src/url.test.ts +11 -0
  106. package/src/url.ts +113 -22
  107. package/tsdown.config.ts +12 -3
  108. /package/dist/tests/{langtag-LDBqlxHv.mjs → langtag-D8FWxjv8.mjs} +0 -0
  109. /package/dist/tests/{langtag-DgySESmk.cjs → langtag-a2-s9uyi.cjs} +0 -0
  110. /package/dist/tests/{link-CYszIegt.mjs → link-CHMtH2-k.mjs} +0 -0
  111. /package/dist/tests/{multibase-BL45eko3.cjs → multibase-CPwWBzL3.cjs} +0 -0
  112. /package/dist/tests/{multibase-Da9VJCw8.mjs → multibase-D85wf3ZR.mjs} +0 -0
  113. /package/dist/tests/{multicodec-ByS9XLsw.cjs → multicodec-B-_CSt1M.cjs} +0 -0
  114. /package/dist/tests/{multicodec-BBPZjRfg.mjs → multicodec-caWdG98t.mjs} +0 -0
@@ -1,1221 +1,38 @@
1
1
  const require_chunk = require("./chunk-C2EiDwsr.cjs");
2
- const require_request = require("./request-pbEw07NE.cjs");
3
- const require_docloader = require("./docloader-CETMi5su.cjs");
4
- const require_url = require("./url-4PAkYhZC.cjs");
2
+ const require_request = require("./request-CqKju-Ll.cjs");
3
+ const require_docloader = require("./docloader-D34pSxKd.cjs");
4
+ const require_url = require("./url-BR7UVTkW.cjs");
5
+ const require_public_fetch = require("./public-fetch-BtiBjQPL.cjs");
6
+ const require_esm = require("./esm-C_ftyt-G.cjs");
5
7
  let node_zlib = require("node:zlib");
6
8
  let _logtape_logtape = require("@logtape/logtape");
7
9
  let node_assert = require("node:assert");
8
- let node_dns_promises = require("node:dns/promises");
9
- node_dns_promises = require_chunk.__toESM(node_dns_promises, 1);
10
10
  let _fedify_fixture = require("@fedify/fixture");
11
11
  let node_http = require("node:http");
12
12
  let jsonld_dist_jsonld_esm_js = require("jsonld/dist/jsonld.esm.js");
13
13
  jsonld_dist_jsonld_esm_js = require_chunk.__toESM(jsonld_dist_jsonld_esm_js, 1);
14
- //#region ../../node_modules/.pnpm/glob-to-regexp@0.4.1/node_modules/glob-to-regexp/index.js
15
- var require_glob_to_regexp = /* @__PURE__ */ require_chunk.__commonJSMin(((exports, module) => {
16
- module.exports = function(glob, opts) {
17
- if (typeof glob !== "string") throw new TypeError("Expected a string");
18
- var str = String(glob);
19
- var reStr = "";
20
- var extended = opts ? !!opts.extended : false;
21
- var globstar = opts ? !!opts.globstar : false;
22
- var inGroup = false;
23
- var flags = opts && typeof opts.flags === "string" ? opts.flags : "";
24
- var c;
25
- for (var i = 0, len = str.length; i < len; i++) {
26
- c = str[i];
27
- switch (c) {
28
- case "/":
29
- case "$":
30
- case "^":
31
- case "+":
32
- case ".":
33
- case "(":
34
- case ")":
35
- case "=":
36
- case "!":
37
- case "|":
38
- reStr += "\\" + c;
39
- break;
40
- case "?": if (extended) {
41
- reStr += ".";
42
- break;
43
- }
44
- case "[":
45
- case "]": if (extended) {
46
- reStr += c;
47
- break;
48
- }
49
- case "{": if (extended) {
50
- inGroup = true;
51
- reStr += "(";
52
- break;
53
- }
54
- case "}": if (extended) {
55
- inGroup = false;
56
- reStr += ")";
57
- break;
58
- }
59
- case ",":
60
- if (inGroup) {
61
- reStr += "|";
62
- break;
63
- }
64
- reStr += "\\" + c;
65
- break;
66
- case "*":
67
- var prevChar = str[i - 1];
68
- var starCount = 1;
69
- while (str[i + 1] === "*") {
70
- starCount++;
71
- i++;
72
- }
73
- var nextChar = str[i + 1];
74
- if (!globstar) reStr += ".*";
75
- else if (starCount > 1 && (prevChar === "/" || prevChar === void 0) && (nextChar === "/" || nextChar === void 0)) {
76
- reStr += "((?:[^/]*(?:/|$))*)";
77
- i++;
78
- } else reStr += "([^/]*)";
79
- break;
80
- default: reStr += c;
81
- }
82
- }
83
- if (!flags || !~flags.indexOf("g")) reStr = "^" + reStr + "$";
84
- return new RegExp(reStr, flags);
85
- };
86
- }));
87
- //#endregion
88
- //#region ../../node_modules/.pnpm/regexparam@3.0.0/node_modules/regexparam/dist/index.mjs
89
- var import_glob_to_regexp = /* @__PURE__ */ require_chunk.__toESM(require_glob_to_regexp(), 1);
90
- /**
91
- * @param {string|RegExp} input The route pattern
92
- * @param {boolean} [loose] Allow open-ended matching. Ignored with `RegExp` input.
93
- */
94
- function parse(input, loose) {
95
- if (input instanceof RegExp) return {
96
- keys: false,
97
- pattern: input
98
- };
99
- var c, o, tmp, ext, keys = [], pattern = "", arr = input.split("/");
100
- arr[0] || arr.shift();
101
- while (tmp = arr.shift()) {
102
- c = tmp[0];
103
- if (c === "*") {
104
- keys.push(c);
105
- pattern += tmp[1] === "?" ? "(?:/(.*))?" : "/(.*)";
106
- } else if (c === ":") {
107
- o = tmp.indexOf("?", 1);
108
- ext = tmp.indexOf(".", 1);
109
- keys.push(tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length));
110
- pattern += !!~o && !~ext ? "(?:/([^/]+?))?" : "/([^/]+?)";
111
- if (!!~ext) pattern += (!!~o ? "?" : "") + "\\" + tmp.substring(ext);
112
- } else pattern += "/" + tmp;
113
- }
114
- return {
115
- keys,
116
- pattern: new RegExp("^" + pattern + (loose ? "(?=$|/)" : "/?$"), "i")
117
- };
118
- }
119
- //#endregion
120
- //#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/TypeDescriptor.js
121
- const valueTypes = new Set([
122
- "boolean",
123
- "number",
124
- "null",
125
- "string",
126
- "undefined"
127
- ]);
128
- const referenceTypes = new Set([
129
- "array",
130
- "function",
131
- "object",
132
- "symbol"
133
- ]);
134
- const detectableTypes = new Set([
135
- "boolean",
136
- "function",
137
- "number",
138
- "string",
139
- "symbol"
140
- ]);
141
- const typeConstructors = new Set([
142
- Boolean,
143
- Number,
144
- String
145
- ]);
146
- var TypeDescriptor = class TypeDescriptor {
147
- constructor(value) {
148
- this.name = TypeDescriptor.of(value);
149
- this.isValueType = TypeDescriptor.isValueType(value);
150
- this.isReferenceType = TypeDescriptor.isReferenceType(value);
151
- this.isArray = TypeDescriptor.isArray(value);
152
- this.isBoolean = TypeDescriptor.isBoolean(value);
153
- this.isFunction = TypeDescriptor.isFunction(value);
154
- this.isNull = TypeDescriptor.isNull(value);
155
- this.isNumber = TypeDescriptor.isNumber(value);
156
- this.isObject = TypeDescriptor.isObject(value);
157
- this.isString = TypeDescriptor.isString(value);
158
- this.isSymbol = TypeDescriptor.isSymbol(value);
159
- this.isUndefined = TypeDescriptor.isUndefined(value);
160
- }
161
- static of(value) {
162
- if (value === null) return "null";
163
- if (value === void 0) return "undefined";
164
- const detectedType = typeof value;
165
- if (detectableTypes.has(detectedType)) return detectedType;
166
- if (detectedType === "object") {
167
- if (Array.isArray(value)) return "array";
168
- if (typeConstructors.has(value.constructor)) return value.constructor.name.toLowerCase();
169
- return detectedType;
170
- }
171
- throw new Error("Failed due to an unknown type.");
172
- }
173
- static from(value) {
174
- return new TypeDescriptor(value);
175
- }
176
- static isValueType(value) {
177
- return valueTypes.has(TypeDescriptor.of(value));
178
- }
179
- static isReferenceType(value) {
180
- return referenceTypes.has(TypeDescriptor.of(value));
181
- }
182
- static isArray(value) {
183
- return TypeDescriptor.of(value) === "array";
184
- }
185
- static isBoolean(value) {
186
- return TypeDescriptor.of(value) === "boolean";
187
- }
188
- static isFunction(value) {
189
- return TypeDescriptor.of(value) === "function";
190
- }
191
- static isNull(value) {
192
- return TypeDescriptor.of(value) === "null";
193
- }
194
- static isNumber(value) {
195
- return TypeDescriptor.of(value) === "number";
196
- }
197
- static isObject(value) {
198
- return TypeDescriptor.of(value) === "object";
199
- }
200
- static isString(value) {
201
- return TypeDescriptor.of(value) === "string";
202
- }
203
- static isSymbol(value) {
204
- return TypeDescriptor.of(value) === "symbol";
205
- }
206
- static isUndefined(value) {
207
- return TypeDescriptor.of(value) === "undefined";
208
- }
209
- };
210
- //#endregion
211
- //#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/IsSubsetOf.js
212
- const allowedTypes = new Set([
213
- "array",
214
- "object",
215
- "function",
216
- "null"
217
- ]);
218
- const isSubsetOf = function(subset, superset, visited = []) {
219
- const subsetType = TypeDescriptor.of(subset);
220
- const supersetType = TypeDescriptor.of(superset);
221
- if (!allowedTypes.has(subsetType)) throw new Error(`Type '${subsetType}' is not supported.`);
222
- if (!allowedTypes.has(supersetType)) throw new Error(`Type '${supersetType}' is not supported.`);
223
- if (TypeDescriptor.isFunction(subset)) {
224
- if (!TypeDescriptor.isFunction(superset)) throw new Error(`Types '${subsetType}' and '${supersetType}' do not match.`);
225
- return subset.toString() === superset.toString();
226
- }
227
- if (TypeDescriptor.isArray(subset)) {
228
- if (!TypeDescriptor.isArray(superset)) throw new Error(`Types '${subsetType}' and '${supersetType}' do not match.`);
229
- if (subset.length > superset.length) return false;
230
- for (const subsetItem of subset) {
231
- const subsetItemType = TypeDescriptor.of(subsetItem);
232
- let isItemInSuperset;
233
- switch (subsetItemType) {
234
- case "array":
235
- case "object":
236
- case "function":
237
- if (visited.includes(subsetItem)) continue;
238
- visited.push(subsetItem);
239
- isItemInSuperset = superset.some((supersetItem) => {
240
- try {
241
- return isSubsetOf(subsetItem, supersetItem, visited);
242
- } catch {
243
- return false;
244
- }
245
- });
246
- break;
247
- default: isItemInSuperset = superset.includes(subsetItem);
248
- }
249
- if (!isItemInSuperset) return false;
250
- }
251
- return true;
252
- }
253
- if (TypeDescriptor.isObject(subset)) {
254
- if (!TypeDescriptor.isObject(superset) || TypeDescriptor.isArray(superset)) throw new Error(`Types '${subsetType}' and '${supersetType}' do not match.`);
255
- if (Object.keys(subset).length > Object.keys(superset).length) return false;
256
- for (const [subsetKey, subsetValue] of Object.entries(subset)) {
257
- const supersetValue = superset[subsetKey];
258
- switch (TypeDescriptor.of(subsetValue)) {
259
- case "array":
260
- case "object":
261
- case "function":
262
- if (visited.includes(subsetValue)) continue;
263
- visited.push(subsetValue);
264
- try {
265
- if (!isSubsetOf(subsetValue, supersetValue, visited)) return false;
266
- } catch {
267
- return false;
268
- }
269
- break;
270
- default: if (subsetValue !== supersetValue) return false;
271
- }
272
- }
273
- return true;
274
- }
275
- if (TypeDescriptor.isNull(subset)) {
276
- if (!TypeDescriptor.isNull(superset)) throw new Error(`Types '${subsetType}' and '${supersetType}' do not match.`);
277
- return true;
278
- }
279
- throw new Error("Invalid operation.");
280
- };
281
- isSubsetOf.structural = function(subset, superset, visited = []) {
282
- if (!TypeDescriptor.isObject(subset)) throw new Error(`Type '${TypeDescriptor.of(subset)}' is not supported.`);
283
- if (!TypeDescriptor.isObject(superset)) throw new Error(`Type '${TypeDescriptor.of(superset)}' is not supported.`);
284
- for (const [subsetKey, subsetValue] of Object.entries(subset)) {
285
- if (superset[subsetKey] === void 0) return false;
286
- const subsetValueType = TypeDescriptor.of(subsetValue);
287
- const supersetValue = superset[subsetKey];
288
- if (subsetValueType === "object") {
289
- if (visited.includes(subsetValue)) continue;
290
- visited.push(subsetValue);
291
- try {
292
- if (!isSubsetOf.structural(subsetValue, supersetValue, visited)) return false;
293
- } catch {
294
- return false;
295
- }
296
- }
297
- }
298
- return true;
299
- };
300
- //#endregion
301
- //#region ../../node_modules/.pnpm/dequal@2.0.3/node_modules/dequal/dist/index.mjs
302
- var has = Object.prototype.hasOwnProperty;
303
- function find(iter, tar, key) {
304
- for (key of iter.keys()) if (dequal(key, tar)) return key;
305
- }
306
- function dequal(foo, bar) {
307
- var ctor, len, tmp;
308
- if (foo === bar) return true;
309
- if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
310
- if (ctor === Date) return foo.getTime() === bar.getTime();
311
- if (ctor === RegExp) return foo.toString() === bar.toString();
312
- if (ctor === Array) {
313
- if ((len = foo.length) === bar.length) while (len-- && dequal(foo[len], bar[len]));
314
- return len === -1;
315
- }
316
- if (ctor === Set) {
317
- if (foo.size !== bar.size) return false;
318
- for (len of foo) {
319
- tmp = len;
320
- if (tmp && typeof tmp === "object") {
321
- tmp = find(bar, tmp);
322
- if (!tmp) return false;
323
- }
324
- if (!bar.has(tmp)) return false;
325
- }
326
- return true;
327
- }
328
- if (ctor === Map) {
329
- if (foo.size !== bar.size) return false;
330
- for (len of foo) {
331
- tmp = len[0];
332
- if (tmp && typeof tmp === "object") {
333
- tmp = find(bar, tmp);
334
- if (!tmp) return false;
335
- }
336
- if (!dequal(len[1], bar.get(tmp))) return false;
337
- }
338
- return true;
339
- }
340
- if (ctor === ArrayBuffer) {
341
- foo = new Uint8Array(foo);
342
- bar = new Uint8Array(bar);
343
- } else if (ctor === DataView) {
344
- if ((len = foo.byteLength) === bar.byteLength) while (len-- && foo.getInt8(len) === bar.getInt8(len));
345
- return len === -1;
346
- }
347
- if (ArrayBuffer.isView(foo)) {
348
- if ((len = foo.byteLength) === bar.byteLength) while (len-- && foo[len] === bar[len]);
349
- return len === -1;
350
- }
351
- if (!ctor || typeof foo === "object") {
352
- len = 0;
353
- for (ctor in foo) {
354
- if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
355
- if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;
356
- }
357
- return Object.keys(bar).length === len;
358
- }
359
- }
360
- return foo !== foo && bar !== bar;
361
- }
362
- //#endregion
363
- //#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/RequestUtils.js
364
- const absoluteUrlRX = /* @__PURE__ */ new RegExp("^[a-z]+://|^data:", "i");
365
- const protocolRelativeUrlRX = /* @__PURE__ */ new RegExp("^//", "i");
366
- function hasCredentialsInUrl(url) {
367
- const urlObject = new URL(url, !absoluteUrlRX.test(url) ? "http://dummy" : void 0);
368
- return Boolean(urlObject.username || urlObject.password);
369
- }
370
- function normalizeUrl(url, allowRelativeUrls) {
371
- if (url instanceof URL) return url.href;
372
- const primitiveUrl = String(url).valueOf();
373
- if (absoluteUrlRX.test(primitiveUrl)) return new URL(primitiveUrl).href;
374
- if (protocolRelativeUrlRX.test(primitiveUrl)) return new URL(primitiveUrl, "http://dummy").href.replace(/^[a-z]+:/, "");
375
- if ("location" in globalThis) if (primitiveUrl.startsWith("/")) return `${globalThis.location.origin}${primitiveUrl}`;
376
- else return `${globalThis.location.href}/${primitiveUrl}`;
377
- else if (allowRelativeUrls) {
378
- const urlInstance = new URL(primitiveUrl, "http://dummy");
379
- return urlInstance.pathname + urlInstance.search;
380
- } else throw new Error("Relative urls are not support by default in node.js tests. Either use a utility such as jsdom to define globalThis.location or set `fetchMock.config.allowRelativeUrls = true`");
381
- }
382
- function createCallLogFromUrlAndOptions(url, options) {
383
- const pendingPromises = [];
384
- if (typeof url === "string" || url instanceof String || url instanceof URL) {
385
- const normalizedUrl = normalizeUrl(url, true);
386
- const derivedOptions = options ? { ...options } : {};
387
- if (derivedOptions.headers) derivedOptions.headers = normalizeHeaders(derivedOptions.headers);
388
- derivedOptions.method = derivedOptions.method ? derivedOptions.method.toLowerCase() : "get";
389
- return {
390
- args: [url, options],
391
- url: normalizedUrl,
392
- queryParams: new URLSearchParams(getQuery(normalizedUrl)),
393
- options: derivedOptions,
394
- signal: derivedOptions.signal,
395
- pendingPromises
396
- };
397
- }
398
- if (typeof url === "object") throw new TypeError("fetch-mock: Unrecognised Request object. Read the Config and Installation sections of the docs");
399
- else throw new TypeError("fetch-mock: Invalid arguments passed to fetch");
400
- }
401
- async function createCallLogFromRequest(request, options) {
402
- const pendingPromises = [];
403
- const derivedOptions = { method: request.method };
404
- try {
405
- try {
406
- derivedOptions.body = await request.clone().formData();
407
- } catch {
408
- derivedOptions.body = await request.clone().text();
409
- }
410
- } catch {}
411
- if (request.headers) derivedOptions.headers = normalizeHeaders(request.headers);
412
- const url = normalizeUrl(request.url, true);
413
- return {
414
- args: [request, options],
415
- url,
416
- queryParams: new URLSearchParams(getQuery(url)),
417
- options: Object.assign(derivedOptions, options || {}),
418
- request,
419
- signal: options && options.signal || request.signal,
420
- pendingPromises
421
- };
422
- }
423
- function getPath(url) {
424
- return (absoluteUrlRX.test(url) ? new URL(url) : new URL(url, "http://dummy")).pathname;
425
- }
426
- function getHost(url) {
427
- if (absoluteUrlRX.test(url)) return new URL(url).host;
428
- else if ("location" in globalThis) return globalThis.location.host;
429
- return null;
430
- }
431
- function getQuery(url) {
432
- const u = absoluteUrlRX.test(url) ? new URL(url) : new URL(url, "http://dummy");
433
- return u.search ? u.search.substr(1) : "";
434
- }
435
- function normalizeHeaders(headers) {
436
- let entries;
437
- if (headers instanceof Headers) entries = [...headers.entries()];
438
- else if (Array.isArray(headers)) entries = headers;
439
- else entries = Object.entries(headers);
440
- return Object.fromEntries(entries.map(([key, val]) => [key.toLowerCase(), String(val).valueOf()]));
441
- }
442
- //#endregion
443
- //#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/Matchers.js
444
- const isUrlMatcher = (matcher) => matcher instanceof RegExp || typeof matcher === "string" || typeof matcher === "object" && "href" in matcher;
445
- const isFunctionMatcher = (matcher) => typeof matcher === "function";
446
- const stringMatchers = {
447
- begin: (targetString) => ({ url }) => url.startsWith(targetString),
448
- end: (targetString) => ({ url }) => url.endsWith(targetString),
449
- include: (targetString) => ({ url }) => url.includes(targetString),
450
- glob: (targetString) => {
451
- const urlRX = (0, import_glob_to_regexp.default)(targetString);
452
- return ({ url }) => urlRX.test(url);
453
- },
454
- express: (targetString) => {
455
- const urlRX = parse(targetString);
456
- return (callLog) => {
457
- const vals = urlRX.pattern.exec(getPath(callLog.url));
458
- if (!vals) {
459
- callLog.expressParams = {};
460
- return false;
461
- }
462
- vals.shift();
463
- callLog.expressParams = urlRX.keys.reduce((map, paramName, i) => vals[i] ? Object.assign(map, { [paramName]: vals[i] }) : map, {});
464
- return true;
465
- };
466
- },
467
- path: (targetString) => {
468
- const dotlessTargetString = getPath(targetString);
469
- return ({ url }) => {
470
- const path = getPath(url);
471
- return path === targetString || path === dotlessTargetString;
472
- };
473
- },
474
- host: (targetString) => {
475
- return ({ url }) => targetString === getHost(url);
476
- }
477
- };
478
- const getHeaderMatcher = ({ headers: expectedHeaders }) => {
479
- if (!expectedHeaders) return;
480
- const expectation = normalizeHeaders(expectedHeaders);
481
- return ({ options: { headers = {} } }) => {
482
- const lowerCaseHeaders = normalizeHeaders(headers);
483
- return Object.keys(expectation).every((headerName) => lowerCaseHeaders[headerName] === expectation[headerName]);
484
- };
485
- };
486
- const getMissingHeaderMatcher = ({ missingHeaders: expectedMissingHeaders }) => {
487
- if (!expectedMissingHeaders) return;
488
- const expectation = expectedMissingHeaders.map((header) => header.toLowerCase());
489
- return ({ options: { headers = {} } }) => {
490
- const lowerCaseHeaders = normalizeHeaders(headers);
491
- return expectation.every((headerName) => !(headerName in lowerCaseHeaders));
492
- };
493
- };
494
- const getMethodMatcher = ({ method: expectedMethod }) => {
495
- if (!expectedMethod) return;
496
- return ({ options: { method } = {} }) => {
497
- return expectedMethod === (method ? method.toLowerCase() : "get");
498
- };
499
- };
500
- const getQueryParamsMatcher = ({ query: passedQuery }) => {
501
- if (!passedQuery) return;
502
- const expectedQuery = new URLSearchParams();
503
- for (const [key, value] of Object.entries(passedQuery)) if (Array.isArray(value)) for (const item of value) expectedQuery.append(key, typeof item === "object" || typeof item === "undefined" ? "" : item.toString());
504
- else expectedQuery.append(key, typeof value === "object" || typeof value === "undefined" ? "" : value.toString());
505
- const keys = Array.from(expectedQuery.keys());
506
- return ({ queryParams }) => {
507
- return keys.every((key) => {
508
- const expectedValues = expectedQuery.getAll(key).sort();
509
- const actualValues = queryParams.getAll(key).sort();
510
- if (expectedValues.length !== actualValues.length) return false;
511
- if (Array.isArray(passedQuery[key])) return expectedValues.every((expected, index) => expected === actualValues[index]);
512
- return dequal(actualValues, expectedValues);
513
- });
514
- };
515
- };
516
- const getExpressParamsMatcher = ({ params: expectedParams, url }) => {
517
- if (!expectedParams) return;
518
- if (!(typeof url === "string" && /express:/.test(url))) throw new Error("fetch-mock: matching on params is only possible when using an express: matcher");
519
- const expectedKeys = Object.keys(expectedParams);
520
- return ({ expressParams = {} }) => {
521
- return expectedKeys.every((key) => expressParams[key] === expectedParams[key]);
522
- };
523
- };
524
- const formDataToObject = (formData) => {
525
- const fields = [...formData];
526
- const result = {};
527
- fields.forEach(([key, value]) => {
528
- result[key] = result[key] || [];
529
- result[key].push(value);
530
- });
531
- return result;
532
- };
533
- const getBodyMatcher = (route) => {
534
- let { body: expectedBody } = route;
535
- let expectedBodyType = "json";
536
- if (!expectedBody) return;
537
- if (expectedBody instanceof FormData) {
538
- expectedBodyType = "formData";
539
- expectedBody = formDataToObject(expectedBody);
540
- }
541
- return ({ options: { body, method = "get" } }) => {
542
- if (["get", "head"].includes(method.toLowerCase())) return false;
543
- let sentBody;
544
- try {
545
- if (typeof body === "string") {
546
- sentBody = JSON.parse(body);
547
- if (expectedBodyType !== "json") return false;
548
- }
549
- } catch {}
550
- if (body instanceof FormData) {
551
- if (expectedBodyType !== "formData") return false;
552
- sentBody = formDataToObject(body);
553
- }
554
- return sentBody && (route.matchPartialBody ? isSubsetOf(expectedBody, sentBody) : dequal(expectedBody, sentBody));
555
- };
556
- };
557
- const getFunctionMatcher = ({ matcherFunction }) => matcherFunction;
558
- const getRegexpMatcher = (regexp) => ({ url }) => regexp.test(url);
559
- const getFullUrlMatcher = (route, matcherUrl, query) => {
560
- const expectedUrl = normalizeUrl(matcherUrl, route.allowRelativeUrls);
561
- if (route.url === matcherUrl) route.url = expectedUrl;
562
- return ({ url }) => {
563
- if (query && expectedUrl.indexOf("?")) return getPath(url) === getPath(expectedUrl);
564
- return normalizeUrl(url, true) === expectedUrl;
565
- };
566
- };
567
- const getUrlMatcher = (route) => {
568
- const { url: matcherUrl, query } = route;
569
- if (matcherUrl === "*") return () => true;
570
- if (matcherUrl instanceof RegExp) return getRegexpMatcher(matcherUrl);
571
- if (matcherUrl instanceof URL) {
572
- if (matcherUrl.href) return getFullUrlMatcher(route, matcherUrl.href, query);
573
- }
574
- if (typeof matcherUrl === "string") {
575
- for (const shorthand in stringMatchers) if (matcherUrl.indexOf(`${shorthand}:`) === 0) {
576
- const urlFragment = matcherUrl.replace(new RegExp(`^${shorthand}:`), "");
577
- return stringMatchers[shorthand](urlFragment);
578
- }
579
- return getFullUrlMatcher(route, matcherUrl, query);
580
- }
581
- if (typeof matcherUrl === "object") {
582
- const matchers = Object.entries(matcherUrl).map(([key, pattern]) => {
583
- if (key === "regexp") return getRegexpMatcher(pattern);
584
- else if (key in stringMatchers) return stringMatchers[key](pattern);
585
- else throw new Error(`unrecognised url matching pattern: ${key}`);
586
- });
587
- return (route) => matchers.every((matcher) => matcher(route));
588
- }
589
- };
590
- const builtInMatchers = [
591
- {
592
- name: "url",
593
- matcher: getUrlMatcher
594
- },
595
- {
596
- name: "query",
597
- matcher: getQueryParamsMatcher
598
- },
599
- {
600
- name: "method",
601
- matcher: getMethodMatcher
602
- },
603
- {
604
- name: "headers",
605
- matcher: getHeaderMatcher
606
- },
607
- {
608
- name: "missingHeaders",
609
- matcher: getMissingHeaderMatcher
610
- },
611
- {
612
- name: "params",
613
- matcher: getExpressParamsMatcher
614
- },
615
- {
616
- name: "body",
617
- matcher: getBodyMatcher,
618
- usesBody: true
619
- },
620
- {
621
- name: "matcherFunction",
622
- matcher: getFunctionMatcher
623
- }
624
- ];
625
- //#endregion
626
- //#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/StatusTextMap.js
627
- const statusTextMap = {
628
- 100: "Continue",
629
- 101: "Switching Protocols",
630
- 102: "Processing",
631
- 200: "OK",
632
- 201: "Created",
633
- 202: "Accepted",
634
- 203: "Non-Authoritative Information",
635
- 204: "No Content",
636
- 205: "Reset Content",
637
- 206: "Partial Content",
638
- 207: "Multi-Status",
639
- 208: "Already Reported",
640
- 226: "IM Used",
641
- 300: "Multiple Choices",
642
- 301: "Moved Permanently",
643
- 302: "Found",
644
- 303: "See Other",
645
- 304: "Not Modified",
646
- 305: "Use Proxy",
647
- 307: "Temporary Redirect",
648
- 308: "Permanent Redirect",
649
- 400: "Bad Request",
650
- 401: "Unauthorized",
651
- 402: "Payment Required",
652
- 403: "Forbidden",
653
- 404: "Not Found",
654
- 405: "Method Not Allowed",
655
- 406: "Not Acceptable",
656
- 407: "Proxy Authentication Required",
657
- 408: "Request Timeout",
658
- 409: "Conflict",
659
- 410: "Gone",
660
- 411: "Length Required",
661
- 412: "Precondition Failed",
662
- 413: "Payload Too Large",
663
- 414: "URI Too Long",
664
- 415: "Unsupported Media Type",
665
- 416: "Range Not Satisfiable",
666
- 417: "Expectation Failed",
667
- 418: "I'm a teapot",
668
- 421: "Misdirected Request",
669
- 422: "Unprocessable Entity",
670
- 423: "Locked",
671
- 424: "Failed Dependency",
672
- 425: "Unordered Collection",
673
- 426: "Upgrade Required",
674
- 428: "Precondition Required",
675
- 429: "Too Many Requests",
676
- 431: "Request Header Fields Too Large",
677
- 451: "Unavailable For Legal Reasons",
678
- 500: "Internal Server Error",
679
- 501: "Not Implemented",
680
- 502: "Bad Gateway",
681
- 503: "Service Unavailable",
682
- 504: "Gateway Timeout",
683
- 505: "HTTP Version Not Supported",
684
- 506: "Variant Also Negotiates",
685
- 507: "Insufficient Storage",
686
- 508: "Loop Detected",
687
- 509: "Bandwidth Limit Exceeded",
688
- 510: "Not Extended",
689
- 511: "Network Authentication Required"
690
- };
14
+ //#region src/jsonld.ts
15
+ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
691
16
  //#endregion
692
- //#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/Route.js
693
- var __classPrivateFieldSet = function(receiver, state, value, kind, f) {
694
- if (kind === "m") throw new TypeError("Private method is not writable");
695
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
696
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
697
- return kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value), value;
698
- };
699
- var __classPrivateFieldGet = function(receiver, state, kind, f) {
700
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
701
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
702
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
703
- };
704
- var _Route_instances, _a, _Route_responseSubscriptions, _Route_validate, _Route_sanitize, _Route_generateMatcher, _Route_limit, _Route_delayResponse;
705
- var RouteConfigWrapper = class {
706
- constructor(config) {
707
- Object.assign(this, config);
708
- }
709
- };
710
- function isBodyInit(body) {
711
- return body instanceof Blob || body instanceof ArrayBuffer || ArrayBuffer.isView(body) || body instanceof DataView || body instanceof FormData || body instanceof ReadableStream || body instanceof URLSearchParams || body instanceof String || typeof body === "string" || body === null;
712
- }
713
- function sanitizeStatus(status) {
714
- if (status === 0) return 200;
715
- if (!status) return 200;
716
- if (typeof status === "number" && parseInt(String(status), 10) !== status && status >= 200 || status < 600) return status;
717
- throw new TypeError(`fetch-mock: Invalid status ${status} passed on response object.
718
- To respond with a JSON object that has status as a property assign the object to body
719
- e.g. {"body": {"status: "registered"}}`);
720
- }
721
- var Route = class {
722
- constructor(config) {
723
- _Route_instances.add(this);
724
- _Route_responseSubscriptions.set(this, void 0);
725
- this.init(config);
726
- }
727
- init(config) {
728
- this.config = config;
729
- __classPrivateFieldSet(this, _Route_responseSubscriptions, [], "f");
730
- __classPrivateFieldGet(this, _Route_instances, "m", _Route_sanitize).call(this);
731
- __classPrivateFieldGet(this, _Route_instances, "m", _Route_validate).call(this);
732
- __classPrivateFieldGet(this, _Route_instances, "m", _Route_generateMatcher).call(this);
733
- __classPrivateFieldGet(this, _Route_instances, "m", _Route_limit).call(this);
734
- __classPrivateFieldGet(this, _Route_instances, "m", _Route_delayResponse).call(this);
735
- }
736
- reset() {}
737
- waitFor(awaitedRoutes) {
738
- const { response } = this.config;
739
- this.config.response = Promise.all(awaitedRoutes.map((awaitedRoute) => new Promise((res) => awaitedRoute.onRespond(() => {
740
- res(void 0);
741
- })))).then(() => response);
742
- }
743
- onRespond(func) {
744
- __classPrivateFieldGet(this, _Route_responseSubscriptions, "f").push(func);
745
- }
746
- constructResponse(responseInput) {
747
- const responseOptions = this.constructResponseOptions(responseInput);
748
- const body = this.constructResponseBody(responseInput, responseOptions);
749
- const responsePackage = {
750
- response: new this.config.Response(body, responseOptions),
751
- responseOptions,
752
- responseInput
753
- };
754
- __classPrivateFieldGet(this, _Route_responseSubscriptions, "f").forEach((func) => func());
755
- return responsePackage;
756
- }
757
- constructResponseOptions(responseInput) {
758
- const options = responseInput.options || {};
759
- options.status = sanitizeStatus(responseInput.status);
760
- options.statusText = statusTextMap[options.status];
761
- options.headers = new this.config.Headers(responseInput.headers);
762
- return options;
763
- }
764
- constructResponseBody(responseInput, responseOptions) {
765
- let body = responseInput.body;
766
- if (!isBodyInit(body)) if (typeof body === "undefined") body = null;
767
- else if (typeof body === "object") {
768
- body = JSON.stringify(body);
769
- if (!responseOptions.headers.has("Content-Type")) responseOptions.headers.set("Content-Type", "application/json");
770
- } else throw new TypeError("Invalid body provided to construct response");
771
- if (this.config.includeContentLength && !responseOptions.headers.has("Content-Length") && !(body instanceof ReadableStream) && !(body instanceof FormData)) {
772
- let length = 0;
773
- if (body instanceof Blob) length = body.size;
774
- else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body) || body instanceof DataView) length = body.byteLength;
775
- else if (body instanceof URLSearchParams) length = body.toString().length;
776
- else if (typeof body === "string" || body instanceof String) length = body.length;
777
- responseOptions.headers.set("Content-Length", length.toString());
778
- }
779
- return body;
780
- }
781
- static defineMatcher(matcher) {
782
- _a.registeredMatchers.push(matcher);
783
- }
17
+ //#region src/docloader.test.ts
18
+ const unpinnedPublicFetch = require_public_fetch.createPublicFetch({
19
+ pinner: () => Promise.resolve(null),
20
+ lookup: () => Promise.reject(/* @__PURE__ */ new Error("Unexpected default lookup"))
21
+ });
22
+ const PUBLIC_ADDRESS = {
23
+ address: "93.184.215.14",
24
+ family: 4
784
25
  };
785
- _a = Route, _Route_responseSubscriptions = /* @__PURE__ */ new WeakMap(), _Route_instances = /* @__PURE__ */ new WeakSet(), _Route_validate = function _Route_validate() {
786
- if (["matched", "unmatched"].includes(this.config.name)) throw new Error(`fetch-mock: Routes cannot use the reserved name \`${this.config.name}\``);
787
- if (!("response" in this.config)) throw new Error("fetch-mock: Each route must define a response");
788
- if (!_a.registeredMatchers.some(({ name }) => name in this.config)) throw new Error("fetch-mock: Each route must specify some criteria for matching calls to fetch. To match all calls use '*'");
789
- }, _Route_sanitize = function _Route_sanitize() {
790
- if (this.config.method) this.config.method = this.config.method.toLowerCase();
791
- }, _Route_generateMatcher = function _Route_generateMatcher() {
792
- const activeMatchers = _a.registeredMatchers.filter(({ name }) => name in this.config).map(({ matcher, usesBody }) => ({
793
- matcher: matcher(this.config),
794
- usesBody
795
- }));
796
- this.config.usesBody = activeMatchers.some(({ usesBody }) => usesBody);
797
- this.matcher = (normalizedRequest) => activeMatchers.every(({ matcher }) => matcher(normalizedRequest));
798
- }, _Route_limit = function _Route_limit() {
799
- if (!this.config.repeat) return;
800
- const originalMatcher = this.matcher;
801
- let timesLeft = this.config.repeat;
802
- this.matcher = (callLog) => {
803
- if (timesLeft && originalMatcher(callLog)) {
804
- timesLeft--;
805
- return true;
806
- }
807
- };
808
- this.reset = () => {
809
- timesLeft = this.config.repeat;
810
- };
811
- }, _Route_delayResponse = function _Route_delayResponse() {
812
- if (this.config.delay) {
813
- const { response } = this.config;
814
- this.config.response = () => {
815
- return new Promise((res) => setTimeout(() => res(response), this.config.delay));
816
- };
817
- }
26
+ const PRIVATE_ADDRESS = {
27
+ address: "10.0.0.1",
28
+ family: 4
818
29
  };
819
- Route.registeredMatchers = [];
820
- builtInMatchers.forEach(Route.defineMatcher);
821
- //#endregion
822
- //#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/Router.js
823
- const responseConfigProps = [
824
- "body",
825
- "headers",
826
- "throws",
827
- "status",
828
- "redirectUrl"
829
- ];
830
- function nameToOptions(options) {
831
- return typeof options === "string" ? { name: options } : options;
832
- }
833
- function isPromise(response) {
834
- return typeof response.then === "function";
835
- }
836
- function normalizeResponseInput(responseInput) {
837
- if (typeof responseInput === "number") return { status: responseInput };
838
- else if (typeof responseInput === "string" || shouldSendAsObject(responseInput)) return { body: responseInput };
839
- return responseInput;
840
- }
841
- function shouldSendAsObject(responseInput) {
842
- if (responseConfigProps.some((prop) => prop in responseInput)) {
843
- if (Object.keys(responseInput).every((key) => responseConfigProps.includes(key))) return false;
844
- return true;
845
- }
846
- return true;
30
+ function lookupTestAddresses(hostname) {
31
+ return Promise.resolve([hostname === "private.example" ? PRIVATE_ADDRESS : PUBLIC_ADDRESS]);
847
32
  }
848
- function throwSpecExceptions({ url, options: { headers, method, body } }) {
849
- if (headers) Object.entries(headers).forEach(([key]) => {
850
- if (/\s/.test(key)) throw new TypeError("Invalid name");
851
- });
852
- if (hasCredentialsInUrl(url)) throw new TypeError(`Request cannot be constructed from a URL that includes credentials: ${url}`);
853
- if (["get", "head"].includes(method) && body) throw new TypeError("Request with GET/HEAD method cannot have body.");
33
+ function openUnpinnedHop(url) {
34
+ return unpinnedPublicFetch.openPublicFetch(url, { lookup: lookupTestAddresses });
854
35
  }
855
- const resolveUntilResponseConfig = async (callLog) => {
856
- let response = callLog.route.config.response;
857
- while (true) if (typeof response === "function") response = response(callLog);
858
- else if (isPromise(response)) response = await response;
859
- else return response;
860
- };
861
- var Router = class {
862
- constructor(fetchMockConfig, { routes, fallbackRoute } = {}) {
863
- this.config = fetchMockConfig;
864
- this.routes = routes || [];
865
- this.fallbackRoute = fallbackRoute;
866
- }
867
- needsToReadBody(request) {
868
- return Boolean(request && this.routes.some((route) => route.config.usesBody));
869
- }
870
- execute(callLog) {
871
- throwSpecExceptions(callLog);
872
- return new Promise(async (resolve, reject) => {
873
- const { url, options, request, pendingPromises } = callLog;
874
- let eventListenerAbortController;
875
- if (callLog.signal) {
876
- const abort = () => {
877
- const error = new DOMException("The operation was aborted.", "AbortError");
878
- const requestBody = request?.body || options?.body;
879
- if (requestBody instanceof ReadableStream) if (requestBody.locked) console.warn("fetch-mock: Locked request body can't be cancelled");
880
- else requestBody.cancel(error);
881
- if (callLog?.response?.body) if (callLog.response.body.locked) console.warn("fetch-mock: Locked response body can't be cancelled");
882
- else callLog.response.body.cancel(error);
883
- reject(error);
884
- };
885
- if (callLog.signal.aborted) abort();
886
- eventListenerAbortController = new AbortController();
887
- callLog.signal.addEventListener("abort", abort, {
888
- once: true,
889
- signal: eventListenerAbortController.signal
890
- });
891
- }
892
- if (this.needsToReadBody(request)) options.body = await options.body;
893
- const route = (this.fallbackRoute ? [...this.routes, this.fallbackRoute] : this.routes).find((route) => route.matcher(callLog));
894
- if (route) try {
895
- callLog.route = route;
896
- const { response, responseOptions, responseInput } = await this.generateResponse(callLog);
897
- const observableResponse = this.createObservableResponse(response, responseOptions, responseInput, url, pendingPromises);
898
- callLog.response = response;
899
- resolve(observableResponse);
900
- } catch (err) {
901
- reject(err);
902
- } finally {
903
- eventListenerAbortController?.abort();
904
- }
905
- else reject(/* @__PURE__ */ new Error(`fetch-mock: No response or fallback rule to cover ${options && options.method || "GET"} to ${url}`));
906
- });
907
- }
908
- async generateResponse(callLog) {
909
- const responseInput = await resolveUntilResponseConfig(callLog);
910
- if (responseInput instanceof Response) return {
911
- response: responseInput.clone(),
912
- responseOptions: {},
913
- responseInput: {}
914
- };
915
- const responseConfig = normalizeResponseInput(responseInput);
916
- if (responseConfig.throws) throw responseConfig.throws;
917
- return callLog.route.constructResponse(responseConfig);
918
- }
919
- createObservableResponse(response, responseConfig, responseInput, responseUrl, pendingPromises) {
920
- return new Proxy(response, { get: (originalResponse, name) => {
921
- if (responseInput.redirectUrl) {
922
- if (name === "url") return responseInput.redirectUrl;
923
- if (name === "redirected") return true;
924
- } else {
925
- if (name === "url") return responseUrl;
926
- if (name === "redirected") return false;
927
- }
928
- if (responseInput.status === 0) {
929
- if (name === "status") return 0;
930
- if (name === "statusText") return "";
931
- }
932
- if (typeof response[name] === "function") return new Proxy(response[name], { apply: (func, thisArg, args) => {
933
- const result = func.apply(response, args);
934
- if (result.then) pendingPromises.push(result.catch(() => void 0));
935
- return result;
936
- } });
937
- return originalResponse[name];
938
- } });
939
- }
940
- addRoute(matcher, response, nameOrOptions) {
941
- const config = {};
942
- if (matcher instanceof RouteConfigWrapper) Object.assign(config, matcher);
943
- if (isUrlMatcher(matcher)) config.url = matcher;
944
- else if (isFunctionMatcher(matcher)) config.matcherFunction = matcher;
945
- else Object.assign(config, matcher);
946
- if (typeof response !== "undefined") config.response = response;
947
- if (nameOrOptions) Object.assign(config, typeof nameOrOptions === "string" ? nameToOptions(nameOrOptions) : nameOrOptions);
948
- const route = new Route({
949
- ...this.config,
950
- ...config
951
- });
952
- if (route.config.name && this.routes.some(({ config: { name: existingName } }) => route.config.name === existingName)) throw new Error("fetch-mock: Adding route with same name as existing route.");
953
- if (route.config.waitFor) {
954
- const routeNamesToWaitFor = Array.isArray(route.config.waitFor) ? route.config.waitFor : [route.config.waitFor];
955
- const routesToAwait = [];
956
- routeNamesToWaitFor.forEach((routeName) => {
957
- const routeToAwait = this.routes.find(({ config: { name: existingName } }) => routeName === existingName);
958
- if (routeToAwait) routesToAwait.push(routeToAwait);
959
- else throw new Error(`Cannot wait for route \`${routeName}\`: route of that name does not exist`);
960
- });
961
- route.waitFor(routesToAwait);
962
- }
963
- this.routes.push(route);
964
- }
965
- setFallback(response) {
966
- if (this.fallbackRoute) console.warn("calling fetchMock.catch() twice - are you sure you want to overwrite the previous fallback response");
967
- this.fallbackRoute = new Route({
968
- matcherFunction: () => true,
969
- response: response || "ok",
970
- ...this.config
971
- });
972
- this.fallbackRoute.config.isFallback = true;
973
- }
974
- removeRoutes({ names, includeSticky, includeFallback } = {}) {
975
- includeFallback = includeFallback ?? true;
976
- this.routes = this.routes.filter(({ config: { sticky, name } }) => {
977
- if (sticky && !includeSticky) return true;
978
- if (!names) return false;
979
- return !names.includes(name);
980
- });
981
- if (includeFallback) delete this.fallbackRoute;
982
- }
983
- modifyRoute(routeName, options) {
984
- const route = this.routes.find(({ config: { name } }) => name === routeName);
985
- if (!route) throw new Error(`Cannot call modifyRoute() on route \`${routeName}\`: route of that name not found`);
986
- if (route.config.sticky) throw new Error(`Cannot call modifyRoute() on route \`${routeName}\`: route is sticky and cannot be modified`);
987
- if ("name" in options) throw new Error(`Cannot rename the route \`${routeName}\` as \`${options.name}\`: renaming routes is not supported`);
988
- if ("sticky" in options) throw new Error(`Altering the stickiness of route \`${routeName}\` is not supported`);
989
- const newConfig = {
990
- ...route.config,
991
- ...options
992
- };
993
- Object.entries(options).forEach(([key, value]) => {
994
- if (value === null) delete newConfig[key];
995
- });
996
- route.init(newConfig);
997
- }
998
- };
999
- //#endregion
1000
- //#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/CallHistory.js
1001
- const isName = (filter) => typeof filter === "string" && /^[\da-zA-Z-]+$/.test(filter) && !["matched", "unmatched"].includes(filter);
1002
- const isMatchedOrUnmatched = (filter) => typeof filter === "boolean" || ["matched", "unmatched"].includes(filter);
1003
- var CallHistory = class {
1004
- constructor(config, router) {
1005
- this.callLogs = [];
1006
- this.config = config;
1007
- this.router = router;
1008
- }
1009
- recordCall(callLog) {
1010
- this.callLogs.push(callLog);
1011
- }
1012
- clear() {
1013
- this.callLogs.forEach(({ route }) => {
1014
- if (route) route.reset();
1015
- });
1016
- this.callLogs = [];
1017
- }
1018
- async flush(waitForResponseMethods) {
1019
- const queuedPromises = this.callLogs.flatMap((call) => call.pendingPromises);
1020
- await Promise.allSettled(queuedPromises);
1021
- if (waitForResponseMethods) {
1022
- await Promise.resolve();
1023
- await this.flush();
1024
- }
1025
- }
1026
- calls(filter, options) {
1027
- let calls = [...this.callLogs];
1028
- if (typeof filter === "undefined" && !options) return calls;
1029
- if (isMatchedOrUnmatched(filter)) {
1030
- if ([true, "matched"].includes(filter)) calls = calls.filter(({ route }) => !route?.config || !route.config.isFallback);
1031
- else if ([false, "unmatched"].includes(filter)) calls = calls.filter(({ route }) => Boolean(route?.config && route.config.isFallback));
1032
- if (!options) return calls;
1033
- } else if (isName(filter)) {
1034
- calls = calls.filter(({ route }) => route?.config?.name === filter);
1035
- if (!options) return calls;
1036
- } else if (isUrlMatcher(filter)) options = {
1037
- url: filter,
1038
- ...options || {}
1039
- };
1040
- else options = {
1041
- ...filter,
1042
- ...options || {}
1043
- };
1044
- const { matcher } = new Route({
1045
- response: "ok",
1046
- ...options
1047
- });
1048
- calls = calls.filter(({ url, options }) => {
1049
- return matcher(createCallLogFromUrlAndOptions(url, options));
1050
- });
1051
- return calls;
1052
- }
1053
- called(filter, options) {
1054
- return Boolean(this.calls(filter, options).length);
1055
- }
1056
- lastCall(filter, options) {
1057
- return this.calls(filter, options).pop();
1058
- }
1059
- done(routeNames) {
1060
- let routesToCheck = this.router.routes;
1061
- if (routeNames) {
1062
- routeNames = Array.isArray(routeNames) ? routeNames : [routeNames];
1063
- routesToCheck = this.router.routes.filter(({ config: { name } }) => routeNames.includes(name));
1064
- }
1065
- return routesToCheck.map((route) => {
1066
- const calls = this.callLogs.filter(({ route: routeApplied }) => routeApplied === route);
1067
- if (!calls.length) {
1068
- console.warn(`Warning: ${route.config.name} not called`);
1069
- return false;
1070
- }
1071
- const expectedTimes = route.config.repeat;
1072
- if (!expectedTimes) return true;
1073
- const actualTimes = calls.length;
1074
- if (expectedTimes > actualTimes) {
1075
- console.warn(`Warning: ${route.config.name} only called ${actualTimes} times, but ${expectedTimes} expected`);
1076
- return false;
1077
- }
1078
- return true;
1079
- }).every((isDone) => isDone);
1080
- }
1081
- };
1082
- //#endregion
1083
- //#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/FetchMock.js
1084
- const defaultFetchMockConfig = {
1085
- includeContentLength: true,
1086
- matchPartialBody: false,
1087
- Request: globalThis.Request,
1088
- Response: globalThis.Response,
1089
- Headers: globalThis.Headers,
1090
- fetch: globalThis.fetch
1091
- };
1092
- const defineShorthand = (shorthandOptions) => {
1093
- function shorthand(matcher, response, options) {
1094
- return this.route(matcher, response, Object.assign(options || {}, shorthandOptions));
1095
- }
1096
- return shorthand;
1097
- };
1098
- const defineGreedyShorthand = (shorthandOptions) => {
1099
- return function(response, options) {
1100
- return this.route("*", response, Object.assign(options || {}, shorthandOptions));
1101
- };
1102
- };
1103
- //#endregion
1104
- //#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/index.js
1105
- var esm_default = new class FetchMock {
1106
- constructor(config, router) {
1107
- this.sticky = defineShorthand({ sticky: true });
1108
- this.once = defineShorthand({ repeat: 1 });
1109
- this.any = defineGreedyShorthand({});
1110
- this.anyOnce = defineGreedyShorthand({ repeat: 1 });
1111
- this.get = defineShorthand({ method: "get" });
1112
- this.getOnce = defineShorthand({
1113
- method: "get",
1114
- repeat: 1
1115
- });
1116
- this.post = defineShorthand({ method: "post" });
1117
- this.postOnce = defineShorthand({
1118
- method: "post",
1119
- repeat: 1
1120
- });
1121
- this.put = defineShorthand({ method: "put" });
1122
- this.putOnce = defineShorthand({
1123
- method: "put",
1124
- repeat: 1
1125
- });
1126
- this.delete = defineShorthand({ method: "delete" });
1127
- this.deleteOnce = defineShorthand({
1128
- method: "delete",
1129
- repeat: 1
1130
- });
1131
- this.head = defineShorthand({ method: "head" });
1132
- this.headOnce = defineShorthand({
1133
- method: "head",
1134
- repeat: 1
1135
- });
1136
- this.patch = defineShorthand({ method: "patch" });
1137
- this.patchOnce = defineShorthand({
1138
- method: "patch",
1139
- repeat: 1
1140
- });
1141
- this.config = config;
1142
- this.router = new Router(this.config, {
1143
- routes: router ? [...router.routes] : [],
1144
- fallbackRoute: router ? router.fallbackRoute : null
1145
- });
1146
- this.callHistory = new CallHistory(this.config, this.router);
1147
- this.fetchHandler = this.fetchHandler.bind(this);
1148
- Object.assign(this.fetchHandler, { fetchMock: this });
1149
- }
1150
- createInstance() {
1151
- return new FetchMock({ ...this.config }, this.router);
1152
- }
1153
- async fetchHandler(requestInput, requestInit) {
1154
- let callLog;
1155
- if (requestInput instanceof this.config.Request) callLog = await createCallLogFromRequest(requestInput, requestInit);
1156
- else callLog = createCallLogFromUrlAndOptions(requestInput, requestInit);
1157
- this.callHistory.recordCall(callLog);
1158
- const responsePromise = this.router.execute(callLog);
1159
- callLog.pendingPromises.push(responsePromise);
1160
- return responsePromise;
1161
- }
1162
- route(matcher, response, options) {
1163
- this.router.addRoute(matcher, response, options);
1164
- return this;
1165
- }
1166
- catch(response) {
1167
- this.router.setFallback(response);
1168
- return this;
1169
- }
1170
- defineMatcher(matcher) {
1171
- Route.defineMatcher(matcher);
1172
- }
1173
- removeRoutes(options) {
1174
- this.router.removeRoutes(options);
1175
- return this;
1176
- }
1177
- removeRoute(routeName) {
1178
- this.router.removeRoutes({ names: [routeName] });
1179
- return this;
1180
- }
1181
- modifyRoute(routeName, options) {
1182
- this.router.modifyRoute(routeName, options);
1183
- return this;
1184
- }
1185
- clearHistory() {
1186
- this.callHistory.clear();
1187
- return this;
1188
- }
1189
- mockGlobal() {
1190
- globalThis.fetch = this.fetchHandler;
1191
- return this;
1192
- }
1193
- unmockGlobal() {
1194
- globalThis.fetch = this.config.fetch;
1195
- return this;
1196
- }
1197
- hardReset(options) {
1198
- this.clearHistory();
1199
- this.removeRoutes(options);
1200
- this.unmockGlobal();
1201
- return this;
1202
- }
1203
- spy(matcher, name) {
1204
- const boundFetch = this.config.fetch.bind(globalThis);
1205
- if (matcher) this.route(matcher, ({ args }) => boundFetch(...args), name);
1206
- else this.catch(({ args }) => boundFetch(...args));
1207
- return this;
1208
- }
1209
- spyGlobal() {
1210
- this.mockGlobal();
1211
- return this.spy();
1212
- }
1213
- }({ ...defaultFetchMockConfig });
1214
- //#endregion
1215
- //#region src/jsonld.ts
1216
- var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1217
- //#endregion
1218
- //#region src/docloader.test.ts
1219
36
  (0, _fedify_fixture.test)("new FetchError()", () => {
1220
37
  const e = new require_request.FetchError("https://example.com/", "An error message.");
1221
38
  (0, node_assert.deepStrictEqual)(e.name, "FetchError");
@@ -1230,9 +47,9 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1230
47
  (0, node_assert.deepStrictEqual)(e2.response.status, 410);
1231
48
  });
1232
49
  (0, _fedify_fixture.test)("getDocumentLoader()", async (t) => {
1233
- const fetchDocumentLoader = require_docloader.getDocumentLoader();
1234
- esm_default.spyGlobal();
1235
- esm_default.get("https://example.com/object", { body: {
50
+ const fetchDocumentLoader = require_docloader.createDocumentLoader({}, { openHop: openUnpinnedHop });
51
+ require_esm.esm_default.spyGlobal();
52
+ require_esm.esm_default.get("https://example.com/object", { body: {
1236
53
  "@context": "https://www.w3.org/ns/activitystreams",
1237
54
  id: "https://example.com/object",
1238
55
  name: "Fetched object",
@@ -1250,7 +67,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1250
67
  }
1251
68
  });
1252
69
  });
1253
- esm_default.get("https://example.com/link-ctx", {
70
+ require_esm.esm_default.get("https://example.com/link-ctx", {
1254
71
  body: {
1255
72
  id: "https://example.com/link-ctx",
1256
73
  name: "Fetched object",
@@ -1261,15 +78,15 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1261
78
  Link: "<https://www.w3.org/ns/activitystreams>; rel=\"http://www.w3.org/ns/json-ld#context\"; type=\"application/ld+json\""
1262
79
  }
1263
80
  });
1264
- esm_default.get("https://example.com/link-obj", { headers: {
81
+ require_esm.esm_default.get("https://example.com/link-obj", { headers: {
1265
82
  "Content-Type": "text/html; charset=utf-8",
1266
83
  Link: "<https://example.com/object>; rel=\"alternate\"; type=\"application/activity+json\""
1267
84
  } });
1268
- esm_default.get("https://example.com/link-obj-relative", { headers: {
85
+ require_esm.esm_default.get("https://example.com/link-obj-relative", { headers: {
1269
86
  "Content-Type": "text/html; charset=utf-8",
1270
87
  Link: "</object>; rel=\"alternate\"; type=\"application/activity+json\""
1271
88
  } });
1272
- esm_default.get("https://example.com/obj-w-wrong-link", {
89
+ require_esm.esm_default.get("https://example.com/obj-w-wrong-link", {
1273
90
  body: {
1274
91
  "@context": "https://www.w3.org/ns/activitystreams",
1275
92
  id: "https://example.com/obj-w-wrong-link",
@@ -1335,7 +152,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1335
152
  }
1336
153
  });
1337
154
  });
1338
- esm_default.get("https://example.com/html-link", {
155
+ require_esm.esm_default.get("https://example.com/html-link", {
1339
156
  body: `<html>
1340
157
  <head>
1341
158
  <meta charset=utf-8>
@@ -1359,7 +176,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1359
176
  }
1360
177
  });
1361
178
  });
1362
- esm_default.get("https://example.com/xhtml-link", {
179
+ require_esm.esm_default.get("https://example.com/xhtml-link", {
1363
180
  body: `<html>
1364
181
  <head>
1365
182
  <meta charset="utf-8" />
@@ -1383,7 +200,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1383
200
  }
1384
201
  });
1385
202
  });
1386
- esm_default.get("https://example.com/html-a", {
203
+ require_esm.esm_default.get("https://example.com/html-a", {
1387
204
  body: `<html>
1388
205
  <head>
1389
206
  <meta charset=utf-8>
@@ -1409,7 +226,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1409
226
  }
1410
227
  });
1411
228
  });
1412
- esm_default.get("https://example.com/wrong-content-type", {
229
+ require_esm.esm_default.get("https://example.com/wrong-content-type", {
1413
230
  body: {
1414
231
  "@context": "https://www.w3.org/ns/activitystreams",
1415
232
  id: "https://example.com/wrong-content-type",
@@ -1430,7 +247,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1430
247
  }
1431
248
  });
1432
249
  });
1433
- esm_default.get("https://example.com/404", { status: 404 });
250
+ require_esm.esm_default.get("https://example.com/404", { status: 404 });
1434
251
  await t.step("not ok", async () => {
1435
252
  await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/404"), require_request.FetchError, "HTTP 404: https://example.com/404");
1436
253
  });
@@ -1456,7 +273,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1456
273
  }]);
1457
274
  (0, node_assert.deepStrictEqual)((await jsonld_default.compact(expanded, context, { documentLoader: fetchDocumentLoader })).attributionDomains, ["blog.example"]);
1458
275
  });
1459
- esm_default.get("https://www.w3.org/ns/cid/v1", { status: 503 });
276
+ require_esm.esm_default.get("https://www.w3.org/ns/cid/v1", { status: 503 });
1460
277
  await t.step("preloaded CID v1 context", async () => {
1461
278
  const url = "https://www.w3.org/ns/cid/v1";
1462
279
  (0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
@@ -1481,7 +298,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1481
298
  } }
1482
299
  });
1483
300
  });
1484
- esm_default.get("https://w3id.org/fep/7aa9", { status: 502 });
301
+ require_esm.esm_default.get("https://w3id.org/fep/7aa9", { status: 502 });
1485
302
  await t.step("preloaded FEP-7aa9 context", async () => {
1486
303
  const url = "https://w3id.org/fep/7aa9";
1487
304
  (0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
@@ -1510,9 +327,9 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1510
327
  }
1511
328
  } }
1512
329
  });
1513
- (0, node_assert.deepStrictEqual)(esm_default.callHistory.calls(url).length, 0);
330
+ (0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls(url).length, 0);
1514
331
  });
1515
- esm_default.get("https://w3id.org/fep/6757", { status: 502 });
332
+ require_esm.esm_default.get("https://w3id.org/fep/6757", { status: 502 });
1516
333
  await t.step("preloaded FEP-6757 context", async () => {
1517
334
  const url = "https://w3id.org/fep/6757";
1518
335
  (0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
@@ -1539,9 +356,9 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1539
356
  "http://purl.org/dc/terms/license": [{ "@id": "https://creativecommons.org/licenses/by/4.0/" }],
1540
357
  "https://w3id.org/fep/6757#preferredLicense": [{ "@id": "https://creativecommons.org/licenses/by-nc/4.0/" }]
1541
358
  }]);
1542
- (0, node_assert.deepStrictEqual)(esm_default.callHistory.calls(url).length, 0);
359
+ (0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls(url).length, 0);
1543
360
  });
1544
- esm_default.get("https://w3id.org/fep/22cd", { status: 502 });
361
+ require_esm.esm_default.get("https://w3id.org/fep/22cd", { status: 502 });
1545
362
  await t.step("preloaded FEP-22cd context", async () => {
1546
363
  const url = "https://w3id.org/fep/22cd";
1547
364
  const articleId = "https://example.com/articles/1";
@@ -1617,17 +434,17 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1617
434
  "https://schema.org/isBasedOn": [{ "@id": revisionId }]
1618
435
  }]
1619
436
  }]);
1620
- (0, node_assert.deepStrictEqual)(esm_default.callHistory.calls(url).length, 0);
1621
- (0, node_assert.deepStrictEqual)(esm_default.callHistory.calls("https://www.w3.org/ns/activitystreams").length, 0);
437
+ (0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls(url).length, 0);
438
+ (0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls("https://www.w3.org/ns/activitystreams").length, 0);
1622
439
  });
1623
440
  await t.step("deny non-HTTP/HTTPS", async () => {
1624
441
  await (0, node_assert.rejects)(() => fetchDocumentLoader("ftp://localhost"), require_url.UrlError);
1625
442
  });
1626
- esm_default.get("https://example.com/localhost-redirect", {
443
+ require_esm.esm_default.get("https://example.com/localhost-redirect", {
1627
444
  status: 302,
1628
445
  headers: { Location: "https://localhost/object" }
1629
446
  });
1630
- esm_default.get("https://example.com/localhost-link", {
447
+ require_esm.esm_default.get("https://example.com/localhost-link", {
1631
448
  body: `<html>
1632
449
  <head>
1633
450
  <meta charset=utf-8>
@@ -1639,16 +456,32 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1639
456
  </html>`,
1640
457
  headers: { "Content-Type": "text/html; charset=utf-8" }
1641
458
  });
1642
- esm_default.get("https://localhost/object", { body: {
459
+ require_esm.esm_default.get("https://localhost/object", { body: {
1643
460
  "@context": "https://www.w3.org/ns/activitystreams",
1644
461
  id: "https://localhost/object",
1645
462
  name: "Fetched object",
1646
463
  type: "Object"
1647
464
  } });
465
+ require_esm.esm_default.get("https://example.com/private-redirect", {
466
+ status: 302,
467
+ headers: { Location: "https://private.example/object" }
468
+ });
469
+ require_esm.esm_default.get("https://private.example/object", { body: {
470
+ "@context": "https://www.w3.org/ns/activitystreams",
471
+ id: "https://private.example/object",
472
+ type: "Object"
473
+ } });
1648
474
  await t.step("allowPrivateAddress: false", async () => {
1649
475
  await (0, node_assert.rejects)(() => fetchDocumentLoader("https://localhost/object"), require_url.UrlError);
1650
476
  await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/localhost-redirect"), require_url.UrlError);
1651
477
  await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/localhost-link"), require_url.UrlError);
478
+ await (0, node_assert.rejects)(() => fetchDocumentLoader("https://private.example/object"), require_url.UrlError);
479
+ await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/private-redirect"), require_url.UrlError);
480
+ (0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls("https://private.example/object").length, 0);
481
+ const defaultLoader = require_docloader.getDocumentLoader();
482
+ await (0, node_assert.rejects)(() => defaultLoader("https://localhost/object"), require_url.UrlError);
483
+ await (0, node_assert.rejects)(() => defaultLoader("https://127.0.0.1/object"), require_url.UrlError);
484
+ (0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls("https://localhost/object").length, 0);
1652
485
  });
1653
486
  const fetchDocumentLoader2 = require_docloader.getDocumentLoader({ allowPrivateAddress: true });
1654
487
  await t.step("allowPrivateAddress: true", async () => {
@@ -1667,7 +500,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1667
500
  (0, node_assert.deepStrictEqual)(await fetchDocumentLoader2("https://example.com/localhost-link"), expected);
1668
501
  });
1669
502
  let redirectAttempts = 0;
1670
- esm_default.get("begin:https://example.com/too-many-redirects/", (cl) => {
503
+ require_esm.esm_default.get("begin:https://example.com/too-many-redirects/", (cl) => {
1671
504
  redirectAttempts++;
1672
505
  return {
1673
506
  status: 302,
@@ -1681,19 +514,19 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1681
514
  });
1682
515
  await t.step("custom max redirection", async () => {
1683
516
  redirectAttempts = 0;
1684
- const loader = require_docloader.getDocumentLoader({ maxRedirection: 1 });
517
+ const loader = require_docloader.createDocumentLoader({ maxRedirection: 1 }, { openHop: openUnpinnedHop });
1685
518
  await (0, node_assert.rejects)(() => loader("https://example.com/too-many-redirects/0"), require_request.FetchError, "Too many redirections");
1686
519
  (0, node_assert.deepStrictEqual)(redirectAttempts, 2);
1687
520
  });
1688
521
  let loopAttempts = 0;
1689
- esm_default.get("https://example.com/redirect-loop-a", () => {
522
+ require_esm.esm_default.get("https://example.com/redirect-loop-a", () => {
1690
523
  loopAttempts++;
1691
524
  return {
1692
525
  status: 302,
1693
526
  headers: { Location: "https://example.com/redirect-loop-b" }
1694
527
  };
1695
528
  });
1696
- esm_default.get("https://example.com/redirect-loop-b", () => {
529
+ require_esm.esm_default.get("https://example.com/redirect-loop-b", () => {
1697
530
  loopAttempts++;
1698
531
  return {
1699
532
  status: 302,
@@ -1706,7 +539,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1706
539
  (0, node_assert.deepStrictEqual)(loopAttempts, 2);
1707
540
  });
1708
541
  let relativeLoopAttempts = 0;
1709
- esm_default.get("https://example.com/redirect-loop-relative", () => {
542
+ require_esm.esm_default.get("https://example.com/redirect-loop-relative", () => {
1710
543
  relativeLoopAttempts++;
1711
544
  return {
1712
545
  status: 302,
@@ -1719,7 +552,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1719
552
  (0, node_assert.deepStrictEqual)(relativeLoopAttempts, 1);
1720
553
  });
1721
554
  const maliciousPayload = "<a" + " a=\"b\"".repeat(30) + " ";
1722
- esm_default.get("https://example.com/redos", {
555
+ require_esm.esm_default.get("https://example.com/redos", {
1723
556
  body: maliciousPayload,
1724
557
  headers: { "Content-Type": "text/html; charset=utf-8" }
1725
558
  });
@@ -1729,14 +562,14 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1729
562
  const elapsed = performance.now() - start;
1730
563
  (0, node_assert.ok)(elapsed < 1e3, `Potential ReDoS vulnerability detected: ${elapsed}ms (expected < 1000ms)`);
1731
564
  });
1732
- esm_default.hardReset();
565
+ require_esm.esm_default.hardReset();
1733
566
  });
1734
567
  (0, _fedify_fixture.test)("getDocumentLoader() bounds JSON, HTML, and alternate documents", async () => {
1735
568
  const url = "https://example.com/bounded";
1736
- esm_default.mockGlobal();
569
+ require_esm.esm_default.mockGlobal();
1737
570
  let oversized = true;
1738
571
  try {
1739
- esm_default.get(url, () => new Response("{\"name\":\"hello\"}", { headers: {
572
+ require_esm.esm_default.get(url, () => new Response("{\"name\":\"hello\"}", { headers: {
1740
573
  "Content-Type": "application/activity+json",
1741
574
  ...oversized ? { "Content-Length": String(16777217) } : {}
1742
575
  } }));
@@ -1745,20 +578,20 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1745
578
  oversized = false;
1746
579
  (0, node_assert.deepStrictEqual)((await loader(url)).document, { name: "hello" });
1747
580
  oversized = true;
1748
- esm_default.get(`${url}/html`, () => new Response(" ".repeat(1048577), { headers: { "Content-Type": "text/html" } }));
581
+ require_esm.esm_default.get(`${url}/html`, () => new Response(" ".repeat(1048577), { headers: { "Content-Type": "text/html" } }));
1749
582
  await (0, node_assert.rejects)(require_docloader.getDocumentLoader({ allowPrivateAddress: true })(`${url}/html`), require_request.FetchError);
1750
- esm_default.get(`${url}/alternate`, () => new Response("", { headers: {
583
+ require_esm.esm_default.get(`${url}/alternate`, () => new Response("", { headers: {
1751
584
  "Content-Type": "text/html",
1752
585
  Link: `<${url}>; rel="alternate"; type="application/activity+json"`
1753
586
  } }));
1754
587
  await (0, node_assert.rejects)(loader(`${url}/alternate`), require_request.FetchError);
1755
- esm_default.get(`${url}/redirect`, {
588
+ require_esm.esm_default.get(`${url}/redirect`, {
1756
589
  status: 302,
1757
590
  headers: { Location: url }
1758
591
  });
1759
592
  await (0, node_assert.rejects)(loader(`${url}/redirect`), require_request.FetchError);
1760
593
  } finally {
1761
- esm_default.hardReset();
594
+ require_esm.esm_default.hardReset();
1762
595
  }
1763
596
  });
1764
597
  (0, _fedify_fixture.test)("getDocumentLoader() rejects oversized gzip responses from fetch", async () => {
@@ -1809,18 +642,14 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1809
642
  (0, node_assert.deepStrictEqual)(pulls, 257);
1810
643
  });
1811
644
  (0, _fedify_fixture.test)("getDocumentLoader() logs DNS failures as such", async (t) => {
1812
- if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") throw new Error("This DNS test requires Deno network permission (--allow-net).");
1813
- const loader = require_docloader.getDocumentLoader();
1814
645
  for (const result of [
1815
646
  "throws",
1816
647
  "empty",
1817
- "private"
648
+ "private",
649
+ "default"
1818
650
  ]) await t.step(result, async () => {
1819
- const originalLookup = node_dns_promises.default.lookup;
1820
- node_dns_promises.default.lookup = (() => result === "throws" ? Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")) : Promise.resolve(result === "empty" ? [] : [{
1821
- address: "127.0.0.1",
1822
- family: 4
1823
- }]));
651
+ const lookup = () => result === "throws" ? Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")) : Promise.resolve(result === "empty" ? [] : [PRIVATE_ADDRESS]);
652
+ const loader = result === "default" ? require_docloader.getDocumentLoader() : require_docloader.createDocumentLoader({}, { openHop: (url) => unpinnedPublicFetch.openPublicFetch(url, { lookup }) });
1824
653
  const records = [];
1825
654
  await (0, _logtape_logtape.configure)({
1826
655
  sinks: { buffer: (record) => records.push(record) },
@@ -1835,19 +664,20 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1835
664
  reset: true
1836
665
  });
1837
666
  try {
1838
- const url = "https://dns-failure.invalid/object";
667
+ const url = result === "default" ? "https://10.0.0.1/object" : "https://dns-failure.invalid/object";
1839
668
  let error;
1840
669
  await (0, node_assert.rejects)(() => loader(url), (e) => {
1841
670
  error = e;
1842
671
  return true;
1843
672
  });
1844
673
  (0, node_assert.ok)(error instanceof require_url.UrlError);
1845
- (0, node_assert.deepStrictEqual)(error.reason, result === "private" ? "disallowed" : "dns");
674
+ const disallowed = result === "private" || result === "default";
675
+ (0, node_assert.deepStrictEqual)(error.reason, disallowed ? "disallowed" : "dns");
1846
676
  (0, node_assert.deepStrictEqual)(records.map((r) => [
1847
677
  r.level,
1848
678
  r.rawMessage,
1849
679
  r.properties.url
1850
- ]), [result === "private" ? [
680
+ ]), [disallowed ? [
1851
681
  "error",
1852
682
  "Disallowed private URL: {url}",
1853
683
  url
@@ -1859,7 +689,6 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1859
689
  (0, node_assert.ok)(records[0].properties.error === error);
1860
690
  } finally {
1861
691
  await (0, _logtape_logtape.reset)();
1862
- node_dns_promises.default.lookup = originalLookup;
1863
692
  }
1864
693
  });
1865
694
  });
@@ -1871,9 +700,9 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1871
700
  "html",
1872
701
  "mixed"
1873
702
  ]) for (const hops of [20, 21]) await t.step(`${mode}: ${hops} hops`, async () => {
1874
- esm_default.mockGlobal();
703
+ require_esm.esm_default.mockGlobal();
1875
704
  let requests = 0;
1876
- esm_default.get(`begin:${base}`, ({ url }) => {
705
+ require_esm.esm_default.get(`begin:${base}`, ({ url }) => {
1877
706
  requests++;
1878
707
  const index = Number(new URL(url).pathname.split("/").at(-1));
1879
708
  if (index === hops || requests > 30) return Response.json({ done: true });
@@ -1890,7 +719,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1890
719
  });
1891
720
  (0, node_assert.deepStrictEqual)(requests, 21);
1892
721
  } finally {
1893
- esm_default.hardReset();
722
+ require_esm.esm_default.hardReset();
1894
723
  }
1895
724
  });
1896
725
  for (const mode of [
@@ -1900,9 +729,9 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1900
729
  "redirect-alternate",
1901
730
  "redirect-intermediate"
1902
731
  ]) await t.step(`${mode}: cycle`, async () => {
1903
- esm_default.mockGlobal();
732
+ require_esm.esm_default.mockGlobal();
1904
733
  let requests = 0;
1905
- esm_default.get(`begin:${base}`, ({ url }) => {
734
+ require_esm.esm_default.get(`begin:${base}`, ({ url }) => {
1906
735
  requests++;
1907
736
  if (requests > 30) return Response.json({ stopped: true });
1908
737
  const index = Number(new URL(url).pathname.split("/").at(-1));
@@ -1918,13 +747,13 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1918
747
  });
1919
748
  (0, node_assert.deepStrictEqual)(requests, mode === "redirect-intermediate" ? 3 : 2);
1920
749
  } finally {
1921
- esm_default.hardReset();
750
+ require_esm.esm_default.hardReset();
1922
751
  }
1923
752
  });
1924
753
  await t.step("alternate followed by 20 redirects shares the limit", async () => {
1925
- esm_default.mockGlobal();
754
+ require_esm.esm_default.mockGlobal();
1926
755
  let requests = 0;
1927
- esm_default.get(`begin:${base}`, ({ url }) => {
756
+ require_esm.esm_default.get(`begin:${base}`, ({ url }) => {
1928
757
  requests++;
1929
758
  const index = Number(new URL(url).pathname.split("/").at(-1));
1930
759
  if (index === 21) return Response.json({ done: true });
@@ -1938,14 +767,14 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1938
767
  });
1939
768
  (0, node_assert.deepStrictEqual)(requests, 21);
1940
769
  } finally {
1941
- esm_default.hardReset();
770
+ require_esm.esm_default.hardReset();
1942
771
  }
1943
772
  });
1944
773
  await t.step("relative alternate after redirect and isolated calls", async () => {
1945
- esm_default.mockGlobal();
1946
- esm_default.get(`${base}start`, Response.redirect(`${base}html`, 302));
1947
- esm_default.get(`${base}html`, alternate("./document", true));
1948
- esm_default.get(`${base}document`, Response.json({ done: true }));
774
+ require_esm.esm_default.mockGlobal();
775
+ require_esm.esm_default.get(`${base}start`, Response.redirect(`${base}html`, 302));
776
+ require_esm.esm_default.get(`${base}html`, alternate("./document", true));
777
+ require_esm.esm_default.get(`${base}document`, Response.json({ done: true }));
1949
778
  try {
1950
779
  for (let i = 0; i < 2; i++) {
1951
780
  const results = await Promise.all([loader(`${base}start`), loader(`${base}start`)]);
@@ -1955,7 +784,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1955
784
  }
1956
785
  }
1957
786
  } finally {
1958
- esm_default.hardReset();
787
+ require_esm.esm_default.hardReset();
1959
788
  }
1960
789
  });
1961
790
  function alternate(next, html) {
@@ -1969,10 +798,10 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1969
798
  const base = "https://example.com/alternate-abort/";
1970
799
  const loader = require_docloader.getDocumentLoader({ allowPrivateAddress: true });
1971
800
  for (const html of [false, true]) await t.step(html ? "HTML" : "Link header", async () => {
1972
- esm_default.mockGlobal();
801
+ require_esm.esm_default.mockGlobal();
1973
802
  const controller = new AbortController();
1974
803
  let requests = 0;
1975
- esm_default.get(`begin:${base}`, ({ url }) => {
804
+ require_esm.esm_default.get(`begin:${base}`, ({ url }) => {
1976
805
  requests++;
1977
806
  const index = Number(new URL(url).pathname.split("/").at(-1));
1978
807
  if (index === 2) return Response.json({ done: true });
@@ -1987,15 +816,15 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
1987
816
  await (0, node_assert.rejects)(loader(`${base}0`, { signal: controller.signal }), { name: "AbortError" });
1988
817
  (0, node_assert.deepStrictEqual)(requests, 2);
1989
818
  } finally {
1990
- esm_default.hardReset();
819
+ require_esm.esm_default.hardReset();
1991
820
  }
1992
821
  });
1993
822
  });
1994
823
  (0, _fedify_fixture.test)("getDocumentLoader() rejects cancellation before fetching", async () => {
1995
- esm_default.mockGlobal();
824
+ require_esm.esm_default.mockGlobal();
1996
825
  let requests = 0;
1997
826
  const url = "https://example.com/pre-aborted-alternate";
1998
- esm_default.get(url, () => {
827
+ require_esm.esm_default.get(url, () => {
1999
828
  requests++;
2000
829
  return Response.json({ done: true });
2001
830
  });
@@ -2006,7 +835,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
2006
835
  await (0, node_assert.rejects)(loader(url, { signal: controller.signal }), { name: "AbortError" });
2007
836
  (0, node_assert.deepStrictEqual)(requests, 0);
2008
837
  } finally {
2009
- esm_default.hardReset();
838
+ require_esm.esm_default.hardReset();
2010
839
  }
2011
840
  });
2012
841
  const unsupportedUrls = [
@@ -2103,4 +932,425 @@ for (const url of unsupportedUrls) {
2103
932
  });
2104
933
  });
2105
934
  }
935
+ function createHopRecorder(responder) {
936
+ const events = [];
937
+ const states = /* @__PURE__ */ new Map();
938
+ const violations = [];
939
+ let responses = 0;
940
+ let opens = 0;
941
+ let closes = 0;
942
+ function respond(url) {
943
+ const n = ++responses;
944
+ events.push(`fetch ${url} #${n}`);
945
+ const { status = 200, headers, body = "", failCancel, hangCancel, responseUrl } = responder(url);
946
+ const state = {
947
+ n,
948
+ settled: false
949
+ };
950
+ let sent = false;
951
+ const stream = new ReadableStream({
952
+ pull(controller) {
953
+ if (!sent && body !== "") {
954
+ sent = true;
955
+ controller.enqueue(new TextEncoder().encode(body));
956
+ return;
957
+ }
958
+ controller.close();
959
+ state.settled = true;
960
+ events.push(`consumed #${n}`);
961
+ },
962
+ cancel() {
963
+ state.settled = true;
964
+ events.push(`cancel #${n}`);
965
+ if (failCancel) throw new Error(`Failed to cancel #${n}`);
966
+ if (hangCancel) return new Promise(() => {});
967
+ }
968
+ }, { highWaterMark: 0 });
969
+ const response = new Response(stream, {
970
+ status,
971
+ headers
972
+ });
973
+ if (responseUrl != null) Object.defineProperty(response, "url", { value: responseUrl });
974
+ states.set(response, state);
975
+ return response;
976
+ }
977
+ function record(url, session) {
978
+ opens++;
979
+ events.push(`open ${url}`);
980
+ const fetched = [];
981
+ let closed = false;
982
+ return {
983
+ get url() {
984
+ return session.url;
985
+ },
986
+ async fetch(input, init) {
987
+ const response = await session.fetch(input, init);
988
+ fetched.push(response);
989
+ return response;
990
+ },
991
+ async close() {
992
+ if (closed) return;
993
+ closed = true;
994
+ for (const response of fetched) {
995
+ const state = states.get(response);
996
+ if (state != null && !state.settled) violations.push(`#${state.n} is still open when closing ${url}`);
997
+ }
998
+ closes++;
999
+ events.push(`close ${url}`);
1000
+ await session.close();
1001
+ }
1002
+ };
1003
+ }
1004
+ function openHop(url) {
1005
+ const origin = new URL(url).origin;
1006
+ return Promise.resolve(record(url, {
1007
+ url: new URL(url),
1008
+ fetch(input) {
1009
+ const target = input instanceof Request ? input.url : String(input);
1010
+ (0, node_assert.ok)(new URL(target).origin === origin, `${target} is not on ${origin}`);
1011
+ return Promise.resolve(respond(target));
1012
+ },
1013
+ close: () => Promise.resolve()
1014
+ }));
1015
+ }
1016
+ return {
1017
+ events,
1018
+ respond,
1019
+ record,
1020
+ openHop,
1021
+ assertInvariants() {
1022
+ (0, node_assert.deepStrictEqual)(violations, []);
1023
+ (0, node_assert.deepStrictEqual)(opens, closes);
1024
+ }
1025
+ };
1026
+ }
1027
+ const lifecycleDocument = {
1028
+ "@context": "https://www.w3.org/ns/activitystreams",
1029
+ id: "https://b.example/object",
1030
+ type: "Object"
1031
+ };
1032
+ (0, _fedify_fixture.test)("getDocumentLoader() closes a redirect hop before the next hop", async () => {
1033
+ const recorder = createHopRecorder((url) => url === "https://a.example/start" ? {
1034
+ status: 302,
1035
+ headers: { Location: "https://b.example/object" }
1036
+ } : { body: JSON.stringify(lifecycleDocument) });
1037
+ (0, node_assert.deepStrictEqual)(await require_docloader.createDocumentLoader({}, { openHop: recorder.openHop })("https://a.example/start"), {
1038
+ contextUrl: null,
1039
+ documentUrl: "https://b.example/object",
1040
+ document: lifecycleDocument
1041
+ });
1042
+ (0, node_assert.deepStrictEqual)(recorder.events, [
1043
+ "open https://a.example/start",
1044
+ "fetch https://a.example/start #1",
1045
+ "cancel #1",
1046
+ "close https://a.example/start",
1047
+ "open https://b.example/object",
1048
+ "fetch https://b.example/object #2",
1049
+ "consumed #2",
1050
+ "close https://b.example/object"
1051
+ ]);
1052
+ recorder.assertInvariants();
1053
+ });
1054
+ (0, _fedify_fixture.test)("getDocumentLoader() cancels the body before a Link header alternate", async () => {
1055
+ const recorder = createHopRecorder((url) => url === "https://a.example/start" ? {
1056
+ headers: {
1057
+ "Content-Type": "text/plain",
1058
+ Link: "<https://b.example/object>; rel=\"alternate\"; type=\"application/activity+json\""
1059
+ },
1060
+ body: "not JSON"
1061
+ } : { body: JSON.stringify(lifecycleDocument) });
1062
+ (0, node_assert.deepStrictEqual)((await require_docloader.createDocumentLoader({}, { openHop: recorder.openHop })("https://a.example/start")).documentUrl, "https://b.example/object");
1063
+ (0, node_assert.deepStrictEqual)(recorder.events, [
1064
+ "open https://a.example/start",
1065
+ "fetch https://a.example/start #1",
1066
+ "cancel #1",
1067
+ "close https://a.example/start",
1068
+ "open https://b.example/object",
1069
+ "fetch https://b.example/object #2",
1070
+ "consumed #2",
1071
+ "close https://b.example/object"
1072
+ ]);
1073
+ recorder.assertInvariants();
1074
+ });
1075
+ (0, _fedify_fixture.test)("getDocumentLoader() reads the body before an HTML alternate", async () => {
1076
+ const recorder = createHopRecorder((url) => url === "https://a.example/start" ? {
1077
+ headers: { "Content-Type": "text/html" },
1078
+ body: "<link rel=\"alternate\" type=\"application/activity+json\" href=\"https://b.example/object\">"
1079
+ } : { body: JSON.stringify(lifecycleDocument) });
1080
+ (0, node_assert.deepStrictEqual)((await require_docloader.createDocumentLoader({}, { openHop: recorder.openHop })("https://a.example/start")).documentUrl, "https://b.example/object");
1081
+ (0, node_assert.deepStrictEqual)(recorder.events, [
1082
+ "open https://a.example/start",
1083
+ "fetch https://a.example/start #1",
1084
+ "consumed #1",
1085
+ "close https://a.example/start",
1086
+ "open https://b.example/object",
1087
+ "fetch https://b.example/object #2",
1088
+ "consumed #2",
1089
+ "close https://b.example/object"
1090
+ ]);
1091
+ recorder.assertInvariants();
1092
+ });
1093
+ /**
1094
+ * Fails instead of blocking the test if the given promise does not settle
1095
+ * within the given time.
1096
+ */
1097
+ async function settlesWithin(promise, milliseconds = 1e3) {
1098
+ let timer;
1099
+ try {
1100
+ return await Promise.race([promise, new Promise((_, reject) => {
1101
+ timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Did not settle in ${milliseconds} ms.`)), milliseconds);
1102
+ })]);
1103
+ } finally {
1104
+ clearTimeout(timer);
1105
+ }
1106
+ }
1107
+ /**
1108
+ * Runs the given function while capturing the document loader's logs, and
1109
+ * waits until the rejections of body cancellations started without being
1110
+ * awaited have been logged.
1111
+ */
1112
+ async function captureDocumentLoaderLogs(fn) {
1113
+ const records = [];
1114
+ await (0, _logtape_logtape.configure)({
1115
+ sinks: { buffer: (record) => records.push(record) },
1116
+ loggers: [{
1117
+ category: [
1118
+ "fedify",
1119
+ "runtime",
1120
+ "docloader"
1121
+ ],
1122
+ sinks: ["buffer"],
1123
+ lowestLevel: "debug"
1124
+ }, {
1125
+ category: ["logtape", "meta"],
1126
+ sinks: []
1127
+ }],
1128
+ reset: true
1129
+ });
1130
+ try {
1131
+ const result = await fn();
1132
+ await new Promise((resolve) => setTimeout(resolve, 0));
1133
+ return {
1134
+ result,
1135
+ records
1136
+ };
1137
+ } finally {
1138
+ await (0, _logtape_logtape.reset)();
1139
+ }
1140
+ }
1141
+ function cancelFailureLogs(records) {
1142
+ return records.filter((record) => record.rawMessage === "Failed to cancel the response body from {url}: {error}").map((record) => [
1143
+ record.level,
1144
+ record.properties.url,
1145
+ String(record.properties.error)
1146
+ ]);
1147
+ }
1148
+ function linkHeaderAlternate(next) {
1149
+ return {
1150
+ headers: {
1151
+ "Content-Type": "text/plain",
1152
+ Link: `<${next}>; rel="alternate"; type="application/activity+json"`
1153
+ },
1154
+ body: "not JSON"
1155
+ };
1156
+ }
1157
+ (0, _fedify_fixture.test)("getDocumentLoader() continues if an intermediate body fails to cancel", async (t) => {
1158
+ for (const kind of ["redirect", "Link header alternate"]) await t.step(kind, async () => {
1159
+ const recorder = createHopRecorder((url) => url === "https://a.example/start" ? {
1160
+ ...kind === "redirect" ? {
1161
+ status: 302,
1162
+ headers: { Location: "https://b.example/object" }
1163
+ } : linkHeaderAlternate("https://b.example/object"),
1164
+ failCancel: true
1165
+ } : { body: JSON.stringify(lifecycleDocument) });
1166
+ const loader = require_docloader.createDocumentLoader({}, { openHop: recorder.openHop });
1167
+ const { result, records } = await captureDocumentLoaderLogs(() => loader("https://a.example/start"));
1168
+ (0, node_assert.deepStrictEqual)(result, {
1169
+ contextUrl: null,
1170
+ documentUrl: "https://b.example/object",
1171
+ document: lifecycleDocument
1172
+ });
1173
+ (0, node_assert.deepStrictEqual)(recorder.events, [
1174
+ "open https://a.example/start",
1175
+ "fetch https://a.example/start #1",
1176
+ "cancel #1",
1177
+ "close https://a.example/start",
1178
+ "open https://b.example/object",
1179
+ "fetch https://b.example/object #2",
1180
+ "consumed #2",
1181
+ "close https://b.example/object"
1182
+ ]);
1183
+ recorder.assertInvariants();
1184
+ (0, node_assert.deepStrictEqual)(cancelFailureLogs(records), [[
1185
+ "debug",
1186
+ "https://a.example/start",
1187
+ "Error: Failed to cancel #1"
1188
+ ]]);
1189
+ });
1190
+ });
1191
+ (0, _fedify_fixture.test)("getDocumentLoader() does not wait for intermediate bodies to be cancelled", async (t) => {
1192
+ for (const kind of ["redirect", "Link header alternate"]) await t.step(kind, async () => {
1193
+ const recorder = createHopRecorder((url) => url === "https://a.example/start" ? {
1194
+ ...kind === "redirect" ? {
1195
+ status: 302,
1196
+ headers: { Location: "https://b.example/object" }
1197
+ } : linkHeaderAlternate("https://b.example/object"),
1198
+ hangCancel: true
1199
+ } : { body: JSON.stringify(lifecycleDocument) });
1200
+ (0, node_assert.deepStrictEqual)((await settlesWithin(require_docloader.createDocumentLoader({}, { openHop: recorder.openHop })("https://a.example/start"))).documentUrl, "https://b.example/object");
1201
+ (0, node_assert.deepStrictEqual)(recorder.events, [
1202
+ "open https://a.example/start",
1203
+ "fetch https://a.example/start #1",
1204
+ "cancel #1",
1205
+ "close https://a.example/start",
1206
+ "open https://b.example/object",
1207
+ "fetch https://b.example/object #2",
1208
+ "consumed #2",
1209
+ "close https://b.example/object"
1210
+ ]);
1211
+ recorder.assertInvariants();
1212
+ });
1213
+ await t.step("public session with a never-settling cancel", async () => {
1214
+ let cancelled = 0;
1215
+ const pinner = {
1216
+ supports: () => true,
1217
+ fetch: () => Promise.resolve(new Response(new ReadableStream({ cancel() {
1218
+ cancelled++;
1219
+ return new Promise(() => {});
1220
+ } }), { status: 404 })),
1221
+ release() {}
1222
+ };
1223
+ const publicFetch = require_public_fetch.createPublicFetch({
1224
+ pinner: () => Promise.resolve(pinner),
1225
+ lookup: () => Promise.reject(/* @__PURE__ */ new Error("Unexpected default lookup"))
1226
+ });
1227
+ await settlesWithin((0, node_assert.rejects)(require_docloader.createDocumentLoader({}, { openHop: (url) => publicFetch.openPublicFetch(url, { lookup: lookupTestAddresses }) })("https://a.example/missing"), (error) => {
1228
+ (0, node_assert.ok)(error instanceof require_request.FetchError);
1229
+ error.response?.body?.cancel().catch(() => {});
1230
+ return true;
1231
+ }));
1232
+ (0, node_assert.deepStrictEqual)(cancelled, 1);
1233
+ });
1234
+ });
1235
+ (0, _fedify_fixture.test)("getDocumentLoader() completes with tee'd fetch-mock responses", async (t) => {
1236
+ const url = "https://example.com/teed";
1237
+ function stream(text) {
1238
+ return new ReadableStream({ start(controller) {
1239
+ controller.enqueue(new TextEncoder().encode(text));
1240
+ controller.close();
1241
+ } });
1242
+ }
1243
+ const loaders = {
1244
+ unpinned: require_docloader.createDocumentLoader({}, { openHop: openUnpinnedHop }),
1245
+ "private addresses allowed": require_docloader.getDocumentLoader({ allowPrivateAddress: true })
1246
+ };
1247
+ for (const [name, loader] of Object.entries(loaders)) for (const body of ["string", "stream"]) await t.step(`${name}: ${body} bodies`, async () => {
1248
+ const make = (text) => body === "string" ? text : stream(text);
1249
+ require_esm.esm_default.mockGlobal();
1250
+ try {
1251
+ require_esm.esm_default.get(`${url}/object`, () => new Response(make(JSON.stringify(lifecycleDocument)), { headers: { "Content-Type": "application/activity+json" } }));
1252
+ require_esm.esm_default.get(`${url}/redirect`, () => new Response(make("redirect"), {
1253
+ status: 302,
1254
+ headers: { Location: `${url}/object` }
1255
+ }));
1256
+ require_esm.esm_default.get(`${url}/alternate`, () => new Response(make("not JSON"), { headers: {
1257
+ "Content-Type": "text/plain",
1258
+ Link: `<${url}/object>; rel="alternate"; type="application/activity+json"`
1259
+ } }));
1260
+ require_esm.esm_default.get(`${url}/missing`, () => new Response(make("not found"), { status: 404 }));
1261
+ for (const path of ["redirect", "alternate"]) (0, node_assert.deepStrictEqual)((await settlesWithin(loader(`${url}/${path}`))).document, lifecycleDocument);
1262
+ await settlesWithin((0, node_assert.rejects)(loader(`${url}/missing`), require_request.FetchError));
1263
+ (0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls().map((call) => call.url), [
1264
+ `${url}/redirect`,
1265
+ `${url}/object`,
1266
+ `${url}/alternate`,
1267
+ `${url}/object`,
1268
+ `${url}/missing`
1269
+ ]);
1270
+ } finally {
1271
+ require_esm.esm_default.hardReset();
1272
+ }
1273
+ });
1274
+ });
1275
+ (0, _fedify_fixture.test)("getDocumentLoader() resolves each hop once", async () => {
1276
+ const lookups = [];
1277
+ const recorder = createHopRecorder((url) => {
1278
+ const responseUrl = `https://${PUBLIC_ADDRESS.address}${new URL(url).pathname}`;
1279
+ if (url === "https://a.example/start") return {
1280
+ status: 302,
1281
+ headers: { Location: "https://b.example/next" },
1282
+ responseUrl
1283
+ };
1284
+ else if (url === "https://b.example/next") return {
1285
+ status: 301,
1286
+ headers: { Location: "/object" },
1287
+ responseUrl
1288
+ };
1289
+ return {
1290
+ body: JSON.stringify(lifecycleDocument),
1291
+ responseUrl
1292
+ };
1293
+ });
1294
+ const pinner = {
1295
+ supports: () => true,
1296
+ fetch: (_grant, request) => Promise.resolve(recorder.respond(request.url)),
1297
+ release() {}
1298
+ };
1299
+ const publicFetch = require_public_fetch.createPublicFetch({
1300
+ pinner: () => Promise.resolve(pinner),
1301
+ lookup: () => Promise.reject(/* @__PURE__ */ new Error("Unexpected default lookup"))
1302
+ });
1303
+ (0, node_assert.deepStrictEqual)(await require_docloader.createDocumentLoader({}, { openHop: async (url) => recorder.record(url, await publicFetch.openPublicFetch(url, { lookup(hostname) {
1304
+ lookups.push(hostname);
1305
+ return Promise.resolve([PUBLIC_ADDRESS]);
1306
+ } })) })("https://a.example/start"), {
1307
+ contextUrl: null,
1308
+ documentUrl: "https://b.example/object",
1309
+ document: lifecycleDocument
1310
+ });
1311
+ (0, node_assert.deepStrictEqual)(lookups, [
1312
+ "a.example",
1313
+ "b.example",
1314
+ "b.example"
1315
+ ]);
1316
+ (0, node_assert.deepStrictEqual)(recorder.events, [
1317
+ "open https://a.example/start",
1318
+ "fetch https://a.example/start #1",
1319
+ "cancel #1",
1320
+ "close https://a.example/start",
1321
+ "open https://b.example/next",
1322
+ "fetch https://b.example/next #2",
1323
+ "cancel #2",
1324
+ "close https://b.example/next",
1325
+ "open https://b.example/object",
1326
+ "fetch https://b.example/object #3",
1327
+ "consumed #3",
1328
+ "close https://b.example/object"
1329
+ ]);
1330
+ recorder.assertInvariants();
1331
+ });
1332
+ (0, _fedify_fixture.test)("getRemoteDocument() cancels the body before loading a Link header alternate", async () => {
1333
+ const recorder = createHopRecorder(() => ({
1334
+ headers: {
1335
+ "Content-Type": "text/html",
1336
+ Link: "<https://b.example/object>; rel=\"alternate\"; type=\"application/activity+json\""
1337
+ },
1338
+ body: "<html></html>"
1339
+ }));
1340
+ const response = recorder.respond("https://a.example/start");
1341
+ const placeholder = {
1342
+ contextUrl: null,
1343
+ document: {},
1344
+ documentUrl: "https://b.example/object"
1345
+ };
1346
+ const alternates = [];
1347
+ (0, node_assert.ok)(await require_docloader.getRemoteDocument("https://a.example/start", response, (url) => {
1348
+ alternates.push(url);
1349
+ (0, node_assert.deepStrictEqual)(recorder.events.at(-1), "cancel #1");
1350
+ (0, node_assert.ok)(response.bodyUsed);
1351
+ return Promise.resolve(placeholder);
1352
+ }) === placeholder);
1353
+ (0, node_assert.deepStrictEqual)(alternates, ["https://b.example/object"]);
1354
+ (0, node_assert.deepStrictEqual)(recorder.events, ["fetch https://a.example/start #1", "cancel #1"]);
1355
+ });
2106
1356
  //#endregion