// The island's node:util shim, differentially against Node: every line // must match real Node byte-for-byte. Exercises inspect (quoting, depth, // break-length wrapping, array grouping, circular refs, Map/Set, classes, // custom inspect, promise state via the host peek), format's specifiers, // promisify/callbackify round trips, inherits, isDeepStrictEqual, // parseArgs, stripVTControlCharacters/styleText (piped: both lanes // uncolored), util.types, and the util/types module identity. import util, { inspect, format, promisify, callbackify, inherits, isDeepStrictEqual, parseArgs, stripVTControlCharacters, styleText, types, } from "node:util"; import { isNativeError } from "node:util/types"; class Foo { constructor() { this.a = 1; this.b = { c: 2 }; } } export async function report() { const lines = []; const show = (label, v, opts) => { lines.push(label + " " + (opts === undefined ? inspect(v) : inspect(v, opts))); }; // primitives and strings show("undef:", undefined); show("null:", null); show("neg0:", -0); show("nan:", NaN); show("exp:", 1e21); show("bigint:", 123n); show("str:", "plain"); show("squote:", "it's"); show("dquote:", 'say "hi"'); show("bothq:", "it's \"quoted\""); show("tick:", "a`b'c\"d"); show("ctrl:", "a\nb\tc\x01d\x7f"); show("uni:", "héllo ☃"); show("sym:", Symbol("desc")); // functions and classes show("fn:", function named() {}); show("anon:", function () {}); show("arrow:", () => {}); show("asyncfn:", async function af() {}); show("genfn:", function* gf() {}); show("cls:", class Klass {}); show("clsext:", class Sub extends Foo {}); const fnProps = function withProps() {}; fnProps.attached = 42; show("fnprops:", fnProps); // arrays show("arr:", [1, 2, 3]); show("arrmix:", [1, "two", null, undefined, true]); show("arrnest:", [[1, [2, [3, [4]]]]]); // eslint-disable-next-line no-sparse-arrays show("holes:", [1, , , 4]); show("arrlong:", Array.from({ length: 150 }, (_, i) => i)); show("arr30:", new Array(30).fill(7)); show("arrstr:", [ "some fairly long string here", "another fairly long string here", "and a third fairly long string", "plus the fourth one for measure", ]); show("arrx:", Object.assign([1, 2], { extra: "x" })); // objects show("obj:", { a: 1, b: "two" }); show("keys:", { "a-b": 1, "0x": 2, valid_$: 3, "": 4 }); show("nest4:", { a: { b: { c: { d: { e: 5 } } } } }); show("depth0:", { a: { b: 1 } }, { depth: 0 }); show("depthnull:", { a: { b: { c: { d: {} } } } }, { depth: null }); show("break40:", { alpha: "one", beta: "two", gamma: "three", delta: "four", }, { breakLength: 40 }); show("sorted:", { b: 1, a: 2 }, { sorted: true }); show("compactoff:", { a: 1, b: 2 }, { compact: false }); show("maxarr:", [1, 2, 3, 4, 5], { maxArrayLength: 2 }); show("maxstr:", "abcdef", { maxStringLength: 3 }); const nullProto = Object.create(null); nullProto.x = 1; show("nullproto:", nullProto); show("symkey:", { plain: 2, [Symbol("k")]: 1 }); const getterObj = {}; Object.defineProperty(getterObj, "g", { get: () => 1, enumerable: true }); Object.defineProperty(getterObj, "gs", { get: () => 1, set: () => {}, enumerable: true }); show("getters:", getterObj); show("inst:", new Foo()); // builtin composites show("map0:", new Map()); show("map:", new Map([["a", 1], ["b", { x: 2 }]])); show("set:", new Set([1, "two", { three: 3 }])); show("date:", new Date(86400000)); show("baddate:", new Date(NaN)); show("re:", /a+b/gi); // A stackless Error shape: engine stack strings never byte-match V8's, // so the differential case pins the no-stack rendering (real errors // carry engine-format stacks — a documented divergence). const errNoStack = Object.create(Error.prototype); errNoStack.message = "boom"; show("err:", errNoStack); show("boxed:", new String("boxed")); show("u8:", new Uint8Array([1, 2, 3])); show("i32:", new Int32Array([-1, 0, 1])); show("ab:", new Uint8Array([0x61, 0x62]).buffer); // circular const circ = { name: "x" }; circ.self = circ; show("circ:", circ); const circArr = [1]; circArr.push(circArr); show("circarr:", circArr); // custom inspect const custom = { [inspect.custom](depth) { return "custom<" + depth + ">"; }, }; show("custom:", custom); // promise state (the host peek; Node reads engine internals the same way) show("pending:", new Promise(() => {})); show("resolved:", Promise.resolve(42)); const rejected = Promise.reject("nope"); show("rejected:", rejected); rejected.catch(() => {}); // format lines.push("f1 " + format("%s|%d|%i|%f|%j", "s", "42.5", "42.9", "42.9x", { a: [1] })); lines.push("f2 " + format("%s", { a: { b: { c: 1 } } })); lines.push("f3 " + format("%d", { a: 1 })); lines.push("f4 " + format("%o", { a: 1 })); lines.push("f5 " + format("%O", { a: { b: { c: 1 } } })); lines.push("f6 " + format("100%% %s", "sure")); lines.push("f7 " + format("%cstyled", "color: red", "after")); lines.push("f8 " + format("%s %s %s", "one")); lines.push("f9 " + format("%s", "one", "two", { three: 3 })); lines.push("f10 " + format(1, "two", { three: 3 })); lines.push("f11 " + format("%d|%s", -0, -0)); lines.push("f12 " + format("%d", 7n)); // promisify / callbackify const padd = promisify((a, b, cb) => cb(null, a + b)); lines.push("promisify " + (await padd(1, 2))); const pfail = promisify((cb) => cb(new Error("no"))); lines.push("promisify-err " + (await pfail().catch((e) => "caught:" + e.message))); const customAsync = () => {}; customAsync[promisify.custom] = async () => "custom-path"; lines.push("promisify-custom " + (await promisify(customAsync)())); const doubled = callbackify(async (x) => x * 2); lines.push("callbackify " + (await new Promise((res) => doubled(21, (err, v) => res(err + ":" + v))))); // inherits function A() {} function B() {} inherits(B, A); lines.push("inherits " + (Object.getPrototypeOf(B.prototype) === A.prototype) + " " + (B.super_ === A)); // isDeepStrictEqual const dse = [ [1, 1], [0, -0], [NaN, NaN], [{ a: 1 }, { a: 1 }], [{ a: 1 }, { a: 2 }], [{ a: 1 }, { a: 1, b: undefined }], [[1, 2], [2, 1]], [new Map([[{ x: 1 }, "a"]]), new Map([[{ x: 1 }, "a"]])], [new Set([{ x: 1 }]), new Set([{ x: 1 }])], [new Date(0), new Date(0)], [/a/g, /a/i], [new Uint8Array([1]), new Int8Array([1])], [new Foo(), new Foo()], [new Foo(), { a: 1, b: { c: 2 } }], [Object.create(null), {}], [new Error("x"), new Error("x")], [new Error("x"), new Error("y")], ]; lines.push("dse " + dse.map(([a, b]) => (isDeepStrictEqual(a, b) ? 1 : 0)).join("")); const ca = { v: 1 }; ca.self = ca; const cb = { v: 1 }; cb.self = cb; lines.push("dse-circ " + isDeepStrictEqual(ca, cb)); // parseArgs const pa = (cfg) => { try { return JSON.stringify(parseArgs(cfg)); } catch (e) { return "threw:" + e.code; } }; lines.push("pa1 " + pa({ args: ["--foo", "bar"], options: { foo: { type: "string" } } })); lines.push("pa2 " + pa({ args: ["--flag", "--foo=inline"], options: { flag: { type: "boolean" }, foo: { type: "string" } } })); lines.push("pa3 " + pa({ args: ["-ab", "-fvalue"], options: { alpha: { type: "boolean", short: "a" }, beta: { type: "boolean", short: "b" }, foo: { type: "string", short: "f" } } })); lines.push("pa4 " + pa({ args: ["--multi", "a", "--multi", "b", "pos"], options: { multi: { type: "string", multiple: true } }, allowPositionals: true })); lines.push("pa5 " + pa({ args: ["--", "--not-an-option"], allowPositionals: true })); lines.push("pa6 " + pa({ args: [], options: { d: { type: "string", default: "dv" } } })); lines.push("pa7 " + pa({ args: ["--unknown"] })); lines.push("pa8 " + pa({ args: ["--foo"], options: { foo: { type: "string" } } })); lines.push("pa9 " + pa({ args: ["pos"] })); lines.push("pa10 " + pa({ args: ["--flag", "pos"], options: { flag: { type: "boolean" } }, allowPositionals: true, tokens: true })); // strip / styleText (stdout is piped in the harness: both lanes uncolored) lines.push("strip " + stripVTControlCharacters("\u001b[31mred\u001b[39m plain")); lines.push("style " + styleText("red", "text") + "|" + styleText(["bold", "green"], "text")); let styleErr; try { styleText("nope", "x"); styleErr = "no"; } catch (e) { styleErr = e.code; } lines.push("style-bad " + styleErr); // types const t = [ types.isDate(new Date()), types.isDate({}), types.isRegExp(/x/), types.isMap(new Map()), types.isSet(new Set()), types.isPromise(Promise.resolve()), types.isNativeError(new TypeError("t")), types.isNativeError({}), types.isArrayBuffer(new ArrayBuffer(1)), types.isDataView(new DataView(new ArrayBuffer(1))), types.isTypedArray(new Uint8Array(1)), types.isTypedArray(new DataView(new ArrayBuffer(1))), types.isUint8Array(new Uint8Array(1)), types.isUint8Array(new Int8Array(1)), types.isGeneratorFunction(function* () {}), types.isAsyncFunction(async () => {}), types.isStringObject(new String("s")), types.isBoxedPrimitive(new Number(1)), types.isArrayBufferView(new Uint8Array(1)), types.isArrayBufferView([]), ]; lines.push("types " + t.map((x) => (x ? 1 : 0)).join("")); lines.push("types-module " + (isNativeError === types.isNativeError) + " " + (util.types === types)); lines.push("legacy " + util.isArray([]) + " " + util.isArray({}) + " " + (typeof util._extend === "function")); lines.push("tenc " + (util.TextEncoder === TextEncoder)); return lines.join("\n"); }