sandboxedjs 0.2.19 → 0.2.20

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
@@ -6841,9 +6841,9 @@ var WasiHost = class {
6841
6841
  if (!(oflags & O_CREAT)) throw new WasiError(WASI_ENOENT);
6842
6842
  vfs.writeFile(abs, new Uint8Array(0), { cred, mode: 420 });
6843
6843
  }
6844
- const truncate = (oflags & O_TRUNC) !== 0;
6845
- const data = truncate ? new Uint8Array(0) : vfs.readFile(abs, cred);
6846
- if (truncate) vfs.writeFile(abs, data, { cred });
6844
+ const truncate2 = (oflags & O_TRUNC) !== 0;
6845
+ const data = truncate2 ? new Uint8Array(0) : vfs.readFile(abs, cred);
6846
+ if (truncate2) vfs.writeFile(abs, data, { cred });
6847
6847
  const size = data.length;
6848
6848
  const rights = rightsBase === 0n ? RIGHTS_ALL : rightsBase;
6849
6849
  const append = (fdflags & FD_APPEND) !== 0;
@@ -21086,7 +21086,7 @@ var wheels_default = {
21086
21086
 
21087
21087
  // package.json
21088
21088
  var package_default = {
21089
- version: "0.2.19"};
21089
+ version: "0.2.20"};
21090
21090
 
21091
21091
  // src/python/extension-abi.ts
