import type { RubyJsJsRuntime } from "./bindgen/interfaces/ruby-js-js-runtime.js"; import type { RubyJsRubyRuntime } from "./bindgen/interfaces/ruby-js-ruby-runtime.js"; import { JsAbiValue } from "./bindgen/legacy/rb-js-abi-host.js"; import { Binding, RbAbiValue } from "./binding.js"; /** * A function type that instantiates a Ruby component */ export type RubyComponentInstantiator = ((getCoreModule: (path: string) => WebAssembly.Module, importObject: any, instantiateCore?: (module: WebAssembly.Module, imports: Record) => WebAssembly.Instance | Promise) => Promise<({ rubyRuntime: typeof RubyJsRubyRuntime; })>); export type RubyInitComponentOptions = { /** * A lower-level instantiation function that instantiates the Ruby component with the given component * that implements "ruby:js/js-runtime" WIT interface. */ instantiate: (_: typeof RubyJsJsRuntime) => Promise; /** * The arguments to pass to the Ruby VM. Note that the first argument must be the Ruby program name. * * @default ["ruby.wasm", "-EUTF-8", "-e_=0"] */ args?: string[]; } | { /** * An `instantiate` function generated by `@bytecodealliance/jco` that instantiates the Ruby component. */ instantiate: RubyComponentInstantiator; /** * A function that returns a WebAssembly Core module within the Ruby component transpiled by `@bytecodealliance/jco`. */ getCoreModule: (path: string) => Promise; /** * An optional function used to instantiate a WebAssembly Core module */ instantiateCore?: (module: WebAssembly.Module, imports: Record) => WebAssembly.Instance | Promise; /** * WASI Preview 2 implementation, typically imported from `import * as wasip2 from "@bytecodealliance/preview2-shim"` */ wasip2: any; /** * The arguments to pass to the Ruby VM. Note that the first argument must be the Ruby program name. * * @default ["ruby.wasm", "-EUTF-8", "-e_=0"] */ args?: string[]; }; export type RubyInitModuleOptions = { /** * The WebAssembly module that contains the Ruby VM */ module: WebAssembly.Module; /** * WASI Preview 1 implementation supporting reactor model ABI */ wasip1: { wasiImport: WebAssembly.ModuleImports; initialize(instance: WebAssembly.Instance): void; }; /** * The arguments to pass to the Ruby VM. Note that the first argument must be the Ruby program name. * @default ["ruby.wasm", "-EUTF-8", "-e_=0"] */ args?: string[]; /** * A hook to add additional imports to the WebAssembly instance */ addToImports?: (imports: WebAssembly.Imports) => void; /** * A hook called with the WebAssembly memory instance just after the Ruby VM is instantiated */ setMemory?: (memory: WebAssembly.Memory) => void; }; export type RubyInitOptions = RubyInitComponentOptions | RubyInitModuleOptions; /** * A Ruby VM instance * @see {@link RubyVM.instantiateComponent} and {@link RubyVM.instantiateModule} to create a new instance * @category Essentials */ export declare class RubyVM { /** * @private Only for internal use. */ guest: Binding; private instance; private transport; private exceptionFormatter; private interfaceState; /** * Instantiate a Ruby VM with the given WebAssembly Core module with WASI Preview 1 implementation. * * @param options The options to instantiate the Ruby VM * @returns A promise that resolves to the Ruby VM instance and the WebAssembly instance * @category Essentials * * @example * * import { WASI } from "@bjorn3/browser_wasi_shim"; * const wasip1 = new WASI([], [], []); * const module = await WebAssembly.compile("./path/to/ruby.wasm"); * const { vm } = await RubyVM.instantiateModule({ module, wasip1 }); * */ static instantiateModule(options: RubyInitModuleOptions): Promise<{ vm: RubyVM; instance: WebAssembly.Instance; }>; /** * Instantiate a Ruby VM with the given WebAssembly component with WASI Preview 2 implementation. * * @param options The options to instantiate the Ruby VM * @returns A promise that resolves to the Ruby VM instance * @category Essentials * * @example * * // First, you need to transpile the Ruby component to a JavaScript module using jco. * // $ jco transpile --no-wasi-shim --instantiation --valid-lifting-optimization ./ruby.component.wasm -o ./component * // Then, you can instantiate the Ruby VM with the component: * * import * as wasip2 from "@bytecodealliance/preview2-shim" * import fs from "fs/promises"; * import path from "path"; * * const { instantiate } = await import("./component/ruby.component.js"); * const getCoreModule = async (relativePath) => { * const buffer = await fs.readFile(path.join("./component", relativePath)); * return WebAssembly.compile(buffer); * } * * const { vm } = await RubyVM.instantiateComponent({ * instantiate, getCoreModule, wasip2, * }); * */ static instantiateComponent(options: RubyInitComponentOptions): Promise<{ vm: RubyVM; }>; /** * Create a new Ruby VM instance with low-level initialization control. * * @category Low-level initialization * @example * * ```javascript * const wasi = new WASI(); * const vm = new RubyVM(); * const imports = { * wasi_snapshot_preview1: wasi.wasiImport, * }; * * vm.addToImports(imports); * * const instance = await WebAssembly.instantiate(rubyModule, imports); * await vm.setInstance(instance); * wasi.initialize(instance); * vm.initialize(); * ``` * */ constructor(); /** * @private Only for internal use. */ constructor(binding?: Binding); private static _instantiate; /** * Initialize the Ruby VM with the given command line arguments * @param args The command line arguments to pass to Ruby. Must be * an array of strings starting with the Ruby program name. * @category Low-level initialization */ initialize(args?: string[]): void; /** * Set a given instance to interact JavaScript and Ruby's * WebAssembly instance. This method must be called before calling * Ruby API. * * @param instance The WebAssembly instance to interact with. Must * be instantiated from a Ruby built with JS extension, and built * with Reactor ABI instead of command line. * @category Low-level initialization */ setInstance(instance: WebAssembly.Instance): Promise; /** * Add intrinsic import entries, which is necessary to interact JavaScript * and Ruby's WebAssembly instance. * @param imports The import object to add to the WebAssembly instance * @category Low-level initialization */ addToImports(imports: WebAssembly.Imports): void; private throwProhibitRewindException; private getImports; /** * Print the Ruby version to stdout */ printVersion(): void; /** * Runs a string of Ruby code from JavaScript * @param code The Ruby code to run * @returns the result of the last expression * @category Essentials * * @example * vm.eval("puts 'hello world'"); * const result = vm.eval("1 + 2"); * console.log(result.toString()); // 3 * */ eval(code: string): RbValue; /** * Runs a string of Ruby code with top-level `JS::Object#await` * Returns a promise that resolves when execution completes. * @param code The Ruby code to run * @returns a promise that resolves to the result of the last expression * @category Essentials * * @example * const text = await vm.evalAsync(` * require 'js' * response = JS.global.fetch('https://example.com').await * response.text.await * `); * console.log(text.toString()); // ... */ evalAsync(code: string): Promise; /** * Wrap a JavaScript value into a Ruby JS::Object * @param value The value to convert to RbValue * @returns the RbValue object representing the given JS value * * @example * const hash = vm.eval(`Hash.new`) * hash.call("store", vm.eval(`"key1"`), vm.wrap(new Object())); */ wrap(value: any): RbValue; /** @private */ private privateObject; /** @private */ private rbValueOfPointer; } /** * Export a JS value held by the Ruby VM to the JS environment. * This is implemented in a dirty way since wit cannot reference resources * defined in other interfaces. * In our case, we can't express `function(v: rb-abi-value) -> js-abi-value` * because `rb-js-abi-host.wit`, that defines `js-abi-value`, is implemented * by embedder side (JS) but `rb-abi-guest.wit`, that defines `rb-abi-value` * is implemented by guest side (Wasm). * * This class is a helper to export by: * 1. Call `function __export_to_js(v: rb-abi-value)` defined in guest from embedder side. * 2. Call `function takeJsValue(v: js-abi-value)` defined in embedder from guest side with * underlying JS value of given `rb-abi-value`. * 3. Then `takeJsValue` implementation escapes the given JS value to the `_takenJsValues` * stored in embedder side. * 4. Finally, embedder side can take `_takenJsValues`. * * Note that `exportJsValue` is not reentrant. * * @private */ declare class JsValueTransport { private _takenJsValue; takeJsValue(value: JsAbiValue): void; consumeJsValue(): JsAbiValue; exportJsValue(value: RbValue): JsAbiValue; importJsValue(value: JsAbiValue, vm: RubyVM): RbValue; } /** * A RbValue is an object that represents a value in Ruby * @category Essentials */ export declare class RbValue { private inner; private vm; private privateObject; /** * @hideconstructor */ constructor(inner: RbAbiValue, vm: RubyVM, privateObject: RubyVMPrivate); /** * Call a given method with given arguments * * @param callee name of the Ruby method to call * @param args arguments to pass to the method. Must be an array of RbValue * @returns The result of the method call as a new RbValue. * * @example * const ary = vm.eval("[1, 2, 3]"); * ary.call("push", 4); * console.log(ary.call("sample").toString()); */ call(callee: string, ...args: RbValue[]): RbValue; /** * Call a given method that may call `JS::Object#await` with given arguments * * @param callee name of the Ruby method to call * @param args arguments to pass to the method. Must be an array of RbValue * @returns A Promise that resolves to the result of the method call as a new RbValue. * * @example * const client = vm.eval(` * require 'js' * class HttpClient * def get(url) * JS.global.fetch(url).await * end * end * HttpClient.new * `); * const response = await client.callAsync("get", vm.eval(`"https://example.com"`)); */ callAsync(callee: string, ...args: RbValue[]): Promise; /** * @see {@link https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive} * @param hint Preferred type of the result primitive value. `"number"`, `"string"`, or `"default"`. */ [Symbol.toPrimitive](hint: string): string; /** * Returns a string representation of the value by calling `to_s` */ toString(): string; /** * Returns a JavaScript object representation of the value * by calling `to_js`. * * Returns null if the value is not convertible to a JavaScript object. */ toJS(): any; } type RubyVMPrivate = { transport: JsValueTransport; exceptionFormatter: RbExceptionFormatter; }; declare class RbExceptionFormatter { private literalsCache; private isFormmatting; format(error: RbValue, vm: RubyVM, privateObject: RubyVMPrivate): string; private _format; formatString(klass: string, message: string, backtrace?: [string, string]): string; } /** * Error class thrown by Ruby execution */ export declare class RbError extends Error { /** * @hideconstructor */ constructor(message: string); } /** * Error class thrown by Ruby execution when it is not possible to recover. * This is usually caused when Ruby VM is in an inconsistent state. */ export declare class RbFatalError extends RbError { /** * @hideconstructor */ constructor(message: string); } export {};