///|
fn node_compat_source() -> String {
  "(function(){ " +
  "const root = globalThis; " +
  "const enqueue = typeof queueMicrotask === 'function' ? queueMicrotask : function(fn){ Promise.resolve().then(fn); }; " +
  "root.global = root; " +
  "const process = root.process ?? (root.process = {}); " +
  "if (!Array.isArray(process.argv)) process.argv = []; " +
  "if (typeof process.env !== 'object' || process.env === null) process.env = {}; " +
  "if (typeof process.cwd !== 'function') process.cwd = function(){ return '/'; }; " +
  "if (typeof process.nextTick !== 'function') process.nextTick = function(cb, ...args){ enqueue(function(){ cb(...args); }); }; " +
  "if (typeof process.version !== 'string') process.version = 'v0.0.0-mizchi-v8'; " +
  "if (typeof process.platform !== 'string') process.platform = 'unknown'; " +
  "if (typeof process.arch !== 'string') process.arch = 'unknown'; " +
  "if (typeof process.versions !== 'object' || process.versions === null) process.versions = {}; " +
  "if (typeof process.versions.node !== 'string') process.versions.node = '0.0.0-mizchi-v8'; " +
  "if (typeof process.versions.v8 !== 'string') process.versions.v8 = '" +
  version() +
  "'; " +
  "if (typeof root.setImmediate !== 'function') root.setImmediate = function(cb, ...args){ enqueue(function(){ cb(...args); }); return 0; }; " +
  "if (typeof root.clearImmediate !== 'function') root.clearImmediate = function(_id){}; " +
  "if (typeof root.Buffer !== 'function') { " +
  "class Buffer extends Uint8Array { " +
  "static from(input){ " +
  "if (typeof input === 'string') { const buf = new Buffer(input.length); for (let i = 0; i < input.length; i += 1) buf[i] = input.charCodeAt(i) & 255; return buf; } " +
  "if (Array.isArray(input)) return new Buffer(input); " +
  "if (input instanceof ArrayBuffer) return new Buffer(new Uint8Array(input)); " +
  "if (ArrayBuffer.isView(input)) return new Buffer(new Uint8Array(input.buffer, input.byteOffset, input.byteLength)); " +
  "throw new TypeError('Buffer.from unsupported input'); " +
  "} " +
  "static alloc(size){ return new Buffer(size); } " +
  "static concat(list){ let total = 0; for (const item of list) total += Buffer.from(item).length; const out = new Buffer(total); let offset = 0; for (const item of list) { const next = Buffer.from(item); out.set(next, offset); offset += next.length; } return out; } " +
  "static byteLength(input){ return Buffer.from(input).length; } " +
  "static isBuffer(value){ return value instanceof Buffer; } " +
  "toString(_encoding){ let out = ''; for (let i = 0; i < this.length; i += 1) out += String.fromCharCode(this[i]); return out; } " +
  "} " +
  "root.Buffer = Buffer; " +
  "} " +
  "return 'ok'; " +
  "})()"
}

///|
pub fn Runtime::install_node_compat(self : Runtime) -> Result[Unit, V8Error] {
  if runtime_has_node_compat(self) {
    Ok(())
  } else {
    let installed = install_compat_source(
      self,
      "file:///compat/node.js",
      node_compat_source(),
    )
    guard installed is Ok(_) else { return Err(installed.unwrap_err()) }
    runtime_mark_node_compat(self)
    Ok(())
  }
}

///|
pub fn RuntimeBuilder::with_node_compat(
  self : RuntimeBuilder,
) -> RuntimeBuilder {
  self.state.val.with_node_compat = true
  apply_runtime_builder_compat(self, node_compat_source())
}

///|
pub fn SnapshotBuilder::with_node_compat(
  self : SnapshotBuilder,
) -> SnapshotBuilder {
  self.state.val.with_node_compat = true
  apply_snapshot_builder_compat(self, node_compat_source())
}