magpie-html 0.1.2 → 0.1.4
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/README.md +35 -35
- package/dist/index.cjs +1787 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +184 -1
- package/dist/index.d.ts +184 -1
- package/dist/index.js +1778 -19
- package/dist/index.js.map +1 -1
- package/package.json +8 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Readability } from '@mozilla/readability';
|
|
2
2
|
import { parseHTML as parseHTML$1 } from 'linkedom';
|
|
3
|
+
import vm4 from 'vm';
|
|
4
|
+
import { setTimeout, clearTimeout, setInterval, clearInterval, setImmediate, clearImmediate } from 'timers';
|
|
3
5
|
|
|
4
6
|
// src/content/quality.ts
|
|
5
7
|
function countWords(text) {
|
|
@@ -830,6 +832,32 @@ function parseAtomDate(dateString) {
|
|
|
830
832
|
}
|
|
831
833
|
|
|
832
834
|
// src/feed/atom/extract-entry.ts
|
|
835
|
+
function extractAtomDate(element) {
|
|
836
|
+
let dateText = element.querySelector("updated")?.textContent;
|
|
837
|
+
if (dateText) {
|
|
838
|
+
const parsed = parseAtomDate(dateText);
|
|
839
|
+
if (parsed) return parsed;
|
|
840
|
+
}
|
|
841
|
+
dateText = element.querySelector("modified")?.textContent;
|
|
842
|
+
if (dateText) {
|
|
843
|
+
const parsed = parseAtomDate(dateText);
|
|
844
|
+
if (parsed) return parsed;
|
|
845
|
+
}
|
|
846
|
+
dateText = element.querySelector("issued")?.textContent;
|
|
847
|
+
if (dateText) {
|
|
848
|
+
const parsed = parseAtomDate(dateText);
|
|
849
|
+
if (parsed) return parsed;
|
|
850
|
+
}
|
|
851
|
+
const dcDateElements = element.children.filter((child) => child.tagName === "dc:date");
|
|
852
|
+
if (dcDateElements.length > 0) {
|
|
853
|
+
dateText = dcDateElements[0].textContent;
|
|
854
|
+
if (dateText) {
|
|
855
|
+
const parsed = parseAtomDate(dateText);
|
|
856
|
+
if (parsed) return parsed;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return null;
|
|
860
|
+
}
|
|
833
861
|
function extractPerson(element) {
|
|
834
862
|
const name = element.querySelector("name")?.textContent;
|
|
835
863
|
if (!name) {
|
|
@@ -972,13 +1000,11 @@ function extractEntry(entryElement) {
|
|
|
972
1000
|
if (!title) {
|
|
973
1001
|
throw new Error("Invalid Atom entry: missing required <title> element");
|
|
974
1002
|
}
|
|
975
|
-
const
|
|
976
|
-
if (!updatedRaw) {
|
|
977
|
-
throw new Error("Invalid Atom entry: missing required <updated> element");
|
|
978
|
-
}
|
|
979
|
-
const updated = parseAtomDate(updatedRaw);
|
|
1003
|
+
const updated = extractAtomDate(entryElement);
|
|
980
1004
|
if (!updated) {
|
|
981
|
-
throw new Error(
|
|
1005
|
+
throw new Error(
|
|
1006
|
+
"Invalid Atom entry: missing or invalid date (tried <updated>, <modified>, <issued>, <dc:date>)"
|
|
1007
|
+
);
|
|
982
1008
|
}
|
|
983
1009
|
const entry = {
|
|
984
1010
|
id: cleanText(id),
|
|
@@ -1228,6 +1254,32 @@ function parseXML(xml) {
|
|
|
1228
1254
|
}
|
|
1229
1255
|
|
|
1230
1256
|
// src/feed/atom/extract-feed.ts
|
|
1257
|
+
function extractAtomDate2(element) {
|
|
1258
|
+
let dateText = element.querySelector("updated")?.textContent;
|
|
1259
|
+
if (dateText) {
|
|
1260
|
+
const parsed = parseAtomDate(dateText);
|
|
1261
|
+
if (parsed) return parsed;
|
|
1262
|
+
}
|
|
1263
|
+
dateText = element.querySelector("modified")?.textContent;
|
|
1264
|
+
if (dateText) {
|
|
1265
|
+
const parsed = parseAtomDate(dateText);
|
|
1266
|
+
if (parsed) return parsed;
|
|
1267
|
+
}
|
|
1268
|
+
dateText = element.querySelector("issued")?.textContent;
|
|
1269
|
+
if (dateText) {
|
|
1270
|
+
const parsed = parseAtomDate(dateText);
|
|
1271
|
+
if (parsed) return parsed;
|
|
1272
|
+
}
|
|
1273
|
+
const dcDateElements = element.children.filter((child) => child.tagName === "dc:date");
|
|
1274
|
+
if (dcDateElements.length > 0) {
|
|
1275
|
+
dateText = dcDateElements[0].textContent;
|
|
1276
|
+
if (dateText) {
|
|
1277
|
+
const parsed = parseAtomDate(dateText);
|
|
1278
|
+
if (parsed) return parsed;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
return null;
|
|
1282
|
+
}
|
|
1231
1283
|
function extractPerson2(element) {
|
|
1232
1284
|
const name = element.querySelector("name")?.textContent;
|
|
1233
1285
|
if (!name) {
|
|
@@ -1375,13 +1427,11 @@ function extractFeed(xml) {
|
|
|
1375
1427
|
if (!title) {
|
|
1376
1428
|
throw new Error("Invalid Atom feed: missing required <title> element");
|
|
1377
1429
|
}
|
|
1378
|
-
const
|
|
1379
|
-
if (!updatedRaw) {
|
|
1380
|
-
throw new Error("Invalid Atom feed: missing required <updated> element");
|
|
1381
|
-
}
|
|
1382
|
-
const updated = parseAtomDate(updatedRaw);
|
|
1430
|
+
const updated = extractAtomDate2(feed);
|
|
1383
1431
|
if (!updated) {
|
|
1384
|
-
throw new Error(
|
|
1432
|
+
throw new Error(
|
|
1433
|
+
"Invalid Atom feed: missing or invalid date (tried <updated>, <modified>, <issued>, <dc:date>)"
|
|
1434
|
+
);
|
|
1385
1435
|
}
|
|
1386
1436
|
const result = {
|
|
1387
1437
|
id: cleanText(id),
|
|
@@ -2443,13 +2493,12 @@ async function pluck(input, init) {
|
|
|
2443
2493
|
const startTime = Date.now();
|
|
2444
2494
|
const options = normalizeOptions2(init);
|
|
2445
2495
|
const originalUrl = typeof input === "string" || input instanceof URL ? String(input) : input.url;
|
|
2446
|
-
const
|
|
2447
|
-
const timeoutId = setTimeout(() => abortController.abort(), options.timeout);
|
|
2496
|
+
const signal = AbortSignal.timeout(options.timeout);
|
|
2448
2497
|
try {
|
|
2449
2498
|
const { response, redirectChain, redirectDuration } = await followRedirects(
|
|
2450
2499
|
input,
|
|
2451
2500
|
options,
|
|
2452
|
-
|
|
2501
|
+
signal
|
|
2453
2502
|
);
|
|
2454
2503
|
const finalUrl = response.url;
|
|
2455
2504
|
if (options.throwOnHttpError && !response.ok) {
|
|
@@ -2486,15 +2535,13 @@ async function pluck(input, init) {
|
|
|
2486
2535
|
if (error instanceof PluckTimeoutError || error instanceof PluckNetworkError) {
|
|
2487
2536
|
throw error;
|
|
2488
2537
|
}
|
|
2489
|
-
if (error.name === "
|
|
2538
|
+
if (error.name === "TimeoutError") {
|
|
2490
2539
|
throw new PluckTimeoutError(`Request timeout after ${options.timeout}ms`, options.timeout);
|
|
2491
2540
|
}
|
|
2492
2541
|
if (error instanceof TypeError) {
|
|
2493
2542
|
throw new PluckNetworkError(`Network error: ${error.message}`, error);
|
|
2494
2543
|
}
|
|
2495
2544
|
throw error;
|
|
2496
|
-
} finally {
|
|
2497
|
-
clearTimeout(timeoutId);
|
|
2498
2545
|
}
|
|
2499
2546
|
}
|
|
2500
2547
|
function normalizeOptions2(init) {
|
|
@@ -5046,6 +5093,1718 @@ function extractVerification(doc) {
|
|
|
5046
5093
|
Object.entries(metadata).filter(([_, value]) => value !== void 0)
|
|
5047
5094
|
);
|
|
5048
5095
|
}
|
|
5096
|
+
|
|
5097
|
+
// src/swoop/errors.ts
|
|
5098
|
+
var SwoopError = class extends Error {
|
|
5099
|
+
name = "SwoopError";
|
|
5100
|
+
};
|
|
5101
|
+
var SwoopEnvironmentError = class extends SwoopError {
|
|
5102
|
+
name = "SwoopEnvironmentError";
|
|
5103
|
+
};
|
|
5104
|
+
var SwoopTimeoutError = class extends SwoopError {
|
|
5105
|
+
name = "SwoopTimeoutError";
|
|
5106
|
+
};
|
|
5107
|
+
var SwoopExecutionError = class extends SwoopError {
|
|
5108
|
+
name = "SwoopExecutionError";
|
|
5109
|
+
};
|
|
5110
|
+
var SwoopSecurityError = class extends SwoopError {
|
|
5111
|
+
name = "SwoopSecurityError";
|
|
5112
|
+
};
|
|
5113
|
+
|
|
5114
|
+
// src/swoop/console.ts
|
|
5115
|
+
function createConsoleCapture(now = () => Date.now()) {
|
|
5116
|
+
const entries = [];
|
|
5117
|
+
const formatError = (err) => {
|
|
5118
|
+
const parts = [];
|
|
5119
|
+
parts.push(`${err.name}: ${err.message}${err.stack ? `
|
|
5120
|
+
${err.stack}` : ""}`.trim());
|
|
5121
|
+
try {
|
|
5122
|
+
const props = {};
|
|
5123
|
+
for (const key of Object.getOwnPropertyNames(err)) {
|
|
5124
|
+
if (key === "name" || key === "message" || key === "stack") continue;
|
|
5125
|
+
props[key] = err[key];
|
|
5126
|
+
}
|
|
5127
|
+
if (Object.keys(props).length > 0) {
|
|
5128
|
+
parts.push(`props: ${JSON.stringify(props)}`);
|
|
5129
|
+
}
|
|
5130
|
+
} catch {
|
|
5131
|
+
}
|
|
5132
|
+
return parts.join("\n");
|
|
5133
|
+
};
|
|
5134
|
+
const record = (level, args) => {
|
|
5135
|
+
const msg = args.map((a) => {
|
|
5136
|
+
try {
|
|
5137
|
+
if (a instanceof Error) return formatError(a);
|
|
5138
|
+
if (typeof a === "string") return a;
|
|
5139
|
+
return String(a);
|
|
5140
|
+
} catch {
|
|
5141
|
+
return "[unstringifiable]";
|
|
5142
|
+
}
|
|
5143
|
+
}).join(" ");
|
|
5144
|
+
const argStrings = args.map((a) => {
|
|
5145
|
+
try {
|
|
5146
|
+
if (a instanceof Error) return formatError(a);
|
|
5147
|
+
if (typeof a === "string") return a;
|
|
5148
|
+
return String(a);
|
|
5149
|
+
} catch {
|
|
5150
|
+
return "[unstringifiable]";
|
|
5151
|
+
}
|
|
5152
|
+
});
|
|
5153
|
+
entries.push({ level, message: msg, args: argStrings, time: now() });
|
|
5154
|
+
};
|
|
5155
|
+
return { entries, record };
|
|
5156
|
+
}
|
|
5157
|
+
function installAsyncEnv(init) {
|
|
5158
|
+
const { globalObj } = init;
|
|
5159
|
+
const hostSetTimeout = setTimeout;
|
|
5160
|
+
const hostClearTimeout = clearTimeout;
|
|
5161
|
+
const hostSetInterval = setInterval;
|
|
5162
|
+
const hostClearInterval = clearInterval;
|
|
5163
|
+
const hostSetImmediate = setImmediate;
|
|
5164
|
+
const hostClearImmediate = clearImmediate;
|
|
5165
|
+
const activeTimeouts = /* @__PURE__ */ new Set();
|
|
5166
|
+
const activeIntervals = /* @__PURE__ */ new Set();
|
|
5167
|
+
const activeImmediates = /* @__PURE__ */ new Set();
|
|
5168
|
+
let lastAsyncActivityAt = Date.now();
|
|
5169
|
+
const noteAsyncActivity = () => {
|
|
5170
|
+
lastAsyncActivityAt = Date.now();
|
|
5171
|
+
if (init.debugProbes) {
|
|
5172
|
+
try {
|
|
5173
|
+
globalObj.__swoopStats.lastAsyncActivityAt = lastAsyncActivityAt;
|
|
5174
|
+
} catch {
|
|
5175
|
+
}
|
|
5176
|
+
}
|
|
5177
|
+
};
|
|
5178
|
+
globalObj.__swoop_requestIdleCallback = (cb, _opts) => {
|
|
5179
|
+
return hostSetTimeout(() => {
|
|
5180
|
+
try {
|
|
5181
|
+
noteAsyncActivity();
|
|
5182
|
+
cb({
|
|
5183
|
+
didTimeout: false,
|
|
5184
|
+
timeRemaining: () => 0
|
|
5185
|
+
});
|
|
5186
|
+
} catch {
|
|
5187
|
+
}
|
|
5188
|
+
}, 0);
|
|
5189
|
+
};
|
|
5190
|
+
globalObj.__swoop_cancelIdleCallback = (handle) => hostClearTimeout(handle);
|
|
5191
|
+
globalObj.__swoop_setTimeout = (...args) => {
|
|
5192
|
+
const handle = hostSetTimeout(() => {
|
|
5193
|
+
activeTimeouts.delete(handle);
|
|
5194
|
+
noteAsyncActivity();
|
|
5195
|
+
if (init.debugProbes) {
|
|
5196
|
+
try {
|
|
5197
|
+
globalObj.__swoopStats.timers.timeoutFired++;
|
|
5198
|
+
} catch {
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
args[0]?.call?.(globalObj);
|
|
5202
|
+
}, args[1]);
|
|
5203
|
+
activeTimeouts.add(handle);
|
|
5204
|
+
if (init.debugProbes) {
|
|
5205
|
+
try {
|
|
5206
|
+
globalObj.__swoopStats.timers.timeoutScheduled++;
|
|
5207
|
+
} catch {
|
|
5208
|
+
}
|
|
5209
|
+
}
|
|
5210
|
+
return handle;
|
|
5211
|
+
};
|
|
5212
|
+
globalObj.__swoop_clearTimeout = (handle) => {
|
|
5213
|
+
activeTimeouts.delete(handle);
|
|
5214
|
+
hostClearTimeout(handle);
|
|
5215
|
+
};
|
|
5216
|
+
globalObj.__swoop_setInterval = (...args) => {
|
|
5217
|
+
const handle = hostSetInterval(() => {
|
|
5218
|
+
noteAsyncActivity();
|
|
5219
|
+
if (init.debugProbes) {
|
|
5220
|
+
try {
|
|
5221
|
+
globalObj.__swoopStats.timers.intervalFired++;
|
|
5222
|
+
} catch {
|
|
5223
|
+
}
|
|
5224
|
+
}
|
|
5225
|
+
args[0]?.call?.(globalObj);
|
|
5226
|
+
}, args[1]);
|
|
5227
|
+
activeIntervals.add(handle);
|
|
5228
|
+
if (init.debugProbes) {
|
|
5229
|
+
try {
|
|
5230
|
+
globalObj.__swoopStats.timers.intervalScheduled++;
|
|
5231
|
+
} catch {
|
|
5232
|
+
}
|
|
5233
|
+
}
|
|
5234
|
+
return handle;
|
|
5235
|
+
};
|
|
5236
|
+
globalObj.__swoop_clearInterval = (handle) => {
|
|
5237
|
+
activeIntervals.delete(handle);
|
|
5238
|
+
hostClearInterval(handle);
|
|
5239
|
+
};
|
|
5240
|
+
globalObj.__swoop_queueMicrotask = (cb) => {
|
|
5241
|
+
try {
|
|
5242
|
+
process.nextTick(() => {
|
|
5243
|
+
noteAsyncActivity();
|
|
5244
|
+
cb.call(globalObj);
|
|
5245
|
+
});
|
|
5246
|
+
} catch {
|
|
5247
|
+
hostSetTimeout(() => {
|
|
5248
|
+
noteAsyncActivity();
|
|
5249
|
+
cb.call(globalObj);
|
|
5250
|
+
}, 0);
|
|
5251
|
+
}
|
|
5252
|
+
};
|
|
5253
|
+
globalObj.__swoop_setImmediate = (cb, ...args) => {
|
|
5254
|
+
const handle = hostSetImmediate(() => {
|
|
5255
|
+
activeImmediates.delete(handle);
|
|
5256
|
+
try {
|
|
5257
|
+
noteAsyncActivity();
|
|
5258
|
+
cb.call(globalObj, ...args);
|
|
5259
|
+
} catch {
|
|
5260
|
+
}
|
|
5261
|
+
});
|
|
5262
|
+
activeImmediates.add(handle);
|
|
5263
|
+
return handle;
|
|
5264
|
+
};
|
|
5265
|
+
globalObj.__swoop_clearImmediate = (handle) => {
|
|
5266
|
+
activeImmediates.delete(handle);
|
|
5267
|
+
hostClearImmediate(handle);
|
|
5268
|
+
};
|
|
5269
|
+
globalObj.__swoop_requestAnimationFrame = (cb) => {
|
|
5270
|
+
if (init.debugProbes) {
|
|
5271
|
+
try {
|
|
5272
|
+
globalObj.__swoopStats.timers.rafScheduled++;
|
|
5273
|
+
} catch {
|
|
5274
|
+
}
|
|
5275
|
+
}
|
|
5276
|
+
const id = hostSetTimeout(() => {
|
|
5277
|
+
if (init.debugProbes) {
|
|
5278
|
+
try {
|
|
5279
|
+
globalObj.__swoopStats.timers.rafFired++;
|
|
5280
|
+
} catch {
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
5283
|
+
noteAsyncActivity();
|
|
5284
|
+
cb.call(globalObj, Date.now());
|
|
5285
|
+
}, 16);
|
|
5286
|
+
return id;
|
|
5287
|
+
};
|
|
5288
|
+
globalObj.__swoop_cancelAnimationFrame = (id) => hostClearTimeout(id);
|
|
5289
|
+
const cleanup = () => {
|
|
5290
|
+
for (const h of activeTimeouts) hostClearTimeout(h);
|
|
5291
|
+
for (const h of activeIntervals) hostClearInterval(h);
|
|
5292
|
+
for (const h of activeImmediates) hostClearImmediate(h);
|
|
5293
|
+
};
|
|
5294
|
+
return {
|
|
5295
|
+
hostSetTimeout,
|
|
5296
|
+
hostClearTimeout,
|
|
5297
|
+
hostSetInterval,
|
|
5298
|
+
hostClearInterval,
|
|
5299
|
+
hostSetImmediate,
|
|
5300
|
+
hostClearImmediate,
|
|
5301
|
+
noteAsyncActivity,
|
|
5302
|
+
getLastAsyncActivityAt: () => lastAsyncActivityAt,
|
|
5303
|
+
cleanup
|
|
5304
|
+
};
|
|
5305
|
+
}
|
|
5306
|
+
|
|
5307
|
+
// src/swoop/env/base.ts
|
|
5308
|
+
function computeBaseForResolve(finalUrl, document) {
|
|
5309
|
+
const pageUrl = new URL(finalUrl);
|
|
5310
|
+
const baseEl = document?.querySelector?.("base") ?? null;
|
|
5311
|
+
const baseTagHref = baseEl?.getAttribute?.("href") ?? null;
|
|
5312
|
+
const baseForResolve = (() => {
|
|
5313
|
+
try {
|
|
5314
|
+
return baseTagHref ? new URL(String(baseTagHref), finalUrl) : pageUrl;
|
|
5315
|
+
} catch {
|
|
5316
|
+
return pageUrl;
|
|
5317
|
+
}
|
|
5318
|
+
})();
|
|
5319
|
+
return { baseForResolve, baseTagHref, baseEl };
|
|
5320
|
+
}
|
|
5321
|
+
function patchDocumentBaseURI(document, baseForResolve) {
|
|
5322
|
+
try {
|
|
5323
|
+
Object.defineProperty(document, "baseURI", {
|
|
5324
|
+
configurable: true,
|
|
5325
|
+
get: () => baseForResolve.href
|
|
5326
|
+
});
|
|
5327
|
+
} catch {
|
|
5328
|
+
}
|
|
5329
|
+
}
|
|
5330
|
+
function patchBaseElementHref(baseEl, baseForResolve) {
|
|
5331
|
+
try {
|
|
5332
|
+
if (baseEl) {
|
|
5333
|
+
Object.defineProperty(baseEl, "href", {
|
|
5334
|
+
configurable: true,
|
|
5335
|
+
get: () => baseForResolve.href,
|
|
5336
|
+
set: (_v) => {
|
|
5337
|
+
}
|
|
5338
|
+
});
|
|
5339
|
+
}
|
|
5340
|
+
} catch {
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
|
|
5344
|
+
// src/swoop/env/browser.ts
|
|
5345
|
+
function installBrowserShims(init) {
|
|
5346
|
+
const { globalObj, domWindow, documentBaseUriForDom } = init;
|
|
5347
|
+
const doc = init.document ?? globalObj.document;
|
|
5348
|
+
try {
|
|
5349
|
+
if (doc && doc.defaultView == null) doc.defaultView = globalObj;
|
|
5350
|
+
} catch {
|
|
5351
|
+
}
|
|
5352
|
+
try {
|
|
5353
|
+
if (doc && doc.baseURI == null) doc.baseURI = documentBaseUriForDom;
|
|
5354
|
+
} catch {
|
|
5355
|
+
}
|
|
5356
|
+
globalObj.navigator ??= {};
|
|
5357
|
+
globalObj.navigator.language ??= "en";
|
|
5358
|
+
globalObj.navigator.languages ??= [globalObj.navigator.language];
|
|
5359
|
+
globalObj.navigator.onLine ??= true;
|
|
5360
|
+
globalObj.screen ??= {
|
|
5361
|
+
width: 1280,
|
|
5362
|
+
height: 720,
|
|
5363
|
+
availWidth: 1280,
|
|
5364
|
+
availHeight: 720,
|
|
5365
|
+
colorDepth: 24
|
|
5366
|
+
};
|
|
5367
|
+
globalObj.devicePixelRatio ??= 1;
|
|
5368
|
+
globalObj.innerWidth ??= globalObj.screen.width ?? 1280;
|
|
5369
|
+
globalObj.innerHeight ??= globalObj.screen.height ?? 720;
|
|
5370
|
+
globalObj.MessageChannel ??= globalThis.MessageChannel;
|
|
5371
|
+
globalObj.MessagePort ??= globalThis.MessagePort;
|
|
5372
|
+
globalObj.MessageEvent ??= globalThis.MessageEvent;
|
|
5373
|
+
globalObj.BroadcastChannel ??= globalThis.BroadcastChannel;
|
|
5374
|
+
globalObj.getComputedStyle ??= (el) => {
|
|
5375
|
+
const style = el?.style ?? {};
|
|
5376
|
+
return new Proxy(style, {
|
|
5377
|
+
get(target, prop) {
|
|
5378
|
+
if (prop === "getPropertyValue") return (name) => target?.[name] ?? "";
|
|
5379
|
+
return target[prop] ?? "";
|
|
5380
|
+
}
|
|
5381
|
+
});
|
|
5382
|
+
};
|
|
5383
|
+
globalObj.PopStateEvent ??= class PopStateEvent extends (globalObj.Event ?? Event) {
|
|
5384
|
+
state;
|
|
5385
|
+
constructor(type, init2) {
|
|
5386
|
+
super(type, init2);
|
|
5387
|
+
this.state = init2?.state;
|
|
5388
|
+
}
|
|
5389
|
+
};
|
|
5390
|
+
globalObj.HashChangeEvent ??= class HashChangeEvent extends (globalObj.Event ?? Event) {
|
|
5391
|
+
oldURL;
|
|
5392
|
+
newURL;
|
|
5393
|
+
constructor(type, init2) {
|
|
5394
|
+
super(type, init2);
|
|
5395
|
+
this.oldURL = init2?.oldURL ?? "";
|
|
5396
|
+
this.newURL = init2?.newURL ?? "";
|
|
5397
|
+
}
|
|
5398
|
+
};
|
|
5399
|
+
globalObj.matchMedia ??= (query) => {
|
|
5400
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
5401
|
+
const mql = {
|
|
5402
|
+
matches: false,
|
|
5403
|
+
media: String(query ?? ""),
|
|
5404
|
+
onchange: null,
|
|
5405
|
+
addListener: (cb) => {
|
|
5406
|
+
if (typeof cb === "function") listeners.add(cb);
|
|
5407
|
+
},
|
|
5408
|
+
removeListener: (cb) => {
|
|
5409
|
+
listeners.delete(cb);
|
|
5410
|
+
},
|
|
5411
|
+
addEventListener: (type, cb) => {
|
|
5412
|
+
if (String(type) === "change" && typeof cb === "function") listeners.add(cb);
|
|
5413
|
+
},
|
|
5414
|
+
removeEventListener: (type, cb) => {
|
|
5415
|
+
if (String(type) === "change") listeners.delete(cb);
|
|
5416
|
+
},
|
|
5417
|
+
dispatchEvent: (evt) => {
|
|
5418
|
+
try {
|
|
5419
|
+
for (const cb of listeners) cb.call(mql, evt);
|
|
5420
|
+
if (typeof mql.onchange === "function") mql.onchange.call(mql, evt);
|
|
5421
|
+
} catch {
|
|
5422
|
+
}
|
|
5423
|
+
return true;
|
|
5424
|
+
}
|
|
5425
|
+
};
|
|
5426
|
+
return mql;
|
|
5427
|
+
};
|
|
5428
|
+
globalObj.IntersectionObserver ??= class IntersectionObserver {
|
|
5429
|
+
observe(_el) {
|
|
5430
|
+
}
|
|
5431
|
+
unobserve(_el) {
|
|
5432
|
+
}
|
|
5433
|
+
disconnect() {
|
|
5434
|
+
}
|
|
5435
|
+
takeRecords() {
|
|
5436
|
+
return [];
|
|
5437
|
+
}
|
|
5438
|
+
};
|
|
5439
|
+
globalObj.ResizeObserver ??= class ResizeObserver {
|
|
5440
|
+
observe(_el) {
|
|
5441
|
+
}
|
|
5442
|
+
unobserve(_el) {
|
|
5443
|
+
}
|
|
5444
|
+
disconnect() {
|
|
5445
|
+
}
|
|
5446
|
+
};
|
|
5447
|
+
globalObj.MutationObserver ??= class MutationObserver {
|
|
5448
|
+
observe(_target, _options) {
|
|
5449
|
+
}
|
|
5450
|
+
disconnect() {
|
|
5451
|
+
}
|
|
5452
|
+
takeRecords() {
|
|
5453
|
+
return [];
|
|
5454
|
+
}
|
|
5455
|
+
};
|
|
5456
|
+
globalObj.URL ??= URL;
|
|
5457
|
+
globalObj.URLSearchParams ??= URLSearchParams;
|
|
5458
|
+
globalObj.WebAssembly ??= WebAssembly;
|
|
5459
|
+
globalObj.TextEncoder ??= TextEncoder;
|
|
5460
|
+
globalObj.TextDecoder ??= TextDecoder;
|
|
5461
|
+
globalObj.Headers ??= globalThis.Headers;
|
|
5462
|
+
globalObj.Request ??= globalThis.Request;
|
|
5463
|
+
globalObj.Response ??= globalThis.Response;
|
|
5464
|
+
globalObj.crypto ??= globalThis.crypto;
|
|
5465
|
+
globalObj.performance ??= globalThis.performance;
|
|
5466
|
+
globalObj.structuredClone ??= globalThis.structuredClone;
|
|
5467
|
+
globalObj.Blob ??= globalThis.Blob;
|
|
5468
|
+
globalObj.FormData ??= globalThis.FormData;
|
|
5469
|
+
globalObj.File ??= globalThis.File;
|
|
5470
|
+
globalObj.ReadableStream ??= globalThis.ReadableStream;
|
|
5471
|
+
globalObj.atob ??= (data) => Buffer.from(data, "base64").toString("binary");
|
|
5472
|
+
globalObj.btoa ??= (data) => Buffer.from(data, "binary").toString("base64");
|
|
5473
|
+
globalObj.WebSocket ??= class WebSocket {
|
|
5474
|
+
constructor() {
|
|
5475
|
+
throw new Error("swoop: WebSocket is not implemented");
|
|
5476
|
+
}
|
|
5477
|
+
};
|
|
5478
|
+
globalObj.EventSource ??= class EventSource {
|
|
5479
|
+
constructor() {
|
|
5480
|
+
throw new Error("swoop: EventSource is not implemented");
|
|
5481
|
+
}
|
|
5482
|
+
};
|
|
5483
|
+
globalObj.Worker ??= class Worker {
|
|
5484
|
+
constructor() {
|
|
5485
|
+
throw new Error("swoop: Worker is not implemented");
|
|
5486
|
+
}
|
|
5487
|
+
};
|
|
5488
|
+
globalObj.SharedWorker ??= class SharedWorker {
|
|
5489
|
+
constructor() {
|
|
5490
|
+
throw new Error("swoop: SharedWorker is not implemented");
|
|
5491
|
+
}
|
|
5492
|
+
};
|
|
5493
|
+
globalObj.indexedDB ??= void 0;
|
|
5494
|
+
globalObj.caches ??= void 0;
|
|
5495
|
+
globalObj.Notification ??= void 0;
|
|
5496
|
+
globalObj.Document ??= domWindow.Document;
|
|
5497
|
+
globalObj.HTMLDocument ??= domWindow.HTMLDocument ?? init.document?.constructor;
|
|
5498
|
+
globalObj.Element ??= domWindow.Element;
|
|
5499
|
+
globalObj.HTMLElement ??= domWindow.HTMLElement;
|
|
5500
|
+
globalObj.Node ??= domWindow.Node;
|
|
5501
|
+
globalObj.NodeFilter ??= domWindow.NodeFilter ?? {
|
|
5502
|
+
FILTER_ACCEPT: 1,
|
|
5503
|
+
FILTER_REJECT: 2,
|
|
5504
|
+
FILTER_SKIP: 3,
|
|
5505
|
+
SHOW_ALL: 4294967295,
|
|
5506
|
+
SHOW_ELEMENT: 1,
|
|
5507
|
+
SHOW_ATTRIBUTE: 2,
|
|
5508
|
+
SHOW_TEXT: 4,
|
|
5509
|
+
SHOW_CDATA_SECTION: 8,
|
|
5510
|
+
SHOW_ENTITY_REFERENCE: 16,
|
|
5511
|
+
SHOW_ENTITY: 32,
|
|
5512
|
+
SHOW_PROCESSING_INSTRUCTION: 64,
|
|
5513
|
+
SHOW_COMMENT: 128,
|
|
5514
|
+
SHOW_DOCUMENT: 256,
|
|
5515
|
+
SHOW_DOCUMENT_TYPE: 512,
|
|
5516
|
+
SHOW_DOCUMENT_FRAGMENT: 1024,
|
|
5517
|
+
SHOW_NOTATION: 2048
|
|
5518
|
+
};
|
|
5519
|
+
}
|
|
5520
|
+
|
|
5521
|
+
// src/swoop/env/cookie.ts
|
|
5522
|
+
function installCookieJar(document) {
|
|
5523
|
+
let cookieJar = "";
|
|
5524
|
+
try {
|
|
5525
|
+
Object.defineProperty(document, "cookie", {
|
|
5526
|
+
configurable: true,
|
|
5527
|
+
get() {
|
|
5528
|
+
return cookieJar;
|
|
5529
|
+
},
|
|
5530
|
+
set(value) {
|
|
5531
|
+
if (typeof value === "string" && value.length > 0) {
|
|
5532
|
+
cookieJar = cookieJar ? `${cookieJar}; ${value}` : value;
|
|
5533
|
+
}
|
|
5534
|
+
}
|
|
5535
|
+
});
|
|
5536
|
+
} catch {
|
|
5537
|
+
}
|
|
5538
|
+
}
|
|
5539
|
+
|
|
5540
|
+
// src/swoop/env/fetch.ts
|
|
5541
|
+
function installFetchShim(init) {
|
|
5542
|
+
let pendingFetches = 0;
|
|
5543
|
+
init.globalObj.__swoop_fetch = typeof init.hostFetch === "function" ? (...args) => {
|
|
5544
|
+
init.noteAsyncActivity();
|
|
5545
|
+
pendingFetches++;
|
|
5546
|
+
try {
|
|
5547
|
+
const input = args[0];
|
|
5548
|
+
const initReq = args[1];
|
|
5549
|
+
let resolvedInput = input;
|
|
5550
|
+
if (typeof input === "string") {
|
|
5551
|
+
resolvedInput = new URL(input, init.baseForResolveHref).href;
|
|
5552
|
+
} else if (input instanceof URL) {
|
|
5553
|
+
resolvedInput = input.href;
|
|
5554
|
+
} else if (typeof input.url === "string") {
|
|
5555
|
+
try {
|
|
5556
|
+
resolvedInput = new URL(input.url, init.baseForResolveHref).href;
|
|
5557
|
+
} catch {
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
if (init.debugFetch) {
|
|
5561
|
+
init.recordDebug(["[fetch]", resolvedInput]);
|
|
5562
|
+
}
|
|
5563
|
+
const controller = new AbortController();
|
|
5564
|
+
const timeoutHandle = init.hostSetTimeout(() => controller.abort(), init.remainingMs());
|
|
5565
|
+
const mergedInit = { ...initReq, signal: controller.signal };
|
|
5566
|
+
const p = init.hostFetch(resolvedInput, mergedInit).finally(() => {
|
|
5567
|
+
init.hostClearTimeout(timeoutHandle);
|
|
5568
|
+
});
|
|
5569
|
+
return p.finally(() => {
|
|
5570
|
+
try {
|
|
5571
|
+
init.queueMicrotask(() => {
|
|
5572
|
+
pendingFetches--;
|
|
5573
|
+
init.noteAsyncActivity();
|
|
5574
|
+
});
|
|
5575
|
+
} catch {
|
|
5576
|
+
pendingFetches--;
|
|
5577
|
+
init.noteAsyncActivity();
|
|
5578
|
+
}
|
|
5579
|
+
});
|
|
5580
|
+
} catch (err) {
|
|
5581
|
+
pendingFetches--;
|
|
5582
|
+
init.noteAsyncActivity();
|
|
5583
|
+
throw err;
|
|
5584
|
+
}
|
|
5585
|
+
} : void 0;
|
|
5586
|
+
return {
|
|
5587
|
+
getPendingFetches: () => pendingFetches
|
|
5588
|
+
};
|
|
5589
|
+
}
|
|
5590
|
+
|
|
5591
|
+
// src/swoop/env/navigation.ts
|
|
5592
|
+
function createNavigationShims(init) {
|
|
5593
|
+
const { pageUrl } = init;
|
|
5594
|
+
const resolveAndSetHref = (href) => {
|
|
5595
|
+
try {
|
|
5596
|
+
const next = new URL(href, pageUrl.href);
|
|
5597
|
+
pageUrl.href = next.href;
|
|
5598
|
+
init.onNavigate?.(pageUrl.href);
|
|
5599
|
+
} catch {
|
|
5600
|
+
}
|
|
5601
|
+
};
|
|
5602
|
+
const location = {};
|
|
5603
|
+
try {
|
|
5604
|
+
Object.defineProperties(location, {
|
|
5605
|
+
href: { get: () => pageUrl.href, set: (v) => resolveAndSetHref(String(v)) },
|
|
5606
|
+
origin: { get: () => pageUrl.origin },
|
|
5607
|
+
protocol: {
|
|
5608
|
+
get: () => pageUrl.protocol,
|
|
5609
|
+
set: (v) => {
|
|
5610
|
+
pageUrl.protocol = String(v);
|
|
5611
|
+
}
|
|
5612
|
+
},
|
|
5613
|
+
host: {
|
|
5614
|
+
get: () => pageUrl.host,
|
|
5615
|
+
set: (v) => {
|
|
5616
|
+
pageUrl.host = String(v);
|
|
5617
|
+
}
|
|
5618
|
+
},
|
|
5619
|
+
hostname: {
|
|
5620
|
+
get: () => pageUrl.hostname,
|
|
5621
|
+
set: (v) => {
|
|
5622
|
+
pageUrl.hostname = String(v);
|
|
5623
|
+
}
|
|
5624
|
+
},
|
|
5625
|
+
port: {
|
|
5626
|
+
get: () => pageUrl.port,
|
|
5627
|
+
set: (v) => {
|
|
5628
|
+
pageUrl.port = String(v);
|
|
5629
|
+
}
|
|
5630
|
+
},
|
|
5631
|
+
pathname: { get: () => pageUrl.pathname, set: (v) => resolveAndSetHref(String(v)) },
|
|
5632
|
+
search: {
|
|
5633
|
+
get: () => pageUrl.search,
|
|
5634
|
+
set: (v) => {
|
|
5635
|
+
pageUrl.search = String(v);
|
|
5636
|
+
}
|
|
5637
|
+
},
|
|
5638
|
+
hash: {
|
|
5639
|
+
get: () => pageUrl.hash,
|
|
5640
|
+
set: (v) => {
|
|
5641
|
+
pageUrl.hash = String(v);
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
});
|
|
5645
|
+
} catch {
|
|
5646
|
+
}
|
|
5647
|
+
location.toString = () => pageUrl.href;
|
|
5648
|
+
location.assign = (href) => resolveAndSetHref(href);
|
|
5649
|
+
location.replace = (href) => resolveAndSetHref(href);
|
|
5650
|
+
location.reload = () => {
|
|
5651
|
+
};
|
|
5652
|
+
let historyState = null;
|
|
5653
|
+
const history = {
|
|
5654
|
+
get state() {
|
|
5655
|
+
return historyState;
|
|
5656
|
+
},
|
|
5657
|
+
pushState: (state, _title, url) => {
|
|
5658
|
+
historyState = state;
|
|
5659
|
+
if (url != null) resolveAndSetHref(String(url));
|
|
5660
|
+
try {
|
|
5661
|
+
init.onPopState?.(state);
|
|
5662
|
+
} catch {
|
|
5663
|
+
}
|
|
5664
|
+
},
|
|
5665
|
+
replaceState: (state, _title, url) => {
|
|
5666
|
+
historyState = state;
|
|
5667
|
+
if (url != null) resolveAndSetHref(String(url));
|
|
5668
|
+
},
|
|
5669
|
+
back: () => {
|
|
5670
|
+
},
|
|
5671
|
+
forward: () => {
|
|
5672
|
+
},
|
|
5673
|
+
go: (_delta) => {
|
|
5674
|
+
}
|
|
5675
|
+
};
|
|
5676
|
+
return { location, history };
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
// src/swoop/env/permissive.ts
|
|
5680
|
+
function installPermissiveShims(globalObj) {
|
|
5681
|
+
const makeStorage = () => {
|
|
5682
|
+
const store = /* @__PURE__ */ new Map();
|
|
5683
|
+
return {
|
|
5684
|
+
get length() {
|
|
5685
|
+
return store.size;
|
|
5686
|
+
},
|
|
5687
|
+
clear() {
|
|
5688
|
+
store.clear();
|
|
5689
|
+
},
|
|
5690
|
+
getItem(key) {
|
|
5691
|
+
return store.has(String(key)) ? store.get(String(key)) : null;
|
|
5692
|
+
},
|
|
5693
|
+
key(index) {
|
|
5694
|
+
return Array.from(store.keys())[index] ?? null;
|
|
5695
|
+
},
|
|
5696
|
+
removeItem(key) {
|
|
5697
|
+
store.delete(String(key));
|
|
5698
|
+
},
|
|
5699
|
+
setItem(key, value) {
|
|
5700
|
+
store.set(String(key), String(value));
|
|
5701
|
+
}
|
|
5702
|
+
};
|
|
5703
|
+
};
|
|
5704
|
+
globalObj.localStorage ??= makeStorage();
|
|
5705
|
+
globalObj.sessionStorage ??= makeStorage();
|
|
5706
|
+
globalObj.scrollTo ??= () => {
|
|
5707
|
+
};
|
|
5708
|
+
try {
|
|
5709
|
+
if (globalObj.HTMLElement?.prototype && !globalObj.HTMLElement.prototype.getBoundingClientRect) {
|
|
5710
|
+
globalObj.HTMLElement.prototype.getBoundingClientRect = () => ({
|
|
5711
|
+
x: 0,
|
|
5712
|
+
y: 0,
|
|
5713
|
+
width: 0,
|
|
5714
|
+
height: 0,
|
|
5715
|
+
top: 0,
|
|
5716
|
+
left: 0,
|
|
5717
|
+
right: 0,
|
|
5718
|
+
bottom: 0,
|
|
5719
|
+
toJSON() {
|
|
5720
|
+
}
|
|
5721
|
+
});
|
|
5722
|
+
}
|
|
5723
|
+
} catch {
|
|
5724
|
+
}
|
|
5725
|
+
globalObj.process ??= { env: { NODE_ENV: "production" } };
|
|
5726
|
+
}
|
|
5727
|
+
|
|
5728
|
+
// src/swoop/env/xhr.ts
|
|
5729
|
+
function installXMLHttpRequest(init) {
|
|
5730
|
+
const g = init.globalObj;
|
|
5731
|
+
if (g.XMLHttpRequest) return;
|
|
5732
|
+
class Xhr {
|
|
5733
|
+
// readyState constants
|
|
5734
|
+
static UNSENT = 0;
|
|
5735
|
+
static OPENED = 1;
|
|
5736
|
+
static HEADERS_RECEIVED = 2;
|
|
5737
|
+
static LOADING = 3;
|
|
5738
|
+
static DONE = 4;
|
|
5739
|
+
UNSENT = 0;
|
|
5740
|
+
OPENED = 1;
|
|
5741
|
+
HEADERS_RECEIVED = 2;
|
|
5742
|
+
LOADING = 3;
|
|
5743
|
+
DONE = 4;
|
|
5744
|
+
readyState = 0;
|
|
5745
|
+
status = 0;
|
|
5746
|
+
statusText = "";
|
|
5747
|
+
responseType = "";
|
|
5748
|
+
response = null;
|
|
5749
|
+
responseText = "";
|
|
5750
|
+
timeout = 0;
|
|
5751
|
+
withCredentials = false;
|
|
5752
|
+
onreadystatechange = null;
|
|
5753
|
+
onload = null;
|
|
5754
|
+
onerror = null;
|
|
5755
|
+
ontimeout = null;
|
|
5756
|
+
onabort = null;
|
|
5757
|
+
_method = "GET";
|
|
5758
|
+
_url = "";
|
|
5759
|
+
_headers = /* @__PURE__ */ new Map();
|
|
5760
|
+
_respHeaders = null;
|
|
5761
|
+
_listeners = /* @__PURE__ */ new Map();
|
|
5762
|
+
_controller = null;
|
|
5763
|
+
addEventListener(type, cb) {
|
|
5764
|
+
const t = String(type);
|
|
5765
|
+
const set = this._listeners.get(t) ?? /* @__PURE__ */ new Set();
|
|
5766
|
+
set.add(cb);
|
|
5767
|
+
this._listeners.set(t, set);
|
|
5768
|
+
}
|
|
5769
|
+
removeEventListener(type, cb) {
|
|
5770
|
+
const t = String(type);
|
|
5771
|
+
this._listeners.get(t)?.delete(cb);
|
|
5772
|
+
}
|
|
5773
|
+
_dispatch(type) {
|
|
5774
|
+
const ev = { type, target: this, currentTarget: this };
|
|
5775
|
+
this._listeners.get(type)?.forEach((cb) => {
|
|
5776
|
+
try {
|
|
5777
|
+
cb(ev);
|
|
5778
|
+
} catch {
|
|
5779
|
+
}
|
|
5780
|
+
});
|
|
5781
|
+
}
|
|
5782
|
+
_setReadyState(n) {
|
|
5783
|
+
this.readyState = n;
|
|
5784
|
+
try {
|
|
5785
|
+
this.onreadystatechange?.();
|
|
5786
|
+
} catch {
|
|
5787
|
+
}
|
|
5788
|
+
this._dispatch("readystatechange");
|
|
5789
|
+
}
|
|
5790
|
+
open(method, url, async = true) {
|
|
5791
|
+
this._method = String(method ?? "GET").toUpperCase();
|
|
5792
|
+
this._url = String(url ?? "");
|
|
5793
|
+
this._headers.clear();
|
|
5794
|
+
this._respHeaders = null;
|
|
5795
|
+
this._controller = null;
|
|
5796
|
+
this._setReadyState(1);
|
|
5797
|
+
}
|
|
5798
|
+
setRequestHeader(name, value) {
|
|
5799
|
+
this._headers.set(String(name), String(value));
|
|
5800
|
+
}
|
|
5801
|
+
getResponseHeader(name) {
|
|
5802
|
+
try {
|
|
5803
|
+
return this._respHeaders?.get(String(name)) ?? null;
|
|
5804
|
+
} catch {
|
|
5805
|
+
return null;
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
getAllResponseHeaders() {
|
|
5809
|
+
try {
|
|
5810
|
+
if (!this._respHeaders) return "";
|
|
5811
|
+
let out = "";
|
|
5812
|
+
this._respHeaders.forEach((v, k) => {
|
|
5813
|
+
out += `${k}: ${v}\r
|
|
5814
|
+
`;
|
|
5815
|
+
});
|
|
5816
|
+
return out;
|
|
5817
|
+
} catch {
|
|
5818
|
+
return "";
|
|
5819
|
+
}
|
|
5820
|
+
}
|
|
5821
|
+
overrideMimeType(_mime) {
|
|
5822
|
+
}
|
|
5823
|
+
abort() {
|
|
5824
|
+
try {
|
|
5825
|
+
this._controller?.abort();
|
|
5826
|
+
} catch {
|
|
5827
|
+
}
|
|
5828
|
+
try {
|
|
5829
|
+
this.onabort?.();
|
|
5830
|
+
} catch {
|
|
5831
|
+
}
|
|
5832
|
+
this._dispatch("abort");
|
|
5833
|
+
}
|
|
5834
|
+
send(body) {
|
|
5835
|
+
const doFetch = async () => {
|
|
5836
|
+
const url = init.resolveUrl(this._url);
|
|
5837
|
+
const controller = new AbortController();
|
|
5838
|
+
this._controller = controller;
|
|
5839
|
+
let timeoutHandle;
|
|
5840
|
+
const ms = Math.min(this.timeout || 0, init.remainingMs());
|
|
5841
|
+
if (ms > 0) {
|
|
5842
|
+
timeoutHandle = init.hostSetTimeout(() => controller.abort(), ms);
|
|
5843
|
+
}
|
|
5844
|
+
try {
|
|
5845
|
+
const headers = {};
|
|
5846
|
+
for (const [k, v] of this._headers.entries()) headers[k] = v;
|
|
5847
|
+
if (!init.fetch) throw new Error("swoop: host fetch is unavailable in this Node runtime");
|
|
5848
|
+
const resp = await init.fetch(url, {
|
|
5849
|
+
method: this._method,
|
|
5850
|
+
headers,
|
|
5851
|
+
body,
|
|
5852
|
+
signal: controller.signal
|
|
5853
|
+
// credentials/withCredentials ignored (Node fetch differs); best-effort.
|
|
5854
|
+
});
|
|
5855
|
+
this._respHeaders = resp.headers;
|
|
5856
|
+
this.status = resp.status;
|
|
5857
|
+
this.statusText = resp.statusText ?? "";
|
|
5858
|
+
this._setReadyState(2);
|
|
5859
|
+
this._setReadyState(3);
|
|
5860
|
+
if (this.responseType === "arraybuffer") {
|
|
5861
|
+
this.response = await resp.arrayBuffer();
|
|
5862
|
+
this.responseText = "";
|
|
5863
|
+
} else if (this.responseType === "blob") {
|
|
5864
|
+
this.response = resp.blob ? await resp.blob() : await resp.arrayBuffer();
|
|
5865
|
+
this.responseText = "";
|
|
5866
|
+
} else {
|
|
5867
|
+
const text = await resp.text();
|
|
5868
|
+
this.responseText = text;
|
|
5869
|
+
if (this.responseType === "json") {
|
|
5870
|
+
try {
|
|
5871
|
+
this.response = JSON.parse(text);
|
|
5872
|
+
} catch {
|
|
5873
|
+
this.response = null;
|
|
5874
|
+
}
|
|
5875
|
+
} else {
|
|
5876
|
+
this.response = text;
|
|
5877
|
+
}
|
|
5878
|
+
}
|
|
5879
|
+
this._setReadyState(4);
|
|
5880
|
+
try {
|
|
5881
|
+
this.onload?.();
|
|
5882
|
+
} catch {
|
|
5883
|
+
}
|
|
5884
|
+
this._dispatch("load");
|
|
5885
|
+
this._dispatch("loadend");
|
|
5886
|
+
} catch {
|
|
5887
|
+
this._setReadyState(4);
|
|
5888
|
+
const aborted = this._controller?.signal?.aborted;
|
|
5889
|
+
if (aborted) {
|
|
5890
|
+
try {
|
|
5891
|
+
this.ontimeout?.();
|
|
5892
|
+
} catch {
|
|
5893
|
+
}
|
|
5894
|
+
this._dispatch("timeout");
|
|
5895
|
+
} else {
|
|
5896
|
+
try {
|
|
5897
|
+
this.onerror?.();
|
|
5898
|
+
} catch {
|
|
5899
|
+
}
|
|
5900
|
+
this._dispatch("error");
|
|
5901
|
+
}
|
|
5902
|
+
this._dispatch("loadend");
|
|
5903
|
+
} finally {
|
|
5904
|
+
if (timeoutHandle) init.hostClearTimeout(timeoutHandle);
|
|
5905
|
+
}
|
|
5906
|
+
};
|
|
5907
|
+
void doFetch();
|
|
5908
|
+
}
|
|
5909
|
+
}
|
|
5910
|
+
g.XMLHttpRequest = Xhr;
|
|
5911
|
+
}
|
|
5912
|
+
function synthesizeLifecycle(context, timeoutMs = 50) {
|
|
5913
|
+
vm4.runInContext(
|
|
5914
|
+
`
|
|
5915
|
+
try {
|
|
5916
|
+
const EventCtor = (globalThis.Event || (globalThis.window && globalThis.window.Event));
|
|
5917
|
+
const dispatchDoc = (type) => { try { document.dispatchEvent(new EventCtor(type)); } catch {} };
|
|
5918
|
+
const dispatchWin = (type) => { try { window.dispatchEvent(new EventCtor(type)); } catch {} };
|
|
5919
|
+
|
|
5920
|
+
if (typeof document.readyState !== "string") {
|
|
5921
|
+
try { document.readyState = "loading"; } catch {}
|
|
5922
|
+
}
|
|
5923
|
+
|
|
5924
|
+
dispatchDoc("readystatechange");
|
|
5925
|
+
try { document.readyState = "interactive"; } catch {}
|
|
5926
|
+
dispatchDoc("readystatechange");
|
|
5927
|
+
dispatchDoc("DOMContentLoaded");
|
|
5928
|
+
try { document.readyState = "complete"; } catch {}
|
|
5929
|
+
dispatchDoc("readystatechange");
|
|
5930
|
+
|
|
5931
|
+
try {
|
|
5932
|
+
if (typeof document.visibilityState !== "string") document.visibilityState = "visible";
|
|
5933
|
+
if (typeof document.hidden !== "boolean") document.hidden = false;
|
|
5934
|
+
if (typeof document.hasFocus !== "function") document.hasFocus = () => true;
|
|
5935
|
+
} catch {}
|
|
5936
|
+
dispatchDoc("visibilitychange");
|
|
5937
|
+
|
|
5938
|
+
dispatchWin("pageshow");
|
|
5939
|
+
dispatchWin("load");
|
|
5940
|
+
|
|
5941
|
+
try { window.dispatchEvent(new PopStateEvent("popstate", { state: (history && history.state) })); } catch {}
|
|
5942
|
+
try {
|
|
5943
|
+
const href = String((location && location.href) || "");
|
|
5944
|
+
window.dispatchEvent(new HashChangeEvent("hashchange", { oldURL: href, newURL: href }));
|
|
5945
|
+
} catch {}
|
|
5946
|
+
} catch {}
|
|
5947
|
+
`,
|
|
5948
|
+
context,
|
|
5949
|
+
{ timeout: timeoutMs }
|
|
5950
|
+
);
|
|
5951
|
+
}
|
|
5952
|
+
|
|
5953
|
+
// src/swoop/probes.ts
|
|
5954
|
+
function installDebugProbes(init) {
|
|
5955
|
+
const { globalObj, hostSetTimeout } = init;
|
|
5956
|
+
globalObj.__swoopStats = {
|
|
5957
|
+
startedAt: Date.now(),
|
|
5958
|
+
domOps: /* @__PURE__ */ Object.create(null),
|
|
5959
|
+
mutations: 0,
|
|
5960
|
+
lastDomActivityAt: 0,
|
|
5961
|
+
listeners: /* @__PURE__ */ Object.create(null),
|
|
5962
|
+
timers: {
|
|
5963
|
+
timeoutScheduled: 0,
|
|
5964
|
+
timeoutFired: 0,
|
|
5965
|
+
intervalScheduled: 0,
|
|
5966
|
+
intervalFired: 0,
|
|
5967
|
+
rafScheduled: 0,
|
|
5968
|
+
rafFired: 0
|
|
5969
|
+
},
|
|
5970
|
+
nav: {
|
|
5971
|
+
pushState: 0,
|
|
5972
|
+
replaceState: 0,
|
|
5973
|
+
lastHref: null
|
|
5974
|
+
},
|
|
5975
|
+
appRootSamples: []
|
|
5976
|
+
};
|
|
5977
|
+
const bump = (name) => {
|
|
5978
|
+
const s = globalObj.__swoopStats;
|
|
5979
|
+
s.domOps[name] = (s.domOps[name] ?? 0) + 1;
|
|
5980
|
+
s.lastDomActivityAt = Date.now();
|
|
5981
|
+
};
|
|
5982
|
+
const bumpListener = (type) => {
|
|
5983
|
+
const s = globalObj.__swoopStats;
|
|
5984
|
+
s.listeners[type] = (s.listeners[type] ?? 0) + 1;
|
|
5985
|
+
};
|
|
5986
|
+
const wrapProto = (proto, fn) => {
|
|
5987
|
+
const orig = proto?.[fn];
|
|
5988
|
+
if (typeof orig !== "function") return;
|
|
5989
|
+
proto[fn] = function(...args) {
|
|
5990
|
+
bump(fn);
|
|
5991
|
+
return orig.apply(this, args);
|
|
5992
|
+
};
|
|
5993
|
+
};
|
|
5994
|
+
wrapProto(globalObj.Node?.prototype, "appendChild");
|
|
5995
|
+
wrapProto(globalObj.Node?.prototype, "insertBefore");
|
|
5996
|
+
wrapProto(globalObj.Node?.prototype, "removeChild");
|
|
5997
|
+
wrapProto(globalObj.Element?.prototype, "setAttribute");
|
|
5998
|
+
wrapProto(globalObj.Element?.prototype, "removeAttribute");
|
|
5999
|
+
wrapProto(globalObj.Element?.prototype, "append");
|
|
6000
|
+
wrapProto(globalObj.Element?.prototype, "prepend");
|
|
6001
|
+
const wrapAddListener = (target, label) => {
|
|
6002
|
+
const orig = target?.addEventListener;
|
|
6003
|
+
if (typeof orig !== "function") return;
|
|
6004
|
+
target.addEventListener = function(type, listener, options) {
|
|
6005
|
+
try {
|
|
6006
|
+
bumpListener(`${label}:${String(type)}`);
|
|
6007
|
+
} catch {
|
|
6008
|
+
}
|
|
6009
|
+
return orig.call(this, type, listener, options);
|
|
6010
|
+
};
|
|
6011
|
+
};
|
|
6012
|
+
wrapAddListener(globalObj, "window");
|
|
6013
|
+
wrapAddListener(globalObj.document, "document");
|
|
6014
|
+
const MO = globalObj.MutationObserver;
|
|
6015
|
+
if (typeof MO === "function") {
|
|
6016
|
+
const mo = new MO(() => {
|
|
6017
|
+
globalObj.__swoopStats.mutations++;
|
|
6018
|
+
globalObj.__swoopStats.lastDomActivityAt = Date.now();
|
|
6019
|
+
});
|
|
6020
|
+
try {
|
|
6021
|
+
mo.observe(globalObj.document?.documentElement, {
|
|
6022
|
+
subtree: true,
|
|
6023
|
+
childList: true,
|
|
6024
|
+
attributes: true,
|
|
6025
|
+
characterData: true
|
|
6026
|
+
});
|
|
6027
|
+
} catch {
|
|
6028
|
+
}
|
|
6029
|
+
}
|
|
6030
|
+
const sample = () => {
|
|
6031
|
+
try {
|
|
6032
|
+
const el = globalObj.document?.querySelector?.("app-root");
|
|
6033
|
+
const len = el?.innerHTML?.length ?? 0;
|
|
6034
|
+
const opacity = el?.getAttribute?.("style")?.includes("opacity") ? String(el.getAttribute("style")) : null;
|
|
6035
|
+
globalObj.__swoopStats.appRootSamples.push({ t: Date.now(), len, opacity });
|
|
6036
|
+
} catch {
|
|
6037
|
+
}
|
|
6038
|
+
};
|
|
6039
|
+
sample();
|
|
6040
|
+
hostSetTimeout(sample, 250);
|
|
6041
|
+
hostSetTimeout(sample, 1e3);
|
|
6042
|
+
hostSetTimeout(sample, 2500);
|
|
6043
|
+
hostSetTimeout(sample, 4500);
|
|
6044
|
+
}
|
|
6045
|
+
function emitDebugProbes(globalObj, recordDebug, now = () => Date.now()) {
|
|
6046
|
+
const stats = globalObj.__swoopStats;
|
|
6047
|
+
if (!stats) return;
|
|
6048
|
+
const appRootHtml = (() => {
|
|
6049
|
+
try {
|
|
6050
|
+
const el = globalObj.document?.querySelector?.("app-root");
|
|
6051
|
+
const raw = el?.innerHTML ?? "";
|
|
6052
|
+
return String(raw).slice(0, 400);
|
|
6053
|
+
} catch {
|
|
6054
|
+
return null;
|
|
6055
|
+
}
|
|
6056
|
+
})();
|
|
6057
|
+
recordDebug([
|
|
6058
|
+
"[swoop probes]",
|
|
6059
|
+
JSON.stringify({
|
|
6060
|
+
mutations: stats.mutations ?? 0,
|
|
6061
|
+
lastDomActivityMsAgo: stats.lastDomActivityAt && typeof stats.lastDomActivityAt === "number" ? now() - stats.lastDomActivityAt : null,
|
|
6062
|
+
topDomOps: Object.entries(stats.domOps ?? {}).sort((a, b) => b[1] - a[1]).slice(0, 15),
|
|
6063
|
+
topListeners: Object.entries(stats.listeners ?? {}).sort((a, b) => b[1] - a[1]).slice(0, 20),
|
|
6064
|
+
timers: stats.timers ?? null,
|
|
6065
|
+
lastAsyncActivityMsAgo: stats.lastAsyncActivityAt && typeof stats.lastAsyncActivityAt === "number" ? now() - stats.lastAsyncActivityAt : null,
|
|
6066
|
+
nav: stats.nav ?? null,
|
|
6067
|
+
location: (() => {
|
|
6068
|
+
try {
|
|
6069
|
+
return {
|
|
6070
|
+
href: String(globalObj.location?.href ?? ""),
|
|
6071
|
+
pathname: String(globalObj.location?.pathname ?? ""),
|
|
6072
|
+
search: String(globalObj.location?.search ?? ""),
|
|
6073
|
+
hash: String(globalObj.location?.hash ?? ""),
|
|
6074
|
+
baseURI: String(globalObj.document?.baseURI ?? "")
|
|
6075
|
+
};
|
|
6076
|
+
} catch {
|
|
6077
|
+
return null;
|
|
6078
|
+
}
|
|
6079
|
+
})(),
|
|
6080
|
+
appRootSamples: stats.appRootSamples ?? [],
|
|
6081
|
+
appRootHtmlHead: appRootHtml
|
|
6082
|
+
})
|
|
6083
|
+
]);
|
|
6084
|
+
}
|
|
6085
|
+
|
|
6086
|
+
// src/swoop/scripts/loader.ts
|
|
6087
|
+
function isScriptEl(node) {
|
|
6088
|
+
return node?.tagName?.toLowerCase?.() === "script";
|
|
6089
|
+
}
|
|
6090
|
+
function createScriptLoader(init) {
|
|
6091
|
+
const loadedScriptSrcs = /* @__PURE__ */ new Set();
|
|
6092
|
+
let pendingScriptLoads = 0;
|
|
6093
|
+
let debugScriptEvents = 0;
|
|
6094
|
+
const debugScript = (...args) => {
|
|
6095
|
+
if (debugScriptEvents++ > init.maxDebugEvents) return;
|
|
6096
|
+
init.debug(args);
|
|
6097
|
+
};
|
|
6098
|
+
const loadScriptElement = async (scriptEl, parentUrlForResolve) => {
|
|
6099
|
+
const rawSrc = scriptEl?.src || scriptEl?.getAttribute?.("src");
|
|
6100
|
+
if (!rawSrc) return;
|
|
6101
|
+
let resolvedSrc;
|
|
6102
|
+
try {
|
|
6103
|
+
resolvedSrc = new URL(String(rawSrc), init.baseForResolveHref).href;
|
|
6104
|
+
} catch {
|
|
6105
|
+
return;
|
|
6106
|
+
}
|
|
6107
|
+
if (loadedScriptSrcs.has(resolvedSrc)) return;
|
|
6108
|
+
loadedScriptSrcs.add(resolvedSrc);
|
|
6109
|
+
init.noteAsyncActivity();
|
|
6110
|
+
pendingScriptLoads++;
|
|
6111
|
+
const type = (scriptEl?.type || scriptEl?.getAttribute?.("type") || "").toString().trim().toLowerCase();
|
|
6112
|
+
const isModule = type === "module";
|
|
6113
|
+
debugScript("[swoop] load <script>", resolvedSrc, isModule ? "module" : "classic");
|
|
6114
|
+
try {
|
|
6115
|
+
if (isModule) {
|
|
6116
|
+
await init.runModuleScript(resolvedSrc, parentUrlForResolve);
|
|
6117
|
+
} else {
|
|
6118
|
+
const code = await init.fetchText(resolvedSrc);
|
|
6119
|
+
init.runClassicScript(code, resolvedSrc);
|
|
6120
|
+
}
|
|
6121
|
+
try {
|
|
6122
|
+
if (typeof scriptEl?.onload === "function")
|
|
6123
|
+
scriptEl.onload(new (init.globalObj.Event ?? Event)("load"));
|
|
6124
|
+
} catch {
|
|
6125
|
+
try {
|
|
6126
|
+
scriptEl.onload?.();
|
|
6127
|
+
} catch {
|
|
6128
|
+
}
|
|
6129
|
+
}
|
|
6130
|
+
try {
|
|
6131
|
+
scriptEl?.dispatchEvent?.(new (init.globalObj.Event ?? Event)("load"));
|
|
6132
|
+
} catch {
|
|
6133
|
+
}
|
|
6134
|
+
} catch (e) {
|
|
6135
|
+
try {
|
|
6136
|
+
init.onError?.(resolvedSrc, e);
|
|
6137
|
+
} catch {
|
|
6138
|
+
}
|
|
6139
|
+
try {
|
|
6140
|
+
if (typeof scriptEl?.onerror === "function") scriptEl.onerror(e);
|
|
6141
|
+
} catch {
|
|
6142
|
+
try {
|
|
6143
|
+
scriptEl.onerror?.();
|
|
6144
|
+
} catch {
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
try {
|
|
6148
|
+
scriptEl?.dispatchEvent?.(new (init.globalObj.Event ?? Event)("error"));
|
|
6149
|
+
} catch {
|
|
6150
|
+
}
|
|
6151
|
+
} finally {
|
|
6152
|
+
pendingScriptLoads--;
|
|
6153
|
+
init.noteAsyncActivity();
|
|
6154
|
+
}
|
|
6155
|
+
};
|
|
6156
|
+
const scheduleScriptLoad = (node, label) => {
|
|
6157
|
+
init.hostSetTimeout(() => {
|
|
6158
|
+
void loadScriptElement(node, `${init.pageUrlHref}#${label}`);
|
|
6159
|
+
}, 0);
|
|
6160
|
+
};
|
|
6161
|
+
const patchScriptInsertion = (container, label) => {
|
|
6162
|
+
if (!container) return;
|
|
6163
|
+
const origAppendChild = container.appendChild?.bind(container);
|
|
6164
|
+
const origInsertBefore = container.insertBefore?.bind(container);
|
|
6165
|
+
const origAppend = container.append?.bind(container);
|
|
6166
|
+
const origPrepend = container.prepend?.bind(container);
|
|
6167
|
+
if (typeof origAppendChild === "function") {
|
|
6168
|
+
container.appendChild = (node) => {
|
|
6169
|
+
const ret = origAppendChild(node);
|
|
6170
|
+
if (isScriptEl(node)) scheduleScriptLoad(node, `${label}.appendChild`);
|
|
6171
|
+
return ret;
|
|
6172
|
+
};
|
|
6173
|
+
}
|
|
6174
|
+
if (typeof origInsertBefore === "function") {
|
|
6175
|
+
container.insertBefore = (node, ref) => {
|
|
6176
|
+
const ret = origInsertBefore(node, ref);
|
|
6177
|
+
if (isScriptEl(node)) scheduleScriptLoad(node, `${label}.insertBefore`);
|
|
6178
|
+
return ret;
|
|
6179
|
+
};
|
|
6180
|
+
}
|
|
6181
|
+
if (typeof origAppend === "function") {
|
|
6182
|
+
container.append = (...nodes) => {
|
|
6183
|
+
const ret = origAppend(...nodes);
|
|
6184
|
+
for (const n of nodes) if (isScriptEl(n)) scheduleScriptLoad(n, `${label}.append`);
|
|
6185
|
+
return ret;
|
|
6186
|
+
};
|
|
6187
|
+
}
|
|
6188
|
+
if (typeof origPrepend === "function") {
|
|
6189
|
+
container.prepend = (...nodes) => {
|
|
6190
|
+
const ret = origPrepend(...nodes);
|
|
6191
|
+
for (const n of nodes) if (isScriptEl(n)) scheduleScriptLoad(n, `${label}.prepend`);
|
|
6192
|
+
return ret;
|
|
6193
|
+
};
|
|
6194
|
+
}
|
|
6195
|
+
};
|
|
6196
|
+
const install = () => {
|
|
6197
|
+
patchScriptInsertion(init.globalObj.document?.head, "head");
|
|
6198
|
+
patchScriptInsertion(init.globalObj.document?.body, "body");
|
|
6199
|
+
try {
|
|
6200
|
+
const elProto = init.globalObj.Element?.prototype;
|
|
6201
|
+
const origSetAttr = elProto?.setAttribute;
|
|
6202
|
+
if (typeof origSetAttr === "function") {
|
|
6203
|
+
elProto.setAttribute = function(name, value) {
|
|
6204
|
+
const ret = origSetAttr.call(this, name, value);
|
|
6205
|
+
try {
|
|
6206
|
+
if (this?.tagName?.toLowerCase?.() === "script" && typeof name === "string" && name.toLowerCase() === "src") {
|
|
6207
|
+
scheduleScriptLoad(this, "script.setAttribute(src)");
|
|
6208
|
+
}
|
|
6209
|
+
} catch {
|
|
6210
|
+
}
|
|
6211
|
+
return ret;
|
|
6212
|
+
};
|
|
6213
|
+
}
|
|
6214
|
+
} catch {
|
|
6215
|
+
}
|
|
6216
|
+
try {
|
|
6217
|
+
const doc = init.globalObj.document;
|
|
6218
|
+
const origCreateElement = doc?.createElement?.bind(doc);
|
|
6219
|
+
if (typeof origCreateElement === "function") {
|
|
6220
|
+
doc.createElement = (tagName, ...rest) => {
|
|
6221
|
+
const el = origCreateElement(tagName, ...rest);
|
|
6222
|
+
try {
|
|
6223
|
+
if (typeof tagName === "string" && tagName.toLowerCase() === "script") {
|
|
6224
|
+
const desc = Object.getOwnPropertyDescriptor(el, "src");
|
|
6225
|
+
if (!desc || desc.configurable) {
|
|
6226
|
+
let _src = el.getAttribute?.("src") ?? "";
|
|
6227
|
+
Object.defineProperty(el, "src", {
|
|
6228
|
+
configurable: true,
|
|
6229
|
+
enumerable: true,
|
|
6230
|
+
get: () => _src,
|
|
6231
|
+
set: (v) => {
|
|
6232
|
+
_src = String(v);
|
|
6233
|
+
try {
|
|
6234
|
+
el.setAttribute?.("src", _src);
|
|
6235
|
+
} catch {
|
|
6236
|
+
}
|
|
6237
|
+
scheduleScriptLoad(el, "script.src=");
|
|
6238
|
+
}
|
|
6239
|
+
});
|
|
6240
|
+
}
|
|
6241
|
+
}
|
|
6242
|
+
} catch {
|
|
6243
|
+
}
|
|
6244
|
+
return el;
|
|
6245
|
+
};
|
|
6246
|
+
}
|
|
6247
|
+
} catch {
|
|
6248
|
+
}
|
|
6249
|
+
};
|
|
6250
|
+
return {
|
|
6251
|
+
getPendingScriptLoads: () => pendingScriptLoads,
|
|
6252
|
+
install
|
|
6253
|
+
};
|
|
6254
|
+
}
|
|
6255
|
+
function isNodeRuntime() {
|
|
6256
|
+
return typeof process !== "undefined" && typeof process.versions === "object" && typeof process.versions.node === "string";
|
|
6257
|
+
}
|
|
6258
|
+
function sleep(ms) {
|
|
6259
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6260
|
+
}
|
|
6261
|
+
function normalizeInit(init) {
|
|
6262
|
+
return {
|
|
6263
|
+
engine: init?.engine ?? "vm",
|
|
6264
|
+
pluck: init?.pluck ?? {},
|
|
6265
|
+
executeScripts: init?.executeScripts ?? true,
|
|
6266
|
+
timeout: init?.timeout ?? 3e3,
|
|
6267
|
+
waitStrategy: init?.waitStrategy ?? "networkidle",
|
|
6268
|
+
idleTime: init?.idleTime ?? 250,
|
|
6269
|
+
pollInterval: init?.pollInterval ?? 25,
|
|
6270
|
+
maxScripts: init?.maxScripts ?? 64,
|
|
6271
|
+
forwardConsole: init?.forwardConsole ?? false,
|
|
6272
|
+
permissiveShims: init?.permissiveShims ?? true,
|
|
6273
|
+
debugFetch: init?.debugFetch ?? false,
|
|
6274
|
+
debugProbes: init?.debugProbes ?? false
|
|
6275
|
+
};
|
|
6276
|
+
}
|
|
6277
|
+
function defineWindowInContext(context, timeoutMs = 50) {
|
|
6278
|
+
vm4.runInContext(
|
|
6279
|
+
`
|
|
6280
|
+
globalThis.Window ??= function Window() {};
|
|
6281
|
+
try { globalThis.Window.prototype = Object.getPrototypeOf(globalThis); } catch {}
|
|
6282
|
+
`,
|
|
6283
|
+
context,
|
|
6284
|
+
{ timeout: timeoutMs }
|
|
6285
|
+
);
|
|
6286
|
+
}
|
|
6287
|
+
function ensureRealmFunctionIntrinsic(context, timeoutMs = 50) {
|
|
6288
|
+
vm4.runInContext(
|
|
6289
|
+
`
|
|
6290
|
+
try { globalThis.Function = (function(){}).constructor; } catch {}
|
|
6291
|
+
`,
|
|
6292
|
+
context,
|
|
6293
|
+
{ timeout: timeoutMs }
|
|
6294
|
+
);
|
|
6295
|
+
}
|
|
6296
|
+
function installRealmWrappers(context, timeoutMs = 50) {
|
|
6297
|
+
vm4.runInContext(
|
|
6298
|
+
`
|
|
6299
|
+
// timers
|
|
6300
|
+
if (typeof globalThis.__swoop_setTimeout === "function") {
|
|
6301
|
+
globalThis.setTimeout = (...args) => globalThis.__swoop_setTimeout(...args);
|
|
6302
|
+
}
|
|
6303
|
+
if (typeof globalThis.__swoop_clearTimeout === "function") {
|
|
6304
|
+
globalThis.clearTimeout = (...args) => globalThis.__swoop_clearTimeout(...args);
|
|
6305
|
+
}
|
|
6306
|
+
if (typeof globalThis.__swoop_setInterval === "function") {
|
|
6307
|
+
globalThis.setInterval = (...args) => globalThis.__swoop_setInterval(...args);
|
|
6308
|
+
}
|
|
6309
|
+
if (typeof globalThis.__swoop_clearInterval === "function") {
|
|
6310
|
+
globalThis.clearInterval = (...args) => globalThis.__swoop_clearInterval(...args);
|
|
6311
|
+
}
|
|
6312
|
+
if (typeof globalThis.__swoop_setImmediate === "function") {
|
|
6313
|
+
globalThis.setImmediate = (...args) => globalThis.__swoop_setImmediate(...args);
|
|
6314
|
+
}
|
|
6315
|
+
if (typeof globalThis.__swoop_clearImmediate === "function") {
|
|
6316
|
+
globalThis.clearImmediate = (...args) => globalThis.__swoop_clearImmediate(...args);
|
|
6317
|
+
}
|
|
6318
|
+
|
|
6319
|
+
// microtasks / raf / idle
|
|
6320
|
+
if (typeof globalThis.__swoop_queueMicrotask === "function") {
|
|
6321
|
+
globalThis.queueMicrotask = (...args) => globalThis.__swoop_queueMicrotask(...args);
|
|
6322
|
+
}
|
|
6323
|
+
if (typeof globalThis.__swoop_requestAnimationFrame === "function") {
|
|
6324
|
+
globalThis.requestAnimationFrame = (...args) => globalThis.__swoop_requestAnimationFrame(...args);
|
|
6325
|
+
}
|
|
6326
|
+
if (typeof globalThis.__swoop_cancelAnimationFrame === "function") {
|
|
6327
|
+
globalThis.cancelAnimationFrame = (...args) => globalThis.__swoop_cancelAnimationFrame(...args);
|
|
6328
|
+
}
|
|
6329
|
+
if (typeof globalThis.__swoop_requestIdleCallback === "function") {
|
|
6330
|
+
globalThis.requestIdleCallback = (...args) => globalThis.__swoop_requestIdleCallback(...args);
|
|
6331
|
+
}
|
|
6332
|
+
if (typeof globalThis.__swoop_cancelIdleCallback === "function") {
|
|
6333
|
+
globalThis.cancelIdleCallback = (...args) => globalThis.__swoop_cancelIdleCallback(...args);
|
|
6334
|
+
}
|
|
6335
|
+
|
|
6336
|
+
// fetch
|
|
6337
|
+
if (typeof globalThis.__swoop_fetch === "function") {
|
|
6338
|
+
globalThis.fetch = (...args) => globalThis.__swoop_fetch(...args);
|
|
6339
|
+
}
|
|
6340
|
+
`,
|
|
6341
|
+
context,
|
|
6342
|
+
{ timeout: timeoutMs }
|
|
6343
|
+
);
|
|
6344
|
+
}
|
|
6345
|
+
function createModuleLoader(init) {
|
|
6346
|
+
const SourceTextModule = vm4.SourceTextModule;
|
|
6347
|
+
const moduleCache = /* @__PURE__ */ new Map();
|
|
6348
|
+
const pluckFn = init.pluckFn ?? pluck;
|
|
6349
|
+
const loadModule = async (specifier, referencingUrl) => {
|
|
6350
|
+
if (init.remainingMs() <= 0)
|
|
6351
|
+
throw new Error("swoop() time budget exhausted while loading modules");
|
|
6352
|
+
const resolved = new URL(specifier, referencingUrl).href;
|
|
6353
|
+
const cached = moduleCache.get(resolved);
|
|
6354
|
+
if (cached) return cached;
|
|
6355
|
+
if (!SourceTextModule) {
|
|
6356
|
+
throw new Error(
|
|
6357
|
+
"Module scripts require Node `--experimental-vm-modules` (vm.SourceTextModule is unavailable)."
|
|
6358
|
+
);
|
|
6359
|
+
}
|
|
6360
|
+
const modRes = await pluckFn(resolved, {
|
|
6361
|
+
...init.pluckInit,
|
|
6362
|
+
timeout: Math.min(init.pluckInit.timeout ?? 3e4, init.remainingMs()),
|
|
6363
|
+
strictContentType: false,
|
|
6364
|
+
throwOnHttpError: true
|
|
6365
|
+
});
|
|
6366
|
+
const rawSource = await modRes.textUtf8();
|
|
6367
|
+
const mod = new SourceTextModule(rawSource, {
|
|
6368
|
+
context: init.context,
|
|
6369
|
+
identifier: resolved,
|
|
6370
|
+
initializeImportMeta: (meta) => {
|
|
6371
|
+
meta.url = resolved;
|
|
6372
|
+
},
|
|
6373
|
+
importModuleDynamically: async (spec) => {
|
|
6374
|
+
const child = await loadModule(spec, resolved);
|
|
6375
|
+
await child.link(linkerFor(resolved));
|
|
6376
|
+
await child.evaluate();
|
|
6377
|
+
return child;
|
|
6378
|
+
}
|
|
6379
|
+
});
|
|
6380
|
+
moduleCache.set(resolved, mod);
|
|
6381
|
+
return mod;
|
|
6382
|
+
};
|
|
6383
|
+
const linkerFor = (referencingUrl) => async (specifier) => {
|
|
6384
|
+
return await loadModule(specifier, referencingUrl);
|
|
6385
|
+
};
|
|
6386
|
+
return { loadModule, linkerFor };
|
|
6387
|
+
}
|
|
6388
|
+
|
|
6389
|
+
// src/swoop/wait.ts
|
|
6390
|
+
async function waitForSettle(init) {
|
|
6391
|
+
if (init.strategy === "timeout") {
|
|
6392
|
+
await init.sleep(Math.max(0, init.deadlineMs - init.now()));
|
|
6393
|
+
return { timedOut: init.now() >= init.deadlineMs };
|
|
6394
|
+
}
|
|
6395
|
+
while (init.now() < init.deadlineMs) {
|
|
6396
|
+
if (init.getPendingFetches() === 0 && init.getPendingScriptLoads() === 0 && init.now() - init.getLastAsyncActivityAt() >= init.idleTimeMs) {
|
|
6397
|
+
return { timedOut: false };
|
|
6398
|
+
}
|
|
6399
|
+
await init.sleep(init.pollIntervalMs);
|
|
6400
|
+
}
|
|
6401
|
+
return { timedOut: true };
|
|
6402
|
+
}
|
|
6403
|
+
|
|
6404
|
+
// src/swoop/engines/vm.ts
|
|
6405
|
+
async function runVmEngine(args) {
|
|
6406
|
+
const { entries: realmConsole, record } = createConsoleCapture();
|
|
6407
|
+
const remainingMs = () => Math.max(0, args.deadline - Date.now());
|
|
6408
|
+
const { window, document } = parseHTML$1(args.html, { url: args.finalUrl });
|
|
6409
|
+
const pageUrl = new URL(args.finalUrl);
|
|
6410
|
+
const { baseForResolve, baseEl } = computeBaseForResolve(args.finalUrl, document);
|
|
6411
|
+
patchDocumentBaseURI(document, baseForResolve);
|
|
6412
|
+
patchBaseElementHref(baseEl, baseForResolve);
|
|
6413
|
+
const documentBaseUriForDom = baseForResolve.href;
|
|
6414
|
+
try {
|
|
6415
|
+
document.readyState ??= "loading";
|
|
6416
|
+
} catch {
|
|
6417
|
+
}
|
|
6418
|
+
const domWindow = window;
|
|
6419
|
+
const globalObj = Object.create(domWindow);
|
|
6420
|
+
globalObj.window = globalObj;
|
|
6421
|
+
globalObj.self = globalObj;
|
|
6422
|
+
globalObj.globalThis = globalObj;
|
|
6423
|
+
globalObj.document = document;
|
|
6424
|
+
try {
|
|
6425
|
+
Object.defineProperty(document, "defaultView", { value: globalObj, configurable: true });
|
|
6426
|
+
} catch {
|
|
6427
|
+
}
|
|
6428
|
+
const { location: locationShim, history: historyShim } = createNavigationShims({
|
|
6429
|
+
pageUrl,
|
|
6430
|
+
onNavigate: (href) => {
|
|
6431
|
+
if (!args.options.debugProbes) return;
|
|
6432
|
+
try {
|
|
6433
|
+
globalObj.__swoopStats.nav.lastHref = String(href);
|
|
6434
|
+
} catch {
|
|
6435
|
+
}
|
|
6436
|
+
},
|
|
6437
|
+
onPopState: (state) => {
|
|
6438
|
+
if (args.options.debugProbes) {
|
|
6439
|
+
try {
|
|
6440
|
+
globalObj.__swoopStats.nav.pushState++;
|
|
6441
|
+
} catch {
|
|
6442
|
+
}
|
|
6443
|
+
}
|
|
6444
|
+
try {
|
|
6445
|
+
globalObj.dispatchEvent?.(new (globalObj.PopStateEvent ?? Event)("popstate", { state }));
|
|
6446
|
+
} catch {
|
|
6447
|
+
}
|
|
6448
|
+
}
|
|
6449
|
+
});
|
|
6450
|
+
try {
|
|
6451
|
+
Object.defineProperty(document, "location", {
|
|
6452
|
+
configurable: true,
|
|
6453
|
+
get: () => locationShim,
|
|
6454
|
+
set: (v) => {
|
|
6455
|
+
try {
|
|
6456
|
+
locationShim.href = String(v);
|
|
6457
|
+
} catch {
|
|
6458
|
+
}
|
|
6459
|
+
}
|
|
6460
|
+
});
|
|
6461
|
+
} catch {
|
|
6462
|
+
}
|
|
6463
|
+
globalObj.location = locationShim;
|
|
6464
|
+
globalObj.history = historyShim;
|
|
6465
|
+
installBrowserShims({ globalObj, domWindow, document, documentBaseUriForDom });
|
|
6466
|
+
installCookieJar(document);
|
|
6467
|
+
const asyncEnv = installAsyncEnv({ globalObj, debugProbes: args.options.debugProbes });
|
|
6468
|
+
const noteAsyncActivity = asyncEnv.noteAsyncActivity;
|
|
6469
|
+
const hostSetTimeout = asyncEnv.hostSetTimeout;
|
|
6470
|
+
const hostClearTimeout = asyncEnv.hostClearTimeout;
|
|
6471
|
+
if (args.options.debugProbes) {
|
|
6472
|
+
try {
|
|
6473
|
+
installDebugProbes({ globalObj, hostSetTimeout });
|
|
6474
|
+
} catch {
|
|
6475
|
+
}
|
|
6476
|
+
}
|
|
6477
|
+
const fetchShim = installFetchShim({
|
|
6478
|
+
globalObj,
|
|
6479
|
+
hostFetch: args.hostFetch,
|
|
6480
|
+
baseForResolveHref: baseForResolve.href,
|
|
6481
|
+
remainingMs,
|
|
6482
|
+
hostSetTimeout,
|
|
6483
|
+
hostClearTimeout,
|
|
6484
|
+
noteAsyncActivity,
|
|
6485
|
+
debugFetch: args.options.debugFetch,
|
|
6486
|
+
recordDebug: (a) => record("debug", a),
|
|
6487
|
+
queueMicrotask: (cb) => globalObj.__swoop_queueMicrotask(cb)
|
|
6488
|
+
});
|
|
6489
|
+
const addEventListener = domWindow.addEventListener?.bind(domWindow);
|
|
6490
|
+
const removeEventListener = domWindow.removeEventListener?.bind(domWindow);
|
|
6491
|
+
const dispatchEvent = domWindow.dispatchEvent?.bind(domWindow);
|
|
6492
|
+
if (typeof addEventListener === "function") globalObj.addEventListener = addEventListener;
|
|
6493
|
+
if (typeof removeEventListener === "function")
|
|
6494
|
+
globalObj.removeEventListener = removeEventListener;
|
|
6495
|
+
if (typeof dispatchEvent === "function") globalObj.dispatchEvent = dispatchEvent;
|
|
6496
|
+
globalObj.console = {
|
|
6497
|
+
debug: (...a) => record("debug", a),
|
|
6498
|
+
info: (...a) => record("info", a),
|
|
6499
|
+
warn: (...a) => record("warn", a),
|
|
6500
|
+
error: (...a) => record("error", a),
|
|
6501
|
+
log: (...a) => record("log", a)
|
|
6502
|
+
};
|
|
6503
|
+
if (args.options.forwardConsole) {
|
|
6504
|
+
for (const level of ["debug", "info", "warn", "error", "log"]) {
|
|
6505
|
+
const original = console[level]?.bind(console);
|
|
6506
|
+
if (typeof original === "function") {
|
|
6507
|
+
const wrapped = globalObj.console[level];
|
|
6508
|
+
globalObj.console[level] = (...a) => {
|
|
6509
|
+
wrapped(...a);
|
|
6510
|
+
original(...a);
|
|
6511
|
+
};
|
|
6512
|
+
}
|
|
6513
|
+
}
|
|
6514
|
+
}
|
|
6515
|
+
try {
|
|
6516
|
+
globalObj.onerror = (message, source, line, col, error) => {
|
|
6517
|
+
record("error", ["[window.onerror]", message, source, line, col, error]);
|
|
6518
|
+
};
|
|
6519
|
+
} catch {
|
|
6520
|
+
}
|
|
6521
|
+
try {
|
|
6522
|
+
globalObj.onunhandledrejection = (event) => {
|
|
6523
|
+
record("error", ["[unhandledrejection]", event?.reason]);
|
|
6524
|
+
};
|
|
6525
|
+
} catch {
|
|
6526
|
+
}
|
|
6527
|
+
try {
|
|
6528
|
+
globalObj.addEventListener?.("error", (event) => {
|
|
6529
|
+
record("error", [
|
|
6530
|
+
"[error event]",
|
|
6531
|
+
event?.message,
|
|
6532
|
+
event?.filename,
|
|
6533
|
+
event?.lineno,
|
|
6534
|
+
event?.colno,
|
|
6535
|
+
event?.error instanceof Error ? `${event.error.name}: ${event.error.message}
|
|
6536
|
+
${event.error.stack ?? ""}`.trim() : event?.error
|
|
6537
|
+
]);
|
|
6538
|
+
});
|
|
6539
|
+
} catch {
|
|
6540
|
+
}
|
|
6541
|
+
if (args.options.permissiveShims) {
|
|
6542
|
+
installPermissiveShims(globalObj);
|
|
6543
|
+
if (!globalObj.XMLHttpRequest) {
|
|
6544
|
+
installXMLHttpRequest({
|
|
6545
|
+
globalObj,
|
|
6546
|
+
resolveUrl: (u) => {
|
|
6547
|
+
try {
|
|
6548
|
+
return new URL(u, baseForResolve.href).href;
|
|
6549
|
+
} catch {
|
|
6550
|
+
return u;
|
|
6551
|
+
}
|
|
6552
|
+
},
|
|
6553
|
+
remainingMs,
|
|
6554
|
+
hostSetTimeout,
|
|
6555
|
+
hostClearTimeout,
|
|
6556
|
+
fetch: args.hostFetch
|
|
6557
|
+
});
|
|
6558
|
+
}
|
|
6559
|
+
}
|
|
6560
|
+
const context = vm4.createContext(globalObj, { name: "magpie-html/swoop" });
|
|
6561
|
+
defineWindowInContext(context);
|
|
6562
|
+
ensureRealmFunctionIntrinsic(context);
|
|
6563
|
+
installRealmWrappers(context);
|
|
6564
|
+
const { loadModule, linkerFor } = createModuleLoader({
|
|
6565
|
+
context,
|
|
6566
|
+
remainingMs,
|
|
6567
|
+
pluckInit: args.options.pluck
|
|
6568
|
+
});
|
|
6569
|
+
const scriptLoader = createScriptLoader({
|
|
6570
|
+
globalObj,
|
|
6571
|
+
pageUrlHref: pageUrl.href,
|
|
6572
|
+
baseForResolveHref: baseForResolve.href,
|
|
6573
|
+
remainingMs,
|
|
6574
|
+
hostSetTimeout,
|
|
6575
|
+
totalBudgetMs: args.totalBudgetMs,
|
|
6576
|
+
maxDebugEvents: args.options.debugProbes ? 80 : 0,
|
|
6577
|
+
debug: (a) => {
|
|
6578
|
+
if (!args.options.debugProbes) return;
|
|
6579
|
+
record("debug", a);
|
|
6580
|
+
},
|
|
6581
|
+
onError: (resolvedSrc, err) => {
|
|
6582
|
+
const e = err;
|
|
6583
|
+
record("error", ["[swoop] script load failed", resolvedSrc, e?.message || String(e)]);
|
|
6584
|
+
},
|
|
6585
|
+
noteAsyncActivity,
|
|
6586
|
+
fetchText: async (url) => {
|
|
6587
|
+
const sres = await pluck(url, {
|
|
6588
|
+
...args.options.pluck,
|
|
6589
|
+
timeout: Math.min(args.options.pluck.timeout ?? 3e4, remainingMs()),
|
|
6590
|
+
strictContentType: false,
|
|
6591
|
+
throwOnHttpError: true
|
|
6592
|
+
});
|
|
6593
|
+
return await sres.textUtf8();
|
|
6594
|
+
},
|
|
6595
|
+
runClassicScript: (code, filename) => {
|
|
6596
|
+
const script = new vm4.Script(code, { filename });
|
|
6597
|
+
script.runInContext(context, { timeout: args.totalBudgetMs });
|
|
6598
|
+
},
|
|
6599
|
+
runModuleScript: async (resolvedSrc, parentUrlForResolve) => {
|
|
6600
|
+
const mod = await loadModule(resolvedSrc, parentUrlForResolve);
|
|
6601
|
+
await mod.link(linkerFor(resolvedSrc));
|
|
6602
|
+
await mod.evaluate();
|
|
6603
|
+
}
|
|
6604
|
+
});
|
|
6605
|
+
scriptLoader.install();
|
|
6606
|
+
const getPendingScriptLoads = () => scriptLoader.getPendingScriptLoads();
|
|
6607
|
+
const engineErrors = [];
|
|
6608
|
+
const classicScripts = args.scripts.filter((s) => !s.isModule);
|
|
6609
|
+
const moduleScriptsInOrder = args.scripts.filter((s) => s.isModule);
|
|
6610
|
+
for (const s of [...classicScripts, ...moduleScriptsInOrder]) {
|
|
6611
|
+
if (remainingMs() <= 0) {
|
|
6612
|
+
engineErrors.push({
|
|
6613
|
+
stage: "wait",
|
|
6614
|
+
message: `Hard time budget (${args.totalBudgetMs}ms) exceeded while executing scripts; returning snapshot.`
|
|
6615
|
+
});
|
|
6616
|
+
break;
|
|
6617
|
+
}
|
|
6618
|
+
const scriptUrl = s.kind === "external" ? s.url : `${args.finalUrl}#inline`;
|
|
6619
|
+
try {
|
|
6620
|
+
if (s.isModule) {
|
|
6621
|
+
const modCode = s.code;
|
|
6622
|
+
const SourceTextModule = vm4.SourceTextModule;
|
|
6623
|
+
if (!SourceTextModule) {
|
|
6624
|
+
throw new Error(
|
|
6625
|
+
"Module scripts require Node `--experimental-vm-modules` (vm.SourceTextModule is unavailable)."
|
|
6626
|
+
);
|
|
6627
|
+
}
|
|
6628
|
+
const mod = new SourceTextModule(modCode, {
|
|
6629
|
+
context,
|
|
6630
|
+
identifier: scriptUrl,
|
|
6631
|
+
initializeImportMeta: (meta) => {
|
|
6632
|
+
meta.url = scriptUrl;
|
|
6633
|
+
},
|
|
6634
|
+
importModuleDynamically: async (spec) => {
|
|
6635
|
+
const child = await loadModule(spec, scriptUrl);
|
|
6636
|
+
await child.link(linkerFor(scriptUrl));
|
|
6637
|
+
await child.evaluate();
|
|
6638
|
+
return child;
|
|
6639
|
+
}
|
|
6640
|
+
});
|
|
6641
|
+
const timeoutP = (label) => sleep(remainingMs()).then(() => {
|
|
6642
|
+
throw new Error(`swoop() time budget exhausted during ${label}`);
|
|
6643
|
+
});
|
|
6644
|
+
const linkP = mod.link(linkerFor(scriptUrl));
|
|
6645
|
+
try {
|
|
6646
|
+
await Promise.race([linkP, timeoutP("module link")]);
|
|
6647
|
+
} catch (e) {
|
|
6648
|
+
void linkP.catch?.(() => {
|
|
6649
|
+
});
|
|
6650
|
+
throw e;
|
|
6651
|
+
}
|
|
6652
|
+
const evalP = mod.evaluate();
|
|
6653
|
+
try {
|
|
6654
|
+
await Promise.race([evalP, timeoutP("module evaluate")]);
|
|
6655
|
+
} catch (e) {
|
|
6656
|
+
void evalP.catch?.(() => {
|
|
6657
|
+
});
|
|
6658
|
+
throw e;
|
|
6659
|
+
}
|
|
6660
|
+
} else {
|
|
6661
|
+
const script = new vm4.Script(s.code, { filename: scriptUrl });
|
|
6662
|
+
script.runInContext(context, { timeout: args.totalBudgetMs });
|
|
6663
|
+
}
|
|
6664
|
+
} catch (err) {
|
|
6665
|
+
const e = err;
|
|
6666
|
+
engineErrors.push({
|
|
6667
|
+
stage: "script",
|
|
6668
|
+
scriptUrl: s.kind === "external" ? s.url : void 0,
|
|
6669
|
+
message: e.message || String(e),
|
|
6670
|
+
stack: e.stack
|
|
6671
|
+
});
|
|
6672
|
+
}
|
|
6673
|
+
}
|
|
6674
|
+
try {
|
|
6675
|
+
synthesizeLifecycle(context, Math.min(50, remainingMs()));
|
|
6676
|
+
} catch {
|
|
6677
|
+
}
|
|
6678
|
+
const settle = await waitForSettle({
|
|
6679
|
+
strategy: args.options.waitStrategy === "timeout" || typeof globalObj.fetch !== "function" ? "timeout" : "networkidle",
|
|
6680
|
+
deadlineMs: args.deadline,
|
|
6681
|
+
idleTimeMs: args.options.idleTime,
|
|
6682
|
+
pollIntervalMs: args.options.pollInterval,
|
|
6683
|
+
sleep,
|
|
6684
|
+
now: () => Date.now(),
|
|
6685
|
+
getPendingFetches: () => fetchShim.getPendingFetches(),
|
|
6686
|
+
getPendingScriptLoads,
|
|
6687
|
+
getLastAsyncActivityAt: () => asyncEnv.getLastAsyncActivityAt()
|
|
6688
|
+
});
|
|
6689
|
+
if (settle.timedOut && args.options.waitStrategy === "networkidle" && typeof globalObj.fetch === "function") {
|
|
6690
|
+
engineErrors.push({
|
|
6691
|
+
stage: "wait",
|
|
6692
|
+
message: `Hard time budget (${args.totalBudgetMs}ms) exceeded waiting for network idle; returning snapshot.`
|
|
6693
|
+
});
|
|
6694
|
+
}
|
|
6695
|
+
const snapshot = document.documentElement?.outerHTML ?? "";
|
|
6696
|
+
if (args.options.debugProbes) {
|
|
6697
|
+
try {
|
|
6698
|
+
emitDebugProbes(globalObj, (a) => record("debug", a));
|
|
6699
|
+
} catch {
|
|
6700
|
+
}
|
|
6701
|
+
}
|
|
6702
|
+
asyncEnv.cleanup();
|
|
6703
|
+
return { snapshot, consoleEntries: realmConsole, engineErrors };
|
|
6704
|
+
}
|
|
6705
|
+
|
|
6706
|
+
// src/swoop/scripts/discover.ts
|
|
6707
|
+
function isExecutableScriptType(type) {
|
|
6708
|
+
if (!type) return true;
|
|
6709
|
+
const t = type.trim().toLowerCase();
|
|
6710
|
+
if (t === "") return true;
|
|
6711
|
+
if (t === "text/javascript") return true;
|
|
6712
|
+
if (t === "application/javascript") return true;
|
|
6713
|
+
if (t === "application/ecmascript") return true;
|
|
6714
|
+
if (t === "text/ecmascript") return true;
|
|
6715
|
+
return false;
|
|
6716
|
+
}
|
|
6717
|
+
function isModuleScript(type) {
|
|
6718
|
+
return (type ?? "").trim().toLowerCase() === "module";
|
|
6719
|
+
}
|
|
6720
|
+
async function discoverAndFetchScripts(html, finalUrl, init, pluckFn = pluck) {
|
|
6721
|
+
const errors = [];
|
|
6722
|
+
const scripts = [];
|
|
6723
|
+
if (!init.executeScripts) return { scripts, errors };
|
|
6724
|
+
const doc = parseHTML(html, finalUrl);
|
|
6725
|
+
const baseHref = doc.querySelector("base[href]")?.getAttribute("href") ?? null;
|
|
6726
|
+
const baseUrl = baseHref ? new URL(baseHref, finalUrl).href : finalUrl;
|
|
6727
|
+
const scriptEls = Array.from(doc.querySelectorAll("script"));
|
|
6728
|
+
for (const el of scriptEls.slice(0, init.maxScripts)) {
|
|
6729
|
+
const type = el.getAttribute("type");
|
|
6730
|
+
const isModule = isModuleScript(type);
|
|
6731
|
+
if (!isModule && !isExecutableScriptType(type)) continue;
|
|
6732
|
+
const src = el.getAttribute("src");
|
|
6733
|
+
if (src) {
|
|
6734
|
+
const scriptUrl = new URL(src, baseUrl).href;
|
|
6735
|
+
try {
|
|
6736
|
+
const res = await pluckFn(scriptUrl, {
|
|
6737
|
+
...init.pluck,
|
|
6738
|
+
strictContentType: false,
|
|
6739
|
+
throwOnHttpError: true
|
|
6740
|
+
});
|
|
6741
|
+
const code2 = await res.textUtf8();
|
|
6742
|
+
scripts.push({ kind: "external", url: scriptUrl, code: code2, isModule });
|
|
6743
|
+
} catch (err) {
|
|
6744
|
+
const e = err;
|
|
6745
|
+
errors.push({
|
|
6746
|
+
stage: "script",
|
|
6747
|
+
scriptUrl,
|
|
6748
|
+
message: `Failed to fetch external script: ${e.message || String(e)}`,
|
|
6749
|
+
stack: e.stack
|
|
6750
|
+
});
|
|
6751
|
+
}
|
|
6752
|
+
continue;
|
|
6753
|
+
}
|
|
6754
|
+
const code = el.textContent ?? "";
|
|
6755
|
+
if (code.trim().length === 0) continue;
|
|
6756
|
+
scripts.push({ kind: "inline", code, isModule });
|
|
6757
|
+
}
|
|
6758
|
+
return { scripts, errors };
|
|
6759
|
+
}
|
|
6760
|
+
|
|
6761
|
+
// src/swoop/swoop.ts
|
|
6762
|
+
var HOST_FETCH = typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
|
|
6763
|
+
async function swoop(url, init) {
|
|
6764
|
+
const start = Date.now();
|
|
6765
|
+
const options = normalizeInit(init);
|
|
6766
|
+
const totalBudgetMs = Math.min(options.timeout, 5e3);
|
|
6767
|
+
const deadline = start + totalBudgetMs;
|
|
6768
|
+
if (!isNodeRuntime()) {
|
|
6769
|
+
throw new SwoopEnvironmentError("swoop() is currently Node.js-only.");
|
|
6770
|
+
}
|
|
6771
|
+
const res = await pluck(String(url), {
|
|
6772
|
+
...options.pluck,
|
|
6773
|
+
timeout: Math.min(options.pluck.timeout ?? 3e4, totalBudgetMs),
|
|
6774
|
+
strictContentType: false,
|
|
6775
|
+
throwOnHttpError: true
|
|
6776
|
+
});
|
|
6777
|
+
const html = await res.textUtf8();
|
|
6778
|
+
const finalUrl = res.finalUrl;
|
|
6779
|
+
const { scripts, errors: preErrors } = await discoverAndFetchScripts(html, finalUrl, options);
|
|
6780
|
+
let snapshot = "";
|
|
6781
|
+
let consoleEntries = [];
|
|
6782
|
+
let engineErrors = [];
|
|
6783
|
+
const r = await runVmEngine({
|
|
6784
|
+
finalUrl,
|
|
6785
|
+
html,
|
|
6786
|
+
scripts,
|
|
6787
|
+
options,
|
|
6788
|
+
totalBudgetMs,
|
|
6789
|
+
deadline,
|
|
6790
|
+
hostFetch: HOST_FETCH
|
|
6791
|
+
});
|
|
6792
|
+
snapshot = r.snapshot;
|
|
6793
|
+
consoleEntries = r.consoleEntries;
|
|
6794
|
+
engineErrors = r.engineErrors;
|
|
6795
|
+
const end = Date.now();
|
|
6796
|
+
return {
|
|
6797
|
+
url: finalUrl,
|
|
6798
|
+
html: snapshot,
|
|
6799
|
+
console: consoleEntries,
|
|
6800
|
+
errors: [...preErrors, ...engineErrors],
|
|
6801
|
+
timing: {
|
|
6802
|
+
start,
|
|
6803
|
+
end,
|
|
6804
|
+
duration: end - start
|
|
6805
|
+
}
|
|
6806
|
+
};
|
|
6807
|
+
}
|
|
5049
6808
|
/**
|
|
5050
6809
|
* Enhanced fetch types for web scraping.
|
|
5051
6810
|
*
|
|
@@ -5144,6 +6903,6 @@ function extractVerification(doc) {
|
|
|
5144
6903
|
* @packageDocumentation
|
|
5145
6904
|
*/
|
|
5146
6905
|
|
|
5147
|
-
export { PluckContentTypeError, PluckEncodingError, PluckError, PluckHttpError, PluckNetworkError, PluckRedirectError, PluckSizeError, PluckTimeoutError, assessContentQuality, calculateReadingTime, countWords, detectFormat, extractAnalytics, extractAssets, extractCanonical, extractContent, extractCopyright, extractDublinCore, extractFeedDiscovery, extractGeo, extractIcons, extractLanguage, extractLinks3 as extractLinks, extractMonetization, extractNews, extractOpenGraph, extractPagination, extractRobots, extractSEO, extractSchemaOrg, extractSecurity, extractSitemapDiscovery, extractSocialProfiles, extractTwitterCard, extractVerification, gatherArticle, gatherFeed, gatherWebsite, htmlToText, isAtom, isFeed, isJSONFeed, isProbablyReaderable, isRSS, parseFeed, parseHTML, pluck };
|
|
6906
|
+
export { PluckContentTypeError, PluckEncodingError, PluckError, PluckHttpError, PluckNetworkError, PluckRedirectError, PluckSizeError, PluckTimeoutError, SwoopEnvironmentError, SwoopError, SwoopExecutionError, SwoopSecurityError, SwoopTimeoutError, assessContentQuality, calculateReadingTime, countWords, detectFormat, extractAnalytics, extractAssets, extractCanonical, extractContent, extractCopyright, extractDublinCore, extractFeedDiscovery, extractGeo, extractIcons, extractLanguage, extractLinks3 as extractLinks, extractMonetization, extractNews, extractOpenGraph, extractPagination, extractRobots, extractSEO, extractSchemaOrg, extractSecurity, extractSitemapDiscovery, extractSocialProfiles, extractTwitterCard, extractVerification, gatherArticle, gatherFeed, gatherWebsite, htmlToText, isAtom, isFeed, isJSONFeed, isProbablyReaderable, isRSS, parseFeed, parseHTML, pluck, swoop };
|
|
5148
6907
|
//# sourceMappingURL=index.js.map
|
|
5149
6908
|
//# sourceMappingURL=index.js.map
|