alef 0.115.2

Opinionated polyglot binding generator for Rust libraries
Documentation
// DO NOT EDIT - auto-generated by alef
"use strict";

// The native binding attaches the structured properties of a failed call to the thrown error as
// the JSON message of its `cause`: the napi runtime cannot set own properties on an error that an
// async Rust function returns. This module moves those properties onto the error itself, so a
// caller reads `error.kind`, `error.statusCode`, `error.retryAfter` and the rest as declared in
// `index.d.ts`. It wraps every exported function, every static and prototype method of an
// exported class, and the async iterator a streaming call returns.
// Constructors are not wrapped: a constructor that throws keeps the plain native error.

const MARKER = "alef:error-info:";
const SKIP = new Set(["constructor", "length", "name", "prototype", "caller", "arguments"]);
const patched = new WeakSet();
const iterables = new WeakSet();

function decorate(error) {
  if (error === null || typeof error !== "object") return error;
  const cause = error.cause;
  if (cause === null || typeof cause !== "object" || typeof cause.message !== "string") return error;
  if (!cause.message.startsWith(MARKER)) return error;
  let info;
  try {
    info = JSON.parse(cause.message.slice(MARKER.length));
  } catch {
    return error;
  }
  for (const [name, value] of Object.entries(info)) {
    Object.defineProperty(error, name, { value, enumerable: true, writable: true, configurable: true });
  }
  try {
    delete error.cause;
  } catch {
    // The payload stays on the cause when the runtime refuses the delete.
  }
  return error;
}

function rethrow(error) {
  throw decorate(error);
}

function wrapIterator(iterator) {
  if (iterator === null || typeof iterator !== "object") return iterator;
  for (const name of ["next", "return", "throw"]) {
    const original = iterator[name];
    if (typeof original !== "function") continue;
    try {
      Object.defineProperty(iterator, name, {
        configurable: true,
        writable: true,
        value: function (...args) {
          return Promise.resolve(original.apply(this, args)).then(undefined, rethrow);
        },
      });
    } catch {
      // A frozen iterator keeps its plain errors.
    }
  }
  return iterator;
}

function wrapIterable(value) {
  if (value === null || typeof value !== "object") return value;
  const open = value[Symbol.asyncIterator];
  if (typeof open !== "function" || iterables.has(value)) return value;
  iterables.add(value);
  try {
    Object.defineProperty(value, Symbol.asyncIterator, {
      configurable: true,
      writable: true,
      value: function () {
        return wrapIterator(open.call(this));
      },
    });
  } catch {
    return value;
  }
  wrapIterator(value);
  return value;
}

function settle(result) {
  if (result !== null && typeof result === "object" && typeof result.then === "function") {
    return result.then(wrapIterable, rethrow);
  }
  return wrapIterable(result);
}

function wrapFunction(fn) {
  return function (...args) {
    let result;
    try {
      result = fn.apply(this, args);
    } catch (error) {
      throw decorate(error);
    }
    return settle(result);
  };
}

function patchMethods(target) {
  if (target === null || (typeof target !== "object" && typeof target !== "function")) return;
  if (patched.has(target)) return;
  patched.add(target);
  for (const key of Reflect.ownKeys(target)) {
    if (typeof key === "string" && SKIP.has(key)) continue;
    const descriptor = Object.getOwnPropertyDescriptor(target, key);
    if (!descriptor || typeof descriptor.value !== "function" || !descriptor.configurable) continue;
    try {
      Object.defineProperty(target, key, { ...descriptor, value: wrapFunction(descriptor.value) });
    } catch {
      // A method the runtime refuses to redefine keeps its plain errors.
    }
  }
}

function wrapExport(value) {
  if (typeof value !== "function") return value;
  patchMethods(value);
  patchMethods(value.prototype);
  // Only `apply` is trapped, so `new`, `instanceof` and static access reach the native class.
  return new Proxy(value, {
    apply(target, thisArg, args) {
      return wrapFunction(target).apply(thisArg, args);
    },
  });
}

module.exports = function decorateBinding(native) {
  const decorated = {};
  for (const key of Object.keys(native)) {
    decorated[key] = wrapExport(native[key]);
  }
  return decorated;
};
module.exports.decorate = decorate;