/* * el_runtime.js — El language JS runtime. * * The browser/Node analog of el_runtime.c. Compiled-from-El JS source * imports this file once; it side-effects globalThis.__el with every * builtin, so generated programs can destructure the names they need * (see codegen-js.el's preamble). * * Value model: * El's tagged el_val_t collapses into JS native types here: * String -> string * Int -> number (caveat: only 53 bits of integer precision) * Float -> number (already a double) * Bool -> boolean * [T] -> Array * Map<,> -> plain object * Void -> undefined * null -> null * * Runtime mode auto-detection: * typeof window === 'undefined' -> Node mode * otherwise -> Browser mode * * See spec/codegen-js.md for the full design rationale. */ const IS_NODE = typeof window === 'undefined' && typeof process !== 'undefined' && process.versions != null && process.versions.node != null; // ── I/O ───────────────────────────────────────────────────────────────────── function println(s) { if (IS_NODE) { process.stdout.write(String(s) + '\n'); } else { console.log(String(s)); } } function print(s) { if (IS_NODE) { process.stdout.write(String(s)); } else { // Browser has no stdout — fall back to console with no newline group console.log(String(s)); } } // ── String builtins ───────────────────────────────────────────────────────── // Coerce both args to string and concat. Mirrors el_str_concat in C; // the C version handles both string-and-string and string-and-int. function el_str_concat(a, b) { return String(a) + String(b); } function str_concat(a, b) { return el_str_concat(a, b); } // Strict equality with string coercion. Matches str_eq() in C — which // strcmp's the underlying char*. Here we just === after coercion. function str_eq(a, b) { if (a === null || b === null) return a === b; return String(a) === String(b); } function str_starts_with(s, prefix) { return String(s).startsWith(String(prefix)); } function str_ends_with(s, suffix) { return String(s).endsWith(String(suffix)); } function str_len(s) { return String(s).length; } function int_to_str(n) { return String(n); } function str_to_int(s) { const n = parseInt(String(s), 10); return Number.isNaN(n) ? 0 : n; } function str_slice(s, start, end) { return String(s).slice(start, end); } function str_contains(s, sub) { return String(s).indexOf(String(sub)) >= 0; } function str_replace(s, from, to) { // Replace ALL occurrences (matches C runtime semantics). return String(s).split(String(from)).join(String(to)); } function str_to_upper(s) { return String(s).toUpperCase(); } function str_to_lower(s) { return String(s).toLowerCase(); } function str_upper(s) { return String(s).toUpperCase(); } function str_lower(s) { return String(s).toLowerCase(); } function str_trim(s) { return String(s).trim(); } function str_index_of(s, sub) { return String(s).indexOf(String(sub)); } function str_split(s, sep) { return String(s).split(String(sep)); } function str_char_at(s, i) { return String(s).charAt(i); } function str_char_code(s, i) { const c = String(s).charCodeAt(i); return Number.isNaN(c) ? 0 : c; } function str_pad_left(s, width, pad) { return String(s).padStart(width, String(pad)); } function str_pad_right(s, width, pad) { return String(s).padEnd(width, String(pad)); } // ── Math ──────────────────────────────────────────────────────────────────── function el_abs(n) { return Math.abs(n); } function el_max(a, b) { return a > b ? a : b; } function el_min(a, b) { return a < b ? a : b; } // ── Refcount (no-op — JS has GC) ──────────────────────────────────────────── function el_retain(_v) { /* no-op */ } function el_release(_v) { /* no-op */ } // ── List ──────────────────────────────────────────────────────────────────── // Variadic constructor matching el_list_new(count, items...). Exposed so // codegen-js can emit the same call shape if we ever want it (currently // codegen-js emits JS array literals directly). function el_list_new(_count, ...items) { return items.slice(0); } function el_list_empty() { return []; } function el_list_clone(list) { return Array.isArray(list) ? list.slice() : []; } function el_list_len(list) { return Array.isArray(list) ? list.length : 0; } function el_list_get(list, index) { if (!Array.isArray(list)) return null; if (index < 0 || index >= list.length) return null; return list[index]; } function el_list_append(list, elem) { if (!Array.isArray(list)) return [elem]; const out = list.slice(); out.push(elem); return out; } function list_push(list, elem) { return el_list_append(list, elem); } function list_push_front(list, elem) { if (!Array.isArray(list)) return [elem]; return [elem, ...list]; } function list_join(list, sep) { if (!Array.isArray(list)) return ''; return list.map(String).join(String(sep)); } function list_range(start, end) { const out = []; for (let i = start; i < end; i++) out.push(i); return out; } // ── Map ───────────────────────────────────────────────────────────────────── // Variadic constructor (key, val, key, val, ...). function el_map_new(_pairCount, ...kvs) { const out = {}; for (let i = 0; i < kvs.length; i += 2) { out[String(kvs[i])] = kvs[i + 1]; } return out; } function el_get_field(map, key) { if (map === null || map === undefined) return null; if (typeof map !== 'object') return null; const k = String(key); if (Object.prototype.hasOwnProperty.call(map, k)) return map[k]; return null; } function el_map_get(map, key) { return el_get_field(map, key); } function el_map_set(map, key, value) { // Match the C runtime: shallow-copy + set, persistent semantics. const out = (map && typeof map === 'object') ? { ...map } : {}; out[String(key)] = value; return out; } // ── Method-call shorthand aliases ────────────────────────────────────────── // `obj.method(args)` compiles to `method(obj, args)` per El convention. function append(list, elem) { return el_list_append(list, elem); } function len(v) { if (Array.isArray(v)) return v.length; if (typeof v === 'string') return v.length; if (v && typeof v === 'object') return Object.keys(v).length; return 0; } function get(list, index) { return el_list_get(list, index); } function map_get(m, k) { return el_get_field(m, k); } function map_set(m, k, v) { return el_map_set(m, k, v); } // ── Native VM aliases ────────────────────────────────────────────────────── function native_list_get(list, index) { return el_list_get(list, index); } function native_list_len(list) { return el_list_len(list); } function native_list_append(list, elem) { return el_list_append(list, elem); } function native_list_empty() { return []; } function native_list_clone(list) { return el_list_clone(list); } function native_string_chars(s) { return String(s).split(''); } function native_int_to_str(n) { return String(n); } // ── HTTP ─────────────────────────────────────────────────────────────────── // // fetch() is async. These return Promise. Generated El code does // not yet emit await — that's the async-taint pass (see spec §5). For // programs that don't touch HTTP this is fine; for programs that do, // the value will appear as "[object Promise]" until the taint pass lands. function http_get(url) { if (typeof fetch === 'undefined') { throw new Error('http_get: fetch() not available in this runtime'); } return fetch(String(url)).then(r => r.text()); } function http_post(url, body) { if (typeof fetch === 'undefined') { throw new Error('http_post: fetch() not available in this runtime'); } return fetch(String(url), { method: 'POST', body: String(body) }).then(r => r.text()); } function http_post_json(url, jsonBody) { if (typeof fetch === 'undefined') { throw new Error('http_post_json: fetch() not available in this runtime'); } return fetch(String(url), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: String(jsonBody), }).then(r => r.text()); } function http_get_with_headers(url, headersMap) { if (typeof fetch === 'undefined') { throw new Error('http_get_with_headers: fetch() not available'); } return fetch(String(url), { headers: headersMap || {} }).then(r => r.text()); } function http_post_with_headers(url, body, headersMap) { if (typeof fetch === 'undefined') { throw new Error('http_post_with_headers: fetch() not available'); } return fetch(String(url), { method: 'POST', headers: headersMap || {}, body: String(body), }).then(r => r.text()); } function http_serve(_port, _handler) { throw new Error('http_serve: not supported in JS target — needs server-side runtime mode'); } function http_set_handler(_name) { throw new Error('http_set_handler: not supported in JS target'); } // ── Filesystem (Node-only) ───────────────────────────────────────────────── function _ensureNode(name) { if (!IS_NODE) { throw new Error(`${name}: not supported in browser runtime`); } } function fs_read(path) { _ensureNode('fs_read'); const fs = require('node:fs'); try { return fs.readFileSync(String(path), 'utf8'); } catch (_e) { return ''; } } function fs_write(path, content) { _ensureNode('fs_write'); const fs = require('node:fs'); try { fs.writeFileSync(String(path), String(content)); return true; } catch (_e) { return false; } } function fs_list(path) { _ensureNode('fs_list'); const fs = require('node:fs'); try { return fs.readdirSync(String(path)); } catch (_e) { return []; } } // ── JSON ─────────────────────────────────────────────────────────────────── function json_parse(s) { try { return JSON.parse(String(s)); } catch (_e) { return null; } } function json_stringify(v) { try { return JSON.stringify(v); } catch (_e) { return ''; } } function json_get(jsonStr, key) { const o = json_parse(jsonStr); if (o === null) return null; return el_get_field(o, key); } function json_get_string(jsonStr, key) { const v = json_get(jsonStr, key); return v === null ? '' : String(v); } function json_get_int(jsonStr, key) { const v = json_get(jsonStr, key); if (typeof v === 'number') return Math.trunc(v); if (typeof v === 'string') return str_to_int(v); return 0; } function json_get_float(jsonStr, key) { const v = json_get(jsonStr, key); return typeof v === 'number' ? v : 0; } function json_get_bool(jsonStr, key) { const v = json_get(jsonStr, key); return v === true; } function json_get_raw(jsonStr, key) { const v = json_get(jsonStr, key); return v === null ? '' : json_stringify(v); } function json_set(jsonStr, key, value) { const o = json_parse(jsonStr) ?? {}; o[String(key)] = value; return json_stringify(o); } function json_array_len(jsonStr) { const o = json_parse(jsonStr); return Array.isArray(o) ? o.length : 0; } // ── Time ─────────────────────────────────────────────────────────────────── function time_now() { return Math.floor(Date.now() / 1000); } function time_now_utc() { // In the C runtime this returns nanoseconds since epoch. JS number // can't represent that range past ~2^53. We return milliseconds — a // safe range — and document the divergence. return Date.now(); } function sleep_secs(secs) { if (!IS_NODE) { throw new Error('sleep_secs: blocking sleep not supported in browser'); } // Simple sync sleep via Atomics.wait on a SharedArrayBuffer-backed Int32. const sab = new SharedArrayBuffer(4); const i32 = new Int32Array(sab); Atomics.wait(i32, 0, 0, Math.floor(secs * 1000)); return secs; } function sleep_ms(ms) { if (!IS_NODE) { throw new Error('sleep_ms: blocking sleep not supported in browser'); } const sab = new SharedArrayBuffer(4); const i32 = new Int32Array(sab); Atomics.wait(i32, 0, 0, Math.floor(ms)); return ms; } // ── Bool ─────────────────────────────────────────────────────────────────── function bool_to_str(b) { return b ? 'true' : 'false'; } // ── Process ──────────────────────────────────────────────────────────────── function exit_program(code) { if (IS_NODE) { process.exit(code | 0); } else { throw new Error(`exit_program(${code}) called in browser`); } } // ── args() ───────────────────────────────────────────────────────────────── function args() { if (IS_NODE) { // process.argv is [node, script, ...args] — slice off node + script. return process.argv.slice(2); } return []; } // ── env ──────────────────────────────────────────────────────────────────── function env(key) { if (IS_NODE) { const v = process.env[String(key)]; return v === undefined ? null : v; } return null; } // ── In-process state K/V ─────────────────────────────────────────────────── const _stateMap = new Map(); function state_set(key, value) { _stateMap.set(String(key), value); return value; } function state_get(key) { const v = _stateMap.get(String(key)); return v === undefined ? '' : v; } function state_del(key) { return _stateMap.delete(String(key)); } function state_keys() { return Array.from(_stateMap.keys()); } // ── UUID ─────────────────────────────────────────────────────────────────── function uuid_v4() { // RFC 4122-ish — uses crypto when available, falls back to Math.random. if (typeof crypto !== 'undefined' && crypto.randomUUID) { return crypto.randomUUID(); } return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } function uuid_new() { return uuid_v4(); } // ── Float formatting ─────────────────────────────────────────────────────── function float_to_str(f) { return String(f); } function int_to_float(n) { return n; } function float_to_int(f) { return Math.trunc(f); } function format_float(f, decimals) { return Number(f).toFixed(decimals); } function decimal_round(f, decimals) { const m = Math.pow(10, decimals); return Math.round(f * m) / m; } function str_to_float(s) { const n = parseFloat(String(s)); return Number.isNaN(n) ? 0 : n; } // ── Math (Float-aware) ───────────────────────────────────────────────────── function math_sqrt(f) { return Math.sqrt(f); } function math_log(f) { return Math.log10(f); } function math_ln(f) { return Math.log(f); } function math_sin(f) { return Math.sin(f); } function math_cos(f) { return Math.cos(f); } function math_pi() { return Math.PI; } // ── DOM bridge (browser-only) ────────────────────────────────────────────── // // These functions wrap the browser DOM API. Each throws a descriptive error // when called from a Node environment, mirroring the pattern used by fs_* // in browser mode. function _ensureBrowser(name) { if (IS_NODE) { throw new Error(`${name}: not supported in Node runtime — DOM is browser-only`); } } function dom_get_element(id) { _ensureBrowser('dom_get_element'); return document.getElementById(String(id)); } function dom_get_value(el) { _ensureBrowser('dom_get_value'); return el == null ? '' : String(el.value ?? ''); } function dom_set_value(el, v) { _ensureBrowser('dom_set_value'); if (el != null) el.value = String(v); } function dom_get_text(el) { _ensureBrowser('dom_get_text'); return el == null ? '' : String(el.textContent ?? ''); } function dom_set_text(el, text) { _ensureBrowser('dom_set_text'); if (el != null) el.textContent = String(text); } function dom_set_prop(el, prop, val) { _ensureBrowser('dom_set_prop'); if (el != null) el[String(prop)] = val; } function dom_get_prop(el, prop) { _ensureBrowser('dom_get_prop'); if (el == null) return null; const v = el[String(prop)]; return v === undefined ? null : v; } function dom_set_style(el, prop, val) { _ensureBrowser('dom_set_style'); if (el != null) el.style[String(prop)] = String(val); } function dom_add_class(el, cls) { _ensureBrowser('dom_add_class'); if (el != null) el.classList.add(String(cls)); } function dom_remove_class(el, cls) { _ensureBrowser('dom_remove_class'); if (el != null) el.classList.remove(String(cls)); } function dom_show(el) { _ensureBrowser('dom_show'); if (el != null) el.style.display = ''; } function dom_hide(el) { _ensureBrowser('dom_hide'); if (el != null) el.style.display = 'none'; } function dom_listen(el, event, handler) { _ensureBrowser('dom_listen'); if (el != null) el.addEventListener(String(event), handler); } function dom_query(selector) { _ensureBrowser('dom_query'); return document.querySelector(String(selector)); } function dom_query_all(selector) { _ensureBrowser('dom_query_all'); return Array.from(document.querySelectorAll(String(selector))); } function dom_create(tag) { _ensureBrowser('dom_create'); return document.createElement(String(tag)); } function dom_append(parent, child) { _ensureBrowser('dom_append'); if (parent != null && child != null) parent.appendChild(child); } function dom_remove(el) { _ensureBrowser('dom_remove'); if (el != null) el.remove(); } function dom_is_null(el) { return el === null || el === undefined; } // ── Extended DOM API (browser-only) ─────────────────────────────────────── function dom_set_attr(el, attr, val) { _ensureBrowser('dom_set_attr'); if (el != null) el.setAttribute(String(attr), String(val)); } function dom_get_attr(el, attr) { _ensureBrowser('dom_get_attr'); if (el == null) return ''; return el.getAttribute(String(attr)) ?? ''; } function dom_remove_attr(el, attr) { _ensureBrowser('dom_remove_attr'); if (el != null) el.removeAttribute(String(attr)); } function dom_set_html(el, html) { _ensureBrowser('dom_set_html'); if (el != null) el.innerHTML = String(html); } function dom_get_html(el) { _ensureBrowser('dom_get_html'); return el == null ? '' : String(el.innerHTML ?? ''); } function dom_get_parent(el) { _ensureBrowser('dom_get_parent'); return el == null ? null : (el.parentElement ?? null); } function dom_contains_class(el, cls) { _ensureBrowser('dom_contains_class'); if (el == null) return false; return el.classList.contains(String(cls)); } function dom_get_checked(el) { _ensureBrowser('dom_get_checked'); return el == null ? false : Boolean(el.checked); } function dom_set_checked(el, val) { _ensureBrowser('dom_set_checked'); if (el != null) el.checked = Boolean(val); } // ── Timer API (browser + Node) ───────────────────────────────────────────── function set_timeout(ms, cb) { if (typeof setTimeout === 'undefined') { throw new Error('set_timeout: setTimeout not available in this environment'); } setTimeout(cb, ms | 0); } function set_interval(ms, cb) { if (typeof setInterval === 'undefined') { throw new Error('set_interval: setInterval not available in this environment'); } return setInterval(cb, ms | 0); } function clear_interval(handle) { if (typeof clearInterval !== 'undefined') clearInterval(handle); } // ── Local storage (browser-only) ─────────────────────────────────────────── function local_storage_get(key) { _ensureBrowser('local_storage_get'); return localStorage.getItem(String(key)) ?? ''; } function local_storage_set(key, val) { _ensureBrowser('local_storage_set'); localStorage.setItem(String(key), String(val)); } function local_storage_remove(key) { _ensureBrowser('local_storage_remove'); localStorage.removeItem(String(key)); } // ── Window location / navigation (browser-only) ──────────────────────────── function window_location() { _ensureBrowser('window_location'); return window.location.href; } function window_redirect(url) { _ensureBrowser('window_redirect'); window.location.href = String(url); } function window_on_load(cb) { if (typeof document !== 'undefined') { document.addEventListener('DOMContentLoaded', cb); } else if (typeof window !== 'undefined') { window.addEventListener('load', cb); } // In Node: no-op } // ── console_log (explicit debug log, distinct from println) ──────────────── function console_log(msg) { // eslint-disable-next-line no-console console.log(String(msg)); } // ── Window export helpers ────────────────────────────────────────────────── // // Expose El functions to the browser's global scope so they can be called // from inline event handlers (onclick="increment()") or by external JS. // In Node mode, writes to globalThis so the same pattern works in tests. function window_set(name, val) { if (typeof window !== 'undefined') { window[String(name)] = val; } else if (typeof globalThis !== 'undefined') { globalThis[String(name)] = val; } } function window_get(name) { if (typeof window !== 'undefined') { const v = window[String(name)]; return v === undefined ? null : v; } return null; } // ── Promise helpers ──────────────────────────────────────────────────────── // // Third-party APIs often return Promises but are not El @async functions. // These helpers let El programs chain .then / .catch without needing // native_js, and without requiring the callee to be @async. function promise_then(p, cb) { return Promise.resolve(p).then(cb); } function promise_catch(p, cb) { return Promise.resolve(p).catch(cb); } function promise_resolve(val) { return Promise.resolve(val); } function promise_reject(msg) { return Promise.reject(new Error(String(msg))); } // ── Object / Array utilities ─────────────────────────────────────────────── // // Structural operations on Any-typed JS values. These complement the // El map/list primitives for interop with third-party library objects. function object_assign(target, source) { return Object.assign(Object.assign({}, target), source); } function object_keys(obj) { if (obj === null || obj === undefined) return []; return Object.keys(obj); } function object_values(obj) { if (obj === null || obj === undefined) return []; return Object.values(obj); } function json_deep_clone(obj) { if (obj === null || obj === undefined) return null; return JSON.parse(JSON.stringify(obj)); } function array_from(iterable) { if (iterable === null || iterable === undefined) return []; return Array.from(iterable); } function type_of(val) { return typeof val; } function instanceof_check(val, constructor_name) { if (typeof globalThis[constructor_name] === 'function') { return val instanceof globalThis[constructor_name]; } return false; } // ── native_js escape hatch ───────────────────────────────────────────────── // // Evaluate arbitrary JS from El source. Intended for calling third-party // browser libraries (Supabase, Stripe, etc.) until proper El bindings exist. // Use sparingly — this bypasses El's type system entirely. function native_js(code) { // eslint-disable-next-line no-eval return eval(String(code)); } function native_js_call(obj, method, args) { if (obj == null) throw new Error('native_js_call: object is null'); return obj[String(method)](...(Array.isArray(args) ? args : [])); } // ── Stubs for not-yet-supported features ─────────────────────────────────── // // These compile but throw when called. See spec/codegen-js.md §7. function _notSupported(name) { return () => { throw new Error(`${name}: not supported in JS target — needs server-side delegation`); }; } // CGI identity function el_cgi_init(_name, _did, _principal, _network, _engram) { // No-op — UI code is not a CGI principal. See spec §7. } // DHARMA — all stubbed. const dharma_connect = _notSupported('dharma_connect'); const dharma_send = _notSupported('dharma_send'); const dharma_activate = _notSupported('dharma_activate'); const dharma_emit = _notSupported('dharma_emit'); const dharma_field = _notSupported('dharma_field'); const dharma_strengthen = _notSupported('dharma_strengthen'); const dharma_relationship = _notSupported('dharma_relationship'); const dharma_peers = _notSupported('dharma_peers'); // Engram — stubbed (could be ported to in-browser later). const engram_node = _notSupported('engram_node'); const engram_node_full = _notSupported('engram_node_full'); const engram_get_node = _notSupported('engram_get_node'); const engram_strengthen = _notSupported('engram_strengthen'); const engram_forget = _notSupported('engram_forget'); const engram_node_count = _notSupported('engram_node_count'); const engram_search = _notSupported('engram_search'); const engram_scan_nodes = _notSupported('engram_scan_nodes'); const engram_connect = _notSupported('engram_connect'); const engram_edge_between = _notSupported('engram_edge_between'); const engram_neighbors = _notSupported('engram_neighbors'); const engram_neighbors_filtered = _notSupported('engram_neighbors_filtered'); const engram_edge_count = _notSupported('engram_edge_count'); const engram_activate = _notSupported('engram_activate'); const engram_save = _notSupported('engram_save'); const engram_load = _notSupported('engram_load'); // LLM — stubbed (browser cannot hold API keys safely). const llm_call = _notSupported('llm_call'); const llm_call_system = _notSupported('llm_call_system'); const llm_call_agentic = _notSupported('llm_call_agentic'); const llm_vision = _notSupported('llm_vision'); const llm_models = _notSupported('llm_models'); const llm_register_tool = _notSupported('llm_register_tool'); // Crypto — stubbed; could be backed by SubtleCrypto later. const sha256_hex = _notSupported('sha256_hex'); const sha256_bytes = _notSupported('sha256_bytes'); const hmac_sha256_hex = _notSupported('hmac_sha256_hex'); const hmac_sha256_bytes = _notSupported('hmac_sha256_bytes'); const base64_encode = _notSupported('base64_encode'); const base64_decode = _notSupported('base64_decode'); const base64url_encode = _notSupported('base64url_encode'); const base64url_decode = _notSupported('base64url_decode'); // ── Export to globalThis.__el ────────────────────────────────────────────── // // Generated programs destructure off this object. Keeping it on globalThis // means a single `import "./el_runtime.js"` is enough; no per-call namespace // prefix is required at codegen time. const __el = { // I/O println, print, // String el_str_concat, str_concat, str_eq, str_starts_with, str_ends_with, str_len, int_to_str, str_to_int, str_slice, str_contains, str_replace, str_to_upper, str_to_lower, str_trim, str_index_of, str_split, str_char_at, str_char_code, str_lower, str_upper, str_pad_left, str_pad_right, // Math el_abs, el_max, el_min, // Refcount el_retain, el_release, // List el_list_new, el_list_empty, el_list_clone, el_list_len, el_list_get, el_list_append, list_push, list_push_front, list_join, list_range, // Map el_map_new, el_get_field, el_map_get, el_map_set, // Method-call shortforms append, len, get, map_get, map_set, // Native VM aliases native_list_get, native_list_len, native_list_append, native_list_empty, native_list_clone, native_string_chars, native_int_to_str, // HTTP http_get, http_post, http_post_json, http_get_with_headers, http_post_with_headers, http_serve, http_set_handler, // FS fs_read, fs_write, fs_list, // JSON json_parse, json_stringify, json_get, json_get_string, json_get_int, json_get_float, json_get_bool, json_get_raw, json_set, json_array_len, // Time time_now, time_now_utc, sleep_secs, sleep_ms, // Bool bool_to_str, // Process exit_program, // Args / env args, env, // State state_set, state_get, state_del, state_keys, // UUID uuid_v4, uuid_new, // Float / math float_to_str, int_to_float, float_to_int, format_float, decimal_round, str_to_float, math_sqrt, math_log, math_ln, math_sin, math_cos, math_pi, // DOM bridge (browser-only) dom_get_element, dom_get_value, dom_set_value, dom_get_text, dom_set_text, dom_set_prop, dom_get_prop, dom_set_style, dom_add_class, dom_remove_class, dom_show, dom_hide, dom_listen, dom_query, dom_query_all, dom_create, dom_append, dom_remove, dom_is_null, // Extended DOM dom_set_attr, dom_get_attr, dom_remove_attr, dom_set_html, dom_get_html, dom_get_parent, dom_contains_class, dom_get_checked, dom_set_checked, // Timers set_timeout, set_interval, clear_interval, // Local storage local_storage_get, local_storage_set, local_storage_remove, // Window location window_location, window_redirect, window_on_load, // Debug console_log, // Window export helpers window_set, window_get, // Promise helpers promise_then, promise_catch, promise_resolve, promise_reject, // Object / Array utilities object_assign, object_keys, object_values, json_deep_clone, array_from, type_of, instanceof_check, // native_js escape hatch native_js, native_js_call, // CGI / DHARMA / Engram / LLM (stubs) el_cgi_init, dharma_connect, dharma_send, dharma_activate, dharma_emit, dharma_field, dharma_strengthen, dharma_relationship, dharma_peers, engram_node, engram_node_full, engram_get_node, engram_strengthen, engram_forget, engram_node_count, engram_search, engram_scan_nodes, engram_connect, engram_edge_between, engram_neighbors, engram_neighbors_filtered, engram_edge_count, engram_activate, engram_save, engram_load, llm_call, llm_call_system, llm_call_agentic, llm_vision, llm_models, llm_register_tool, // Crypto (stubs) sha256_hex, sha256_bytes, hmac_sha256_hex, hmac_sha256_bytes, base64_encode, base64_decode, base64url_encode, base64url_decode, }; globalThis.__el = __el; // Also re-export as ES module exports for consumers that prefer that style. export { __el as default }; export { println, print, el_str_concat, str_concat, str_eq, str_starts_with, str_ends_with, str_len, int_to_str, str_to_int, str_slice, str_contains, str_replace, str_to_upper, str_to_lower, str_trim, str_index_of, str_split, str_char_at, str_char_code, str_lower, str_upper, el_abs, el_max, el_min, el_retain, el_release, el_list_new, el_list_empty, el_list_clone, el_list_len, el_list_get, el_list_append, list_push, list_push_front, list_join, list_range, el_map_new, el_get_field, el_map_get, el_map_set, append, len, get, map_get, map_set, native_list_get, native_list_len, native_list_append, native_list_empty, native_list_clone, native_string_chars, native_int_to_str, http_get, http_post, http_post_json, fs_read, fs_write, fs_list, json_parse, json_stringify, json_get, json_get_string, json_get_int, time_now, time_now_utc, sleep_ms, bool_to_str, exit_program, args, env, state_set, state_get, state_del, state_keys, el_cgi_init, dharma_connect, dharma_send, dharma_activate, dharma_emit, dharma_field, engram_node, engram_search, engram_activate, llm_call, llm_call_system, // DOM bridge dom_get_element, dom_get_value, dom_set_value, dom_get_text, dom_set_text, dom_set_prop, dom_get_prop, dom_set_style, dom_add_class, dom_remove_class, dom_show, dom_hide, dom_listen, dom_query, dom_query_all, dom_create, dom_append, dom_remove, dom_is_null, // Extended DOM dom_set_attr, dom_get_attr, dom_remove_attr, dom_set_html, dom_get_html, dom_get_parent, dom_contains_class, dom_get_checked, dom_set_checked, // Timers set_timeout, set_interval, clear_interval, // Local storage local_storage_get, local_storage_set, local_storage_remove, // Window location window_location, window_redirect, window_on_load, // Debug console_log, // Window / native_js window_set, window_get, native_js, native_js_call, // Promise helpers promise_then, promise_catch, promise_resolve, promise_reject, // Object / Array utilities object_assign, object_keys, object_values, json_deep_clone, array_from, type_of, instanceof_check, };