nodeCompatPrelude top-level constant

String const nodeCompatPrelude

The compat surface, as one JS bootstrap evaluated by installNodeCompat.

Tier-2 stubs are call-time errors and typeof-safe: typeof Buffer answers 'function' (so feature-guard idioms keep working) while an actual call throws the documented alternative.

Implementation

const String nodeCompatPrelude = r'''
(function () {
    var cfg = globalThis.__ncConfig ||
        { env: {}, platform: 'linux', arch: 'x64', nodeVersion: 'v22.0.0-compat' };

    function safeStringify(v) {
        try { return JSON.stringify(v); } catch (e) { return String(v); }
    }
    function inspectValue(v, depth) {
        if (v === null) return 'null';
        if (v === undefined) return 'undefined';
        var t = typeof v;
        if (t === 'string') return "'" + v.replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'";
        if (t === 'number' || t === 'boolean' || t === 'bigint') return String(v);
        if (t === 'function') return '[Function: ' + (v.name || 'anonymous') + ']';
        if (Array.isArray(v)) {
            if ((depth || 0) > 2) return '[Array]';
            return '[ ' + v.map(function (x) { return inspectValue(x, (depth || 0) + 1); }).join(', ') + ' ]';
        }
        if (v instanceof Error) return v.name + ': ' + v.message;
        if (v instanceof Uint8Array) {
            return 'Uint8Array(' + v.length + ') ' + safeStringify(Array.prototype.slice.call(v, 0, 32));
        }
        if (t === 'object') {
            if ((depth || 0) > 2) return '[Object]';
            var keys = Object.keys(v);
            var body = keys.slice(0, 32).map(function (k) {
                return k + ': ' + inspectValue(v[k], (depth || 0) + 1);
            });
            if (keys.length > 32) body.push('...');
            return '{ ' + body.join(', ') + ' }';
        }
        return String(v);
    }

    globalThis.__ncUnsupported = unsupported;
    function unsupported(name, alternative) {
        var fn = function () {
            throw new Error(name + ' is not available in quickjs_runtime: ' +
                alternative);
        };
        return fn;
    }

    // ── console ──
    function fmtArg(a) {
        if (typeof a === 'string') return a;
        return inspectValue(a);
    }
    function consoleWrite(level, args) {
        var parts = [];
        for (var i = 0; i < args.length; i++) parts.push(fmtArg(args[i]));
        __ncConsoleWrite(level, __ncIndent + parts.join(' '));
    }
    var timers = {};
    var counters = {};
    var groupDepth = 0;
    Object.defineProperty(globalThis, '__ncIndent', {
        get: function () { return '  '.repeat(groupDepth); },
        configurable: true
    });
    var consoleObj = {};
    ['log', 'info', 'warn', 'error', 'debug'].forEach(function (level) {
        consoleObj[level] = function () { consoleWrite(level, arguments); };
    });
    consoleObj.trace = function () {
        var args = ['Trace'];
        for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
        consoleWrite('error', args);
    };
    consoleObj.dir = function (obj) {
        __ncConsoleWrite('log', __ncIndent + inspectValue(obj));
    };
    consoleObj.time = function (label) {
        timers[label === undefined ? 'default' : String(label)] = __ncNow();
    };
    consoleObj.timeLog = function (label) {
        var key = label === undefined ? 'default' : String(label);
        var start = timers[key];
        if (start === undefined) {
            consoleWrite('warn', ['Timer \'' + key + '\' does not exist']);
            return;
        }
        var rest = [];
        for (var i = 1; i < arguments.length; i++) rest.push(arguments[i]);
        consoleWrite('log', [key + ': ' + (__ncNow() - start) + 'ms'].concat(rest));
    };
    consoleObj.timeEnd = function (label) {
        var key = label === undefined ? 'default' : String(label);
        var start = timers[key];
        if (start === undefined) {
            consoleWrite('warn', ['Timer \'' + key + '\' does not exist']);
            return;
        }
        delete timers[key];
        consoleWrite('log', [key + ': ' + (__ncNow() - start) + 'ms']);
    };
    consoleObj.count = function (label) {
        var key = label === undefined ? 'default' : String(label);
        counters[key] = (counters[key] || 0) + 1;
        consoleWrite('log', [key + ': ' + counters[key]]);
    };
    consoleObj.countReset = function (label) {
        var key = label === undefined ? 'default' : String(label);
        delete counters[key];
    };
    consoleObj.group = function () {
        if (arguments.length) consoleWrite('log', arguments);
        groupDepth++;
    };
    consoleObj.groupCollapsed = consoleObj.group;
    consoleObj.groupEnd = function () {
        if (groupDepth > 0) groupDepth--;
    };
    consoleObj.table = function (data) {
        __ncConsoleWrite('log', __ncIndent + renderTable(data));
    };
    function padCell(v, width) {
        var s = String(v);
        var out = s;
        for (var i = s.length; i < width; i++) out += ' ';
        return out;
    }
    function renderTable(data) {
        var rows;
        var isArr = Array.isArray(data);
        if (isArr) {
            rows = data.map(function (v, i) { return [String(i), v]; });
        } else if (data && typeof data === 'object') {
            rows = Object.keys(data).map(function (k) { return [k, data[k]]; });
        } else {
            return safeStringify(data);
        }
        var cols = [];
        var headerSet = {};
        rows.forEach(function (r) {
            var v = r[1];
            if (v && typeof v === 'object' && !(v instanceof Date)) {
                Object.keys(v).forEach(function (k) {
                    if (!headerSet[k]) { headerSet[k] = true; cols.push(k); }
                });
            }
        });
        var header = isArr ? ['(iteration index)'] : ['(index)'];
        header = header.concat(cols.length ? cols : ['Values']);
        var lines = [];
        var widths = header.map(function (h) { return String(h).length; });
        var table = rows.map(function (r) {
            var v = r[1];
            if (v && typeof v === 'object') {
                return [r[0]].concat(cols.map(function (c) {
                    return c in v ? safeStringify(v[c]) : '';
                }));
            }
            return [r[0]].concat(cols.length ? [] : [safeStringify(v)]);
        });
        [header].concat(table).forEach(function (row) {
            row.forEach(function (cell, i) {
                if (String(cell).length > widths[i]) widths[i] = String(cell).length;
            });
        });
        function renderRow(row) {
            return '\u2502 ' + row.map(function (c, i) {
                return padCell(String(c), widths[i]);
            }).join(' \u2502 ') + ' \u2502';
        }
        var sep = '\u250c' + widths.map(function (w) {
            var d = '';
            for (var i = 0; i < w + 2; i++) d += '\u2500';
            return d;
        }).join('\u252c') + '\u2510';
        var sepMid = '\u251c' + widths.map(function (w) {
            var d = '';
            for (var i = 0; i < w + 2; i++) d += '\u2500';
            return d;
        }).join('\u253c') + '\u2524';
        var sepEnd = '\u2514' + widths.map(function (w) {
            var d = '';
            for (var i = 0; i < w + 2; i++) d += '\u2500';
            return d;
        }).join('\u2534') + '\u2518';
        lines.push(sep, renderRow(header), sepMid);
        table.forEach(function (row, idx) {
            lines.push(renderRow(row));
            if (idx < table.length - 1) lines.push(sepMid);
        });
        lines.push(sepEnd);
        return lines.join('\n');
    }
    globalThis.console = consoleObj;

    // ── global ──
    globalThis.global = globalThis;

    // ── process ──
    var envObj = {};
    Object.keys(cfg.env || {}).forEach(function (k) {
        envObj[k] = String(cfg.env[k]);
    });
    function cwdSafe() {
        try { return __ncCwd(); } catch (e) { return '/'; }
    }
    var exitListeners = [];
    var argvList = (cfg.argv && cfg.argv.length) ? cfg.argv.slice()
        : ['<runtime>', cfg.scriptPath || '<main>'];
    var clockZero = null;
    function monoMs() {
        var now = __ncNow();
        if (clockZero === null) clockZero = now;
        return now - clockZero;
    }
    globalThis.process = {
        env: envObj,
        platform: cfg.platform,
        arch: cfg.arch,
        version: cfg.nodeVersion,
        exitCode: 0,
        pid: cfg.pid || 1,
        execPath: argvList[0] || '<runtime>',
        argv: argvList,
        cwd: cwdSafe,
        hrtime: function hrtime(previous) {
            var ms = monoMs();
            var secs = Math.floor(ms / 1000);
            var nanos = Math.round((ms - secs * 1000) * 1e6);
            if (Array.isArray(previous)) {
                var dSecs = secs - previous[0];
                var dNanos = nanos - previous[1];
                if (dNanos < 0) { dSecs -= 1; dNanos += 1e9; }
                return [dSecs, dNanos];
            }
            return [secs, nanos];
        },
        uptime: function () { return monoMs() / 1000; },
        memoryUsage: function () {
            return { rss: 0, heapTotal: 0, heapUsed: 0, external: 0,
                arrayBuffers: 0 };
        },
        on: function (event, listener) {
            if (event === 'exit') exitListeners.push(listener);
            return globalThis.process;
        },
        addListener: function (event, listener) {
            return globalThis.process.on(event, listener);
        },
        stdout: {
            isTTY: false,
            write: function (s) {
                __ncConsoleWrite('log', __ncIndent + String(s));
                return true;
            }
        },
        stderr: {
            isTTY: false,
            write: function (s) {
                __ncConsoleWrite('error', __ncIndent + String(s));
                return true;
            }
        },
        stdin: { readable: false, on: function () { return this; } },
        cwd: cwdSafe,
        exit: function (code) {
            try { __ncExit(code || 0); } catch (e) { /* host hook only */ }
            for (var i = 0; i < exitListeners.length; i++) {
                try { exitListeners[i](code || 0); } catch (e2) { /* stay sync */ }
            }
            throw new Error('ProcessExit: ' + (code || 0));
        },
        // Deviation vs Node: mapped onto the microtask queue, so
        // nextTick callbacks interleave in promise order instead of
        // running before all promise reactions.
        nextTick: function (fn) {
            if (typeof fn !== 'function') {
                throw new TypeError('process.nextTick callback must be a function');
            }
            Promise.resolve().then(fn);
        }
    };
    process.hrtime.bigint = function () {
        var ms = monoMs();
        return BigInt(Math.round(ms * 1e6));
    };
    globalThis.__ncSetScriptPath = function (p) {
        argvList[1] = p;
        var idx = String(p).lastIndexOf('/');
        globalThis.__filename = p;
        globalThis.__dirname = idx < 0 ? '.' : (idx === 0 ? '/' : String(p).slice(0, idx));
    };
    if (cfg.scriptPath) {
        globalThis.__ncSetScriptPath(cfg.scriptPath);
    } else {
        globalThis.__filename = '[eval]';
        globalThis.__dirname = cwdSafe();
    }

    // ── performance ──
    globalThis.performance = {
        timeOrigin: 0,
        now: function () { return __ncNow(); }
    };

    // ── base64 ──
    globalThis.btoa = function (text) {
        return __ncBase64Encode(String(text));
    };
    globalThis.atob = function (text) {
        return __ncBase64Decode(String(text));
    };

    // ── TextEncoder / TextDecoder (utf-8 via host hooks) ──
    function TextEncoder() {}
    TextEncoder.prototype.encoding = 'utf-8';
    TextEncoder.prototype.encode = function (text) {
        var bytes = __ncUtf8Encode(text === undefined ? '' : String(text));
        return Uint8Array.from(bytes);
    };
    TextEncoder.prototype.encodeInto = unsupported('TextEncoder.encodeInto',
        'use encode() instead');
    globalThis.TextEncoder = TextEncoder;

    function TextDecoder() {}
    TextDecoder.prototype.encoding = 'utf-8';
    TextDecoder.prototype.decode = function (bytes) {
        var arr = [];
        if (bytes) {
            for (var i = 0; i < bytes.length; i++) arr.push(bytes[i] & 0xff);
        }
        return __ncUtf8Decode(arr);
    };
    globalThis.TextDecoder = TextDecoder;

    // ── crypto (subset) ──
    globalThis.crypto = {
        getRandomValues: function (array) {
            var bytes = __ncRandomValues(array ? array.length : 0);
            for (var i = 0; i < array.length; i++) {
                array[i] = bytes[i % bytes.length];
            }
            return array;
        },
        randomUUID: function () { return __ncRandomUuid(); }
    };

    // ── structuredClone (JSON fidelity, documented) ──
    globalThis.structuredClone = function (value) {
        return JSON.parse(JSON.stringify(value));
    };

    // ── path (posix subset) ──
    function isAbsolute(p) {
        return typeof p === 'string' && p.charAt(0) === '/';
    }
    function normalize(p) {
        var abs = isAbsolute(p);
        var parts = String(p).split('/');
        var out = [];
        for (var i = 0; i < parts.length; i++) {
            var part = parts[i];
            if (part === '' || part === '.') continue;
            if (part === '..') {
                if (out.length && out[out.length - 1] !== '..') out.pop();
                else if (!abs) out.push('..');
                continue;
            }
            out.push(part);
        }
        var joined = out.join('/');
        return (abs ? '/' : '') + (joined || (abs ? '' : '.'));
    }
    function join() {
        var parts = [];
        for (var i = 0; i < arguments.length; i++) {
            if (typeof arguments[i] === 'string' && arguments[i] !== '') {
                parts.push(arguments[i]);
            }
        }
        return normalize(parts.join('/'));
    }
    function resolve() {
        var parts = [];
        for (var i = arguments.length - 1; i >= 0; i--) {
            var a = arguments[i];
            if (typeof a === 'string' && a !== '') {
                parts.unshift(a);
                if (isAbsolute(a)) break;
            }
        }
        if (!parts.length || !isAbsolute(parts[0])) parts.unshift(cwdSafe());
        return normalize(parts.join('/'));
    }
    function basename(p, ext) {
        var b = String(p).split('/').pop() || '';
        if (ext && b.slice(-ext.length) === ext && b !== ext) {
            b = b.slice(0, b.length - ext.length);
        }
        return b;
    }
    function dirname(p) {
        var s = String(p);
        var idx = s.lastIndexOf('/');
        if (idx < 0) return '.';
        if (idx === 0) return '/';
        return s.slice(0, idx) || '/';
    }
    function extname(p) {
        var b = basename(p);
        var idx = b.lastIndexOf('.');
        return idx <= 0 ? '' : b.slice(idx);
    }
    function relative(from, to) {
        from = resolve(from);
        to = resolve(to);
        if (from === to) return '';
        var f = from.split('/').filter(Boolean);
        var t = to.split('/').filter(Boolean);
        var i = 0;
        while (i < f.length && i < t.length && f[i] === t[i]) i++;
        var out = [];
        for (var u = 0; u < f.length - i; u++) out.push('..');
        for (var d = i; d < t.length; d++) out.push(t[d]);
        return out.join('/') || '.';
    }
    var path = {
        sep: '/',
        delimiter: ':',
        posix: null,
        win32: unsupported('path.win32', 'posix only in this runtime'),
        isAbsolute: isAbsolute,
        normalize: normalize,
        join: join,
        resolve: resolve,
        basename: basename,
        dirname: dirname,
        extname: extname,
        relative: relative
    };
    path.posix = path;
    globalThis.path = path;

    // ── assert (node-like subset) ──
    function fail(message) {
        var err = new Error(message);
        err.name = 'AssertionError';
        return err;
    }
    function assert(value, message) {
        if (!value) {
            throw fail(message ||
                'The expression evaluated to a falsy value.');
        }
    }
    assert.ok = assert;
    assert.equal = function (a, b, m) {
        if (a != b) {
            throw fail(m || 'Expected ' + safeStringify(a) +
                ' == ' + safeStringify(b));
        }
    };
    assert.notEqual = function (a, b, m) {
        if (a == b) {
            throw fail(m || 'Expected ' + safeStringify(a) +
                ' != ' + safeStringify(b));
        }
    };
    assert.deepEqual = function (a, b, m) {
        if (safeStringify(a) !== safeStringify(b)) {
            throw fail(m || 'Expected ' + safeStringify(a) +
                ' to deeply equal ' + safeStringify(b));
        }
    };
    assert.notDeepEqual = function (a, b, m) {
        if (safeStringify(a) === safeStringify(b)) {
            throw fail(m || 'Expected different deep values');
        }
    };
    assert.throws = function (fn, expected, m) {
        try {
            fn();
        } catch (e) {
            if (expected instanceof RegExp) {
                if (!expected.test(e.message)) {
                    throw fail(m || 'Expected error message to match ' +
                        expected + ', got: ' + e.message);
                }
            }
            return e;
        }
        throw fail(m || 'Expected the function to throw');
    };
    assert.doesNotThrow = function (fn, m) {
        try {
            fn();
        } catch (e) {
            throw fail(m || 'Expected the function not to throw, got: ' +
                e.message);
        }
    };
    assert.match = function (str, re, m) {
        if (!re.test(String(str))) {
            throw fail(m || 'Expected ' + safeStringify(String(str)) +
                ' to match ' + String(re));
        }
    };
    assert.fail = function (m) { throw fail(m || 'assert.fail()'); };
    assert.strict = assert;
    globalThis.assert = assert;

    // ── util (subset) ──
    function format() {
        var args = Array.prototype.slice.call(arguments);
        var out = String(args.shift() || '').replace(/%[sdjf%]/g, function (spec) {
            if (spec === '%%') return '%';
            if (!args.length) return spec;
            var v = args.shift();
            if (spec === '%s') return String(v);
            if (spec === '%d') return String(parseInt(v, 10));
            return safeStringify(v);
        });
        if (args.length) {
            // Node: extra string args are inserted raw; the rest via util.inspect.
            out += ' ' + args.map(function (v) {
                return typeof v === 'string' ? v : inspectValue(v, 0);
            }).join(' ');
        }
        return out;
    }
    globalThis.util = {
        inspect: function (v) { return inspectValue(v, 0); },
        format: format,
        isArray: Array.isArray,
        isString: function (v) { return typeof v === 'string'; },
        isNumber: function (v) { return typeof v === 'number'; },
        isBoolean: function (v) { return typeof v === 'boolean'; },
        isNull: function (v) { return v === null; },
        isUndefined: function (v) { return v === undefined; },
        isFunction: function (v) { return typeof v === 'function'; },
        types: {
            isPromise: function (v) { return v instanceof Promise; }
        },
        // Node-shaped promisify: last-argument (err, value) callback →
        // Promise. Real since the microtask queue is drained by the host.
        promisify: function (fn) {
            if (typeof fn !== 'function') {
                throw new TypeError(
                    'util.promisify argument must be a function');
            }
            if (fn.__ncPromisified) return fn;
            var wrapped = function () {
                var self = this;
                var args = Array.prototype.slice.call(arguments);
                return new Promise(function (resolve, reject) {
                    args.push(function (err, value) {
                        if (err) reject(err); else resolve(value);
                    });
                    try {
                        fn.apply(self, args);
                    } catch (e) {
                        reject(e);
                    }
                });
            };
            wrapped.__ncPromisified = true;
            return wrapped;
        },
        // Node-shaped callbackify: Promise-returning function →
        // (err, value) callback style; rejections surface as the err
        // argument.
        callbackify: function (fn) {
            if (typeof fn !== 'function') {
                throw new TypeError(
                    'util.callbackify argument must be a function');
            }
            return function () {
                var self = this;
                var args = Array.prototype.slice.call(arguments);
                var cb = args.pop();
                if (typeof cb !== 'function') {
                    throw new TypeError(
                        'util.callbackify last argument must be a function');
                }
                try {
                    fn.apply(self, args).then(
                        function (value) { cb(null, value); },
                        function (err) {
                            cb(err || new Error('falsy rejection'));
                        });
                } catch (e) {
                    cb(e);
                }
            };
        }
    };

    // ── require: builtin compat modules + consumer registry + fallback ──
    var registry = {};
    globalThis.__ncRegistry = registry;
    var builtins = {
        path: function () { return path; },
        assert: function () { return assert; },
        util: function () { return globalThis.util; },
        url: function () { return globalThis.__ncRegistry.url(); },
        buffer: function () { return globalThis.__ncRegistry.buffer(); }
    };
    // The async prelude (events/os/timers) augments this same map after eval.
    globalThis.__ncBuiltins = builtins;
    var baseRequire = typeof require === 'function' ? require : null;
    function compatRequire(name) {
        if (Object.prototype.hasOwnProperty.call(registry, name)) {
            return registry[name](name);
        }
        if (Object.prototype.hasOwnProperty.call(builtins, name)) {
            return builtins[name](name);
        }
        if (baseRequire) return baseRequire(name);
        throw new Error("Cannot find module '" + name +
            "' (compat builtins: path, assert, util, os, url, buffer, events" +
            (Object.keys(registry).length
                ? '; consumer-registered: ' + Object.keys(registry).join(', ')
                : '') + ')');
    }
    globalThis.require = compatRequire;

    // ── Intl (typeof-safe, call-time stubs — no ICU in QuickJS) ──
    function intlStub(name) {
        return function () {
            throw new Error(
                'Intl.' + name + ' is not available in quickjs_runtime: ' +
                'no ICU in QuickJS — format in the host or with plain JS');
        };
    }
    globalThis.Intl = {
        NumberFormat: intlStub('NumberFormat'),
        DateTimeFormat: intlStub('DateTimeFormat'),
        Collator: intlStub('Collator'),
        PluralRules: intlStub('PluralRules'),
        RelativeTimeFormat: intlStub('RelativeTimeFormat'),
        ListFormat: intlStub('ListFormat'),
        Segmenter: intlStub('Segmenter'),
        DisplayNames: intlStub('DisplayNames'),
        SupportedLocales: function () { return []; },
        getCanonicalLocales: function (locales) {
            if (locales === undefined || locales === null) return [];
            return Array.isArray(locales) ? locales.slice()
                : [String(locales)];
        }
    };

})();
''';