{
  "template/napi_types.mbt": "// 使用 #external 注解定义 N-API 的不透明指针，由 C 侧管理生命周期\n\n///|\n#external\npub type NapiEnv\n\n///|\n#external\npub type NapiValue\n\n///|\n#external\npub type NapiCallbackInfo\n\n///|\n#external\npub type UnmanagedBuffer\n\n///|\n#external\npub type NapiDeferred\n\n///|\n#external\npub type NapiThreadsafeFunction\n\n///|\npub struct NapiPromise {\n  promise : NapiValue\n  deferred : NapiDeferred\n  mut settled : Bool\n}\n\n///|\npub(all) enum NapiTsfnStatus {\n  Accepted\n  QueueFull\n  Closing\n}\n\n///|\npub(all) enum NapiTsfnCallMode {\n  Nonblocking\n  Blocking\n}\n\n///|\npub(all) enum NapiTsfnReleaseMode {\n  Release\n  Abort\n}\n",
  "template/napi_bindings.mbt": "// 声明 N-API 基础函数的 FFI 绑定\n\n///|\n#borrow(result)\nextern \"c\" fn napi_create_int32(\n  env : NapiEnv,\n  value : Int32,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_create_int32\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_get_value_double(\n  env : NapiEnv,\n  value : NapiValue,\n  result : Ref[Double],\n) -> Int32 = \"napi_get_value_double\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_create_double(\n  env : NapiEnv,\n  value : Double,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_create_double\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_get_value_bool(\n  env : NapiEnv,\n  value : NapiValue,\n  result : Ref[Bool],\n) -> Int32 = \"napi_get_value_bool\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_get_boolean(\n  env : NapiEnv,\n  value : Bool,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_get_boolean\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_get_undefined(\n  env : NapiEnv,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_get_undefined\"\n\n///|\n#borrow(status)\nextern \"c\" fn napi_read_string(\n  env : NapiEnv,\n  value : NapiValue,\n  status : Ref[Int32],\n) -> String = \"moonbit_napi_read_string\"\n\n///|\n#borrow(str, result)\nextern \"c\" fn napi_write_string(\n  env : NapiEnv,\n  str : String,\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_write_string\"\n\n///|\nextern \"c\" fn napi_copy_bytes(data : UnmanagedBuffer, length : Int32) -> Bytes = \"moonbit_napi_copy_bytes\"\n\n///|\n#borrow(data, length)\nextern \"c\" fn napi_get_buffer_info(\n  env : NapiEnv,\n  value : NapiValue,\n  data : Ref[UnmanagedBuffer],\n  length : Ref[Int32],\n) -> Int32 = \"moonbit_napi_get_buffer_info\"\n\n///|\nextern \"c\" fn UnmanagedBuffer::load8(\n  self : UnmanagedBuffer,\n  offset : Int32,\n) -> Int32 = \"moonbit_ptr_load8\"\n\n///|\nextern \"c\" fn UnmanagedBuffer::store8(\n  self : UnmanagedBuffer,\n  offset : Int32,\n  val : Int32,\n) = \"moonbit_ptr_store8\"\n\n///|\n#borrow(data, result, result_data)\nextern \"c\" fn napi_create_buffer_copy(\n  env : NapiEnv,\n  length : Int32,\n  data : Bytes,\n  result_data : Ref[UnmanagedBuffer],\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_create_buffer_copy\"\n\n///|\nextern \"c\" fn moonbit_dummy_napi_value() -> NapiValue = \"moonbit_dummy_napi_value\"\n\n///|\nextern \"c\" fn moonbit_dummy_unmanaged_buffer() -> UnmanagedBuffer = \"moonbit_dummy_unmanaged_buffer\"\n\n///|\nextern \"c\" fn moonbit_dummy_deferred() -> NapiDeferred = \"moonbit_dummy_deferred\"\n\n///|\n#borrow(deferred, promise)\nextern \"c\" fn napi_create_promise(\n  env : NapiEnv,\n  deferred : Ref[NapiDeferred],\n  promise : Ref[NapiValue],\n) -> Int32 = \"napi_create_promise\"\n\n///|\nextern \"c\" fn napi_resolve_deferred(\n  env : NapiEnv,\n  deferred : NapiDeferred,\n  resolution : NapiValue,\n) -> Int32 = \"napi_resolve_deferred\"\n\n///|\nextern \"c\" fn napi_reject_deferred(\n  env : NapiEnv,\n  deferred : NapiDeferred,\n  rejection : NapiValue,\n) -> Int32 = \"napi_reject_deferred\"\n\n///|\n#borrow(is_promise)\nextern \"c\" fn napi_is_promise(\n  env : NapiEnv,\n  value : NapiValue,\n  is_promise : Ref[Bool],\n) -> Int32 = \"moonbit_napi_is_promise\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_create_bigint_int64(\n  env : NapiEnv,\n  value : Int64,\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_create_bigint_int64\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_create_bigint_uint64(\n  env : NapiEnv,\n  value : UInt64,\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_create_bigint_uint64\"\n\n///|\n#borrow(result, lossless)\nextern \"c\" fn napi_get_value_bigint_int64(\n  env : NapiEnv,\n  value : NapiValue,\n  result : Ref[Int64],\n  lossless : Ref[Bool],\n) -> Int32 = \"moonbit_napi_get_value_bigint_int64\"\n\n///|\n#borrow(result, lossless)\nextern \"c\" fn napi_get_value_bigint_uint64(\n  env : NapiEnv,\n  value : NapiValue,\n  result : Ref[UInt64],\n  lossless : Ref[Bool],\n) -> Int32 = \"moonbit_napi_get_value_bigint_uint64\"\n\n///|\nextern \"c\" fn napi_threadsafe_function_dummy() -> NapiThreadsafeFunction = \"moonbit_dummy_threadsafe_function\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_create_threadsafe_function(\n  env : NapiEnv,\n  func : NapiValue,\n  async_resource : NapiValue,\n  async_resource_name : NapiValue,\n  max_queue_size : Int32,\n  initial_thread_count : Int32,\n  thread_finalize_data : UnmanagedBuffer,\n  thread_finalize_cb : UnmanagedBuffer,\n  context : UnmanagedBuffer,\n  call_js_cb : UnmanagedBuffer,\n  result : Ref[NapiThreadsafeFunction],\n) -> Int32 = \"moonbit_napi_create_threadsafe_function\"\n\n///|\nextern \"c\" fn napi_call_threadsafe_function(\n  func : NapiThreadsafeFunction,\n  data : UnmanagedBuffer,\n  mode : Int32,\n) -> Int32 = \"moonbit_napi_call_threadsafe_function\"\n\n///|\nextern \"c\" fn napi_acquire_threadsafe_function(\n  func : NapiThreadsafeFunction,\n) -> Int32 = \"moonbit_napi_acquire_threadsafe_function\"\n\n///|\nextern \"c\" fn napi_release_threadsafe_function(\n  func : NapiThreadsafeFunction,\n  mode : Int32,\n) -> Int32 = \"moonbit_napi_release_threadsafe_function\"\n",
  "template/napi_features.mbt": "///|\n/// Promise support is available in Node-API v1.\npub fn napi_promise_new(env : NapiEnv) -> NapiPromise raise NapiError {\n  let deferred = Ref(moonbit_dummy_deferred())\n  let promise = Ref(moonbit_dummy_napi_value())\n  let status = napi_create_promise(env, deferred, promise)\n  result_from_status(env, status, b\"Failed to create Promise\")\n  { promise: promise.val, deferred: deferred.val, settled: false, }\n}\n\n///|\npub fn napi_promise_resolve(\n  env : NapiEnv,\n  promise : NapiPromise,\n  value : NapiValue,\n) -> Unit raise NapiError {\n  if promise.settled {\n    raise napi_failure(\n      1,\n      Error,\n      \"ERR_NAPI_PROMISE_SETTLED\",\n      \"Promise has already settled\",\n    )\n  }\n  promise.settled = true\n  let status = napi_resolve_deferred(env, promise.deferred, value)\n  result_from_status(env, status, b\"Failed to resolve Promise\")\n}\n\n///|\npub fn napi_promise_reject(\n  env : NapiEnv,\n  promise : NapiPromise,\n  reason : NapiValue,\n) -> Unit raise NapiError {\n  if promise.settled {\n    raise napi_failure(\n      1,\n      Error,\n      \"ERR_NAPI_PROMISE_SETTLED\",\n      \"Promise has already settled\",\n    )\n  }\n  promise.settled = true\n  let status = napi_reject_deferred(env, promise.deferred, reason)\n  result_from_status(env, status, b\"Failed to reject Promise\")\n}\n\n///|\n/// Returns whether a value is a JavaScript Promise.\npub fn napi_promise_is(\n  env : NapiEnv,\n  value : NapiValue,\n) -> Bool raise NapiError {\n  let result = Ref(false)\n  let status = napi_is_promise(env, value, result)\n  result_from_status(env, status, b\"Failed to inspect Promise\")\n  result.val\n}\n\n///|\npub fn napi_bigint_from_int64(\n  env : NapiEnv,\n  value : Int64,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  let status = napi_create_bigint_int64(env, value, result)\n  result_from_status(env, status, b\"Failed to create BigInt\")\n  result.val\n}\n\n///|\npub fn napi_bigint_from_uint64(\n  env : NapiEnv,\n  value : UInt64,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  let status = napi_create_bigint_uint64(env, value, result)\n  result_from_status(env, status, b\"Failed to create BigInt\")\n  result.val\n}\n\n///|\n/// Reads a signed BigInt and returns the value plus the lossless flag.\npub fn napi_bigint_to_int64(\n  env : NapiEnv,\n  value : NapiValue,\n) -> (Int64, Bool) raise NapiError {\n  let result = Ref(0L)\n  let lossless = Ref(false)\n  let status = napi_get_value_bigint_int64(env, value, result, lossless)\n  result_from_status(env, status, b\"Failed to read BigInt\")\n  (result.val, lossless.val)\n}\n\n///|\n/// Reads an unsigned BigInt and returns the value plus the lossless flag.\npub fn napi_bigint_to_uint64(\n  env : NapiEnv,\n  value : NapiValue,\n) -> (UInt64, Bool) raise NapiError {\n  let result = Ref(0UL)\n  let lossless = Ref(false)\n  let status = napi_get_value_bigint_uint64(env, value, result, lossless)\n  result_from_status(env, status, b\"Failed to read BigInt\")\n  (result.val, lossless.val)\n}\n\n///|\n/// Threadsafe Function lifecycle helper. Threadsafe Functions require N-API v4.\npub fn napi_threadsafe_function_release(\n  func : NapiThreadsafeFunction,\n  mode : NapiTsfnReleaseMode,\n) -> NapiTsfnStatus raise NapiError {\n  let mode = match mode {\n    Release => 0\n    Abort => 1\n  }\n  let status = napi_release_threadsafe_function(func, mode)\n  tsfn_status(status, \"Failed to release Threadsafe Function\")\n}\n\n///|\n/// Creates a Threadsafe Function. Callback pointers are opaque unmanaged\n/// pointers supplied by a C layer.\npub fn napi_threadsafe_function_create(\n  env : NapiEnv,\n  func : NapiValue,\n  async_resource : NapiValue,\n  async_resource_name : NapiValue,\n  max_queue_size : Int32,\n  initial_thread_count : Int32,\n  finalize_data : UnmanagedBuffer,\n  finalize_cb : UnmanagedBuffer,\n  context : UnmanagedBuffer,\n  call_js_cb : UnmanagedBuffer,\n) -> NapiThreadsafeFunction raise NapiError {\n  if max_queue_size < 0 || initial_thread_count <= 0 {\n    raise napi_failure(\n      1,\n      RangeError,\n      \"ERR_NAPI_TSFN_OPTIONS\",\n      \"TSFN needs nonnegative queue size and positive thread count\",\n    )\n  }\n  let result = Ref(napi_threadsafe_function_dummy())\n  let status = napi_create_threadsafe_function(\n    env, func, async_resource, async_resource_name, max_queue_size, initial_thread_count,\n    finalize_data, finalize_cb, context, call_js_cb, result,\n  )\n  result_from_status(env, status, b\"Failed to create Threadsafe Function\")\n  result.val\n}\n\n///|\npub fn napi_threadsafe_function_call(\n  func : NapiThreadsafeFunction,\n  data : UnmanagedBuffer,\n  mode : NapiTsfnCallMode,\n) -> NapiTsfnStatus raise NapiError {\n  let mode = match mode {\n    Nonblocking => 0\n    Blocking => 1\n  }\n  let status = napi_call_threadsafe_function(func, data, mode)\n  tsfn_status(status, \"Failed to call Threadsafe Function\")\n}\n\n///|\npub fn napi_threadsafe_function_acquire(\n  func : NapiThreadsafeFunction,\n) -> NapiTsfnStatus raise NapiError {\n  let status = napi_acquire_threadsafe_function(func)\n  tsfn_status(status, \"Failed to acquire Threadsafe Function\")\n}\n\n///|\nfn tsfn_status(\n  status : Int32,\n  context : String,\n) -> NapiTsfnStatus raise NapiError {\n  match status {\n    0 => Accepted\n    15 => QueueFull\n    16 => Closing\n    _ => raise napi_failure(status, Error, \"ERR_NAPI_TSFN\", context)\n  }\n}\n",
  "template/napi_runtime.mbt": "///|\nlet active_buffer_views : Ref[Int32] = Ref(0)\n\n///|\npub struct NapiBufferView {\n  env : NapiEnv\n  value : NapiValue\n  mut is_alive : Bool\n}\n\n///|\nfn NapiBufferView::validate(\n  self : NapiBufferView,\n) -> (UnmanagedBuffer, Int32) raise NapiError {\n  if !self.is_alive {\n    raise napi_failure(\n      1,\n      Error,\n      \"ERR_NAPI_BUFFER_EXPIRED\",\n      \"Buffer view is no longer valid\",\n    )\n  }\n  let data = Ref(moonbit_dummy_unmanaged_buffer())\n  let len = Ref(0)\n  let status = napi_get_buffer_info(self.env, self.value, data, len)\n  result_from_status(\n    self.env,\n    status,\n    b\"Buffer backing store is detached or invalid\",\n  )\n  (data.val, len.val)\n}\n\n///|\npub fn NapiBufferView::length(self : NapiBufferView) -> Int32 raise NapiError {\n  self.validate().1\n}\n\n///|\npub fn NapiBufferView::load8(\n  self : NapiBufferView,\n  offset : Int32,\n) -> Int32 raise NapiError {\n  let (buf, len) = self.validate()\n  if offset < 0 || offset >= len {\n    raise napi_failure(\n      1,\n      RangeError,\n      \"ERR_NAPI_BUFFER_BOUNDS\",\n      \"Buffer view out of bounds\",\n    )\n  }\n  buf.load8(offset)\n}\n\n///|\npub fn NapiBufferView::store8(\n  self : NapiBufferView,\n  offset : Int32,\n  val : Int32,\n) -> Unit raise NapiError {\n  let (buf, len) = self.validate()\n  if offset < 0 || offset >= len {\n    raise napi_failure(\n      1,\n      RangeError,\n      \"ERR_NAPI_BUFFER_BOUNDS\",\n      \"Buffer view out of bounds\",\n    )\n  }\n  buf.store8(offset, val)\n}\n\n///|\n/// Invalidates a borrowed Buffer view after the callback returns.\npub fn NapiBufferView::invalidate(self : NapiBufferView) -> Unit {\n  if self.is_alive {\n    self.is_alive = false\n    active_buffer_views.val -= 1\n  }\n}\n\n///|\npub fn get_Int(env : NapiEnv, val : NapiValue) -> Int32 raise NapiError {\n  let res = Ref(0.0)\n  let status = napi_get_value_double(env, val, res)\n  result_from_status(env, status, b\"Invalid argument, expected Int\")\n  let value = res.val\n  if !(value >= -2147483648.0 && value <= 2147483647.0) ||\n    value != value.to_int().to_double() {\n    raise napi_failure(\n      1,\n      RangeError,\n      \"ERR_NAPI_INT_RANGE\",\n      \"Int requires a finite integral number in [-2147483648, 2147483647]\",\n    )\n  }\n  value.to_int()\n}\n\n///|\npub fn set_Int(env : NapiEnv, val : Int32) -> NapiValue raise NapiError {\n  let res = Ref(moonbit_dummy_napi_value())\n  let status = napi_create_int32(env, val, res)\n  result_from_status(env, status, b\"Failed to create Int\")\n  res.val\n}\n\n///|\npub fn get_UInt(env : NapiEnv, value : NapiValue) -> UInt32 raise NapiError {\n  let number = get_Double(env, value)\n  if !(number >= 0.0 && number <= 4294967295.0) ||\n    UInt32::trunc_double(number).to_double() != number {\n    raise napi_failure(\n      1,\n      RangeError,\n      \"ERR_NAPI_UINT_RANGE\",\n      \"UInt requires a finite integer in [0, 4294967295]\",\n    )\n  }\n  UInt32::trunc_double(number)\n}\n\n///|\npub fn set_UInt(env : NapiEnv, value : UInt32) -> NapiValue raise NapiError {\n  set_Double(env, value.to_double())\n}\n\n///|\npub fn get_Float(env : NapiEnv, value : NapiValue) -> Float raise NapiError {\n  Float::from_double(get_Double(env, value))\n}\n\n///|\npub fn set_Float(env : NapiEnv, value : Float) -> NapiValue raise NapiError {\n  set_Double(env, value.to_double())\n}\n\n///|\npub fn get_Double(env : NapiEnv, val : NapiValue) -> Double raise NapiError {\n  let res = Ref(0.0)\n  let status = napi_get_value_double(env, val, res)\n  result_from_status(env, status, b\"Invalid argument, expected Double\")\n  res.val\n}\n\n///|\npub fn set_Double(env : NapiEnv, val : Double) -> NapiValue raise NapiError {\n  let res = Ref(moonbit_dummy_napi_value())\n  let status = napi_create_double(env, val, res)\n  result_from_status(env, status, b\"Failed to create Double\")\n  res.val\n}\n\n///|\npub fn get_Bool(env : NapiEnv, val : NapiValue) -> Bool raise NapiError {\n  let res = Ref(false)\n  let status = napi_get_value_bool(env, val, res)\n  result_from_status(env, status, b\"Invalid argument, expected Bool\")\n  res.val\n}\n\n///|\npub fn set_Bool(env : NapiEnv, val : Bool) -> NapiValue raise NapiError {\n  let res = Ref(moonbit_dummy_napi_value())\n  let status = napi_get_boolean(env, val, res)\n  result_from_status(env, status, b\"Failed to create Bool\")\n  res.val\n}\n\n///|\npub fn get_String(env : NapiEnv, val : NapiValue) -> String raise NapiError {\n  let status = Ref(0)\n  let result = napi_read_string(env, val, status)\n  result_from_status(env, status.val, b\"Reading UTF-16 String\")\n  result\n}\n\n///|\npub fn set_String(env : NapiEnv, val : String) -> NapiValue raise NapiError {\n  let res = Ref(moonbit_dummy_napi_value())\n  let status = napi_write_string(env, val, res)\n  result_from_status(env, status, b\"Failed to create String\")\n  res.val\n}\n\n///|\npub fn get_Bytes(env : NapiEnv, val : NapiValue) -> Bytes raise NapiError {\n  let len = Ref(0)\n  let unmanaged = Ref(moonbit_dummy_unmanaged_buffer())\n  let status = napi_get_buffer_info(env, val, unmanaged, len)\n  result_from_status(env, status, b\"Invalid argument, expected Buffer\")\n\n  napi_copy_bytes(unmanaged.val, len.val)\n}\n\n///|\npub fn set_Bytes(env : NapiEnv, val : Bytes) -> NapiValue raise NapiError {\n  let res = Ref(moonbit_dummy_napi_value())\n  let data = Ref(moonbit_dummy_unmanaged_buffer())\n  let status = napi_create_buffer_copy(env, val.length(), val, data, res)\n  result_from_status(env, status, b\"Failed to create buffer\")\n  res.val\n}\n\n///|\npub fn get_NapiBufferView(\n  env : NapiEnv,\n  val : NapiValue,\n) -> NapiBufferView raise NapiError {\n  let len = Ref(0)\n  let unmanaged = Ref(moonbit_dummy_unmanaged_buffer())\n  let status = napi_get_buffer_info(env, val, unmanaged, len)\n  result_from_status(env, status, b\"Invalid argument, expected Buffer\")\n  active_buffer_views.val += 1\n  { env, value: val, is_alive: true, }\n}\n\n///|\npub fn set_Unit(env : NapiEnv, _val : Unit) -> NapiValue raise NapiError {\n  let res = Ref(moonbit_dummy_napi_value())\n  let status = napi_get_undefined(env, res)\n  result_from_status(env, status, b\"Failed to create undefined\")\n  res.val\n}\n",
  "template/stub.c": "#include <node_api.h>\n#include <moonbit.h>\n#include <string.h>\n#include <stdint.h>\n#include <limits.h>\n#include \"napi_lifecycle.h\"\n#include \"napi_callbacks.h\"\n#include \"napi_owned_binary.h\"\n\n/* Shared exception boundary for C callbacks and MoonBit adapters. Never replace\n * a JS exception already pending on the environment. Every operation is checked. */\nint napi_mbt_throw_status(napi_env env, int status, int kind, const char *code, const char *message) {\n    bool pending = false;\n    napi_status s = napi_is_exception_pending(env, &pending);\n    if (s != napi_ok || pending) return (int)s;\n    napi_value js_code, js_message, error, js_status;\n    s = napi_create_string_utf8(env, code, NAPI_AUTO_LENGTH, &js_code);\n    if (s != napi_ok) goto fallback;\n    s = napi_create_string_utf8(env, message, NAPI_AUTO_LENGTH, &js_message);\n    if (s != napi_ok) goto fallback;\n    s = kind == 1 ? napi_create_type_error(env, js_code, js_message, &error)\n      : kind == 2 ? napi_create_range_error(env, js_code, js_message, &error)\n      : napi_create_error(env, js_code, js_message, &error);\n    if (s != napi_ok) goto fallback;\n    s = napi_create_int32(env, status, &js_status);\n    if (s != napi_ok) goto fallback;\n    s = napi_set_named_property(env, error, \"status\", js_status);\n    if (s != napi_ok) goto fallback;\n    s = napi_set_named_property(env, error, \"context\", js_message);\n    if (s != napi_ok) goto fallback;\n    return (int)napi_throw(env, error);\nfallback:\n    /* Even diagnostics can fail. Preserve any exception that caused that\n     * failure; otherwise make one final attempt through the simpler API. */\n    if (napi_is_exception_pending(env, &pending) != napi_ok || pending) return (int)s;\n    return (int)napi_throw_error(env, code, message);\n}\n\nMOONBIT_FFI_EXPORT int moonbit_napi_throw(napi_env env, int status, int kind, const char *code, const char *message) {\n    return napi_mbt_throw_status(env, status, kind, code, message);\n}\n\n#if NAPI_VERSION < 4\ntypedef void* napi_threadsafe_function;\n#endif\n\nMOONBIT_FFI_EXPORT\nnapi_value moonbit_dummy_napi_value() {\n    return NULL;\n}\n\nMOONBIT_FFI_EXPORT\nvoid* moonbit_dummy_unmanaged_buffer() {\n    return NULL;\n}\n\nMOONBIT_FFI_EXPORT\nvoid* moonbit_dummy_deferred() {\n    return NULL;\n}\n\nMOONBIT_FFI_EXPORT\nvoid* moonbit_dummy_threadsafe_function() {\n    return NULL;\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_is_promise(napi_env env, napi_value value, int* result) {\n    bool is_promise = false;\n    napi_status status = napi_is_promise(env, value, &is_promise);\n    if (result) *result = is_promise ? 1 : 0;\n    return (int)status;\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_get_value_bigint_int64(napi_env env, napi_value value, int64_t* result, int* lossless) {\n#if NAPI_VERSION >= 6\n    bool exact = false;\n    napi_status status = napi_get_value_bigint_int64(env, value, result, &exact);\n    if (lossless) *lossless = exact ? 1 : 0;\n    return (int)status;\n#else\n    (void)env; (void)value; (void)result; (void)lossless;\n    return (int)napi_generic_failure;\n#endif\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_create_bigint_int64(napi_env env, int64_t value, napi_value* result) {\n#if NAPI_VERSION >= 6\n    return (int)napi_create_bigint_int64(env, value, result);\n#else\n    (void)env; (void)value; (void)result;\n    return (int)napi_generic_failure;\n#endif\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_create_bigint_uint64(napi_env env, uint64_t value, napi_value* result) {\n#if NAPI_VERSION >= 6\n    return (int)napi_create_bigint_uint64(env, value, result);\n#else\n    (void)env; (void)value; (void)result;\n    return (int)napi_generic_failure;\n#endif\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_get_value_bigint_uint64(napi_env env, napi_value value, uint64_t* result, int* lossless) {\n#if NAPI_VERSION >= 6\n    bool exact = false;\n    napi_status status = napi_get_value_bigint_uint64(env, value, result, &exact);\n    if (lossless) *lossless = exact ? 1 : 0;\n    return (int)status;\n#else\n    (void)env; (void)value; (void)result; (void)lossless;\n    return (int)napi_generic_failure;\n#endif\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_create_threadsafe_function(\n    napi_env env, napi_value func, napi_value async_resource,\n    napi_value async_resource_name, int max_queue_size,\n    int initial_thread_count, void* finalize_data, void* finalize_cb,\n    void* context, void* call_js_cb, napi_threadsafe_function* result) {\n#if NAPI_VERSION >= 4\n    return (int)napi_create_threadsafe_function(\n        env, func, async_resource, async_resource_name,\n        (size_t)max_queue_size, (size_t)initial_thread_count,\n        finalize_data, (napi_finalize)finalize_cb, context,\n        (napi_threadsafe_function_call_js)call_js_cb, result);\n#else\n    (void)env; (void)func; (void)async_resource; (void)async_resource_name;\n    (void)max_queue_size; (void)initial_thread_count; (void)finalize_data;\n    (void)finalize_cb; (void)context; (void)call_js_cb; (void)result;\n    return (int)napi_generic_failure;\n#endif\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_call_threadsafe_function(napi_threadsafe_function func, void* data, int mode) {\n#if NAPI_VERSION >= 4\n    return (int)napi_call_threadsafe_function(func, data, (napi_threadsafe_function_call_mode)mode);\n#else\n    (void)func; (void)data; (void)mode;\n    return (int)napi_generic_failure;\n#endif\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_acquire_threadsafe_function(napi_threadsafe_function func) {\n#if NAPI_VERSION >= 4\n    return (int)napi_acquire_threadsafe_function(func);\n#else\n    (void)func;\n    return (int)napi_generic_failure;\n#endif\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_release_threadsafe_function(napi_threadsafe_function func, int mode) {\n#if NAPI_VERSION >= 4\n    return (int)napi_release_threadsafe_function(func, (napi_threadsafe_function_release_mode)mode);\n#else\n    (void)func; (void)mode;\n    return (int)napi_generic_failure;\n#endif\n}\n\nMOONBIT_FFI_EXPORT\nmoonbit_string_t moonbit_napi_read_string(napi_env env, napi_value value, int32_t *status_out) {\n    size_t length = 0;\n    napi_status status = napi_get_value_string_utf16(env, value, NULL, 0, &length);\n    *status_out = (int32_t)status;\n    if (status != napi_ok) return moonbit_make_string(0, 0);\n    if (length > INT_MAX) {\n        *status_out = 1002;\n        return moonbit_make_string(0, 0);\n    }\n    // MoonBit allocates a trailing UTF-16 zero beyond the logical length.\n    // Node writes directly into this exclusively owned, not-yet-published value.\n    moonbit_string_t result = moonbit_make_string_raw((int32_t)length);\n    size_t written = 0;\n    status = napi_get_value_string_utf16(env, value, (char16_t *)result, length + 1, &written);\n    if (status != napi_ok || written != length) {\n        moonbit_decref(result);\n        *status_out = status == napi_ok ? (int32_t)napi_generic_failure : (int32_t)status;\n        return moonbit_make_string(0, 0);\n    }\n    return result;\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_write_string(napi_env env, moonbit_string_t value, napi_value *result) {\n    // Borrowed for this call. Node copies the code units, including lone surrogates.\n    return (int)napi_create_string_utf16(env, (const char16_t *)value, (size_t)Moonbit_array_length(value), result);\n}\n\nMOONBIT_FFI_EXPORT\nmoonbit_bytes_t moonbit_napi_copy_bytes(const void *data, int32_t length) {\n    moonbit_bytes_t result = moonbit_make_bytes_raw(length);\n    // Empty Buffers can have a null backing pointer; do not pass it to memcpy.\n    if (length) memcpy(result, data, (size_t)length);\n    return result;\n}\n\nMOONBIT_FFI_EXPORT\nint moonbit_napi_get_buffer_info(napi_env env, napi_value value, void** data, int* length) {\n    bool buffer = false;\n    napi_status status = napi_is_buffer(env, value, &buffer);\n    if (status != napi_ok) return (int)status;\n    if (!buffer) return (int)napi_invalid_arg;\n#if NAPI_VERSION >= 7\n    napi_typedarray_type type;\n    size_t elements, offset;\n    void *typed_data;\n    napi_value backing;\n    status = napi_get_typedarray_info(env, value, &type, &elements, &typed_data, &backing, &offset);\n    if (status != napi_ok) return (int)status;\n    bool detached = false;\n    status = napi_is_detached_arraybuffer(env, backing, &detached);\n    if (status != napi_ok) return (int)status;\n    if (detached) return 1001;\n#else\n    return (int)napi_generic_failure;\n#endif\n    size_t size = 0;\n    status = napi_get_buffer_info(env, value, data, &size);\n    if (size > INT_MAX) return 1002;\n    if (length) *length = (int)size;\n    return (int)status;\n}\n\nstatic void napi_noop_finalizer(napi_env env, void* finalize_data, void* finalize_hint) {\n    (void)env; (void)finalize_data; (void)finalize_hint;\n}\n\nMOONBIT_FFI_EXPORT int moonbit_ptr_load8(const unsigned char* ptr, int offset) {\n    return ptr[offset];\n}\n\nMOONBIT_FFI_EXPORT void moonbit_ptr_store8(unsigned char* ptr, int offset, int val) {\n    ptr[offset] = val;\n}\n\nMOONBIT_FFI_EXPORT int moonbit_napi_value_kind(napi_env env, napi_value value, int *result) {\n    napi_valuetype kind;\n    napi_status status = napi_typeof(env, value, &kind);\n    if (status == napi_ok) *result = (int)kind;\n    return (int)status;\n}\n\nMOONBIT_FFI_EXPORT int moonbit_napi_value_same(napi_env env, napi_value a, napi_value b, int *result) {\n    bool same = false;\n    napi_status status = napi_strict_equals(env, a, b, &same);\n    if (status == napi_ok) *result = same ? 1 : 0;\n    return (int)status;\n}\n\nMOONBIT_FFI_EXPORT int moonbit_napi_array_length(napi_env env, napi_value value, int *result) {\n    bool array = false;\n    napi_status status = napi_is_array(env, value, &array);\n    if (status != napi_ok) return (int)status;\n    if (!array) return (int)napi_array_expected;\n    uint32_t length = 0;\n    status = napi_get_array_length(env, value, &length);\n    if (status != napi_ok) return (int)status;\n    if (length > INT_MAX) return 1002;\n    *result = (int)length;\n    return (int)napi_ok;\n}\n\nMOONBIT_FFI_EXPORT int moonbit_napi_array_new(napi_env env, int length, napi_value *result) {\n    if (length < 0) return (int)napi_invalid_arg;\n    return (int)napi_create_array_with_length(env, (size_t)length, result);\n}\n\nMOONBIT_FFI_EXPORT int moonbit_napi_field_get(napi_env env, napi_value value, const char *name, int required, napi_value *result) {\n    napi_valuetype kind;\n    napi_status status = napi_typeof(env, value, &kind);\n    if (status != napi_ok) return (int)status;\n    if (kind != napi_object) return (int)napi_object_expected;\n    napi_value key;\n    status = napi_create_string_utf8(env, name, NAPI_AUTO_LENGTH, &key);\n    if (status != napi_ok) return (int)status;\n    bool present = false;\n    status = napi_has_own_property(env, value, key, &present);\n    if (status != napi_ok) return (int)status;\n    if (!present) return required ? 1003 : (int)napi_get_undefined(env, result);\n    return (int)napi_get_property(env, value, key, result);\n}\n\nMOONBIT_FFI_EXPORT int moonbit_napi_field_set(napi_env env, napi_value value, const char *name, napi_value field) {\n    // Define a data property so a field named __proto__ cannot invoke a setter.\n    napi_property_descriptor descriptor = {0};\n    descriptor.utf8name = name;\n    descriptor.value = field;\n    descriptor.attributes = (napi_property_attributes)(napi_writable | napi_enumerable | napi_configurable);\n    return (int)napi_define_properties(env, value, 1, &descriptor);\n}\n\nMOONBIT_FFI_EXPORT int moonbit_napi_is_array(napi_env env, napi_value value, int *result) {\n    bool array = false;\n    napi_status status = napi_is_array(env, value, &array);\n    *result = array ? 1 : 0;\n    return (int)status;\n}\n\nMOONBIT_FFI_EXPORT int moonbit_napi_keys(napi_env env, napi_value value, napi_value *result) {\n    napi_valuetype type;\n    napi_status status = napi_typeof(env, value, &type);\n    if (status != napi_ok) return (int)status;\n    if (type != napi_object) return (int)napi_object_expected;\n    bool array = false;\n    status = napi_is_array(env, value, &array);\n    if (status != napi_ok) return (int)status;\n    if (array) return (int)napi_object_expected;\n    napi_value keys;\n    status = napi_get_property_names(env, value, &keys);\n    if (status != napi_ok) return (int)status;\n    uint32_t length;\n    status = napi_get_array_length(env, keys, &length);\n    if (status != napi_ok) return (int)status;\n    status = napi_create_array(env, result);\n    if (status != napi_ok) return (int)status;\n    uint32_t output = 0;\n    for (uint32_t i = 0; i < length; ++i) {\n        napi_value key;\n        bool own;\n        status = napi_get_element(env, keys, i, &key);\n        if (status != napi_ok) return (int)status;\n        status = napi_coerce_to_string(env, key, &key);\n        if (status != napi_ok) return (int)status;\n        status = napi_has_own_property(env, value, key, &own);\n        if (status != napi_ok) return (int)status;\n        if (own) {\n            status = napi_set_element(env, *result, output++, key);\n            if (status != napi_ok) return (int)status;\n        }\n    }\n    return (int)napi_ok;\n}\n\nMOONBIT_FFI_EXPORT int moonbit_napi_key_set(napi_env env, napi_value value, napi_value key, napi_value field) {\n    napi_property_descriptor descriptor = {0};\n    descriptor.name = key;\n    descriptor.value = field;\n    descriptor.attributes = (napi_property_attributes)(napi_writable | napi_enumerable | napi_configurable);\n    return (int)napi_define_properties(env, value, 1, &descriptor);\n}\n",
  "include/node_api.h": "#ifndef SRC_NODE_API_H_\n#define SRC_NODE_API_H_\n\n#if defined(BUILDING_NODE_EXTENSION) && !defined(NAPI_EXTERN)\n#ifdef _WIN32\n// Building native addon against node\n#define NAPI_EXTERN __declspec(dllimport)\n#elif defined(__wasm__)\n#define NAPI_EXTERN __attribute__((__import_module__(\"napi\")))\n#endif\n#endif\n#include \"js_native_api.h\"\n#include \"node_api_types.h\"\n\nstruct uv_loop_s;  // Forward declaration.\n\n#ifdef _WIN32\n#define NAPI_MODULE_EXPORT __declspec(dllexport)\n#else\n#ifdef __EMSCRIPTEN__\n#define NAPI_MODULE_EXPORT                                                     \\\n  __attribute__((visibility(\"default\"))) __attribute__((used))\n#else\n#define NAPI_MODULE_EXPORT __attribute__((visibility(\"default\")))\n#endif\n#endif\n\n#if defined(__GNUC__)\n#define NAPI_NO_RETURN __attribute__((noreturn))\n#elif defined(_WIN32)\n#define NAPI_NO_RETURN __declspec(noreturn)\n#else\n#define NAPI_NO_RETURN\n#endif\n\n// Used by deprecated registration method napi_module_register.\ntypedef struct napi_module {\n  int nm_version;\n  unsigned int nm_flags;\n  const char* nm_filename;\n  napi_addon_register_func nm_register_func;\n  const char* nm_modname;\n  void* nm_priv;\n  void* reserved[4];\n} napi_module;\n\n#define NAPI_MODULE_VERSION 1\n\n#define NAPI_MODULE_INITIALIZER_X(base, version)                               \\\n  NAPI_MODULE_INITIALIZER_X_HELPER(base, version)\n#define NAPI_MODULE_INITIALIZER_X_HELPER(base, version) base##version\n\n#ifdef __wasm__\n#define NAPI_MODULE_INITIALIZER_BASE napi_register_wasm_v\n#else\n#define NAPI_MODULE_INITIALIZER_BASE napi_register_module_v\n#endif\n\n#define NODE_API_MODULE_GET_API_VERSION_BASE node_api_module_get_api_version_v\n\n#define NAPI_MODULE_INITIALIZER                                                \\\n  NAPI_MODULE_INITIALIZER_X(NAPI_MODULE_INITIALIZER_BASE, NAPI_MODULE_VERSION)\n\n#define NODE_API_MODULE_GET_API_VERSION                                        \\\n  NAPI_MODULE_INITIALIZER_X(NODE_API_MODULE_GET_API_VERSION_BASE,              \\\n                            NAPI_MODULE_VERSION)\n\n#define NAPI_MODULE_INIT()                                                     \\\n  EXTERN_C_START                                                               \\\n  NAPI_MODULE_EXPORT int32_t NODE_API_MODULE_GET_API_VERSION(void) {           \\\n    return NAPI_VERSION;                                                       \\\n  }                                                                            \\\n  NAPI_MODULE_EXPORT napi_value NAPI_MODULE_INITIALIZER(napi_env env,          \\\n                                                        napi_value exports);   \\\n  EXTERN_C_END                                                                 \\\n  napi_value NAPI_MODULE_INITIALIZER(napi_env env, napi_value exports)\n\n#define NAPI_MODULE(modname, regfunc)                                          \\\n  NAPI_MODULE_INIT() { return regfunc(env, exports); }\n\n// Deprecated. Use NAPI_MODULE.\n#define NAPI_MODULE_X(modname, regfunc, priv, flags)                           \\\n  NAPI_MODULE(modname, regfunc)\n\nEXTERN_C_START\n\n// Deprecated. Replaced by symbol-based registration defined by NAPI_MODULE\n// and NAPI_MODULE_INIT macros.\nNAPI_EXTERN void NAPI_CDECL\nnapi_module_register(napi_module* mod);\n\nNAPI_EXTERN NAPI_NO_RETURN void NAPI_CDECL\nnapi_fatal_error(const char* location,\n                 size_t location_len,\n                 const char* message,\n                 size_t message_len);\n\n// Methods for custom handling of async operations\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_async_init(napi_env env,\n                napi_value async_resource,\n                napi_value async_resource_name,\n                napi_async_context* result);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_async_destroy(napi_env env, napi_async_context async_context);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_make_callback(napi_env env,\n                   napi_async_context async_context,\n                   napi_value recv,\n                   napi_value func,\n                   size_t argc,\n                   const napi_value* argv,\n                   napi_value* result);\n\n// Methods to provide node::Buffer functionality with napi types\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_buffer(napi_env env,\n                                                      size_t length,\n                                                      void** data,\n                                                      napi_value* result);\n#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_create_external_buffer(napi_env env,\n                            size_t length,\n                            void* data,\n                            node_api_basic_finalize finalize_cb,\n                            void* finalize_hint,\n                            napi_value* result);\n#endif  // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED\n\n#if NAPI_VERSION >= 10\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnode_api_create_buffer_from_arraybuffer(napi_env env,\n                                        napi_value arraybuffer,\n                                        size_t byte_offset,\n                                        size_t byte_length,\n                                        napi_value* result);\n#endif  // NAPI_VERSION >= 10\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env,\n                                                           size_t length,\n                                                           const void* data,\n                                                           void** result_data,\n                                                           napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_is_buffer(napi_env env,\n                                                  napi_value value,\n                                                  bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_buffer_info(napi_env env,\n                                                        napi_value value,\n                                                        void** data,\n                                                        size_t* length);\n\n// Methods to manage simple async operations\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_create_async_work(napi_env env,\n                       napi_value async_resource,\n                       napi_value async_resource_name,\n                       napi_async_execute_callback execute,\n                       napi_async_complete_callback complete,\n                       void* data,\n                       napi_async_work* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_delete_async_work(napi_env env,\n                                                          napi_async_work work);\nNAPI_EXTERN napi_status NAPI_CDECL napi_queue_async_work(node_api_basic_env env,\n                                                         napi_async_work work);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_cancel_async_work(node_api_basic_env env, napi_async_work work);\n\n// version management\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_node_version(\n    node_api_basic_env env, const napi_node_version** version);\n\n#if NAPI_VERSION >= 2\n\n// Return the current libuv event loop for a given environment\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_get_uv_event_loop(node_api_basic_env env, struct uv_loop_s** loop);\n\n#endif  // NAPI_VERSION >= 2\n\n#if NAPI_VERSION >= 3\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_fatal_exception(napi_env env,\n                                                        napi_value err);\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_add_env_cleanup_hook(\n    node_api_basic_env env, napi_cleanup_hook fun, void* arg);\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_remove_env_cleanup_hook(\n    node_api_basic_env env, napi_cleanup_hook fun, void* arg);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_open_callback_scope(napi_env env,\n                         napi_value resource_object,\n                         napi_async_context context,\n                         napi_callback_scope* result);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_close_callback_scope(napi_env env, napi_callback_scope scope);\n\n#endif  // NAPI_VERSION >= 3\n\n#if NAPI_VERSION >= 4\n\n// Calling into JS from other threads\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_create_threadsafe_function(napi_env env,\n                                napi_value func,\n                                napi_value async_resource,\n                                napi_value async_resource_name,\n                                size_t max_queue_size,\n                                size_t initial_thread_count,\n                                void* thread_finalize_data,\n                                napi_finalize thread_finalize_cb,\n                                void* context,\n                                napi_threadsafe_function_call_js call_js_cb,\n                                napi_threadsafe_function* result);\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_threadsafe_function_context(\n    napi_threadsafe_function func, void** result);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_call_threadsafe_function(napi_threadsafe_function func,\n                              void* data,\n                              napi_threadsafe_function_call_mode is_blocking);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_acquire_threadsafe_function(napi_threadsafe_function func);\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_release_threadsafe_function(\n    napi_threadsafe_function func, napi_threadsafe_function_release_mode mode);\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_unref_threadsafe_function(\n    node_api_basic_env env, napi_threadsafe_function func);\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_ref_threadsafe_function(\n    node_api_basic_env env, napi_threadsafe_function func);\n\n#endif  // NAPI_VERSION >= 4\n\n#if NAPI_VERSION >= 8\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_add_async_cleanup_hook(node_api_basic_env env,\n                            napi_async_cleanup_hook hook,\n                            void* arg,\n                            napi_async_cleanup_hook_handle* remove_handle);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle);\n\n#endif  // NAPI_VERSION >= 8\n\n#if NAPI_VERSION >= 9\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnode_api_get_module_file_name(node_api_basic_env env, const char** result);\n\n#endif  // NAPI_VERSION >= 9\n\nEXTERN_C_END\n\n#endif  // SRC_NODE_API_H_\n",
  "include/node_api_types.h": "#ifndef SRC_NODE_API_TYPES_H_\n#define SRC_NODE_API_TYPES_H_\n\n#include \"js_native_api_types.h\"\n\ntypedef napi_value(NAPI_CDECL* napi_addon_register_func)(napi_env env,\n                                                         napi_value exports);\n// False positive: https://github.com/cpplint/cpplint/issues/409\n// NOLINTNEXTLINE (readability/casting)\ntypedef int32_t(NAPI_CDECL* node_api_addon_get_api_version_func)(void);\n\ntypedef struct napi_callback_scope__* napi_callback_scope;\ntypedef struct napi_async_context__* napi_async_context;\ntypedef struct napi_async_work__* napi_async_work;\n\n#if NAPI_VERSION >= 3\ntypedef void(NAPI_CDECL* napi_cleanup_hook)(void* arg);\n#endif  // NAPI_VERSION >= 3\n\n#if NAPI_VERSION >= 4\ntypedef struct napi_threadsafe_function__* napi_threadsafe_function;\n#endif  // NAPI_VERSION >= 4\n\n#if NAPI_VERSION >= 4\ntypedef enum {\n  napi_tsfn_release,\n  napi_tsfn_abort\n} napi_threadsafe_function_release_mode;\n\ntypedef enum {\n  napi_tsfn_nonblocking,\n  napi_tsfn_blocking\n} napi_threadsafe_function_call_mode;\n#endif  // NAPI_VERSION >= 4\n\ntypedef void(NAPI_CDECL* napi_async_execute_callback)(napi_env env, void* data);\ntypedef void(NAPI_CDECL* napi_async_complete_callback)(napi_env env,\n                                                       napi_status status,\n                                                       void* data);\n#if NAPI_VERSION >= 4\ntypedef void(NAPI_CDECL* napi_threadsafe_function_call_js)(\n    napi_env env, napi_value js_callback, void* context, void* data);\n#endif  // NAPI_VERSION >= 4\n\ntypedef struct {\n  uint32_t major;\n  uint32_t minor;\n  uint32_t patch;\n  const char* release;\n} napi_node_version;\n\n#if NAPI_VERSION >= 8\ntypedef struct napi_async_cleanup_hook_handle__* napi_async_cleanup_hook_handle;\ntypedef void(NAPI_CDECL* napi_async_cleanup_hook)(\n    napi_async_cleanup_hook_handle handle, void* data);\n#endif  // NAPI_VERSION >= 8\n\n#endif  // SRC_NODE_API_TYPES_H_\n",
  "include/js_native_api.h": "#ifndef SRC_JS_NATIVE_API_H_\n#define SRC_JS_NATIVE_API_H_\n\n// This file needs to be compatible with C compilers.\n#include <stdbool.h>  // NOLINT(modernize-deprecated-headers)\n#include <stddef.h>   // NOLINT(modernize-deprecated-headers)\n\n#include \"js_native_api_types.h\"\n\n// If you need __declspec(dllimport), either include <node_api.h> instead, or\n// define NAPI_EXTERN as __declspec(dllimport) on the compiler's command line.\n#ifndef NAPI_EXTERN\n#ifdef _WIN32\n#define NAPI_EXTERN __declspec(dllexport)\n#elif defined(__wasm__)\n#define NAPI_EXTERN                                                            \\\n  __attribute__((visibility(\"default\")))                                       \\\n  __attribute__((__import_module__(\"napi\")))\n#else\n#define NAPI_EXTERN __attribute__((visibility(\"default\")))\n#endif\n#endif\n\n#define NAPI_AUTO_LENGTH SIZE_MAX\n\n#ifdef __cplusplus\n#define EXTERN_C_START extern \"C\" {\n#define EXTERN_C_END }\n#else\n#define EXTERN_C_START\n#define EXTERN_C_END\n#endif\n\nEXTERN_C_START\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_last_error_info(\n    node_api_basic_env env, const napi_extended_error_info** result);\n\n// Getters for defined singletons\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_undefined(napi_env env,\n                                                      napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_null(napi_env env,\n                                                 napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_global(napi_env env,\n                                                   napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_boolean(napi_env env,\n                                                    bool value,\n                                                    napi_value* result);\n\n// Methods to create Primitive types/Objects\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_object(napi_env env,\n                                                      napi_value* result);\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_array(napi_env env,\n                                                     napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_create_array_with_length(napi_env env, size_t length, napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_double(napi_env env,\n                                                      double value,\n                                                      napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_int32(napi_env env,\n                                                     int32_t value,\n                                                     napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_uint32(napi_env env,\n                                                      uint32_t value,\n                                                      napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_int64(napi_env env,\n                                                     int64_t value,\n                                                     napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_string_latin1(\n    napi_env env, const char* str, size_t length, napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_string_utf8(napi_env env,\n                                                           const char* str,\n                                                           size_t length,\n                                                           napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_string_utf16(napi_env env,\n                                                            const char16_t* str,\n                                                            size_t length,\n                                                            napi_value* result);\n#if NAPI_VERSION >= 10\nNAPI_EXTERN napi_status NAPI_CDECL node_api_create_external_string_latin1(\n    napi_env env,\n    char* str,\n    size_t length,\n    node_api_basic_finalize finalize_callback,\n    void* finalize_hint,\n    napi_value* result,\n    bool* copied);\nNAPI_EXTERN napi_status NAPI_CDECL\nnode_api_create_external_string_utf16(napi_env env,\n                                      char16_t* str,\n                                      size_t length,\n                                      node_api_basic_finalize finalize_callback,\n                                      void* finalize_hint,\n                                      napi_value* result,\n                                      bool* copied);\n\nNAPI_EXTERN napi_status NAPI_CDECL node_api_create_property_key_latin1(\n    napi_env env, const char* str, size_t length, napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL node_api_create_property_key_utf8(\n    napi_env env, const char* str, size_t length, napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL node_api_create_property_key_utf16(\n    napi_env env, const char16_t* str, size_t length, napi_value* result);\n#endif  // NAPI_VERSION >= 10\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_symbol(napi_env env,\n                                                      napi_value description,\n                                                      napi_value* result);\n#if NAPI_VERSION >= 9\nNAPI_EXTERN napi_status NAPI_CDECL\nnode_api_symbol_for(napi_env env,\n                    const char* utf8description,\n                    size_t length,\n                    napi_value* result);\n#endif  // NAPI_VERSION >= 9\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_function(napi_env env,\n                                                        const char* utf8name,\n                                                        size_t length,\n                                                        napi_callback cb,\n                                                        void* data,\n                                                        napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_error(napi_env env,\n                                                     napi_value code,\n                                                     napi_value msg,\n                                                     napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_type_error(napi_env env,\n                                                          napi_value code,\n                                                          napi_value msg,\n                                                          napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_range_error(napi_env env,\n                                                           napi_value code,\n                                                           napi_value msg,\n                                                           napi_value* result);\n#if NAPI_VERSION >= 9\nNAPI_EXTERN napi_status NAPI_CDECL node_api_create_syntax_error(\n    napi_env env, napi_value code, napi_value msg, napi_value* result);\n#endif  // NAPI_VERSION >= 9\n\n// Methods to get the native napi_value from Primitive type\nNAPI_EXTERN napi_status NAPI_CDECL napi_typeof(napi_env env,\n                                               napi_value value,\n                                               napi_valuetype* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_double(napi_env env,\n                                                         napi_value value,\n                                                         double* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_int32(napi_env env,\n                                                        napi_value value,\n                                                        int32_t* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_uint32(napi_env env,\n                                                         napi_value value,\n                                                         uint32_t* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_int64(napi_env env,\n                                                        napi_value value,\n                                                        int64_t* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bool(napi_env env,\n                                                       napi_value value,\n                                                       bool* result);\n\n// Copies LATIN-1 encoded bytes from a string into a buffer.\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_string_latin1(\n    napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result);\n\n// Copies UTF-8 encoded bytes from a string into a buffer.\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_string_utf8(\n    napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result);\n\n// Copies UTF-16 encoded bytes from a string into a buffer.\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_string_utf16(napi_env env,\n                                                               napi_value value,\n                                                               char16_t* buf,\n                                                               size_t bufsize,\n                                                               size_t* result);\n\n// Methods to coerce values\n// These APIs may execute user scripts\nNAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_bool(napi_env env,\n                                                       napi_value value,\n                                                       napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_number(napi_env env,\n                                                         napi_value value,\n                                                         napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_object(napi_env env,\n                                                         napi_value value,\n                                                         napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_string(napi_env env,\n                                                         napi_value value,\n                                                         napi_value* result);\n\n// Methods to work with Objects\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_prototype(napi_env env,\n                                                      napi_value object,\n                                                      napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_property_names(napi_env env,\n                                                           napi_value object,\n                                                           napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_set_property(napi_env env,\n                                                     napi_value object,\n                                                     napi_value key,\n                                                     napi_value value);\nNAPI_EXTERN napi_status NAPI_CDECL napi_has_property(napi_env env,\n                                                     napi_value object,\n                                                     napi_value key,\n                                                     bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_property(napi_env env,\n                                                     napi_value object,\n                                                     napi_value key,\n                                                     napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_delete_property(napi_env env,\n                                                        napi_value object,\n                                                        napi_value key,\n                                                        bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_has_own_property(napi_env env,\n                                                         napi_value object,\n                                                         napi_value key,\n                                                         bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_set_named_property(napi_env env,\n                                                           napi_value object,\n                                                           const char* utf8name,\n                                                           napi_value value);\nNAPI_EXTERN napi_status NAPI_CDECL napi_has_named_property(napi_env env,\n                                                           napi_value object,\n                                                           const char* utf8name,\n                                                           bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_named_property(napi_env env,\n                                                           napi_value object,\n                                                           const char* utf8name,\n                                                           napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_set_element(napi_env env,\n                                                    napi_value object,\n                                                    uint32_t index,\n                                                    napi_value value);\nNAPI_EXTERN napi_status NAPI_CDECL napi_has_element(napi_env env,\n                                                    napi_value object,\n                                                    uint32_t index,\n                                                    bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_element(napi_env env,\n                                                    napi_value object,\n                                                    uint32_t index,\n                                                    napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_delete_element(napi_env env,\n                                                       napi_value object,\n                                                       uint32_t index,\n                                                       bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_define_properties(napi_env env,\n                       napi_value object,\n                       size_t property_count,\n                       const napi_property_descriptor* properties);\n\n// Methods to work with Arrays\nNAPI_EXTERN napi_status NAPI_CDECL napi_is_array(napi_env env,\n                                                 napi_value value,\n                                                 bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_array_length(napi_env env,\n                                                         napi_value value,\n                                                         uint32_t* result);\n\n// Methods to compare values\nNAPI_EXTERN napi_status NAPI_CDECL napi_strict_equals(napi_env env,\n                                                      napi_value lhs,\n                                                      napi_value rhs,\n                                                      bool* result);\n\n// Methods to work with Functions\nNAPI_EXTERN napi_status NAPI_CDECL napi_call_function(napi_env env,\n                                                      napi_value recv,\n                                                      napi_value func,\n                                                      size_t argc,\n                                                      const napi_value* argv,\n                                                      napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_new_instance(napi_env env,\n                                                     napi_value constructor,\n                                                     size_t argc,\n                                                     const napi_value* argv,\n                                                     napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_instanceof(napi_env env,\n                                                   napi_value object,\n                                                   napi_value constructor,\n                                                   bool* result);\n\n// Methods to work with napi_callbacks\n\n// Gets all callback info in a single call. (Ugly, but faster.)\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_cb_info(\n    napi_env env,               // [in] Node-API environment handle\n    napi_callback_info cbinfo,  // [in] Opaque callback-info handle\n    size_t* argc,      // [in-out] Specifies the size of the provided argv array\n                       // and receives the actual count of args.\n    napi_value* argv,  // [out] Array of values\n    napi_value* this_arg,  // [out] Receives the JS 'this' arg for the call\n    void** data);          // [out] Receives the data pointer for the callback.\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_new_target(\n    napi_env env, napi_callback_info cbinfo, napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_define_class(napi_env env,\n                  const char* utf8name,\n                  size_t length,\n                  napi_callback constructor,\n                  void* data,\n                  size_t property_count,\n                  const napi_property_descriptor* properties,\n                  napi_value* result);\n\n// Methods to work with external data objects\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_wrap(napi_env env,\n          napi_value js_object,\n          void* native_object,\n          node_api_basic_finalize finalize_cb,\n          void* finalize_hint,\n          napi_ref* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_unwrap(napi_env env,\n                                               napi_value js_object,\n                                               void** result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_remove_wrap(napi_env env,\n                                                    napi_value js_object,\n                                                    void** result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_create_external(napi_env env,\n                     void* data,\n                     node_api_basic_finalize finalize_cb,\n                     void* finalize_hint,\n                     napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_external(napi_env env,\n                                                           napi_value value,\n                                                           void** result);\n\n// Methods to control object lifespan\n\n// Set initial_refcount to 0 for a weak reference, >0 for a strong reference.\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_create_reference(napi_env env,\n                      napi_value value,\n                      uint32_t initial_refcount,\n                      napi_ref* result);\n\n// Deletes a reference. The referenced value is released, and may\n// be GC'd unless there are other references to it.\nNAPI_EXTERN napi_status NAPI_CDECL napi_delete_reference(node_api_basic_env env,\n                                                         napi_ref ref);\n\n// Increments the reference count, optionally returning the resulting count.\n// After this call the  reference will be a strong reference because its\n// refcount is >0, and the referenced object is effectively \"pinned\".\n// Calling this when the refcount is 0 and the object is unavailable\n// results in an error.\nNAPI_EXTERN napi_status NAPI_CDECL napi_reference_ref(napi_env env,\n                                                      napi_ref ref,\n                                                      uint32_t* result);\n\n// Decrements the reference count, optionally returning the resulting count.\n// If the result is 0 the reference is now weak and the object may be GC'd\n// at any time if there are no other references. Calling this when the\n// refcount is already 0 results in an error.\nNAPI_EXTERN napi_status NAPI_CDECL napi_reference_unref(napi_env env,\n                                                        napi_ref ref,\n                                                        uint32_t* result);\n\n// Attempts to get a referenced value. If the reference is weak,\n// the value might no longer be available, in that case the call\n// is still successful but the result is NULL.\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_reference_value(napi_env env,\n                                                            napi_ref ref,\n                                                            napi_value* result);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_open_handle_scope(napi_env env, napi_handle_scope* result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_close_handle_scope(napi_env env, napi_handle_scope scope);\nNAPI_EXTERN napi_status NAPI_CDECL napi_open_escapable_handle_scope(\n    napi_env env, napi_escapable_handle_scope* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_close_escapable_handle_scope(\n    napi_env env, napi_escapable_handle_scope scope);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_escape_handle(napi_env env,\n                   napi_escapable_handle_scope scope,\n                   napi_value escapee,\n                   napi_value* result);\n\n// Methods to support error handling\nNAPI_EXTERN napi_status NAPI_CDECL napi_throw(napi_env env, napi_value error);\nNAPI_EXTERN napi_status NAPI_CDECL napi_throw_error(napi_env env,\n                                                    const char* code,\n                                                    const char* msg);\nNAPI_EXTERN napi_status NAPI_CDECL napi_throw_type_error(napi_env env,\n                                                         const char* code,\n                                                         const char* msg);\nNAPI_EXTERN napi_status NAPI_CDECL napi_throw_range_error(napi_env env,\n                                                          const char* code,\n                                                          const char* msg);\n#if NAPI_VERSION >= 9\nNAPI_EXTERN napi_status NAPI_CDECL node_api_throw_syntax_error(napi_env env,\n                                                               const char* code,\n                                                               const char* msg);\n#endif  // NAPI_VERSION >= 9\nNAPI_EXTERN napi_status NAPI_CDECL napi_is_error(napi_env env,\n                                                 napi_value value,\n                                                 bool* result);\n\n// Methods to support catching exceptions\nNAPI_EXTERN napi_status NAPI_CDECL napi_is_exception_pending(napi_env env,\n                                                             bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_get_and_clear_last_exception(napi_env env, napi_value* result);\n\n// Methods to work with array buffers and typed arrays\nNAPI_EXTERN napi_status NAPI_CDECL napi_is_arraybuffer(napi_env env,\n                                                       napi_value value,\n                                                       bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_arraybuffer(napi_env env,\n                                                           size_t byte_length,\n                                                           void** data,\n                                                           napi_value* result);\n#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_create_external_arraybuffer(napi_env env,\n                                 void* external_data,\n                                 size_t byte_length,\n                                 node_api_basic_finalize finalize_cb,\n                                 void* finalize_hint,\n                                 napi_value* result);\n#endif  // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_arraybuffer_info(\n    napi_env env, napi_value arraybuffer, void** data, size_t* byte_length);\nNAPI_EXTERN napi_status NAPI_CDECL napi_is_typedarray(napi_env env,\n                                                      napi_value value,\n                                                      bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_create_typedarray(napi_env env,\n                       napi_typedarray_type type,\n                       size_t length,\n                       napi_value arraybuffer,\n                       size_t byte_offset,\n                       napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_get_typedarray_info(napi_env env,\n                         napi_value typedarray,\n                         napi_typedarray_type* type,\n                         size_t* length,\n                         void** data,\n                         napi_value* arraybuffer,\n                         size_t* byte_offset);\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_dataview(napi_env env,\n                                                        size_t length,\n                                                        napi_value arraybuffer,\n                                                        size_t byte_offset,\n                                                        napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_is_dataview(napi_env env,\n                                                    napi_value value,\n                                                    bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_get_dataview_info(napi_env env,\n                       napi_value dataview,\n                       size_t* bytelength,\n                       void** data,\n                       napi_value* arraybuffer,\n                       size_t* byte_offset);\n\n\n// version management\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_version(node_api_basic_env env,\n                                                    uint32_t* result);\n\n// Promises\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_promise(napi_env env,\n                                                       napi_deferred* deferred,\n                                                       napi_value* promise);\nNAPI_EXTERN napi_status NAPI_CDECL napi_resolve_deferred(napi_env env,\n                                                         napi_deferred deferred,\n                                                         napi_value resolution);\nNAPI_EXTERN napi_status NAPI_CDECL napi_reject_deferred(napi_env env,\n                                                        napi_deferred deferred,\n                                                        napi_value rejection);\nNAPI_EXTERN napi_status NAPI_CDECL napi_is_promise(napi_env env,\n                                                   napi_value value,\n                                                   bool* is_promise);\n\n// Running a script\nNAPI_EXTERN napi_status NAPI_CDECL napi_run_script(napi_env env,\n                                                   napi_value script,\n                                                   napi_value* result);\n\n// Memory management\nNAPI_EXTERN napi_status NAPI_CDECL napi_adjust_external_memory(\n    node_api_basic_env env, int64_t change_in_bytes, int64_t* adjusted_value);\n\n#if NAPI_VERSION >= 5\n\n// Dates\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_date(napi_env env,\n                                                    double time,\n                                                    napi_value* result);\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_is_date(napi_env env,\n                                                napi_value value,\n                                                bool* is_date);\n\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_date_value(napi_env env,\n                                                       napi_value value,\n                                                       double* result);\n\n// Add finalizer for pointer\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_add_finalizer(napi_env env,\n                   napi_value js_object,\n                   void* finalize_data,\n                   node_api_basic_finalize finalize_cb,\n                   void* finalize_hint,\n                   napi_ref* result);\n\n#endif  // NAPI_VERSION >= 5\n\n\n#if NAPI_VERSION >= 6\n\n// BigInt\nNAPI_EXTERN napi_status NAPI_CDECL napi_create_bigint_int64(napi_env env,\n                                                            int64_t value,\n                                                            napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_create_bigint_uint64(napi_env env, uint64_t value, napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_create_bigint_words(napi_env env,\n                         int sign_bit,\n                         size_t word_count,\n                         const uint64_t* words,\n                         napi_value* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bigint_int64(napi_env env,\n                                                               napi_value value,\n                                                               int64_t* result,\n                                                               bool* lossless);\nNAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bigint_uint64(\n    napi_env env, napi_value value, uint64_t* result, bool* lossless);\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_get_value_bigint_words(napi_env env,\n                            napi_value value,\n                            int* sign_bit,\n                            size_t* word_count,\n                            uint64_t* words);\n\n// Object\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_get_all_property_names(napi_env env,\n                            napi_value object,\n                            napi_key_collection_mode key_mode,\n                            napi_key_filter key_filter,\n                            napi_key_conversion key_conversion,\n                            napi_value* result);\n\n// Instance data\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_set_instance_data(node_api_basic_env env,\n                       void* data,\n                       napi_finalize finalize_cb,\n                       void* finalize_hint);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_get_instance_data(node_api_basic_env env, void** data);\n#endif  // NAPI_VERSION >= 6\n\n#if NAPI_VERSION >= 7\n// ArrayBuffer detaching\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_detach_arraybuffer(napi_env env, napi_value arraybuffer);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_is_detached_arraybuffer(napi_env env, napi_value value, bool* result);\n#endif  // NAPI_VERSION >= 7\n\n#if NAPI_VERSION >= 8\n// Type tagging\nNAPI_EXTERN napi_status NAPI_CDECL napi_type_tag_object(\n    napi_env env, napi_value value, const napi_type_tag* type_tag);\n\nNAPI_EXTERN napi_status NAPI_CDECL\nnapi_check_object_type_tag(napi_env env,\n                           napi_value value,\n                           const napi_type_tag* type_tag,\n                           bool* result);\nNAPI_EXTERN napi_status NAPI_CDECL napi_object_freeze(napi_env env,\n                                                      napi_value object);\nNAPI_EXTERN napi_status NAPI_CDECL napi_object_seal(napi_env env,\n                                                    napi_value object);\n#endif  // NAPI_VERSION >= 8\n\nEXTERN_C_END\n\n#endif  // SRC_JS_NATIVE_API_H_\n",
  "include/js_native_api_types.h": "#ifndef SRC_JS_NATIVE_API_TYPES_H_\n#define SRC_JS_NATIVE_API_TYPES_H_\n\n// Use INT_MAX, this should only be consumed by the pre-processor anyway.\n#define NAPI_VERSION_EXPERIMENTAL 2147483647\n#ifndef NAPI_VERSION\n// The baseline version for Node-API.\n// NAPI_VERSION controls which version is used by default when compiling\n// a native addon. If the addon developer wants to use functions from a\n// newer Node-API version not yet available in all LTS versions, they can\n// set NAPI_VERSION to explicitly depend on that version.\n#define NAPI_VERSION 8\n#endif\n\n\n// This file needs to be compatible with C compilers.\n// This is a public include file, and these includes have essentially\n// become part of its API.\n#include <stddef.h>  // NOLINT(modernize-deprecated-headers)\n#include <stdint.h>  // NOLINT(modernize-deprecated-headers)\n\n#if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900)\ntypedef uint16_t char16_t;\n#endif\n\n#ifndef NAPI_CDECL\n#ifdef _WIN32\n#define NAPI_CDECL __cdecl\n#else\n#define NAPI_CDECL\n#endif\n#endif\n\n// JSVM API types are all opaque pointers for ABI stability\n// typedef undefined structs instead of void* for compile time type safety\ntypedef struct napi_env__* napi_env;\n\n// We need to mark APIs which can be called during garbage collection (GC),\n// meaning that they do not affect the state of the JS engine, and can\n// therefore be called synchronously from a finalizer that itself runs\n// synchronously during GC. Such APIs can receive either a `napi_env` or a\n// `node_api_basic_env` as their first parameter, because we should be able to\n// also call them during normal, non-garbage-collecting operations, whereas\n// APIs that affect the state of the JS engine can only receive a `napi_env` as\n// their first parameter, because we must not call them during GC. In lieu of\n// inheritance, we use the properties of the const qualifier to accomplish\n// this, because both a const and a non-const value can be passed to an API\n// expecting a const value, but only a non-const value can be passed to an API\n// expecting a non-const value.\n//\n// In conjunction with appropriate CFLAGS to warn us if we're passing a const\n// (basic) environment into an API that expects a non-const environment, and\n// the definition of basic finalizer function pointer types below, which\n// receive a basic environment as their first parameter, and can thus only call\n// basic APIs (unless the user explicitly casts the environment), we achieve\n// the ability to ensure at compile time that we do not call APIs that affect\n// the state of the JS engine from a synchronous (basic) finalizer.\ntypedef struct napi_env__* node_api_nogc_env;\ntypedef node_api_nogc_env node_api_basic_env;\n\ntypedef struct napi_value__* napi_value;\ntypedef struct napi_ref__* napi_ref;\ntypedef struct napi_handle_scope__* napi_handle_scope;\ntypedef struct napi_escapable_handle_scope__* napi_escapable_handle_scope;\ntypedef struct napi_callback_info__* napi_callback_info;\ntypedef struct napi_deferred__* napi_deferred;\n\ntypedef enum {\n  napi_default = 0,\n  napi_writable = 1 << 0,\n  napi_enumerable = 1 << 1,\n  napi_configurable = 1 << 2,\n\n  // Used with napi_define_class to distinguish static properties\n  // from instance properties. Ignored by napi_define_properties.\n  napi_static = 1 << 10,\n\n#if NAPI_VERSION >= 8\n  // Default for class methods.\n  napi_default_method = napi_writable | napi_configurable,\n\n  // Default for object properties, like in JS obj[prop].\n  napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable,\n#endif  // NAPI_VERSION >= 8\n} napi_property_attributes;\n\ntypedef enum {\n  // ES6 types (corresponds to typeof)\n  napi_undefined,\n  napi_null,\n  napi_boolean,\n  napi_number,\n  napi_string,\n  napi_symbol,\n  napi_object,\n  napi_function,\n  napi_external,\n  napi_bigint,\n} napi_valuetype;\n\ntypedef enum {\n  napi_int8_array,\n  napi_uint8_array,\n  napi_uint8_clamped_array,\n  napi_int16_array,\n  napi_uint16_array,\n  napi_int32_array,\n  napi_uint32_array,\n  napi_float32_array,\n  napi_float64_array,\n  napi_bigint64_array,\n  napi_biguint64_array,\n#define NODE_API_HAS_FLOAT16_ARRAY\n  napi_float16_array,\n} napi_typedarray_type;\n\ntypedef enum {\n  napi_ok,\n  napi_invalid_arg,\n  napi_object_expected,\n  napi_string_expected,\n  napi_name_expected,\n  napi_function_expected,\n  napi_number_expected,\n  napi_boolean_expected,\n  napi_array_expected,\n  napi_generic_failure,\n  napi_pending_exception,\n  napi_cancelled,\n  napi_escape_called_twice,\n  napi_handle_scope_mismatch,\n  napi_callback_scope_mismatch,\n  napi_queue_full,\n  napi_closing,\n  napi_bigint_expected,\n  napi_date_expected,\n  napi_arraybuffer_expected,\n  napi_detachable_arraybuffer_expected,\n  napi_would_deadlock,  // unused\n  napi_no_external_buffers_allowed,\n  napi_cannot_run_js,\n} napi_status;\n// Note: when adding a new enum value to `napi_status`, please also update\n//   * `const int last_status` in the definition of `napi_get_last_error_info()'\n//     in file js_native_api_v8.cc.\n//   * `const char* error_messages[]` in file js_native_api_v8.cc with a brief\n//     message explaining the error.\n//   * the definition of `napi_status` in doc/api/n-api.md to reflect the newly\n//     added value(s).\n\ntypedef napi_value(NAPI_CDECL* napi_callback)(napi_env env,\n                                              napi_callback_info info);\ntypedef void(NAPI_CDECL* napi_finalize)(napi_env env,\n                                        void* finalize_data,\n                                        void* finalize_hint);\n\ntypedef napi_finalize node_api_nogc_finalize;\ntypedef node_api_nogc_finalize node_api_basic_finalize;\n\n// A finalizer that can be called from any thread and at any time.\ntypedef void(NAPI_CDECL* node_api_noenv_finalize)(void* finalize_data,\n                                                  void* finalize_hint);\n\ntypedef struct {\n  // One of utf8name or name should be NULL.\n  const char* utf8name;\n  napi_value name;\n\n  napi_callback method;\n  napi_callback getter;\n  napi_callback setter;\n  napi_value value;\n\n  napi_property_attributes attributes;\n  void* data;\n} napi_property_descriptor;\n\ntypedef struct {\n  const char* error_message;\n  void* engine_reserved;\n  uint32_t engine_error_code;\n  napi_status error_code;\n} napi_extended_error_info;\n\n#if NAPI_VERSION >= 6\ntypedef enum {\n  napi_key_include_prototypes,\n  napi_key_own_only\n} napi_key_collection_mode;\n\ntypedef enum {\n  napi_key_all_properties = 0,\n  napi_key_writable = 1,\n  napi_key_enumerable = 1 << 1,\n  napi_key_configurable = 1 << 2,\n  napi_key_skip_strings = 1 << 3,\n  napi_key_skip_symbols = 1 << 4\n} napi_key_filter;\n\ntypedef enum {\n  napi_key_keep_numbers,\n  napi_key_numbers_to_strings\n} napi_key_conversion;\n#endif  // NAPI_VERSION >= 6\n\n#if NAPI_VERSION >= 8\ntypedef struct {\n  uint64_t lower;\n  uint64_t upper;\n} napi_type_tag;\n#endif  // NAPI_VERSION >= 8\n\n#endif  // SRC_JS_NATIVE_API_TYPES_H_\n",
  "node_api.def": "NAME NODE.EXE\nEXPORTS\nnapi_async_destroy\nnapi_async_init\nnapi_cancel_async_work\nnapi_create_async_work\nnapi_create_buffer\nnapi_create_buffer_copy\nnapi_create_external_buffer\nnapi_delete_async_work\nnapi_fatal_error\nnapi_get_buffer_info\nnapi_get_node_version\nnapi_is_buffer\nnapi_make_callback\nnapi_module_register\nnapi_queue_async_work\nnapi_adjust_external_memory\nnapi_call_function\nnapi_close_escapable_handle_scope\nnapi_close_handle_scope\nnapi_coerce_to_bool\nnapi_coerce_to_number\nnapi_coerce_to_object\nnapi_coerce_to_string\nnapi_create_array\nnapi_create_array_with_length\nnapi_create_arraybuffer\nnapi_create_dataview\nnapi_create_double\nnapi_create_error\nnapi_create_external\nnapi_create_external_arraybuffer\nnapi_create_function\nnapi_create_int32\nnapi_create_int64\nnapi_create_object\nnapi_create_promise\nnapi_create_range_error\nnapi_create_reference\nnapi_create_string_latin1\nnapi_create_string_utf16\nnapi_create_string_utf8\nnapi_create_symbol\nnapi_create_type_error\nnapi_create_typedarray\nnapi_create_uint32\nnapi_define_class\nnapi_define_properties\nnapi_delete_element\nnapi_delete_property\nnapi_delete_reference\nnapi_escape_handle\nnapi_get_and_clear_last_exception\nnapi_get_array_length\nnapi_get_arraybuffer_info\nnapi_get_boolean\nnapi_get_cb_info\nnapi_get_dataview_info\nnapi_get_element\nnapi_get_global\nnapi_get_last_error_info\nnapi_get_named_property\nnapi_get_new_target\nnapi_get_null\nnapi_get_property\nnapi_get_property_names\nnapi_get_prototype\nnapi_get_reference_value\nnapi_get_typedarray_info\nnapi_get_undefined\nnapi_get_value_bool\nnapi_get_value_double\nnapi_get_value_external\nnapi_get_value_int32\nnapi_get_value_int64\nnapi_get_value_string_latin1\nnapi_get_value_string_utf16\nnapi_get_value_string_utf8\nnapi_get_value_uint32\nnapi_get_version\nnapi_has_element\nnapi_has_named_property\nnapi_has_own_property\nnapi_has_property\nnapi_instanceof\nnapi_is_array\nnapi_is_arraybuffer\nnapi_is_dataview\nnapi_is_error\nnapi_is_exception_pending\nnapi_is_promise\nnapi_is_typedarray\nnapi_new_instance\nnapi_open_escapable_handle_scope\nnapi_open_handle_scope\nnapi_reference_ref\nnapi_reference_unref\nnapi_reject_deferred\nnapi_remove_wrap\nnapi_resolve_deferred\nnapi_run_script\nnapi_set_element\nnapi_set_named_property\nnapi_set_property\nnapi_strict_equals\nnapi_throw\nnapi_throw_error\nnapi_throw_range_error\nnapi_throw_type_error\nnapi_typeof\nnapi_unwrap\nnapi_wrap\nnapi_get_uv_event_loop\nnapi_add_env_cleanup_hook\nnapi_close_callback_scope\nnapi_fatal_exception\nnapi_open_callback_scope\nnapi_remove_env_cleanup_hook\nnapi_acquire_threadsafe_function\nnapi_call_threadsafe_function\nnapi_create_threadsafe_function\nnapi_get_threadsafe_function_context\nnapi_ref_threadsafe_function\nnapi_release_threadsafe_function\nnapi_unref_threadsafe_function\nnapi_add_finalizer\nnapi_create_date\nnapi_get_date_value\nnapi_is_date\nnapi_create_bigint_int64\nnapi_create_bigint_uint64\nnapi_create_bigint_words\nnapi_get_all_property_names\nnapi_get_instance_data\nnapi_get_value_bigint_int64\nnapi_get_value_bigint_uint64\nnapi_get_value_bigint_words\nnapi_set_instance_data\nnapi_detach_arraybuffer\nnapi_is_detached_arraybuffer\nnapi_add_async_cleanup_hook\nnapi_remove_async_cleanup_hook\nnapi_check_object_type_tag\nnapi_object_freeze\nnapi_object_seal\nnapi_type_tag_object\nnode_api_get_module_file_name\nnode_api_create_syntax_error\nnode_api_symbol_for\nnode_api_throw_syntax_error\nnode_api_create_buffer_from_arraybuffer\nnode_api_create_external_string_latin1\nnode_api_create_external_string_utf16\nnode_api_create_property_key_latin1\nnode_api_create_property_key_utf16\nnode_api_create_property_key_utf8",
  "NODE-API-LICENSE": "MIT License\n\nCopyright (c) 2021 Node.js\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n",
  "template/napi_error.mbt": "///|\npub(all) enum NapiErrorKind {\n  Error\n  TypeError\n  RangeError\n}\n\n///|\npub(all) suberror NapiError {\n  JavaScriptException(NapiReference)\n  ApiError(\n    status~ : Int32,\n    kind~ : NapiErrorKind,\n    code~ : String,\n    context~ : String\n  )\n}\n\n///|\nfn napi_failure(\n  status : Int32,\n  kind : NapiErrorKind,\n  code : String,\n  context : String,\n) -> NapiError {\n  ApiError(status~, kind~, code~, context~)\n}\n\n///|\nfn result_from_status(\n  env : NapiEnv,\n  status : Int32,\n  msg : Bytes,\n) -> Unit raise NapiError {\n  if status != 0 {\n    if status == 10 {\n      let value = Ref(moonbit_dummy_napi_value())\n      let captured = napi_take_exception(env, value)\n      if captured == 0 {\n        raise JavaScriptException(NapiReference::new(env, value.val))\n      }\n    }\n    match status {\n      1004 =>\n        raise napi_failure(\n          status,\n          Error,\n          \"ERR_NAPI_ENV_CLOSED\",\n          \"JavaScript environment has closed\",\n        )\n      1005 =>\n        raise napi_failure(\n          status,\n          Error,\n          \"ERR_NAPI_ENVIRONMENT\",\n          \"Value belongs to a different JavaScript environment or thread\",\n        )\n      1006 =>\n        raise napi_failure(\n          status,\n          Error,\n          \"ERR_NAPI_DISPOSED\",\n          \"Native resource has been disposed\",\n        )\n      1007 =>\n        raise napi_failure(\n          status,\n          TypeError,\n          \"ERR_NAPI_RECEIVER\",\n          \"Invalid receiver or native type\",\n        )\n      _ => ()\n    }\n    if status == 1001 {\n      raise napi_failure(\n        status,\n        Error,\n        \"ERR_NAPI_BUFFER_DETACHED\",\n        \"Buffer backing store is detached\",\n      )\n    }\n    if status == 1002 {\n      raise napi_failure(\n        status,\n        RangeError,\n        \"ERR_NAPI_SIZE\",\n        \"Value exceeds MoonBit's supported size\",\n      )\n    }\n    let kind = match status {\n      1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 17 | 18 | 19 | 20 => TypeError\n      _ => Error\n    }\n    let code = if kind is TypeError {\n      \"ERR_NAPI_TYPE\"\n    } else if status == 10 {\n      \"ERR_NAPI_PENDING_EXCEPTION\"\n    } else {\n      \"ERR_NAPI_STATUS\"\n    }\n    raise napi_failure(status, kind, code, @utf8.decode_lossy(msg))\n  }\n}\n\n///|\n#borrow(code, context)\nextern \"c\" fn throw_napi_error_raw(\n  env : NapiEnv,\n  status : Int32,\n  kind : Int32,\n  code : Bytes,\n  context : Bytes,\n) -> Int32 = \"moonbit_napi_throw\"\n\n///|\n// The only JS exception boundary. The C side leaves an existing pending\n// exception intact, including errors originating from reentrant JS callbacks.\nfn throw_napi_error(env : NapiEnv, error : Error) -> Unit {\n  if error is NapiError::JavaScriptException(reference) {\n    let status = napi_reference_throw(env, reference)\n    if status == 0 || status == 10 {\n      return\n    }\n    ignore(\n      throw_napi_error_raw(\n        env, status, 0, b\"ERR_NAPI_ENVIRONMENT\", b\"Restoring JavaScript exception\",\n      ),\n    )\n    return\n  }\n  let (status, kind, code, context) = match error {\n    NapiError::ApiError(status~, kind~, code~, context~) =>\n      (status, kind, code, context)\n    _ => (9, Error, \"ERR_NAPI_CALL\", error.to_string())\n  }\n  let kind = match kind {\n    Error => 0\n    TypeError => 1\n    RangeError => 2\n  }\n  // A throwing Node-API operation may itself report pending_exception. The\n  // adapter must return NULL on either result; it cannot resume user code.\n  ignore(\n    throw_napi_error_raw(\n      env,\n      status,\n      kind,\n      @utf8.encode(code),\n      @utf8.encode(context),\n    ),\n  )\n}\n",
  "template/napi_runtime_version.mbt": "///|\npub const NAPI_MBT_RUNTIME_VERSION : String = \"13\"\n",
  "runtime-version": "13",
  "template/napi_values.mbt": "///|\n#borrow(result)\nextern \"c\" fn napi_value_kind(\n  env : NapiEnv,\n  value : NapiValue,\n  result : Ref[Int32],\n) -> Int32 = \"moonbit_napi_value_kind\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_value_same(\n  env : NapiEnv,\n  a : NapiValue,\n  b : NapiValue,\n  result : Ref[Bool],\n) -> Int32 = \"moonbit_napi_value_same\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_null_raw(env : NapiEnv, result : Ref[NapiValue]) -> Int32 = \"napi_get_null\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_array_length_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  result : Ref[Int32],\n) -> Int32 = \"moonbit_napi_array_length\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_array_new_raw(\n  env : NapiEnv,\n  length : Int32,\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_array_new\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_array_get_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  index : Int32,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_get_element\"\n\n///|\nextern \"c\" fn napi_array_set_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  index : Int32,\n  element : NapiValue,\n) -> Int32 = \"napi_set_element\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_object_new_raw(\n  env : NapiEnv,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_create_object\"\n\n///|\n#borrow(name, result)\nextern \"c\" fn napi_field_get_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  name : Bytes,\n  required : Bool,\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_field_get\"\n\n///|\n#borrow(name)\nextern \"c\" fn napi_field_set_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  name : Bytes,\n  field : NapiValue,\n) -> Int32 = \"moonbit_napi_field_set\"\n\n///|\npub fn napi_is_nullish(\n  env : NapiEnv,\n  value : NapiValue,\n) -> Bool raise NapiError {\n  let kind = Ref(0)\n  result_from_status(\n    env,\n    napi_value_kind(env, value, kind),\n    b\"Inspecting optional value\",\n  )\n  kind.val == 0 || kind.val == 1\n}\n\n///|\npub fn napi_require_object(\n  env : NapiEnv,\n  value : NapiValue,\n) -> Unit raise NapiError {\n  let kind = Ref(0)\n  result_from_status(\n    env,\n    napi_value_kind(env, value, kind),\n    b\"Inspecting object\",\n  )\n  if kind.val != 6 {\n    raise napi_failure(2, TypeError, \"ERR_NAPI_TYPE\", \"Expected object\")\n  }\n}\n\n///|\npub fn napi_null(env : NapiEnv) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(env, napi_null_raw(env, result), b\"Creating null\")\n  result.val\n}\n\n///|\n// A path belongs to one synchronous conversion, including reentrant getters.\n// Repeated references in different branches are allowed; actual cycles are not.\npub fn napi_enter_value(\n  env : NapiEnv,\n  value : NapiValue,\n  path : Array[NapiValue],\n) -> Unit raise NapiError {\n  if path.length() >= 256 {\n    raise napi_failure(\n      1,\n      RangeError,\n      \"ERR_NAPI_DEPTH\",\n      \"Structured value exceeds maximum nesting depth\",\n    )\n  }\n  for ancestor in path {\n    let same = Ref(false)\n    result_from_status(\n      env,\n      napi_value_same(env, ancestor, value, same),\n      b\"Checking structured value identity\",\n    )\n    if same.val {\n      raise napi_failure(\n        1,\n        TypeError,\n        \"ERR_NAPI_CYCLE\",\n        \"Cyclic JavaScript input\",\n      )\n    }\n  }\n  path.push(value)\n}\n\n///|\npub fn napi_check_depth(depth : Int32) -> Unit raise NapiError {\n  if depth >= 256 {\n    raise napi_failure(\n      1,\n      RangeError,\n      \"ERR_NAPI_DEPTH\",\n      \"Structured result exceeds maximum nesting depth\",\n    )\n  }\n}\n\n///|\npub fn napi_array_length(\n  env : NapiEnv,\n  value : NapiValue,\n) -> Int32 raise NapiError {\n  let result = Ref(0)\n  result_from_status(\n    env,\n    napi_array_length_raw(env, value, result),\n    b\"Expected Array\",\n  )\n  result.val\n}\n\n///|\npub fn napi_array_new(\n  env : NapiEnv,\n  length : Int32,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_array_new_raw(env, length, result),\n    b\"Creating Array\",\n  )\n  result.val\n}\n\n///|\npub fn napi_array_get(\n  env : NapiEnv,\n  value : NapiValue,\n  index : Int32,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_array_get_raw(env, value, index, result),\n    b\"Reading Array element\",\n  )\n  result.val\n}\n\n///|\npub fn napi_array_set(\n  env : NapiEnv,\n  value : NapiValue,\n  index : Int32,\n  element : NapiValue,\n) -> Unit raise NapiError {\n  result_from_status(\n    env,\n    napi_array_set_raw(env, value, index, element),\n    b\"Writing Array element\",\n  )\n}\n\n///|\npub fn napi_object_new(env : NapiEnv) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(env, napi_object_new_raw(env, result), b\"Creating Object\")\n  result.val\n}\n\n///|\npub fn napi_field_get(\n  env : NapiEnv,\n  value : NapiValue,\n  name : String,\n  required : Bool,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  let status = napi_field_get_raw(\n    env,\n    value,\n    @utf8.encode(name),\n    required,\n    result,\n  )\n  if status == 1003 {\n    raise napi_failure(\n      status,\n      TypeError,\n      \"ERR_NAPI_FIELD\",\n      \"Missing required field: \" + name,\n    )\n  }\n  result_from_status(env, status, @utf8.encode(\"Reading field: \" + name))\n  result.val\n}\n\n///|\npub fn napi_field_set(\n  env : NapiEnv,\n  value : NapiValue,\n  name : String,\n  field : NapiValue,\n) -> Unit raise NapiError {\n  result_from_status(\n    env,\n    napi_field_set_raw(env, value, @utf8.encode(name), field),\n    @utf8.encode(\"Writing field: \" + name),\n  )\n}\n\n///|\npub fn get_Int64(env : NapiEnv, value : NapiValue) -> Int64 raise NapiError {\n  let (result, lossless) = napi_bigint_to_int64(env, value)\n  if !lossless {\n    raise napi_failure(\n      1,\n      RangeError,\n      \"ERR_NAPI_BIGINT_RANGE\",\n      \"BigInt does not fit Int64 losslessly\",\n    )\n  }\n  result\n}\n\n///|\npub fn get_UInt64(env : NapiEnv, value : NapiValue) -> UInt64 raise NapiError {\n  let (result, lossless) = napi_bigint_to_uint64(env, value)\n  if !lossless {\n    raise napi_failure(\n      1,\n      RangeError,\n      \"ERR_NAPI_BIGINT_RANGE\",\n      \"BigInt does not fit UInt64 losslessly\",\n    )\n  }\n  result\n}\n\n///|\npub fn set_Int64(env : NapiEnv, value : Int64) -> NapiValue raise NapiError {\n  napi_bigint_from_int64(env, value)\n}\n\n///|\npub fn set_UInt64(env : NapiEnv, value : UInt64) -> NapiValue raise NapiError {\n  napi_bigint_from_uint64(env, value)\n}\n",
  "template/napi_lifecycle.h": "/* Runtime implementation, included once by stub.c. All MoonBit execution and\n * reference counting runs under this recursive lock, on a Node environment\n * thread. Background I/O code must not use these entry points. */\n#ifndef NAPI_MBT_LIFECYCLE_H\n#define NAPI_MBT_LIFECYCLE_H\n#include <stdlib.h>\n#if defined(_WIN32)\n#include <windows.h>\nstatic INIT_ONCE napi_mbt_once = INIT_ONCE_STATIC_INIT;\nstatic CRITICAL_SECTION napi_mbt_mutex;\nstatic BOOL CALLBACK napi_mbt_init_mutex(PINIT_ONCE once, PVOID parameter, PVOID *context) {\n    (void)once; (void)parameter; (void)context;\n    InitializeCriticalSection(&napi_mbt_mutex);\n    return TRUE;\n}\nstatic void napi_mbt_lock(void) {\n    InitOnceExecuteOnce(&napi_mbt_once, napi_mbt_init_mutex, NULL, NULL);\n    EnterCriticalSection(&napi_mbt_mutex);\n}\nstatic void napi_mbt_unlock(void) { LeaveCriticalSection(&napi_mbt_mutex); }\ntypedef DWORD napi_mbt_thread;\nstatic napi_mbt_thread napi_mbt_thread_self(void) { return GetCurrentThreadId(); }\nstatic bool napi_mbt_same_thread(napi_mbt_thread a, napi_mbt_thread b) { return a == b; }\n#else\n#include <pthread.h>\nstatic pthread_once_t napi_mbt_once = PTHREAD_ONCE_INIT;\nstatic pthread_mutex_t napi_mbt_mutex;\nstatic void napi_mbt_init_mutex(void) {\n    pthread_mutexattr_t attr;\n    pthread_mutexattr_init(&attr);\n    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);\n    pthread_mutex_init(&napi_mbt_mutex, &attr);\n    pthread_mutexattr_destroy(&attr);\n}\nstatic void napi_mbt_lock(void) { pthread_once(&napi_mbt_once, napi_mbt_init_mutex); pthread_mutex_lock(&napi_mbt_mutex); }\nstatic void napi_mbt_unlock(void) { pthread_mutex_unlock(&napi_mbt_mutex); }\ntypedef pthread_t napi_mbt_thread;\nstatic napi_mbt_thread napi_mbt_thread_self(void) { return pthread_self(); }\nstatic bool napi_mbt_same_thread(napi_mbt_thread a, napi_mbt_thread b) { return pthread_equal(a, b) != 0; }\n#endif\n\ntypedef struct napi_mbt_environment napi_mbt_environment;\ntypedef struct napi_mbt_resource napi_mbt_resource;\ntypedef struct napi_mbt_arguments napi_mbt_arguments;\ntypedef struct napi_mbt_construction {\n    int type;\n    void *payload;\n    bool claimed;\n    struct napi_mbt_construction *previous;\n} napi_mbt_construction;\nstruct napi_mbt_resource {\n    napi_mbt_environment *owner;\n    napi_mbt_resource *next;\n    napi_mbt_resource *previous;\n    void *payload;\n    napi_ref reference;\n    napi_value (*trampoline)(void *, napi_env, napi_mbt_arguments *);\n    int kind;\n    int type;\n    bool disposed;\n    bool pending_free;\n    bool disposing;\n    bool inert_on_dispose;\n};\nstruct napi_mbt_environment {\n    napi_env env;\n    napi_mbt_thread thread;\n    napi_mbt_environment *next;\n    napi_mbt_resource *resources;\n    bool closing;\n    int cleanup_status;\n    napi_mbt_construction *construction;\n    void *async_context;\n    void (*async_close)(napi_mbt_environment *);\n    bool cleanup_requested;\n};\n\nstatic napi_mbt_environment *napi_mbt_environments = NULL;\nstatic napi_mbt_environment *napi_mbt_active = NULL;\nstatic bool napi_mbt_initialized = false;\nstatic int napi_mbt_environment_count = 0;\nstatic int napi_mbt_resource_count = 0;\nextern void moonbit_runtime_init(int argc, char **argv);\nextern void moonbit_init(void);\n\nstatic void napi_mbt_unlink_resource(napi_mbt_resource *resource) {\n    if (!resource->owner) return;\n    if (resource->previous) resource->previous->next = resource->next;\n    else resource->owner->resources = resource->next;\n    if (resource->next) resource->next->previous = resource->previous;\n    resource->owner = NULL;\n    resource->next = resource->previous = NULL;\n}\n\nstatic int napi_mbt_dispose_resource(napi_mbt_resource *resource) {\n    if (resource->disposed) return napi_ok;\n    napi_mbt_environment *owner = resource->owner;\n    if (!owner || !napi_mbt_same_thread(owner->thread, napi_mbt_thread_self())) return 1005;\n    if (resource->reference) {\n        napi_status status = napi_delete_reference(owner->env, resource->reference);\n        if (status != napi_ok && !owner->closing) return (int)status;\n        if (status != napi_ok) owner->cleanup_status = (int)status;\n        resource->reference = NULL;\n    }\n    resource->disposed = true;\n    resource->disposing = true;\n    void *payload = resource->payload;\n    resource->payload = NULL;\n    if (payload) moonbit_decref(payload);\n    resource->disposing = false;\n    return napi_ok;\n}\n\nstatic void napi_mbt_free_resource(napi_mbt_resource *resource) {\n    if (!resource) return;\n    napi_mbt_lock();\n    if (resource->disposing) {\n        resource->pending_free = true;\n        napi_mbt_unlock();\n        return;\n    }\n    int status = napi_mbt_dispose_resource(resource);\n    if (status != napi_ok) {\n        // A MoonBit global may release a callback captured in another environment.\n        // Leave the N-API reference for its owner's next entry or cleanup hook.\n        resource->pending_free = true;\n        napi_mbt_unlock();\n        return;\n    }\n    napi_mbt_unlink_resource(resource);\n    napi_mbt_resource_count--;\n    free(resource);\n    napi_mbt_unlock();\n}\n\nstatic void napi_mbt_flush_resources(napi_mbt_environment *owner) {\n    // Re-scan after every decref: finalizers may unlink other resources.\n    for (;;) {\n        napi_mbt_resource *resource = owner->resources;\n        while (resource && (!resource->pending_free || resource->disposing)) resource = resource->next;\n        if (!resource) return;\n        if (napi_mbt_dispose_resource(resource) != napi_ok) return;\n        napi_mbt_free_resource(resource);\n    }\n}\n\nstatic void napi_mbt_cleanup_environment(void *data) {\n    napi_mbt_lock();\n    napi_mbt_environment *owner = (napi_mbt_environment *)data;\n    if (owner->async_context) {\n        owner->cleanup_requested = true;\n        owner->async_close(owner);\n        napi_mbt_unlock();\n        return;\n    }\n    napi_mbt_environment *previous = napi_mbt_active;\n    napi_mbt_active = owner;\n    owner->closing = true;\n    napi_mbt_flush_resources(owner);\n    while (owner->resources) {\n        napi_mbt_resource *resource = owner->resources;\n        (void)napi_mbt_dispose_resource(resource);\n        if (resource->pending_free || resource->kind == 3) napi_mbt_free_resource(resource);\n        else napi_mbt_unlink_resource(resource);\n        // Live JS/MoonBit wrappers own the container; their finalizer frees it.\n        // No wrapper keeps the environment alive after shutdown.\n    }\n    napi_mbt_environment **entry = &napi_mbt_environments;\n    while (*entry && *entry != owner) entry = &(*entry)->next;\n    if (*entry) *entry = owner->next;\n    napi_mbt_active = previous == owner ? NULL : previous;\n    napi_mbt_environment_count--;\n    free(owner);\n    napi_mbt_unlock();\n}\n\nint napi_mbt_enter(napi_env env, void **previous) {\n    napi_mbt_lock();\n    napi_mbt_environment *owner = napi_mbt_environments;\n    while (owner && owner->env != env) owner = owner->next;\n#if NAPI_VERSION >= 3\n    if (!owner) {\n        owner = (napi_mbt_environment *)calloc(1, sizeof(*owner));\n        if (!owner) { napi_mbt_unlock(); return napi_generic_failure; }\n        owner->env = env;\n        owner->thread = napi_mbt_thread_self();\n        napi_status status = napi_add_env_cleanup_hook(env, napi_mbt_cleanup_environment, owner);\n        if (status != napi_ok) { free(owner); napi_mbt_unlock(); return (int)status; }\n        owner->next = napi_mbt_environments;\n        napi_mbt_environments = owner;\n        napi_mbt_environment_count++;\n    }\n#endif\n    if (owner && (owner->closing || !napi_mbt_same_thread(owner->thread, napi_mbt_thread_self()))) {\n        napi_mbt_unlock();\n        return 1005;\n    }\n    *previous = napi_mbt_active;\n    napi_mbt_active = owner;\n    if (!napi_mbt_initialized) {\n        /* Since MoonBit 0.10.17 the addon is linked from an executable package,\n         * whose moonbit_init leaves runtime initialization to main. */\n        moonbit_runtime_init(0, 0);\n        moonbit_init();\n        napi_mbt_initialized = true;\n    }\n    if (owner) napi_mbt_flush_resources(owner);\n    return napi_ok;\n}\n\nvoid napi_mbt_leave(void *previous) {\n    napi_mbt_active = (napi_mbt_environment *)previous;\n    napi_mbt_unlock();\n}\n\nstatic napi_mbt_resource *napi_mbt_new_resource(napi_env env, int kind, int type) {\n    if (!napi_mbt_active || napi_mbt_active->env != env || napi_mbt_active->closing) return NULL;\n    napi_mbt_resource *resource = (napi_mbt_resource *)calloc(1, sizeof(*resource));\n    if (!resource) return NULL;\n    resource->owner = napi_mbt_active;\n    resource->kind = kind;\n    resource->type = type;\n    resource->next = napi_mbt_active->resources;\n    if (resource->next) resource->next->previous = resource;\n    napi_mbt_active->resources = resource;\n    napi_mbt_resource_count++;\n    return resource;\n}\n\nstatic int napi_mbt_check_resource(napi_mbt_resource *resource, napi_env env) {\n    if (!resource || resource->disposed || !resource->owner) return 1006;\n    if (resource->owner != napi_mbt_active || resource->owner->env != env || !napi_mbt_same_thread(resource->owner->thread, napi_mbt_thread_self())) return 1005;\n    if (resource->owner->closing) return 1004;\n    return napi_ok;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_live_environments(void) { return napi_mbt_environment_count; }\nMOONBIT_FFI_EXPORT int napi_mbt_live_resources(void) { return napi_mbt_resource_count; }\nMOONBIT_FFI_EXPORT int napi_mbt_live_resource_kind(int kind) {\n    int count = 0;\n    for (napi_mbt_environment *env = napi_mbt_environments; env; env = env->next)\n        for (napi_mbt_resource *r = env->resources; r; r = r->next)\n            if (r->kind == kind) count++;\n    return count;\n}\n\nstatic void napi_mbt_native_finalizer(napi_env env, void *data, void *hint) {\n    (void)env; (void)hint;\n    napi_mbt_free_resource((napi_mbt_resource *)data);\n}\n\n// Lookup compares an untrusted napi_unwrap pointer against our own registry\n// before dereferencing it. Other addons' wrapped objects are never cast.\nstatic int napi_mbt_find_instance(napi_env env, napi_value value, int type, bool allow_disposed, napi_mbt_resource **result) {\n    if (!napi_mbt_active || napi_mbt_active->env != env) return 1005;\n    void *pointer = NULL;\n    napi_status status = napi_unwrap(env, value, &pointer);\n    if (status != napi_ok) return status == napi_pending_exception ? (int)status : 1007;\n    napi_mbt_resource *resource = napi_mbt_active->resources;\n    while (resource && resource != pointer) resource = resource->next;\n    if (!resource || resource->kind != 1 || resource->type != type) return 1007;\n    if (!allow_disposed) {\n        int checked = napi_mbt_check_resource(resource, env);\n        if (checked != napi_ok) return checked;\n    }\n    *result = resource;\n    return napi_ok;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_class_lookup(napi_env env, napi_value value, int type, void **result) {\n    napi_mbt_resource *resource = NULL;\n    int status = napi_mbt_find_instance(env, value, type, false, &resource);\n    if (status == napi_ok) *result = resource;\n    return status;\n}\n\n// Only called immediately after successful lookup, while holding the lock.\n// Return ownership of one reference to the concrete generated MoonBit type.\nMOONBIT_FFI_EXPORT void *napi_mbt_class_payload(void *pointer) {\n    napi_mbt_resource *resource = (napi_mbt_resource *)pointer;\n    moonbit_incref(resource->payload);\n    return resource->payload;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_class_wrap(napi_env env, napi_value receiver, void *payload, int type) {\n    napi_mbt_resource *resource = napi_mbt_new_resource(env, 1, type);\n    if (!resource) return napi_generic_failure;\n    napi_status status = napi_wrap(env, receiver, resource, napi_mbt_native_finalizer, NULL, NULL);\n    if (status != napi_ok) { napi_mbt_free_resource(resource); return (int)status; }\n    moonbit_incref(payload);\n    resource->payload = payload;\n    return napi_ok;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_class_dispose(napi_env env, napi_value receiver, int type) {\n    napi_mbt_resource *resource = NULL;\n    int status = napi_mbt_find_instance(env, receiver, type, true, &resource);\n    if (status != napi_ok) return status;\n    return napi_mbt_dispose_resource(resource);\n}\n\nint napi_mbt_save_constructor(napi_env env, napi_value constructor, int type) {\n    napi_mbt_resource *resource = napi_mbt_new_resource(env, 3, type);\n    if (!resource) return napi_generic_failure;\n    napi_status status = napi_create_reference(env, constructor, 1, &resource->reference);\n    if (status != napi_ok) { napi_mbt_free_resource(resource); return (int)status; }\n    return napi_ok;\n}\n\nint napi_mbt_construct_pending(napi_env env, napi_value receiver, int type, bool *handled) {\n    *handled = false;\n    if (!napi_mbt_active || napi_mbt_active->env != env) return 1005;\n    napi_mbt_construction *pending = napi_mbt_active->construction;\n    if (!pending || pending->type != type || pending->claimed) return napi_ok;\n    int status = napi_mbt_class_wrap(env, receiver, pending->payload, type);\n    if (status == napi_ok) { pending->claimed = true; *handled = true; }\n    return status;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_class_create(napi_env env, void *payload, int type, napi_value *result) {\n    if (!napi_mbt_active || napi_mbt_active->env != env) return 1005;\n    napi_mbt_resource *constructor = napi_mbt_active->resources;\n    while (constructor && !(constructor->kind == 3 && constructor->type == type && !constructor->disposed)) constructor = constructor->next;\n    if (!constructor) return 1007;\n    napi_value function;\n    napi_status status = napi_get_reference_value(env, constructor->reference, &function);\n    if (status != napi_ok) return (int)status;\n    napi_mbt_construction pending = { type, payload, false, napi_mbt_active->construction };\n    napi_mbt_active->construction = &pending;\n    status = napi_new_instance(env, function, 0, NULL, result);\n    napi_mbt_active->construction = pending.previous;\n    if (status != napi_ok) return (int)status;\n    return pending.claimed ? napi_ok : napi_generic_failure;\n}\n#endif\n",
  "template/napi_classes.mbt": "///|\n#borrow(result)\nextern \"c\" fn napi_class_lookup_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  type_id : Int32,\n  result : Ref[UnmanagedBuffer],\n) -> Int32 = \"napi_mbt_class_lookup\"\n\n///|\npub fn napi_class_handle(\n  env : NapiEnv,\n  value : NapiValue,\n  type_id : Int32,\n) -> UnmanagedBuffer raise NapiError {\n  let result = Ref(moonbit_dummy_unmanaged_buffer())\n  result_from_status(\n    env,\n    napi_class_lookup_raw(env, value, type_id, result),\n    b\"Validating native receiver\",\n  )\n  result.val\n}\n\n///|\nextern \"c\" fn napi_class_dispose_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  type_id : Int32,\n) -> Int32 = \"napi_mbt_class_dispose\"\n\n///|\npub fn napi_class_dispose(\n  env : NapiEnv,\n  value : NapiValue,\n  type_id : Int32,\n) -> Unit raise NapiError {\n  result_from_status(\n    env,\n    napi_class_dispose_raw(env, value, type_id),\n    b\"Disposing native receiver\",\n  )\n}\n",
  "template/napi_callbacks.mbt": "///|\n/// An owned reference to a JavaScript function. The reference belongs to its\n/// creating environment; dispose is idempotent and GC is a fallback.\npub type NapiCallback\n\n///|\n#external\npub type NapiArguments\n\n///|\nextern \"c\" fn napi_callback_empty() -> NapiCallback = \"napi_mbt_callback_empty\"\n\n///|\n#borrow(callback)\nextern \"c\" fn napi_callback_bind(\n  env : NapiEnv,\n  value : NapiValue,\n  callback : NapiCallback,\n) -> Int32 = \"napi_mbt_callback_bind\"\n\n///|\n#borrow(callback, result)\nextern \"c\" fn napi_callback_env_raw(\n  callback : NapiCallback,\n  result : Ref[NapiEnv],\n) -> Int32 = \"napi_mbt_callback_env\"\n\n///|\n#borrow(callback, result)\nextern \"c\" fn napi_callback_call_raw(\n  callback : NapiCallback,\n  args : NapiValue,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_mbt_callback_call\"\n\n///|\n#borrow(callback)\nextern \"c\" fn napi_callback_dispose_raw(callback : NapiCallback) -> Int32 = \"napi_mbt_callback_dispose\"\n\n///|\npub fn NapiCallback::new(\n  env : NapiEnv,\n  value : NapiValue,\n) -> NapiCallback raise NapiError {\n  let callback = napi_callback_empty()\n  result_from_status(\n    env,\n    napi_callback_bind(env, value, callback),\n    b\"Retaining JavaScript callback\",\n  )\n  callback\n}\n\n///|\npub fn NapiCallback::environment(\n  self : NapiCallback,\n) -> NapiEnv raise NapiError {\n  let result = Ref(napi_dummy_env())\n  let status = napi_callback_env_raw(self, result)\n  // The stored environment may already have closed. Do not call N-API with it.\n  if status != 0 {\n    raise napi_failure(\n      status,\n      Error,\n      if status == 1006 {\n        \"ERR_NAPI_DISPOSED\"\n      } else {\n        \"ERR_NAPI_ENVIRONMENT\"\n      },\n      \"Calling callback outside its owning environment\",\n    )\n  }\n  result.val\n}\n\n///|\nextern \"c\" fn napi_dummy_env() -> NapiEnv = \"napi_mbt_dummy_env\"\n\n///|\npub fn NapiCallback::call(\n  self : NapiCallback,\n  args : NapiValue,\n) -> NapiValue raise NapiError {\n  let env = self.environment()\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_callback_call_raw(self, args, result),\n    b\"Calling JavaScript callback\",\n  )\n  result.val\n}\n\n///|\npub fn NapiCallback::dispose(self : NapiCallback) -> Unit raise NapiError {\n  let status = napi_callback_dispose_raw(self)\n  if status != 0 {\n    raise napi_failure(\n      status,\n      Error,\n      \"ERR_NAPI_ENVIRONMENT\",\n      \"Disposing JavaScript callback\",\n    )\n  }\n}\n\n///|\nextern \"c\" fn napi_arguments_length(args : NapiArguments) -> Int32 = \"napi_mbt_arguments_length\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_arguments_get_raw(\n  env : NapiEnv,\n  args : NapiArguments,\n  index : Int32,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_mbt_arguments_get\"\n\n///|\npub fn NapiArguments::require(\n  self : NapiArguments,\n  count : Int32,\n) -> Unit raise NapiError {\n  if napi_arguments_length(self) < count {\n    raise napi_failure(\n      1,\n      TypeError,\n      \"ERR_NAPI_ARGUMENTS\",\n      \"Missing callback argument\",\n    )\n  }\n}\n\n///|\npub fn NapiArguments::get(\n  self : NapiArguments,\n  env : NapiEnv,\n  index : Int32,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_arguments_get_raw(env, self, index, result),\n    b\"Reading closure argument\",\n  )\n  result.val\n}\n\n///|\n#borrow(callback, result)\nextern \"c\" fn napi_closure_new_raw(\n  env : NapiEnv,\n  callback : (NapiEnv, NapiArguments) -> NapiValue,\n  trampoline : FuncRef[\n    ((NapiEnv, NapiArguments) -> NapiValue, NapiEnv, NapiArguments) -> NapiValue,\n  ],\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_mbt_closure_new\"\n\n///|\npub fn napi_closure_new(\n  env : NapiEnv,\n  callback : (NapiEnv, NapiArguments) -> NapiValue,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_closure_new_raw(\n      env,\n      callback,\n      fn(f, env, args) { f(env, args) },\n      result,\n    ),\n    b\"Creating MoonBit closure\",\n  )\n  result.val\n}\n",
  "template/napi_callbacks.h": "/* Included after napi_lifecycle.h; all operations run on the owning JS thread. */\ntypedef struct { napi_mbt_resource *resource; bool borrowed; bool boxed; } napi_mbt_callback_ref;\nstruct napi_mbt_arguments { size_t count; napi_value *values; };\ntypedef napi_value (*napi_mbt_trampoline)(void *, napi_env, napi_mbt_arguments *);\n\nstatic void napi_mbt_callback_finalizer(void *data) {\n    napi_mbt_callback_ref *callback = data;\n    /* Listener pins borrow a JS-owned container. In particular their MoonBit\n     * finalizer can run while environment cleanup decrefs that very closure. */\n    if (!callback->borrowed) napi_mbt_free_resource(callback->resource);\n}\n\nMOONBIT_FFI_EXPORT void *napi_mbt_callback_empty(void) {\n    napi_mbt_callback_ref *callback = moonbit_make_external_object(napi_mbt_callback_finalizer, sizeof(*callback));\n    callback->resource = NULL;\n    callback->borrowed = false;\n    callback->boxed = false;\n    return callback;\n}\nMOONBIT_FFI_EXPORT napi_env napi_mbt_dummy_env(void) { return NULL; }\n\n/* Keep object-like values directly. Older N-API versions reject primitive\n * references, so only those values need a temporary box. This matters during\n * Node 26 Worker::terminate: creating an Array is a V8 allocation and can\n * trip IsOnCentralStack while the isolate is being disposed. */\nMOONBIT_FFI_EXPORT int napi_mbt_reference_bind(napi_env env, napi_value value, napi_mbt_callback_ref *reference) {\n    /* Node 26 can run native async cleanup while the isolate is already off\n     * its central JS stack. Do not create a new V8 handle/reference once the\n     * owning environment has started closing; the scheduler will propagate\n     * ERR_NAPI_ENV_CLOSED and the normal cleanup path will release existing\n     * references without entering JavaScript. */\n    if (napi_mbt_active && napi_mbt_active->closing) return 1004;\n    napi_valuetype type;\n    napi_status status = napi_typeof(env, value, &type);\n    if (status != napi_ok) return (int)status;\n    napi_mbt_resource *resource = napi_mbt_new_resource(env, 5, 0);\n    if (!resource) return napi_generic_failure;\n    napi_value target = value;\n    napi_value box;\n    bool boxed = false;\n    if (type != napi_object && type != napi_function && type != napi_symbol) {\n        status = napi_create_array_with_length(env, 1, &box);\n        if (status == napi_ok) status = napi_set_element(env, box, 0, value);\n        target = box;\n        boxed = true;\n    }\n    if (status == napi_ok) status = napi_create_reference(env, target, 1, &resource->reference);\n    if (status != napi_ok) { napi_mbt_free_resource(resource); return (int)status; }\n    reference->resource = resource;\n    reference->boxed = boxed;\n    return napi_ok;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_reference_get(napi_env env, napi_mbt_callback_ref *reference, napi_value *result) {\n    int status = napi_mbt_check_resource(reference->resource, env);\n    if (status != napi_ok) return status;\n    napi_value box;\n    status = napi_get_reference_value(env, reference->resource->reference, &box);\n    if (status == napi_ok) {\n        if (reference->boxed) status = napi_get_element(env, box, 0, result);\n        else *result = box;\n    }\n    return status;\n}\n\n/* Own the listener payload independently of its JS wrapper. Cleanup can release\n * it without creating JS handles or invoking JS during environment teardown. */\nMOONBIT_FFI_EXPORT int napi_mbt_closure_pin(napi_env env, napi_value value, napi_mbt_callback_ref *reference) {\n    void *pointer = NULL;\n    int status = napi_unwrap(env, value, &pointer);\n    if (status != napi_ok) return status;\n    napi_mbt_resource *resource = napi_mbt_active->resources;\n    while (resource && resource != pointer) resource = resource->next;\n    if (!resource || resource->kind != 2) return 1007;\n    /* This reference borrows the container; its JS wrapper is retained by a\n     * separate NapiReference until the pin has been disposed. */\n    reference->resource = resource;\n    reference->borrowed = true;\n    return napi_ok;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_closure_unpin(napi_mbt_callback_ref *reference) {\n    napi_mbt_resource *resource = reference->resource;\n    reference->resource = NULL;\n    if (!resource) return napi_ok;\n    /* After teardown the borrowed pointer may be stale. Compare it against the\n     * current owner's registry before touching it. */\n    napi_mbt_resource *entry = napi_mbt_active ? napi_mbt_active->resources : NULL;\n    while (entry && entry != resource) entry = entry->next;\n    return entry ? napi_mbt_dispose_resource(entry) : napi_ok;\n}\n\n/* Promise reactions cannot be removed from JS. Drop their MoonBit payload on\n * completion/cancellation, and let a later reaction return undefined safely. */\nMOONBIT_FFI_EXPORT int napi_mbt_closure_inert(napi_env env, napi_value value) {\n    void *pointer = NULL;\n    int status = napi_unwrap(env, value, &pointer);\n    if (status != napi_ok) return status;\n    napi_mbt_resource *resource = napi_mbt_active->resources;\n    while (resource && resource != pointer) resource = resource->next;\n    if (!resource || resource->kind != 2) return 1007;\n    resource->inert_on_dispose = true;\n    return napi_ok;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_owned_reference_dispose(napi_mbt_callback_ref *reference) {\n    if (!reference->resource || reference->resource->disposed) return napi_ok;\n    if (reference->resource->owner != napi_mbt_active) return 1005;\n    return napi_mbt_dispose_resource(reference->resource);\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_take_exception(napi_env env, napi_value *result) {\n    return (int)napi_get_and_clear_last_exception(env, result);\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_reference_throw(napi_env env, napi_mbt_callback_ref *reference) {\n    bool pending = false;\n    napi_status status = napi_is_exception_pending(env, &pending);\n    if (status != napi_ok || pending) return (int)status;\n    napi_value value;\n    int checked = napi_mbt_reference_get(env, reference, &value);\n    return checked == napi_ok ? (int)napi_throw(env, value) : checked;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_callback_bind(napi_env env, napi_value value, napi_mbt_callback_ref *callback) {\n    napi_valuetype type;\n    napi_status status = napi_typeof(env, value, &type);\n    if (status != napi_ok) return (int)status;\n    if (type != napi_function) return napi_function_expected;\n    napi_mbt_resource *resource = napi_mbt_new_resource(env, 4, 0);\n    if (!resource) return napi_generic_failure;\n    status = napi_create_reference(env, value, 1, &resource->reference);\n    if (status != napi_ok) { napi_mbt_free_resource(resource); return (int)status; }\n    callback->resource = resource;\n    return napi_ok;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_callback_env(napi_mbt_callback_ref *callback, napi_env *result) {\n    if (!napi_mbt_active) return 1005;\n    int status = napi_mbt_check_resource(callback->resource, napi_mbt_active->env);\n    if (status == napi_ok) *result = napi_mbt_active->env;\n    return status;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_callback_dispose(napi_mbt_callback_ref *callback) {\n    if (!callback->resource || callback->resource->disposed) return napi_ok;\n    napi_env env;\n    int status = napi_mbt_callback_env(callback, &env);\n    return status == napi_ok ? napi_mbt_dispose_resource(callback->resource) : status;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_callback_call(napi_mbt_callback_ref *callback, napi_value args, napi_value *result) {\n    napi_env env;\n    int checked = napi_mbt_callback_env(callback, &env);\n    if (checked != napi_ok) return checked;\n    napi_value function, receiver;\n    uint32_t count;\n    napi_status status = napi_get_reference_value(env, callback->resource->reference, &function);\n    if (status != napi_ok) return (int)status;\n    status = napi_get_undefined(env, &receiver);\n    if (status != napi_ok) return (int)status;\n    status = napi_get_array_length(env, args, &count);\n    if (status != napi_ok) return (int)status;\n    if (count > SIZE_MAX / sizeof(napi_value)) return napi_invalid_arg;\n    napi_value *values = count ? malloc(sizeof(*values) * count) : NULL;\n    if (count && !values) return napi_generic_failure;\n    for (uint32_t i = 0; i < count; ++i) {\n        status = napi_get_element(env, args, i, &values[i]);\n        if (status != napi_ok) { free(values); return (int)status; }\n    }\n    status = napi_call_function(env, receiver, function, count, values, result);\n    free(values);\n    return (int)status;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_arguments_length(napi_mbt_arguments *args) { return (int)args->count; }\nMOONBIT_FFI_EXPORT int napi_mbt_arguments_get(napi_env env, napi_mbt_arguments *args, int index, napi_value *result) {\n    if (index < 0) return napi_invalid_arg;\n    if ((size_t)index >= args->count) return napi_get_undefined(env, result);\n    *result = args->values[index];\n    return napi_ok;\n}\n\nextern int napi_mbt_throw_status(napi_env, int, int, const char *, const char *);\nstatic napi_value napi_mbt_closure_error(napi_env env, int status) {\n    const char *code = status == 1006 ? \"ERR_NAPI_DISPOSED\" : status == 1007 ? \"ERR_NAPI_RECEIVER\" : \"ERR_NAPI_CALLBACK\";\n    (void)napi_mbt_throw_status(env, status, status == 1007 ? 1 : 0, code, \"Calling MoonBit closure\");\n    return NULL;\n}\n\nstatic napi_value napi_mbt_closure_call(napi_env env, napi_callback_info info) {\n    void *previous;\n    int checked = napi_mbt_enter(env, &previous);\n    if (checked != napi_ok) return napi_mbt_closure_error(env, checked);\n    napi_mbt_arguments args = {0, NULL};\n    napi_mbt_resource *resource = NULL;\n    napi_value result = NULL;\n    checked = napi_get_cb_info(env, info, &args.count, NULL, NULL, (void **)&resource);\n    if (checked != napi_ok) goto done;\n    checked = napi_mbt_check_resource(resource, env);\n    if (checked == 1006 && resource->inert_on_dispose) {\n        checked = napi_get_undefined(env, &result);\n        goto done;\n    }\n    if (checked != napi_ok) goto done;\n    if (args.count > INT32_MAX || args.count > SIZE_MAX / sizeof(napi_value)) { checked = napi_invalid_arg; goto done; }\n    args.values = args.count ? malloc(sizeof(napi_value) * args.count) : NULL;\n    if (args.count && !args.values) { checked = napi_generic_failure; goto done; }\n    checked = napi_get_cb_info(env, info, &args.count, args.values, NULL, NULL);\n    if (checked != napi_ok) goto done;\n    // FuncRef arguments are borrowed. Pin the closure across the call, including\n    // reentrant dispose which clears resource->payload, then release our pin.\n    void *payload = resource->payload;\n    moonbit_incref(payload);\n    result = resource->trampoline(payload, env, &args);\n    moonbit_decref(payload);\ndone:\n    free(args.values);\n    if (checked != napi_ok) result = napi_mbt_closure_error(env, checked);\n    napi_mbt_leave(previous);\n    return result;\n}\n\nstatic napi_value napi_mbt_closure_dispose(napi_env env, napi_callback_info info) {\n    void *previous;\n    int checked = napi_mbt_enter(env, &previous);\n    if (checked != napi_ok) return napi_mbt_closure_error(env, checked);\n    napi_value receiver, result = NULL;\n    void *pointer = NULL;\n    checked = napi_get_cb_info(env, info, NULL, NULL, &receiver, NULL);\n    if (checked == napi_ok) checked = napi_unwrap(env, receiver, &pointer);\n    napi_mbt_resource *resource = napi_mbt_active->resources;\n    while (resource && resource != pointer) resource = resource->next;\n    if (!resource || resource->kind != 2) checked = 1007;\n    if (checked == napi_ok) checked = napi_mbt_dispose_resource(resource);\n    if (checked == napi_ok) checked = napi_get_undefined(env, &result);\n    if (checked != napi_ok) result = napi_mbt_closure_error(env, checked);\n    napi_mbt_leave(previous);\n    return result;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_closure_new(napi_env env, void *callback, napi_mbt_trampoline trampoline, napi_value *result) {\n    napi_mbt_resource *resource = napi_mbt_new_resource(env, 2, 0);\n    if (!resource) return napi_generic_failure;\n    napi_status status = napi_create_function(env, \"MoonBitClosure\", NAPI_AUTO_LENGTH, napi_mbt_closure_call, resource, result);\n    if (status != napi_ok) { napi_mbt_free_resource(resource); return (int)status; }\n    status = napi_wrap(env, *result, resource, napi_mbt_native_finalizer, NULL, NULL);\n    if (status != napi_ok) { napi_mbt_free_resource(resource); return (int)status; }\n    moonbit_incref(callback);\n    resource->payload = callback;\n    resource->trampoline = trampoline;\n    napi_property_descriptor property = {0};\n    property.utf8name = \"dispose\";\n    property.method = napi_mbt_closure_dispose;\n    status = napi_define_properties(env, *result, 1, &property);\n    if (status != napi_ok) (void)napi_mbt_dispose_resource(resource);\n    return (int)status;\n}\n",
  "async/manifest.json": "{\n  \"dependency\": \"moonbitlang/async\",\n  \"version\": \"0.22.4\",\n  \"patchVersion\": 6,\n  \"license\": \"Apache-2.0\",\n  \"checksum\": \"fnv1a64-utf8-lf\",\n  \"files\": [\n    {\n      \"path\": \"src/internal/coroutine/scheduler.mbt\",\n      \"before\": \"2bd74311a8bfebab\",\n      \"after\": \"603bcef1bd8aa8df\"\n    },\n    {\n      \"path\": \"src/internal/coroutine/coroutine.mbt\",\n      \"before\": \"bdcbc1546b605af6\",\n      \"after\": \"9a67b04a6dd574b3\"\n    },\n    {\n      \"path\": \"src/internal/event_loop/event_loop.mbt\",\n      \"before\": \"ab2090710a20373a\",\n      \"after\": \"22b98404d49f5e13\"\n    },\n    {\n      \"path\": \"src/internal/event_loop/stdio.mbt\",\n      \"before\": \"3b7cb785a3c2a66a\",\n      \"after\": \"dc13cca0db9a09db\"\n    },\n    {\n      \"path\": \"src/stdio/stdio.mbt\",\n      \"before\": \"f93eaa1e14c7b797\",\n      \"after\": \"e2d024df56fba1c9\"\n    },\n    {\n      \"path\": \"src/socket/ifname.mbt\",\n      \"before\": \"7ac79ba29ba0fba6\",\n      \"after\": \"db0df6ec4c956fcd\"\n    },\n    {\n      \"path\": \"src/internal/event_loop/thread_pool.mbt\",\n      \"before\": \"2e5408455a77b04f\",\n      \"after\": \"5ef6ca9c8db836c9\"\n    },\n    {\n      \"path\": \"src/internal/event_loop/timer.mbt\",\n      \"before\": \"85abadd66f4a56e5\",\n      \"after\": \"cf5787a713b053b4\"\n    },\n    {\n      \"path\": \"src/internal/event_loop/thread_pool.c\",\n      \"before\": \"18df225bc93c5044\",\n      \"after\": \"29c3775577cb2f9e\"\n    },\n    {\n      \"path\": \"src/internal/event_loop/napi_embedded.mbt\",\n      \"before\": null,\n      \"after\": \"969429156cfa7d75\"\n    },\n    {\n      \"path\": \"src/napi_host/moon.pkg\",\n      \"before\": null,\n      \"after\": \"73a85ea2d32cf0a5\"\n    },\n    {\n      \"path\": \"src/napi_host/host.mbt\",\n      \"before\": null,\n      \"after\": \"d72ba0140b63030c\"\n    }\n  ]\n}\n",
  "async/LICENSE": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n",
  "async/napi_async.h": "/* Opaque libuv API declarations from libuv 1.40.0 (MIT; see LIBUV-LICENSE).\n * Allocate with uv_handle_size so no platform struct layout is copied. Node\n * versions supporting N-API 8 provide these APIs in their embedded libuv. */\n#include <stdio.h>\ntypedef struct uv_timer_s uv_timer_t;\ntypedef struct uv_handle_s uv_handle_t;\ntypedef struct uv_loop_s uv_loop_t;\nextern size_t uv_handle_size(int type);\nextern void *uv_handle_get_data(const uv_handle_t *);\nextern void uv_handle_set_data(uv_handle_t *, void *);\nextern int uv_timer_init(uv_loop_t *, uv_timer_t *);\nextern int uv_timer_start(uv_timer_t *, void (*)(uv_timer_t *), uint64_t, uint64_t);\nextern int uv_timer_stop(uv_timer_t *);\nextern void uv_close(uv_handle_t *, void (*)(uv_handle_t *));\n\ntypedef struct {\n    napi_mbt_environment *owner;\n    uv_timer_t *timer;\n    void *state;\n    int32_t (*tick)(void *, int32_t);\n    napi_async_cleanup_hook_handle cleanup;\n    napi_async_context async_context;\n    napi_ref async_resource;\n    bool closing;\n    bool handle_closing;\n} napi_mbt_async_context;\n\nstatic void napi_mbt_async_timer(uv_timer_t *timer);\nstatic void napi_mbt_async_begin_close(napi_mbt_environment *owner);\n\nstatic void napi_mbt_async_handle_closed(uv_handle_t *handle) {\n    napi_mbt_async_context *context = uv_handle_get_data(handle);\n    napi_mbt_lock();\n    napi_mbt_environment *owner = context->owner;\n    napi_mbt_environment *previous = napi_mbt_active;\n    napi_mbt_active = owner;\n    moonbit_decref(context->state);\n    if (context->async_context) {\n        napi_status status = napi_async_destroy(owner->env, context->async_context);\n        if (status != napi_ok) owner->cleanup_status = status;\n    }\n    if (context->async_resource) {\n        napi_status status = napi_delete_reference(owner->env, context->async_resource);\n        if (status != napi_ok) owner->cleanup_status = status;\n    }\n    owner->async_context = NULL;\n    owner->async_close = NULL;\n    napi_mbt_active = previous;\n    napi_async_cleanup_hook_handle cleanup = context->cleanup;\n    free(context->timer);\n    free(context);\n    if (owner->cleanup_requested) napi_mbt_cleanup_environment(owner);\n    if (cleanup) {\n        // No JavaScript can be called while an environment is shutting down.\n        napi_status status = napi_remove_async_cleanup_hook(cleanup);\n        if (status != napi_ok) fprintf(stderr, \"napi-mbt: removing async cleanup hook failed (%d)\\n\", (int)status);\n    }\n    napi_mbt_unlock();\n}\n\nstatic void napi_mbt_async_timer(uv_timer_t *timer) {\n    napi_mbt_async_context *context = uv_handle_get_data((uv_handle_t *)timer);\n    napi_mbt_lock();\n    napi_mbt_environment *previous = napi_mbt_active;\n    napi_mbt_active = context->owner;\n    napi_handle_scope scope = NULL;\n    napi_callback_scope callback_scope = NULL;\n    if (!context->closing) {\n        napi_status status = napi_open_handle_scope(context->owner->env, &scope);\n        napi_value resource, runnable;\n        if (status == napi_ok) status = napi_get_reference_value(context->owner->env, context->async_resource, &resource);\n        /* Opening a callback scope does not check whether Node can still enter\n         * JS. During Worker::terminate, Node drains native async work before\n         * running cleanup hooks, so our timer can fire in that interval. Boolean\n         * coercion of our own plain resource cannot execute user code, but does\n         * perform the Node-API execution-state check before opening the scope. */\n        if (status == napi_ok) status = napi_coerce_to_bool(context->owner->env, resource, &runnable);\n        if (status == napi_ok) status = napi_open_callback_scope(context->owner->env, resource, context->async_context, &callback_scope);\n        if (status != napi_ok) { context->closing = true; context->owner->closing = true; }\n    }\n    int delay = context->tick(context->state, context->closing);\n    if (delay == -2) {\n        context->handle_closing = true;\n        uv_close((uv_handle_t *)timer, napi_mbt_async_handle_closed);\n    } else if (delay >= 0) {\n        int status = uv_timer_start(timer, napi_mbt_async_timer, delay ? (uint64_t)delay : 1, 0);\n        if (status != 0) fprintf(stderr, \"napi-mbt: async timer failed (%d)\\n\", status);\n    }\n    // Arm before draining microtasks: a reentrant export can schedule an earlier\n    // tick, which must not be overwritten by the previously computed delay.\n    if (callback_scope) {\n        napi_status status = napi_close_callback_scope(context->owner->env, callback_scope);\n        if (status != napi_ok) context->owner->cleanup_status = status;\n    }\n    if (scope) {\n        napi_status status = napi_close_handle_scope(context->owner->env, scope);\n        if (status != napi_ok) context->owner->cleanup_status = status;\n    }\n    napi_mbt_active = previous;\n    napi_mbt_unlock();\n}\n\nstatic void napi_mbt_async_begin_close(napi_mbt_environment *owner) {\n    napi_mbt_async_context *context = owner->async_context;\n    if (context->handle_closing || context->closing) return;\n    context->closing = true;\n    owner->closing = true;\n    int status = uv_timer_start(context->timer, napi_mbt_async_timer, 0, 0);\n    if (status != 0) fprintf(stderr, \"napi-mbt: async shutdown timer failed (%d)\\n\", status);\n}\n\nstatic void napi_mbt_async_cleanup(napi_async_cleanup_hook_handle handle, void *data) {\n    napi_mbt_async_context *context = data;\n    napi_mbt_lock();\n    context->cleanup = handle;\n    napi_mbt_async_begin_close(context->owner);\n    napi_mbt_unlock();\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_async_has(napi_env env) {\n    return napi_mbt_active && napi_mbt_active->env == env && napi_mbt_active->async_context != NULL;\n}\nMOONBIT_FFI_EXPORT void *napi_mbt_async_get(napi_env env) {\n    (void)env;\n    napi_mbt_async_context *context = napi_mbt_active->async_context;\n    moonbit_incref(context->state);\n    return context->state;\n}\nMOONBIT_FFI_EXPORT int napi_mbt_async_attach(napi_env env, void *state, int32_t (*tick)(void *, int32_t)) {\n    if (!napi_mbt_active || napi_mbt_active->env != env || napi_mbt_active->closing) return 1004;\n    uv_loop_t *event_loop;\n    napi_status status = napi_get_uv_event_loop(env, &event_loop);\n    if (status != napi_ok) return (int)status;\n    napi_mbt_async_context *context = calloc(1, sizeof(*context));\n    if (!context) return napi_generic_failure;\n    napi_value resource, name;\n    status = napi_create_object(env, &resource);\n    if (status == napi_ok) status = napi_create_string_utf8(env, \"napi-mbt:async\", NAPI_AUTO_LENGTH, &name);\n    if (status == napi_ok) status = napi_create_reference(env, resource, 1, &context->async_resource);\n    if (status == napi_ok) status = napi_async_init(env, resource, name, &context->async_context);\n    if (status != napi_ok) {\n        if (context->async_resource) (void)napi_delete_reference(env, context->async_resource);\n        free(context); return (int)status;\n    }\n    context->timer = calloc(1, uv_handle_size(13 /* UV_TIMER */));\n    if (!context->timer || uv_timer_init(event_loop, context->timer) != 0) {\n        (void)napi_async_destroy(env, context->async_context);\n        (void)napi_delete_reference(env, context->async_resource);\n        free(context->timer); free(context); return napi_generic_failure;\n    }\n    context->owner = napi_mbt_active;\n    context->state = state;\n    context->tick = tick;\n    moonbit_incref(state);\n    uv_handle_set_data((uv_handle_t *)context->timer, context);\n    napi_mbt_active->async_context = context;\n    napi_mbt_active->async_close = napi_mbt_async_begin_close;\n    status = napi_add_async_cleanup_hook(env, napi_mbt_async_cleanup, context, &context->cleanup);\n    if (status != napi_ok) {\n        context->handle_closing = true;\n        uv_close((uv_handle_t *)context->timer, napi_mbt_async_handle_closed);\n    }\n    return (int)status;\n}\nMOONBIT_FFI_EXPORT int napi_mbt_async_schedule(napi_env env) {\n    if (!napi_mbt_active || napi_mbt_active->env != env || napi_mbt_active->closing) return 1004;\n    napi_mbt_async_context *context = napi_mbt_active->async_context;\n    return uv_timer_start(context->timer, napi_mbt_async_timer, 0, 0) == 0 ? napi_ok : napi_generic_failure;\n}\nMOONBIT_FFI_EXPORT int napi_mbt_async_closing(napi_env env) {\n    return !napi_mbt_active || napi_mbt_active->env != env || napi_mbt_active->closing;\n}\nMOONBIT_FFI_EXPORT int napi_mbt_async_promise_create(napi_env env, napi_value executor, napi_value *result) {\n    napi_value global, constructor;\n    napi_status status = napi_get_global(env, &global);\n    if (status == napi_ok) status = napi_get_named_property(env, global, \"Promise\", &constructor);\n    if (status == napi_ok) status = napi_new_instance(env, constructor, 1, &executor, result);\n    bool is_promise = false;\n    if (status == napi_ok) status = napi_is_promise(env, *result, &is_promise);\n    if (status == napi_ok && !is_promise) status = napi_invalid_arg;\n    return (int)status;\n}\nMOONBIT_FFI_EXPORT napi_env napi_mbt_current_env(void) {\n    return napi_mbt_active ? napi_mbt_active->env : NULL;\n}\nMOONBIT_FFI_EXPORT int napi_mbt_async_owns(napi_env env) {\n    return napi_mbt_active && napi_mbt_active->env == env;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_promise_then(napi_env env, napi_value promise, napi_value resolve, napi_value reject) {\n    napi_value then, result;\n    napi_status status = napi_get_named_property(env, promise, \"then\", &then);\n    napi_value args[] = { resolve, reject };\n    if (status == napi_ok) status = napi_call_function(env, promise, then, 2, args, &result);\n    return (int)status;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_signal_get(napi_env env, napi_value object, const char *name, napi_value *result) {\n    return (int)napi_get_named_property(env, object, name, result);\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_signal_listen(napi_env env, napi_value signal, napi_value listener, int add) {\n    napi_value function, event, result, options, once;\n    napi_status status = napi_get_named_property(env, signal, add ? \"addEventListener\" : \"removeEventListener\", &function);\n    if (status == napi_ok) status = napi_create_string_utf8(env, \"abort\", NAPI_AUTO_LENGTH, &event);\n    if (status == napi_ok) status = napi_create_object(env, &options);\n    if (status == napi_ok) status = napi_get_boolean(env, true, &once);\n    if (status == napi_ok) status = napi_set_named_property(env, options, \"once\", once);\n    napi_value args[] = { event, listener, options };\n    if (status == napi_ok) status = napi_call_function(env, signal, function, add ? 3 : 2, args, &result);\n    return (int)status;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_abort_reason(napi_env env, napi_value signal, napi_value *result) {\n    napi_status status = napi_get_named_property(env, signal, \"reason\", result);\n    napi_valuetype type;\n    if (status == napi_ok) status = napi_typeof(env, *result, &type);\n    if (status != napi_ok || type != napi_undefined) return (int)status;\n    napi_value code, message, name;\n    status = napi_create_string_utf8(env, \"ABORT_ERR\", NAPI_AUTO_LENGTH, &code);\n    if (status == napi_ok) status = napi_create_string_utf8(env, \"The operation was aborted\", NAPI_AUTO_LENGTH, &message);\n    if (status == napi_ok) status = napi_create_error(env, code, message, result);\n    if (status == napi_ok) status = napi_create_string_utf8(env, \"AbortError\", NAPI_AUTO_LENGTH, &name);\n    if (status == napi_ok) status = napi_set_named_property(env, *result, \"name\", name);\n    return (int)status;\n}\n\nMOONBIT_FFI_EXPORT int napi_mbt_stream_factory(napi_env env, int input, napi_value a, napi_value b, napi_value *result) {\n  napi_value source, factory, receiver, flag;\n  napi_status status = napi_create_string_utf8(env, \"// Embedded product runtime. Evaluates to a factory, without global state.\\n((input, a, b) => {\\n  'use strict';\\n  if (input) {\\n    let iterator, reader, ended = false;\\n    if (a == null) throw new TypeError('Expected AsyncIterable or ReadableStream');\\n    const method = a[Symbol.asyncIterator];\\n    // Prefer a reader so cancellation can interrupt a pending Web stream read.\\n    if (typeof a.getReader === 'function') reader = a.getReader();\\n    else if (typeof method === 'function') iterator = method.call(a);\\n    else throw new TypeError('Expected AsyncIterable or ReadableStream');\\n    return {\\n      async next() {\\n        if (ended) return {done: true};\\n        const result = await (reader ? reader.read() : iterator.next());\\n        if (!result || typeof result !== 'object') throw new TypeError('Invalid iterator result');\\n        const done = Boolean(result.done);\\n        return done ? {done: true} : {done: false, value: result.value};\\n      },\\n      async close() {\\n        if (ended) return;\\n        ended = true;\\n        try {\\n          if (reader) await reader.cancel();\\n          else if (typeof iterator.return === 'function') await iterator.return();\\n        } finally {\\n          if (reader) reader.releaseLock();\\n          reader = iterator = a = null;\\n        }\\n      }\\n    };\\n  }\\n  let next = a, close = b, ended = false, chain = Promise.resolve(), closing;\\n  a = b = null;\\n  let mode = null;\\n  const done = () => ({done: true, value: undefined});\\n  const finish = () => {\\n    if (closing) return closing;\\n    ended = true;\\n    const release = close;\\n    next = close = null;\\n    closing = release ? Promise.resolve(release()).then(done) : Promise.resolve(done());\\n    return closing;\\n  };\\n  const read = () => {\\n    const result = chain.then(async () => {\\n      if (ended) return done();\\n      try {\\n        const result = await next();\\n        if (result.done) { ended = true; next = close = null; }\\n        return result;\\n      } catch (error) {\\n        // Native production failures already run the common close path.\\n        ended = true; next = close = null;\\n        throw error;\\n      }\\n    });\\n    chain = result.catch(() => {});\\n    return result;\\n  };\\n  return {\\n    [Symbol.asyncIterator]() {\\n      if (mode !== null) throw new TypeError('Stream already has a consumption chain');\\n      mode = 'iterator'; return this;\\n    },\\n    next() {\\n      if (mode === 'stream') return Promise.reject(new TypeError('Stream already has a consumption chain'));\\n      if (mode === null) mode = 'manual';\\n      return read();\\n    },\\n    return() { return finish(); },\\n    async throw(error) { await finish(); throw error; },\\n    toReadableStream({signal} = {}) {\\n      if (mode !== null) throw new TypeError('Stream already has a consumption chain');\\n      if (typeof ReadableStream !== 'function') throw new Error('Web streams require Node 18 or later');\\n      mode = 'stream';\\n      let abort, cancelled = false;\\n      const detach = () => { if (signal && abort) signal.removeEventListener('abort', abort); };\\n      return new ReadableStream({\\n        start(controller) {\\n          if (!signal) return;\\n          abort = () => {\\n            cancelled = true; detach();\\n            controller.error(signal.reason);\\n            finish().catch(() => {});\\n          };\\n          if (signal.aborted) abort(); else signal.addEventListener('abort', abort, {once: true});\\n        },\\n        async pull(controller) {\\n          try {\\n            const result = await read();\\n            if (cancelled) return;\\n            if (result.done) { detach(); controller.close(); }\\n            else controller.enqueue(result.value);\\n          } catch (error) { if (!cancelled) { detach(); controller.error(error); } }\\n        },\\n        cancel() { cancelled = true; detach(); return finish(); }\\n      }, {highWaterMark: 0});\\n    }\\n  };\\n})\\n\", NAPI_AUTO_LENGTH, &source);\n  if (status == napi_ok) status = napi_run_script(env, source, &factory);\n  if (status == napi_ok) status = napi_get_undefined(env, &receiver);\n  if (status == napi_ok) status = napi_get_boolean(env, input != 0, &flag);\n  napi_value args[] = { flag, a, b };\n  if (status == napi_ok) status = napi_call_function(env, receiver, factory, 3, args, result);\n  return (int)status;\n}\n",
  "async/napi_async.mbt": "///|\npriv struct NapiAsyncState {\n  scheduler : @napi_mbt_async.Loop\n  mut cancelled : Bool\n}\n\n///|\nextern \"c\" fn napi_async_has(env : NapiEnv) -> Bool = \"napi_mbt_async_has\"\n\n///|\nextern \"c\" fn napi_async_get(env : NapiEnv) -> NapiAsyncState = \"napi_mbt_async_get\"\n\n///|\n#borrow(state)\nextern \"c\" fn napi_async_attach(env : NapiEnv, state : NapiAsyncState, tick : FuncRef[(NapiAsyncState, Bool) -> Int32]) -> Int32 = \"napi_mbt_async_attach\"\n\n///|\nextern \"c\" fn napi_async_schedule(env : NapiEnv) -> Int32 = \"napi_mbt_async_schedule\"\n\n///|\nextern \"c\" fn napi_async_closing(env : NapiEnv) -> Bool = \"napi_mbt_async_closing\"\n\n///|\nextern \"c\" fn napi_current_env() -> NapiEnv = \"napi_mbt_current_env\"\n\n///|\npub fn napi_async_spawn(task : async () -> Unit) -> @napi_mbt_async.Task raise {\n  napi_async_submit(napi_current_env(), task)\n}\n\n///|\nfn napi_async_tick(state : NapiAsyncState, closing : Bool) -> Int32 {\n  try {\n    if closing && !state.cancelled { state.cancelled = true; state.scheduler.cancel_all() }\n    let pending = state.scheduler.step()\n    if closing && pending == 0 { state.scheduler.close(); -2 } else { state.scheduler.next_wake() }\n  } catch {\n    error => { eprintln(\"napi-mbt async scheduler: \\{error}\"); 2 }\n  }\n}\n\n///|\nfn napi_async_submit(env : NapiEnv, task : async () -> Unit) -> @napi_mbt_async.Task raise {\n  if napi_async_closing(env) { raise napi_failure(1004, Error, \"ERR_NAPI_ENV_CLOSED\", \"Starting asynchronous task\") }\n  let state = if napi_async_has(env) { napi_async_get(env) } else {\n    let state = { scheduler: @napi_mbt_async.Loop::new(), cancelled: false }\n    errdefer { state.scheduler.close() catch { _ => () } }\n    result_from_status(env, napi_async_attach(env, state, fn(state, closing) { napi_async_tick(state, closing) }), b\"Attaching async scheduler\")\n    state\n  }\n  let handle = state.scheduler.submit(task)\n  result_from_status(env, napi_async_schedule(env), b\"Scheduling async task\")\n  handle\n}\n\n///|\npriv struct NapiAsyncCall {\n  env : NapiEnv\n  promise : NapiAsyncPromise\n  mut task : @napi_mbt_async.Task?\n  mut signal : NapiReference?\n  mut listener : NapiReference?\n  mut cleaned : Bool\n  pin : NapiReference\n}\n\n///|\n// Node-API provides no release operation for an unsettled napi_deferred.\n// Own JS resolver references instead so worker teardown can release them\n// without entering JavaScript or settling a Promise in a closing environment.\npriv struct NapiAsyncPromise {\n  mut promise : NapiValue\n  mut resolve : NapiCallback?\n  mut reject : NapiCallback?\n  mut accepting : Bool\n  mut settled : Bool\n}\n\n///|\n#borrow(result)\nextern \"c\" fn napi_async_promise_create(env : NapiEnv, executor : NapiValue, result : Ref[NapiValue]) -> Int32 = \"napi_mbt_async_promise_create\"\n\n///|\nfn NapiAsyncPromise::dispose(self : NapiAsyncPromise) -> Unit {\n  let resolve = self.resolve\n  let reject = self.reject\n  self.resolve = None\n  self.reject = None\n  self.accepting = false\n  self.settled = true\n  if resolve is Some(callback) { callback.dispose() catch { _ => () } }\n  if reject is Some(callback) { callback.dispose() catch { _ => () } }\n}\n\n///|\nfn napi_async_promise_new(env : NapiEnv) -> NapiAsyncPromise raise {\n  let state : NapiAsyncPromise = { promise: moonbit_dummy_napi_value(), resolve: None, reject: None, accepting: true, settled: false }\n  errdefer state.dispose()\n  defer { state.accepting = false }\n  let executor = napi_closure_new(env, fn(env, args) {\n    try {\n      if !state.accepting { raise napi_failure(1, TypeError, \"ERR_NAPI_PROMISE_EXECUTOR\", \"Promise executor invoked outside construction\") }\n      state.accepting = false\n      args.require(2)\n      state.resolve = Some(NapiCallback::new(env, args.get(env, 0)))\n      state.reject = Some(NapiCallback::new(env, args.get(env, 1)))\n      set_Unit(env, ())\n    } catch { error => { throw_napi_error(env, error); moonbit_dummy_napi_value() } }\n  })\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(env, napi_async_promise_create(env, executor, result), b\"Constructing async Promise\")\n  if state.resolve is None || state.reject is None { raise napi_failure(1, TypeError, \"ERR_NAPI_PROMISE_EXECUTOR\", \"Promise constructor did not provide resolvers\") }\n  state.promise = result.val\n  state\n}\n\n///|\nfn NapiAsyncPromise::settle(self : NapiAsyncPromise, env : NapiEnv, value : NapiValue, reject~ : Bool) -> Unit raise {\n  if self.settled { raise napi_failure(1, Error, \"ERR_NAPI_PROMISE_SETTLED\", \"Promise has already settled\") }\n  self.settled = true\n  defer self.dispose()\n  guard (if reject { self.reject } else { self.resolve }) is Some(callback) else { raise napi_failure(1, Error, \"ERR_NAPI_PROMISE_EXECUTOR\", \"Missing Promise resolver\") }\n  let args = napi_array_new(env, 1)\n  napi_array_set(env, args, 0, value)\n  ignore(callback.call(args))\n}\n\n///|\n#borrow(name, result)\nextern \"c\" fn napi_signal_get_raw(env : NapiEnv, object : NapiValue, name : Bytes, result : Ref[NapiValue]) -> Int32 = \"napi_mbt_signal_get\"\n\n///|\nextern \"c\" fn napi_signal_listen(env : NapiEnv, signal : NapiValue, listener : NapiValue, add : Bool) -> Int32 = \"napi_mbt_signal_listen\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_abort_reason(env : NapiEnv, signal : NapiValue, result : Ref[NapiValue]) -> Int32 = \"napi_mbt_abort_reason\"\n\n///|\n#borrow(reference)\nextern \"c\" fn napi_listener_pin(env : NapiEnv, listener : NapiValue, reference : NapiReference) -> Int32 = \"napi_mbt_closure_pin\"\n\n///|\n#borrow(reference)\nextern \"c\" fn napi_listener_unpin(reference : NapiReference) -> Int32 = \"napi_mbt_closure_unpin\"\n\n///|\nfn napi_signal_get(env : NapiEnv, object : NapiValue, name : Bytes) -> NapiValue raise {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(env, napi_signal_get_raw(env, object, name, result), b\"Reading AbortSignal\")\n  result.val\n}\n\n///|\nfn NapiAsyncCall::cleanup(self : NapiAsyncCall) -> Unit {\n  if self.cleaned { return }\n  self.cleaned = true\n  self.promise.dispose()\n  // Clear fields before invoking JS: removeEventListener may reenter the addon.\n  let signal = self.signal\n  let listener = self.listener\n  self.signal = None\n  self.listener = None\n  self.task = None\n  if !napi_async_closing(self.env) && signal is Some(signal) && listener is Some(listener) {\n    try {\n      result_from_status(self.env, napi_signal_listen(self.env, signal.get(self.env), listener.get(self.env), false), b\"Removing abort listener\")\n    } catch { _ => () } // Preserve the original settlement; still release payload.\n  }\n  ignore(napi_listener_unpin(self.pin))\n  if listener is Some(reference) { reference.dispose() catch { _ => () } }\n  if signal is Some(reference) { reference.dispose() catch { _ => () } }\n}\n\n///|\nfn NapiAsyncCall::reject(self : NapiAsyncCall, error : Error) -> Unit {\n  if !self.promise.settled && !napi_async_closing(self.env) {\n    try { self.promise.settle(self.env, napi_error_value(self.env, error), reject=true) }\n    catch { error => { eprintln(\"napi-mbt: rejecting async call: \" + (error : Error).to_string()) } }\n  }\n  self.cleanup()\n}\n\n///|\nfn NapiAsyncCall::abort(self : NapiAsyncCall) -> Unit {\n  if self.promise.settled { return }\n  let task = self.task\n  try {\n    if self.signal is Some(signal) {\n      let reason = Ref(moonbit_dummy_napi_value())\n      result_from_status(self.env, napi_abort_reason(self.env, signal.get(self.env), reason), b\"Reading abort reason\")\n      self.promise.settle(self.env, reason.val, reject=true)\n    }\n  } catch { error => self.reject(error) }\n  if task is Some(task) {\n    task.cancel() catch { error => { eprintln(\"napi-mbt: cancelling task: \\{error}\") } }\n    ignore(napi_async_schedule(self.env))\n  }\n  self.cleanup()\n}\n\n///|\nfn NapiAsyncCall::listen(self : NapiAsyncCall, options : NapiValue) -> Unit raise {\n  if napi_is_nullish(self.env, options) { return }\n  napi_require_object(self.env, options)\n  let signal = napi_signal_get(self.env, options, b\"signal\")\n  if napi_is_nullish(self.env, signal) { return }\n  napi_require_object(self.env, signal)\n  self.signal = Some(NapiReference::new(self.env, signal))\n  if get_Bool(self.env, napi_signal_get(self.env, signal, b\"aborted\")) { self.abort(); return }\n  let listener = napi_closure_new(self.env, (env, _) => { self.abort(); set_Unit(env, ()) catch { _ => moonbit_dummy_napi_value() } })\n  self.listener = Some(NapiReference::new(self.env, listener))\n  result_from_status(self.env, napi_listener_pin(self.env, listener, self.pin), b\"Retaining abort listener\")\n  result_from_status(self.env, napi_signal_listen(self.env, signal, listener, true), b\"Adding abort listener\")\n  if !self.promise.settled && get_Bool(self.env, napi_signal_get(self.env, signal, b\"aborted\")) { self.abort() }\n}\n\n///|\nfn napi_async_call_new(env : NapiEnv) -> NapiAsyncCall raise {\n  { env, promise: napi_async_promise_new(env), task: None, signal: None, listener: None, cleaned: false, pin: napi_reference_empty() }\n}\n\n///|\nfn NapiAsyncCall::start(self : NapiAsyncCall, work : async () -> NapiValue) -> Unit raise {\n  if self.promise.settled { return }\n  self.task = Some(napi_async_submit(self.env, () => {\n    defer self.cleanup()\n    if self.promise.settled || napi_async_closing(self.env) { return }\n    try {\n      let result = work()\n      if !self.promise.settled && !napi_async_closing(self.env) { self.promise.settle(self.env, result, reject=false) }\n    } catch { error => self.reject(error) }\n  }))\n}\n\n///|\n/// A typed Promise owned by the creating Node environment. Waiting is cancellable;\n/// JS rejection values retain identity. A completed value may be awaited again.\npub struct NapiJsPromise[T] {\n  priv env : NapiEnv\n  priv mut result : Result[T, Error]?\n  priv mut promise : NapiReference?\n  priv mut handlers : Array[(NapiReference, NapiReference)]\n  priv mut waiters : Array[(Int, @napi_mbt_async.Task)]\n  priv mut next_id : Int\n  priv mut closed : Bool\n}\n\n///|\nextern \"c\" fn napi_promise_then(env : NapiEnv, promise : NapiValue, resolve : NapiValue, reject : NapiValue) -> Int = \"napi_mbt_promise_then\"\n\n///|\nextern \"c\" fn napi_reaction_inert(env : NapiEnv, value : NapiValue) -> Int = \"napi_mbt_closure_inert\"\n\n///|\nextern \"c\" fn napi_async_owns(env : NapiEnv) -> Bool = \"napi_mbt_async_owns\"\n\n///|\nfn[T] NapiJsPromise::release_handlers(self : NapiJsPromise[T]) -> Unit {\n  let handlers = self.handlers\n  let promise = self.promise\n  self.handlers = []\n  self.promise = None\n  for (pin, reference) in handlers {\n    ignore(napi_listener_unpin(pin))\n    reference.dispose() catch { _ => () }\n  }\n  if promise is Some(reference) { reference.dispose() catch { _ => () } }\n}\n\n///|\nfn[T] NapiJsPromise::wake(self : NapiJsPromise[T]) -> Unit {\n  let waiters = self.waiters\n  self.waiters = []\n  for (_, task) in waiters { task.wake() catch { _ => () } }\n  if napi_async_has(self.env) && !napi_async_closing(self.env) { ignore(napi_async_schedule(self.env)) }\n}\n\n///|\n/// Release a pending subscription; late JS reactions become inert. Idempotent.\npub fn[T] NapiJsPromise::dispose(self : NapiJsPromise[T]) -> Unit raise NapiError {\n  if !napi_async_owns(self.env) { raise napi_failure(1005, Error, \"ERR_NAPI_ENVIRONMENT\", \"Disposing a Promise outside its owning environment\") }\n  self.close_owned()\n}\n\n///|\nfn[T] NapiJsPromise::close_owned(self : NapiJsPromise[T]) -> Unit {\n  if self.closed { return }\n  self.closed = true\n  self.result = None\n  self.release_handlers()\n  self.wake()\n}\n\n///|\npub async fn[T] NapiJsPromise::wait(self : NapiJsPromise[T]) -> T {\n  if napi_async_closing(self.env) { raise napi_failure(1005, Error, \"ERR_NAPI_ENVIRONMENT\", \"Promise belongs to another or closed environment\") }\n  if self.closed { raise napi_failure(1006, Error, \"ERR_NAPI_DISPOSED\", \"Promise subscription is closed\") }\n  let id = self.next_id\n  self.next_id += 1\n  if self.result is None {\n    self.waiters.push((id, napi_async_get(self.env).scheduler.current_task()))\n  }\n  defer {\n    self.waiters = self.waiters.filter(waiter => waiter.0 != id)\n    if self.result is None && self.waiters.is_empty() { self.close_owned() }\n  }\n  while self.result is None && !self.closed { @napi_mbt_async.suspend() }\n  match self.result {\n    Some(Ok(value)) => value\n    Some(Err(error)) => raise error\n    None => raise napi_failure(1006, Error, \"ERR_NAPI_DISPOSED\", \"Promise subscription is closed\")\n  }\n}\n\n///|\nfn[T] get_NapiJsPromise(env : NapiEnv, value : NapiValue, convert : (NapiEnv, NapiValue) -> T raise) -> NapiJsPromise[T] raise {\n  let valid = Ref(false)\n  result_from_status(env, napi_is_promise(env, value, valid), b\"Reading JavaScript Promise\")\n  if !valid.val { raise napi_failure(1, TypeError, \"ERR_NAPI_PROMISE\", \"Expected JavaScript Promise\") }\n  let state : NapiJsPromise[T] = { env, result: None, promise: Some(NapiReference::new(env, value)), handlers: [], waiters: [], next_id: 0, closed: false }\n  errdefer state.close_owned()\n  fn reaction(reject : Bool) -> NapiValue raise {\n    let callback = napi_closure_new(env, fn(env, args) {\n      if !state.closed && state.result is None {\n        state.result = Some(try {\n          let value = args.get(env, 0)\n          if reject { raise NapiError::JavaScriptException(NapiReference::new(env, value)) }\n          Ok(convert(env, value))\n        } catch { error => Err(error) })\n        state.release_handlers()\n        state.wake()\n      }\n      set_Unit(env, ()) catch { _ => moonbit_dummy_napi_value() }\n    })\n    let reference = NapiReference::new(env, callback)\n    let pin = napi_reference_empty()\n    result_from_status(env, napi_reaction_inert(env, callback), b\"Preparing Promise reaction\")\n    result_from_status(env, napi_listener_pin(env, callback, pin), b\"Retaining Promise reaction\")\n    state.handlers.push((pin, reference))\n    callback\n  }\n  let resolve = reaction(false)\n  let reject = reaction(true)\n  result_from_status(env, napi_promise_then(env, value, resolve, reject), b\"Subscribing to JavaScript Promise\")\n  state\n}\n\n///|\npriv struct NapiStreamRead[T] {\n  mut result : Result[T?, Error]?\n  task : @napi_mbt_async.Task\n}\n\n///|\n/// A demand-driven iterator owned by one Node environment. close is idempotent.\npub struct NapiAsyncIterator[T] {\n  priv env : NapiEnv\n  priv produce : async () -> T?\n  priv finish : async () -> Unit\n  priv mut closed : Bool\n  priv mut finished : Result[Unit, Error]?\n  priv mut queue : Array[NapiStreamRead[T]]\n  priv mut active : NapiStreamRead[T]?\n  priv mut producer : @napi_mbt_async.Task?\n  priv mut lifetime : @napi_mbt_async.Task?\n  priv mut waiters : Array[@napi_mbt_async.Task]\n  priv mut pins : Array[(NapiReference, NapiReference)]\n}\n\n///|\nfn[T] NapiAsyncIterator::check(self : NapiAsyncIterator[T]) -> Unit raise {\n  if !napi_async_owns(self.env) { raise napi_failure(1005, Error, \"ERR_NAPI_ENVIRONMENT\", \"Stream belongs to another environment\") }\n}\n\n///|\nfn[T] NapiAsyncIterator::notify(self : NapiAsyncIterator[T]) -> Unit {\n  let tasks = self.waiters\n  self.waiters = []\n  for task in tasks { task.wake() catch { _ => () } }\n  if !napi_async_closing(self.env) { ignore(napi_async_schedule(self.env)) }\n}\n\n///|\nfn[T] NapiAsyncIterator::begin_close(self : NapiAsyncIterator[T]) -> Unit {\n  if self.closed { return }\n  self.closed = true\n  let tasks = self.queue\n  self.queue = []\n  if self.active is Some(request) { tasks.push(request) }\n  for request in tasks {\n    if request.result is None { request.result = Some(Ok(None)) }\n    request.task.wake() catch { _ => () }\n  }\n  if self.producer is Some(task) { task.cancel() catch { _ => () } }\n  if self.lifetime is Some(task) { task.wake() catch { _ => () } }\n  self.notify()\n}\n\n///|\npub fn[T] NapiAsyncIterator::new(produce : async () -> T?, close : async () -> Unit) -> NapiAsyncIterator[T] raise {\n  let env = napi_current_env()\n  let state : NapiAsyncIterator[T] = { env, produce, finish: close, closed: false, finished: None, queue: [], active: None, producer: None, lifetime: None, waiters: [], pins: [] }\n  state.lifetime = Some(napi_async_submit(env, () => {\n    defer @napi_mbt_async.protect(() => {\n      state.begin_close()\n      state.finished = Some(try { (state.finish)(); Ok(()) } catch { error => Err(error) })\n      let pins = state.pins\n      state.pins = []\n      for (pin, reference) in pins { ignore(napi_listener_unpin(pin)); reference.dispose() catch { _ => () } }\n      state.lifetime = None\n      state.notify()\n    })\n    while !state.closed { @napi_mbt_async.suspend() }\n  }))\n  state\n}\n\n///|\npub async fn[T] NapiAsyncIterator::next(self : NapiAsyncIterator[T]) -> T? {\n  self.check()\n  if self.closed { return None }\n  let request : NapiStreamRead[T] = { result: None, task: napi_async_get(self.env).scheduler.current_task() }\n  self.queue.push(request)\n  defer { if request.result is None { self.begin_close() } }\n  if self.producer is None {\n    self.producer = Some(napi_async_submit(self.env, () => {\n      defer { self.producer = None; self.active = None }\n      while !self.closed && !self.queue.is_empty() {\n        let request = self.queue[0]\n        self.queue = self.queue[1:].to_owned()\n        self.active = Some(request)\n        let result = try { Ok((self.produce)()) } catch { error => Err(error) }\n        if request.result is None { request.result = Some(result) }\n        request.task.wake() catch { _ => () }\n        self.active = None\n        if result is Ok(None) || result is Err(_) { self.begin_close() }\n      }\n    }))\n  }\n  while request.result is None { @napi_mbt_async.suspend() }\n  match request.result.unwrap() { Ok(None) => { self.close(); None }; Ok(value) => value; Err(error) => raise error }\n}\n\n///|\npub async fn[T] NapiAsyncIterator::close(self : NapiAsyncIterator[T]) -> Unit {\n  self.check()\n  self.begin_close()\n  while self.finished is None {\n    self.waiters.push(napi_async_get(self.env).scheduler.current_task())\n    @napi_mbt_async.suspend()\n  }\n  match self.finished.unwrap() { Ok(_) => (); Err(error) => raise error }\n}\n\n///|\n#borrow(result)\nextern \"c\" fn napi_stream_factory(env : NapiEnv, input : Bool, a : NapiValue, b : NapiValue, result : Ref[NapiValue]) -> Int = \"napi_mbt_stream_factory\"\n\n///|\nfn napi_stream_make(env : NapiEnv, input : Bool, a : NapiValue, b : NapiValue) -> NapiValue raise {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(env, napi_stream_factory(env, input, a, b, result), b\"Creating stream adapter\")\n  result.val\n}\n\n///|\nfn[T] get_NapiAsyncIterator(env : NapiEnv, value : NapiValue, convert : (NapiEnv, NapiValue) -> T raise) -> NapiAsyncIterator[T] raise {\n  let normalized = napi_stream_make(env, true, value, napi_null(env))\n  let next = NapiCallback::new(env, napi_field_get(env, normalized, \"next\", true))\n  errdefer { next.dispose() catch { _ => () } }\n  let close = NapiCallback::new(env, napi_field_get(env, normalized, \"close\", true))\n  errdefer { close.dispose() catch { _ => () } }\n  NapiAsyncIterator::new(() => {\n    if napi_async_closing(env) { raise napi_failure(1004, Error, \"ERR_NAPI_ENV_CLOSED\", \"Reading stream during shutdown\") }\n    let promise = get_NapiJsPromise(env, next.call(napi_array_new(env, 0)), fn(env, value) {\n      napi_require_object(env, value)\n      if get_Bool(env, napi_field_get(env, value, \"done\", true)) { None } else { Some(convert(env, napi_field_get(env, value, \"value\", true))) }\n    })\n    promise.wait()\n  }, () => {\n    defer { next.dispose() catch { _ => () }; close.dispose() catch { _ => () } }\n    if !napi_async_closing(env) {\n      let promise = get_NapiJsPromise(env, close.call(napi_array_new(env, 0)), fn(_, _) { () })\n      promise.wait()\n    }\n  })\n}\n\n///|\nfn[T] set_NapiAsyncIterator(env : NapiEnv, value : NapiAsyncIterator[T], convert : (NapiEnv, T) -> NapiValue raise) -> NapiValue raise {\n  value.check()\n  fn callback(closing : Bool) -> NapiValue raise {\n    let callback = napi_closure_new(env, fn(env, _) {\n      try {\n        let call = napi_async_call_new(env)\n        call.start(() => {\n          let item = if closing { value.close(); None } else { value.next() }\n          if napi_async_closing(env) { raise napi_failure(1004, Error, \"ERR_NAPI_ENV_CLOSED\", \"Completing stream during shutdown\") }\n          let result = napi_object_new(env)\n          napi_field_set(env, result, \"done\", set_Bool(env, item is None))\n          if item is Some(item) {\n            try { napi_field_set(env, result, \"value\", convert(env, item)) }\n            catch { error => { value.begin_close(); raise error } }\n          }\n          result\n        })\n        call.promise.promise\n      } catch { error => { throw_napi_error(env, error); moonbit_dummy_napi_value() } }\n    })\n    let pin = napi_reference_empty()\n    let reference = NapiReference::new(env, callback)\n    result_from_status(env, napi_listener_pin(env, callback, pin), b\"Retaining stream callback\")\n    value.pins.push((pin, reference))\n    callback\n  }\n  let next = callback(false)\n  let close = callback(true)\n  napi_stream_make(env, false, next, close)\n}\n",
  "async/LIBUV-LICENSE": "Copyright Joyent, Inc. and other Node contributors. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to\ndeal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE.\n",
  "template/napi_reference.mbt": "///|\n/// An owned, environment-bound JavaScript value, safe across handle scopes.\npub type NapiReference\n\n///|\nextern \"c\" fn napi_reference_empty() -> NapiReference = \"napi_mbt_callback_empty\"\n\n///|\n#borrow(reference)\nextern \"c\" fn napi_reference_bind(\n  env : NapiEnv,\n  value : NapiValue,\n  reference : NapiReference,\n) -> Int32 = \"napi_mbt_reference_bind\"\n\n///|\n#borrow(reference, result)\nextern \"c\" fn napi_reference_get(\n  env : NapiEnv,\n  reference : NapiReference,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_mbt_reference_get\"\n\n///|\n#borrow(reference)\nextern \"c\" fn napi_reference_dispose(reference : NapiReference) -> Int32 = \"napi_mbt_owned_reference_dispose\"\n\n///|\n#borrow(reference)\nextern \"c\" fn napi_reference_throw(\n  env : NapiEnv,\n  reference : NapiReference,\n) -> Int32 = \"napi_mbt_reference_throw\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_take_exception(\n  env : NapiEnv,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_mbt_take_exception\"\n\n///|\npub fn NapiReference::new(\n  env : NapiEnv,\n  value : NapiValue,\n) -> NapiReference raise NapiError {\n  let reference = napi_reference_empty()\n  result_from_status(\n    env,\n    napi_reference_bind(env, value, reference),\n    b\"Retaining JavaScript value\",\n  )\n  reference\n}\n\n///|\npub fn NapiReference::get(\n  self : NapiReference,\n  env : NapiEnv,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_reference_get(env, self, result),\n    b\"Reading JavaScript reference\",\n  )\n  result.val\n}\n\n///|\npub fn NapiReference::dispose(self : NapiReference) -> Unit raise NapiError {\n  let status = napi_reference_dispose(self)\n  if status != 0 {\n    raise napi_failure(\n      status,\n      Error,\n      \"ERR_NAPI_ENVIRONMENT\",\n      \"Disposing JavaScript reference\",\n    )\n  }\n}\n\n///|\npub fn napi_error_value(\n  env : NapiEnv,\n  error : Error,\n) -> NapiValue raise NapiError {\n  throw_napi_error(env, error)\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_take_exception(env, result),\n    b\"Capturing JavaScript exception\",\n  )\n  result.val\n}\n",
  "RUNTIME-LICENSE": "MIT License\n\nCopyright (c) 2026 UnMoonBit\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.",
  "template/napi_collections.mbt": "///|\n#borrow(result)\nextern \"c\" fn napi_keys_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_keys\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_key_get_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  key : NapiValue,\n  result : Ref[NapiValue],\n) -> Int32 = \"napi_get_property\"\n\n///|\nextern \"c\" fn napi_key_set_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  key : NapiValue,\n  field : NapiValue,\n) -> Int32 = \"moonbit_napi_key_set\"\n\n///|\n#borrow(result)\nextern \"c\" fn napi_is_array_raw(\n  env : NapiEnv,\n  value : NapiValue,\n  result : Ref[Bool],\n) -> Int32 = \"moonbit_napi_is_array\"\n\n///|\npub fn napi_object_keys(\n  env : NapiEnv,\n  value : NapiValue,\n) -> Array[String] raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_keys_raw(env, value, result),\n    b\"Reading own enumerable string keys\",\n  )\n  Array::makei(napi_array_length(env, result.val), i => {\n    get_String(env, napi_array_get(env, result.val, i))\n  })\n}\n\n///|\npub fn napi_key_get(\n  env : NapiEnv,\n  value : NapiValue,\n  key : String,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_key_get_raw(env, value, set_String(env, key), result),\n    b\"Reading record property\",\n  )\n  result.val\n}\n\n///|\npub fn napi_key_set(\n  env : NapiEnv,\n  value : NapiValue,\n  key : String,\n  field : NapiValue,\n) -> Unit raise NapiError {\n  result_from_status(\n    env,\n    napi_key_set_raw(env, value, set_String(env, key), field),\n    b\"Writing record property\",\n  )\n}\n\n///|\npub fn napi_read_json(\n  env : NapiEnv,\n  value : NapiValue,\n  path : Array[NapiValue],\n) -> Json raise NapiError {\n  let kind = Ref(0)\n  result_from_status(\n    env,\n    napi_value_kind(env, value, kind),\n    b\"Reading JSON kind\",\n  )\n  match kind.val {\n    1 => Json::null()\n    2 => Json(get_Bool(env, value))\n    3 => {\n      let number = get_Double(env, value)\n      if number.is_nan() || number.is_inf() {\n        raise napi_failure(\n          1,\n          TypeError,\n          \"ERR_NAPI_JSON\",\n          \"JSON numbers must be finite\",\n        )\n      }\n      Json(number)\n    }\n    4 => Json(get_String(env, value))\n    6 => {\n      napi_enter_value(env, value, path)\n      defer ignore(path.pop())\n      let array = Ref(false)\n      result_from_status(\n        env,\n        napi_is_array_raw(env, value, array),\n        b\"Reading JSON array\",\n      )\n      if array.val {\n        Json::array(\n          Array::makei(napi_array_length(env, value), i => {\n            napi_read_json(env, napi_array_get(env, value, i), path)\n          }),\n        )\n      } else {\n        let fields : Map[String, Json] = Map([])\n        for key in napi_object_keys(env, value) {\n          fields[key] = napi_read_json(env, napi_key_get(env, value, key), path)\n        }\n        Json::object(fields)\n      }\n    }\n    _ =>\n      raise napi_failure(1, TypeError, \"ERR_NAPI_JSON\", \"Expected a JSON value\")\n  }\n}\n\n///|\npub fn napi_write_json(\n  env : NapiEnv,\n  value : Json,\n  depth : Int32,\n) -> NapiValue raise NapiError {\n  napi_check_depth(depth)\n  match value {\n    Null => napi_null(env)\n    True => set_Bool(env, true)\n    False => set_Bool(env, false)\n    String(text) => set_String(env, text)\n    Number(number, ..) => {\n      if number.is_nan() || number.is_inf() {\n        raise napi_failure(\n          1,\n          TypeError,\n          \"ERR_NAPI_JSON\",\n          \"JSON numbers must be finite\",\n        )\n      }\n      set_Double(env, number)\n    }\n    Array(items) => {\n      let result = napi_array_new(env, items.length())\n      for i, item in items {\n        napi_array_set(env, result, i, napi_write_json(env, item, depth + 1))\n      }\n      result\n    }\n    Object(fields) => {\n      let result = napi_object_new(env)\n      for key, item in fields {\n        napi_key_set(env, result, key, napi_write_json(env, item, depth + 1))\n      }\n      result\n    }\n  }\n}\n",
  "template/napi_owned_binary.mbt": "///|\n/// Owned data copied from/to JavaScript. No JavaScript handles or borrowed storage.\npub struct NapiUint8Array {\n  priv data : FixedArray[Byte]\n}\n\n///|\npub fn NapiUint8Array::new(values : Array[Byte]) -> NapiUint8Array {\n  { data: FixedArray::makei(values.length(), i => values[i]), }\n}\n\n///|\npub fn NapiUint8Array::length(self : NapiUint8Array) -> Int32 {\n  self.data.length()\n}\n\n///|\npub fn NapiUint8Array::to_array(self : NapiUint8Array) -> Array[Byte] {\n  Array::makei(self.data.length(), i => self.data[i])\n}\n\n///|\n#borrow(status)\nextern \"c\" fn napi_read_Uint8Array(\n  env : NapiEnv,\n  value : NapiValue,\n  status : Ref[Int32],\n) -> FixedArray[Byte] = \"moonbit_napi_read_Uint8Array\"\n\n///|\n#borrow(data, result)\nextern \"c\" fn napi_write_Uint8Array(\n  env : NapiEnv,\n  data : FixedArray[Byte],\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_write_Uint8Array\"\n\n///|\npub fn get_NapiUint8Array(\n  env : NapiEnv,\n  value : NapiValue,\n) -> NapiUint8Array raise NapiError {\n  let status = Ref(0)\n  let data = napi_read_Uint8Array(env, value, status)\n  result_from_status(env, status.val, b\"Reading owned Uint8Array\")\n  { data, }\n}\n\n///|\npub fn set_NapiUint8Array(\n  env : NapiEnv,\n  value : NapiUint8Array,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_write_Uint8Array(env, value.data, result),\n    b\"Writing owned Uint8Array\",\n  )\n  result.val\n}\n\n///|\n/// Owned data copied from/to JavaScript. No JavaScript handles or borrowed storage.\npub struct NapiInt32Array {\n  priv data : FixedArray[Int32]\n}\n\n///|\npub fn NapiInt32Array::new(values : Array[Int32]) -> NapiInt32Array {\n  { data: FixedArray::makei(values.length(), i => values[i]), }\n}\n\n///|\npub fn NapiInt32Array::length(self : NapiInt32Array) -> Int32 {\n  self.data.length()\n}\n\n///|\npub fn NapiInt32Array::to_array(self : NapiInt32Array) -> Array[Int32] {\n  Array::makei(self.data.length(), i => self.data[i])\n}\n\n///|\n#borrow(status)\nextern \"c\" fn napi_read_Int32Array(\n  env : NapiEnv,\n  value : NapiValue,\n  status : Ref[Int32],\n) -> FixedArray[Int32] = \"moonbit_napi_read_Int32Array\"\n\n///|\n#borrow(data, result)\nextern \"c\" fn napi_write_Int32Array(\n  env : NapiEnv,\n  data : FixedArray[Int32],\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_write_Int32Array\"\n\n///|\npub fn get_NapiInt32Array(\n  env : NapiEnv,\n  value : NapiValue,\n) -> NapiInt32Array raise NapiError {\n  let status = Ref(0)\n  let data = napi_read_Int32Array(env, value, status)\n  result_from_status(env, status.val, b\"Reading owned Int32Array\")\n  { data, }\n}\n\n///|\npub fn set_NapiInt32Array(\n  env : NapiEnv,\n  value : NapiInt32Array,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_write_Int32Array(env, value.data, result),\n    b\"Writing owned Int32Array\",\n  )\n  result.val\n}\n\n///|\n/// Owned data copied from/to JavaScript. No JavaScript handles or borrowed storage.\npub struct NapiUint32Array {\n  priv data : FixedArray[UInt32]\n}\n\n///|\npub fn NapiUint32Array::new(values : Array[UInt32]) -> NapiUint32Array {\n  { data: FixedArray::makei(values.length(), i => values[i]), }\n}\n\n///|\npub fn NapiUint32Array::length(self : NapiUint32Array) -> Int32 {\n  self.data.length()\n}\n\n///|\npub fn NapiUint32Array::to_array(self : NapiUint32Array) -> Array[UInt32] {\n  Array::makei(self.data.length(), i => self.data[i])\n}\n\n///|\n#borrow(status)\nextern \"c\" fn napi_read_Uint32Array(\n  env : NapiEnv,\n  value : NapiValue,\n  status : Ref[Int32],\n) -> FixedArray[UInt32] = \"moonbit_napi_read_Uint32Array\"\n\n///|\n#borrow(data, result)\nextern \"c\" fn napi_write_Uint32Array(\n  env : NapiEnv,\n  data : FixedArray[UInt32],\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_write_Uint32Array\"\n\n///|\npub fn get_NapiUint32Array(\n  env : NapiEnv,\n  value : NapiValue,\n) -> NapiUint32Array raise NapiError {\n  let status = Ref(0)\n  let data = napi_read_Uint32Array(env, value, status)\n  result_from_status(env, status.val, b\"Reading owned Uint32Array\")\n  { data, }\n}\n\n///|\npub fn set_NapiUint32Array(\n  env : NapiEnv,\n  value : NapiUint32Array,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_write_Uint32Array(env, value.data, result),\n    b\"Writing owned Uint32Array\",\n  )\n  result.val\n}\n\n///|\n/// Owned data copied from/to JavaScript. No JavaScript handles or borrowed storage.\npub struct NapiFloat32Array {\n  priv data : FixedArray[Float]\n}\n\n///|\npub fn NapiFloat32Array::new(values : Array[Float]) -> NapiFloat32Array {\n  { data: FixedArray::makei(values.length(), i => values[i]), }\n}\n\n///|\npub fn NapiFloat32Array::length(self : NapiFloat32Array) -> Int32 {\n  self.data.length()\n}\n\n///|\npub fn NapiFloat32Array::to_array(self : NapiFloat32Array) -> Array[Float] {\n  Array::makei(self.data.length(), i => self.data[i])\n}\n\n///|\n#borrow(status)\nextern \"c\" fn napi_read_Float32Array(\n  env : NapiEnv,\n  value : NapiValue,\n  status : Ref[Int32],\n) -> FixedArray[Float] = \"moonbit_napi_read_Float32Array\"\n\n///|\n#borrow(data, result)\nextern \"c\" fn napi_write_Float32Array(\n  env : NapiEnv,\n  data : FixedArray[Float],\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_write_Float32Array\"\n\n///|\npub fn get_NapiFloat32Array(\n  env : NapiEnv,\n  value : NapiValue,\n) -> NapiFloat32Array raise NapiError {\n  let status = Ref(0)\n  let data = napi_read_Float32Array(env, value, status)\n  result_from_status(env, status.val, b\"Reading owned Float32Array\")\n  { data, }\n}\n\n///|\npub fn set_NapiFloat32Array(\n  env : NapiEnv,\n  value : NapiFloat32Array,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_write_Float32Array(env, value.data, result),\n    b\"Writing owned Float32Array\",\n  )\n  result.val\n}\n\n///|\n/// Owned data copied from/to JavaScript. No JavaScript handles or borrowed storage.\npub struct NapiFloat64Array {\n  priv data : FixedArray[Double]\n}\n\n///|\npub fn NapiFloat64Array::new(values : Array[Double]) -> NapiFloat64Array {\n  { data: FixedArray::makei(values.length(), i => values[i]), }\n}\n\n///|\npub fn NapiFloat64Array::length(self : NapiFloat64Array) -> Int32 {\n  self.data.length()\n}\n\n///|\npub fn NapiFloat64Array::to_array(self : NapiFloat64Array) -> Array[Double] {\n  Array::makei(self.data.length(), i => self.data[i])\n}\n\n///|\n#borrow(status)\nextern \"c\" fn napi_read_Float64Array(\n  env : NapiEnv,\n  value : NapiValue,\n  status : Ref[Int32],\n) -> FixedArray[Double] = \"moonbit_napi_read_Float64Array\"\n\n///|\n#borrow(data, result)\nextern \"c\" fn napi_write_Float64Array(\n  env : NapiEnv,\n  data : FixedArray[Double],\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_write_Float64Array\"\n\n///|\npub fn get_NapiFloat64Array(\n  env : NapiEnv,\n  value : NapiValue,\n) -> NapiFloat64Array raise NapiError {\n  let status = Ref(0)\n  let data = napi_read_Float64Array(env, value, status)\n  result_from_status(env, status.val, b\"Reading owned Float64Array\")\n  { data, }\n}\n\n///|\npub fn set_NapiFloat64Array(\n  env : NapiEnv,\n  value : NapiFloat64Array,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_write_Float64Array(env, value.data, result),\n    b\"Writing owned Float64Array\",\n  )\n  result.val\n}\n",
  "template/napi_arraybuffer.mbt": "///|\n/// An owned immutable byte sequence, copied from/to an independent ArrayBuffer.\npub struct NapiArrayBuffer {\n  priv data : Bytes\n}\n\n///|\npub fn NapiArrayBuffer::from_bytes(data : Bytes) -> NapiArrayBuffer {\n  { data, }\n}\n\n///|\npub fn NapiArrayBuffer::to_bytes(self : NapiArrayBuffer) -> Bytes {\n  self.data\n}\n\n///|\npub fn NapiArrayBuffer::byte_length(self : NapiArrayBuffer) -> Int32 {\n  self.data.length()\n}\n\n///|\n#borrow(status)\nextern \"c\" fn napi_read_ArrayBuffer(\n  env : NapiEnv,\n  value : NapiValue,\n  status : Ref[Int32],\n) -> Bytes = \"moonbit_napi_read_ArrayBuffer\"\n\n///|\n#borrow(data, result)\nextern \"c\" fn napi_write_ArrayBuffer(\n  env : NapiEnv,\n  data : Bytes,\n  result : Ref[NapiValue],\n) -> Int32 = \"moonbit_napi_write_ArrayBuffer\"\n\n///|\npub fn get_NapiArrayBuffer(\n  env : NapiEnv,\n  value : NapiValue,\n) -> NapiArrayBuffer raise NapiError {\n  let status = Ref(0)\n  let data = napi_read_ArrayBuffer(env, value, status)\n  result_from_status(env, status.val, b\"Reading owned ArrayBuffer\")\n  { data, }\n}\n\n///|\npub fn set_NapiArrayBuffer(\n  env : NapiEnv,\n  value : NapiArrayBuffer,\n) -> NapiValue raise NapiError {\n  let result = Ref(moonbit_dummy_napi_value())\n  result_from_status(\n    env,\n    napi_write_ArrayBuffer(env, value.data, result),\n    b\"Writing owned ArrayBuffer\",\n  )\n  result.val\n}\n",
  "template/napi_owned_binary.h": "#ifndef NAPI_MBT_OWNED_BINARY_H\n#define NAPI_MBT_OWNED_BINARY_H\n\n/* Owner-thread-only conversion. Both directions copy; no JS backing pointer or\n * JS handle is stored in a MoonBit value. The arrays may outlive this call. */\nstatic size_t napi_mbt_binary_width(int kind) {\n    return kind == 1 || kind == -1 ? 1 : kind == 8 ? 8 : 4;\n}\n\nstatic void *napi_mbt_binary_allocate(int kind, int32_t length) {\n    switch (kind) {\n    case 5: case 6: return moonbit_make_int32_array_raw(length);\n    case 7: return moonbit_make_float_array_raw(length);\n    case 8: return moonbit_make_double_array_raw(length);\n    default: return moonbit_make_bytes_raw(length);\n    }\n}\n\nstatic void *napi_mbt_read_owned_binary(\n    napi_env env, napi_value value, int kind, int32_t *error) {\n    void *data = NULL;\n    size_t length = 0;\n    napi_status status = napi_ok;\n#if NAPI_VERSION >= 7\n    napi_value backing = value;\n    bool valid = false, detached = false;\n    if (kind != -1) {\n        status = napi_is_typedarray(env, value, &valid);\n        if (status != napi_ok) goto done;\n        if (!valid) { status = napi_invalid_arg; goto done; }\n        napi_typedarray_type type;\n        size_t offset;\n        status = napi_get_typedarray_info(env, value, &type, &length, &data, &backing, &offset);\n        if (status != napi_ok) goto done;\n        if ((int)type != kind) { status = napi_invalid_arg; goto done; }\n    }\n    /* napi_is_arraybuffer explicitly excludes SharedArrayBuffer, including a\n     * shared store behind an otherwise valid typed view. Never copy racing data. */\n    status = napi_is_arraybuffer(env, backing, &valid);\n    if (status != napi_ok) goto done;\n    if (!valid) { status = napi_invalid_arg; goto done; }\n    status = napi_is_detached_arraybuffer(env, backing, &detached);\n    if (status != napi_ok) goto done;\n    if (detached) { *error = 1001; return napi_mbt_binary_allocate(kind, 0); }\n    if (kind == -1) {\n        status = napi_get_arraybuffer_info(env, backing, &data, &length);\n        if (status != napi_ok) goto done;\n    }\n    if (length > INT_MAX || length > SIZE_MAX / napi_mbt_binary_width(kind)) {\n        *error = 1002;\n        return napi_mbt_binary_allocate(kind, 0);\n    }\n    if (length && data == NULL) { status = napi_invalid_arg; goto done; }\ndone:\n#else\n    (void)env; (void)value;\n    status = napi_generic_failure;\n#endif\n    *error = (int32_t)status;\n    void *result = napi_mbt_binary_allocate(kind, status == napi_ok ? (int32_t)length : 0);\n    if (status == napi_ok && length) memcpy(result, data, length * napi_mbt_binary_width(kind));\n    return result;\n}\n\nstatic int napi_mbt_write_owned_binary(\n    napi_env env, const void *array, int kind, napi_value *result) {\n    size_t length = (size_t)Moonbit_array_length(array);\n    size_t width = napi_mbt_binary_width(kind);\n    if (length > SIZE_MAX / width) return 1002;\n    void *data = NULL;\n    napi_value backing;\n    napi_status status = napi_create_arraybuffer(env, length * width, &data, &backing);\n    if (status != napi_ok) return (int)status;\n    if (length) memcpy(data, array, length * width);\n    if (kind == -1) { *result = backing; return (int)napi_ok; }\n    return (int)napi_create_typedarray(env, (napi_typedarray_type)kind, length, backing, 0, result);\n}\n\n#define NAPI_MBT_BINARY_API(tag, type, kind) \\\nMOONBIT_FFI_EXPORT type *moonbit_napi_read_##tag(napi_env env, napi_value value, int32_t *status) { \\\n    return (type *)napi_mbt_read_owned_binary(env, value, kind, status); \\\n} \\\nMOONBIT_FFI_EXPORT int moonbit_napi_write_##tag(napi_env env, const type *data, napi_value *result) { \\\n    return napi_mbt_write_owned_binary(env, data, kind, result); \\\n}\n\nNAPI_MBT_BINARY_API(Uint8Array, uint8_t, 1)\nNAPI_MBT_BINARY_API(Int32Array, int32_t, 5)\nNAPI_MBT_BINARY_API(Uint32Array, uint32_t, 6)\nNAPI_MBT_BINARY_API(Float32Array, float, 7)\nNAPI_MBT_BINARY_API(Float64Array, double, 8)\nNAPI_MBT_BINARY_API(ArrayBuffer, uint8_t, -1)\n#undef NAPI_MBT_BINARY_API\n#endif\n",
  "async/native_tasks.mbt": "///|\npriv type NapiNativeTaskHandle\n\n///|\n#borrow(name, input, status)\nextern \"c\" fn napi_native_start(env : NapiEnv, name : Bytes, input : Bytes, status : Ref[Int]) -> NapiNativeTaskHandle = \"napi_mbt_native_start\"\n\n///|\n#borrow(handle)\nextern \"c\" fn napi_native_close(handle : NapiNativeTaskHandle) -> Unit = \"napi_mbt_native_close\"\n\n///|\n#borrow(handle)\nextern \"c\" fn napi_native_status(handle : NapiNativeTaskHandle) -> Int = \"napi_mbt_native_status\"\n\n///|\n#borrow(handle)\nextern \"c\" fn napi_native_output(handle : NapiNativeTaskHandle) -> Bytes = \"napi_mbt_native_output\"\n\n///|\n#borrow(handle)\nextern \"c\" fn napi_native_error(handle : NapiNativeTaskHandle, code : Bool) -> Bytes = \"napi_mbt_native_error\"\n\n///|\n/// Invoke a task registered in napi-mbt.json. Input and output are C-owned copies\n/// while running; cancellation requests cooperative termination and releases the\n/// caller immediately. The native work retains its storage until it exits.\npub async fn napi_native_task(name : String, input : Bytes) -> Bytes {\n  if name.contains(\"\\u0000\") { raise napi_failure(1, TypeError, \"ERR_NAPI_NATIVE_NAME\", \"Invalid native task name\") }\n  let env = napi_current_env()\n  let status = Ref(0)\n  let handle = napi_native_start(env, @utf8.encode(name), input, status)\n  defer napi_native_close(handle)\n  result_from_status(env, status.val, b\"Starting registered native task\")\n  while napi_native_status(handle) == 0 { @napi_mbt_tasks.sleep(1) }\n  match napi_native_status(handle) {\n    1 => napi_native_output(handle)\n    3 => raise napi_failure(11, Error, \"ERR_NAPI_NATIVE_CANCELLED\", \"Native task cancelled\")\n    _ => raise napi_failure(9, Error, @utf8.decode_lossy(napi_native_error(handle, true)), @utf8.decode_lossy(napi_native_error(handle, false)))\n  }\n}\n",
  "template/napi_native_task.h": "#ifndef NAPI_MBT_NATIVE_TASK_H\n#define NAPI_MBT_NATIVE_TASK_H\n#include <stddef.h>\n#include <stdint.h>\n#define NAPI_MBT_TASK_ABI_VERSION 1\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/* ABI 1: called on a Node worker thread. The context and input are borrowed\n * until the task returns. Do not retain them, call JS, or touch MoonBit values.\n * result/error copy their data immediately. Submit exactly one result or error.\n * C++ entry points must catch every exception before returning across this ABI. */\ntypedef struct napi_mbt_task_context napi_mbt_task_context;\ntypedef void (*napi_mbt_task_function)(napi_mbt_task_context *);\nuint32_t napi_mbt_task_abi_version(void);\nconst uint8_t *napi_mbt_task_input(napi_mbt_task_context *, size_t *length);\nint napi_mbt_task_cancelled(napi_mbt_task_context *);\nint napi_mbt_task_result(napi_mbt_task_context *, const void *, size_t length);\nint napi_mbt_task_error(napi_mbt_task_context *, const char *code, const char *message);\n#ifdef __cplusplus\n}\n#endif\n#endif\n",
  "template/napi_native_task_runtime.h": "#ifndef NAPI_MBT_NATIVE_TASK_RUNTIME_H\n#define NAPI_MBT_NATIVE_TASK_RUNTIME_H\n#include <moonbit.h>\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n#include \"napi_native_task.h\"\n#if NAPI_VERSION < 8\n#error Native tasks require N-API 8 asynchronous cleanup\n#endif\n#ifdef _WIN32\n#include <windows.h>\ntypedef volatile LONG napi_mbt_task_atomic;\nstatic void napi_mbt_task_cancel_store(napi_mbt_task_atomic *v) { InterlockedExchange(v, 1); }\nstatic int napi_mbt_task_cancel_load(napi_mbt_task_atomic *v) { return InterlockedCompareExchange(v, 0, 0) != 0; }\n#else\n#include <stdatomic.h>\ntypedef atomic_int napi_mbt_task_atomic;\nstatic void napi_mbt_task_cancel_store(napi_mbt_task_atomic *v) { atomic_store(v, 1); }\nstatic int napi_mbt_task_cancel_load(napi_mbt_task_atomic *v) { return atomic_load(v); }\n#endif\n\nstruct napi_mbt_task_context {\n    /* Only cancellation is shared. Execute owns the output until complete runs.\n     * The work queue publishes that output to complete. All other state and\n     * refcounts are owner-thread-only; there are no MoonBit/JS references here. */\n    napi_mbt_task_atomic cancelled;\n    uint8_t *input, *output;\n    size_t input_length, output_length;\n    char *code, *message;\n    int submitted, allocation_failed;\n    napi_env env;\n    napi_async_work work;\n    napi_async_cleanup_hook_handle cleanup;\n    napi_mbt_task_function execute;\n    int refs, done, closing;\n};\ntypedef struct { napi_mbt_task_context *context; } napi_mbt_task_handle;\n\nuint32_t napi_mbt_task_abi_version(void) { return NAPI_MBT_TASK_ABI_VERSION; }\nconst uint8_t *napi_mbt_task_input(napi_mbt_task_context *c, size_t *length) {\n    if (length) *length = c->input_length;\n    return c->input;\n}\nint napi_mbt_task_cancelled(napi_mbt_task_context *c) { return napi_mbt_task_cancel_load(&c->cancelled); }\nint napi_mbt_task_result(napi_mbt_task_context *c, const void *data, size_t length) {\n    if (c->submitted || length > INT_MAX || (length && !data)) return 0;\n    c->submitted = 1;\n    c->output = malloc(length ? length : 1);\n    if (!c->output) { c->allocation_failed = 1; return 0; }\n    if (length) memcpy(c->output, data, length);\n    c->output_length = length;\n    return 1;\n}\nstatic char *napi_mbt_task_string(const char *s) {\n    size_t n = strlen(s) + 1;\n    char *result = malloc(n);\n    if (result) memcpy(result, s, n);\n    return result;\n}\nint napi_mbt_task_error(napi_mbt_task_context *c, const char *code, const char *message) {\n    if (c->submitted) return 0;\n    c->submitted = 1;\n    c->code = napi_mbt_task_string(code ? code : \"ERR_NAPI_NATIVE_TASK\");\n    c->message = napi_mbt_task_string(message ? message : \"Native task failed\");\n    if (!c->code || !c->message) { c->allocation_failed = 1; return 0; }\n    return 1;\n}\nstatic void napi_mbt_task_release(napi_mbt_task_context *c) {\n    if (--c->refs) return;\n    free(c->input); free(c->output); free(c->code); free(c->message); free(c);\n}\nstatic void napi_mbt_task_cancel(napi_mbt_task_context *c) {\n    if (c->done) return;\n    napi_mbt_task_cancel_store(&c->cancelled);\n    if (c->work) (void)napi_cancel_async_work(c->env, c->work);\n}\nstatic void napi_mbt_task_finalize(void *data) {\n    napi_mbt_task_handle *handle = data;\n    napi_mbt_task_context *c = handle->context;\n    handle->context = NULL;\n    if (c) { napi_mbt_task_cancel(c); napi_mbt_task_release(c); }\n}\nstatic void napi_mbt_task_execute(napi_env env, void *data) {\n    (void)env;\n    napi_mbt_task_context *c = data;\n    if (!napi_mbt_task_cancelled(c)) c->execute(c);\n}\nstatic void napi_mbt_task_complete(napi_env env, napi_status status, void *data) {\n    napi_mbt_task_context *c = data;\n    if (status != napi_ok) napi_mbt_task_cancel_store(&c->cancelled);\n    c->done = 1;\n    (void)napi_delete_async_work(env, c->work);\n    c->work = NULL;\n    napi_async_cleanup_hook_handle cleanup = c->cleanup;\n    c->cleanup = NULL;\n    /* No JS is invoked, including during teardown. Keep the cleanup hook until\n     * the actual worker has exited, even after the caller has been cancelled. */\n    if (cleanup) (void)napi_remove_async_cleanup_hook(cleanup);\n    napi_mbt_task_release(c);\n}\nstatic void napi_mbt_task_cleanup(napi_async_cleanup_hook_handle hook, void *data) {\n    napi_mbt_task_context *c = data;\n    c->cleanup = hook;\n    c->closing = 1;\n    napi_mbt_task_cancel(c);\n}\n\n/* Generated in this translation unit. Lookup cannot be supplied by JS. */\nstatic napi_mbt_task_function napi_mbt_task_lookup(const char *name);\n\nMOONBIT_FFI_EXPORT napi_mbt_task_handle *napi_mbt_native_start(\n    napi_env env, const char *name, const uint8_t *input, int32_t *error) {\n    napi_mbt_task_handle *handle = moonbit_make_external_object(napi_mbt_task_finalize, sizeof(*handle));\n    handle->context = NULL;\n    napi_mbt_task_function execute = napi_mbt_task_lookup(name);\n    *error = napi_invalid_arg;\n    if (!execute) return handle;\n    napi_mbt_task_context *c = calloc(1, sizeof(*c));\n    *error = napi_generic_failure;\n    if (!c) return handle;\n#ifndef _WIN32\n    atomic_init(&c->cancelled, 0);\n#endif\n    c->refs = 1; c->env = env; c->execute = execute;\n    c->input_length = (size_t)Moonbit_array_length(input);\n    c->input = malloc(c->input_length ? c->input_length : 1);\n    if (!c->input) { napi_mbt_task_release(c); return handle; }\n    if (c->input_length) memcpy(c->input, input, c->input_length);\n    napi_value resource;\n    napi_status status = napi_create_string_utf8(env, name, NAPI_AUTO_LENGTH, &resource);\n    if (status == napi_ok) status = napi_create_async_work(env, NULL, resource, napi_mbt_task_execute, napi_mbt_task_complete, c, &c->work);\n    if (status == napi_ok) status = napi_add_async_cleanup_hook(env, napi_mbt_task_cleanup, c, &c->cleanup);\n    if (status == napi_ok) status = napi_queue_async_work(env, c->work);\n    *error = (int32_t)status;\n    if (status != napi_ok) {\n        if (c->cleanup) (void)napi_remove_async_cleanup_hook(c->cleanup);\n        if (c->work) (void)napi_delete_async_work(env, c->work);\n        napi_mbt_task_release(c);\n        return handle;\n    }\n    c->refs++; /* work + returned MoonBit handle; only the owner updates refs */\n    handle->context = c;\n    return handle;\n}\nMOONBIT_FFI_EXPORT void napi_mbt_native_close(napi_mbt_task_handle *handle) { napi_mbt_task_finalize(handle); }\nMOONBIT_FFI_EXPORT int32_t napi_mbt_native_status(napi_mbt_task_handle *handle) {\n    napi_mbt_task_context *c = handle->context;\n    if (!c || c->closing) return 3;\n    if (!c->done) return 0;\n    if (napi_mbt_task_cancelled(c)) return 3;\n    return c->allocation_failed || c->code || !c->submitted ? 2 : 1;\n}\nMOONBIT_FFI_EXPORT moonbit_bytes_t napi_mbt_native_output(napi_mbt_task_handle *handle) {\n    napi_mbt_task_context *c = handle->context;\n    int32_t length = c && c->done && c->output ? (int32_t)c->output_length : 0;\n    moonbit_bytes_t bytes = moonbit_make_bytes(length, 0);\n    if (length) memcpy(bytes, c->output, (size_t)length);\n    return bytes;\n}\nMOONBIT_FFI_EXPORT moonbit_bytes_t napi_mbt_native_error(napi_mbt_task_handle *handle, int32_t code) {\n    napi_mbt_task_context *c = handle->context;\n    const char *s = code ? \"ERR_NAPI_NATIVE_TASK\" : \"Native task did not produce a result\";\n    if (c && c->done) {\n        if (c->allocation_failed) s = code ? \"ERR_NAPI_NATIVE_MEMORY\" : \"Native task allocation failed\";\n        else if (code && c->code) s = c->code;\n        else if (!code && c->message) s = c->message;\n    }\n    size_t length = strlen(s);\n    moonbit_bytes_t bytes = moonbit_make_bytes((int32_t)length, 0);\n    if (length) memcpy(bytes, s, length);\n    return bytes;\n}\n#endif\n",
  "template/napi_worker_lifecycle.h": "#ifndef NAPI_MBT_WORKER_LIFECYCLE_H\n#define NAPI_MBT_WORKER_LIFECYCLE_H\n#include <stdlib.h>\n#include <stdint.h>\n#ifdef _WIN32\n#include <windows.h>\n#else\n#include <pthread.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <signal.h>\n#include <unistd.h>\n#if defined(__linux__)\n#include <sys/syscall.h>\n#endif\n#endif\n\n/* A cleanup hook holds a native process identity independently of JS events.\n * No MoonBit state or JS is touched by cleanup/finalization. */\ntypedef struct napi_mbt_worker_watch {\n    napi_env env;\n    int env_hooked;\n    napi_async_cleanup_hook_handle async_hook;\n    int async_hooked;\n    int cleanup_refs;\n    struct napi_mbt_worker_watch *next;\n#ifdef _WIN32\n    HANDLE process;\n#else\n    pid_t pid;\n    int pidfd;\n#endif\n} napi_mbt_worker_watch;\nstatic napi_mbt_worker_watch *napi_mbt_worker_registry = NULL;\n#ifdef _WIN32\nstatic INIT_ONCE napi_mbt_worker_registry_once = INIT_ONCE_STATIC_INIT;\nstatic CRITICAL_SECTION napi_mbt_worker_registry_lock;\nstatic BOOL CALLBACK napi_mbt_worker_registry_init(PINIT_ONCE once, PVOID param, PVOID *context) {\n    (void)once; (void)param; (void)context;\n    InitializeCriticalSection(&napi_mbt_worker_registry_lock);\n    return TRUE;\n}\nstatic void napi_mbt_worker_lock(void) {\n    InitOnceExecuteOnce(&napi_mbt_worker_registry_once, napi_mbt_worker_registry_init, NULL, NULL);\n    EnterCriticalSection(&napi_mbt_worker_registry_lock);\n}\nstatic void napi_mbt_worker_unlock(void) { LeaveCriticalSection(&napi_mbt_worker_registry_lock); }\n#else\nstatic pthread_mutex_t napi_mbt_worker_registry_lock = PTHREAD_MUTEX_INITIALIZER;\nstatic void napi_mbt_worker_lock(void) { (void)pthread_mutex_lock(&napi_mbt_worker_registry_lock); }\nstatic void napi_mbt_worker_unlock(void) { (void)pthread_mutex_unlock(&napi_mbt_worker_registry_lock); }\n#endif\nstatic void napi_mbt_worker_register(napi_mbt_worker_watch *watch) {\n    napi_mbt_worker_lock();\n    watch->next = napi_mbt_worker_registry;\n    napi_mbt_worker_registry = watch;\n    napi_mbt_worker_unlock();\n}\nstatic napi_mbt_worker_watch *napi_mbt_worker_take(uintptr_t raw) {\n    napi_mbt_worker_watch *result = NULL;\n    napi_mbt_worker_watch **link;\n    napi_mbt_worker_lock();\n    for (link = &napi_mbt_worker_registry; *link; link = &(*link)->next) {\n        if ((uintptr_t)*link == raw) {\n            result = *link;\n            *link = result->next;\n            break;\n        }\n    }\n    napi_mbt_worker_unlock();\n    return result;\n}\nstatic void napi_mbt_worker_close(napi_mbt_worker_watch *watch, int terminate) {\n#ifdef _WIN32\n    if (watch->process) {\n        if (terminate && WaitForSingleObject(watch->process, 0) == WAIT_TIMEOUT)\n            (void)TerminateProcess(watch->process, 1);\n        CloseHandle(watch->process);\n        watch->process = NULL;\n    }\n#else\n    if (watch->pid > 0 && terminate) {\n#if defined(__linux__) && defined(SYS_pidfd_send_signal)\n        if (watch->pidfd >= 0) {\n            (void)syscall(SYS_pidfd_send_signal, watch->pidfd, SIGKILL, NULL, 0);\n        } else {\n            /* WNOWAIT never steals Node/libuv's child exit status. After Node\n             * reaps a child ECHILD prevents signalling an unrelated process. */\n            siginfo_t info = {0};\n            if (waitid(P_PID, (id_t)watch->pid, &info, WEXITED | WNOHANG | WNOWAIT) == 0 && info.si_pid == 0)\n                (void)kill(watch->pid, SIGKILL);\n        }\n#elif defined(__APPLE__)\n        /* Node owns the child wait status on macOS.  A cleanup hook only\n         * tracks the process created by this pool, so a direct kill avoids\n         * waitid(ECHILD) races with libuv's child watcher. */\n        (void)kill(watch->pid, SIGKILL);\n#else\n        {\n            /* WNOWAIT never steals Node/libuv's child exit status. After Node\n             * reaps a child ECHILD prevents signalling an unrelated process. */\n            siginfo_t info = {0};\n            if (waitid(P_PID, (id_t)watch->pid, &info, WEXITED | WNOHANG | WNOWAIT) == 0 && info.si_pid == 0)\n                (void)kill(watch->pid, SIGKILL);\n        }\n#endif\n    }\n    if (watch->pidfd >= 0) close(watch->pidfd);\n    watch->pidfd = -1;\n    watch->pid = 0;\n#endif\n}\nstatic void napi_mbt_worker_release(napi_mbt_worker_watch *watch) {\n    if (--watch->cleanup_refs == 0) free(watch);\n}\nstatic void napi_mbt_worker_cleanup(void *data) {\n    napi_mbt_worker_watch *watch = data;\n    watch->env_hooked = 0;\n    watch->env = NULL;\n    napi_mbt_worker_close(watch, 1);\n    napi_mbt_worker_release(watch);\n}\nstatic void napi_mbt_worker_async_cleanup(napi_async_cleanup_hook_handle handle, void *data) {\n    napi_mbt_worker_watch *watch = data;\n    (void)napi_mbt_worker_take((uintptr_t)watch);\n    watch->async_hook = NULL;\n    watch->async_hooked = 0;\n    napi_mbt_worker_close(watch, 1);\n    (void)napi_remove_async_cleanup_hook(handle);\n    napi_mbt_worker_release(watch);\n}\nstatic napi_value napi_mbt_worker_track(napi_env env, napi_callback_info info) {\n    size_t count = 1;\n    napi_value arg, result;\n    int32_t pid = 0;\n    if (napi_get_cb_info(env, info, &count, &arg, NULL, NULL) != napi_ok || count != 1 ||\n        napi_get_value_int32(env, arg, &pid) != napi_ok || pid <= 0) {\n        napi_throw_type_error(env, \"ERR_NAPI_WORKER_PID\", \"Expected a child process id\"); return NULL;\n    }\n    napi_mbt_worker_watch *watch = calloc(1, sizeof(*watch));\n    if (!watch) { napi_throw_error(env, \"ERR_NAPI_WORKER_MEMORY\", \"Cannot register worker\"); return NULL; }\n    watch->env = env;\n#ifdef _WIN32\n    watch->process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, (DWORD)pid);\n    if (!watch->process) { free(watch); napi_throw_error(env, \"ERR_NAPI_WORKER_PID\", \"Cannot open child process\"); return NULL; }\n#else\n    watch->pid = (pid_t)pid;\n    watch->pidfd = -1;\n#if defined(__linux__) && defined(SYS_pidfd_open)\n    watch->pidfd = (int)syscall(SYS_pidfd_open, pid, 0);\n#endif\n#endif\n    napi_status status = napi_add_env_cleanup_hook(env, napi_mbt_worker_cleanup, watch);\n    if (status == napi_ok) watch->env_hooked = 1;\n    if (status == napi_ok) status = napi_add_async_cleanup_hook(env, napi_mbt_worker_async_cleanup, watch, &watch->async_hook);\n    if (status == napi_ok) watch->async_hooked = 1;\n    if (status == napi_ok) watch->cleanup_refs = 2;\n    if (status == napi_ok) napi_mbt_worker_register(watch);\n    if (status == napi_ok) status = napi_create_bigint_uint64(env, (uint64_t)(uintptr_t)watch, &result);\n    if (status != napi_ok) {\n        if (watch->env_hooked) (void)napi_remove_env_cleanup_hook(env, napi_mbt_worker_cleanup, watch);\n        if (watch->async_hooked) (void)napi_remove_async_cleanup_hook(watch->async_hook);\n        (void)napi_mbt_worker_take((uintptr_t)watch);\n        napi_mbt_worker_close(watch, 1);\n        free(watch);\n        napi_throw_error(env, \"ERR_NAPI_WORKER_REGISTER\", \"Cannot register worker cleanup\"); return NULL;\n    }\n    return result;\n}\nstatic napi_value napi_mbt_worker_untrack(napi_env env, napi_callback_info info) {\n    size_t count = 1;\n    napi_value arg, result;\n    bool lossless = false;\n    uint64_t raw = 0;\n    if (napi_get_cb_info(env, info, &count, &arg, NULL, NULL) != napi_ok || count != 1 ||\n        napi_get_value_bigint_uint64(env, arg, &raw, &lossless) != napi_ok || !lossless) {\n        napi_throw_type_error(env, \"ERR_NAPI_WORKER_TOKEN\", \"Invalid worker cleanup token\"); return NULL;\n    }\n    napi_mbt_worker_watch *watch = napi_mbt_worker_take((uintptr_t)raw);\n    if (!watch) {\n        (void)napi_get_undefined(env, &result);\n        return result;\n    }\n    if (watch->env_hooked) { (void)napi_remove_env_cleanup_hook(env, napi_mbt_worker_cleanup, watch); watch->env_hooked = 0; }\n    if (watch->async_hooked) { (void)napi_remove_async_cleanup_hook(watch->async_hook); watch->async_hooked = 0; }\n    napi_mbt_worker_close(watch, 0);\n    watch->env = NULL;\n    free(watch);\n    (void)napi_get_undefined(env, &result);\n    return result;\n}\nstatic int napi_mbt_workers_register(napi_env env, napi_value exports) {\n    napi_property_descriptor methods[] = {\n        { \"__napi_mbt_worker_watch\", NULL, napi_mbt_worker_track, NULL, NULL, NULL, napi_default, NULL },\n        { \"__napi_mbt_worker_unwatch\", NULL, napi_mbt_worker_untrack, NULL, NULL, NULL, napi_default, NULL }\n    };\n    return (int)napi_define_properties(env, exports, 2, methods);\n}\n#endif\n",
  "workers/pool.cjs": "'use strict';\nconst { fork } = require('child_process');\nconst path = require('path');\nconst pools = new WeakMap();\nconst configuration = /* NAPI_MBT_WORKER_CONFIG */;\n\nfunction failure(code, message) { const error = new Error(message); error.code = code; return error; }\nfunction abortReason(signal) {\n  if (signal.reason !== undefined) return signal.reason;\n  const error = failure('ABORT_ERR', 'The operation was aborted'); error.name = 'AbortError'; return error;\n}\nmodule.exports = function (native, binary) {\n  if (pools.has(native)) return pools.get(native);\n  const facade = Object.create(null);\n  for (const name of configuration.exports) facade[name] = native[name];\n  const waiting = [], workers = new Set();\n  let nextId = 1, closed = false;\n  function settle(job, error, value) {\n    if (job.settled) return;\n    job.settled = true;\n    if (job.signal && job.listener) {\n      try { job.signal.removeEventListener('abort', job.listener); } catch (_) { /* preserve settlement */ }\n    }\n    job.listener = null; job.signal = null;\n    if (error) job.reject(value); else job.resolve(value);\n  }\n  function idle(worker) {\n    worker.child.unref();\n    if (worker.child.channel) worker.child.channel.unref();\n    worker.timer = setTimeout(() => stop(worker), configuration.idleTimeoutMs);\n    worker.timer.unref();\n  }\n  function stop(worker) {\n    if (worker.stopping) return;\n    worker.stopping = true;\n    clearTimeout(worker.timer);\n    worker.child.kill('SIGKILL');\n  }\n  function start() {\n    const child = fork(path.join(__dirname, 'napi_worker_entry.cjs'), [binary], {\n      serialization: 'advanced',\n      // Parent --eval/--test/inspector arguments do not belong in worker entries.\n      execArgv: [], stdio: ['ignore', 'inherit', 'inherit', 'ipc'],\n    });\n    child.on('error', () => {}); // also cover spawn failure before native registration\n    let token;\n    try { token = native.__napi_mbt_worker_watch(child.pid); }\n    catch (error) { child.kill('SIGKILL'); throw error; }\n    const worker = { child, token, job: null, ready: false, stopping: false, timer: null };\n    workers.add(worker);\n    child.on('message', message => {\n      if (worker.stopping || !message) return;\n      if (message.ready === true) {\n        worker.ready = true;\n        if (worker.job) send(worker); else idle(worker);\n        dispatch(); return;\n      }\n      const job = worker.job;\n      if (!job || message.id !== job.id) { stop(worker); return; }\n      worker.job = null;\n      idle(worker);\n      if (message.ok === true) settle(job, false, message.value);\n      else {\n        const remote = message.error || {};\n        const error = new Error(String(remote.message || 'Worker task failed'));\n        if (typeof remote.name === 'string') error.name = remote.name;\n        if (typeof remote.stack === 'string') error.stack = remote.stack;\n        if (remote.code !== undefined) error.code = remote.code;\n        settle(job, true, error);\n      }\n      dispatch();\n    });\n    child.on('error', error => { if (worker.job) settle(worker.job, true, error); stop(worker); });\n    child.once('exit', (code, signal) => {\n      clearTimeout(worker.timer);\n      workers.delete(worker);\n      native.__napi_mbt_worker_unwatch(token);\n      worker.token = null;\n      if (worker.job) settle(worker.job, true, failure('ERR_NAPI_WORKER_EXIT', `Worker exited (code=${code}, signal=${signal})`));\n      // A crashing or cancelled calculation is never replayed.\n      dispatch();\n    });\n    return worker;\n  }\n  function send(worker) {\n    const job = worker.job;\n    worker.child.send({ id: job.id, name: job.name, args: job.args }, error => {\n      if (error && worker.job === job) { settle(job, true, error); stop(worker); }\n    });\n  }\n  function assign(worker, job) {\n    clearTimeout(worker.timer);\n    worker.job = job; job.worker = worker;\n    worker.child.ref();\n    if (worker.child.channel) worker.child.channel.ref();\n    if (worker.ready) send(worker);\n  }\n  function dispatch() {\n    if (closed) return;\n    for (const worker of workers) {\n      if (!waiting.length) break;\n      if (worker.stopping || worker.job) continue;\n      assign(worker, waiting.shift());\n    }\n    const needed = Math.min(waiting.length, configuration.size - workers.size);\n    for (let i = 0; i < needed; i++) {\n      try { assign(start(), waiting.shift()); }\n      catch (error) { for (const job of waiting.splice(0)) settle(job, true, error); break; }\n    }\n  }\n  function submit(name, count, args) {\n    return new Promise((resolve, reject) => {\n      if (closed) throw failure('ERR_NAPI_WORKER_CLOSED', 'Worker pool has closed');\n      const options = args[count];\n      if (options != null && (typeof options !== 'object' || Array.isArray(options))) throw new TypeError('Expected task options');\n      const signal = options == null ? undefined : options.signal;\n      if (signal != null && (typeof signal !== 'object' || typeof signal.aborted !== 'boolean' || typeof signal.addEventListener !== 'function' || typeof signal.removeEventListener !== 'function')) throw new TypeError('Expected AbortSignal');\n      if (signal && signal.aborted) { reject(abortReason(signal)); return; }\n      // Native IR conversions validate getters, cycles, depth and owned storage\n      // now. Neither queued jobs nor IPC can observe later input mutation.\n      const snapshot = native['__napi_mbt_snapshot_' + name](...args.slice(0, count));\n      const available = Array.from(workers).some(worker => worker.ready && !worker.job && !worker.stopping) || workers.size < configuration.size;\n      if (!available && waiting.length >= configuration.maxQueue) throw failure('ERR_NAPI_WORKER_QUEUE_FULL', 'Worker task queue is full');\n      const job = { id: nextId++, name, args: snapshot, resolve, reject, signal, listener: null, worker: null, settled: false };\n      if (signal) {\n        job.listener = () => {\n          if (job.settled) return;\n          let reason;\n          try { reason = abortReason(signal); } catch (error) { reason = error; }\n          const index = waiting.indexOf(job);\n          if (index !== -1) waiting.splice(index, 1);\n          settle(job, true, reason);\n          if (job.worker) stop(job.worker);\n        };\n        try {\n          signal.addEventListener('abort', job.listener, { once: true });\n          if (signal.aborted) job.listener();\n        } catch (error) { settle(job, true, error); }\n      }\n      if (!job.settled) { waiting.push(job); dispatch(); }\n    });\n  }\n  for (const [name, count] of Object.entries(configuration.tasks)) facade[name] = (...args) => submit(name, count, args);\n  process.once('exit', () => {\n    closed = true;\n    for (const worker of workers) stop(worker);\n  });\n  pools.set(native, facade);\n  return facade;\n};\n",
  "workers/entry.cjs": "'use strict';\n// Load the resolved binary directly. Requiring the package facade would submit\n// recursively to another process pool instead of running the synchronous body.\nconst addon = require(process.argv[2]);\nlet busy = false;\nprocess.on('disconnect', () => process.exit(0));\nprocess.on('message', message => {\n  if (busy || !message || !Number.isSafeInteger(message.id) || typeof message.name !== 'string' || !Array.isArray(message.args)) process.exit(2);\n  busy = true;\n  let result;\n  try {\n    if (typeof addon['__napi_mbt_snapshot_' + message.name] !== 'function') throw new TypeError('Unregistered worker task');\n    result = { id: message.id, ok: true, value: addon[message.name](...message.args) };\n  } catch (error) {\n    let name = 'Error', text = 'Worker task failed', stack, code;\n    try {\n      name = String(error.name || name); text = String(error.message || error);\n      stack = typeof error.stack === 'string' ? error.stack : undefined;\n      code = typeof error.code === 'string' || typeof error.code === 'number' ? error.code : undefined;\n    } catch (_) { /* hostile thrown values are still reported */ }\n    result = { id: message.id, ok: false, error: { name, message: text, stack, code } };\n  }\n  busy = false;\n  process.send(result, error => { if (error) process.exit(3); });\n});\nprocess.send({ ready: true });\n",
  "async/files/src/internal/coroutine/scheduler.mbt": "// Copyright 2025 International Digital Economy Academy\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n///|\npub struct Scheduler {\n  mut coro_id : Int\n  mut curr_coro : Coroutine?\n  run_later : @deque.Deque[Coroutine]\n  all_coros : Set[Coroutine]\n}\n\n///|\nlet scheduler : Ref[Scheduler] = Ref(embedded_new_scheduler())\n\n///|\npub fn current_coroutine() -> Coroutine {\n  scheduler.val.curr_coro.unwrap()\n}\n\n///|\npub fn has_immediately_ready_task() -> Bool {\n  !scheduler.val.run_later.is_empty()\n}\n\n///|\n/// Perform a new rousd of scheduling by running all ready coroutines.\n/// While running these coroutines, new coroutines may get waken or spawned,\n/// these new coroutines will *not* get executed in this run of `reschedule`.\n/// This give the event loop a chance to check of completed IO operations\n/// and prevent starvation, even when there is always immediately ready coroutine.\npub fn reschedule() -> Unit {\n  let n = scheduler.val.run_later.length()\n  for _ in 0..<n {\n    guard scheduler.val.run_later.pop_front() is Some(coro) else { break }\n    coro.ready = false\n    guard coro.state is Suspend(cont) else {  }\n    coro.state = Running\n    let last_coro = scheduler.val.curr_coro\n    scheduler.val.curr_coro = Some(coro)\n    if coro.cancelled && !coro.shielded {\n      cont(Cancelled)\n    } else {\n      cont(Continue)\n    }\n    scheduler.val.curr_coro = last_coro\n  }\n}\n\n///|\npub fn all_coroutines() -> Set[Coroutine] {\n  scheduler.val.all_coros\n}\n\n///|\npub fn embedded_new_scheduler() -> Scheduler {\n  { coro_id: 0, curr_coro: None, run_later: Deque([]), all_coros: Set([]) }\n}\n\n///|\npub fn embedded_swap_scheduler(next : Scheduler) -> Scheduler {\n  let previous = scheduler.val\n  scheduler.val = next\n  previous\n}\n",
  "async/files/src/internal/coroutine/coroutine.mbt": "// Copyright 2025 International Digital Economy Academy\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n///|\npub enum SuspendResult {\n  Continue\n  Cancelled\n}\n\n///|\npriv enum State {\n  Done\n  Fail(Error)\n  Cancelled\n  Running\n  Suspend((SuspendResult) -> Unit)\n}\n\n///|\npub struct Coroutine {\n  priv coro_id : Int\n  priv mut state : State\n  priv mut shielded : Bool\n  priv mut cancelled : Bool\n  priv mut ready : Bool\n  priv downstream : Set[Coroutine]\n  loc : SourceLoc\n}\n\n///|\npub impl Eq for Coroutine with fn equal(c1, c2) {\n  c1.coro_id == c2.coro_id\n}\n\n///|\npub impl Hash for Coroutine with fn hash_combine(self, hasher) {\n  Hash::hash_combine(self.coro_id, hasher)\n}\n\n///|\npub fn Coroutine::wake(self : Coroutine) -> Unit {\n  if !self.ready {\n    self.ready = true\n    scheduler.val.run_later.push_back(self)\n  }\n}\n\n///|\npub fn is_being_cancelled() -> Bool {\n  let coro = current_coroutine()\n  coro.cancelled && !coro.shielded\n}\n\n///|\npub async fn check_cancellation() -> Unit noraise {\n  if is_being_cancelled() {\n    raise_cancellation_signal()\n  }\n}\n\n///|\npub fn Coroutine::cancel(self : Coroutine) -> Unit {\n  self.cancelled = true\n  if !self.shielded {\n    self.wake()\n  }\n}\n\n///|\npub async fn pause() -> Unit noraise {\n  guard! scheduler.val.curr_coro is Some(coro)\n  if coro.cancelled && !coro.shielded {\n    raise_cancellation_signal()\n  }\n  let result = async_suspend() <| cont => {\n    guard! coro.state is Running\n    coro.state = Suspend(cont)\n    coro.ready = true\n    scheduler.val.run_later.push_back(coro)\n  }\n  match result {\n    Continue => ()\n    Cancelled => raise_cancellation_signal()\n  }\n}\n\n///|\n/// Helper for a non-cancellable `pause`, more efficient than `protect_from_cancel`\npub async fn pause_nocancel() -> Unit noraise + nocancel {\n  guard! scheduler.val.curr_coro is Some(coro)\n  let shielded = coro.shielded\n  coro.shielded = true\n  defer {\n    coro.shielded = shielded\n  }\n  let result = async_suspend() <| cont => {\n    guard! coro.state is Running\n    coro.state = Suspend(cont)\n    coro.ready = true\n    scheduler.val.run_later.push_back(coro)\n  }\n  guard! result is Continue\n}\n\n///|\npub async fn suspend_check_cancel() -> SuspendResult noraise {\n  guard! scheduler.val.curr_coro is Some(coro)\n  if coro.cancelled && !coro.shielded {\n    return Cancelled\n  }\n  async_suspend() <| cont => {\n    guard! coro.state is Running\n    coro.state = Suspend(cont)\n  }\n}\n\n///|\npub async fn suspend() -> Unit noraise {\n  match suspend_check_cancel() {\n    Continue => ()\n    Cancelled => raise_cancellation_signal()\n  }\n}\n\n///|\n#callsite(autofill(loc))\npub fn spawn(f : async () -> Unit, loc~ : SourceLoc) -> Coroutine {\n  scheduler.val.coro_id += 1\n  let coro = {\n    state: Running,\n    ready: true,\n    shielded: false,\n    downstream: Set([]),\n    coro_id: scheduler.val.coro_id,\n    cancelled: false,\n    loc,\n  }\n  fn run(_) {\n    // The parameter (suspend result) is deliberately ignored here.\n    // We want to ensure the body of coroutine always get a chance to execute,\n    // even if cancelled immediately before starting.\n    // This way, the user can rely on `defer` at the start of coroutine body\n    // always getting triggered, which is useful for many programming patterns.\n    run_async() <| () => {\n      try capture_cancellation(f, handler=() => coro.state = Cancelled) catch {\n        err => coro.state = Fail(err)\n      } noraise {\n        _ if coro.state is Running => coro.state = Done\n        _ => ()\n      }\n      for coro in coro.downstream {\n        coro.wake()\n      }\n      scheduler.val.all_coros.remove(coro)\n    }\n  }\n\n  coro.state = Suspend(run)\n  scheduler.val.run_later.push_back(coro)\n  scheduler.val.all_coros.add(coro)\n  coro\n}\n\n///|\npub fn Coroutine::unwrap(self : Coroutine) -> SuspendResult raise {\n  match self.state {\n    Done => Continue\n    Cancelled => Cancelled\n    Fail(err) => raise err\n    Running | Suspend(_) => panic()\n  }\n}\n\n///|\npub async fn Coroutine::wait(target : Coroutine) -> SuspendResult {\n  guard! scheduler.val.curr_coro is Some(coro)\n  guard! !physical_equal(coro, target)\n  match target.state {\n    Done => return Continue\n    Cancelled => return Cancelled\n    Fail(err) => raise err\n    Running | Suspend(_) => ()\n  }\n  target.downstream.add(coro)\n  defer target.downstream.remove(coro)\n  suspend()\n  target.unwrap()\n}\n\n///|\npub fn Coroutine::check_error(coro : Coroutine) -> Unit raise {\n  match coro.state {\n    Fail(err) => raise err\n    Done | Cancelled | Running | Suspend(_) => ()\n  }\n}\n\n///|\npub async fn[X] protect_from_cancel(f : async () -> X) -> X nocancel {\n  guard! scheduler.val.curr_coro is Some(coro)\n  // When `coro.shielded` is `true`, the scheduler never cancel this coroutine,\n  // so `capture_cancellation` here is unnecessary, just for fixing the type\n  capture_cancellation(handler=() => panic()) <| () => {\n    if coro.shielded {\n      // already in a shield, do nothing\n      f()\n    } else {\n      coro.shielded = true\n      defer {\n        coro.shielded = false\n      }\n      f()\n    }\n  }\n}\n\n///|\npub async fn[X] handle_cancellation(f : async () -> X) -> X? {\n  capture_cancellation(handler=() => None, () => Some(f()))\n}\n",
  "async/files/src/internal/event_loop/event_loop.mbt": "// Copyright 2025 International Digital Economy Academy\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n///|\npub using @env_util {platform}\n\n///|\npriv struct EventLoop {\n  stdio : Map[@fd_util.Fd, IoHandle]\n  stdin_buffer : @io.ReaderBuffer\n  bus : EventBus\n  fds : Map[@fd_util.Fd, IoHandle]\n  extra : PlatformEventLoopExtra\n  /// a special file descriptor used to indicate job completion message from thread pool\n  notify_recv : @fd_util.Fd\n  max_worker_count : Int\n  mut timer_id : Int\n  mut job_id : Int\n  job_queue : Set[QueuedJob]\n  idle_workers : @deque.Deque[Worker]\n  running_workers : Map[Int, Worker]\n  jobs : Map[Int, @coroutine.Coroutine]\n  timers : @sorted_set.SortedSet[Timer]\n  mut main : @coroutine.Coroutine?\n  mut killed_by_signal : Int?\n  mut blocked_tasks : Int\n}\n\n///|\npriv struct QueuedJob {\n  job_id : Int\n  job : Job\n  waiter : @coroutine.Coroutine\n}\n\n///|\nimpl Eq for QueuedJob with fn equal(x, y) {\n  x.job_id == y.job_id\n}\n\n///|\nimpl Hash for QueuedJob with fn hash_combine(job, hasher) {\n  Hash::hash_combine(job.job_id, hasher)\n}\n\n///|\nlet curr_loop : Ref[EventLoop?] = Ref(None)\n\n///|\npriv struct EventLoopHook {\n  init : (() -> Unit raise)?\n  exit : (() -> Unit raise)?\n}\n\n///|\nlet hooks : Array[EventLoopHook] = []\n\n///|\npub fn register_hook(\n  init? : () -> Unit raise,\n  exit? : () -> Unit raise,\n) -> Unit {\n  hooks.push({ init, exit, })\n}\n\n///|\nfn EventLoop::new(max_worker_count~ : Int) -> EventLoop raise {\n  let bus = EventBus::new()\n  errdefer bus.destroy()\n  let notify_recv = init_thread_pool_ffi(bus)\n  if !@fd_util.fd_is_valid(notify_recv) {\n    @os_error.check_errno(\"initialize thread pool\")\n  }\n  errdefer destroy_thread_pool()\n  let extra = PlatformEventLoopExtra::new()\n  {\n    bus,\n    fds: Map([]),\n    stdio: Map([]),\n    stdin_buffer: @io.ReaderBuffer::new(),\n    extra,\n    notify_recv,\n    max_worker_count,\n    timer_id: 0,\n    job_id: 0,\n    job_queue: Set([]),\n    idle_workers: Deque([]),\n    running_workers: Map([]),\n    jobs: Map([]),\n    timers: SortedSet([]),\n    main: None,\n    killed_by_signal: None,\n    blocked_tasks: 0,\n  }\n}\n\n///|\nfn EventLoop::destroy(evloop : EventLoop) -> Unit {\n  evloop.bus.destroy()\n  destroy_thread_pool()\n  evloop.extra.destroy()\n}\n\n///|\nusing @external_loop_integration {trait ExternalEventLoop}\n\n///|\nlet external_loop : Ref[&ExternalEventLoop?] = Ref(None)\n\n///|\npub fn set_external_event_loop(extloop : &ExternalEventLoop) -> Unit {\n  if curr_loop.val is Some(evloop) && evloop.main is Some(_) {\n    abort(\"`set_external_event_loop` called after event loop already started\")\n  }\n  guard external_loop.val is None else {\n    abort(\"An external event loop is already set\")\n  }\n  external_loop.val = Some(extloop)\n}\n\n///|\npub fn with_event_loop(\n  f : async () -> Unit,\n  max_worker_count? : Int = 1024,\n) -> Unit raise {\n  if curr_loop.val is Some(_) { raise Failure::Failure(\"Nested event loop is not supported by the Node host\") }\n  let evloop = EventLoop::new(max_worker_count~)\n  defer evloop.destroy()\n  curr_loop.val = Some(evloop)\n  defer {\n    curr_loop.val = None\n  }\n  for hook in hooks {\n    if hook.init is Some(init) {\n      init()\n    }\n  }\n  setup_signal_handler()\n  let main = @coroutine.spawn(() => {\n    defer evloop.terminate_signal_handler()\n    f()\n  })\n  @coroutine.reschedule()\n  evloop.main = Some(main)\n  try {\n    if external_loop.val is Some(extloop) {\n      external_loop.val = None\n      defer extloop.terminate()\n      evloop.run_with_external_loop(extloop)\n    } else {\n      evloop.run_forever()\n    }\n  } catch {\n    err => {\n      eprintln(\"fatal runtime error: \\{err}\")\n      panic()\n    }\n  }\n  ignore(main.unwrap())\n}\n\n///|\nasync fn EventLoop::suspend(evloop : EventLoop) -> Unit noraise {\n  evloop.blocked_tasks += 1\n  defer {\n    evloop.blocked_tasks -= 1\n  }\n  @coroutine.suspend()\n}\n\n///|\nasync fn EventLoop::suspend_check_cancel(\n  evloop : EventLoop,\n) -> @coroutine.SuspendResult noraise {\n  evloop.blocked_tasks += 1\n  defer {\n    evloop.blocked_tasks -= 1\n  }\n  @coroutine.suspend_check_cancel()\n}\n\n///|\n#cfg(target=\"native\")\npub fn now() -> Int64 {\n  @env.now().reinterpret_as_int64()\n}\n\n///|\n#cfg(target=\"wasm\")\n#unsafe_skip_stub_check\npub fn now() -> Int64 = \"moonbitlang/async\" \"time/get_ms_since_epoch\"\n\n///|\nfn EventLoop::wait_for_event(self : EventLoop, timeout~ : Int) -> Int raise {\n  let n = self.bus.wait(timeout~)\n  if n < 0 {\n    if errno_is_cancelled(@os_error.get_errno()) {\n      // `EINTR`\n      return 0\n    }\n    @os_error.check_errno(\"runtime internal: poll_wait\")\n  }\n  n\n}\n\n///|\nfn EventLoop::handle_completed_job(self : Self, job_id : Int) -> Unit {\n  // The thread pool pipe may receive job completion event or signal events.\n  // Here we encode signal events by setting the most significant bit to one.\n  guard (job_id & (1 << 31)) == 0 else {\n    // signal\n    if self.main is Some(main) {\n      main.cancel()\n    }\n    self.killed_by_signal = Some(job_id ^ (1 << 31))\n  }\n  guard self.running_workers.get(job_id) is Some(worker) else { return }\n  // Completion message for a job may receive in two cases:\n  //\n  // - the job indeed completed\n  // - the job is being cancelled, and we need to retry cancellation\n  //\n  // `check_cancellation_retry` check the second case and perform retry automatically.\n  // If the completion message is for retry, do not wake the waiter yet.\n  if worker.check_cancellation_retry() {\n    return\n  }\n\n  self.running_workers.remove(job_id)\n  match self.job_queue.iter().next() {\n    None => {\n      worker.enter_idle()\n      self.idle_workers.push_back(worker)\n    }\n    Some(job) => {\n      self.running_workers[job.job_id] = worker\n      worker.wake(job.job_id, job.job)\n      self.job_queue.remove(job)\n      job.waiter.wake()\n    }\n  }\n  if self.jobs.get(job_id) is Some(coro) {\n    coro.wake()\n  }\n}\n\n///|\npub let check_fd_leak : Ref[Bool] = Ref(\n  @env.get_env_var(\"MOONBIT_ASYNC_CHECK_FD_LEAK\") is Some(_),\n)\n\n///|\nfn EventLoop::check_fd_leak(self : Self) -> Unit {\n  if !self.fds.is_empty() {\n    let leaked_handles = [\n      for handle in self.fds.values() => @debug.to_string(handle.kind)\n    ].join(\", \")\n    eprintln(\"file descriptor leak detected: \\{leaked_handles}\")\n    panic()\n  }\n}\n\n///|\nfn EventLoop::cleanup(self : Self) -> Unit raise {\n  for _, handle in self.stdio { handle.detach_from_event_loop() }\n  self.stdio.clear()\n  for hook in hooks {\n    if hook.exit is Some(exit) {\n      exit()\n    }\n  }\n  for fd, handle in self.fds {\n    if handle.read is Waiting(coro) {\n      coro.cancel()\n    }\n    if handle.write is Waiting(coro) {\n      coro.cancel()\n    }\n    @fd_util.close(\n      fd,\n      kind=handle.kind,\n      context=\"runtime internal: close leaking fd\",\n    )\n  }\n  if check_fd_leak.val {\n    self.check_fd_leak()\n  }\n  self.fds.clear()\n  for _, coro in self.jobs {\n    coro.cancel()\n  }\n  self.extra.cleanup()\n  for worker in self.idle_workers {\n    worker.free()\n  }\n}\n\n///|\nfn EventLoop::get_next_timeout(self : Self) -> Int {\n  if @coroutine.has_immediately_ready_task() {\n    0\n  } else if !self.timers.is_empty() {\n    guard! self.timers.iter().head() is Some(first_timer)\n    let timeout = first_timer.expire_time - now()\n    @cmp.maximum(0, timeout.to_int())\n  } else {\n    -1\n  }\n}\n\n///|\nfn EventLoop::handle_timers(self : Self) -> Unit {\n  let now = now()\n  for timer in self.timers {\n    if timer.expire_time <= now {\n      (timer.callback)()\n    } else {\n      break\n    }\n  }\n}\n\n///|\nlet deadlock_handler : Ref[(Array[SourceLoc]) -> Unit] = Ref(locs => {\n  let locs = locs.map(loc => loc.to_string())\n  eprintln(\n    \"Dead lock detected. Tasks spawned at the following locations are still alive: \\{Repr(locs)}\",\n  )\n  panic()\n})\n\n///|\npub fn set_deadlock_handler(f : (Array[SourceLoc]) -> Unit) -> Unit {\n  deadlock_handler.val = f\n}\n\n///|\nfn EventLoop::check_dead_lock(_self : Self) -> Unit {\n  let all_coros = @coroutine.all_coroutines().iter()\n  let all_coros = all_coros.map(coro => coro.loc).collect()\n  if all_coros.length() > 0 {\n    (deadlock_handler.val)(all_coros)\n  }\n}\n\n///|\nfn EventLoop::run_forever(self : Self) -> Unit raise {\n  let mut checked_deadlock = false\n  while !@coroutine.all_coroutines().is_empty() {\n    let timeout = self.get_next_timeout()\n    if timeout < 0 && self.blocked_tasks is 0 && !checked_deadlock {\n      // The user may decide to ignore deadlock situation,\n      // in this case we only perform the deadlock check once.\n      checked_deadlock = true\n      self.check_dead_lock()\n    }\n    let n = self.wait_for_event(timeout~)\n    if timeout >= 0 {\n      self.handle_timers()\n    }\n    for i in 0..<n {\n      let event = self.bus.get_event(i)\n      self.extra.handle_event(self, event)\n    }\n    @coroutine.reschedule()\n  }\n  self.cleanup()\n}\n\n///|\n#cfg(target=\"native\")\nfn EventLoop::run_with_external_loop(\n  self : Self,\n  extloop : &ExternalEventLoop,\n) -> Unit raise {\n  let waiter = self.bus.spawn_waiter(\n    wakeup_callback=extloop.get_wakeup_callback_for_foreign_thread(),\n  )\n  if waiter.0.is_null() {\n    @os_error.check_errno(\"runtime internal: spawn waiter\")\n  }\n  defer waiter.terminate()\n  while !@coroutine.all_coroutines().is_empty() {\n    let timeout = match self.get_next_timeout() {\n      _..<0 => None\n      timeout => Some(timeout)\n    }\n    extloop.poll(timeout?)\n    if !self.timers.is_empty() {\n      self.handle_timers()\n    }\n    let n = waiter.get_events()\n    if n > 0 {\n      defer waiter.wake()\n      for i in 0..<n {\n        let event = self.bus.get_event(i)\n        self.extra.handle_event(self, event)\n      }\n    } else if n < 0 {\n      @os_error.check_errno(\"runtime internal: poll_wait\")\n    }\n    @coroutine.reschedule()\n  }\n  self.cleanup()\n}\n\n///|\n#cfg(target=\"wasm\")\nfn EventLoop::run_with_external_loop(\n  _self : Self,\n  _extloop : &ExternalEventLoop,\n) -> Unit raise {\n  raise Failure::Failure(\n    \"external event loop integration is not supported on Wasm\",\n  )\n}\n\n///|\npriv enum CancellationStatus {\n  NoWait\n  NeedWait\n  RetryLater\n}\n\n///|\n/// Generic function for performing cancellation on an IO operation.\n/// `cancel_func` performs the cancellation work under the hood.\n/// For Windows IOCP/thread pool operations, the OS/thread pool hold references to user objects.\n/// We must wait for the completion of the operation before dropping those user objects,\n/// even in case of cancellation.\nasync fn wait_for_cancellation(\n  cancel_func : () -> CancellationStatus raise,\n) -> Unit nocancel {\n  guard! curr_loop.val is Some(evloop)\n  let status = {\n    errdefer @coroutine.protect_from_cancel(() => evloop.suspend())\n    cancel_func()\n  }\n  match status {\n    NeedWait =>\n      // The cancellation operation has succeeded,\n      // but the thread pool or OS may need some more time to finish all the cleanup.\n      // So wait for the completion of the cleanup process here.\n      @coroutine.protect_from_cancel(() => evloop.suspend())\n    NoWait =>\n      // The cancellation operation has succeeded,\n      // and the cancelled operation has already terminated.\n      // Return immediately here.\n      ()\n    RetryLater => {\n      // Due to some inherent race condition, the cancellaion attempt did not succeed.\n      // Retry again after a small timeout.\n      @coroutine.protect_from_cancel(() => sleep(10))\n      wait_for_cancellation(cancel_func)\n    }\n  }\n}\n\n///|\nasync fn EventLoop::wait_for_job(\n  evloop : EventLoop,\n  job_id : Int,\n  cancellable~ : Bool,\n  context~ : String,\n) -> Unit {\n  evloop.jobs[job_id] = @coroutine.current_coroutine()\n  defer evloop.jobs.remove(job_id)\n  if cancellable {\n    match evloop.suspend_check_cancel() {\n      Continue => ()\n      Cancelled =>\n        // The cancellation error is swallowed here,\n        // because in case the operation completed before cancellation,\n        // we need to return the result normally to retain cancellation safety\n        // and avoid data loss.\n        // In case the cancellation succeeded,\n        // the underlying operation itself should report cancelled status properly\n        // via error code (`EINTR` or `ERROR_OPERATION_ABORTED`)\n        wait_for_cancellation(() => {\n          evloop.cancel_job_in_worker(job_id, context~)\n        })\n    }\n  } else {\n    @coroutine.protect_from_cancel(() => evloop.suspend())\n  }\n}\n\n///|\n#cfg(target=\"native\")\nextern \"C\" fn errno_is_cancelled(errno : Int) -> Bool = \"moonbitlang_async_errno_is_cancelled\"\n\n///|\n#cfg(target=\"wasm\")\n#unsafe_skip_stub_check\nfn errno_is_cancelled(errno : Int) -> Bool = \"moonbitlang/async\" \"thread_pool/errno_is_cancelled\"\n\n///|\nasync fn perform_job_in_worker(\n  job : Job,\n  context~ : String,\n  cancellable? : Bool = false,\n) -> Int {\n  @coroutine.check_cancellation()\n  guard! curr_loop.val is Some(evloop)\n  let job_id = evloop.job_id\n  evloop.job_id += 1\n  match evloop.idle_workers.pop_front() {\n    None if evloop.running_workers.length() >= evloop.max_worker_count => {\n      let waiter = @coroutine.current_coroutine()\n      let queued_job = { job_id, job, waiter, }\n      evloop.job_queue.add(queued_job)\n      match @coroutine.suspend_check_cancel() {\n        Continue => ()\n        Cancelled if evloop.job_queue.contains(queued_job) => {\n          evloop.job_queue.remove(queued_job)\n          @coroutine.raise_cancellation_signal()\n        }\n        Cancelled =>\n          // If `queued_job` is already removed, the job must have been submitted already,\n          // within the same tick as the cancellation request.\n          // In this case, swallow the cancellation to avoid problems. \n          ()\n      }\n    }\n    None => {\n      let worker = Worker::spawn(job_id, job)\n      if worker.is_null() { @os_error.check_errno(\"Starting async worker\") }\n      evloop.running_workers[job_id] = worker\n    }\n    Some(worker) => {\n      evloop.running_workers[job_id] = worker\n      worker.wake(job_id, job)\n    }\n  }\n  evloop.wait_for_job(job_id, cancellable~, context~)\n  if job.err() is err && err > 0 {\n    if @coroutine.is_being_cancelled() && errno_is_cancelled(err) {\n      @coroutine.raise_cancellation_signal()\n    } else {\n      raise @os_error.OSError(err, context~)\n    }\n  } else {\n    job.copy_output()\n    job.ret()\n  }\n}\n\n///|\nfn EventLoop::cancel_job_in_worker(\n  evloop : EventLoop,\n  job_id : Int,\n  context~ : String,\n) -> CancellationStatus raise {\n  if evloop.running_workers.get(job_id) is Some(worker) {\n    worker.cancel(context~)\n  } else {\n    NoWait\n  }\n}\n",
  "async/files/src/internal/event_loop/stdio.mbt": "// Copyright 2025 International Digital Economy Academy\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n///|\n#cfg(target=\"native\")\n#borrow(buf)\nextern \"C\" fn fstatx_sync(\n  fd : @fd_util.Fd,\n  request : UInt,\n  buf : FixedArray[Byte],\n  buf_len : Int,\n) -> Int = \"moonbitlang_async_fstatx_sync\"\n\n///|\n#cfg(target=\"native\")\nfn kind_of_fd_sync(\n  fd : @fd_util.Fd,\n  context~ : String,\n) -> @fd_util.FileKind raise {\n  let buf = FixedArray::make(16, b'\\x00')\n  if fstatx_sync(fd, STAT_FILE_KIND, buf, buf.length()) < 0 {\n    @os_error.check_errno(context)\n  }\n  guard! buf is [u32le(_), u32le(mask), i64le(kind)]\n  guard mask == STAT_FILE_KIND else {\n    raise @os_error.OSError(@os_error.errno_ENOTSUP, context~)\n  }\n  @fd_util.FileKind::unsafe_from_int(kind.to_int())\n}\n\n///|\n#cfg(target=\"wasm\")\n#unsafe_skip_stub_check\nfn kind_of_fd_sync_ffi(fd : @fd_util.Fd) -> Int = \"moonbitlang/async\" \"fd_util/kind_of_fd\"\n\n///|\n#cfg(target=\"wasm\")\nfn kind_of_fd_sync(\n  fd : @fd_util.Fd,\n  context~ : String,\n) -> @fd_util.FileKind raise {\n  let kind = kind_of_fd_sync_ffi(fd)\n  if kind < 0 {\n    @os_error.check_errno(context)\n  }\n  @fd_util.FileKind::unsafe_from_int(kind)\n}\n\n///|\n#cfg(target=\"native\")\nextern \"C\" fn get_stdio_handle(id : Int) -> @fd_util.Fd = \"moonbitlang_async_get_stdio_handle\"\n\n///|\n#cfg(target=\"wasm\")\n#unsafe_skip_stub_check\nfn get_stdio_handle(id : Int) -> @fd_util.Fd = \"moonbitlang/async\" \"stdio/get_stdio_handle\"\n\n///|\npub fn napi_stdin_buffer() -> @io.ReaderBuffer { curr_loop.val.unwrap().stdin_buffer }\n\n///|\nfn setup_stdio(id : Int, context~ : String) -> IoHandle raise {\n  guard curr_loop.val is Some(evloop) else { fail(\"stdio requires an active async environment\") }\n  let stdio_handles = evloop.stdio\n  let fd = get_stdio_handle(id)\n  if !@fd_util.fd_is_valid(fd) {\n    @os_error.check_errno(context)\n  }\n  if stdio_handles.get(fd) is Some(handle) {\n    // On Windows, when `stdout` and `stderr` are redirected to the same file/pipe,\n    // `GetStdHande` will return the same handle for them.\n    // In this case, avoid problem caused by duplicated handle value\n    return handle\n  }\n  let kind = kind_of_fd_sync(fd, context~)\n  let handle = {\n    fd,\n    kind,\n    // Stdio channels may be shared between many different processes,\n    // and some (argubly misbehaving ones) of them may\n    // change the blocking/non-blocking status of stdio channels.\n    // So we play safe here and always use the thread pool for stdio channels.\n    // As a consequence, the thread pool need to handle non blocking fd as well,\n    // see the code for `read_job_worker`/`write_job_worker` in `thread_pool.c` for more details.\n    is_async: false,\n    read: Idle,\n    // The tricky case here is regular file.\n    // When the user access a file via the `@fs.File` interface.\n    // we maintain independent private read/write offset etc. to provide better semantic.\n    // However, when the user is accessing a file via the `@stdio` interface,\n    // we should simply provide whatever the OS provides,\n    // respecting internal file pointer, append mode setting etc.\n    read_offset: -1,\n    write: Idle,\n    write_offset: -1,\n    console_state: WindowsConsoleBuffer(kind),\n  }\n  stdio_handles[fd] = handle\n  evloop.fds[fd] = handle\n  handle\n}\n\n///|\npub fn napi_stdio(id : Int) -> IoHandle raise { setup_stdio(id, context=\"Opening standard stream\") }\n",
  "async/files/src/stdio/stdio.mbt": "// Copyright 2025 International Digital Economy Academy\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n///|\nstruct Input {\n  id : Int\n}\n\n///|\n/// Return the file descriptor of an input channel.\n///\n/// If the initialization of the input channel failed,\n/// for example when `stdin` does not exist (Windows) or is not a valid fd (Unix-like),\n/// This method will fail with error.\n/// So `.fd()` can be used as a way to tell if `stdin` is valid.\npub fn Input::fd(self : Input) -> @fd_util.Fd raise {\n  @event_loop.napi_stdio(self.id).fd()\n}\n\n///|\npub impl @io.Reader for Input with fn _direct_read(self, buf, offset~, max_len~) {\n  @event_loop.napi_stdio(self.id)\n  .read(buf, offset~, len=max_len, context=\"@stdio.Input::read()\")\n}\n\n///|\npub impl @io.Reader for Input with fn _get_internal_buffer(self) {\n  @event_loop.napi_stdin_buffer()\n}\n\n///|\npub extend Input with @io.Reader::{\n  read,\n  read_some,\n  drop,\n  read_all,\n  read_until,\n  read_exactly,\n}\n\n///|\nstruct Output(Int)\n\n///|\n/// Return the file descriptor of an output channel.\n///\n/// If the initialization of the output channel failed,\n/// for example when `stdout` does not exist (Windows) or is not a valid fd (Unix-like),\n/// This method will fail with error.\n/// So `.fd()` can be used as a way to tell if `stdout`/`stderr` is valid.\npub fn Output::fd(self : Output) -> @fd_util.Fd raise {\n  @event_loop.napi_stdio(self.0).fd()\n}\n\n///|\npub impl @io.Writer for Output with fn write_once(self, buf, offset~, len~) {\n  @event_loop.napi_stdio(self.0)\n  .write(buf, offset~, len~, context=\"@stdio.Output::write()\")\n}\n\n///|\npub extend Output with @io.Writer::{write_once, write, write_reader}\n\n///|\n/// The standard input channel.\n///\n/// If `stdin` does not exist or is invalid,\n/// all operations on `stdin` will immediately fail with error.\n///\n/// Windows note: console handles are text based on Windows.\n/// If `stdin` is a Windows console input,\n/// unicode text read from the console will be decoded to UTF-8.\npub let stdin : Input = {\n  id: 0,\n}\n\n///|\n/// The standard output channel.\n///\n/// If `stdout` does not exist or is invalid,\n/// all operations on `stdout` will immediately fail with error.\n///\n/// Windows note: console handles are text based on Windows.\n/// If `stdout` is a Windows console output,\n/// binary IO over `stdout` will be interpreted as UTF-8 sequence,\n/// and submitted to the actual console screen buffer via unicode-safe API.\n/// If the input is not valid UTF-8, the correct parts will be preserved,\n/// but the final output for incorrect parts is undefined\npub let stdout : Output = 1\n\n///|\n/// The standard error channel.\n///\n/// If `stderr` does not exist or is invalid,\n/// all operations on `stderr` will immediately fail with error.\n///\n/// Windows note: console handles are text based on Windows.\n/// If `stderr` is a Windows console output,\n/// binary IO over `stderr` will be interpreted as UTF-8 sequence,\n/// and submitted to the actual console screen buffer via unicode-safe API.\n/// If the input is not valid UTF-8, the correct parts will be preserved,\n/// but the final output for incorrect parts is undefined\npub let stderr : Output = 2\n",
  "async/files/src/socket/ifname.mbt": "// Copyright 2025 International Digital Economy Academy\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n///|\nfn napi_ifname_socket() -> @fd_util.Fd raise {\n  if @event_loop.platform is Linux { make_udp_socket(IPv4, multicast=false, context=\"Creating interface lookup socket\") } else { @fd_util.invalid_fd }\n}\n\n///|\n#borrow(name)\nextern \"C\" fn if_nametoindex_ffi(\n  global_sock : @fd_util.Fd,\n  name : @os_string.OsString,\n) -> UInt = \"moonbitlang_async_if_nametoindex\"\n\n///|\nextern \"C\" fn if_indextoname_ffi(\n  global_sock : @fd_util.Fd,\n  index : UInt,\n) -> @c_buffer.Buffer = \"moonbitlang_async_if_indextoname\"\n\n///|\n#doc(hidden)\n#internal(internal, \"for internal use only\")\npub fn if_nametoindex(name : StringView, context~ : String) -> UInt raise {\n  let os_name = @os_string.encode(name)\n  let fd = napi_ifname_socket()\n  defer { if @fd_util.fd_is_valid(fd) { @fd_util.close(fd, kind=Socket, context=\"Closing interface lookup socket\") } }\n  let index = if_nametoindex_ffi(fd, os_name)\n  if index is 0 {\n    @os_error.check_errno(\"\\{context}: if_nametoindex(\\{name.escape()})\")\n  }\n  index\n}\n\n///|\n#doc(hidden)\n#internal(internal, \"for internal use only\")\npub fn if_indextoname(index : UInt, context~ : String) -> String raise {\n  let fd = napi_ifname_socket()\n  defer { if @fd_util.fd_is_valid(fd) { @fd_util.close(fd, kind=Socket, context=\"Closing interface lookup socket\") } }\n  let name = if_indextoname_ffi(fd, index)\n  if name.is_null() {\n    @os_error.check_errno(\"\\{context}: if_indextoname(\\{index})\")\n  }\n  @os_string.decode(name)\n}\n",
  "async/files/src/internal/event_loop/thread_pool.mbt": "// Copyright 2025 International Digital Economy Academy\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n///|\n#external\npriv type Worker\n\n///|\nextern \"C\" fn Worker::is_null(worker : Worker) -> Bool = \"napi_mbt_async_worker_is_null\"\n\n///|\nextern \"C\" fn init_thread_pool_ffi(event_bus : EventBus) -> @fd_util.Fd = \"moonbitlang_async_init_thread_pool\"\n\n///|\nextern \"C\" fn destroy_thread_pool() = \"moonbitlang_async_destroy_thread_pool\"\n\n///|\nextern \"C\" fn Worker::spawn(init_job_id : Int, init_job : Job) -> Worker = \"moonbitlang_async_spawn_worker\"\n\n///|\nextern \"C\" fn Worker::free(worker : Worker) = \"moonbitlang_async_free_worker\"\n\n///|\nextern \"C\" fn Worker::wake(worker : Worker, job_id : Int, job : Job) = \"moonbitlang_async_wake_worker\"\n\n///|\nextern \"C\" fn Worker::enter_idle(worker : Worker) = \"moonbitlang_async_worker_enter_idle\"\n\n///|\nextern \"C\" fn Worker::cancel_ffi(worker : Worker) -> Int = \"moonbitlang_async_cancel_worker\"\n\n///|\nfn Worker::cancel(\n  worker : Worker,\n  context~ : String,\n) -> CancellationStatus raise {\n  match worker.cancel_ffi() {\n    0 => RetryLater\n    1 => NeedWait\n    _ => {\n      @os_error.check_errno(context)\n      NoWait\n    }\n  }\n}\n\n///|\nextern \"C\" fn Worker::check_cancellation_retry(worker : Worker) -> Bool = \"moonbitlang_async_worker_check_cancellation_retry\"\n\n///|\n#cfg(not(platform=\"windows\"))\n#borrow(output)\nextern \"C\" fn fetch_completion_ffi(\n  notify_recv : @fd_util.Fd,\n  output : FixedArray[Int],\n) -> Int = \"moonbitlang_async_fetch_completion\"\n\n///|\n#external\npriv type Job\n\n///|\nextern \"C\" fn Job::free(self : Job) -> Unit = \"moonbitlang_async_free_job\"\n\n///|\nextern \"C\" fn Job::ret(self : Job) -> Int = \"moonbitlang_async_job_get_ret\"\n\n///|\nextern \"C\" fn Job::err(self : Job) -> Int = \"moonbitlang_async_job_get_err\"\n\n///|\n/// This is no-op on native, just to align API shape with Wasm.\nfn Job::copy_output(self : Job) -> Unit {\n  ignore(self)\n}\n\n///|\n#owned(buf)\nextern \"C\" fn Job::read(\n  fd : @fd_util.Fd,\n  buf : FixedArray[Byte],\n  offset : Int,\n  len : Int,\n  // `-1` indicates no offset\n  position~ : Int64,\n) -> Job = \"moonbitlang_async_make_read_job\"\n\n///|\n#owned(buf)\nextern \"C\" fn Job::write(\n  fd : @fd_util.Fd,\n  buf : Bytes,\n  offset : Int,\n  len : Int,\n  // `-1` indicates no offset\n  position~ : Int64,\n) -> Job = \"moonbitlang_async_make_write_job\"\n\n///|\n#owned(buf)\nextern \"C\" fn Job::read_console(\n  fd : @fd_util.Fd,\n  buf : FixedArray[UInt16],\n  offset : Int,\n  len : Int,\n) -> Job = \"moonbitlang_async_make_read_console_job\"\n\n///|\n#owned(buf)\nextern \"C\" fn Job::write_console(\n  fd : @fd_util.Fd,\n  buf : FixedArray[UInt16],\n  offset : Int,\n  len : Int,\n) -> Job = \"moonbitlang_async_make_write_console_job\"\n\n///|\n#owned(filename, stat_buf)\nextern \"C\" fn Job::open(\n  filename : @os_string.OsString,\n  // 0 => read only, 1 => write only, 2 => read write\n  access : Int,\n  // See `@fs.CreateMode` for more details\n  create~ : Int,\n  append~ : Bool,\n  // 0 => no sync, 1 => data sync, 2 => full sync\n  sync~ : Int,\n  // permission for file when new file is created\n  mode~ : Int,\n  stat_request~ : UInt,\n  stat_buf~ : FixedArray[Byte],\n  stat_buf_len~ : Int,\n) -> OpenJob = \"moonbitlang_async_make_open_job\"\n\n///|\npriv struct OpenJob(Job)\n\n///|\nextern \"C\" fn OpenJob::fd(job : OpenJob) -> @fd_util.Fd = \"moonbitlang_async_open_job_get_fd\"\n\n///|\npub const STAT_FILE_KIND : UInt = 0x0001\n\n///|\npub const STAT_FILE_SIZE : UInt = 0x0002\n\n///|\npub const STAT_DEVICE_ID : UInt = 0x0004\n\n///|\npub const STAT_FILE_ID : UInt = 0x0008\n\n///|\npub const STAT_ACCESS_TIME : UInt = 0x0010\n\n///|\npub const STAT_MODIFY_TIME : UInt = 0x0020\n\n///|\npub const STAT_CHANGE_TIME : UInt = 0x0040\n\n///|\npub const STAT_CREATE_TIME : UInt = 0x0080\n\n///|\n#owned(buf)\nextern \"C\" fn Job::fstatx(\n  fd : @fd_util.Fd,\n  request : UInt,\n  buf : FixedArray[Byte],\n  buf_len : Int,\n) -> Job = \"moonbitlang_async_make_fstatx_job\"\n\n///|\n#owned(path, buf)\nextern \"C\" fn Job::statx(\n  path : @os_string.OsString,\n  request : UInt,\n  buf : FixedArray[Byte],\n  buf_len : Int,\n  parent~ : @fd_util.Fd,\n  follow_symlink~ : Bool,\n) -> Job = \"moonbitlang_async_make_statx_job\"\n\n///|\npub(all) enum Access {\n  Exist = 0\n  CanRead = 1\n  CanWrite = 2\n  CanExecute = 3\n}\n\n///|\n#owned(path)\nextern \"C\" fn Job::access(path : @os_string.OsString, access : Access) -> Job = \"moonbitlang_async_make_access_job\"\n\n///|\n#owned(path)\nextern \"C\" fn Job::chmod(path : @os_string.OsString, mode : Int) -> Job = \"moonbitlang_async_make_chmod_job\"\n\n///|\nextern \"C\" fn Job::fsync(fd : @fd_util.Fd, only_data : Bool) -> Job = \"moonbitlang_async_make_fsync_job\"\n\n///|\nextern \"C\" fn Job::flock(fd : @fd_util.Fd, exclusive : Bool) -> Job = \"moonbitlang_async_make_flock_job\"\n\n///|\n#owned(path)\nextern \"C\" fn Job::remove(path : @os_string.OsString) -> Job = \"moonbitlang_async_make_remove_job\"\n\n///|\n#owned(old_path, new_path)\nextern \"C\" fn Job::rename(\n  old_path : @os_string.OsString,\n  new_path : @os_string.OsString,\n  replace~ : Bool,\n) -> Job = \"moonbitlang_async_make_rename_job\"\n\n///|\n#owned(target, path)\nextern \"C\" fn Job::symlink(\n  target : @os_string.OsString,\n  path : @os_string.OsString,\n  force_symlink~ : Bool,\n) -> Job = \"moonbitlang_async_make_symlink_job\"\n\n///|\n#owned(path)\nextern \"C\" fn Job::mkdir(path : @os_string.OsString, mode : Int) -> Job = \"moonbitlang_async_make_mkdir_job\"\n\n///|\n#owned(path)\nextern \"C\" fn Job::rmdir(path : @os_string.OsString) -> Job = \"moonbitlang_async_make_rmdir_job\"\n\n///|\nextern \"C\" fn Job::readdir(\n  dir : @fd_util.Fd,\n  buf : @c_buffer.Buffer,\n  len : Int,\n  restart : Bool,\n) -> Job = \"moonbitlang_async_make_readdir_job\"\n\n///|\n#owned(path)\nextern \"C\" fn Job::realpath(path : @os_string.OsString) -> Job = \"moonbitlang_async_make_realpath_job\"\n\n///|\nextern \"C\" fn Job::get_realpath_result(job : Job) -> @c_buffer.Buffer = \"moonbitlang_async_get_realpath_result\"\n\n///|\npriv struct SpawnJob(Job)\n\n///|\n#cfg(not(platform=\"windows\"))\n#owned(path, args, env)\nextern \"C\" fn Job::spawn_unix_ffi(\n  path : @os_string.OsString,\n  args : @c_buffer.Buffer,\n  env~ : @c_buffer.Buffer,\n  stdin~ : @fd_util.Fd,\n  stdout~ : @fd_util.Fd,\n  stderr~ : @fd_util.Fd,\n  is_orphan~ : Bool,\n) -> SpawnJob = \"moonbitlang_async_make_spawn_job\"\n\n///|\n#cfg(platform=\"windows\")\n#owned(command_line, env)\nextern \"C\" fn Job::spawn_windows_ffi(\n  command_line : @os_string.OsString,\n  env~ : @c_buffer.Buffer,\n  stdin~ : @fd_util.Fd,\n  stdout~ : @fd_util.Fd,\n  stderr~ : @fd_util.Fd,\n  is_orphan~ : Bool,\n) -> SpawnJob = \"moonbitlang_async_make_spawn_job\"\n\n///|\n#borrow(job)\n#owned(cwd)\nextern \"C\" fn SpawnJob::set_cwd(job : SpawnJob, cwd : @os_string.OsString) = \"moonbitlang_async_spawn_job_set_cwd\"\n\n///|\n#cfg(platform=\"windows\")\n#borrow(job)\nextern \"C\" fn SpawnJob::set_no_console_window(job : SpawnJob) = \"moonbitlang_async_spawn_job_set_no_console_window\"\n\n///|\nextern \"C\" fn SpawnJob::result_handle(job : SpawnJob) -> @fd_util.Fd = \"moonbitlang_async_get_spawn_job_result_handle\"\n\n///|\n#cfg(not(platform=\"windows\"))\nfn Job::spawn_unix(\n  path : @os_string.OsString,\n  args : @c_buffer.Buffer,\n  env~ : @c_buffer.Buffer,\n  stdin~ : @fd_util.Fd,\n  stdout~ : @fd_util.Fd,\n  stderr~ : @fd_util.Fd,\n  is_orphan~ : Bool,\n  cwd~ : @os_string.OsString?,\n) -> SpawnJob {\n  let job = Job::spawn_unix_ffi(\n    path,\n    args,\n    env~,\n    stdin~,\n    stdout~,\n    stderr~,\n    is_orphan~,\n  )\n  if cwd is Some(cwd) {\n    job.set_cwd(cwd)\n  }\n  job\n}\n\n///|\n#cfg(platform=\"windows\")\nfn Job::spawn_windows(\n  command_line : @os_string.OsString,\n  env~ : @c_buffer.Buffer,\n  stdin~ : @fd_util.Fd,\n  stdout~ : @fd_util.Fd,\n  stderr~ : @fd_util.Fd,\n  is_orphan~ : Bool,\n  cwd~ : @os_string.OsString?,\n  no_console_window~ : Bool,\n) -> SpawnJob {\n  let job = Job::spawn_windows_ffi(\n    command_line,\n    env~,\n    stdin~,\n    stdout~,\n    stderr~,\n    is_orphan~,\n  )\n  if cwd is Some(cwd) {\n    job.set_cwd(cwd)\n  }\n  if no_console_window {\n    job.set_no_console_window()\n  }\n  job\n}\n\n///|\n// `handle` is used by Windows, `pid` is used by Linux\nextern \"C\" fn Job::wait_for_process(handle : @fd_util.Fd, pid~ : Int) -> Job = \"moonbitlang_async_make_wait_for_process_job\"\n\n///|\n#owned(addr)\nextern \"C\" fn Job::bind(socket : @fd_util.Fd, addr : Bytes) -> Job = \"moonbitlang_async_make_bind_job\"\n\n///|\n#owned(host)\nextern \"C\" fn Job::getaddrinfo(host : @os_string.OsString) -> Job = \"moonbitlang_async_make_getaddrinfo_job\"\n\n///|\nextern \"C\" fn Job::get_getaddrinfo_result(job : Job) -> AddrInfo = \"moonbitlang_async_get_getaddrinfo_result\"\n\n///|\n#cfg(not(platform=\"windows\"))\n#owned(path)\nextern \"C\" fn Job::inotify_add_watch(\n  inotify : @fd_util.Fd,\n  path : @os_string.OsString,\n  is_dir : Bool,\n) -> Job = \"moonbitlang_async_make_inotify_add_watch_job\"\n",
  "async/files/src/internal/event_loop/timer.mbt": "// Copyright 2025 International Digital Economy Academy\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n///|\nstruct Timer {\n  timer_id : Int\n  expire_time : Int64\n  callback : () -> Unit\n}\n\n///|\n// Timer identifiers belong to the active environment.\n\n///|\npub fn Timer::new(duration : Int, callback : () -> Unit) -> Timer {\n  guard! curr_loop.val is Some(evloop)\n  let expire_time = now() + duration.to_int64()\n  evloop.timer_id += 1\n  let timer = { timer_id: evloop.timer_id, expire_time, callback, }\n  evloop.timers.add(timer)\n  timer\n}\n\n///|\npub fn Timer::cancel(timer : Timer) -> Unit {\n  guard! curr_loop.val is Some(evloop)\n  evloop.timers.remove(timer)\n}\n\n///|\nimpl Eq for Timer with fn equal(t1, t2) {\n  t1.timer_id == t2.timer_id\n}\n\n///|\nimpl Compare for Timer with fn compare(t1, t2) {\n  match t1.expire_time.compare(t2.expire_time) {\n    0 => t1.timer_id.compare(t2.timer_id)\n    o => o\n  }\n}\n\n///|\npub async fn sleep(duration : Int) -> Unit noraise {\n  let coro = @coroutine.current_coroutine()\n  let timer = Timer::new(duration, () => coro.wake())\n  defer timer.cancel()\n  @coroutine.suspend()\n}\n",
  "async/files/src/internal/event_loop/thread_pool.c": "/*\n * Copyright 2025 International Digital Economy Academy\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdint.h>\n#include <moonbit.h>\n\n#ifdef _WIN32\n\n#ifndef _MSC_VER\n#error \"Currently only MSVC is supported on Windows\"\n#endif\n\n#include <winsock2.h>\n#include <windows.h>\n#include <ws2tcpip.h>\n#include <stddef.h>\n\n#pragma comment(lib, \"ws2_32.lib\")\n\ntypedef volatile int32_t atomic_int32_t;\n\n#else\n\n#include <pthread.h>\n#include <signal.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <fcntl.h>\n#include <string.h>\n#include <stdatomic.h>\n#include <errno.h>\n#include <time.h>\n#if !defined(__ANDROID__) || __ANDROID_API__ >= 28\n#include <spawn.h>\n#endif\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <sys/wait.h>\n#include <poll.h>\n\n#ifdef __linux__\n#include <sys/syscall.h>\n#include <sys/inotify.h>\n#include <linux/version.h>\n#endif\n\n#ifdef __MACH__\n#include <sys/attr.h>\n#endif\n\ntypedef int HANDLE;\ntypedef int SOCKET;\n#define GetLastError() errno\n#define SetLastError(err) errno = err\n\ntypedef _Atomic int32_t atomic_int32_t;\n\n#endif\n\n\n#ifdef _WIN32\n// Windows, use native event system to wake up worker thread\n#define WAKEUP_METHOD_EVENT\n\n#elif defined(__MACH__)\n// MacOS, there are some bug with thread-directed signal and `sigwait`,\n// so use condition variable to wake up worker threads\n#include <sys/event.h>\n#define WAKEUP_METHOD_COND_VAR\n\n#include <Availability.h>\n#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED\n#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 260000\n#define posix_spawn_file_actions_addchdir_np posix_spawn_file_actions_addchdir\n#endif\n#endif\n\n#else\n// Other UNIX-like systems, use signal and `sigwait` to wake up worker threads\n#define WAKEUP_METHOD_SIGNAL\n\n#if defined(__ANDROID__) && __ANDROID_API__ < 34\n#define posix_spawn_file_actions_addchdir_np(...) ENOSYS\n#endif\n\n#endif\n\nMOONBIT_FFI_EXPORT\nint32_t moonbitlang_async_get_platform() {\n#ifdef __linux__\n  return 0;\n#elif defined(__MACH__)\n  return 1;\n#elif defined(_WIN32)\n  return 2;\n#else\n  abort();\n#endif\n}\n\nstruct job {\n  // the return value of the job.\n  // should be set by the worker and read by waiter.\n  // for result that cannot fit in an integer,\n  // jobs can also store extra result in their payload\n  int32_t ret;\n\n  // the error code of the job.\n  // should be zefo iff the job succeeds\n  int32_t err;\n\n  // The worker that actually performs the job.\n  // it will receive the payload of the job as parameter.\n  // The return value of `worker` will become the return status of the whole job.\n  // In case of failure, `worker` should write error number to the second parameter.\n  int32_t (*worker)(void *, int32_t *);\n\n  // finalizer for the job\n  void (*free)(void *);\n\n  // Special cancellation function for the job.\n  // May be `NULL`, in this case the default one is used.\n  // Receive the payload of the job as parameter.\n  int32_t (*cancel_handler)(void *);\n\n  // Extra payload of the specific job,\n  // Directly inlined into `struct job` to avoid indirection.\n  char payload[];\n};\n\n// We pass jobs by payload pointer for convenience,\n// the `JOB_HEADER` macro extracts the header pointer when necessary.\n#define JOB_HEADER(payload_ptr) ((struct job*)(payload_ptr) - 1)\n\nMOONBIT_FFI_EXPORT\nvoid moonbitlang_async_free_job(void *payload_ptr) {\n  struct job *job = JOB_HEADER(payload_ptr);\n  (job->free)(job->payload);\n  free(job);\n}\n\nMOONBIT_FFI_EXPORT\nint32_t moonbitlang_async_job_get_ret(void *job) {\n  return JOB_HEADER(job)->ret;\n}\n\nMOONBIT_FFI_EXPORT\nint32_t moonbitlang_async_job_get_err(void *job) {\n  return JOB_HEADER(job)->err;\n}\n\n// =======================================================\n// =================== the thread pool ===================\n// =======================================================\n\n/* Per-environment worker pool. No process signal handlers are installed.\n * Windows uses CancelSynchronousIo. POSIX cancellation is enabled only inside\n * upstream cancellable syscall regions; cancellation cleanup only updates C\n * job state and notifies the owning loop. MoonBit RC stays on the host thread. */\nstruct napi_mbt_async_pool {\n  int initialized;\n  HANDLE notify_send;\n  HANDLE notify_recv;\n#ifndef _WIN32\n  sigset_t old_sigmask;\n#endif\n};\n#ifdef _WIN32\n#define NAPI_MBT_ASYNC_TLS __declspec(thread)\n#else\n#define NAPI_MBT_ASYNC_TLS _Thread_local\n#endif\nstatic NAPI_MBT_ASYNC_TLS struct napi_mbt_async_pool *active_pool;\nstatic NAPI_MBT_ASYNC_TLS struct worker *current_worker;\n#define pool (*active_pool)\nMOONBIT_FFI_EXPORT void *napi_mbt_async_pool_current(void) { return active_pool; }\nMOONBIT_FFI_EXPORT void *napi_mbt_async_pool_null(void) { return NULL; }\nMOONBIT_FFI_EXPORT void *napi_mbt_async_pool_swap(void *next) {\n  void *previous = active_pool; active_pool = next; return previous;\n}\n\nenum WorkerState { Running = 0, Cancelling, Cancelled, Waiting };\nstruct worker {\n#ifdef _WIN32\n  HANDLE id;\n  HANDLE event;\n#else\n  pthread_t id;\n  int thread_valid;\n#endif\n  struct napi_mbt_async_pool *owner;\n  int32_t job_id;\n  struct job *job;\n  atomic_int32_t state;\n  atomic_int32_t inside_cancellable_region;\n};\nenum { CANCELLATION_STATUS_RETRY_LATER = 0, CANCELLATION_STATUS_NEED_WAIT = 1 };\n\nvoid moonbitlang_async_notify_event_loop(int32_t data) {\n#ifdef _WIN32\n  PostQueuedCompletionStatus(pool.notify_send, data, (ULONG_PTR)pool.notify_recv, 0);\n#else\n  while (write(pool.notify_send, &data, sizeof(data)) < 0 && errno == EINTR) {}\n#endif\n}\nstruct worker *moonbitlang_async_get_current_worker(void) { return current_worker; }\n\n#ifndef _WIN32\nstatic void worker_cancelled(void *data) {\n  struct worker *worker = data;\n  worker->job->ret = -1;\n  worker->job->err = ECANCELED;\n  worker->inside_cancellable_region = 0;\n  worker->state = Waiting;\n  moonbitlang_async_notify_event_loop(worker->job_id);\n}\n// A POSIX job owns its thread until completion/cancellation. Joining before\n// reuse makes deferred pthread cancellation incapable of affecting a later job.\nstatic void *worker_loop(void *data) {\n  struct worker *worker = data;\n  active_pool = worker->owner;\n  current_worker = worker;\n  pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);\n  pthread_cleanup_push(worker_cancelled, worker);\n  worker->job->err = 0;\n  worker->job->ret = worker->job->worker(worker->job->payload, &worker->job->err);\n  worker->inside_cancellable_region = 0;\n  worker->state = Waiting;\n  moonbitlang_async_notify_event_loop(worker->job_id);\n  pthread_cleanup_pop(0);\n  return NULL;\n}\n#else\nstatic DWORD WINAPI worker_loop(void *data) {\n  struct worker *worker = data;\n  active_pool = worker->owner;\n  current_worker = worker;\n  while (worker->job) {\n    worker->job->err = 0;\n    worker->job->ret = worker->job->worker(worker->job->payload, &worker->job->err);\n    worker->inside_cancellable_region = 0;\n    worker->state = Waiting;\n    moonbitlang_async_notify_event_loop(worker->job_id);\n    WaitForSingleObject(worker->event, INFINITE);\n  }\n  return 0;\n}\n#endif\n\nint32_t moonbitlang_async_enter_cancellable_region(struct worker *worker) {\n  if (!worker) return 0;\n  worker->inside_cancellable_region = 1;\n  if (worker->state == Cancelling) {\n    worker->state = Cancelled;\n    worker->inside_cancellable_region = 0;\n    return 1;\n  }\n#ifndef _WIN32\n  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);\n  pthread_testcancel();\n#endif\n  return 0;\n}\nvoid moonbitlang_async_leave_cancellable_region(struct worker *worker) {\n  if (!worker) return;\n#ifndef _WIN32\n  pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);\n#endif\n  worker->inside_cancellable_region = 0;\n}\nMOONBIT_FFI_EXPORT void moonbitlang_async_worker_enter_idle(struct worker *worker) {\n  worker->job = NULL;\n}\nMOONBIT_FFI_EXPORT int32_t moonbitlang_async_cancel_worker(struct worker *worker) {\n  if (worker->state == Cancelled || worker->state == Waiting) return CANCELLATION_STATUS_NEED_WAIT;\n  worker->state = Cancelling;\n  if (worker->job->cancel_handler) return worker->job->cancel_handler(worker->job->payload);\n#ifdef _WIN32\n  if (CancelSynchronousIo(worker->id)) return CANCELLATION_STATUS_NEED_WAIT;\n  return GetLastError() == ERROR_NOT_FOUND ? CANCELLATION_STATUS_RETRY_LATER : -1;\n#else\n  int result = pthread_cancel(worker->id);\n  if (result) { errno = result; return -1; }\n  return CANCELLATION_STATUS_NEED_WAIT;\n#endif\n}\nMOONBIT_FFI_EXPORT int32_t moonbitlang_async_worker_check_cancellation_retry(struct worker *worker) {\n  if (worker->state == Waiting) return 0;\n  moonbitlang_async_cancel_worker(worker);\n  return 1;\n}\n\nMOONBIT_FFI_EXPORT void moonbitlang_async_wake_worker(struct worker *worker, int32_t job_id, void *job) {\n#ifndef _WIN32\n  if (worker->thread_valid) pthread_join(worker->id, NULL);\n  worker->thread_valid = 0;\n#endif\n  worker->job_id = job_id;\n  worker->job = job ? JOB_HEADER(job) : NULL;\n  worker->state = Running;\n#ifdef _WIN32\n  SetEvent(worker->event);\n#else\n  if (worker->job) {\n    int status = pthread_create(&worker->id, NULL, worker_loop, worker);\n    if (status) {\n      worker->job->ret = -1; worker->job->err = status; worker->state = Waiting;\n      moonbitlang_async_notify_event_loop(job_id);\n    } else worker->thread_valid = 1;\n  }\n#endif\n}\nMOONBIT_FFI_EXPORT void moonbitlang_async_free_worker(struct worker *worker) {\n#ifdef _WIN32\n  moonbitlang_async_wake_worker(worker, 0, NULL);\n  WaitForSingleObject(worker->id, INFINITE);\n  CloseHandle(worker->id);\n  CloseHandle(worker->event);\n#else\n  if (worker->thread_valid) pthread_join(worker->id, NULL);\n#endif\n  free(worker);\n}\n\n#ifndef _WIN32\nint moonbitlang_async_event_bus_register(int event_bus, int fd, int32_t read_only);\n#endif\nMOONBIT_FFI_EXPORT HANDLE moonbitlang_async_init_thread_pool(HANDLE event_bus) {\n  if (active_pool) {\n#ifdef _WIN32\n    SetLastError(ERROR_INVALID_STATE); return NULL;\n#else\n    errno = EBUSY; return -1;\n#endif\n  }\n  active_pool = calloc(1, sizeof(*active_pool));\n  if (!active_pool) {\n#ifdef _WIN32\n    SetLastError(ERROR_OUTOFMEMORY); return NULL;\n#else\n    errno = ENOMEM; return -1;\n#endif\n  }\n#ifdef _WIN32\n  pool.notify_send = pool.notify_recv = event_bus;\n#else\n  pthread_sigmask(SIG_SETMASK, NULL, &pool.old_sigmask);\n  int pipefds[2];\n  if (pipe(pipefds) < 0) { free(active_pool); active_pool = NULL; return -1; }\n  pool.notify_recv = pipefds[0]; pool.notify_send = pipefds[1];\n  for (int i = 0; i < 2; ++i) {\n    int flags = fcntl(pipefds[i], F_GETFD);\n    if (flags < 0 || fcntl(pipefds[i], F_SETFD, flags | FD_CLOEXEC) < 0) goto failed;\n  }\n  if (moonbitlang_async_event_bus_register(event_bus, pool.notify_recv, 1) < 0) goto failed;\n#endif\n  pool.initialized = 1;\n  return pool.notify_recv;\n#ifndef _WIN32\nfailed:\n  { int saved = errno; close(pipefds[0]); close(pipefds[1]); free(active_pool); active_pool = NULL; errno = saved; return -1; }\n#endif\n}\nMOONBIT_FFI_EXPORT void moonbitlang_async_destroy_thread_pool(void) {\n  if (!active_pool) return;\n#ifndef _WIN32\n  close(pool.notify_recv); close(pool.notify_send);\n#endif\n  free(active_pool); active_pool = NULL;\n}\n\nMOONBIT_FFI_EXPORT struct worker *moonbitlang_async_spawn_worker(int32_t init_job_id, void *init_job) {\n  struct worker *worker = calloc(1, sizeof(*worker));\n  if (!worker) {\n#ifdef _WIN32\n    SetLastError(ERROR_OUTOFMEMORY);\n#else\n    errno = ENOMEM;\n#endif\n    return NULL;\n  }\n  worker->owner = active_pool;\n  worker->job_id = init_job_id;\n  worker->job = JOB_HEADER(init_job);\n  worker->state = Running;\n#ifdef _WIN32\n  worker->event = CreateEventA(NULL, FALSE, FALSE, NULL);\n  if (!worker->event) { free(worker); return NULL; }\n  worker->id = CreateThread(NULL, 0, worker_loop, worker, 0, NULL);\n  if (!worker->id) { CloseHandle(worker->event); free(worker); return NULL; }\n#else\n  int status = pthread_create(&worker->id, NULL, worker_loop, worker);\n  if (status) { free(worker); errno = status; return NULL; }\n  worker->thread_valid = 1;\n#endif\n  return worker;\n}\nMOONBIT_FFI_EXPORT int32_t napi_mbt_async_worker_is_null(struct worker *worker) { return worker == NULL; }\n\n#ifndef _WIN32\nint32_t moonbitlang_async_fetch_completion(int notify_recv, int32_t *output) {\n  int max_len = Moonbit_array_length(output);\n  return read(notify_recv, (char*)output, max_len * sizeof(int32_t));\n}\n#endif\n\nMOONBIT_FFI_EXPORT\nint32_t moonbitlang_async_errno_is_cancelled(int32_t err) {\n#ifdef _WIN32\n  return err == ERROR_OPERATION_ABORTED;\n#else\n  return err == EINTR || err == ECANCELED;\n#endif\n}\n\n// =========================================================\n// ===================== concrete jobs =====================\n// =========================================================\n\nMOONBIT_FFI_EXPORT\nvoid *moonbitlang_async_make_job(\n  int32_t size,\n  void (*free)(void*),\n  int32_t (*worker)(void*, int32_t*),\n  int32_t (*cancel_handler)(void*)\n) {\n  struct job *job = (struct job*)malloc(size + sizeof(struct job));\n  job->ret = 0;\n  job->err = 0;\n  job->worker = worker;\n  job->free = free;\n  job->cancel_handler = cancel_handler;\n  return job->payload;\n}\n\n#define MAKE_JOB(name, cancel_handler) (struct name##_job*)moonbitlang_async_make_job(\\\n  sizeof(struct name##_job),\\\n  (void (*)(void*))free_##name##_job,\\\n  (int32_t (*)(void*, int32_t*)) name##_job_worker,\\\n  (int32_t (*)(void*))cancel_handler\\\n)\n\n// ===== sleep job, sleep via thread pool, for testing only =====\n\nstruct sleep_job {\n  int duration;\n};\n\nstatic\nvoid free_sleep_job(struct sleep_job *job) {}\n\nstatic\nint32_t sleep_job_worker(struct sleep_job *job, int32_t *err_out) {\n#ifdef _WIN32\n  Sleep(job->duration);\n#else\n  int32_t ms = job->duration;\n  struct timespec duration = { ms / 1000, (ms % 1000) * 1000000 };\n\n#ifdef __MACH__\n  // On GitHub CI MacOS runner, `nanosleep` is very imprecise,\n  // causing corrupted test result.\n  // However `kqueue` seems to have very accurate timing.\n  // Since `OP_SLEEP` is only for testing purpose,\n  // here we use `kqueue` (in an absolutely wrong way) to perform sleep.\n  int kqfd = kqueue();\n  struct kevent kev;\n  kevent(kqfd, 0, 0, &kev, 1, &duration);\n  close(kqfd);\n#else\n  nanosleep(&duration, 0);\n#endif\n\n#endif\n\n  return 0;\n}\n\nMOONBIT_FFI_EXPORT\nstruct sleep_job *moonbitlang_async_make_sleep_job(int ms) {\n  struct sleep_job *job = MAKE_JOB(sleep, 0);\n  job->duration = ms;\n  return job;\n}\n\n// ===== read job, for reading non-pollable stuff =====\n\nstruct read_job {\n  HANDLE fd;\n  char *buf;\n  int offset;\n  int len;\n  int64_t position;\n};\n\nstatic\nvoid free_read_job(struct read_job *job) {\n  moonbit_decref(job->buf);\n}\n\nstatic\nint32_t read_job_worker(struct read_job *job, int32_t *err_out) {\n  struct worker *worker = moonbitlang_async_get_current_worker();\n\n#ifdef _WIN32\n\n   OVERLAPPED overlapped;\n   memset(&overlapped, 0, sizeof(OVERLAPPED));\n   if (job->position >= 0) {\n     overlapped.Offset = job->position & 0xffffffff;\n     overlapped.OffsetHigh = job->position >> 32;\n   }\n\n   if (moonbitlang_async_enter_cancellable_region(worker)) {\n     *err_out = ERROR_OPERATION_ABORTED;\n     return -1;\n   }\n\n   DWORD bytes_transferred;\n   BOOL result = ReadFile(\n     job->fd,\n     job->buf + job->offset,\n     job->len,\n     &bytes_transferred,\n     job->position < 0 ? NULL : &overlapped\n   );\n\n   moonbitlang_async_leave_cancellable_region(worker);\n\n   if (result) {\n     return bytes_transferred;\n   } else {\n     int err = GetLastError();\n     if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) {\n       return 0;\n     } else {\n       *err_out = err;\n       return -1;\n     }\n   }\n\n#else\n\n  int32_t ret;\n  if (job->position < 0) {\n    while (1) {\n      if (moonbitlang_async_enter_cancellable_region(worker)) {\n        *err_out = EINTR;\n        return -1;\n      }\n\n      ret = read(job->fd, job->buf + job->offset, job->len);\n      moonbitlang_async_leave_cancellable_region(worker);\n\n      if (ret >= 0)\n        break;\n\n      if (errno != EAGAIN && errno != EWOULDBLOCK)\n        break;\n\n      struct pollfd pfd = { job->fd, POLL_IN, 0 };\n      if (moonbitlang_async_enter_cancellable_region(worker)) {\n        *err_out = EINTR;\n        return -1;\n      }\n\n      ret = poll(&pfd, 1, -1);\n      moonbitlang_async_leave_cancellable_region(worker);\n\n      if (ret < 0)\n        break;\n    }\n  } else {\n    ret = pread(\n      job->fd,\n      job->buf + job->offset,\n      job->len,\n      job->position\n    );\n  }\n\n  if (ret < 0)\n    *err_out = errno;\n\n  return ret;\n\n#endif\n}\n\nstruct read_job *moonbitlang_async_make_read_job(\n  HANDLE fd,\n  char *buf,\n  int offset,\n  int len,\n  int64_t position\n) {\n  struct read_job *job = MAKE_JOB(read, 0);\n  job->fd = fd;\n  job->buf = buf;\n  job->offset = offset;\n  job->len = len;\n  job->position = position;\n  return job;\n}\n\n// ===== write job, for writing non-pollable stuff =====\n\nstruct write_job {\n  HANDLE fd;\n  char *buf;\n  int offset;\n  int len;\n  int64_t position;\n};\n\nstatic\nvoid free_write_job(struct write_job *job) {\n  moonbit_decref(job->buf);\n}\n\nstatic\nint32_t write_job_worker(struct write_job *job, int32_t *err_out) {\n  struct worker *worker = moonbitlang_async_get_current_worker();\n\n#ifdef _WIN32\n\n   OVERLAPPED overlapped;\n   memset(&overlapped, 0, sizeof(OVERLAPPED));\n   if (job->position >= 0) {\n     overlapped.Offset = job->position & 0xffffffff;\n     overlapped.OffsetHigh = job->position >> 32;\n   }\n\n   DWORD bytes_transferred;\n   if (moonbitlang_async_enter_cancellable_region(worker)) {\n     *err_out = ERROR_OPERATION_ABORTED;\n     return -1;\n   }\n\n   BOOL result = WriteFile(\n     job->fd,\n     job->buf + job->offset,\n     job->len,\n     &bytes_transferred,\n     job->position < 0 ? NULL : &overlapped\n   );\n   moonbitlang_async_leave_cancellable_region(worker);\n\n   if (result)\n     return bytes_transferred;\n   else {\n     *err_out = GetLastError();\n     return -1;\n   }\n\n#else\n\n  int32_t ret;\n  if (job->position < 0) {\n    while (1) {\n      if (moonbitlang_async_enter_cancellable_region(worker)) {\n        *err_out = EINTR;\n        return -1;\n      }\n\n      ret = write(job->fd, job->buf + job->offset, job->len);\n      moonbitlang_async_leave_cancellable_region(worker);\n\n      if (ret >= 0)\n        break;\n\n      if (errno != EAGAIN && errno != EWOULDBLOCK)\n        break;\n\n      struct pollfd pfd = { job->fd, POLL_OUT, 0 };\n      if (moonbitlang_async_enter_cancellable_region(worker)) {\n        *err_out = EINTR;\n        return -1;\n      }\n\n      ret = poll(&pfd, 1, -1);\n      moonbitlang_async_leave_cancellable_region(worker);\n\n      if (ret < 0)\n        break;\n    }\n  } else {\n    ret = pwrite(\n      job->fd,\n      job->buf + job->offset,\n      job->len,\n      job->position\n    );\n  }\n  if (ret < 0)\n    *err_out = errno;\n  return ret;\n\n#endif\n}\n\nstruct write_job *moonbitlang_async_make_write_job(\n  HANDLE fd,\n  char *buf,\n  int offset,\n  int len,\n  int64_t position\n) {\n  struct write_job *job = MAKE_JOB(write, 0);\n  job->fd = fd;\n  job->buf = buf;\n  job->offset = offset;\n  job->len = len;\n  job->position = position;\n  return job;\n}\n\n// ===== read_console job, read UTF-16 data from a Windows console =====\nstruct read_console_job {\n  HANDLE fd;\n  char *buf;\n  int32_t offset;\n  int32_t len;\n};\n\nstatic\nvoid free_read_console_job(struct read_console_job *job) {\n  moonbit_decref(job->buf);\n}\n\nstatic\nint32_t read_console_job_worker(struct read_console_job *job, int32_t *err_out) {\n  struct worker *worker = moonbitlang_async_get_current_worker();\n\n#ifdef _WIN32\n\n   if (moonbitlang_async_enter_cancellable_region(worker)) {\n     *err_out = ERROR_OPERATION_ABORTED;\n     return -1;\n   }\n\n   DWORD bytes_transferred;\n   BOOL result = ReadConsoleW(\n     job->fd,\n     (WCHAR*)job->buf + job->offset,\n     job->len,\n     &bytes_transferred,\n     NULL\n   );\n\n   moonbitlang_async_leave_cancellable_region(worker);\n\n   if (result) {\n     return bytes_transferred;\n   } else {\n     int err = GetLastError();\n     if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) {\n       return 0;\n     } else {\n       *err_out = err;\n       return -1;\n     }\n   }\n\n#else\n\n  *err_out = ENOSYS;\n  return -1;\n\n#endif\n}\n\nstruct read_console_job *moonbitlang_async_make_read_console_job(\n  HANDLE fd,\n  char *buf,\n  int32_t offset,\n  int32_t len\n) {\n  struct read_console_job *job = MAKE_JOB(read_console, 0);\n  job->fd = fd;\n  job->buf = buf;\n  job->offset = offset;\n  job->len = len;\n  return job;\n}\n\n\n// ===== write console job, write UTF-16 data to Windows console =====\n\nstruct write_console_job {\n  HANDLE fd;\n  char *buf;\n  int offset;\n  int len;\n};\n\nstatic\nvoid free_write_console_job(struct write_console_job *job) {\n  moonbit_decref(job->buf);\n}\n\nstatic\nint32_t write_console_job_worker(struct write_console_job *job, int32_t *err_out) {\n  struct worker *worker = moonbitlang_async_get_current_worker();\n\n#ifdef _WIN32\n\n   DWORD bytes_transferred;\n   if (moonbitlang_async_enter_cancellable_region(worker)) {\n     *err_out = ERROR_OPERATION_ABORTED;\n     return -1;\n   }\n\n   BOOL result = WriteConsoleW(\n     job->fd,\n     (WCHAR*)job->buf + job->offset,\n     job->len,\n     &bytes_transferred,\n     NULL\n   );\n   moonbitlang_async_leave_cancellable_region(worker);\n\n   if (result)\n     return bytes_transferred;\n   else {\n     *err_out = GetLastError();\n     return -1;\n   }\n\n#else\n\n  *err_out = ENOSYS;\n  return -1;\n\n#endif\n}\n\nstruct write_console_job *moonbitlang_async_make_write_console_job(\n  HANDLE fd,\n  char *buf,\n  int offset,\n  int len\n) {\n  struct write_console_job *job = MAKE_JOB(write_console, 0);\n  job->fd = fd;\n  job->buf = buf;\n  job->offset = offset;\n  job->len = len;\n  return job;\n}\n\n\n// ===== spawn job, spawn foreign process =====\n#ifdef _WIN32\n\nstatic\nHANDLE global_job_object = INVALID_HANDLE_VALUE;\n\nstatic\nBOOL init_global_job_object() {\n  HANDLE job = CreateJobObjectA(NULL, NULL);\n  if (job == NULL)\n    return 0;\n\n  JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;\n  memset(&info, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));\n\n  info.BasicLimitInformation.LimitFlags =\n    JOB_OBJECT_LIMIT_BREAKAWAY_OK\n    | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK\n    | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;\n\n  if (\n    !SetInformationJobObject(\n      job,\n      JobObjectExtendedLimitInformation,\n      &info,\n      sizeof(info)\n    )\n  ) {\n    goto on_error;\n  }\n\n  if (!AssignProcessToJobObject(job, GetCurrentProcess())) {\n    if (GetLastError() == ERROR_ACCESS_DENIED) {\n      // Here we try to assign current process to the job for two purpose:\n      //\n      // 1. workaround the bug mentioned in https://github.com/libuv/libuv/pull/4152\n      // 2. according to https://learn.microsoft.com/en-us/windows/win32/api/jobapi2/nf-jobapi2-assignprocesstojobobject,\n      //    prior to Windows 8, a process can only be associated with a single job.\n      //    In this case, if the main process is already in a job,\n      //    our child process will by default also be in the same job,\n      //    so we cannot associate child process to a new job.\n      //    So here we simply give up auto-kill child process if\n      //    we cannot assign current process to the new object, which means:\n      //    \n      //    a. we are in Windows 7 or earlier\n      //    b. the main process is already associated with a job\n      SetLastError(0);\n    }\n    goto on_error;\n  }\n\n  global_job_object = job;\n  return 1;\n\non_error:\n  CloseHandle(job);\n  return 0;\n}\n\nstruct spawn_job {\n  LPWSTR command_line;\n  void *environment;\n  HANDLE stdio[3];\n  LPWSTR cwd;\n  int32_t no_console_window;\n  int32_t is_orphan;\n  int32_t init_error;\n  HANDLE result;\n};\n\nstatic\nvoid free_spawn_job(struct spawn_job *job) {\n  moonbit_decref(job->command_line);\n  free(job->environment);\n  if (job->cwd)\n    moonbit_decref(job->cwd);\n  if (job->result != INVALID_HANDLE_VALUE)\n    CloseHandle(job->result);\n}\n\nstatic\nint32_t spawn_job_worker(struct spawn_job *job, int32_t *err_out) {\n  static DWORD std_handle_values[] = {\n    STD_INPUT_HANDLE,\n    STD_OUTPUT_HANDLE,\n    STD_ERROR_HANDLE\n  };\n\n  if (job->init_error) {\n    // Handle error from initializing global job object,\n    // see `moonbitlang_async_make_spawn_job` below.\n    *err_out = job->init_error;\n    return 0;\n  }\n\n  HANDLE handles_to_inherit[3];\n  DWORD number_of_handles_to_inherit = 0;\n\n  for (int i = 0; i < 3; ++i) {\n    if (job->stdio[i] == INVALID_HANDLE_VALUE)\n      job->stdio[i] = GetStdHandle(std_handle_values[i]);\n\n    if (job->stdio[i] == INVALID_HANDLE_VALUE || job->stdio[i] == NULL)\n      // On Windows, no stdio channel is a valid & common case\n      // for GUI applications.\n      goto handle_already_added;\n\n    for (int j = 0; j < number_of_handles_to_inherit; ++j) {\n      if (handles_to_inherit[j] == job->stdio[i])\n        goto handle_already_added;\n    }\n\n    handles_to_inherit[number_of_handles_to_inherit++] = job->stdio[i];\n\n    if (\n      !SetHandleInformation(\n        job->stdio[i],\n        HANDLE_FLAG_INHERIT,\n        HANDLE_FLAG_INHERIT\n      )\n    ) {\n      *err_out = GetLastError();\n      return 0;\n    }\n  handle_already_added:\n    ;\n  }\n\n  DWORD create_flags =\n    CREATE_NEW_PROCESS_GROUP // so that we can gracefully terminate this process\n                             // via sending Ctrl+Break console event\n    | CREATE_UNICODE_ENVIRONMENT\n    | EXTENDED_STARTUPINFO_PRESENT;\n\n  if (job->no_console_window)\n    create_flags |= CREATE_NO_WINDOW;\n\n  STARTUPINFOEXW startup_info;\n  memset(&startup_info, 0, sizeof(STARTUPINFOEXW));\n  startup_info.StartupInfo.cb = sizeof(STARTUPINFOEXW);\n  startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES;\n  startup_info.StartupInfo.hStdInput = job->stdio[0];\n  startup_info.StartupInfo.hStdOutput = job->stdio[1];\n  startup_info.StartupInfo.hStdError = job->stdio[2];\n\n  SIZE_T attrs_size;\n  InitializeProcThreadAttributeList(NULL, 2, 0, &attrs_size);\n  startup_info.lpAttributeList = malloc(attrs_size);\n  if (\n    !InitializeProcThreadAttributeList(\n      startup_info.lpAttributeList,\n      2,\n      0,\n      &attrs_size\n    )\n  ) {\n    *err_out = GetLastError();\n    DeleteProcThreadAttributeList(startup_info.lpAttributeList);\n    free(startup_info.lpAttributeList);\n    return 0;\n  }\n\n  // `UpdateProcThreadAttribute` rejects an empty handle list with\n  // `ERROR_BAD_LENGTH`. Having no standard handles is valid for GUI processes.\n  if (\n    number_of_handles_to_inherit > 0\n    &&\n    !UpdateProcThreadAttribute(\n      startup_info.lpAttributeList,\n      0, // reserved\n      PROC_THREAD_ATTRIBUTE_HANDLE_LIST,\n      handles_to_inherit,\n      number_of_handles_to_inherit * sizeof(HANDLE),\n      NULL, // reserved\n      NULL // reserved\n    )\n  ) {\n    *err_out = GetLastError();\n    DeleteProcThreadAttributeList(startup_info.lpAttributeList);\n    free(startup_info.lpAttributeList);\n    return 0;\n  }\n\n#ifdef PROC_THREAD_ATTRIBUTE_JOB_LIST\n\n  if (!job->is_orphan) {\n    // On Windows 10 and later, there is a way to\n    // atomically assign a child process to new job atomically on creation.\n    // This can avoid race condition when main process is killed after `CreateProcess`,\n    // but before `AssignProcessToJobObject` on the child process.\n    if (\n      !UpdateProcThreadAttribute(\n        startup_info.lpAttributeList,\n        0,\n        PROC_THREAD_ATTRIBUTE_JOB_LIST,\n        &global_job_object,\n        sizeof(global_job_object),\n        NULL,\n        NULL\n      )\n    ) {\n      *err_out = GetLastError();\n      DeleteProcThreadAttributeList(startup_info.lpAttributeList);\n      free(startup_info.lpAttributeList);\n      return 0;\n    }\n  }\n\n#else\n\n  // Notice that we are not setting `CREATE_BREAKAWAY_FROM_JOB` for orphan process here.\n  // Because in case the main process is already in a job disallowing break away,\n  // setting `CREATE_BREAKAWAY_FROM_JOB` will fail the `CreateProcess` call.\n  if (!job->is_orphan && global_job_object != INVALID_HANDLE_VALUE) {\n    // to avoid the child process exit too fast\n    // before we assign it to the job object\n    create_flags |= CREATE_SUSPENDED;\n  }\n\n#endif\n\n  PROCESS_INFORMATION process_info;\n  BOOL result = CreateProcessW(\n    NULL,\n    job->command_line,\n    NULL, // security attributes for process\n    NULL, // security attributes for main thread\n    TRUE, // do not inherit handle\n    create_flags,\n    job->environment,\n    job->cwd,\n    (LPSTARTUPINFOW)&startup_info,\n    &process_info\n  );\n\n  if (startup_info.lpAttributeList) {\n    DeleteProcThreadAttributeList(startup_info.lpAttributeList);\n    free(startup_info.lpAttributeList);\n  }\n\n  if (!result) {\n    *err_out = GetLastError();\n    return 0;\n  }\n\n  if (create_flags & CREATE_SUSPENDED) {\n    // On Windows, hard termination is much more common,\n    // due to lack of a universal way for graceful process termination.\n    // So assign the new process to a job object,\n    // so that it is automatically killed when the main process is killed.\n    if (!AssignProcessToJobObject(global_job_object, process_info.hProcess)) {\n      *err_out = GetLastError();\n      TerminateProcess(process_info.hProcess, 1);\n      return 0;\n    }\n    ResumeThread(process_info.hThread);\n  }\n\n  CloseHandle(process_info.hThread);\n  job->result = process_info.hProcess;\n\n  return process_info.dwProcessId;\n}\n\nstruct spawn_job *moonbitlang_async_make_spawn_job(\n  LPWSTR command_line,\n  void *environment,\n  HANDLE stdin_handle,\n  HANDLE stdout_handle,\n  HANDLE stderr_handle,\n  int32_t is_orphan\n) {\n  struct spawn_job *job = MAKE_JOB(spawn, 0);\n  job->command_line = command_line;\n  job->environment = environment;\n  job->stdio[0] = stdin_handle;\n  job->stdio[1] = stdout_handle;\n  job->stdio[2] = stderr_handle;\n  job->cwd = NULL;\n  job->no_console_window = FALSE;\n  job->is_orphan = is_orphan;\n  job->init_error = 0;\n  job->result = INVALID_HANDLE_VALUE;\n\n  if (global_job_object == INVALID_HANDLE_VALUE && !init_global_job_object())\n    // To avoid race condition, we must initialize the global job object\n    // in the main thread. However job spawning functions cannot report error,\n    // so we delay the error reporting to the thread pool job\n    job->init_error = errno;\n\n  return job;\n}\n\nHANDLE moonbitlang_async_get_spawn_job_result_handle(struct spawn_job *job) {\n  HANDLE result = job->result;\n  job->result = INVALID_HANDLE_VALUE;\n  return result;\n}\n\nvoid moonbitlang_async_spawn_job_set_cwd(struct spawn_job *job, LPWSTR cwd) {\n  job->cwd = cwd;\n}\n\nvoid moonbitlang_async_spawn_job_set_no_console_window(struct spawn_job *job) {\n  job->no_console_window = TRUE;\n}\n\n// For windows, waiting for process is done via one dedicated thread per process,\n// As a future optimization, we may wait for multiple processes in a single thread.\n// But that would make cancellation a lot trickier.\n\nstruct wait_for_process_job {\n  HANDLE process;\n  HANDLE cancel;\n};\n\nstatic\nvoid free_wait_for_process_job(struct wait_for_process_job *job) {\n  CloseHandle(job->cancel);\n};\n\nstatic\nint32_t cancel_wait_for_process_job(struct wait_for_process_job *job) {\n  SetEvent(job->cancel);\n  return CANCELLATION_STATUS_NEED_WAIT;\n}\n\nstatic\nint32_t wait_for_process_job_worker(struct wait_for_process_job *job, int32_t *err_out) {\n  HANDLE handles[2] = { job->process, job->cancel };\n\n  DWORD result = WaitForMultipleObjects(2, handles, FALSE, INFINITE);\n  if (result == WAIT_FAILED)\n    *err_out = GetLastError();\n  else if (result == WAIT_OBJECT_0 + 1)\n    *err_out = ERROR_OPERATION_ABORTED;\n  return 0;\n}\n\nstruct wait_for_process_job *moonbitlang_async_make_wait_for_process_job(\n  HANDLE process,\n  int32_t pid\n) {\n  struct wait_for_process_job *job = MAKE_JOB(wait_for_process, cancel_wait_for_process_job);\n  job->process = process;\n  job->cancel = CreateEventA(NULL, FALSE, FALSE, NULL);\n  return job;\n}\n\n#else\n\nstruct spawn_job {\n  char *path;\n  char **args;\n  char **envp;\n  int stdio[3];\n  char *cwd;\n  int pidfd;\n};\n\nstatic\nvoid free_spawn_job(struct spawn_job *job) {\n  moonbit_decref(job->path);\n  for (char **cursor = job->args; *cursor; ++cursor)\n    free(*cursor);\n  free(job->args);\n  for (int i = 0; (job->envp)[i]; ++i) {\n    free((job->envp)[i]);\n  }\n  free(job->envp);\n  if (job->cwd)\n    moonbit_decref(job->cwd);\n  if (job->pidfd >= 0)\n    close(job->pidfd);\n}\n\n#if defined(__ANDROID__) && __ANDROID_API__ < 28\n\n// posix_spawn is unavailable on Android API < 28.\n// Return a job pre-filled with ENOSYS so the caller gets a proper error\n// instead of hanging forever (a NULL job causes the worker thread to exit\n// without sending a completion notification).\nstatic\nint32_t spawn_job_worker(struct spawn_job *job, int32_t *err_out) {\n  *err_out = ENOSYS;\n  return 0;\n}\n\n#else // posix_spawn available\n\nstatic\nint32_t spawn_job_worker(struct spawn_job *job, int32_t *err_out) {\n  posix_spawnattr_t attr;\n  posix_spawnattr_init(&attr);\n  posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF);\n\n  posix_spawnattr_setsigmask(&attr, &pool.old_sigmask);\n\n  sigset_t all_signals;\n  sigfillset(&all_signals);\n  posix_spawnattr_setsigdefault(&attr, &all_signals);\n\n  posix_spawn_file_actions_t file_actions;\n  posix_spawn_file_actions_init(&file_actions);\n  for (int i = 0; i < 3; ++i) {\n    int fd = job->stdio[i];\n    if (fd >= 0) {\n      *err_out = posix_spawn_file_actions_adddup2(&file_actions, fd, i);\n      if (*err_out) goto exit;\n    }\n  }\n  if (job->cwd) {\n    *err_out = posix_spawn_file_actions_addchdir_np(&file_actions, job->cwd);\n    if (*err_out) goto exit;\n  }\n\n  int32_t ret = 0;\n  if (strchr(job->path, '/')) {\n    *err_out = posix_spawn(\n      &ret,\n      job->path,\n      &file_actions,\n      &attr,\n      job->args,\n      job->envp\n    );\n  } else {\n    *err_out = posix_spawnp(\n      &ret,\n      job->path,\n      &file_actions,\n      &attr,\n      job->args,\n      job->envp\n    );\n  }\n\n#ifdef __linux__\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)\n  if (!*err_out) {\n    /* A failed `pidfd_open` — `EMFILE` under descriptor pressure, or\n       `ENOSYS`/`EPERM` in some containers — must not turn a successful spawn\n       into an error: the child is already running, and reporting failure\n       here would let it escape ownership, unkilled and unreaped. Leave the\n       handle invalid instead; `wait_pid` falls back to a blocking `waitpid`\n       in a worker thread when there is no pidfd. */\n    job->pidfd = syscall(SYS_pidfd_open, ret, 0);\n  }\n#endif // #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)\n#endif // #ifdef __linux__\n\nexit:\n  posix_spawnattr_destroy(&attr);\n  posix_spawn_file_actions_destroy(&file_actions);\n  return ret;\n}\n\nint moonbitlang_async_get_spawn_job_result_handle(struct spawn_job *job) {\n  int result = job->pidfd;\n  job->pidfd = -1;\n  return result;\n}\n\n#endif // posix_spawn availability\n\nstruct spawn_job *moonbitlang_async_make_spawn_job(\n  char *path,\n  char **args,\n  char **envp,\n  int stdin_fd,\n  int stdout_fd,\n  int stderr_fd,\n  int32_t is_orphan\n) {\n  struct spawn_job *job = MAKE_JOB(spawn, 0);\n  job->path = path;\n  job->args = args;\n  job->envp = envp;\n  job->stdio[0] = stdin_fd;\n  job->stdio[1] = stdout_fd;\n  job->stdio[2] = stderr_fd;\n  job->cwd = 0;\n  job->pidfd = -1;\n  return job;\n}\n\nvoid moonbitlang_async_spawn_job_set_cwd(struct spawn_job *job, char *cwd) {\n  job->cwd = cwd;\n}\n\n// Unix wait_for_process: blocking waitpid in worker thread\n// Used as fallback when pidfd_open is not available (e.g. Android, older Linux)\n\nstruct wait_for_process_job {\n  pid_t pid;\n};\n\nstatic\nvoid free_wait_for_process_job(struct wait_for_process_job *job) {}\n\nstatic\nint32_t wait_for_process_job_worker(struct wait_for_process_job *job, int32_t *err_out) {\n  struct worker *worker = moonbitlang_async_get_current_worker();\n\n  int status;\n  if (moonbitlang_async_enter_cancellable_region(worker)) {\n    *err_out = EINTR;\n    return -1;\n  }\n\n  int ret = waitpid(job->pid, &status, 0);\n  moonbitlang_async_leave_cancellable_region(worker);\n\n  if (ret == job->pid) {\n    return WEXITSTATUS(status);\n  } else {\n    *err_out = errno;\n    return 0;\n  }\n}\n\nstruct wait_for_process_job *moonbitlang_async_make_wait_for_process_job(\n  HANDLE handle,\n  int32_t pid\n) {\n  struct wait_for_process_job *job = MAKE_JOB(wait_for_process, 0);\n  job->pid = pid;\n  return job;\n}\n\n#endif\n\n// ===== bind job, bind socket to specific address =====\nstruct bind_job {\n  HANDLE socket;\n  struct sockaddr *addr;\n};\n\nstatic\nvoid free_bind_job(struct bind_job *job) {\n  moonbit_decref(job->addr);\n}\n\nstatic\nint32_t bind_job_worker(struct bind_job *job, int32_t *err_out) {\n  int32_t ret = bind((SOCKET)job->socket, job->addr, Moonbit_array_length(job->addr));\n\n  if (ret < 0)\n#ifdef _WIN32\n    *err_out = GetLastError();\n#else\n    *err_out = errno;\n#endif\n  return ret;\n}\n\nMOONBIT_FFI_EXPORT\nstruct bind_job *moonbitlang_async_make_bind_job(HANDLE socket, struct sockaddr *addr) {\n  struct bind_job *job = MAKE_JOB(bind, 0);\n  job->socket = socket;\n  job->addr = addr;\n  return job;\n}\n\n// ===== getaddrinfo job, resolve host name via `getaddrinfo` =====\n\n#ifdef _WIN32\ntypedef ADDRINFOW addrinfo_t;\n#else\ntypedef struct addrinfo addrinfo_t;\n#endif\n\nstruct getaddrinfo_job {\n  char *hostname;\n  addrinfo_t *result;\n  int32_t result_fetched;\n};\n\nstatic\nvoid free_getaddrinfo_job(struct getaddrinfo_job *job) {\n  moonbit_decref(job->hostname);\n  if (job->result && !job->result_fetched) {\n#ifdef _WIN32\n    FreeAddrInfoW(job->result);\n#else\n    freeaddrinfo(job->result);\n#endif\n  }\n}\n\nstatic\nint32_t getaddrinfo_job_worker(struct getaddrinfo_job *job, int32_t *err_out) {\n  addrinfo_t hint = {\n    AI_ADDRCONFIG, // ai_flags\n    AF_UNSPEC, // ai_family, support both IPv4 and IPv6\n    0, // ai_socktype\n    0, // ai_protocol\n    0, 0, 0, 0\n  };\n\n#ifdef _WIN32\n  int err = GetAddrInfoW(\n    (LPCWSTR)job->hostname,\n    0,\n    &hint,\n    &(job->result)\n  );\n  // https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfow#return-value\n  switch (err) {\n    case WSATRY_AGAIN:\n    case WSANO_RECOVERY:\n    case WSAEAFNOSUPPORT:\n    case WSAHOST_NOT_FOUND:\n    case WSATYPE_NOT_FOUND:\n    case WSAESOCKTNOSUPPORT:\n      return err;\n    default:\n      *err_out = err;\n      return 0;\n  }\n\n#else\n\n  int32_t ret = getaddrinfo(\n    job->hostname,\n    0,\n    &hint,\n    &(job->result)\n  );\n  if (ret == EAI_SYSTEM)\n    *err_out = errno;\n  return ret;\n\n#endif\n}\n\nstruct getaddrinfo_job *moonbitlang_async_make_getaddrinfo_job(char *hostname) {\n  struct getaddrinfo_job *job = MAKE_JOB(getaddrinfo, 0);\n  job->hostname = hostname;\n  job->result = 0;\n  job->result_fetched = 0;\n  return job;\n}\n\naddrinfo_t *moonbitlang_async_get_getaddrinfo_result(struct getaddrinfo_job *job) {\n  job->result_fetched = 1;\n  return job->result;\n}\n",
  "async/files/src/internal/event_loop/napi_embedded.mbt": "// Copyright 2026 napi-mbt contributors. Apache-2.0, matching moonbitlang/async.\n// Host-driven event loop. Each operation activates one environment's scheduler\n// and C thread pool, and restores the previous context on every exit path.\n\n///|\npub struct EmbeddedLoop {\n  priv evloop : EventLoop\n  priv scheduler : @coroutine.Scheduler\n  priv pool : @c_buffer.Buffer\n  priv mut closed : Bool\n  priv mut stepping : Bool\n}\n\n///|\nextern \"C\" fn embedded_pool_current() -> @c_buffer.Buffer = \"napi_mbt_async_pool_current\"\n\n///|\nextern \"C\" fn embedded_pool_swap(pool : @c_buffer.Buffer) -> @c_buffer.Buffer = \"napi_mbt_async_pool_swap\"\n\n///|\nextern \"C\" fn embedded_pool_null() -> @c_buffer.Buffer = \"napi_mbt_async_pool_null\"\n\n///|\npub fn EmbeddedLoop::new() -> EmbeddedLoop raise {\n  let previous = embedded_pool_swap(embedded_pool_null())\n  defer ignore(embedded_pool_swap(previous))\n  let evloop = EventLoop::new(max_worker_count=64)\n  { evloop, scheduler: @coroutine.embedded_new_scheduler(), pool: embedded_pool_current(), closed: false, stepping: false }\n}\n\n///|\nfn[T] EmbeddedLoop::activate(self : EmbeddedLoop, f : () -> T raise) -> T raise {\n  if self.closed { raise Failure::Failure(\"Embedded async environment is closed\") }\n  let previous_scheduler = @coroutine.embedded_swap_scheduler(self.scheduler)\n  let previous_loop = curr_loop.val\n  let previous_pool = embedded_pool_swap(self.pool)\n  curr_loop.val = Some(self.evloop)\n  defer {\n    curr_loop.val = previous_loop\n    ignore(@coroutine.embedded_swap_scheduler(previous_scheduler))\n    ignore(embedded_pool_swap(previous_pool))\n  }\n  f()\n}\n\n///|\npub fn EmbeddedLoop::submit(self : EmbeddedLoop, f : async () -> Unit) -> @coroutine.Coroutine raise {\n  self.activate(() => @coroutine.spawn(f))\n}\n\n///|\npub fn EmbeddedLoop::cancel(self : EmbeddedLoop, task : @coroutine.Coroutine) -> Unit raise {\n  self.activate(() => task.cancel())\n}\n\n///|\npub fn EmbeddedLoop::wake(self : EmbeddedLoop, task : @coroutine.Coroutine) -> Unit raise {\n  self.activate(() => task.wake())\n}\n\n///|\npub fn EmbeddedLoop::cancel_all(self : EmbeddedLoop) -> Unit raise {\n  self.activate(() => {\n    for task in @coroutine.all_coroutines().iter().to_array() { task.cancel() }\n  })\n}\n\n///|\n/// One bounded scheduling round. No blocking wait and no nested main loop.\npub fn EmbeddedLoop::step(self : EmbeddedLoop) -> Int raise {\n  if self.stepping { raise Failure::Failure(\"Nested embedded async scheduling\") }\n  self.stepping = true\n  defer { self.stepping = false }\n  self.activate(() => {\n    let n = self.evloop.wait_for_event(timeout=0)\n    self.evloop.handle_timers()\n    for i in 0..<n {\n      let event = self.evloop.bus.get_event(i)\n      self.evloop.extra.handle_event(self.evloop, event)\n    }\n    @coroutine.reschedule()\n    @coroutine.all_coroutines().length()\n  })\n}\n\n///|\n/// -1 means idle. Timer deadlines and immediately runnable tasks are exact;\n/// pending OS I/O is polled at most two milliseconds later by the host.\npub fn EmbeddedLoop::next_wake(self : EmbeddedLoop) -> Int raise {\n  self.activate(() => {\n    if @coroutine.all_coroutines().is_empty() { return -1 }\n    let timeout = self.evloop.get_next_timeout()\n    if self.evloop.blocked_tasks > 0 {\n      if timeout < 0 { 2 } else { @cmp.minimum(timeout, 2) }\n    } else { timeout }\n  })\n}\n\n///|\n/// Call only after cancellation has drained all coroutines and outstanding I/O.\npub fn EmbeddedLoop::close(self : EmbeddedLoop) -> Unit raise {\n  if self.closed { return }\n  self.activate(() => {\n    if !@coroutine.all_coroutines().is_empty() || !self.evloop.running_workers.is_empty() {\n      raise Failure::Failure(\"Cannot close embedded async loop with pending work\")\n    }\n    self.evloop.cleanup()\n    self.evloop.destroy()\n  })\n  self.closed = true\n}\n",
  "async/files/src/napi_host/moon.pkg": "import { \"moonbitlang/async/internal/event_loop\", \"moonbitlang/async/internal/coroutine\" }\n",
  "async/files/src/napi_host/host.mbt": "// Copyright 2026 napi-mbt contributors. Apache-2.0.\n///|\npub struct Loop { priv inner : @event_loop.EmbeddedLoop }\n\n///|\npub struct Task { priv owner : Loop; priv inner : @coroutine.Coroutine }\n\n///|\npub fn Loop::new() -> Loop raise { { inner: @event_loop.EmbeddedLoop::new() } }\n\n///|\npub fn Loop::submit(self : Loop, f : async () -> Unit) -> Task raise {\n  { owner: self, inner: self.inner.submit(f) }\n}\n\n///|\npub fn Task::cancel(self : Task) -> Unit raise { self.owner.inner.cancel(self.inner) }\n\n///|\npub fn Task::wake(self : Task) -> Unit raise { self.owner.inner.wake(self.inner) }\n\n///|\npub fn Loop::current_task(self : Loop) -> Task { { owner: self, inner: @coroutine.current_coroutine() } }\n\n///|\npub async fn suspend() -> Unit { @coroutine.suspend() }\n\n///|\npub async fn protect(f : async () -> Unit) -> Unit nocancel { @coroutine.protect_from_cancel(f) }\n\n///|\npub fn Loop::cancel_all(self : Loop) -> Unit raise { self.inner.cancel_all() }\n\n///|\npub fn Loop::step(self : Loop) -> Int raise { self.inner.step() }\n\n///|\npub fn Loop::next_wake(self : Loop) -> Int raise { self.inner.next_wake() }\n\n///|\npub fn Loop::close(self : Loop) -> Unit raise { self.inner.close() }\n"
}
