///|
/// FFI: Evaluate JavaScript expression asynchronously (awaits Promise)
/// Returns a Promise that resolves to JSON string
extern "js" fn js_evaluate_expression_async(
expression : String,
user_activation : Bool,
ctx_id : String,
) -> @core.Any =
#| async (expression, userActivation, ctxId) => {
#| const consoleEntries = [];
#| const captureConsole = true;
#| const originalConsole = {
#| target: console,
#| log: console.log,
#| warn: console.warn,
#| error: console.error,
#| info: console.info,
#| debug: console.debug,
#| assert: console.assert,
#| table: console.table,
#| trace: console.trace,
#| time: console.time,
#| timeEnd: console.timeEnd
#| };
#|
#| const consoleArgToText = (arg) => {
#| if (typeof arg === "string") return arg;
#| if (arg === undefined) return "undefined";
#| if (typeof arg === "bigint") return `${arg}n`;
#| try {
#| const json = JSON.stringify(arg);
#| if (json !== undefined) return json;
#| } catch (_e) {}
#| return String(arg);
#| };
#|
#| const pushConsoleEntry = (level, method, args, textArgs = args) => {
#| consoleEntries.push({
#| level,
#| method,
#| text: textArgs.map(consoleArgToText).join(" "),
#| args: args.map(toBidiValue),
#| timestamp: Date.now()
#| });
#| };
#|
#| const makeInterceptor = (level, method) => (...args) => {
#| pushConsoleEntry(level, method, args);
#| };
#|
#| console.log = makeInterceptor('info', 'log');
#| console.warn = makeInterceptor('warn', 'warn');
#| console.error = makeInterceptor('error', 'error');
#| console.info = makeInterceptor('info', 'info');
#| console.debug = makeInterceptor('debug', 'debug');
#| console.table = makeInterceptor('info', 'table');
#| console.trace = makeInterceptor('debug', 'trace');
#| console.assert = (...args) => {
#| if (args.length > 0 && args[0]) {
#| return;
#| }
#| const textArgs = args.length > 1 ? args.slice(1) : ["Assertion failed"];
#| pushConsoleEntry("error", "assert", textArgs);
#| };
#| console.time = (...args) => {
#| };
#| console.timeEnd = (...args) => {
#| const textArgs = args.length > 0 ? args : ["default"];
#| pushConsoleEntry("info", "timeEnd", textArgs);
#| };
#|
#| const isActive = !!userActivation;
#| globalThis.__bidiUserActivation = isActive;
#| globalThis.__bidiCurrentContext = ctxId;
#| if (!globalThis.__bidiProxyInstances) globalThis.__bidiProxyInstances = new WeakSet();
#| if (!globalThis.__bidiNativeProxy && typeof globalThis.Proxy === "function") {
#| const NativeProxy = globalThis.Proxy;
#| const tracked = globalThis.__bidiProxyInstances;
#| function TrackedProxy(target, handler) {
#| const proxied = new NativeProxy(target, handler);
#| tracked.add(proxied);
#| return proxied;
#| }
#| TrackedProxy.revocable = function(target, handler) {
#| const revocable = NativeProxy.revocable(target, handler);
#| tracked.add(revocable.proxy);
#| return revocable;
#| };
#| globalThis.__bidiNativeProxy = NativeProxy;
#| globalThis.Proxy = TrackedProxy;
#| }
#| if (!globalThis.navigator) globalThis.navigator = {};
#| globalThis.navigator.userActivation = {
#| isActive,
#| hasBeenActive: isActive
#| };
#| if (!globalThis.__bidiContextWindows) globalThis.__bidiContextWindows = new Map();
#| if (!globalThis.__bidiContextWindows.has(ctxId)) {
#| const win = {};
#| win.__bidiContextId = String(ctxId);
#| win.window = win;
#| win.innerWidth = Number(globalThis.innerWidth || 1024);
#| win.innerHeight = Number(globalThis.innerHeight || 768);
#| win.outerWidth = Number(globalThis.outerWidth || win.innerWidth);
#| win.outerHeight = Number(globalThis.outerHeight || win.innerHeight);
#| win.devicePixelRatio = Number(globalThis.devicePixelRatio || 1);
#| win.pageXOffset = 0;
#| win.pageYOffset = 0;
#| win._listeners = {};
#| win.addEventListener = function(type, fn) {
#| if (!this._listeners[type]) this._listeners[type] = [];
#| this._listeners[type].push(fn);
#| };
#| win.removeEventListener = function(type, fn) {
#| if (!this._listeners[type]) return;
#| this._listeners[type] = this._listeners[type].filter((f) => f !== fn);
#| };
#| win.dispatchEvent = function(event) {
#| try { event.currentTarget = this; } catch (_e) {}
#| const listeners = this._listeners[event.type] || [];
#| for (const fn of listeners) {
#| try { fn.call(this, event); } catch (_e) {}
#| }
#| return !event.defaultPrevented;
#| };
#| globalThis.__bidiContextWindows.set(ctxId, win);
#| }
#| const contextWindow = globalThis.__bidiContextWindows.get(ctxId);
#| if (!contextWindow.window) contextWindow.window = contextWindow;
#| const frameWindows = Array.isArray(contextWindow.frames) ? contextWindow.frames : [];
#| globalThis.window = contextWindow;
#| globalThis.frames = frameWindows;
#| if (contextWindow.document && typeof contextWindow.document === "object") {
#| globalThis.document = contextWindow.document;
#| }
#| if (contextWindow.location && typeof contextWindow.location === "object") {
#| globalThis.location = contextWindow.location;
#| try { globalThis.__pageUrl = String(contextWindow.location.href || "about:blank"); } catch (_e) {}
#| } else {
#| const locationHref = "about:blank";
#| contextWindow.location = {
#| href: locationHref,
#| origin: '',
#| protocol: 'about:',
#| host: '',
#| hostname: '',
#| port: '',
#| pathname: 'blank',
#| search: '',
#| hash: '',
#| assign: function(url) { const next = String(url); globalThis.__pageUrl = next; this.href = next; },
#| replace: function(url) { const next = String(url); globalThis.__pageUrl = next; this.href = next; },
#| reload: function() {}
#| };
#| globalThis.location = contextWindow.location;
#| globalThis.__pageUrl = locationHref;
#| }
#| if (!globalThis.document) {
#| const body = {
#| appendChild(node) { return node; }
#| };
#| const createElement = (tagName) => {
#| const name = String(tagName || "div");
#| return {
#| tagName: name.toUpperCase(),
#| localName: name.toLowerCase(),
#| nodeType: 1,
#| namespaceURI: "http://www.w3.org/1999/xhtml",
#| _attrs: {},
#| _children: [],
#| childNodes: [],
#| shadowRoot: null,
#| setAttribute(name, value) { this._attrs[String(name)] = String(value); },
#| };
#| };
#| globalThis.document = {
#| body,
#| createElement,
#| createTextNode(text) { return { nodeType: 3, textContent: String(text) }; },
#| createDocumentFragment() {
#| return {
#| nodeType: 11,
#| nodeName: "#document-fragment",
#| _children: [],
#| childNodes: [],
#| };
#| },
#| execCommand(command) {
#| const cmd = String(command || '').toLowerCase();
#| if (cmd === 'selectall') return true;
#| if (cmd === 'copy') return !!globalThis.__bidiUserActivation;
#| return false;
#| }
#| };
#| if (!globalThis.DocumentFragment) {
#| globalThis.DocumentFragment = class DocumentFragment {
#| constructor() {
#| return globalThis.document.createDocumentFragment();
#| }
#| };
#| }
#| }
#| if (globalThis.document && !globalThis.window.document) {
#| contextWindow.document = globalThis.document;
#| }
#| if (globalThis.document) {
#| globalThis.document.execCommand = function(command) {
#| const cmd = String(command || '').toLowerCase();
#| if (cmd === 'selectall') return true;
#| if (cmd === 'copy') return !!globalThis.__bidiUserActivation;
#| return false;
#| };
#| }
#| if (globalThis.document && typeof globalThis.__bidiEnsureDocumentRange === "function") {
#| globalThis.__bidiEnsureDocumentRange(globalThis.document);
#| contextWindow.document = globalThis.document;
#| }
#|
#| function getNodeSharedId(node) {
#| const sharedIds = globalThis.__bidiNodeSharedIds || (globalThis.__bidiNodeSharedIds = new WeakMap());
#| const nodeStore = globalThis.__bidiSharedNodeStore || (globalThis.__bidiSharedNodeStore = new Map());
#| if (typeof globalThis.__bidiNextNodeSharedId !== "number") {
#| globalThis.__bidiNextNodeSharedId = 1;
#| }
#| let sharedId = sharedIds.get(node);
#| if (!sharedId) {
#| sharedId = `node-${globalThis.__bidiNextNodeSharedId++}`;
#| sharedIds.set(node, sharedId);
#| }
#| nodeStore.set(sharedId, node);
#| return sharedId;
#| }
#|
#| function getNodeAttributes(node) {
#| const attrs = {};
#| if (!node || typeof node !== "object") return attrs;
#| if (node._attrs && typeof node._attrs === "object") {
#| for (const [k, v] of Object.entries(node._attrs)) attrs[k] = String(v);
#| return attrs;
#| }
#| if (node.attributes && typeof node.attributes.length === "number") {
#| for (const attr of Array.from(node.attributes)) {
#| if (attr && typeof attr.name === "string") attrs[attr.name] = String(attr.value ?? "");
#| }
#| }
#| return attrs;
#| }
#|
#| function toBidiNodeValue(node) {
#| const childNodes = Array.isArray(node._children)
#| ? node._children
#| : (Array.isArray(node.childNodes) ? node.childNodes : []);
#| const nodeType = typeof node.nodeType === "number" ? node.nodeType : 1;
#| const localName = node.localName || node._tagName || (node.tagName ? String(node.tagName).toLowerCase() : "");
#| const namespaceURI = node.namespaceURI || "http://www.w3.org/1999/xhtml";
#| const shadowRoot = node.shadowRoot
#| ? { type: "node", sharedId: getNodeSharedId(node.shadowRoot) }
#| : null;
#| return {
#| type: "node",
#| sharedId: getNodeSharedId(node),
#| value: {
#| nodeType,
#| localName: String(localName),
#| namespaceURI: String(namespaceURI),
#| childNodeCount: childNodes.length,
#| children: [],
#| attributes: getNodeAttributes(node),
#| shadowRoot
#| }
#| };
#| }
#|
#| const internalIds = new WeakMap();
#| const referenceCounts = new WeakMap();
#| let nextInternalId = 1;
#| const trackReference = (obj) => {
#| const prev = referenceCounts.get(obj) || 0;
#| referenceCounts.set(obj, prev + 1);
#| };
#| const collectReferences = (value, seen = new WeakSet()) => {
#| if (!value || typeof value !== "object") return;
#| if (typeof value.nodeType === "number") {
#| trackReference(value);
#| return;
#| }
#| if (
#| value instanceof Date ||
#| value instanceof RegExp ||
#| value instanceof Error ||
#| value instanceof Promise ||
#| value instanceof WeakMap ||
#| value instanceof WeakSet ||
#| value instanceof ArrayBuffer ||
#| ArrayBuffer.isView(value)
#| ) {
#| return;
#| }
#| if (globalThis.__bidiProxyInstances && globalThis.__bidiProxyInstances.has(value)) return;
#| const isContainer =
#| Array.isArray(value) ||
#| value instanceof Map ||
#| value instanceof Set ||
#| value.constructor === Object;
#| if (!isContainer) return;
#| trackReference(value);
#| if (seen.has(value)) return;
#| seen.add(value);
#| if (Array.isArray(value)) {
#| for (const item of value) collectReferences(item, seen);
#| return;
#| }
#| if (value instanceof Map) {
#| for (const [k, v] of value) {
#| if (typeof k !== "string") collectReferences(k, seen);
#| collectReferences(v, seen);
#| }
#| return;
#| }
#| if (value instanceof Set) {
#| for (const item of value) collectReferences(item, seen);
#| return;
#| }
#| for (const entryValue of Object.values(value)) {
#| collectReferences(entryValue, seen);
#| }
#| };
#| const withInternalId = (originalValue, serialized) => {
#| if (!serialized || typeof serialized !== "object") return serialized;
#| if (!originalValue || (typeof originalValue !== "object" && typeof originalValue !== "function")) {
#| return serialized;
#| }
#| const serializedType = serialized.type;
#| if (serializedType !== "array" && serializedType !== "map" && serializedType !== "set" && serializedType !== "object" && serializedType !== "node") {
#| return serialized;
#| }
#| const count = referenceCounts.get(originalValue) || 0;
#| if (count <= 1) return serialized;
#| let internalId = internalIds.get(originalValue);
#| if (!internalId) {
#| internalId = `internal-${nextInternalId++}`;
#| internalIds.set(originalValue, internalId);
#| }
#| if (serialized.internalId) return serialized;
#| return { ...serialized, internalId };
#| };
#|
#| function toBidiValue(value) {
#| if (value === undefined) return { type: "undefined" };
#| if (value === null) return { type: "null" };
#| const type = typeof value;
#| if (type === "boolean") return { type: "boolean", value };
#| if (type === "number") {
#| if (Number.isNaN(value)) return { type: "number", value: "NaN" };
#| if (!Number.isFinite(value)) return { type: "number", value: value > 0 ? "Infinity" : "-Infinity" };
#| if (Object.is(value, -0)) return { type: "number", value: "-0" };
#| return { type: "number", value };
#| }
#| if (type === "string") return { type: "string", value };
#| if (type === "bigint") return { type: "bigint", value: String(value) };
#| if (type === "symbol") return { type: "symbol" };
#| if (type === "function") return { type: "function" };
#| const objectTag = type === "object" ? Object.prototype.toString.call(value) : "";
#| if (objectTag === "[object Generator]" || objectTag === "[object AsyncGenerator]") {
#| return { type: "generator" };
#| }
#| if (globalThis.__bidiProxyInstances && globalThis.__bidiProxyInstances.has(value)) {
#| return { type: "proxy" };
#| }
#| if (value && type === "object" && typeof value.__bidiContextId === "string") {
#| return { type: "window", value: { context: String(value.__bidiContextId) } };
#| }
#| if (value === globalThis || value === globalThis.window) {
#| return {
#| type: "window",
#| value: { context: String(globalThis.__bidiCurrentContext || "default-context") }
#| };
#| }
#| if (value && type === "object" && typeof value.nodeType === "number") {
#| return withInternalId(value, toBidiNodeValue(value));
#| }
#| if (value instanceof RegExp) return { type: "regexp", value: { pattern: value.source, flags: value.flags } };
#| if (value instanceof Date) return { type: "date", value: value.toISOString() };
#| if (value instanceof Error) return { type: "error" };
#| if (value instanceof Promise) return { type: "promise" };
#| if (value instanceof Map) {
#| const entries = [];
#| for (const [k, v] of value) entries.push([typeof k === "string" ? k : toBidiValue(k), toBidiValue(v)]);
#| return withInternalId(value, { type: "map", value: entries });
#| }
#| if (value instanceof Set) {
#| return withInternalId(
#| value,
#| { type: "set", value: Array.from(value).map(toBidiValue) },
#| );
#| }
#| if (value instanceof WeakMap) return { type: "weakmap" };
#| if (value instanceof WeakSet) return { type: "weakset" };
#| if (ArrayBuffer.isView(value)) return { type: "typedarray" };
#| if (value instanceof ArrayBuffer) return { type: "arraybuffer" };
#| if (Array.isArray(value)) {
#| return withInternalId(
#| value,
#| { type: "array", value: value.map(toBidiValue) },
#| );
#| }
#| if (value.constructor === Object) {
#| const entries = Object.entries(value).map(([k, v]) => [k, toBidiValue(v)]);
#| return withInternalId(value, { type: "object", value: entries });
#| }
#| return { type: "object" };
#| }
#|
#| try {
#| if (typeof globalThis.__bidiSyncWindowPropertiesToGlobal === "function") {
#| globalThis.__bidiSyncWindowPropertiesToGlobal(globalThis.window);
#| }
#| let result = (0, eval)(expression);
#| if (typeof globalThis.__bidiSyncWindowPropertiesToGlobal === "function") {
#| globalThis.__bidiSyncWindowPropertiesToGlobal(globalThis.window);
#| }
#| // Await if result is a Promise
#| if (result instanceof Promise) {
#| result = await result;
#| }
#| collectReferences(result);
#| return JSON.stringify({ type: "success", result: toBidiValue(result), consoleEntries });
#| } catch (e) {
#| return JSON.stringify({
#| type: "exception",
#| exceptionDetails: {
#| columnNumber: 0,
#| lineNumber: 0,
#| text: String(e),
#| exception: toBidiValue(e),
#| stackTrace: { callFrames: [] }
#| },
#| consoleEntries
#| });
#| } finally {
#| if (captureConsole && originalConsole) {
#| const restoreConsole = originalConsole.target || console;
#| restoreConsole.log = originalConsole.log;
#| restoreConsole.warn = originalConsole.warn;
#| restoreConsole.error = originalConsole.error;
#| restoreConsole.info = originalConsole.info;
#| restoreConsole.debug = originalConsole.debug;
#| restoreConsole.assert = originalConsole.assert;
#| restoreConsole.table = originalConsole.table;
#| restoreConsole.trace = originalConsole.trace;
#| restoreConsole.time = originalConsole.time;
#| restoreConsole.timeEnd = originalConsole.timeEnd;
#| }
#| }
#| }
///|
/// FFI: Await a JavaScript Promise and return the result as string
extern "js" fn js_await_promise(promise : @core.Any) -> String =
#| async (promise) => await promise
///|
/// Evaluate JavaScript expression asynchronously (awaits Promise if awaitPromise=true)
/// Returns a Promise (@core.Any) that resolves to JSON string
pub fn evaluate_js_async(expression : String) -> @core.Any {
js_evaluate_expression_async(expression, false, "")
}
///|
/// FFI: Evaluate expression asynchronously and send result via WebSocket
/// This runs the evaluation, awaits any Promise result, and sends response directly
extern "js" fn js_eval_and_send_async(
socket : @core.Any,
request_id : Int,
expression : String,
ctx_id : String,
realm_id : String,
capture_console : Bool,
user_activation : Bool,
root_ownership : Bool,
unwrap_result : Bool,
) -> Unit =
#| async (socket, requestId, expression, ctxId, realmId, captureConsole, userActivation, rootOwnership, unwrapResult) => {
#| const consoleEntries = [];
#| let originalConsole = null;
#| if (captureConsole) {
#| originalConsole = {
#| target: console,
#| log: console.log, warn: console.warn, error: console.error,
#| info: console.info, debug: console.debug,
#| assert: console.assert, table: console.table, trace: console.trace,
#| time: console.time, timeEnd: console.timeEnd
#| };
#|
#| const consoleArgToText = (arg) => {
#| if (typeof arg === "string") return arg;
#| if (arg === undefined) return "undefined";
#| if (typeof arg === "bigint") return `${arg}n`;
#| try {
#| const json = JSON.stringify(arg);
#| if (json !== undefined) return json;
#| } catch (_e) {}
#| return String(arg);
#| };
#|
#| const pushConsoleEntry = (level, method, args, textArgs = args) => {
#| consoleEntries.push({
#| level, method,
#| text: textArgs.map(consoleArgToText).join(" "),
#| args: args.map(toBidiValue),
#| timestamp: Date.now()
#| });
#| };
#|
#| const makeInterceptor = (level, method) => (...args) => {
#| pushConsoleEntry(level, method, args);
#| };
#|
#| console.log = makeInterceptor('info', 'log');
#| console.warn = makeInterceptor('warn', 'warn');
#| console.error = makeInterceptor('error', 'error');
#| console.info = makeInterceptor('info', 'info');
#| console.debug = makeInterceptor('debug', 'debug');
#| console.table = makeInterceptor('info', 'table');
#| console.trace = makeInterceptor('debug', 'trace');
#| console.assert = (...args) => {
#| if (args.length > 0 && args[0]) {
#| return;
#| }
#| const textArgs = args.length > 1 ? args.slice(1) : ["Assertion failed"];
#| pushConsoleEntry("error", "assert", textArgs);
#| };
#| console.time = (...args) => {
#| };
#| console.timeEnd = (...args) => {
#| const textArgs = args.length > 0 ? args : ["default"];
#| pushConsoleEntry("info", "timeEnd", textArgs);
#| };
#| }
#|
#| const isActive = !!userActivation;
#| globalThis.__bidiUserActivation = isActive;
#| globalThis.__bidiCurrentContext = ctxId;
#| if (!globalThis.__bidiProxyInstances) globalThis.__bidiProxyInstances = new WeakSet();
#| if (!globalThis.__bidiNativeProxy && typeof globalThis.Proxy === "function") {
#| const NativeProxy = globalThis.Proxy;
#| const tracked = globalThis.__bidiProxyInstances;
#| function TrackedProxy(target, handler) {
#| const proxied = new NativeProxy(target, handler);
#| tracked.add(proxied);
#| return proxied;
#| }
#| TrackedProxy.revocable = function(target, handler) {
#| const revocable = NativeProxy.revocable(target, handler);
#| tracked.add(revocable.proxy);
#| return revocable;
#| };
#| globalThis.__bidiNativeProxy = NativeProxy;
#| globalThis.Proxy = TrackedProxy;
#| }
#| if (!globalThis.navigator) globalThis.navigator = {};
#| globalThis.navigator.userActivation = {
#| isActive,
#| hasBeenActive: isActive
#| };
#| if (!globalThis.__bidiContextWindows) globalThis.__bidiContextWindows = new Map();
#| if (!globalThis.__bidiContextWindows.has(ctxId)) {
#| const win = {};
#| win.__bidiContextId = String(ctxId);
#| win.window = win;
#| win.innerWidth = Number(globalThis.innerWidth || 1024);
#| win.innerHeight = Number(globalThis.innerHeight || 768);
#| win.outerWidth = Number(globalThis.outerWidth || win.innerWidth);
#| win.outerHeight = Number(globalThis.outerHeight || win.innerHeight);
#| win.devicePixelRatio = Number(globalThis.devicePixelRatio || 1);
#| win.pageXOffset = 0;
#| win.pageYOffset = 0;
#| win._listeners = {};
#| win.addEventListener = function(type, fn) {
#| if (!this._listeners[type]) this._listeners[type] = [];
#| this._listeners[type].push(fn);
#| };
#| win.removeEventListener = function(type, fn) {
#| if (!this._listeners[type]) return;
#| this._listeners[type] = this._listeners[type].filter((f) => f !== fn);
#| };
#| win.dispatchEvent = function(event) {
#| try { event.currentTarget = this; } catch (_e) {}
#| const listeners = this._listeners[event.type] || [];
#| for (const fn of listeners) {
#| try { fn.call(this, event); } catch (_e) {}
#| }
#| return !event.defaultPrevented;
#| };
#| globalThis.__bidiContextWindows.set(ctxId, win);
#| }
#| const contextWindow = globalThis.__bidiContextWindows.get(ctxId);
#| if (!contextWindow.window) contextWindow.window = contextWindow;
#| const frameWindows = Array.isArray(contextWindow.frames) ? contextWindow.frames : [];
#| globalThis.window = contextWindow;
#| globalThis.frames = frameWindows;
#| if (contextWindow.document && typeof contextWindow.document === "object") {
#| globalThis.document = contextWindow.document;
#| }
#| if (contextWindow.location && typeof contextWindow.location === "object") {
#| globalThis.location = contextWindow.location;
#| try { globalThis.__pageUrl = String(contextWindow.location.href || "about:blank"); } catch (_e) {}
#| } else {
#| const locationHref = "about:blank";
#| contextWindow.location = {
#| href: locationHref,
#| origin: '',
#| protocol: 'about:',
#| host: '',
#| hostname: '',
#| port: '',
#| pathname: 'blank',
#| search: '',
#| hash: '',
#| assign: function(url) { const next = String(url); globalThis.__pageUrl = next; this.href = next; },
#| replace: function(url) { const next = String(url); globalThis.__pageUrl = next; this.href = next; },
#| reload: function() {}
#| };
#| globalThis.location = contextWindow.location;
#| globalThis.__pageUrl = locationHref;
#| }
#| if (!globalThis.document) {
#| const body = {
#| appendChild(node) { return node; }
#| };
#| const createElement = (tagName) => {
#| const name = String(tagName || "div");
#| return {
#| tagName: name.toUpperCase(),
#| localName: name.toLowerCase(),
#| nodeType: 1,
#| namespaceURI: "http://www.w3.org/1999/xhtml",
#| _attrs: {},
#| _children: [],
#| childNodes: [],
#| shadowRoot: null,
#| setAttribute(name, value) { this._attrs[String(name)] = String(value); },
#| };
#| };
#| globalThis.document = {
#| body,
#| createElement,
#| createTextNode(text) { return { nodeType: 3, textContent: String(text) }; },
#| createDocumentFragment() {
#| return {
#| nodeType: 11,
#| nodeName: "#document-fragment",
#| _children: [],
#| childNodes: [],
#| };
#| },
#| execCommand(command) {
#| const cmd = String(command || '').toLowerCase();
#| if (cmd === 'selectall') return true;
#| if (cmd === 'copy') return !!globalThis.__bidiUserActivation;
#| return false;
#| }
#| };
#| if (!globalThis.DocumentFragment) {
#| globalThis.DocumentFragment = class DocumentFragment {
#| constructor() {
#| return globalThis.document.createDocumentFragment();
#| }
#| };
#| }
#| }
#| if (globalThis.document) {
#| globalThis.document.execCommand = function(command) {
#| const cmd = String(command || '').toLowerCase();
#| if (cmd === 'selectall') return true;
#| if (cmd === 'copy') return !!globalThis.__bidiUserActivation;
#| return false;
#| };
#| }
#| if (globalThis.document && !contextWindow.document) {
#| contextWindow.document = globalThis.document;
#| }
#| if (globalThis.document && typeof globalThis.__bidiEnsureDocumentRange === "function") {
#| globalThis.__bidiEnsureDocumentRange(globalThis.document);
#| contextWindow.document = globalThis.document;
#| }
#|
#| function getNodeSharedId(node) {
#| const sharedIds = globalThis.__bidiNodeSharedIds || (globalThis.__bidiNodeSharedIds = new WeakMap());
#| const nodeStore = globalThis.__bidiSharedNodeStore || (globalThis.__bidiSharedNodeStore = new Map());
#| if (typeof globalThis.__bidiNextNodeSharedId !== "number") {
#| globalThis.__bidiNextNodeSharedId = 1;
#| }
#| let sharedId = sharedIds.get(node);
#| if (!sharedId) {
#| sharedId = `node-${globalThis.__bidiNextNodeSharedId++}`;
#| sharedIds.set(node, sharedId);
#| }
#| nodeStore.set(sharedId, node);
#| return sharedId;
#| }
#|
#| function getNodeAttributes(node) {
#| const attrs = {};
#| if (!node || typeof node !== "object") return attrs;
#| if (node._attrs && typeof node._attrs === "object") {
#| for (const [k, v] of Object.entries(node._attrs)) attrs[k] = String(v);
#| return attrs;
#| }
#| if (node.attributes && typeof node.attributes.length === "number") {
#| for (const attr of Array.from(node.attributes)) {
#| if (attr && typeof attr.name === "string") attrs[attr.name] = String(attr.value ?? "");
#| }
#| }
#| return attrs;
#| }
#|
#| function toBidiNodeValue(node) {
#| const childNodes = Array.isArray(node._children)
#| ? node._children
#| : (Array.isArray(node.childNodes) ? node.childNodes : []);
#| const nodeType = typeof node.nodeType === "number" ? node.nodeType : 1;
#| const localName = node.localName || node._tagName || (node.tagName ? String(node.tagName).toLowerCase() : "");
#| const namespaceURI = node.namespaceURI || "http://www.w3.org/1999/xhtml";
#| const shadowRoot = node.shadowRoot
#| ? { type: "node", sharedId: getNodeSharedId(node.shadowRoot) }
#| : null;
#| return {
#| type: "node",
#| sharedId: getNodeSharedId(node),
#| value: {
#| nodeType,
#| localName: String(localName),
#| namespaceURI: String(namespaceURI),
#| childNodeCount: childNodes.length,
#| children: [],
#| attributes: getNodeAttributes(node),
#| shadowRoot
#| }
#| };
#| }
#|
#| const internalIds = new WeakMap();
#| const referenceCounts = new WeakMap();
#| let nextInternalId = 1;
#| const trackReference = (obj) => {
#| const prev = referenceCounts.get(obj) || 0;
#| referenceCounts.set(obj, prev + 1);
#| };
#| const collectReferences = (value, seen = new WeakSet()) => {
#| if (!value || typeof value !== "object") return;
#| if (typeof value.nodeType === "number") {
#| trackReference(value);
#| return;
#| }
#| if (
#| value instanceof Date ||
#| value instanceof RegExp ||
#| value instanceof Error ||
#| value instanceof Promise ||
#| value instanceof WeakMap ||
#| value instanceof WeakSet ||
#| value instanceof ArrayBuffer ||
#| ArrayBuffer.isView(value)
#| ) {
#| return;
#| }
#| if (globalThis.__bidiProxyInstances && globalThis.__bidiProxyInstances.has(value)) return;
#| const isContainer =
#| Array.isArray(value) ||
#| value instanceof Map ||
#| value instanceof Set ||
#| value.constructor === Object;
#| if (!isContainer) return;
#| trackReference(value);
#| if (seen.has(value)) return;
#| seen.add(value);
#| if (Array.isArray(value)) {
#| for (const item of value) collectReferences(item, seen);
#| return;
#| }
#| if (value instanceof Map) {
#| for (const [k, v] of value) {
#| if (typeof k !== "string") collectReferences(k, seen);
#| collectReferences(v, seen);
#| }
#| return;
#| }
#| if (value instanceof Set) {
#| for (const item of value) collectReferences(item, seen);
#| return;
#| }
#| for (const entryValue of Object.values(value)) {
#| collectReferences(entryValue, seen);
#| }
#| };
#| const withInternalId = (originalValue, serialized) => {
#| if (!serialized || typeof serialized !== "object") return serialized;
#| if (!originalValue || (typeof originalValue !== "object" && typeof originalValue !== "function")) {
#| return serialized;
#| }
#| const serializedType = serialized.type;
#| if (serializedType !== "array" && serializedType !== "map" && serializedType !== "set" && serializedType !== "object" && serializedType !== "node") {
#| return serialized;
#| }
#| const count = referenceCounts.get(originalValue) || 0;
#| if (count <= 1) return serialized;
#| let internalId = internalIds.get(originalValue);
#| if (!internalId) {
#| internalId = `internal-${nextInternalId++}`;
#| internalIds.set(originalValue, internalId);
#| }
#| if (serialized.internalId) return serialized;
#| return { ...serialized, internalId };
#| };
#|
#| function toBidiValue(value) {
#| if (value === undefined) return { type: "undefined" };
#| if (value === null) return { type: "null" };
#| const type = typeof value;
#| if (type === "boolean") return { type: "boolean", value };
#| if (type === "number") {
#| if (Number.isNaN(value)) return { type: "number", value: "NaN" };
#| if (!Number.isFinite(value)) return { type: "number", value: value > 0 ? "Infinity" : "-Infinity" };
#| if (Object.is(value, -0)) return { type: "number", value: "-0" };
#| return { type: "number", value };
#| }
#| if (type === "string") return { type: "string", value };
#| if (type === "bigint") return { type: "bigint", value: String(value) };
#| if (type === "symbol") return { type: "symbol" };
#| if (type === "function") return { type: "function" };
#| const objectTag = type === "object" ? Object.prototype.toString.call(value) : "";
#| if (objectTag === "[object Generator]" || objectTag === "[object AsyncGenerator]") {
#| return { type: "generator" };
#| }
#| if (globalThis.__bidiProxyInstances && globalThis.__bidiProxyInstances.has(value)) {
#| return { type: "proxy" };
#| }
#| if (value && type === "object" && typeof value.__bidiContextId === "string") {
#| return { type: "window", value: { context: String(value.__bidiContextId) } };
#| }
#| if (value === globalThis || value === globalThis.window) {
#| return {
#| type: "window",
#| value: { context: String(globalThis.__bidiCurrentContext || "default-context") }
#| };
#| }
#| if (value && type === "object" && typeof value.nodeType === "number") {
#| return withInternalId(value, toBidiNodeValue(value));
#| }
#| if (value instanceof RegExp) return { type: "regexp", value: { pattern: value.source, flags: value.flags } };
#| if (value instanceof Date) return { type: "date", value: value.toISOString() };
#| if (value instanceof Error) return { type: "error" };
#| if (value instanceof Promise) return { type: "promise" };
#| if (value instanceof Map) {
#| const entries = [];
#| for (const [k, v] of value) entries.push([typeof k === "string" ? k : toBidiValue(k), toBidiValue(v)]);
#| return withInternalId(value, { type: "map", value: entries });
#| }
#| if (value instanceof Set) {
#| return withInternalId(
#| value,
#| { type: "set", value: Array.from(value).map(toBidiValue) },
#| );
#| }
#| if (value instanceof WeakMap) return { type: "weakmap" };
#| if (value instanceof WeakSet) return { type: "weakset" };
#| if (ArrayBuffer.isView(value)) return { type: "typedarray" };
#| if (value instanceof ArrayBuffer) return { type: "arraybuffer" };
#| if (Array.isArray(value)) {
#| return withInternalId(
#| value,
#| { type: "array", value: value.map(toBidiValue) },
#| );
#| }
#| if (value.constructor === Object) {
#| const entries = Object.entries(value).map(([k, v]) => [k, toBidiValue(v)]);
#| return withInternalId(value, { type: "object", value: entries });
#| }
#| return { type: "object" };
#| }
#| if (!globalThis.__bidiHandleStore) globalThis.__bidiHandleStore = new Map();
#| if (!globalThis.__bidiHandleContextStore) globalThis.__bidiHandleContextStore = new Map();
#| if (typeof globalThis.__bidiNextHandleId !== "number") {
#| globalThis.__bidiNextHandleId = 1;
#| }
#| const attachHandle = (value, serialized, includeHandle) => {
#| if (!includeHandle) return serialized;
#| if (value === null || value === undefined) return serialized;
#| const valueType = typeof value;
#| if (valueType !== "object" && valueType !== "function" && valueType !== "symbol") return serialized;
#| if (!serialized || typeof serialized !== "object" || serialized.handle) return serialized;
#| const handle = `handle-${globalThis.__bidiNextHandleId++}`;
#| globalThis.__bidiHandleStore.set(handle, value);
#| globalThis.__bidiHandleContextStore.set(
#| handle,
#| String(globalThis.__bidiCurrentContext || ""),
#| );
#| return { ...serialized, handle };
#| };
#| const toBidiValueFn = (value, includeHandle = false) =>
#| attachHandle(value, toBidiValue(value), includeHandle);
#|
#| let response;
#| try {
#| const runInContext = (code) => {
#| // Use indirect eval so script.evaluate keeps script-style sloppy mode
#| // unless the expression explicitly opts into strict mode.
#| return (0, eval)(code);
#| };
#| if (typeof globalThis.__bidiSyncWindowPropertiesToGlobal === "function") {
#| globalThis.__bidiSyncWindowPropertiesToGlobal(globalThis.window);
#| }
#| let result = runInContext(expression);
#| if (typeof globalThis.__bidiSyncWindowPropertiesToGlobal === "function") {
#| globalThis.__bidiSyncWindowPropertiesToGlobal(globalThis.window);
#| }
#| // Await if result is a Promise
#| if (result instanceof Promise) {
#| result = await result;
#| }
#| collectReferences(result);
#| response = {
#| id: requestId,
#| type: "success",
#| result: unwrapResult
#| ? toBidiValueFn(result, !!rootOwnership)
#| : {
#| realm: realmId,
#| result: toBidiValueFn(result, !!rootOwnership)
#| }
#| };
#| } catch (e) {
#| const exceptionResult = {
#| realm: realmId,
#| exceptionDetails: {
#| columnNumber: 0,
#| lineNumber: 0,
#| text: String(e),
#| exception: toBidiValueFn(e, !!rootOwnership),
#| stackTrace: { callFrames: [] }
#| }
#| };
#| response = {
#| id: requestId,
#| type: "success",
#| result: exceptionResult
#| };
#| } finally {
#| if (captureConsole && originalConsole) {
#| const restoreConsole = originalConsole.target || console;
#| restoreConsole.log = originalConsole.log;
#| restoreConsole.warn = originalConsole.warn;
#| restoreConsole.error = originalConsole.error;
#| restoreConsole.info = originalConsole.info;
#| restoreConsole.debug = originalConsole.debug;
#| restoreConsole.assert = originalConsole.assert;
#| restoreConsole.table = originalConsole.table;
#| restoreConsole.trace = originalConsole.trace;
#| restoreConsole.time = originalConsole.time;
#| restoreConsole.timeEnd = originalConsole.timeEnd;
#| }
#| }
#|
#| // Send console log events before response
#| for (const entry of consoleEntries) {
#| const logEvent = {
#| type: "event",
#| method: "log.entryAdded",
#| params: {
#| level: entry.level,
#| source: { realm: realmId, context: ctxId },
#| text: entry.text,
#| timestamp: entry.timestamp,
#| method: entry.method,
#| args: entry.args || [],
#| type: "console"
#| }
#| };
#| socket.send(JSON.stringify(logEvent));
#| }
#|
#| // Send the response
#| socket.send(JSON.stringify(response));
#| }
///|
/// Evaluate expression asynchronously and send result via WebSocket
fn eval_and_send_async_with_console(
socket : @core.Any,
request_id : Int,
expression : String,
ctx_id : String,
realm_id : String,
capture_console : Bool,
user_activation : Bool,
root_ownership : Bool,
unwrap_result : Bool,
) -> Unit {
js_eval_and_send_async(
socket, request_id, expression, ctx_id, realm_id, capture_console, user_activation,
root_ownership, unwrap_result,
)
}
///|
/// Evaluate expression asynchronously and send result via WebSocket
pub fn eval_and_send_async(
socket : @core.Any,
request_id : Int,
expression : String,
ctx_id : String,
) -> Unit {
eval_and_send_async_with_console(
socket, request_id, expression, ctx_id, "default-realm", true, false, false,
false,
)
}
///|
/// Await a Promise and return result as String
pub fn await_promise(promise : @core.Any) -> String {
js_await_promise(promise)
}