@fedify/vocab-runtime 2.5.0-dev.2448 → 2.5.0-dev.2494
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deno.json +5 -2
- package/dist/{docloader-BBnBpbsG.d.cts → docloader-DD6binpN.d.cts} +4 -8
- package/dist/{docloader-BBnBpbsG.d.ts → docloader-DD6binpN.d.ts} +4 -8
- package/dist/internal/jsonld-cache.cjs +1 -1
- package/dist/internal/jsonld-cache.d.cts +1 -1
- package/dist/internal/jsonld-cache.d.ts +1 -1
- package/dist/internal/jsonld-cache.js +1 -1
- package/dist/internal/portable-dereference.cjs +1 -1
- package/dist/internal/portable-dereference.d.cts +2 -2
- package/dist/internal/portable-dereference.d.ts +2 -2
- package/dist/internal/portable-dereference.js +1 -1
- package/dist/internal.cjs +6 -0
- package/dist/internal.d.cts +108 -0
- package/dist/internal.d.ts +108 -0
- package/dist/internal.js +3 -0
- package/dist/mod.cjs +104 -38
- package/dist/mod.d.cts +3 -336
- package/dist/mod.d.ts +4 -336
- package/dist/mod.js +104 -38
- package/dist/{portable-BDydjp5H.d.ts → portable-1VSnSJFj.d.ts} +1 -1
- package/dist/{portable-Dfvcg_Wy.d.cts → portable-Cr7NwZwb.d.cts} +1 -1
- package/dist/public-fetch-Dvfvawei.cjs +527 -0
- package/dist/public-fetch-ziZ39L5I.js +510 -0
- package/dist/tests/{body-DP2u2wUX.cjs → body-0Or8U7lA.cjs} +1 -1
- package/dist/tests/{body-oV99TVdA.mjs → body-DfH6VTh9.mjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/decimal.test.cjs +4 -4
- package/dist/tests/decimal.test.mjs +4 -4
- package/dist/tests/{docloader-D3mA7aPJ.mjs → docloader-BZvFOza_.mjs} +106 -40
- package/dist/tests/{docloader-CwxZguqN.cjs → docloader-W_RHEh5W.cjs} +111 -39
- package/dist/tests/docloader.test.cjs +531 -1293
- package/dist/tests/docloader.test.mjs +472 -1252
- package/dist/tests/esm-Ds-eHTfY.mjs +1229 -0
- package/dist/tests/esm-Q2tdh5f3.cjs +1215 -0
- package/dist/tests/internal/portable-dereference.test.cjs +3 -3
- package/dist/tests/internal/portable-dereference.test.mjs +3 -3
- package/dist/tests/{jsonld-cache-BEXiiM-V.cjs → jsonld-cache-DKIWKxEX.cjs} +1 -1
- package/dist/tests/{jsonld-cache-DFnWsLuZ.mjs → jsonld-cache-KH40z9_7.mjs} +1 -1
- package/dist/tests/jsonld-cache.test.cjs +2 -2
- package/dist/tests/jsonld-cache.test.mjs +2 -2
- package/dist/tests/node-CbBR-th-.mjs +84 -0
- package/dist/tests/node-Ch29PDXR.cjs +89 -0
- package/dist/tests/pin-grant-CPrpk1l4.cjs +70 -0
- package/dist/tests/pin-grant-H5XhM1Xs.mjs +53 -0
- package/dist/tests/pin-grant.test.cjs +87 -0
- package/dist/tests/pin-grant.test.d.cts +1 -0
- package/dist/tests/pin-grant.test.d.mts +1 -0
- package/dist/tests/pin-grant.test.mjs +88 -0
- package/dist/tests/pinners/bun.test.cjs +166 -0
- package/dist/tests/pinners/bun.test.d.cts +1 -0
- package/dist/tests/pinners/bun.test.d.mts +1 -0
- package/dist/tests/pinners/bun.test.mjs +167 -0
- package/dist/tests/pinners/node.test.cjs +269 -0
- package/dist/tests/pinners/node.test.d.cts +1 -0
- package/dist/tests/pinners/node.test.d.mts +1 -0
- package/dist/tests/pinners/node.test.mjs +268 -0
- package/dist/tests/pinners/pinners.test.cjs +196 -0
- package/dist/tests/pinners/pinners.test.d.cts +1 -0
- package/dist/tests/pinners/pinners.test.d.mts +1 -0
- package/dist/tests/pinners/pinners.test.mjs +197 -0
- package/dist/tests/pinners/testdata/node-child.cjs +135 -0
- package/dist/tests/pinners/testdata/node-child.d.cts +1 -0
- package/dist/tests/pinners/testdata/node-child.d.mts +1 -0
- package/dist/tests/pinners/testdata/node-child.mjs +134 -0
- package/dist/tests/pinners-DLH_npLf.cjs +220 -0
- package/dist/tests/pinners-tGcl49uF.mjs +203 -0
- package/dist/tests/{portable-dereference-DaTBBvCj.cjs → portable-dereference-BUoDzMxk.cjs} +2 -2
- package/dist/tests/{portable-dereference-sGRs_qdT.mjs → portable-dereference-Du2k3ngE.mjs} +2 -2
- package/dist/tests/{portable-media-B-oCeotd.mjs → portable-media-CqCq3wqj.mjs} +3 -3
- package/dist/tests/{portable-media-D7ClSjFO.cjs → portable-media-Ls_EOkMN.cjs} +3 -3
- package/dist/tests/portable-media.test.cjs +1 -1
- package/dist/tests/portable-media.test.mjs +1 -1
- package/dist/tests/portable-workers.test.cjs +2 -2
- package/dist/tests/portable-workers.test.mjs +2 -2
- package/dist/tests/public-fetch-BGj5pzUD.cjs +186 -0
- package/dist/tests/public-fetch-Cxo3Yzdf.mjs +170 -0
- package/dist/tests/public-fetch.test.cjs +1073 -0
- package/dist/tests/public-fetch.test.d.cts +1 -0
- package/dist/tests/public-fetch.test.d.mts +1 -0
- package/dist/tests/public-fetch.test.mjs +1074 -0
- package/dist/tests/{request-BlLYI4x3.mjs → request-Bo8cJJhV.mjs} +2 -2
- package/dist/tests/{request-B77_oZnk.cjs → request-Cb6h3X72.cjs} +2 -2
- package/dist/tests/request.test.cjs +2 -2
- package/dist/tests/request.test.mjs +2 -2
- package/dist/tests/support-Bj44xlQu.mjs +272 -0
- package/dist/tests/support-BrdlbLJk.cjs +363 -0
- package/dist/tests/{url-BNakuZ8k.cjs → url-Bdj-rTcC.cjs} +88 -12
- package/dist/tests/{url-DMxmp7ZG.mjs → url-COj5Q8mb.mjs} +65 -13
- package/dist/tests/url.test.cjs +4 -1
- package/dist/tests/url.test.mjs +4 -1
- package/dist/url-4EE56sNP.d.cts +352 -0
- package/dist/url-4EE56sNP.d.ts +352 -0
- package/dist/{url-DrGTR8yv.cjs → url-DIKZnaJv.cjs} +88 -12
- package/dist/{url-Dzyp-NsC.js → url-mWveR5GA.js} +65 -13
- package/package.json +14 -3
- package/src/docloader.test.ts +696 -48
- package/src/docloader.ts +186 -75
- package/src/internal.ts +13 -0
- package/src/pin-grant.test.ts +118 -0
- package/src/pin-grant.ts +99 -0
- package/src/pinners/bun.test.ts +278 -0
- package/src/pinners/bun.ts +223 -0
- package/src/pinners/mod.ts +59 -0
- package/src/pinners/node.test.ts +415 -0
- package/src/pinners/node.ts +144 -0
- package/src/pinners/pinners.test.ts +316 -0
- package/src/pinners/testdata/node-child.ts +147 -0
- package/src/pinners/testdata/proxy-child.ts +82 -0
- package/src/pinners/testdata/support.ts +343 -0
- package/src/pinners/testdata/tls-cert.pem +12 -0
- package/src/pinners/testdata/tls-child.ts +88 -0
- package/src/pinners/testdata/tls-key.pem +5 -0
- package/src/pinners/types.ts +48 -0
- package/src/public-fetch.test.ts +1286 -0
- package/src/public-fetch.ts +338 -0
- package/src/testdata/public-fetch-deno-worker.ts +74 -0
- package/src/url.test.ts +11 -0
- package/src/url.ts +113 -22
- package/tsdown.config.ts +7 -2
|
@@ -1,1224 +1,15 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const require_url = require("./url-BNakuZ8k.cjs");
|
|
1
|
+
const require_request = require("./request-Cb6h3X72.cjs");
|
|
2
|
+
const require_url = require("./url-Bdj-rTcC.cjs");
|
|
4
3
|
const require_contexts = require("./contexts-C9Lj11SC.cjs");
|
|
5
|
-
const
|
|
4
|
+
const require_public_fetch = require("./public-fetch-BGj5pzUD.cjs");
|
|
5
|
+
const require_docloader = require("./docloader-W_RHEh5W.cjs");
|
|
6
|
+
const require_esm = require("./esm-Q2tdh5f3.cjs");
|
|
6
7
|
const require_jsonld = require("./jsonld-CmDhHNTx.cjs");
|
|
7
8
|
let node_zlib = require("node:zlib");
|
|
8
9
|
let _logtape_logtape = require("@logtape/logtape");
|
|
9
|
-
let node_dns_promises = require("node:dns/promises");
|
|
10
|
-
node_dns_promises = require_rolldown_runtime.__toESM(node_dns_promises, 1);
|
|
11
10
|
let node_assert = require("node:assert");
|
|
12
11
|
let _fedify_fixture = require("@fedify/fixture");
|
|
13
12
|
let node_http = require("node:http");
|
|
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_rolldown_runtime.__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_rolldown_runtime.__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 = /* @__PURE__ */ new Set([
|
|
122
|
-
"boolean",
|
|
123
|
-
"number",
|
|
124
|
-
"null",
|
|
125
|
-
"string",
|
|
126
|
-
"undefined"
|
|
127
|
-
]);
|
|
128
|
-
const referenceTypes = /* @__PURE__ */ new Set([
|
|
129
|
-
"array",
|
|
130
|
-
"function",
|
|
131
|
-
"object",
|
|
132
|
-
"symbol"
|
|
133
|
-
]);
|
|
134
|
-
const detectableTypes = /* @__PURE__ */ new Set([
|
|
135
|
-
"boolean",
|
|
136
|
-
"function",
|
|
137
|
-
"number",
|
|
138
|
-
"string",
|
|
139
|
-
"symbol"
|
|
140
|
-
]);
|
|
141
|
-
const typeConstructors = /* @__PURE__ */ 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 = /* @__PURE__ */ 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
|
-
};
|
|
691
|
-
//#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;
|
|
705
|
-
var _a;
|
|
706
|
-
var _Route_responseSubscriptions;
|
|
707
|
-
var _Route_validate;
|
|
708
|
-
var _Route_sanitize;
|
|
709
|
-
var _Route_generateMatcher;
|
|
710
|
-
var _Route_limit;
|
|
711
|
-
var _Route_delayResponse;
|
|
712
|
-
var RouteConfigWrapper = class {
|
|
713
|
-
constructor(config) {
|
|
714
|
-
Object.assign(this, config);
|
|
715
|
-
}
|
|
716
|
-
};
|
|
717
|
-
function isBodyInit(body) {
|
|
718
|
-
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;
|
|
719
|
-
}
|
|
720
|
-
function sanitizeStatus(status) {
|
|
721
|
-
if (status === 0) return 200;
|
|
722
|
-
if (!status) return 200;
|
|
723
|
-
if (typeof status === "number" && parseInt(String(status), 10) !== status && status >= 200 || status < 600) return status;
|
|
724
|
-
throw new TypeError(`fetch-mock: Invalid status ${status} passed on response object.
|
|
725
|
-
To respond with a JSON object that has status as a property assign the object to body
|
|
726
|
-
e.g. {"body": {"status: "registered"}}`);
|
|
727
|
-
}
|
|
728
|
-
var Route = class {
|
|
729
|
-
constructor(config) {
|
|
730
|
-
_Route_instances.add(this);
|
|
731
|
-
_Route_responseSubscriptions.set(this, void 0);
|
|
732
|
-
this.init(config);
|
|
733
|
-
}
|
|
734
|
-
init(config) {
|
|
735
|
-
this.config = config;
|
|
736
|
-
__classPrivateFieldSet(this, _Route_responseSubscriptions, [], "f");
|
|
737
|
-
__classPrivateFieldGet(this, _Route_instances, "m", _Route_sanitize).call(this);
|
|
738
|
-
__classPrivateFieldGet(this, _Route_instances, "m", _Route_validate).call(this);
|
|
739
|
-
__classPrivateFieldGet(this, _Route_instances, "m", _Route_generateMatcher).call(this);
|
|
740
|
-
__classPrivateFieldGet(this, _Route_instances, "m", _Route_limit).call(this);
|
|
741
|
-
__classPrivateFieldGet(this, _Route_instances, "m", _Route_delayResponse).call(this);
|
|
742
|
-
}
|
|
743
|
-
reset() {}
|
|
744
|
-
waitFor(awaitedRoutes) {
|
|
745
|
-
const { response } = this.config;
|
|
746
|
-
this.config.response = Promise.all(awaitedRoutes.map((awaitedRoute) => new Promise((res) => awaitedRoute.onRespond(() => {
|
|
747
|
-
res(void 0);
|
|
748
|
-
})))).then(() => response);
|
|
749
|
-
}
|
|
750
|
-
onRespond(func) {
|
|
751
|
-
__classPrivateFieldGet(this, _Route_responseSubscriptions, "f").push(func);
|
|
752
|
-
}
|
|
753
|
-
constructResponse(responseInput) {
|
|
754
|
-
const responseOptions = this.constructResponseOptions(responseInput);
|
|
755
|
-
const body = this.constructResponseBody(responseInput, responseOptions);
|
|
756
|
-
const responsePackage = {
|
|
757
|
-
response: new this.config.Response(body, responseOptions),
|
|
758
|
-
responseOptions,
|
|
759
|
-
responseInput
|
|
760
|
-
};
|
|
761
|
-
__classPrivateFieldGet(this, _Route_responseSubscriptions, "f").forEach((func) => func());
|
|
762
|
-
return responsePackage;
|
|
763
|
-
}
|
|
764
|
-
constructResponseOptions(responseInput) {
|
|
765
|
-
const options = responseInput.options || {};
|
|
766
|
-
options.status = sanitizeStatus(responseInput.status);
|
|
767
|
-
options.statusText = statusTextMap[options.status];
|
|
768
|
-
options.headers = new this.config.Headers(responseInput.headers);
|
|
769
|
-
return options;
|
|
770
|
-
}
|
|
771
|
-
constructResponseBody(responseInput, responseOptions) {
|
|
772
|
-
let body = responseInput.body;
|
|
773
|
-
if (!isBodyInit(body)) if (typeof body === "undefined") body = null;
|
|
774
|
-
else if (typeof body === "object") {
|
|
775
|
-
body = JSON.stringify(body);
|
|
776
|
-
if (!responseOptions.headers.has("Content-Type")) responseOptions.headers.set("Content-Type", "application/json");
|
|
777
|
-
} else throw new TypeError("Invalid body provided to construct response");
|
|
778
|
-
if (this.config.includeContentLength && !responseOptions.headers.has("Content-Length") && !(body instanceof ReadableStream) && !(body instanceof FormData)) {
|
|
779
|
-
let length = 0;
|
|
780
|
-
if (body instanceof Blob) length = body.size;
|
|
781
|
-
else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body) || body instanceof DataView) length = body.byteLength;
|
|
782
|
-
else if (body instanceof URLSearchParams) length = body.toString().length;
|
|
783
|
-
else if (typeof body === "string" || body instanceof String) length = body.length;
|
|
784
|
-
responseOptions.headers.set("Content-Length", length.toString());
|
|
785
|
-
}
|
|
786
|
-
return body;
|
|
787
|
-
}
|
|
788
|
-
static defineMatcher(matcher) {
|
|
789
|
-
_a.registeredMatchers.push(matcher);
|
|
790
|
-
}
|
|
791
|
-
};
|
|
792
|
-
_a = Route, _Route_responseSubscriptions = /* @__PURE__ */ new WeakMap(), _Route_instances = /* @__PURE__ */ new WeakSet(), _Route_validate = function _Route_validate() {
|
|
793
|
-
if (["matched", "unmatched"].includes(this.config.name)) throw new Error(`fetch-mock: Routes cannot use the reserved name \`${this.config.name}\``);
|
|
794
|
-
if (!("response" in this.config)) throw new Error("fetch-mock: Each route must define a response");
|
|
795
|
-
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 '*'");
|
|
796
|
-
}, _Route_sanitize = function _Route_sanitize() {
|
|
797
|
-
if (this.config.method) this.config.method = this.config.method.toLowerCase();
|
|
798
|
-
}, _Route_generateMatcher = function _Route_generateMatcher() {
|
|
799
|
-
const activeMatchers = _a.registeredMatchers.filter(({ name }) => name in this.config).map(({ matcher, usesBody }) => ({
|
|
800
|
-
matcher: matcher(this.config),
|
|
801
|
-
usesBody
|
|
802
|
-
}));
|
|
803
|
-
this.config.usesBody = activeMatchers.some(({ usesBody }) => usesBody);
|
|
804
|
-
this.matcher = (normalizedRequest) => activeMatchers.every(({ matcher }) => matcher(normalizedRequest));
|
|
805
|
-
}, _Route_limit = function _Route_limit() {
|
|
806
|
-
if (!this.config.repeat) return;
|
|
807
|
-
const originalMatcher = this.matcher;
|
|
808
|
-
let timesLeft = this.config.repeat;
|
|
809
|
-
this.matcher = (callLog) => {
|
|
810
|
-
if (timesLeft && originalMatcher(callLog)) {
|
|
811
|
-
timesLeft--;
|
|
812
|
-
return true;
|
|
813
|
-
}
|
|
814
|
-
};
|
|
815
|
-
this.reset = () => {
|
|
816
|
-
timesLeft = this.config.repeat;
|
|
817
|
-
};
|
|
818
|
-
}, _Route_delayResponse = function _Route_delayResponse() {
|
|
819
|
-
if (this.config.delay) {
|
|
820
|
-
const { response } = this.config;
|
|
821
|
-
this.config.response = () => {
|
|
822
|
-
return new Promise((res) => setTimeout(() => res(response), this.config.delay));
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
};
|
|
826
|
-
Route.registeredMatchers = [];
|
|
827
|
-
builtInMatchers.forEach(Route.defineMatcher);
|
|
828
|
-
//#endregion
|
|
829
|
-
//#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/Router.js
|
|
830
|
-
const responseConfigProps = [
|
|
831
|
-
"body",
|
|
832
|
-
"headers",
|
|
833
|
-
"throws",
|
|
834
|
-
"status",
|
|
835
|
-
"redirectUrl"
|
|
836
|
-
];
|
|
837
|
-
function nameToOptions(options) {
|
|
838
|
-
return typeof options === "string" ? { name: options } : options;
|
|
839
|
-
}
|
|
840
|
-
function isPromise(response) {
|
|
841
|
-
return typeof response.then === "function";
|
|
842
|
-
}
|
|
843
|
-
function normalizeResponseInput(responseInput) {
|
|
844
|
-
if (typeof responseInput === "number") return { status: responseInput };
|
|
845
|
-
else if (typeof responseInput === "string" || shouldSendAsObject(responseInput)) return { body: responseInput };
|
|
846
|
-
return responseInput;
|
|
847
|
-
}
|
|
848
|
-
function shouldSendAsObject(responseInput) {
|
|
849
|
-
if (responseConfigProps.some((prop) => prop in responseInput)) {
|
|
850
|
-
if (Object.keys(responseInput).every((key) => responseConfigProps.includes(key))) return false;
|
|
851
|
-
return true;
|
|
852
|
-
}
|
|
853
|
-
return true;
|
|
854
|
-
}
|
|
855
|
-
function throwSpecExceptions({ url, options: { headers, method, body } }) {
|
|
856
|
-
if (headers) Object.entries(headers).forEach(([key]) => {
|
|
857
|
-
if (/\s/.test(key)) throw new TypeError("Invalid name");
|
|
858
|
-
});
|
|
859
|
-
if (hasCredentialsInUrl(url)) throw new TypeError(`Request cannot be constructed from a URL that includes credentials: ${url}`);
|
|
860
|
-
if (["get", "head"].includes(method) && body) throw new TypeError("Request with GET/HEAD method cannot have body.");
|
|
861
|
-
}
|
|
862
|
-
const resolveUntilResponseConfig = async (callLog) => {
|
|
863
|
-
let response = callLog.route.config.response;
|
|
864
|
-
while (true) if (typeof response === "function") response = response(callLog);
|
|
865
|
-
else if (isPromise(response)) response = await response;
|
|
866
|
-
else return response;
|
|
867
|
-
};
|
|
868
|
-
var Router = class {
|
|
869
|
-
constructor(fetchMockConfig, { routes, fallbackRoute } = {}) {
|
|
870
|
-
this.config = fetchMockConfig;
|
|
871
|
-
this.routes = routes || [];
|
|
872
|
-
this.fallbackRoute = fallbackRoute;
|
|
873
|
-
}
|
|
874
|
-
needsToReadBody(request) {
|
|
875
|
-
return Boolean(request && this.routes.some((route) => route.config.usesBody));
|
|
876
|
-
}
|
|
877
|
-
execute(callLog) {
|
|
878
|
-
throwSpecExceptions(callLog);
|
|
879
|
-
return new Promise(async (resolve, reject) => {
|
|
880
|
-
const { url, options, request, pendingPromises } = callLog;
|
|
881
|
-
let eventListenerAbortController;
|
|
882
|
-
if (callLog.signal) {
|
|
883
|
-
const abort = () => {
|
|
884
|
-
const error = new DOMException("The operation was aborted.", "AbortError");
|
|
885
|
-
const requestBody = request?.body || options?.body;
|
|
886
|
-
if (requestBody instanceof ReadableStream) if (requestBody.locked) console.warn("fetch-mock: Locked request body can't be cancelled");
|
|
887
|
-
else requestBody.cancel(error);
|
|
888
|
-
if (callLog?.response?.body) if (callLog.response.body.locked) console.warn("fetch-mock: Locked response body can't be cancelled");
|
|
889
|
-
else callLog.response.body.cancel(error);
|
|
890
|
-
reject(error);
|
|
891
|
-
};
|
|
892
|
-
if (callLog.signal.aborted) abort();
|
|
893
|
-
eventListenerAbortController = new AbortController();
|
|
894
|
-
callLog.signal.addEventListener("abort", abort, {
|
|
895
|
-
once: true,
|
|
896
|
-
signal: eventListenerAbortController.signal
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
if (this.needsToReadBody(request)) options.body = await options.body;
|
|
900
|
-
const route = (this.fallbackRoute ? [...this.routes, this.fallbackRoute] : this.routes).find((route) => route.matcher(callLog));
|
|
901
|
-
if (route) try {
|
|
902
|
-
callLog.route = route;
|
|
903
|
-
const { response, responseOptions, responseInput } = await this.generateResponse(callLog);
|
|
904
|
-
const observableResponse = this.createObservableResponse(response, responseOptions, responseInput, url, pendingPromises);
|
|
905
|
-
callLog.response = response;
|
|
906
|
-
resolve(observableResponse);
|
|
907
|
-
} catch (err) {
|
|
908
|
-
reject(err);
|
|
909
|
-
} finally {
|
|
910
|
-
eventListenerAbortController?.abort();
|
|
911
|
-
}
|
|
912
|
-
else reject(/* @__PURE__ */ new Error(`fetch-mock: No response or fallback rule to cover ${options && options.method || "GET"} to ${url}`));
|
|
913
|
-
});
|
|
914
|
-
}
|
|
915
|
-
async generateResponse(callLog) {
|
|
916
|
-
const responseInput = await resolveUntilResponseConfig(callLog);
|
|
917
|
-
if (responseInput instanceof Response) return {
|
|
918
|
-
response: responseInput.clone(),
|
|
919
|
-
responseOptions: {},
|
|
920
|
-
responseInput: {}
|
|
921
|
-
};
|
|
922
|
-
const responseConfig = normalizeResponseInput(responseInput);
|
|
923
|
-
if (responseConfig.throws) throw responseConfig.throws;
|
|
924
|
-
return callLog.route.constructResponse(responseConfig);
|
|
925
|
-
}
|
|
926
|
-
createObservableResponse(response, responseConfig, responseInput, responseUrl, pendingPromises) {
|
|
927
|
-
return new Proxy(response, { get: (originalResponse, name) => {
|
|
928
|
-
if (responseInput.redirectUrl) {
|
|
929
|
-
if (name === "url") return responseInput.redirectUrl;
|
|
930
|
-
if (name === "redirected") return true;
|
|
931
|
-
} else {
|
|
932
|
-
if (name === "url") return responseUrl;
|
|
933
|
-
if (name === "redirected") return false;
|
|
934
|
-
}
|
|
935
|
-
if (responseInput.status === 0) {
|
|
936
|
-
if (name === "status") return 0;
|
|
937
|
-
if (name === "statusText") return "";
|
|
938
|
-
}
|
|
939
|
-
if (typeof response[name] === "function") return new Proxy(response[name], { apply: (func, thisArg, args) => {
|
|
940
|
-
const result = func.apply(response, args);
|
|
941
|
-
if (result.then) pendingPromises.push(result.catch(() => void 0));
|
|
942
|
-
return result;
|
|
943
|
-
} });
|
|
944
|
-
return originalResponse[name];
|
|
945
|
-
} });
|
|
946
|
-
}
|
|
947
|
-
addRoute(matcher, response, nameOrOptions) {
|
|
948
|
-
const config = {};
|
|
949
|
-
if (matcher instanceof RouteConfigWrapper) Object.assign(config, matcher);
|
|
950
|
-
if (isUrlMatcher(matcher)) config.url = matcher;
|
|
951
|
-
else if (isFunctionMatcher(matcher)) config.matcherFunction = matcher;
|
|
952
|
-
else Object.assign(config, matcher);
|
|
953
|
-
if (typeof response !== "undefined") config.response = response;
|
|
954
|
-
if (nameOrOptions) Object.assign(config, typeof nameOrOptions === "string" ? nameToOptions(nameOrOptions) : nameOrOptions);
|
|
955
|
-
const route = new Route({
|
|
956
|
-
...this.config,
|
|
957
|
-
...config
|
|
958
|
-
});
|
|
959
|
-
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.");
|
|
960
|
-
if (route.config.waitFor) {
|
|
961
|
-
const routeNamesToWaitFor = Array.isArray(route.config.waitFor) ? route.config.waitFor : [route.config.waitFor];
|
|
962
|
-
const routesToAwait = [];
|
|
963
|
-
routeNamesToWaitFor.forEach((routeName) => {
|
|
964
|
-
const routeToAwait = this.routes.find(({ config: { name: existingName } }) => routeName === existingName);
|
|
965
|
-
if (routeToAwait) routesToAwait.push(routeToAwait);
|
|
966
|
-
else throw new Error(`Cannot wait for route \`${routeName}\`: route of that name does not exist`);
|
|
967
|
-
});
|
|
968
|
-
route.waitFor(routesToAwait);
|
|
969
|
-
}
|
|
970
|
-
this.routes.push(route);
|
|
971
|
-
}
|
|
972
|
-
setFallback(response) {
|
|
973
|
-
if (this.fallbackRoute) console.warn("calling fetchMock.catch() twice - are you sure you want to overwrite the previous fallback response");
|
|
974
|
-
this.fallbackRoute = new Route({
|
|
975
|
-
matcherFunction: () => true,
|
|
976
|
-
response: response || "ok",
|
|
977
|
-
...this.config
|
|
978
|
-
});
|
|
979
|
-
this.fallbackRoute.config.isFallback = true;
|
|
980
|
-
}
|
|
981
|
-
removeRoutes({ names, includeSticky, includeFallback } = {}) {
|
|
982
|
-
includeFallback = includeFallback ?? true;
|
|
983
|
-
this.routes = this.routes.filter(({ config: { sticky, name } }) => {
|
|
984
|
-
if (sticky && !includeSticky) return true;
|
|
985
|
-
if (!names) return false;
|
|
986
|
-
return !names.includes(name);
|
|
987
|
-
});
|
|
988
|
-
if (includeFallback) delete this.fallbackRoute;
|
|
989
|
-
}
|
|
990
|
-
modifyRoute(routeName, options) {
|
|
991
|
-
const route = this.routes.find(({ config: { name } }) => name === routeName);
|
|
992
|
-
if (!route) throw new Error(`Cannot call modifyRoute() on route \`${routeName}\`: route of that name not found`);
|
|
993
|
-
if (route.config.sticky) throw new Error(`Cannot call modifyRoute() on route \`${routeName}\`: route is sticky and cannot be modified`);
|
|
994
|
-
if ("name" in options) throw new Error(`Cannot rename the route \`${routeName}\` as \`${options.name}\`: renaming routes is not supported`);
|
|
995
|
-
if ("sticky" in options) throw new Error(`Altering the stickiness of route \`${routeName}\` is not supported`);
|
|
996
|
-
const newConfig = {
|
|
997
|
-
...route.config,
|
|
998
|
-
...options
|
|
999
|
-
};
|
|
1000
|
-
Object.entries(options).forEach(([key, value]) => {
|
|
1001
|
-
if (value === null) delete newConfig[key];
|
|
1002
|
-
});
|
|
1003
|
-
route.init(newConfig);
|
|
1004
|
-
}
|
|
1005
|
-
};
|
|
1006
|
-
//#endregion
|
|
1007
|
-
//#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/CallHistory.js
|
|
1008
|
-
const isName = (filter) => typeof filter === "string" && /^[\da-zA-Z-]+$/.test(filter) && !["matched", "unmatched"].includes(filter);
|
|
1009
|
-
const isMatchedOrUnmatched = (filter) => typeof filter === "boolean" || ["matched", "unmatched"].includes(filter);
|
|
1010
|
-
var CallHistory = class {
|
|
1011
|
-
constructor(config, router) {
|
|
1012
|
-
this.callLogs = [];
|
|
1013
|
-
this.config = config;
|
|
1014
|
-
this.router = router;
|
|
1015
|
-
}
|
|
1016
|
-
recordCall(callLog) {
|
|
1017
|
-
this.callLogs.push(callLog);
|
|
1018
|
-
}
|
|
1019
|
-
clear() {
|
|
1020
|
-
this.callLogs.forEach(({ route }) => {
|
|
1021
|
-
if (route) route.reset();
|
|
1022
|
-
});
|
|
1023
|
-
this.callLogs = [];
|
|
1024
|
-
}
|
|
1025
|
-
async flush(waitForResponseMethods) {
|
|
1026
|
-
const queuedPromises = this.callLogs.flatMap((call) => call.pendingPromises);
|
|
1027
|
-
await Promise.allSettled(queuedPromises);
|
|
1028
|
-
if (waitForResponseMethods) {
|
|
1029
|
-
await Promise.resolve();
|
|
1030
|
-
await this.flush();
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
calls(filter, options) {
|
|
1034
|
-
let calls = [...this.callLogs];
|
|
1035
|
-
if (typeof filter === "undefined" && !options) return calls;
|
|
1036
|
-
if (isMatchedOrUnmatched(filter)) {
|
|
1037
|
-
if ([true, "matched"].includes(filter)) calls = calls.filter(({ route }) => !route?.config || !route.config.isFallback);
|
|
1038
|
-
else if ([false, "unmatched"].includes(filter)) calls = calls.filter(({ route }) => Boolean(route?.config && route.config.isFallback));
|
|
1039
|
-
if (!options) return calls;
|
|
1040
|
-
} else if (isName(filter)) {
|
|
1041
|
-
calls = calls.filter(({ route }) => route?.config?.name === filter);
|
|
1042
|
-
if (!options) return calls;
|
|
1043
|
-
} else if (isUrlMatcher(filter)) options = {
|
|
1044
|
-
url: filter,
|
|
1045
|
-
...options || {}
|
|
1046
|
-
};
|
|
1047
|
-
else options = {
|
|
1048
|
-
...filter,
|
|
1049
|
-
...options || {}
|
|
1050
|
-
};
|
|
1051
|
-
const { matcher } = new Route({
|
|
1052
|
-
response: "ok",
|
|
1053
|
-
...options
|
|
1054
|
-
});
|
|
1055
|
-
calls = calls.filter(({ url, options }) => {
|
|
1056
|
-
return matcher(createCallLogFromUrlAndOptions(url, options));
|
|
1057
|
-
});
|
|
1058
|
-
return calls;
|
|
1059
|
-
}
|
|
1060
|
-
called(filter, options) {
|
|
1061
|
-
return Boolean(this.calls(filter, options).length);
|
|
1062
|
-
}
|
|
1063
|
-
lastCall(filter, options) {
|
|
1064
|
-
return this.calls(filter, options).pop();
|
|
1065
|
-
}
|
|
1066
|
-
done(routeNames) {
|
|
1067
|
-
let routesToCheck = this.router.routes;
|
|
1068
|
-
if (routeNames) {
|
|
1069
|
-
routeNames = Array.isArray(routeNames) ? routeNames : [routeNames];
|
|
1070
|
-
routesToCheck = this.router.routes.filter(({ config: { name } }) => routeNames.includes(name));
|
|
1071
|
-
}
|
|
1072
|
-
return routesToCheck.map((route) => {
|
|
1073
|
-
const calls = this.callLogs.filter(({ route: routeApplied }) => routeApplied === route);
|
|
1074
|
-
if (!calls.length) {
|
|
1075
|
-
console.warn(`Warning: ${route.config.name} not called`);
|
|
1076
|
-
return false;
|
|
1077
|
-
}
|
|
1078
|
-
const expectedTimes = route.config.repeat;
|
|
1079
|
-
if (!expectedTimes) return true;
|
|
1080
|
-
const actualTimes = calls.length;
|
|
1081
|
-
if (expectedTimes > actualTimes) {
|
|
1082
|
-
console.warn(`Warning: ${route.config.name} only called ${actualTimes} times, but ${expectedTimes} expected`);
|
|
1083
|
-
return false;
|
|
1084
|
-
}
|
|
1085
|
-
return true;
|
|
1086
|
-
}).every((isDone) => isDone);
|
|
1087
|
-
}
|
|
1088
|
-
};
|
|
1089
|
-
//#endregion
|
|
1090
|
-
//#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/FetchMock.js
|
|
1091
|
-
const defaultFetchMockConfig = {
|
|
1092
|
-
includeContentLength: true,
|
|
1093
|
-
matchPartialBody: false,
|
|
1094
|
-
Request: globalThis.Request,
|
|
1095
|
-
Response: globalThis.Response,
|
|
1096
|
-
Headers: globalThis.Headers,
|
|
1097
|
-
fetch: globalThis.fetch
|
|
1098
|
-
};
|
|
1099
|
-
const defineShorthand = (shorthandOptions) => {
|
|
1100
|
-
function shorthand(matcher, response, options) {
|
|
1101
|
-
return this.route(matcher, response, Object.assign(options || {}, shorthandOptions));
|
|
1102
|
-
}
|
|
1103
|
-
return shorthand;
|
|
1104
|
-
};
|
|
1105
|
-
const defineGreedyShorthand = (shorthandOptions) => {
|
|
1106
|
-
return function(response, options) {
|
|
1107
|
-
return this.route("*", response, Object.assign(options || {}, shorthandOptions));
|
|
1108
|
-
};
|
|
1109
|
-
};
|
|
1110
|
-
//#endregion
|
|
1111
|
-
//#region ../../node_modules/.pnpm/fetch-mock@12.6.0/node_modules/fetch-mock/dist/esm/index.js
|
|
1112
|
-
var esm_default = new class FetchMock {
|
|
1113
|
-
constructor(config, router) {
|
|
1114
|
-
this.sticky = defineShorthand({ sticky: true });
|
|
1115
|
-
this.once = defineShorthand({ repeat: 1 });
|
|
1116
|
-
this.any = defineGreedyShorthand({});
|
|
1117
|
-
this.anyOnce = defineGreedyShorthand({ repeat: 1 });
|
|
1118
|
-
this.get = defineShorthand({ method: "get" });
|
|
1119
|
-
this.getOnce = defineShorthand({
|
|
1120
|
-
method: "get",
|
|
1121
|
-
repeat: 1
|
|
1122
|
-
});
|
|
1123
|
-
this.post = defineShorthand({ method: "post" });
|
|
1124
|
-
this.postOnce = defineShorthand({
|
|
1125
|
-
method: "post",
|
|
1126
|
-
repeat: 1
|
|
1127
|
-
});
|
|
1128
|
-
this.put = defineShorthand({ method: "put" });
|
|
1129
|
-
this.putOnce = defineShorthand({
|
|
1130
|
-
method: "put",
|
|
1131
|
-
repeat: 1
|
|
1132
|
-
});
|
|
1133
|
-
this.delete = defineShorthand({ method: "delete" });
|
|
1134
|
-
this.deleteOnce = defineShorthand({
|
|
1135
|
-
method: "delete",
|
|
1136
|
-
repeat: 1
|
|
1137
|
-
});
|
|
1138
|
-
this.head = defineShorthand({ method: "head" });
|
|
1139
|
-
this.headOnce = defineShorthand({
|
|
1140
|
-
method: "head",
|
|
1141
|
-
repeat: 1
|
|
1142
|
-
});
|
|
1143
|
-
this.patch = defineShorthand({ method: "patch" });
|
|
1144
|
-
this.patchOnce = defineShorthand({
|
|
1145
|
-
method: "patch",
|
|
1146
|
-
repeat: 1
|
|
1147
|
-
});
|
|
1148
|
-
this.config = config;
|
|
1149
|
-
this.router = new Router(this.config, {
|
|
1150
|
-
routes: router ? [...router.routes] : [],
|
|
1151
|
-
fallbackRoute: router ? router.fallbackRoute : null
|
|
1152
|
-
});
|
|
1153
|
-
this.callHistory = new CallHistory(this.config, this.router);
|
|
1154
|
-
this.fetchHandler = this.fetchHandler.bind(this);
|
|
1155
|
-
Object.assign(this.fetchHandler, { fetchMock: this });
|
|
1156
|
-
}
|
|
1157
|
-
createInstance() {
|
|
1158
|
-
return new FetchMock({ ...this.config }, this.router);
|
|
1159
|
-
}
|
|
1160
|
-
async fetchHandler(requestInput, requestInit) {
|
|
1161
|
-
let callLog;
|
|
1162
|
-
if (requestInput instanceof this.config.Request) callLog = await createCallLogFromRequest(requestInput, requestInit);
|
|
1163
|
-
else callLog = createCallLogFromUrlAndOptions(requestInput, requestInit);
|
|
1164
|
-
this.callHistory.recordCall(callLog);
|
|
1165
|
-
const responsePromise = this.router.execute(callLog);
|
|
1166
|
-
callLog.pendingPromises.push(responsePromise);
|
|
1167
|
-
return responsePromise;
|
|
1168
|
-
}
|
|
1169
|
-
route(matcher, response, options) {
|
|
1170
|
-
this.router.addRoute(matcher, response, options);
|
|
1171
|
-
return this;
|
|
1172
|
-
}
|
|
1173
|
-
catch(response) {
|
|
1174
|
-
this.router.setFallback(response);
|
|
1175
|
-
return this;
|
|
1176
|
-
}
|
|
1177
|
-
defineMatcher(matcher) {
|
|
1178
|
-
Route.defineMatcher(matcher);
|
|
1179
|
-
}
|
|
1180
|
-
removeRoutes(options) {
|
|
1181
|
-
this.router.removeRoutes(options);
|
|
1182
|
-
return this;
|
|
1183
|
-
}
|
|
1184
|
-
removeRoute(routeName) {
|
|
1185
|
-
this.router.removeRoutes({ names: [routeName] });
|
|
1186
|
-
return this;
|
|
1187
|
-
}
|
|
1188
|
-
modifyRoute(routeName, options) {
|
|
1189
|
-
this.router.modifyRoute(routeName, options);
|
|
1190
|
-
return this;
|
|
1191
|
-
}
|
|
1192
|
-
clearHistory() {
|
|
1193
|
-
this.callHistory.clear();
|
|
1194
|
-
return this;
|
|
1195
|
-
}
|
|
1196
|
-
mockGlobal() {
|
|
1197
|
-
globalThis.fetch = this.fetchHandler;
|
|
1198
|
-
return this;
|
|
1199
|
-
}
|
|
1200
|
-
unmockGlobal() {
|
|
1201
|
-
globalThis.fetch = this.config.fetch;
|
|
1202
|
-
return this;
|
|
1203
|
-
}
|
|
1204
|
-
hardReset(options) {
|
|
1205
|
-
this.clearHistory();
|
|
1206
|
-
this.removeRoutes(options);
|
|
1207
|
-
this.unmockGlobal();
|
|
1208
|
-
return this;
|
|
1209
|
-
}
|
|
1210
|
-
spy(matcher, name) {
|
|
1211
|
-
const boundFetch = this.config.fetch.bind(globalThis);
|
|
1212
|
-
if (matcher) this.route(matcher, ({ args }) => boundFetch(...args), name);
|
|
1213
|
-
else this.catch(({ args }) => boundFetch(...args));
|
|
1214
|
-
return this;
|
|
1215
|
-
}
|
|
1216
|
-
spyGlobal() {
|
|
1217
|
-
this.mockGlobal();
|
|
1218
|
-
return this.spy();
|
|
1219
|
-
}
|
|
1220
|
-
}({ ...defaultFetchMockConfig });
|
|
1221
|
-
//#endregion
|
|
1222
13
|
//#region src/docloader.test.ts
|
|
1223
14
|
(0, _fedify_fixture.test)("getRemoteDocument() forwards options to alternate documents", async () => {
|
|
1224
15
|
const records = [];
|
|
@@ -1266,6 +57,24 @@ var esm_default = new class FetchMock {
|
|
|
1266
57
|
await (0, _logtape_logtape.reset)();
|
|
1267
58
|
}
|
|
1268
59
|
});
|
|
60
|
+
const unpinnedPublicFetch = require_public_fetch.createPublicFetch({
|
|
61
|
+
pinner: () => Promise.resolve(null),
|
|
62
|
+
lookup: () => Promise.reject(/* @__PURE__ */ new Error("Unexpected default lookup"))
|
|
63
|
+
});
|
|
64
|
+
const PUBLIC_ADDRESS = {
|
|
65
|
+
address: "93.184.215.14",
|
|
66
|
+
family: 4
|
|
67
|
+
};
|
|
68
|
+
const PRIVATE_ADDRESS = {
|
|
69
|
+
address: "10.0.0.1",
|
|
70
|
+
family: 4
|
|
71
|
+
};
|
|
72
|
+
function lookupTestAddresses(hostname) {
|
|
73
|
+
return Promise.resolve([hostname === "private.example" ? PRIVATE_ADDRESS : PUBLIC_ADDRESS]);
|
|
74
|
+
}
|
|
75
|
+
function openUnpinnedHop(url) {
|
|
76
|
+
return unpinnedPublicFetch.openPublicFetch(url, { lookup: lookupTestAddresses });
|
|
77
|
+
}
|
|
1269
78
|
(0, _fedify_fixture.test)("new FetchError()", () => {
|
|
1270
79
|
const e = new require_request.FetchError("https://example.com/", "An error message.");
|
|
1271
80
|
(0, node_assert.deepStrictEqual)(e.name, "FetchError");
|
|
@@ -1280,9 +89,9 @@ var esm_default = new class FetchMock {
|
|
|
1280
89
|
(0, node_assert.deepStrictEqual)(e2.response.status, 410);
|
|
1281
90
|
});
|
|
1282
91
|
(0, _fedify_fixture.test)("getDocumentLoader()", async (t) => {
|
|
1283
|
-
const fetchDocumentLoader = require_docloader.
|
|
1284
|
-
esm_default.spyGlobal();
|
|
1285
|
-
esm_default.get("https://example.com/object", { body: {
|
|
92
|
+
const fetchDocumentLoader = require_docloader.createDocumentLoader({}, { openHop: openUnpinnedHop });
|
|
93
|
+
require_esm.esm_default.spyGlobal();
|
|
94
|
+
require_esm.esm_default.get("https://example.com/object", { body: {
|
|
1286
95
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1287
96
|
id: "https://example.com/object",
|
|
1288
97
|
name: "Fetched object",
|
|
@@ -1300,7 +109,7 @@ var esm_default = new class FetchMock {
|
|
|
1300
109
|
}
|
|
1301
110
|
});
|
|
1302
111
|
});
|
|
1303
|
-
esm_default.get("https://example.com/link-ctx", {
|
|
112
|
+
require_esm.esm_default.get("https://example.com/link-ctx", {
|
|
1304
113
|
body: {
|
|
1305
114
|
id: "https://example.com/link-ctx",
|
|
1306
115
|
name: "Fetched object",
|
|
@@ -1311,15 +120,15 @@ var esm_default = new class FetchMock {
|
|
|
1311
120
|
Link: "<https://www.w3.org/ns/activitystreams>; rel=\"http://www.w3.org/ns/json-ld#context\"; type=\"application/ld+json\""
|
|
1312
121
|
}
|
|
1313
122
|
});
|
|
1314
|
-
esm_default.get("https://example.com/link-obj", { headers: {
|
|
123
|
+
require_esm.esm_default.get("https://example.com/link-obj", { headers: {
|
|
1315
124
|
"Content-Type": "text/html; charset=utf-8",
|
|
1316
125
|
Link: "<https://example.com/object>; rel=\"alternate\"; type=\"application/activity+json\""
|
|
1317
126
|
} });
|
|
1318
|
-
esm_default.get("https://example.com/link-obj-relative", { headers: {
|
|
127
|
+
require_esm.esm_default.get("https://example.com/link-obj-relative", { headers: {
|
|
1319
128
|
"Content-Type": "text/html; charset=utf-8",
|
|
1320
129
|
Link: "</object>; rel=\"alternate\"; type=\"application/activity+json\""
|
|
1321
130
|
} });
|
|
1322
|
-
esm_default.get("https://example.com/obj-w-wrong-link", {
|
|
131
|
+
require_esm.esm_default.get("https://example.com/obj-w-wrong-link", {
|
|
1323
132
|
body: {
|
|
1324
133
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1325
134
|
id: "https://example.com/obj-w-wrong-link",
|
|
@@ -1385,7 +194,7 @@ var esm_default = new class FetchMock {
|
|
|
1385
194
|
}
|
|
1386
195
|
});
|
|
1387
196
|
});
|
|
1388
|
-
esm_default.get("https://example.com/html-link", {
|
|
197
|
+
require_esm.esm_default.get("https://example.com/html-link", {
|
|
1389
198
|
body: `<html>
|
|
1390
199
|
<head>
|
|
1391
200
|
<meta charset=utf-8>
|
|
@@ -1409,7 +218,7 @@ var esm_default = new class FetchMock {
|
|
|
1409
218
|
}
|
|
1410
219
|
});
|
|
1411
220
|
});
|
|
1412
|
-
esm_default.get("https://example.com/xhtml-link", {
|
|
221
|
+
require_esm.esm_default.get("https://example.com/xhtml-link", {
|
|
1413
222
|
body: `<html>
|
|
1414
223
|
<head>
|
|
1415
224
|
<meta charset="utf-8" />
|
|
@@ -1433,7 +242,7 @@ var esm_default = new class FetchMock {
|
|
|
1433
242
|
}
|
|
1434
243
|
});
|
|
1435
244
|
});
|
|
1436
|
-
esm_default.get("https://example.com/html-a", {
|
|
245
|
+
require_esm.esm_default.get("https://example.com/html-a", {
|
|
1437
246
|
body: `<html>
|
|
1438
247
|
<head>
|
|
1439
248
|
<meta charset=utf-8>
|
|
@@ -1459,7 +268,7 @@ var esm_default = new class FetchMock {
|
|
|
1459
268
|
}
|
|
1460
269
|
});
|
|
1461
270
|
});
|
|
1462
|
-
esm_default.get("https://example.com/html-no-alternate", {
|
|
271
|
+
require_esm.esm_default.get("https://example.com/html-no-alternate", {
|
|
1463
272
|
body: `<!DOCTYPE html>
|
|
1464
273
|
<html>
|
|
1465
274
|
<head>
|
|
@@ -1480,7 +289,7 @@ var esm_default = new class FetchMock {
|
|
|
1480
289
|
return true;
|
|
1481
290
|
});
|
|
1482
291
|
});
|
|
1483
|
-
esm_default.get("https://example.com/wrong-content-type", {
|
|
292
|
+
require_esm.esm_default.get("https://example.com/wrong-content-type", {
|
|
1484
293
|
body: {
|
|
1485
294
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1486
295
|
id: "https://example.com/wrong-content-type",
|
|
@@ -1501,7 +310,7 @@ var esm_default = new class FetchMock {
|
|
|
1501
310
|
}
|
|
1502
311
|
});
|
|
1503
312
|
});
|
|
1504
|
-
esm_default.get("https://example.com/large-html", {
|
|
313
|
+
require_esm.esm_default.get("https://example.com/large-html", {
|
|
1505
314
|
body: "<!DOCTYPE html>",
|
|
1506
315
|
headers: {
|
|
1507
316
|
"Content-Length": String(1048577),
|
|
@@ -1532,7 +341,7 @@ var esm_default = new class FetchMock {
|
|
|
1532
341
|
}), require_request.FetchError);
|
|
1533
342
|
(0, node_assert.deepStrictEqual)(canceled, true);
|
|
1534
343
|
});
|
|
1535
|
-
esm_default.get("https://example.com/404", { status: 404 });
|
|
344
|
+
require_esm.esm_default.get("https://example.com/404", { status: 404 });
|
|
1536
345
|
await t.step("not ok", async () => {
|
|
1537
346
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/404"), require_request.FetchError, "HTTP 404: https://example.com/404");
|
|
1538
347
|
});
|
|
@@ -1558,7 +367,7 @@ var esm_default = new class FetchMock {
|
|
|
1558
367
|
}]);
|
|
1559
368
|
(0, node_assert.deepStrictEqual)((await require_jsonld.jsonld_default.compact(expanded, context, { documentLoader: fetchDocumentLoader })).attributionDomains, ["blog.example"]);
|
|
1560
369
|
});
|
|
1561
|
-
esm_default.get("https://www.w3.org/ns/cid/v1", { status: 503 });
|
|
370
|
+
require_esm.esm_default.get("https://www.w3.org/ns/cid/v1", { status: 503 });
|
|
1562
371
|
await t.step("preloaded CID v1 context", async () => {
|
|
1563
372
|
const url = "https://www.w3.org/ns/cid/v1";
|
|
1564
373
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
|
|
@@ -1583,7 +392,7 @@ var esm_default = new class FetchMock {
|
|
|
1583
392
|
} }
|
|
1584
393
|
});
|
|
1585
394
|
});
|
|
1586
|
-
esm_default.get("https://w3id.org/fep/7aa9", { status: 502 });
|
|
395
|
+
require_esm.esm_default.get("https://w3id.org/fep/7aa9", { status: 502 });
|
|
1587
396
|
await t.step("preloaded FEP-7aa9 context", async () => {
|
|
1588
397
|
const url = "https://w3id.org/fep/7aa9";
|
|
1589
398
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
|
|
@@ -1612,9 +421,9 @@ var esm_default = new class FetchMock {
|
|
|
1612
421
|
}
|
|
1613
422
|
} }
|
|
1614
423
|
});
|
|
1615
|
-
(0, node_assert.deepStrictEqual)(esm_default.callHistory.calls(url).length, 0);
|
|
424
|
+
(0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls(url).length, 0);
|
|
1616
425
|
});
|
|
1617
|
-
esm_default.get("https://w3id.org/fep/6757", { status: 502 });
|
|
426
|
+
require_esm.esm_default.get("https://w3id.org/fep/6757", { status: 502 });
|
|
1618
427
|
await t.step("preloaded FEP-6757 context", async () => {
|
|
1619
428
|
const url = "https://w3id.org/fep/6757";
|
|
1620
429
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
|
|
@@ -1641,9 +450,9 @@ var esm_default = new class FetchMock {
|
|
|
1641
450
|
"http://purl.org/dc/terms/license": [{ "@id": "https://creativecommons.org/licenses/by/4.0/" }],
|
|
1642
451
|
"https://w3id.org/fep/6757#preferredLicense": [{ "@id": "https://creativecommons.org/licenses/by-nc/4.0/" }]
|
|
1643
452
|
}]);
|
|
1644
|
-
(0, node_assert.deepStrictEqual)(esm_default.callHistory.calls(url).length, 0);
|
|
453
|
+
(0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls(url).length, 0);
|
|
1645
454
|
});
|
|
1646
|
-
esm_default.get("https://w3id.org/fep/22cd", { status: 502 });
|
|
455
|
+
require_esm.esm_default.get("https://w3id.org/fep/22cd", { status: 502 });
|
|
1647
456
|
await t.step("preloaded FEP-22cd context", async () => {
|
|
1648
457
|
const url = "https://w3id.org/fep/22cd";
|
|
1649
458
|
const articleId = "https://example.com/articles/1";
|
|
@@ -1719,17 +528,17 @@ var esm_default = new class FetchMock {
|
|
|
1719
528
|
"https://schema.org/isBasedOn": [{ "@id": revisionId }]
|
|
1720
529
|
}]
|
|
1721
530
|
}]);
|
|
1722
|
-
(0, node_assert.deepStrictEqual)(esm_default.callHistory.calls(url).length, 0);
|
|
1723
|
-
(0, node_assert.deepStrictEqual)(esm_default.callHistory.calls("https://www.w3.org/ns/activitystreams").length, 0);
|
|
531
|
+
(0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls(url).length, 0);
|
|
532
|
+
(0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls("https://www.w3.org/ns/activitystreams").length, 0);
|
|
1724
533
|
});
|
|
1725
534
|
await t.step("deny non-HTTP/HTTPS", async () => {
|
|
1726
535
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("ftp://localhost"), require_url.UrlError);
|
|
1727
536
|
});
|
|
1728
|
-
esm_default.get("https://example.com/localhost-redirect", {
|
|
537
|
+
require_esm.esm_default.get("https://example.com/localhost-redirect", {
|
|
1729
538
|
status: 302,
|
|
1730
539
|
headers: { Location: "https://localhost/object" }
|
|
1731
540
|
});
|
|
1732
|
-
esm_default.get("https://example.com/localhost-link", {
|
|
541
|
+
require_esm.esm_default.get("https://example.com/localhost-link", {
|
|
1733
542
|
body: `<html>
|
|
1734
543
|
<head>
|
|
1735
544
|
<meta charset=utf-8>
|
|
@@ -1741,16 +550,32 @@ var esm_default = new class FetchMock {
|
|
|
1741
550
|
</html>`,
|
|
1742
551
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1743
552
|
});
|
|
1744
|
-
esm_default.get("https://localhost/object", { body: {
|
|
553
|
+
require_esm.esm_default.get("https://localhost/object", { body: {
|
|
1745
554
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1746
555
|
id: "https://localhost/object",
|
|
1747
556
|
name: "Fetched object",
|
|
1748
557
|
type: "Object"
|
|
1749
558
|
} });
|
|
559
|
+
require_esm.esm_default.get("https://example.com/private-redirect", {
|
|
560
|
+
status: 302,
|
|
561
|
+
headers: { Location: "https://private.example/object" }
|
|
562
|
+
});
|
|
563
|
+
require_esm.esm_default.get("https://private.example/object", { body: {
|
|
564
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
565
|
+
id: "https://private.example/object",
|
|
566
|
+
type: "Object"
|
|
567
|
+
} });
|
|
1750
568
|
await t.step("allowPrivateAddress: false", async () => {
|
|
1751
569
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://localhost/object"), require_url.UrlError);
|
|
1752
570
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/localhost-redirect"), require_url.UrlError);
|
|
1753
571
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/localhost-link"), require_url.UrlError);
|
|
572
|
+
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://private.example/object"), require_url.UrlError);
|
|
573
|
+
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/private-redirect"), require_url.UrlError);
|
|
574
|
+
(0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls("https://private.example/object").length, 0);
|
|
575
|
+
const defaultLoader = require_docloader.getDocumentLoader();
|
|
576
|
+
await (0, node_assert.rejects)(() => defaultLoader("https://localhost/object"), require_url.UrlError);
|
|
577
|
+
await (0, node_assert.rejects)(() => defaultLoader("https://127.0.0.1/object"), require_url.UrlError);
|
|
578
|
+
(0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls("https://localhost/object").length, 0);
|
|
1754
579
|
});
|
|
1755
580
|
const fetchDocumentLoader2 = require_docloader.getDocumentLoader({ allowPrivateAddress: true });
|
|
1756
581
|
await t.step("allowPrivateAddress: true", async () => {
|
|
@@ -1769,7 +594,7 @@ var esm_default = new class FetchMock {
|
|
|
1769
594
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader2("https://example.com/localhost-link"), expected);
|
|
1770
595
|
});
|
|
1771
596
|
let redirectAttempts = 0;
|
|
1772
|
-
esm_default.get("begin:https://example.com/too-many-redirects/", (cl) => {
|
|
597
|
+
require_esm.esm_default.get("begin:https://example.com/too-many-redirects/", (cl) => {
|
|
1773
598
|
redirectAttempts++;
|
|
1774
599
|
return {
|
|
1775
600
|
status: 302,
|
|
@@ -1783,19 +608,19 @@ var esm_default = new class FetchMock {
|
|
|
1783
608
|
});
|
|
1784
609
|
await t.step("custom max redirection", async () => {
|
|
1785
610
|
redirectAttempts = 0;
|
|
1786
|
-
const loader = require_docloader.
|
|
611
|
+
const loader = require_docloader.createDocumentLoader({ maxRedirection: 1 }, { openHop: openUnpinnedHop });
|
|
1787
612
|
await (0, node_assert.rejects)(() => loader("https://example.com/too-many-redirects/0"), require_request.FetchError, "Too many redirections");
|
|
1788
613
|
(0, node_assert.deepStrictEqual)(redirectAttempts, 2);
|
|
1789
614
|
});
|
|
1790
615
|
let loopAttempts = 0;
|
|
1791
|
-
esm_default.get("https://example.com/redirect-loop-a", () => {
|
|
616
|
+
require_esm.esm_default.get("https://example.com/redirect-loop-a", () => {
|
|
1792
617
|
loopAttempts++;
|
|
1793
618
|
return {
|
|
1794
619
|
status: 302,
|
|
1795
620
|
headers: { Location: "https://example.com/redirect-loop-b" }
|
|
1796
621
|
};
|
|
1797
622
|
});
|
|
1798
|
-
esm_default.get("https://example.com/redirect-loop-b", () => {
|
|
623
|
+
require_esm.esm_default.get("https://example.com/redirect-loop-b", () => {
|
|
1799
624
|
loopAttempts++;
|
|
1800
625
|
return {
|
|
1801
626
|
status: 302,
|
|
@@ -1808,7 +633,7 @@ var esm_default = new class FetchMock {
|
|
|
1808
633
|
(0, node_assert.deepStrictEqual)(loopAttempts, 2);
|
|
1809
634
|
});
|
|
1810
635
|
let relativeLoopAttempts = 0;
|
|
1811
|
-
esm_default.get("https://example.com/redirect-loop-relative", () => {
|
|
636
|
+
require_esm.esm_default.get("https://example.com/redirect-loop-relative", () => {
|
|
1812
637
|
relativeLoopAttempts++;
|
|
1813
638
|
return {
|
|
1814
639
|
status: 302,
|
|
@@ -1821,7 +646,7 @@ var esm_default = new class FetchMock {
|
|
|
1821
646
|
(0, node_assert.deepStrictEqual)(relativeLoopAttempts, 1);
|
|
1822
647
|
});
|
|
1823
648
|
const maliciousPayload = "<a" + " a=\"b\"".repeat(30) + " ";
|
|
1824
|
-
esm_default.get("https://example.com/redos", {
|
|
649
|
+
require_esm.esm_default.get("https://example.com/redos", {
|
|
1825
650
|
body: maliciousPayload,
|
|
1826
651
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
1827
652
|
});
|
|
@@ -1831,14 +656,14 @@ var esm_default = new class FetchMock {
|
|
|
1831
656
|
const elapsed = performance.now() - start;
|
|
1832
657
|
(0, node_assert.ok)(elapsed < 1e3, `Potential ReDoS vulnerability detected: ${elapsed}ms (expected < 1000ms)`);
|
|
1833
658
|
});
|
|
1834
|
-
esm_default.hardReset();
|
|
659
|
+
require_esm.esm_default.hardReset();
|
|
1835
660
|
});
|
|
1836
661
|
(0, _fedify_fixture.test)("getDocumentLoader() bounds JSON, HTML, and alternate documents", async () => {
|
|
1837
662
|
const url = "https://example.com/bounded";
|
|
1838
|
-
esm_default.mockGlobal();
|
|
663
|
+
require_esm.esm_default.mockGlobal();
|
|
1839
664
|
let oversized = true;
|
|
1840
665
|
try {
|
|
1841
|
-
esm_default.get(url, () => new Response("{\"name\":\"hello\"}", { headers: {
|
|
666
|
+
require_esm.esm_default.get(url, () => new Response("{\"name\":\"hello\"}", { headers: {
|
|
1842
667
|
"Content-Type": "application/activity+json",
|
|
1843
668
|
...oversized ? { "Content-Length": String(16777217) } : {}
|
|
1844
669
|
} }));
|
|
@@ -1847,20 +672,20 @@ var esm_default = new class FetchMock {
|
|
|
1847
672
|
oversized = false;
|
|
1848
673
|
(0, node_assert.deepStrictEqual)((await loader(url)).document, { name: "hello" });
|
|
1849
674
|
oversized = true;
|
|
1850
|
-
esm_default.get(`${url}/html`, () => new Response(" ".repeat(1048577), { headers: { "Content-Type": "text/html" } }));
|
|
675
|
+
require_esm.esm_default.get(`${url}/html`, () => new Response(" ".repeat(1048577), { headers: { "Content-Type": "text/html" } }));
|
|
1851
676
|
await (0, node_assert.rejects)(require_docloader.getDocumentLoader({ allowPrivateAddress: true })(`${url}/html`), require_request.FetchError);
|
|
1852
|
-
esm_default.get(`${url}/alternate`, () => new Response("", { headers: {
|
|
677
|
+
require_esm.esm_default.get(`${url}/alternate`, () => new Response("", { headers: {
|
|
1853
678
|
"Content-Type": "text/html",
|
|
1854
679
|
Link: `<${url}>; rel="alternate"; type="application/activity+json"`
|
|
1855
680
|
} }));
|
|
1856
681
|
await (0, node_assert.rejects)(loader(`${url}/alternate`), require_request.FetchError);
|
|
1857
|
-
esm_default.get(`${url}/redirect`, {
|
|
682
|
+
require_esm.esm_default.get(`${url}/redirect`, {
|
|
1858
683
|
status: 302,
|
|
1859
684
|
headers: { Location: url }
|
|
1860
685
|
});
|
|
1861
686
|
await (0, node_assert.rejects)(loader(`${url}/redirect`), require_request.FetchError);
|
|
1862
687
|
} finally {
|
|
1863
|
-
esm_default.hardReset();
|
|
688
|
+
require_esm.esm_default.hardReset();
|
|
1864
689
|
}
|
|
1865
690
|
});
|
|
1866
691
|
(0, _fedify_fixture.test)("getDocumentLoader() rejects oversized gzip responses from fetch", async () => {
|
|
@@ -1911,18 +736,14 @@ var esm_default = new class FetchMock {
|
|
|
1911
736
|
(0, node_assert.deepStrictEqual)(pulls, 257);
|
|
1912
737
|
});
|
|
1913
738
|
(0, _fedify_fixture.test)("getDocumentLoader() logs DNS failures as such", async (t) => {
|
|
1914
|
-
if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") throw new Error("This DNS test requires Deno network permission (--allow-net).");
|
|
1915
|
-
const loader = require_docloader.getDocumentLoader();
|
|
1916
739
|
for (const result of [
|
|
1917
740
|
"throws",
|
|
1918
741
|
"empty",
|
|
1919
|
-
"private"
|
|
742
|
+
"private",
|
|
743
|
+
"default"
|
|
1920
744
|
]) await t.step(result, async () => {
|
|
1921
|
-
const
|
|
1922
|
-
|
|
1923
|
-
address: "127.0.0.1",
|
|
1924
|
-
family: 4
|
|
1925
|
-
}]));
|
|
745
|
+
const lookup = () => result === "throws" ? Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")) : Promise.resolve(result === "empty" ? [] : [PRIVATE_ADDRESS]);
|
|
746
|
+
const loader = result === "default" ? require_docloader.getDocumentLoader() : require_docloader.createDocumentLoader({}, { openHop: (url) => unpinnedPublicFetch.openPublicFetch(url, { lookup }) });
|
|
1926
747
|
const records = [];
|
|
1927
748
|
await (0, _logtape_logtape.configure)({
|
|
1928
749
|
sinks: { buffer: (record) => records.push(record) },
|
|
@@ -1937,19 +758,20 @@ var esm_default = new class FetchMock {
|
|
|
1937
758
|
reset: true
|
|
1938
759
|
});
|
|
1939
760
|
try {
|
|
1940
|
-
const url = "https://dns-failure.invalid/object";
|
|
761
|
+
const url = result === "default" ? "https://10.0.0.1/object" : "https://dns-failure.invalid/object";
|
|
1941
762
|
let error;
|
|
1942
763
|
await (0, node_assert.rejects)(() => loader(url), (e) => {
|
|
1943
764
|
error = e;
|
|
1944
765
|
return true;
|
|
1945
766
|
});
|
|
1946
767
|
(0, node_assert.ok)(error instanceof require_url.UrlError);
|
|
1947
|
-
|
|
768
|
+
const disallowed = result === "private" || result === "default";
|
|
769
|
+
(0, node_assert.deepStrictEqual)(error.reason, disallowed ? "disallowed" : "dns");
|
|
1948
770
|
(0, node_assert.deepStrictEqual)(records.map((r) => [
|
|
1949
771
|
r.level,
|
|
1950
772
|
r.rawMessage,
|
|
1951
773
|
r.properties.url
|
|
1952
|
-
]), [
|
|
774
|
+
]), [disallowed ? [
|
|
1953
775
|
"error",
|
|
1954
776
|
"Disallowed private URL: {url}",
|
|
1955
777
|
url
|
|
@@ -1961,7 +783,6 @@ var esm_default = new class FetchMock {
|
|
|
1961
783
|
(0, node_assert.ok)(records[0].properties.error === error);
|
|
1962
784
|
} finally {
|
|
1963
785
|
await (0, _logtape_logtape.reset)();
|
|
1964
|
-
node_dns_promises.default.lookup = originalLookup;
|
|
1965
786
|
}
|
|
1966
787
|
});
|
|
1967
788
|
});
|
|
@@ -1973,9 +794,9 @@ var esm_default = new class FetchMock {
|
|
|
1973
794
|
"html",
|
|
1974
795
|
"mixed"
|
|
1975
796
|
]) for (const hops of [20, 21]) await t.step(`${mode}: ${hops} hops`, async () => {
|
|
1976
|
-
esm_default.mockGlobal();
|
|
797
|
+
require_esm.esm_default.mockGlobal();
|
|
1977
798
|
let requests = 0;
|
|
1978
|
-
esm_default.get(`begin:${base}`, ({ url }) => {
|
|
799
|
+
require_esm.esm_default.get(`begin:${base}`, ({ url }) => {
|
|
1979
800
|
requests++;
|
|
1980
801
|
const index = Number(new URL(url).pathname.split("/").at(-1));
|
|
1981
802
|
if (index === hops || requests > 30) return Response.json({ done: true });
|
|
@@ -1992,7 +813,7 @@ var esm_default = new class FetchMock {
|
|
|
1992
813
|
});
|
|
1993
814
|
(0, node_assert.deepStrictEqual)(requests, 21);
|
|
1994
815
|
} finally {
|
|
1995
|
-
esm_default.hardReset();
|
|
816
|
+
require_esm.esm_default.hardReset();
|
|
1996
817
|
}
|
|
1997
818
|
});
|
|
1998
819
|
for (const mode of [
|
|
@@ -2002,9 +823,9 @@ var esm_default = new class FetchMock {
|
|
|
2002
823
|
"redirect-alternate",
|
|
2003
824
|
"redirect-intermediate"
|
|
2004
825
|
]) await t.step(`${mode}: cycle`, async () => {
|
|
2005
|
-
esm_default.mockGlobal();
|
|
826
|
+
require_esm.esm_default.mockGlobal();
|
|
2006
827
|
let requests = 0;
|
|
2007
|
-
esm_default.get(`begin:${base}`, ({ url }) => {
|
|
828
|
+
require_esm.esm_default.get(`begin:${base}`, ({ url }) => {
|
|
2008
829
|
requests++;
|
|
2009
830
|
if (requests > 30) return Response.json({ stopped: true });
|
|
2010
831
|
const index = Number(new URL(url).pathname.split("/").at(-1));
|
|
@@ -2020,13 +841,13 @@ var esm_default = new class FetchMock {
|
|
|
2020
841
|
});
|
|
2021
842
|
(0, node_assert.deepStrictEqual)(requests, mode === "redirect-intermediate" ? 3 : 2);
|
|
2022
843
|
} finally {
|
|
2023
|
-
esm_default.hardReset();
|
|
844
|
+
require_esm.esm_default.hardReset();
|
|
2024
845
|
}
|
|
2025
846
|
});
|
|
2026
847
|
await t.step("alternate followed by 20 redirects shares the limit", async () => {
|
|
2027
|
-
esm_default.mockGlobal();
|
|
848
|
+
require_esm.esm_default.mockGlobal();
|
|
2028
849
|
let requests = 0;
|
|
2029
|
-
esm_default.get(`begin:${base}`, ({ url }) => {
|
|
850
|
+
require_esm.esm_default.get(`begin:${base}`, ({ url }) => {
|
|
2030
851
|
requests++;
|
|
2031
852
|
const index = Number(new URL(url).pathname.split("/").at(-1));
|
|
2032
853
|
if (index === 21) return Response.json({ done: true });
|
|
@@ -2040,14 +861,14 @@ var esm_default = new class FetchMock {
|
|
|
2040
861
|
});
|
|
2041
862
|
(0, node_assert.deepStrictEqual)(requests, 21);
|
|
2042
863
|
} finally {
|
|
2043
|
-
esm_default.hardReset();
|
|
864
|
+
require_esm.esm_default.hardReset();
|
|
2044
865
|
}
|
|
2045
866
|
});
|
|
2046
867
|
await t.step("relative alternate after redirect and isolated calls", async () => {
|
|
2047
|
-
esm_default.mockGlobal();
|
|
2048
|
-
esm_default.get(`${base}start`, Response.redirect(`${base}html`, 302));
|
|
2049
|
-
esm_default.get(`${base}html`, alternate("./document", true));
|
|
2050
|
-
esm_default.get(`${base}document`, Response.json({ done: true }));
|
|
868
|
+
require_esm.esm_default.mockGlobal();
|
|
869
|
+
require_esm.esm_default.get(`${base}start`, Response.redirect(`${base}html`, 302));
|
|
870
|
+
require_esm.esm_default.get(`${base}html`, alternate("./document", true));
|
|
871
|
+
require_esm.esm_default.get(`${base}document`, Response.json({ done: true }));
|
|
2051
872
|
try {
|
|
2052
873
|
for (let i = 0; i < 2; i++) {
|
|
2053
874
|
const results = await Promise.all([loader(`${base}start`), loader(`${base}start`)]);
|
|
@@ -2057,7 +878,7 @@ var esm_default = new class FetchMock {
|
|
|
2057
878
|
}
|
|
2058
879
|
}
|
|
2059
880
|
} finally {
|
|
2060
|
-
esm_default.hardReset();
|
|
881
|
+
require_esm.esm_default.hardReset();
|
|
2061
882
|
}
|
|
2062
883
|
});
|
|
2063
884
|
function alternate(next, html) {
|
|
@@ -2071,10 +892,10 @@ var esm_default = new class FetchMock {
|
|
|
2071
892
|
const base = "https://example.com/alternate-abort/";
|
|
2072
893
|
const loader = require_docloader.getDocumentLoader({ allowPrivateAddress: true });
|
|
2073
894
|
for (const html of [false, true]) await t.step(html ? "HTML" : "Link header", async () => {
|
|
2074
|
-
esm_default.mockGlobal();
|
|
895
|
+
require_esm.esm_default.mockGlobal();
|
|
2075
896
|
const controller = new AbortController();
|
|
2076
897
|
let requests = 0;
|
|
2077
|
-
esm_default.get(`begin:${base}`, ({ url }) => {
|
|
898
|
+
require_esm.esm_default.get(`begin:${base}`, ({ url }) => {
|
|
2078
899
|
requests++;
|
|
2079
900
|
const index = Number(new URL(url).pathname.split("/").at(-1));
|
|
2080
901
|
if (index === 2) return Response.json({ done: true });
|
|
@@ -2089,15 +910,15 @@ var esm_default = new class FetchMock {
|
|
|
2089
910
|
await (0, node_assert.rejects)(loader(`${base}0`, { signal: controller.signal }), { name: "AbortError" });
|
|
2090
911
|
(0, node_assert.deepStrictEqual)(requests, 2);
|
|
2091
912
|
} finally {
|
|
2092
|
-
esm_default.hardReset();
|
|
913
|
+
require_esm.esm_default.hardReset();
|
|
2093
914
|
}
|
|
2094
915
|
});
|
|
2095
916
|
});
|
|
2096
917
|
(0, _fedify_fixture.test)("getDocumentLoader() rejects cancellation before fetching", async () => {
|
|
2097
|
-
esm_default.mockGlobal();
|
|
918
|
+
require_esm.esm_default.mockGlobal();
|
|
2098
919
|
let requests = 0;
|
|
2099
920
|
const url = "https://example.com/pre-aborted-alternate";
|
|
2100
|
-
esm_default.get(url, () => {
|
|
921
|
+
require_esm.esm_default.get(url, () => {
|
|
2101
922
|
requests++;
|
|
2102
923
|
return Response.json({ done: true });
|
|
2103
924
|
});
|
|
@@ -2108,7 +929,7 @@ var esm_default = new class FetchMock {
|
|
|
2108
929
|
await (0, node_assert.rejects)(loader(url, { signal: controller.signal }), { name: "AbortError" });
|
|
2109
930
|
(0, node_assert.deepStrictEqual)(requests, 0);
|
|
2110
931
|
} finally {
|
|
2111
|
-
esm_default.hardReset();
|
|
932
|
+
require_esm.esm_default.hardReset();
|
|
2112
933
|
}
|
|
2113
934
|
});
|
|
2114
935
|
async function withServer(handler, callback) {
|
|
@@ -2259,32 +1080,29 @@ function isTimeoutFetchError(error, url) {
|
|
|
2259
1080
|
});
|
|
2260
1081
|
});
|
|
2261
1082
|
(0, _fedify_fixture.test)("getDocumentLoader() bounds DNS lookups that ignore the timeout", async () => {
|
|
2262
|
-
|
|
2263
|
-
esm_default.mockGlobal();
|
|
1083
|
+
require_esm.esm_default.mockGlobal();
|
|
2264
1084
|
let requests = 0;
|
|
2265
1085
|
const url = "https://slow-dns.example/object";
|
|
2266
|
-
esm_default.get(url, () => {
|
|
1086
|
+
require_esm.esm_default.get(url, () => {
|
|
2267
1087
|
requests++;
|
|
2268
1088
|
return Response.json({ id: url });
|
|
2269
1089
|
});
|
|
2270
|
-
const originalLookup = node_dns_promises.default.lookup;
|
|
2271
1090
|
let resolveLookup = () => {};
|
|
2272
1091
|
const lookedUp = new Promise((resolve) => resolveLookup = resolve);
|
|
2273
|
-
|
|
1092
|
+
const lookup = () => new Promise((resolve) => setTimeout(() => {
|
|
2274
1093
|
resolve([{
|
|
2275
1094
|
address: "93.184.215.14",
|
|
2276
1095
|
family: 4
|
|
2277
1096
|
}]);
|
|
2278
1097
|
resolveLookup();
|
|
2279
|
-
}, 200))
|
|
1098
|
+
}, 200));
|
|
2280
1099
|
try {
|
|
2281
|
-
await (0, node_assert.rejects)(require_docloader.
|
|
1100
|
+
await (0, node_assert.rejects)(require_docloader.createDocumentLoader({ timeout: 50 }, { openHop: (url) => unpinnedPublicFetch.openPublicFetch(url, { lookup }) })(url), (e) => isTimeoutFetchError(e, url));
|
|
2282
1101
|
await lookedUp;
|
|
2283
1102
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
2284
1103
|
(0, node_assert.deepStrictEqual)(requests, 0);
|
|
2285
1104
|
} finally {
|
|
2286
|
-
|
|
2287
|
-
esm_default.hardReset();
|
|
1105
|
+
require_esm.esm_default.hardReset();
|
|
2288
1106
|
}
|
|
2289
1107
|
});
|
|
2290
1108
|
(0, _fedify_fixture.test)("withDocumentLoaderTimeout() cleans up after each call", async () => {
|
|
@@ -2465,4 +1283,424 @@ for (const url of unsupportedUrls) {
|
|
|
2465
1283
|
});
|
|
2466
1284
|
});
|
|
2467
1285
|
}
|
|
1286
|
+
function createHopRecorder(responder) {
|
|
1287
|
+
const events = [];
|
|
1288
|
+
const states = /* @__PURE__ */ new Map();
|
|
1289
|
+
const violations = [];
|
|
1290
|
+
let responses = 0;
|
|
1291
|
+
let opens = 0;
|
|
1292
|
+
let closes = 0;
|
|
1293
|
+
function respond(url) {
|
|
1294
|
+
const n = ++responses;
|
|
1295
|
+
events.push(`fetch ${url} #${n}`);
|
|
1296
|
+
const { status = 200, headers, body = "", failCancel, hangCancel, responseUrl } = responder(url);
|
|
1297
|
+
const state = {
|
|
1298
|
+
n,
|
|
1299
|
+
settled: false
|
|
1300
|
+
};
|
|
1301
|
+
let sent = false;
|
|
1302
|
+
const stream = new ReadableStream({
|
|
1303
|
+
pull(controller) {
|
|
1304
|
+
if (!sent && body !== "") {
|
|
1305
|
+
sent = true;
|
|
1306
|
+
controller.enqueue(new TextEncoder().encode(body));
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
controller.close();
|
|
1310
|
+
state.settled = true;
|
|
1311
|
+
events.push(`consumed #${n}`);
|
|
1312
|
+
},
|
|
1313
|
+
cancel() {
|
|
1314
|
+
state.settled = true;
|
|
1315
|
+
events.push(`cancel #${n}`);
|
|
1316
|
+
if (failCancel) throw new Error(`Failed to cancel #${n}`);
|
|
1317
|
+
if (hangCancel) return new Promise(() => {});
|
|
1318
|
+
}
|
|
1319
|
+
}, { highWaterMark: 0 });
|
|
1320
|
+
const response = new Response(stream, {
|
|
1321
|
+
status,
|
|
1322
|
+
headers
|
|
1323
|
+
});
|
|
1324
|
+
if (responseUrl != null) Object.defineProperty(response, "url", { value: responseUrl });
|
|
1325
|
+
states.set(response, state);
|
|
1326
|
+
return response;
|
|
1327
|
+
}
|
|
1328
|
+
function record(url, session) {
|
|
1329
|
+
opens++;
|
|
1330
|
+
events.push(`open ${url}`);
|
|
1331
|
+
const fetched = [];
|
|
1332
|
+
let closed = false;
|
|
1333
|
+
return {
|
|
1334
|
+
get url() {
|
|
1335
|
+
return session.url;
|
|
1336
|
+
},
|
|
1337
|
+
async fetch(input, init) {
|
|
1338
|
+
const response = await session.fetch(input, init);
|
|
1339
|
+
fetched.push(response);
|
|
1340
|
+
return response;
|
|
1341
|
+
},
|
|
1342
|
+
async close() {
|
|
1343
|
+
if (closed) return;
|
|
1344
|
+
closed = true;
|
|
1345
|
+
for (const response of fetched) {
|
|
1346
|
+
const state = states.get(response);
|
|
1347
|
+
if (state != null && !state.settled) violations.push(`#${state.n} is still open when closing ${url}`);
|
|
1348
|
+
}
|
|
1349
|
+
closes++;
|
|
1350
|
+
events.push(`close ${url}`);
|
|
1351
|
+
await session.close();
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
function openHop(url) {
|
|
1356
|
+
const origin = new URL(url).origin;
|
|
1357
|
+
return Promise.resolve(record(url, {
|
|
1358
|
+
url: new URL(url),
|
|
1359
|
+
fetch(input) {
|
|
1360
|
+
const target = input instanceof Request ? input.url : String(input);
|
|
1361
|
+
(0, node_assert.ok)(new URL(target).origin === origin, `${target} is not on ${origin}`);
|
|
1362
|
+
return Promise.resolve(respond(target));
|
|
1363
|
+
},
|
|
1364
|
+
close: () => Promise.resolve()
|
|
1365
|
+
}));
|
|
1366
|
+
}
|
|
1367
|
+
return {
|
|
1368
|
+
events,
|
|
1369
|
+
respond,
|
|
1370
|
+
record,
|
|
1371
|
+
openHop,
|
|
1372
|
+
assertInvariants() {
|
|
1373
|
+
(0, node_assert.deepStrictEqual)(violations, []);
|
|
1374
|
+
(0, node_assert.deepStrictEqual)(opens, closes);
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
const lifecycleDocument = {
|
|
1379
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1380
|
+
id: "https://b.example/object",
|
|
1381
|
+
type: "Object"
|
|
1382
|
+
};
|
|
1383
|
+
(0, _fedify_fixture.test)("getDocumentLoader() closes a redirect hop before the next hop", async () => {
|
|
1384
|
+
const recorder = createHopRecorder((url) => url === "https://a.example/start" ? {
|
|
1385
|
+
status: 302,
|
|
1386
|
+
headers: { Location: "https://b.example/object" }
|
|
1387
|
+
} : { body: JSON.stringify(lifecycleDocument) });
|
|
1388
|
+
(0, node_assert.deepStrictEqual)(await require_docloader.createDocumentLoader({}, { openHop: recorder.openHop })("https://a.example/start"), {
|
|
1389
|
+
contextUrl: null,
|
|
1390
|
+
documentUrl: "https://b.example/object",
|
|
1391
|
+
document: lifecycleDocument
|
|
1392
|
+
});
|
|
1393
|
+
(0, node_assert.deepStrictEqual)(recorder.events, [
|
|
1394
|
+
"open https://a.example/start",
|
|
1395
|
+
"fetch https://a.example/start #1",
|
|
1396
|
+
"cancel #1",
|
|
1397
|
+
"close https://a.example/start",
|
|
1398
|
+
"open https://b.example/object",
|
|
1399
|
+
"fetch https://b.example/object #2",
|
|
1400
|
+
"consumed #2",
|
|
1401
|
+
"close https://b.example/object"
|
|
1402
|
+
]);
|
|
1403
|
+
recorder.assertInvariants();
|
|
1404
|
+
});
|
|
1405
|
+
(0, _fedify_fixture.test)("getDocumentLoader() cancels the body before a Link header alternate", async () => {
|
|
1406
|
+
const recorder = createHopRecorder((url) => url === "https://a.example/start" ? {
|
|
1407
|
+
headers: {
|
|
1408
|
+
"Content-Type": "text/plain",
|
|
1409
|
+
Link: "<https://b.example/object>; rel=\"alternate\"; type=\"application/activity+json\""
|
|
1410
|
+
},
|
|
1411
|
+
body: "not JSON"
|
|
1412
|
+
} : { body: JSON.stringify(lifecycleDocument) });
|
|
1413
|
+
(0, node_assert.deepStrictEqual)((await require_docloader.createDocumentLoader({}, { openHop: recorder.openHop })("https://a.example/start")).documentUrl, "https://b.example/object");
|
|
1414
|
+
(0, node_assert.deepStrictEqual)(recorder.events, [
|
|
1415
|
+
"open https://a.example/start",
|
|
1416
|
+
"fetch https://a.example/start #1",
|
|
1417
|
+
"cancel #1",
|
|
1418
|
+
"close https://a.example/start",
|
|
1419
|
+
"open https://b.example/object",
|
|
1420
|
+
"fetch https://b.example/object #2",
|
|
1421
|
+
"consumed #2",
|
|
1422
|
+
"close https://b.example/object"
|
|
1423
|
+
]);
|
|
1424
|
+
recorder.assertInvariants();
|
|
1425
|
+
});
|
|
1426
|
+
(0, _fedify_fixture.test)("getDocumentLoader() reads the body before an HTML alternate", async () => {
|
|
1427
|
+
const recorder = createHopRecorder((url) => url === "https://a.example/start" ? {
|
|
1428
|
+
headers: { "Content-Type": "text/html" },
|
|
1429
|
+
body: "<link rel=\"alternate\" type=\"application/activity+json\" href=\"https://b.example/object\">"
|
|
1430
|
+
} : { body: JSON.stringify(lifecycleDocument) });
|
|
1431
|
+
(0, node_assert.deepStrictEqual)((await require_docloader.createDocumentLoader({}, { openHop: recorder.openHop })("https://a.example/start")).documentUrl, "https://b.example/object");
|
|
1432
|
+
(0, node_assert.deepStrictEqual)(recorder.events, [
|
|
1433
|
+
"open https://a.example/start",
|
|
1434
|
+
"fetch https://a.example/start #1",
|
|
1435
|
+
"consumed #1",
|
|
1436
|
+
"close https://a.example/start",
|
|
1437
|
+
"open https://b.example/object",
|
|
1438
|
+
"fetch https://b.example/object #2",
|
|
1439
|
+
"consumed #2",
|
|
1440
|
+
"close https://b.example/object"
|
|
1441
|
+
]);
|
|
1442
|
+
recorder.assertInvariants();
|
|
1443
|
+
});
|
|
1444
|
+
/**
|
|
1445
|
+
* Fails instead of blocking the test if the given promise does not settle
|
|
1446
|
+
* within the given time.
|
|
1447
|
+
*/
|
|
1448
|
+
async function settlesWithin(promise, milliseconds = 1e3) {
|
|
1449
|
+
let timer;
|
|
1450
|
+
try {
|
|
1451
|
+
return await Promise.race([promise, new Promise((_, reject) => {
|
|
1452
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Did not settle in ${milliseconds} ms.`)), milliseconds);
|
|
1453
|
+
})]);
|
|
1454
|
+
} finally {
|
|
1455
|
+
clearTimeout(timer);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Runs the given function while capturing the document loader's logs, and
|
|
1460
|
+
* waits until the rejections of body cancellations started without being
|
|
1461
|
+
* awaited have been logged.
|
|
1462
|
+
*/
|
|
1463
|
+
async function captureDocumentLoaderLogs(fn) {
|
|
1464
|
+
const records = [];
|
|
1465
|
+
await (0, _logtape_logtape.configure)({
|
|
1466
|
+
sinks: { buffer: (record) => records.push(record) },
|
|
1467
|
+
loggers: [{
|
|
1468
|
+
category: [
|
|
1469
|
+
"fedify",
|
|
1470
|
+
"runtime",
|
|
1471
|
+
"docloader"
|
|
1472
|
+
],
|
|
1473
|
+
sinks: ["buffer"],
|
|
1474
|
+
lowestLevel: "debug"
|
|
1475
|
+
}, {
|
|
1476
|
+
category: ["logtape", "meta"],
|
|
1477
|
+
sinks: []
|
|
1478
|
+
}],
|
|
1479
|
+
reset: true
|
|
1480
|
+
});
|
|
1481
|
+
try {
|
|
1482
|
+
const result = await fn();
|
|
1483
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1484
|
+
return {
|
|
1485
|
+
result,
|
|
1486
|
+
records
|
|
1487
|
+
};
|
|
1488
|
+
} finally {
|
|
1489
|
+
await (0, _logtape_logtape.reset)();
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
function cancelFailureLogs(records) {
|
|
1493
|
+
return records.filter((record) => record.rawMessage === "Failed to cancel the response body from {url}: {error}").map((record) => [
|
|
1494
|
+
record.level,
|
|
1495
|
+
record.properties.url,
|
|
1496
|
+
String(record.properties.error)
|
|
1497
|
+
]);
|
|
1498
|
+
}
|
|
1499
|
+
function linkHeaderAlternate(next) {
|
|
1500
|
+
return {
|
|
1501
|
+
headers: {
|
|
1502
|
+
"Content-Type": "text/plain",
|
|
1503
|
+
Link: `<${next}>; rel="alternate"; type="application/activity+json"`
|
|
1504
|
+
},
|
|
1505
|
+
body: "not JSON"
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
(0, _fedify_fixture.test)("getDocumentLoader() continues if an intermediate body fails to cancel", async (t) => {
|
|
1509
|
+
for (const kind of ["redirect", "Link header alternate"]) await t.step(kind, async () => {
|
|
1510
|
+
const recorder = createHopRecorder((url) => url === "https://a.example/start" ? {
|
|
1511
|
+
...kind === "redirect" ? {
|
|
1512
|
+
status: 302,
|
|
1513
|
+
headers: { Location: "https://b.example/object" }
|
|
1514
|
+
} : linkHeaderAlternate("https://b.example/object"),
|
|
1515
|
+
failCancel: true
|
|
1516
|
+
} : { body: JSON.stringify(lifecycleDocument) });
|
|
1517
|
+
const loader = require_docloader.createDocumentLoader({}, { openHop: recorder.openHop });
|
|
1518
|
+
const { result, records } = await captureDocumentLoaderLogs(() => loader("https://a.example/start"));
|
|
1519
|
+
(0, node_assert.deepStrictEqual)(result, {
|
|
1520
|
+
contextUrl: null,
|
|
1521
|
+
documentUrl: "https://b.example/object",
|
|
1522
|
+
document: lifecycleDocument
|
|
1523
|
+
});
|
|
1524
|
+
(0, node_assert.deepStrictEqual)(recorder.events, [
|
|
1525
|
+
"open https://a.example/start",
|
|
1526
|
+
"fetch https://a.example/start #1",
|
|
1527
|
+
"cancel #1",
|
|
1528
|
+
"close https://a.example/start",
|
|
1529
|
+
"open https://b.example/object",
|
|
1530
|
+
"fetch https://b.example/object #2",
|
|
1531
|
+
"consumed #2",
|
|
1532
|
+
"close https://b.example/object"
|
|
1533
|
+
]);
|
|
1534
|
+
recorder.assertInvariants();
|
|
1535
|
+
(0, node_assert.deepStrictEqual)(cancelFailureLogs(records), [[
|
|
1536
|
+
"debug",
|
|
1537
|
+
"https://a.example/start",
|
|
1538
|
+
"Error: Failed to cancel #1"
|
|
1539
|
+
]]);
|
|
1540
|
+
});
|
|
1541
|
+
});
|
|
1542
|
+
(0, _fedify_fixture.test)("getDocumentLoader() does not wait for intermediate bodies to be cancelled", async (t) => {
|
|
1543
|
+
for (const kind of ["redirect", "Link header alternate"]) await t.step(kind, async () => {
|
|
1544
|
+
const recorder = createHopRecorder((url) => url === "https://a.example/start" ? {
|
|
1545
|
+
...kind === "redirect" ? {
|
|
1546
|
+
status: 302,
|
|
1547
|
+
headers: { Location: "https://b.example/object" }
|
|
1548
|
+
} : linkHeaderAlternate("https://b.example/object"),
|
|
1549
|
+
hangCancel: true
|
|
1550
|
+
} : { body: JSON.stringify(lifecycleDocument) });
|
|
1551
|
+
(0, node_assert.deepStrictEqual)((await settlesWithin(require_docloader.createDocumentLoader({}, { openHop: recorder.openHop })("https://a.example/start"))).documentUrl, "https://b.example/object");
|
|
1552
|
+
(0, node_assert.deepStrictEqual)(recorder.events, [
|
|
1553
|
+
"open https://a.example/start",
|
|
1554
|
+
"fetch https://a.example/start #1",
|
|
1555
|
+
"cancel #1",
|
|
1556
|
+
"close https://a.example/start",
|
|
1557
|
+
"open https://b.example/object",
|
|
1558
|
+
"fetch https://b.example/object #2",
|
|
1559
|
+
"consumed #2",
|
|
1560
|
+
"close https://b.example/object"
|
|
1561
|
+
]);
|
|
1562
|
+
recorder.assertInvariants();
|
|
1563
|
+
});
|
|
1564
|
+
await t.step("public session with a never-settling cancel", async () => {
|
|
1565
|
+
let cancelled = 0;
|
|
1566
|
+
const pinner = {
|
|
1567
|
+
supports: () => true,
|
|
1568
|
+
fetch: () => Promise.resolve(new Response(new ReadableStream({ cancel() {
|
|
1569
|
+
cancelled++;
|
|
1570
|
+
return new Promise(() => {});
|
|
1571
|
+
} }), {
|
|
1572
|
+
status: 302,
|
|
1573
|
+
headers: { Location: "file:///private-document" }
|
|
1574
|
+
})),
|
|
1575
|
+
release() {}
|
|
1576
|
+
};
|
|
1577
|
+
const publicFetch = require_public_fetch.createPublicFetch({
|
|
1578
|
+
pinner: () => Promise.resolve(pinner),
|
|
1579
|
+
lookup: () => Promise.reject(/* @__PURE__ */ new Error("Unexpected default lookup"))
|
|
1580
|
+
});
|
|
1581
|
+
await settlesWithin((0, node_assert.rejects)(require_docloader.createDocumentLoader({}, { openHop: (url) => publicFetch.openPublicFetch(url, { lookup: lookupTestAddresses }) })("https://a.example/redirect"), require_url.UrlError));
|
|
1582
|
+
(0, node_assert.deepStrictEqual)(cancelled, 1);
|
|
1583
|
+
});
|
|
1584
|
+
});
|
|
1585
|
+
(0, _fedify_fixture.test)("getDocumentLoader() completes with tee'd fetch-mock responses", async (t) => {
|
|
1586
|
+
const url = "https://example.com/teed";
|
|
1587
|
+
function stream(text) {
|
|
1588
|
+
return new ReadableStream({ start(controller) {
|
|
1589
|
+
controller.enqueue(new TextEncoder().encode(text));
|
|
1590
|
+
controller.close();
|
|
1591
|
+
} });
|
|
1592
|
+
}
|
|
1593
|
+
const loaders = {
|
|
1594
|
+
unpinned: require_docloader.createDocumentLoader({}, { openHop: openUnpinnedHop }),
|
|
1595
|
+
"private addresses allowed": require_docloader.getDocumentLoader({ allowPrivateAddress: true })
|
|
1596
|
+
};
|
|
1597
|
+
for (const [name, loader] of Object.entries(loaders)) for (const body of ["string", "stream"]) await t.step(`${name}: ${body} bodies`, async () => {
|
|
1598
|
+
const make = (text) => body === "string" ? text : stream(text);
|
|
1599
|
+
require_esm.esm_default.mockGlobal();
|
|
1600
|
+
try {
|
|
1601
|
+
require_esm.esm_default.get(`${url}/object`, () => new Response(make(JSON.stringify(lifecycleDocument)), { headers: { "Content-Type": "application/activity+json" } }));
|
|
1602
|
+
require_esm.esm_default.get(`${url}/redirect`, () => new Response(make("redirect"), {
|
|
1603
|
+
status: 302,
|
|
1604
|
+
headers: { Location: `${url}/object` }
|
|
1605
|
+
}));
|
|
1606
|
+
require_esm.esm_default.get(`${url}/alternate`, () => new Response(make("not JSON"), { headers: {
|
|
1607
|
+
"Content-Type": "text/plain",
|
|
1608
|
+
Link: `<${url}/object>; rel="alternate"; type="application/activity+json"`
|
|
1609
|
+
} }));
|
|
1610
|
+
require_esm.esm_default.get(`${url}/missing`, () => new Response(make("not found"), { status: 404 }));
|
|
1611
|
+
for (const path of ["redirect", "alternate"]) (0, node_assert.deepStrictEqual)((await settlesWithin(loader(`${url}/${path}`))).document, lifecycleDocument);
|
|
1612
|
+
await settlesWithin((0, node_assert.rejects)(loader(`${url}/missing`), require_request.FetchError));
|
|
1613
|
+
(0, node_assert.deepStrictEqual)(require_esm.esm_default.callHistory.calls().map((call) => call.url), [
|
|
1614
|
+
`${url}/redirect`,
|
|
1615
|
+
`${url}/object`,
|
|
1616
|
+
`${url}/alternate`,
|
|
1617
|
+
`${url}/object`,
|
|
1618
|
+
`${url}/missing`
|
|
1619
|
+
]);
|
|
1620
|
+
} finally {
|
|
1621
|
+
require_esm.esm_default.hardReset();
|
|
1622
|
+
}
|
|
1623
|
+
});
|
|
1624
|
+
});
|
|
1625
|
+
(0, _fedify_fixture.test)("getDocumentLoader() resolves each hop once", async () => {
|
|
1626
|
+
const lookups = [];
|
|
1627
|
+
const recorder = createHopRecorder((url) => {
|
|
1628
|
+
const responseUrl = `https://${PUBLIC_ADDRESS.address}${new URL(url).pathname}`;
|
|
1629
|
+
if (url === "https://a.example/start") return {
|
|
1630
|
+
status: 302,
|
|
1631
|
+
headers: { Location: "https://b.example/next" },
|
|
1632
|
+
responseUrl
|
|
1633
|
+
};
|
|
1634
|
+
else if (url === "https://b.example/next") return {
|
|
1635
|
+
status: 301,
|
|
1636
|
+
headers: { Location: "/object" },
|
|
1637
|
+
responseUrl
|
|
1638
|
+
};
|
|
1639
|
+
return {
|
|
1640
|
+
body: JSON.stringify(lifecycleDocument),
|
|
1641
|
+
responseUrl
|
|
1642
|
+
};
|
|
1643
|
+
});
|
|
1644
|
+
const pinner = {
|
|
1645
|
+
supports: () => true,
|
|
1646
|
+
fetch: (_grant, request) => Promise.resolve(recorder.respond(request.url)),
|
|
1647
|
+
release() {}
|
|
1648
|
+
};
|
|
1649
|
+
const publicFetch = require_public_fetch.createPublicFetch({
|
|
1650
|
+
pinner: () => Promise.resolve(pinner),
|
|
1651
|
+
lookup: () => Promise.reject(/* @__PURE__ */ new Error("Unexpected default lookup"))
|
|
1652
|
+
});
|
|
1653
|
+
(0, node_assert.deepStrictEqual)(await require_docloader.createDocumentLoader({}, { openHop: async (url) => recorder.record(url, await publicFetch.openPublicFetch(url, { lookup(hostname) {
|
|
1654
|
+
lookups.push(hostname);
|
|
1655
|
+
return Promise.resolve([PUBLIC_ADDRESS]);
|
|
1656
|
+
} })) })("https://a.example/start"), {
|
|
1657
|
+
contextUrl: null,
|
|
1658
|
+
documentUrl: "https://b.example/object",
|
|
1659
|
+
document: lifecycleDocument
|
|
1660
|
+
});
|
|
1661
|
+
(0, node_assert.deepStrictEqual)(lookups, [
|
|
1662
|
+
"a.example",
|
|
1663
|
+
"b.example",
|
|
1664
|
+
"b.example"
|
|
1665
|
+
]);
|
|
1666
|
+
(0, node_assert.deepStrictEqual)(recorder.events, [
|
|
1667
|
+
"open https://a.example/start",
|
|
1668
|
+
"fetch https://a.example/start #1",
|
|
1669
|
+
"cancel #1",
|
|
1670
|
+
"close https://a.example/start",
|
|
1671
|
+
"open https://b.example/next",
|
|
1672
|
+
"fetch https://b.example/next #2",
|
|
1673
|
+
"cancel #2",
|
|
1674
|
+
"close https://b.example/next",
|
|
1675
|
+
"open https://b.example/object",
|
|
1676
|
+
"fetch https://b.example/object #3",
|
|
1677
|
+
"consumed #3",
|
|
1678
|
+
"close https://b.example/object"
|
|
1679
|
+
]);
|
|
1680
|
+
recorder.assertInvariants();
|
|
1681
|
+
});
|
|
1682
|
+
(0, _fedify_fixture.test)("getRemoteDocument() cancels the body before loading a Link header alternate", async () => {
|
|
1683
|
+
const recorder = createHopRecorder(() => ({
|
|
1684
|
+
headers: {
|
|
1685
|
+
"Content-Type": "text/html",
|
|
1686
|
+
Link: "<https://b.example/object>; rel=\"alternate\"; type=\"application/activity+json\""
|
|
1687
|
+
},
|
|
1688
|
+
body: "<html></html>"
|
|
1689
|
+
}));
|
|
1690
|
+
const response = recorder.respond("https://a.example/start");
|
|
1691
|
+
const placeholder = {
|
|
1692
|
+
contextUrl: null,
|
|
1693
|
+
document: {},
|
|
1694
|
+
documentUrl: "https://b.example/object"
|
|
1695
|
+
};
|
|
1696
|
+
const alternates = [];
|
|
1697
|
+
(0, node_assert.ok)(await require_docloader.getRemoteDocument("https://a.example/start", response, (url) => {
|
|
1698
|
+
alternates.push(url);
|
|
1699
|
+
(0, node_assert.deepStrictEqual)(recorder.events.at(-1), "cancel #1");
|
|
1700
|
+
(0, node_assert.ok)(response.bodyUsed);
|
|
1701
|
+
return Promise.resolve(placeholder);
|
|
1702
|
+
}) === placeholder);
|
|
1703
|
+
(0, node_assert.deepStrictEqual)(alternates, ["https://b.example/object"]);
|
|
1704
|
+
(0, node_assert.deepStrictEqual)(recorder.events, ["fetch https://a.example/start #1", "cancel #1"]);
|
|
1705
|
+
});
|
|
2468
1706
|
//#endregion
|