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/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 updatedRaw = entryElement.querySelector("updated")?.textContent;
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("Invalid Atom entry: invalid <updated> date");
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 updatedRaw = feed.querySelector("updated")?.textContent;
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("Invalid Atom feed: invalid <updated> date");
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 abortController = new AbortController();
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
- abortController.signal
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 === "AbortError") {
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