21092
21092
  var EXTENSION_ABI = {
@@ -23896,7 +23896,7 @@ async function resolve2(options) {
23896
23896
  const clauses = (state.constraints.get(name) ?? []).flatMap((entry) => entry.spec.map((s) => `${s.op}${s.version}`)).sort();
23897
23897
  return `${name}|${[...new Set(clauses)].join(",")}`;
23898
23898
  };
23899
- const search2 = async (queue, state) => {
23899
+ const search3 = async (queue, state) => {
23900
23900
  checkBudget();
23901
23901
  if (queue.length === 0) return state;
23902
23902
  const [edge, ...rest] = queue;
@@ -23904,7 +23904,7 @@ async function resolve2(options) {
23904
23904
  const name = requirement.name;
23905
23905
  if (!markerApplies(requirement.marker, environment, extras)) {
23906
23906
  if (requirement.marker) skipped.push({ name, marker: requirement.marker });
23907
- return search2(rest, state);
23907
+ return search3(rest, state);
23908
23908
  }
23909
23909
  const previous = state.constraints.get(name) ?? [];
23910
23910
  const constraints = new Map(state.constraints);
@@ -23921,7 +23921,7 @@ async function resolve2(options) {
23921
23921
  explain(name, next.constraints, already.version)
23922
23922
  );
23923
23923
  }
23924
- return search2(rest, next);
23924
+ return search3(rest, next);
23925
23925
  }
23926
23926
  const signature = signatureOf(name, next);
23927
23927
  const learned = nogoods.get(signature);
@@ -23984,7 +23984,7 @@ async function resolve2(options) {
23984
23984
  const chosen = new Map(next.chosen);
23985
23985
  chosen.set(name, { ...candidate, requires });
23986
23986
  try {
23987
- return await search2([...edges, ...rest], { chosen, constraints: next.constraints });
23987
+ return await search3([...edges, ...rest], { chosen, constraints: next.constraints });
23988
23988
  } catch (error) {
23989
23989
  if (!(error instanceof ResolutionError)) throw error;
23990
23990
  if (/cancelled|budget/.test(error.message)) throw error;
@@ -23999,7 +23999,7 @@ async function resolve2(options) {
23999
23999
  if (specifiers.length === 0 || !markerApplies(marker, environment, root.extras)) continue;
24000
24000
  seeded.set(name, [...seeded.get(name) ?? [], { spec: specifiers, origin: root.origin }]);
24001
24001
  }
24002
- const solved = await search2(roots, { chosen: /* @__PURE__ */ new Map(), constraints: seeded });
24002
+ const solved = await search3(roots, { chosen: /* @__PURE__ */ new Map(), constraints: seeded });
24003
24003
  const distributions = [...solved.chosen.values()].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
24004
24004
  const reported = new Map(skipped.map((s) => [`${s.name}\0${s.marker}`, s]));
24005
24005
  return { distributions, skipped: [...reported.values()] };
@@ -24246,7 +24246,7 @@ function consoleScripts(text2) {
24246
24246
  return result;
24247
24247
  }
24248
24248
  function consoleLauncher(target) {
24249
- const [module, attribute = ""] = target.split(":", 2);
24249
+ const [module, attribute2 = ""] = target.split(":", 2);
24250
24250
  return `#!/usr/bin/python3
24251
24251
  import sys
24252
24252
  from importlib import import_module
@@ -24254,7 +24254,7 @@ from importlib import import_module
24254
24254
 
24255
24255
  def _entry():
24256
24256
  entry = import_module(${JSON.stringify(module)})
24257
- for part in ${JSON.stringify(attribute)}.split('.'):
24257
+ for part in ${JSON.stringify(attribute2)}.split('.'):
24258
24258
  if part:
24259
24259
  entry = getattr(entry, part)
24260
24260
  return entry
@@ -27631,7 +27631,7 @@ var defaultRealmFactory = async (hooks) => {
27631
27631
  vm = void 0;
27632
27632
  }
27633
27633
  if (vm?.createContext) {
27634
- const context = vm.createContext({ console: console2, setTimeout, clearTimeout, setInterval, clearInterval, queueMicrotask });
27634
+ const context = vm.createContext({ console: console2, URL, setTimeout, clearTimeout, setInterval, clearInterval, queueMicrotask });
27635
27635
  return { evaluate: (source) => vm.runInContext(source, context), dispose: () => {
27636
27636
  } };
27637
27637
  }
@@ -27758,6 +27758,28 @@ var CdpServer = class {
27758
27758
  return {};
27759
27759
  case "Runtime.runIfWaitingForDebugger":
27760
27760
  return {};
27761
+ case "Input.insertText": {
27762
+ const target = page ?? this.pages.values().next().value;
27763
+ if (!target) return {};
27764
+ for (const context of target.contexts.values()) context.realm.evaluate(`(() => { const element = document.activeElement; if (!element || element === document.body) return; element.value = (element.value || '') + ${JSON.stringify(String(params.text ?? ""))}; element.dispatchEvent(new Event('input', { bubbles: true })); })()`);
27765
+ return {};
27766
+ }
27767
+ case "Input.dispatchKeyEvent": {
27768
+ const target = page ?? this.pages.values().next().value;
27769
+ if (!target) return {};
27770
+ if (params.type === "char" || params.type === "keyDown" && params.text) {
27771
+ for (const context of target.contexts.values()) context.realm.evaluate(`(() => { const element = document.activeElement; if (!element || element === document.body) return; const text = ${JSON.stringify(String(params.text ?? ""))}; element.value = (element.value || '') + text; element.dispatchEvent(new KeyboardEvent('keydown', { key: text, bubbles: true })); element.dispatchEvent(new Event('input', { bubbles: true })); })()`);
27772
+ }
27773
+ return {};
27774
+ }
27775
+ case "Input.dispatchMouseEvent": {
27776
+ const target = page ?? this.pages.values().next().value;
27777
+ if (!target) return {};
27778
+ if (params.type === "mouseReleased") {
27779
+ for (const context of target.contexts.values()) context.realm.evaluate(`document.elementFromPoint(${Number(params.x) || 0}, ${Number(params.y) || 0})?.click()`);
27780
+ }
27781
+ return {};
27782
+ }
27761
27783
  }
27762
27784
  const [, name = ""] = method.split(".");
27763
27785
  if (name === "enable" || name === "disable" || /^set[A-Z]/.test(name)) return {};
@@ -27779,7 +27801,8 @@ var CdpServer = class {
27779
27801
  url: "about:blank",
27780
27802
  loader: 1,
27781
27803
  contexts: /* @__PURE__ */ new Map(),
27782
- worlds: /* @__PURE__ */ new Set()
27804
+ worlds: /* @__PURE__ */ new Set(),
27805
+ document: void 0
27783
27806
  };
27784
27807
  this.pages.set(page.sessionId, page);
27785
27808
  this.options.send({
@@ -27829,6 +27852,22 @@ var CdpServer = class {
27829
27852
  timestamp: Date.now()
27830
27853
  })
27831
27854
  });
27855
+ if (page.document) realm.evaluate(domBootstrap(page.document));
27856
+ if (page.document) {
27857
+ for (const script of documentScripts(page.document.root)) {
27858
+ try {
27859
+ realm.evaluate(`document.currentScript = document.__nodeByPath(${JSON.stringify(script.path)});
27860
+ ${script.code}
27861
+ //# sourceURL=${script.url}`);
27862
+ } catch (error) {
27863
+ this.event(page, "Runtime.exceptionThrown", {
27864
+ timestamp: Date.now(),
27865
+ exceptionDetails: { text: String(error), lineNumber: 0, columnNumber: 0 }
27866
+ });
27867
+ }
27868
+ }
27869
+ realm.evaluate("document.currentScript = null");
27870
+ }
27832
27871
  const context = { id, name, isDefault, realm, objects: /* @__PURE__ */ new Map(), nextObject: 0 };
27833
27872
  page.contexts.set(id, context);
27834
27873
  this.announceContext(page, context);
@@ -27864,6 +27903,13 @@ var CdpServer = class {
27864
27903
  return DEFERRED;
27865
27904
  }
27866
27905
  async commitNavigation(page, url) {
27906
+ if (this.options.navigate && /^(https?|data):/i.test(url)) {
27907
+ const loaded = await this.options.navigate(url);
27908
+ url = loaded.url;
27909
+ page.document = loaded.document;
27910
+ } else {
27911
+ page.document = void 0;
27912
+ }
27867
27913
  page.loader += 1;
27868
27914
  page.url = url;
27869
27915
  this.destroyContexts(page, true);
@@ -27896,14 +27942,22 @@ var CdpServer = class {
27896
27942
  callFunctionOn(page, params) {
27897
27943
  const context = params.objectId ? this.contextOfObject(page, params.objectId) : this.contextFor(page, params.executionContextId);
27898
27944
  const receiver = params.objectId ? context.objects.get(params.objectId) : void 0;
27899
- const args = (params.arguments ?? []).map((argument) => this.argument(page, argument));
27945
+ const args = (params.arguments ?? []).map((argument) => this.argument(page, argument, context));
27900
27946
  return this.settle(context, () => {
27901
27947
  const fn = context.realm.evaluate(`(${params.functionDeclaration})`);
27902
27948
  return fn.apply(receiver, args);
27903
27949
  }, params);
27904
27950
  }
27905
- argument(page, argument) {
27906
- if (argument.objectId) return this.contextOfObject(page, argument.objectId).objects.get(argument.objectId);
27951
+ argument(page, argument, target) {
27952
+ if (argument.objectId) {
27953
+ const source = this.contextOfObject(page, argument.objectId);
27954
+ const value = source.objects.get(argument.objectId);
27955
+ if (target && source !== target && value && typeof value === "object" && "__vireoPath" in value) {
27956
+ const path = value.__vireoPath;
27957
+ return target.realm.evaluate(`document.__nodeByPath(${JSON.stringify(path)})`);
27958
+ }
27959
+ return value;
27960
+ }
27907
27961
  if (argument.unserializableValue !== void 0) {
27908
27962
  const raw = String(argument.unserializableValue);
27909
27963
  if (raw === "NaN") return NaN;
@@ -27953,83 +28007,321 @@ var CdpServer = class {
27953
28007
  return { type, objectId, className, description: type === "function" ? "function" : className, ...subtype ? { subtype } : {} };
27954
28008
  }
27955
28009
  };
28010
+ function documentScripts(root) {
28011
+ const scripts = [];
28012
+ const visit = (node2, path) => {
28013
+ if (node2.type === "element" && node2.name === "script") {
28014
+ const type = node2.attrs?.type?.trim().toLowerCase();
28015
+ if (!type || type === "text/javascript" || type === "application/javascript") {
28016
+ const inline = (node2.children ?? []).filter((child) => child.type === "text").map((child) => child.text ?? "").join("");
28017
+ scripts.push({ path, code: node2.resourceText ?? inline, url: node2.attrs?.src ?? "vireo:inline-script" });
28018
+ }
28019
+ }
28020
+ node2.children?.forEach((child, index) => visit(child, [...path, index]));
28021
+ };
28022
+ visit(root, []);
28023
+ return scripts;
28024
+ }
28025
+ function domBootstrap(document2) {
28026
+ return `(() => {
28027
+ const snapshot = ${JSON.stringify(document2)};
28028
+ const escapeText = value => String(value).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
28029
+ const escapeAttr = value => escapeText(value).replaceAll('"', '&quot;');
28030
+ class VireoNode {
28031
+ constructor(data, parent = null) {
28032
+ this.__data = data; this.parentNode = parent;
28033
+ this.__listeners = new Map();
28034
+ this.childNodes = (data.children || []).map(child => new VireoNode(child, this));
28035
+ }
28036
+ get nodeType() { return this.__data.type === 'document' ? 9 : this.__data.type === 'text' ? 3 : 1; }
28037
+ get nodeName() { return this.nodeType === 9 ? '#document' : this.nodeType === 3 ? '#text' : this.__data.name.toUpperCase(); }
28038
+ get textContent() { return this.nodeType === 3 ? this.__data.text : this.childNodes.map(node => node.textContent).join(''); }
28039
+ set textContent(value) { if (this.nodeType === 3) this.__data.text = String(value); else { const child = new VireoNode({ type:'text', text:String(value) }, this); this.childNodes = [child]; } notify(this, 'characterData'); }
28040
+ get children() { return this.childNodes.filter(node => node.nodeType === 1); }
28041
+ get firstChild() { return this.childNodes[0] || null; }
28042
+ get parentElement() { return this.parentNode?.nodeType === 1 ? this.parentNode : null; }
28043
+ get ownerDocument() { let node = this; while (node.parentNode) node = node.parentNode; return node.nodeType === 9 ? node : null; }
28044
+ getRootNode() { let node = this; while (node.parentNode) node = node.parentNode; return node; }
28045
+ get isConnected() { let node = this; while (node.parentNode) node = node.parentNode; return node.nodeType === 9; }
28046
+ contains(other) { for (let node = other; node; node = node.parentNode) if (node === this) return true; return false; }
28047
+ addEventListener(type, listener) { const set = this.__listeners.get(type) || new Set(); set.add(listener); this.__listeners.set(type, set); }
28048
+ removeEventListener(type, listener) { this.__listeners.get(type)?.delete(listener); }
28049
+ dispatchEvent(event) { for (const listener of this.__listeners.get(event.type) || []) listener.call(this, event); return true; }
28050
+ querySelector(selector) { return query(this, selector)[0] || null; }
28051
+ querySelectorAll(selector) { return query(this, selector); }
28052
+ appendChild(child) { child.remove(); child.parentNode = this; this.childNodes.push(child); notify(this, 'childList'); return child; }
28053
+ append(...items) { for (const item of items) this.appendChild(typeof item === 'string' ? document.createTextNode(item) : item); }
28054
+ prepend(...items) { for (const item of items.reverse()) { const child = typeof item === 'string' ? document.createTextNode(item) : item; child.remove(); child.parentNode = this; this.childNodes.unshift(child); } notify(this, 'childList'); }
28055
+ removeChild(child) { const index = this.childNodes.indexOf(child); if (index < 0) throw new Error('NotFoundError'); this.childNodes.splice(index, 1); child.parentNode = null; notify(this, 'childList'); return child; }
28056
+ remove() { this.parentNode?.removeChild(this); }
28057
+ replaceChildren(...items) { for (const child of this.childNodes) child.parentNode = null; this.childNodes = []; this.append(...items); }
28058
+ insertBefore(child, reference) { if (reference == null) return this.appendChild(child); const index = this.childNodes.indexOf(reference); if (index < 0) throw new Error('NotFoundError'); child.remove(); child.parentNode = this; this.childNodes.splice(index, 0, child); notify(this, 'childList'); return child; }
28059
+ }
28060
+ class VireoElement extends VireoNode {}
28061
+ Object.assign(VireoNode, { ELEMENT_NODE: 1, TEXT_NODE: 3, DOCUMENT_NODE: 9 });
28062
+ Object.defineProperties(VireoNode.prototype, {
28063
+ tagName: { get() { return this.nodeType === 1 ? this.__data.name.toUpperCase() : undefined; } },
28064
+ localName: { get() { return this.nodeType === 1 ? this.__data.name : undefined; } },
28065
+ namespaceURI: { get() { return this.nodeType === 1 ? 'http://www.w3.org/1999/xhtml' : null; } },
28066
+ id: { get() { return this.getAttribute?.('id') || ''; }, set(value) { this.setAttribute('id', value); } },
28067
+ className: { get() { return this.getAttribute?.('class') || ''; }, set(value) { this.setAttribute('class', value); } },
28068
+ classList: { get() { const node = this; const values = () => node.className.split(/\\s+/).filter(Boolean); return { add(...names) { node.className = [...new Set([...values(), ...names])].join(' '); }, remove(...names) { node.className = values().filter(value => !names.includes(value)).join(' '); }, contains(name) { return values().includes(name); }, toggle(name, force) { const present = values().includes(name); const next = force === undefined ? !present : Boolean(force); next ? this.add(name) : this.remove(name); return next; }, toString() { return node.className; } }; } },
28069
+ innerText: { get() { return this.textContent.replace(/\\s+/g, ' ').trim(); } },
28070
+ attributes: { get() { return Object.entries(this.__data.attrs || {}).map(([name, value]) => ({ name, value })); } },
28071
+ innerHTML: { get() { return this.childNodes.map(serialize).join(''); } },
28072
+ outerHTML: { get() { return serialize(this); } },
28073
+ value: { get() { return this.__value ?? this.getAttribute?.('value') ?? ''; }, set(value) { this.__value = String(value); this.setAttribute?.('value', this.__value); } },
28074
+ selectionStart: { get() { return this.__selectionStart ?? 0; }, set(value) { this.__selectionStart = Number(value); } },
28075
+ selectionEnd: { get() { return this.__selectionEnd ?? this.value.length; }, set(value) { this.__selectionEnd = Number(value); } },
28076
+ checked: { get() { return this.__checked ?? this.hasAttribute?.('checked') ?? false; }, set(value) { this.__checked = Boolean(value); } },
28077
+ disabled: { get() { return this.hasAttribute?.('disabled') ?? false; }, set(value) { value ? this.setAttribute('disabled', '') : this.removeAttribute('disabled'); } },
28078
+ readOnly: { get() { return this.hasAttribute?.('readonly') ?? false; }, set(value) { value ? this.setAttribute('readonly', '') : this.removeAttribute('readonly'); } },
28079
+ type: { get() { return this.getAttribute?.('type') || ''; }, set(value) { this.setAttribute('type', value); } },
28080
+ isContentEditable: { get() { return this.getAttribute?.('contenteditable') === 'true'; } },
28081
+ contentDocument: { get() { return this.__contentDocument || null; } },
28082
+ contentWindow: { get() { return this.__contentDocument?.defaultView || null; } },
28083
+ });
28084
+ VireoNode.prototype.getAttribute = function(name) { const attrs = this.__data.attrs || {}; return Object.hasOwn(attrs, name) ? attrs[name] : null; };
28085
+ VireoNode.prototype.hasAttribute = function(name) { return this.getAttribute(name) !== null; };
28086
+ VireoNode.prototype.setAttribute = function(name, value) { (this.__data.attrs ||= {})[String(name)] = String(value); notify(this, 'attributes'); };
28087
+ VireoNode.prototype.removeAttribute = function(name) { delete (this.__data.attrs ||= {})[String(name)]; notify(this, 'attributes'); };
28088
+ VireoNode.prototype.matches = function(selector) { return matches(this, selector); };
28089
+ VireoNode.prototype.focus = function() { document.activeElement = this; this.dispatchEvent(new Event('focus')); };
28090
+ VireoNode.prototype.blur = function() { if (document.activeElement === this) document.activeElement = document.body; this.dispatchEvent(new Event('blur')); };
28091
+ VireoNode.prototype.click = function() { this.focus(); if (this.localName === 'input' && ['checkbox','radio'].includes(this.getAttribute('type'))) this.checked = !this.checked; this.dispatchEvent(new MouseEvent('click', { bubbles: true })); };
28092
+ VireoNode.prototype.getBoundingClientRect = function() { const all = descendants(document).filter(node => node.nodeType === 1); const index = Math.max(0, all.indexOf(this)); const top = index * 24; return { x: 0, y: top, top, left: 0, width: 800, height: 20, right: 800, bottom: top + 20, toJSON() { return { x:0, y:top, width:800, height:20, top, left:0, right:800, bottom:top+20 }; } }; };
28093
+ VireoNode.prototype.getClientRects = function() { return [this.getBoundingClientRect()]; };
28094
+ VireoNode.prototype.scrollIntoView = function() {};
28095
+ VireoNode.prototype.select = function() { this.selectionStart = 0; this.selectionEnd = this.value.length; };
28096
+ VireoNode.prototype.setSelectionRange = function(start, end) { this.selectionStart = start; this.selectionEnd = end; };
28097
+ VireoNode.prototype.submit = function() { this.dispatchEvent(new Event('submit', { bubbles: true })); };
28098
+ VireoNode.prototype.requestSubmit = function() { this.submit(); };
28099
+ const make = (data, parent = null, path = []) => {
28100
+ const node = data.type === 'element' ? new VireoElement(data, parent) : new VireoNode(data, parent);
28101
+ node.__vireoPath = path;
28102
+ node.childNodes = (data.children || []).map((child, index) => make(child, node, [...path, index]));
28103
+ if (data.contentDocument) node.__pendingContentDocument = data.contentDocument;
28104
+ return node;
28105
+ };
28106
+ const root = make(snapshot.root, null, []);
28107
+ function descendants(node) { return node.childNodes.flatMap(child => [child, ...descendants(child)]); }
28108
+ function matches(node, selector) {
28109
+ if (node.nodeType !== 1) return false;
28110
+ const part = selector.trim();
28111
+ const attr = /\\[([\\w:-]+)(?:=["']?([^\\]"']+)["']?)?\\]/.exec(part);
28112
+ const id = /#([\\w-]+)/.exec(part); const classes = [...part.matchAll(/\\.([\\w-]+)/g)].map(m => m[1]);
28113
+ const tag = /^([a-z][\\w-]*|\\*)/i.exec(part)?.[1];
28114
+ return (!tag || tag === '*' || node.localName === tag.toLowerCase())
28115
+ && (!id || node.id === id[1])
28116
+ && classes.every(name => node.className.split(/\\s+/).includes(name))
28117
+ && (!attr || (node.hasAttribute(attr[1]) && (attr[2] === undefined || node.getAttribute(attr[1]) === attr[2])));
28118
+ }
28119
+ function query(node, selector) {
28120
+ const groups = selector.split(',').map(value => value.trim()).filter(Boolean); const out = [];
28121
+ for (const group of groups) {
28122
+ let scope = [node];
28123
+ for (const part of group.split(/\\s+/)) scope = scope.flatMap(parent => descendants(parent).filter(child => matches(child, part)));
28124
+ for (const match of scope) if (!out.includes(match)) out.push(match);
28125
+ }
28126
+ return out;
28127
+ }
28128
+ function serialize(node) {
28129
+ if (node.nodeType === 3) return escapeText(node.textContent);
28130
+ if (node.nodeType === 9) return node.childNodes.map(serialize).join('');
28131
+ const attrs = Object.entries(node.__data.attrs || {}).map(([k,v]) => ' ' + k + '="' + escapeAttr(v) + '"').join('');
28132
+ return '<' + node.localName + attrs + '>' + node.childNodes.map(serialize).join('') + '</' + node.localName + '>';
28133
+ }
28134
+ const document = root;
28135
+ document.URL = snapshot.url; document.documentURI = snapshot.url; document.readyState = 'complete';
28136
+ document.title = snapshot.title;
28137
+ document.documentElement = document.querySelector('html'); document.head = document.querySelector('head'); document.body = document.querySelector('body');
28138
+ document.getElementById = id => document.querySelector('#' + id);
28139
+ document.createElement = name => make({ type:'element', name:String(name).toLowerCase(), attrs:{}, children:[] });
28140
+ document.createTextNode = text => make({ type:'text', text:String(text) });
28141
+ document.activeElement = document.body;
28142
+ document.hasFocus = () => true;
28143
+ document.elementFromPoint = (_x, y) => descendants(document).filter(node => node.nodeType === 1).find(node => { const rect = node.getBoundingClientRect(); return y >= rect.top && y <= rect.bottom; }) || document.body;
28144
+ document.__nodeByPath = path => path.reduce((node, index) => node?.childNodes[index], document);
28145
+ const listeners = new Map();
28146
+ globalThis.addEventListener = (type, listener) => { const set = listeners.get(type) || new Set(); set.add(listener); listeners.set(type, set); };
28147
+ globalThis.removeEventListener = (type, listener) => listeners.get(type)?.delete(listener);
28148
+ globalThis.dispatchEvent = event => { for (const listener of listeners.get(event.type) || []) listener.call(globalThis, event); return true; };
28149
+ const observers = new Set();
28150
+ const notify = (target, type) => queueMicrotask(() => { for (const observer of observers) observer.callback([{ target, type }], observer); });
28151
+ class MutationObserver { constructor(callback) { this.callback = callback; } observe() { observers.add(this); } disconnect() { observers.delete(this); } takeRecords() { return []; } }
28152
+ class Event { constructor(type, init = {}) { this.type = type; Object.assign(this, init); } }
28153
+ class CustomEvent extends Event { constructor(type, init = {}) { super(type, init); this.detail = init.detail; } }
28154
+ class MouseEvent extends Event {}
28155
+ class KeyboardEvent extends Event {}
28156
+ const requestAnimationFrame = callback => setTimeout(() => callback(Date.now()), 0);
28157
+ const cancelAnimationFrame = id => clearTimeout(id);
28158
+ const getComputedStyle = element => ({ display: element.hasAttribute?.('hidden') ? 'none' : 'block', visibility: 'visible', opacity: '1', pointerEvents: 'auto', getPropertyValue() { return ''; } });
28159
+ Object.assign(globalThis, { window: globalThis, self: globalThis, document, Node: VireoNode, Element: VireoElement, HTMLElement: VireoElement, HTMLInputElement: VireoElement, HTMLTextAreaElement: VireoElement, HTMLSelectElement: VireoElement, HTMLButtonElement: VireoElement, HTMLFormElement: VireoElement, Event, CustomEvent, MouseEvent, KeyboardEvent, MutationObserver, requestAnimationFrame, cancelAnimationFrame, getComputedStyle, innerWidth: 1280, innerHeight: 720, scrollX: 0, scrollY: 0, location: new URL(snapshot.url), navigator: { userAgent: ${JSON.stringify(VIRTUAL_USER_AGENT)} } });
28160
+ document.defaultView = globalThis;
28161
+ for (const frame of document.querySelectorAll('iframe')) if (frame.__pendingContentDocument) frame.__contentDocument = buildNestedDocument(frame.__pendingContentDocument);
28162
+ function buildNestedDocument(childSnapshot) {
28163
+ const child = make(childSnapshot.root); child.URL = childSnapshot.url; child.title = childSnapshot.title; child.readyState = 'complete';
28164
+ child.documentElement = child.querySelector('html'); child.head = child.querySelector('head'); child.body = child.querySelector('body'); child.defaultView = { document: child, frameElement: null, location: new URL(childSnapshot.url) }; return child;
28165
+ }
28166
+ })()`;
28167
+ }
27956
28168
  var DEFERRED = /* @__PURE__ */ Symbol("reply already sent");
27957
28169
 
27958
- // src/browser/chrome-command.ts
27959
- var USAGE = "chrome --remote-debugging-pipe [chromium flags\u2026]";
27960
- async function run2(ctx) {
27961
- if (ctx.args.includes("--version")) {
27962
- ctx.line(`Chromium ${VIRTUAL_CHROMIUM_VERSION} (SandboxedJs virtual browser)`);
27963
- return 0;
27964
- }
27965
- if (!ctx.args.includes("--remote-debugging-pipe")) {
27966
- return ctx.fail(
27967
- "only --remote-debugging-pipe is supported: this is the SandboxedJs virtual browser, driven through the DevTools protocol (Playwright, Puppeteer), not an interactive Chromium"
27968
- );
27969
- }
27970
- const input = ctx.proc.fds.get(3);
27971
- const output = ctx.proc.fds.get(4);
27972
- if (!input || !output) {
27973
- return ctx.fail("--remote-debugging-pipe needs descriptors 3 and 4 open as pipes (stdio: ['ignore','pipe','pipe','pipe','pipe'])");
27974
- }
27975
- let closed = false;
27976
- const server = new CdpServer({
27977
- send: (message) => {
27978
- if (output.closed) return;
27979
- output.write(`${JSON.stringify(message)}\0`);
27980
- },
27981
- onClose: () => {
27982
- closed = true;
27983
- input.close();
28170
+ // src/browser/search-command.ts
28171
+ var DEFAULT_SEARCH_ENDPOINT = "https://html.duckduckgo.com/html/?q=";
28172
+ var USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
28173
+ function decodeEntities(value) {
28174
+ const named = {
28175
+ amp: "&",
28176
+ apos: "'",
28177
+ gt: ">",
28178
+ hellip: "\u2026",
28179
+ lt: "<",
28180
+ nbsp: " ",
28181
+ quot: '"'
28182
+ };
28183
+ return value.replace(/&(#x[\da-f]+|#\d+|[a-z][\da-z]+);/gi, (whole, entity) => {
28184
+ if (entity[0] === "#") {
28185
+ const hex = entity[1]?.toLowerCase() === "x";
28186
+ const code = Number.parseInt(entity.slice(hex ? 2 : 1), hex ? 16 : 10);
28187
+ return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
27984
28188
  }
28189
+ return named[entity.toLowerCase()] ?? whole;
27985
28190
  });
27986
- ctx.stderr.write(`
27987
- DevTools listening on pipe (SandboxedJs virtual browser ${VIRTUAL_CHROMIUM_VERSION})
27988
- `);
27989
- const decoder9 = new TextDecoder();
27990
- let pending = "";
27991
- const aborted = new Promise((resolve3) => ctx.signal.addEventListener("abort", () => resolve3(null), { once: true }));
28191
+ }
28192
+ function plainText(html) {
28193
+ return decodeEntities(html.replace(/<[^>]*>/g, " ")).replace(/\s+/g, " ").trim();
28194
+ }
28195
+ function attribute(tag2, name) {
28196
+ const match2 = new RegExp(`\\b${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "i").exec(tag2);
28197
+ return match2 ? decodeEntities(match2[1] ?? match2[2] ?? match2[3] ?? "") : void 0;
28198
+ }
28199
+ function absoluteUrl(raw, base2) {
27992
28200
  try {
27993
- while (!closed) {
27994
- const chunk = await Promise.race([input.read(), aborted]);
27995
- if (chunk === null) break;
27996
- pending += decoder9.decode(chunk, { stream: true });
27997
- let end;
27998
- while ((end = pending.indexOf("\0")) >= 0) {
27999
- const frame = pending.slice(0, end);
28000
- pending = pending.slice(end + 1);
28001
- if (!frame) continue;
28002
- let message;
28003
- try {
28004
- message = JSON.parse(frame);
28005
- } catch {
28006
- ctx.warn(`dropping malformed DevTools message: ${frame.slice(0, 80)}`);
28007
- continue;
28008
- }
28009
- void server.dispatch(message);
28010
- }
28011
- }
28012
- } finally {
28013
- server.dispose();
28014
- output.end();
28201
+ const url = new URL(raw, base2);
28202
+ if (!/^https?:$/.test(url.protocol)) return void 0;
28203
+ return url.toString();
28204
+ } catch {
28205
+ return void 0;
28015
28206
  }
28016
- return 0;
28017
28207
  }
28018
- function browserCommands() {
28019
- return [
28020
- defineCommand({
28021
- name: "chrome",
28022
- aliases: ["chromium", "chromium-browser", "google-chrome", "chrome-headless-shell"],
28023
- summary: "SandboxedJs virtual browser, driven over the DevTools protocol",
28024
- usage: USAGE,
28025
- run: run2
28026
- })
28027
- ];
28208
+ function resultUrl(raw, base2) {
28209
+ const resolved = absoluteUrl(raw, base2);
28210
+ if (!resolved) return void 0;
28211
+ const url = new URL(resolved);
28212
+ const redirected = url.searchParams.get("uddg");
28213
+ return redirected ? absoluteUrl(redirected, base2) : resolved;
28028
28214
  }
28029
-
28030
- // src/apps/pm.ts
28031
- init_binary();
28032
- init_zip();
28215
+ function parseSearchResults(html, base2 = "https://html.duckduckgo.com/") {
28216
+ const results = [];
28217
+ const anchors = /<a\b[^>]*class\s*=\s*["'][^"']*\bresult__a\b[^"']*["'][^>]*>[\s\S]*?<\/a>/gi;
28218
+ for (const match2 of html.matchAll(anchors)) {
28219
+ const tag2 = /^<a\b[^>]*>/i.exec(match2[0])?.[0] ?? "";
28220
+ const url = resultUrl(attribute(tag2, "href") ?? "", base2);
28221
+ if (!url) continue;
28222
+ const title = plainText(match2[0]);
28223
+ const tail2 = html.slice((match2.index ?? 0) + match2[0].length, (match2.index ?? 0) + match2[0].length + 4e3);
28224
+ const snippetMatch = /<(?:a|div)\b[^>]*class\s*=\s*["'][^"']*\bresult__snippet\b[^"']*["'][^>]*>([\s\S]*?)<\/(?:a|div)>/i.exec(tail2);
28225
+ results.push({ title, url, snippet: snippetMatch ? plainText(snippetMatch[1]) : "" });
28226
+ }
28227
+ return results;
28228
+ }
28229
+ function extractPage(html, url, status = 200) {
28230
+ const titleMatch = /<title\b[^>]*>([\s\S]*?)<\/title>/i.exec(html);
28231
+ const links = [];
28232
+ for (const match2 of html.matchAll(/<a\b[^>]*>[\s\S]*?<\/a>/gi)) {
28233
+ const tag2 = /^<a\b[^>]*>/i.exec(match2[0])?.[0] ?? "";
28234
+ const href = absoluteUrl(attribute(tag2, "href") ?? "", url);
28235
+ const text2 = plainText(match2[0]);
28236
+ if (href && text2 && !links.some((link) => link.url === href)) links.push({ text: text2, url: href });
28237
+ if (links.length >= 50) break;
28238
+ }
28239
+ const visible2 = html.replace(/<!--([\s\S]*?)-->/g, " ").replace(/<head\b[^>]*>[\s\S]*?<\/head>/gi, " ").replace(/<title\b[^>]*>[\s\S]*?<\/title>/gi, " ").replace(/<(script|style|noscript|svg|template)\b[^>]*>[\s\S]*?<\/\1>/gi, " ").replace(/<(br|hr)\b[^>]*>|<\/(p|div|article|section|main|header|footer|li|h[1-6])>/gi, "\n");
28240
+ return {
28241
+ type: "page",
28242
+ url,
28243
+ status,
28244
+ title: titleMatch ? plainText(titleMatch[1]) : "",
28245
+ text: decodeEntities(visible2.replace(/<[^>]*>/g, " ")).replace(/[ \t\f\v]+/g, " ").replace(/ *\n */g, "\n").replace(/\n{3,}/g, "\n\n").trim(),
28246
+ links
28247
+ };
28248
+ }
28249
+ function targetUrl(raw) {
28250
+ try {
28251
+ if (/^https?:\/\//i.test(raw)) return new URL(raw);
28252
+ if (/^(localhost|127\.0\.0\.1|\[::1\])(?::\d+)?(?:\/|$)/i.test(raw)) return new URL(`http://${raw}`);
28253
+ if (/^[\w.-]+\.[a-z]{2,}(?::\d+)?(?:\/|$)/i.test(raw)) return new URL(`https://${raw}`);
28254
+ } catch {
28255
+ }
28256
+ return void 0;
28257
+ }
28258
+ async function get(ctx, url, redirects = 5) {
28259
+ let current2 = url;
28260
+ for (let n = 0; ; n++) {
28261
+ const result = await performRequest(ctx, current2, {
28262
+ method: "GET",
28263
+ headers: { accept: "text/html,application/xhtml+xml,text/plain;q=0.9,*/*;q=0.1", "user-agent": USER_AGENT }
28264
+ });
28265
+ const location2 = result.headers.location;
28266
+ if (result.status < 300 || result.status >= 400 || !location2) return result;
28267
+ if (n >= redirects) throw new Error("too many redirects");
28268
+ current2 = new URL(location2, current2);
28269
+ }
28270
+ }
28271
+ function truncate(text2, max) {
28272
+ return text2.length <= max ? text2 : `${text2.slice(0, Math.max(0, max - 1)).trimEnd()}\u2026`;
28273
+ }
28274
+ var search = defineCommand({
28275
+ name: "search",
28276
+ path: "/usr/bin/search",
28277
+ summary: "search the web or read a URL headlessly",
28278
+ usage: "search [--json] [-n count] [--max-chars count] <query or URL>",
28279
+ manual: "Searches DuckDuckGo's HTML endpoint for a query, or fetches and extracts readable text from an HTTP(S) URL. Outbound access must be enabled. This text browser does not execute page JavaScript.",
28280
+ async run(ctx) {
28281
+ const args = parseArgs(ctx.args, [
28282
+ { long: "json" },
28283
+ { short: "n", long: "count", arg: true, default: "5" },
28284
+ { long: "max-chars", arg: true, default: "12000" }
28285
+ ]);
28286
+ const raw = args.positional.join(" ").trim();
28287
+ if (!raw) return ctx.fail("missing query or URL", 2);
28288
+ const count = Math.max(1, Math.min(20, args.num("count", 5) ?? 5));
28289
+ const maxChars = Math.max(200, Math.min(1e5, args.num("max-chars", 12e3) ?? 12e3));
28290
+ try {
28291
+ const direct = targetUrl(raw);
28292
+ if (direct) {
28293
+ const response2 = await get(ctx, direct);
28294
+ const contentType = response2.headers["content-type"] ?? "";
28295
+ const decoded = new TextDecoder().decode(response2.body);
28296
+ const page = /html|xhtml/i.test(contentType) || /^\s*</.test(decoded) ? extractPage(decoded, response2.url, response2.status) : { type: "page", url: response2.url, status: response2.status, title: "", text: decoded, links: [] };
28297
+ page.text = truncate(page.text, maxChars);
28298
+ if (args.has("json")) ctx.line(JSON.stringify(page, null, 2));
28299
+ else {
28300
+ ctx.line(`# ${page.title || page.url}`);
28301
+ ctx.line(`URL: ${page.url}`);
28302
+ ctx.line(`Status: ${page.status}`);
28303
+ ctx.line();
28304
+ ctx.line(page.text);
28305
+ }
28306
+ return response2.status >= 400 ? 22 : 0;
28307
+ }
28308
+ const endpoint = ctx.env.SEARCH_ENDPOINT || DEFAULT_SEARCH_ENDPOINT;
28309
+ const response = await get(ctx, new URL(`${endpoint}${encodeURIComponent(raw)}`));
28310
+ const results = parseSearchResults(new TextDecoder().decode(response.body), response.url).slice(0, count);
28311
+ const output = { type: "search", query: raw, results };
28312
+ if (args.has("json")) ctx.line(JSON.stringify(output, null, 2));
28313
+ else if (!results.length) ctx.line("No search results found.");
28314
+ else results.forEach((result, index) => {
28315
+ ctx.line(`${index + 1}. ${result.title}`);
28316
+ ctx.line(` ${result.url}`);
28317
+ if (result.snippet) ctx.line(` ${result.snippet}`);
28318
+ });
28319
+ return response.status >= 400 ? 22 : 0;
28320
+ } catch (error) {
28321
+ return ctx.fail(error instanceof Error ? error.message : String(error), 1);
28322
+ }
28323
+ }
28324
+ });
28033
28325
 
28034
28326
  // src/apps/registry.ts
28035
28327
  var APPS_ROOT = "/opt";
@@ -28126,8 +28418,242 @@ function validateManifest2(manifest, source) {
28126
28418
  throw new AppRegistryError(`${source}: a service needs an integer port`);
28127
28419
  }
28128
28420
  }
28421
+ if (app.browserEngine) {
28422
+ if (app.browserEngine.abi !== "sandboxedjs-browser-engine-v1") {
28423
+ throw new AppRegistryError(`${source}: unsupported browser engine ABI ${JSON.stringify(app.browserEngine.abi)}`);
28424
+ }
28425
+ if (typeof app.browserEngine.wasm !== "string" || !safeRelativePath(app.browserEngine.wasm)) {
28426
+ throw new AppRegistryError(`${source}: browserEngine.wasm must be a safe package-relative path`);
28427
+ }
28428
+ if (typeof app.browserEngine.product !== "string" || !app.browserEngine.product) {
28429
+ throw new AppRegistryError(`${source}: browserEngine.product is required`);
28430
+ }
28431
+ }
28129
28432
  return app;
28130
28433
  }
28434
+ function safeRelativePath(path) {
28435
+ return path !== "" && !path.startsWith("/") && !path.split("/").some((part) => part === "" || part === "." || part === "..");
28436
+ }
28437
+
28438
+ // src/browser/vireo.ts
28439
+ var VIREO_APP = "vireo";
28440
+ var VireoEngine = class _VireoEngine {
28441
+ constructor(product, wasm) {
28442
+ this.product = product;
28443
+ this.wasm = wasm;
28444
+ }
28445
+ product;
28446
+ wasm;
28447
+ static async load(ctx) {
28448
+ const app = installedVireo(ctx);
28449
+ if (!app?.browserEngine) return null;
28450
+ const manifest = app.browserEngine;
28451
+ const bytes2 = ctx.vfs.readFile(`${app.root}/${manifest.wasm}`, ctx.cred);
28452
+ const loaded = await WebAssembly.instantiate(bytes2, {});
28453
+ const instance = loaded instanceof WebAssembly.Instance ? loaded : loaded.instance;
28454
+ const wasm = instance.exports;
28455
+ for (const name of ["memory", "vireo_alloc", "vireo_load_html", "vireo_snapshot_pointer", "vireo_snapshot_length", "vireo_free_document"]) {
28456
+ if (!(name in wasm)) throw new Error(`Vireo's ${manifest.abi} module does not export ${name}`);
28457
+ }
28458
+ return new _VireoEngine(manifest.product, wasm);
28459
+ }
28460
+ parse(html, url) {
28461
+ const urlBytes = new TextEncoder().encode(url);
28462
+ const htmlPointer = this.copy(html);
28463
+ const urlPointer = this.copy(urlBytes);
28464
+ const handle = this.wasm.vireo_load_html(htmlPointer, html.length, urlPointer, urlBytes.length);
28465
+ if (!handle) throw new Error("Vireo could not parse the document");
28466
+ try {
28467
+ const pointer = this.wasm.vireo_snapshot_pointer(handle);
28468
+ const length = this.wasm.vireo_snapshot_length(handle);
28469
+ if (!pointer || !length) throw new Error("Vireo returned an empty document snapshot");
28470
+ const json = new TextDecoder().decode(new Uint8Array(this.wasm.memory.buffer, pointer, length).slice());
28471
+ return JSON.parse(json);
28472
+ } finally {
28473
+ this.wasm.vireo_free_document(handle);
28474
+ }
28475
+ }
28476
+ copy(bytes2) {
28477
+ if (!bytes2.length) return 0;
28478
+ const pointer = this.wasm.vireo_alloc(bytes2.length);
28479
+ if (!pointer) throw new Error("Vireo ran out of memory");
28480
+ new Uint8Array(this.wasm.memory.buffer, pointer, bytes2.length).set(bytes2);
28481
+ return pointer;
28482
+ }
28483
+ };
28484
+ function installedVireo(ctx) {
28485
+ try {
28486
+ const state = JSON.parse(ctx.vfs.readText(APP_STATE, ctx.cred));
28487
+ return state.apps?.find((app) => app.name === VIREO_APP);
28488
+ } catch {
28489
+ return void 0;
28490
+ }
28491
+ }
28492
+ async function ensureVireo(ctx) {
28493
+ const present = await VireoEngine.load(ctx);
28494
+ if (present) return present;
28495
+ if (ctx.env.SBX_VIREO_AUTOINSTALL === "0") return null;
28496
+ const result = await ctx.kernel.run(["pm", "install", VIREO_APP], {
28497
+ cwd: ctx.cwd,
28498
+ env: ctx.env,
28499
+ cred: ctx.cred,
28500
+ ppid: ctx.proc.pid,
28501
+ timeoutMs: 12e4
28502
+ });
28503
+ if (result.exitCode !== 0) {
28504
+ ctx.warn(`Vireo is not installed and automatic installation failed: ${(result.stderr || result.stdout).trim()}`);
28505
+ return null;
28506
+ }
28507
+ return VireoEngine.load(ctx);
28508
+ }
28509
+
28510
+ // src/browser/chrome-command.ts
28511
+ var USAGE = "chrome --remote-debugging-pipe [chromium flags\u2026]";
28512
+ async function run2(ctx) {
28513
+ if (ctx.args.includes("--version")) {
28514
+ ctx.line(`Chromium ${VIRTUAL_CHROMIUM_VERSION} (SandboxedJs virtual browser)`);
28515
+ return 0;
28516
+ }
28517
+ if (!ctx.args.includes("--remote-debugging-pipe")) {
28518
+ return ctx.fail(
28519
+ "only --remote-debugging-pipe is supported: this is the SandboxedJs virtual browser, driven through the DevTools protocol (Playwright, Puppeteer), not an interactive Chromium"
28520
+ );
28521
+ }
28522
+ const input = ctx.proc.fds.get(3);
28523
+ const output = ctx.proc.fds.get(4);
28524
+ if (!input || !output) {
28525
+ return ctx.fail("--remote-debugging-pipe needs descriptors 3 and 4 open as pipes (stdio: ['ignore','pipe','pipe','pipe','pipe'])");
28526
+ }
28527
+ const vireo = await ensureVireo(ctx);
28528
+ let closed = false;
28529
+ const server = new CdpServer({
28530
+ send: (message) => {
28531
+ if (output.closed) return;
28532
+ output.write(`${JSON.stringify(message)}\0`);
28533
+ },
28534
+ onClose: () => {
28535
+ closed = true;
28536
+ input.close();
28537
+ },
28538
+ ...vireo ? {
28539
+ navigate: (rawUrl) => loadDocument(ctx, vireo, rawUrl)
28540
+ } : {}
28541
+ });
28542
+ ctx.stderr.write(`
28543
+ DevTools listening on pipe (${vireo?.product ?? `SandboxedJs virtual browser ${VIRTUAL_CHROMIUM_VERSION}`})
28544
+ `);
28545
+ const decoder9 = new TextDecoder();
28546
+ let pending = "";
28547
+ const aborted = new Promise((resolve3) => ctx.signal.addEventListener("abort", () => resolve3(null), { once: true }));
28548
+ try {
28549
+ while (!closed) {
28550
+ const chunk = await Promise.race([input.read(), aborted]);
28551
+ if (chunk === null) break;
28552
+ pending += decoder9.decode(chunk, { stream: true });
28553
+ let end;
28554
+ while ((end = pending.indexOf("\0")) >= 0) {
28555
+ const frame = pending.slice(0, end);
28556
+ pending = pending.slice(end + 1);
28557
+ if (!frame) continue;
28558
+ let message;
28559
+ try {
28560
+ message = JSON.parse(frame);
28561
+ } catch {
28562
+ ctx.warn(`dropping malformed DevTools message: ${frame.slice(0, 80)}`);
28563
+ continue;
28564
+ }
28565
+ void server.dispatch(message);
28566
+ }
28567
+ }
28568
+ } finally {
28569
+ server.dispose();
28570
+ output.end();
28571
+ }
28572
+ return 0;
28573
+ }
28574
+ async function requestResource(ctx, rawUrl, accept) {
28575
+ let url = new URL(rawUrl);
28576
+ let response;
28577
+ for (let redirects = 0; ; redirects += 1) {
28578
+ response = await performRequest(ctx, url, {
28579
+ method: "GET",
28580
+ headers: { accept, "user-agent": "Mozilla/5.0 (X11; Linux x86_64) Vireo/0.2" }
28581
+ });
28582
+ const location2 = response.headers.location;
28583
+ if (response.status < 300 || response.status >= 400 || !location2) break;
28584
+ if (redirects >= 5) throw new Error("net::ERR_TOO_MANY_REDIRECTS");
28585
+ url = new URL(location2, url);
28586
+ }
28587
+ if (response.status >= 400) throw new Error(`HTTP ${response.status} ${response.statusText}`);
28588
+ return { response, url: response.url || url.toString() };
28589
+ }
28590
+ async function loadDocument(ctx, engine, rawUrl, depth = 0) {
28591
+ if (rawUrl.startsWith("data:")) {
28592
+ const comma = rawUrl.indexOf(",");
28593
+ if (comma < 0) throw new Error("net::ERR_INVALID_URL");
28594
+ const metadata = rawUrl.slice(5, comma);
28595
+ const raw = rawUrl.slice(comma + 1);
28596
+ const text2 = /;base64(?:;|$)/i.test(metadata) ? new TextDecoder().decode(Uint8Array.from(atob(raw), (character) => character.charCodeAt(0))) : decodeURIComponent(raw);
28597
+ const document3 = engine.parse(new TextEncoder().encode(text2), rawUrl);
28598
+ await loadSubresources(ctx, engine, document3.root, rawUrl, depth);
28599
+ return { url: rawUrl, document: document3 };
28600
+ }
28601
+ const loaded = await requestResource(ctx, rawUrl, "text/html,application/xhtml+xml");
28602
+ const document2 = engine.parse(loaded.response.body, loaded.url);
28603
+ await loadSubresources(ctx, engine, document2.root, loaded.url, depth);
28604
+ return { url: loaded.url, document: document2 };
28605
+ }
28606
+ async function loadSubresources(ctx, engine, node2, baseUrl, depth) {
28607
+ const tasks = [];
28608
+ if (node2.type === "element" && node2.name === "script" && node2.attrs?.src) {
28609
+ tasks.push((async () => {
28610
+ try {
28611
+ const resource = await requestResource(ctx, new URL(node2.attrs.src, baseUrl).toString(), "text/javascript,application/javascript,*/*;q=0.1");
28612
+ node2.resourceText = new TextDecoder().decode(resource.response.body);
28613
+ node2.attrs.src = resource.url;
28614
+ } catch {
28615
+ node2.resourceText = "";
28616
+ }
28617
+ })());
28618
+ }
28619
+ if (node2.type === "element" && node2.name === "link" && /stylesheet/i.test(node2.attrs?.rel ?? "") && node2.attrs?.href) {
28620
+ tasks.push((async () => {
28621
+ try {
28622
+ const resource = await requestResource(ctx, new URL(node2.attrs.href, baseUrl).toString(), "text/css,*/*;q=0.1");
28623
+ node2.resourceText = new TextDecoder().decode(resource.response.body);
28624
+ node2.attrs.href = resource.url;
28625
+ } catch {
28626
+ node2.resourceText = "";
28627
+ }
28628
+ })());
28629
+ }
28630
+ if (depth < 3 && node2.type === "element" && node2.name === "iframe" && node2.attrs?.src) {
28631
+ tasks.push((async () => {
28632
+ try {
28633
+ node2.contentDocument = (await loadDocument(ctx, engine, new URL(node2.attrs.src, baseUrl).toString(), depth + 1)).document;
28634
+ } catch {
28635
+ }
28636
+ })());
28637
+ }
28638
+ for (const child of node2.children ?? []) tasks.push(loadSubresources(ctx, engine, child, baseUrl, depth));
28639
+ await Promise.all(tasks);
28640
+ }
28641
+ function browserCommands() {
28642
+ return [
28643
+ defineCommand({
28644
+ name: "chrome",
28645
+ aliases: ["chromium", "chromium-browser", "google-chrome", "chrome-headless-shell"],
28646
+ summary: "SandboxedJs virtual browser, driven over the DevTools protocol",
28647
+ usage: USAGE,
28648
+ run: run2
28649
+ }),
28650
+ search
28651
+ ];
28652
+ }
28653
+
28654
+ // src/apps/pm.ts
28655
+ init_binary();
28656
+ init_zip();
28131
28657
 
28132
28658
  // src/apps/sbjs.ts
28133
28659
  init_binary();
@@ -28315,7 +28841,7 @@ since. It does not say the program is safe to run.`,
28315
28841
  case "info":
28316
28842
  return await info(ctx, rest);
28317
28843
  case "search":
28318
- return await search(ctx, rest);
28844
+ return await search2(ctx, rest);
28319
28845
  case "available":
28320
28846
  return await available(ctx);
28321
28847
  case "trust":
@@ -28665,7 +29191,7 @@ async function info(ctx, args) {
28665
29191
  }
28666
29192
  return 0;
28667
29193
  }
28668
- async function search(ctx, args) {
29194
+ async function search2(ctx, args) {
28669
29195
  const term = (args[0] ?? "").toLowerCase();
28670
29196
  const index = await fetchIndex(ctx);
28671
29197
  const matches3 = index.apps.filter(
@@ -30216,9 +30742,9 @@ var vmModule = {
30216
30742
  measureMemory: async () => ({ total: { jsMemoryEstimate: 0, jsMemoryRange: [0, 0] } })
30217
30743
  };
30218
30744
  var vm_module_default = vmModule;
30219
- function define(proto, name, get) {
30745
+ function define(proto, name, get2) {
30220
30746
  if (Object.getOwnPropertyDescriptor(proto, name)) return;
30221
- Object.defineProperty(proto, name, { configurable: true, enumerable: false, get });
30747
+ Object.defineProperty(proto, name, { configurable: true, enumerable: false, get: get2 });
30222
30748
  }
30223
30749
  var installed = false;
30224
30750
  function installWebStreamAdapters(stream) {
@@ -32125,7 +32651,7 @@ function createHttpModule(router, owner, options = {}, defaultProtocol = "http:"
32125
32651
  if (callback) req.on("response", callback);
32126
32652
  return req;
32127
32653
  };
32128
- const get = (input, second, third) => {
32654
+ const get2 = (input, second, third) => {
32129
32655
  const req = request(input, second, third);
32130
32656
  req.end();
32131
32657
  return req;
@@ -32133,7 +32659,7 @@ function createHttpModule(router, owner, options = {}, defaultProtocol = "http:"
32133
32659
  return {
32134
32660
  createServer,
32135
32661
  request,
32136
- get,
32662
+ get: get2,
32137
32663
  Server: VirtualHttpServer,
32138
32664
  ServerResponse: VirtualServerResponse,
32139
32665
  IncomingMessage: VirtualIncomingMessage,
@@ -39396,7 +39922,7 @@ var str = (s) => {
39396
39922
  var section = (id, data) => [id, ...leb(data.length), ...data];
39397
39923
  function compileInstruction({ op, next }) {
39398
39924
  const c = [];
39399
- const get = (r) => c.push(35, r);
39925
+ const get2 = (r) => c.push(35, r);
39400
39926
  const constant = (n) => c.push(66, ...signed(BigInt.asIntN(64, n)));
39401
39927
  if (op.kind === "jump") {
39402
39928
  c.push(65, ...signed(BigInt.asIntN(32, BigInt(op.target))));
@@ -39408,13 +39934,13 @@ function compileInstruction({ op, next }) {
39408
39934
  } else {
39409
39935
  if ("dst" in op) {
39410
39936
  if (op.kind !== "mov") {
39411
- get(op.dst);
39937
+ get2(op.dst);
39412
39938
  if (!op.wide) {
39413
39939
  constant(0xffffffffn);
39414
39940
  c.push(131);
39415
39941
  }
39416
39942
  }
39417
- if (op.src !== void 0) get(op.src);
39943
+ if (op.src !== void 0) get2(op.src);
39418
39944
  else constant(op.imm);
39419
39945
  if (!op.wide) {
39420
39946
  constant(0xffffffffn);
@@ -39429,7 +39955,7 @@ function compileInstruction({ op, next }) {
39429
39955
  else {
39430
39956
  c.push(36, op.dst);
39431
39957
  if (op.kind !== "mov") {
39432
- get(op.dst);
39958
+ get2(op.dst);
39433
39959
  c.push(80, 36, 16);
39434
39960
  }
39435
39961
  }
@@ -39478,7 +40004,7 @@ async function execute2(ctx, source, translated, budget) {
39478
40004
  const image = { ...source, memory: source.memory.slice() };
39479
40005
  const globals = Array.from({ length: 17 }, (_, i) => new WebAssembly.Global({ value: i === 16 ? "i32" : "i64", mutable: true }, i === 16 ? 0 : 0n));
39480
40006
  const registers = Object.fromEntries(globals.map((g, i) => [String(i), g]));
39481
- const get = (r) => globals[r].value;
40007
+ const get2 = (r) => globals[r].value;
39482
40008
  const set = (r, n) => {
39483
40009
  globals[r].value = u64(n);
39484
40010
  };
@@ -39510,15 +40036,15 @@ async function execute2(ctx, source, translated, budget) {
39510
40036
  if (!op.condition || (op.condition === "zero" ? globals[16].value !== 0 : globals[16].value === 0)) pc = op.target;
39511
40037
  } else if ("dst" in op) {
39512
40038
  const width = op.wide ? 64 : 32;
39513
- const a = BigInt.asUintN(width, get(op.dst));
39514
- const b = BigInt.asUintN(width, op.src !== void 0 ? get(op.src) : op.imm);
40039
+ const a = BigInt.asUintN(width, get2(op.dst));
40040
+ const b = BigInt.asUintN(width, op.src !== void 0 ? get2(op.src) : op.imm);
39515
40041
  const value = BigInt.asUintN(width, op.kind === "mov" ? b : op.kind === "xor" ? a ^ b : op.kind === "add" ? a + b : a - b);
39516
40042
  if (op.kind !== "mov") globals[16].value = Number(value === 0n);
39517
40043
  if (op.kind !== "cmp") set(op.dst, value);
39518
40044
  }
39519
40045
  }
39520
40046
  if (op.kind === "syscall") {
39521
- const number = get(0), fd = get(7), address = get(6), length = get(2);
40047
+ const number = get2(0), fd = get2(7), address = get2(6), length = get2(2);
39522
40048
  set(1, BigInt(next));
39523
40049
  set(11, 2n | (globals[16].value ? 64n : 0n));
39524
40050
  if (number === 60n || number === 231n) return Number(fd & 255n);