From 0e44e25d21ad8045e7ce0015216065ec25651f16 Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Fri, 30 Aug 2024 16:13:02 -0600 Subject: [PATCH] feat: make get_log_level available to plugin, respect log level --- examples/deno.ts | 17 +- examples/node.js | 9 +- src/background-plugin.ts | 296 +++++++++------ src/call-context.ts | 261 +++++++------- src/foreground-plugin.ts | 197 +++++++--- src/interfaces.ts | 163 +++++++-- src/mod.test.ts | 750 ++++++++++++++++++++++++--------------- src/mod.ts | 67 ++-- src/worker.ts | 155 +++++--- wasm/log.wasm | Bin 211444 -> 213879 bytes 10 files changed, 1236 insertions(+), 679 deletions(-) mode change 100644 => 100755 wasm/log.wasm diff --git a/examples/deno.ts b/examples/deno.ts index 3285199..dfda8a8 100755 --- a/examples/deno.ts +++ b/examples/deno.ts @@ -1,19 +1,22 @@ #!/usr/bin/env deno run -A -import createPlugin from '../src/mod.ts'; +import createPlugin from "../src/mod.ts"; -const filename = Deno.args[0] || 'wasm/hello.wasm'; -const funcname = Deno.args[1] || 'run_test'; -const input = Deno.args[2] || 'this is a test'; +const filename = Deno.args[0] || "wasm/hello.wasm"; +const funcname = Deno.args[1] || "run_test"; +const input = Deno.args[2] || "this is a test"; const plugin = await createPlugin(filename, { useWasi: true, + logLevel: 'trace', + logger: console, config: { - thing: 'testing', + thing: "testing", }, }); +console.log("calling", { filename, funcname, input }); const res = await plugin.call(funcname, new TextEncoder().encode(input)); -const s = new TextDecoder().decode(res.buffer); -console.log(s); +// const s = new TextDecoder().decode(res.buffer); +// console.log(s); await plugin.close(); diff --git a/examples/node.js b/examples/node.js index 1782aba..56d99bb 100755 --- a/examples/node.js +++ b/examples/node.js @@ -10,14 +10,17 @@ async function main() { const plugin = await createPlugin(filename, { useWasi: true, + logger: console, config: { thing: 'testing' }, allowedHosts: ['*.typicode.com'], - runInWorker: true + runInWorker: true, + logLevel: 'trace', }); + console.log('calling', { filename, funcname, input }); const res = await plugin.call(funcname, new TextEncoder().encode(input)); - const s = new TextDecoder().decode(res.buffer); - console.log(s); + // const s = new TextDecoder().decode(res.buffer); + // console.log(s); await plugin.close(); } diff --git a/src/background-plugin.ts b/src/background-plugin.ts index 0ad5873..640520a 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -1,20 +1,34 @@ /*eslint-disable no-empty*/ -import { CallContext, RESET, IMPORT_STATE, EXPORT_STATE, STORE, GET_BLOCK, ENV, SET_HOST_CONTEXT } from './call-context.ts'; -import { MemoryOptions, PluginOutput, SAB_BASE_OFFSET, SharedArrayBufferSection, type InternalConfig } from './interfaces.ts'; -import { readBodyUpTo } from './utils.ts'; -import { WORKER_URL } from './worker-url.ts'; -import { Worker } from 'node:worker_threads'; -import { CAPABILITIES } from './polyfills/deno-capabilities.ts'; -import { EXTISM_ENV } from './foreground-plugin.ts'; -import { matches } from './polyfills/deno-minimatch.ts'; +import { + CallContext, + ENV, + EXPORT_STATE, + GET_BLOCK, + IMPORT_STATE, + RESET, + SET_HOST_CONTEXT, + STORE, +} from "./call-context.ts"; +import { + type InternalConfig, + MemoryOptions, + PluginOutput, + SAB_BASE_OFFSET, + SharedArrayBufferSection, +} from "./interfaces.ts"; +import { readBodyUpTo } from "./utils.ts"; +import { WORKER_URL } from "./worker-url.ts"; +import { Worker } from "node:worker_threads"; +import { CAPABILITIES } from "./polyfills/deno-capabilities.ts"; +import { EXTISM_ENV } from "./foreground-plugin.ts"; +import { matches } from "./polyfills/deno-minimatch.ts"; // Firefox has not yet implemented Atomics.waitAsync, but we can polyfill // it using a worker as a one-off. // // TODO: we should probably give _each_ background plugin its own waiter // script. -const AtomicsWaitAsync = - Atomics.waitAsync || +const AtomicsWaitAsync = Atomics.waitAsync || (() => { const src = `onmessage = ev => { const [b, i, v] = ev.data @@ -22,12 +36,12 @@ const AtomicsWaitAsync = postMessage(Atomics.wait(f, i, v)); }`; - const blob = new (Blob as any)([src], { type: 'text/javascript' }); + const blob = new (Blob as any)([src], { type: "text/javascript" }); const url = URL.createObjectURL(blob); const w = new Worker(url); return (ia: any, index, value) => { const promise = new Promise((resolve) => { - w.once('message', (data) => { + w.once("message", (data) => { resolve(data); }); }); @@ -48,7 +62,14 @@ class BackgroundPlugin { #context: CallContext; #request: [(result: any) => void, (result: any) => void] | null = null; - constructor(worker: Worker, sharedData: SharedArrayBuffer, names: string[], modules: WebAssembly.Module[], opts: InternalConfig, context: CallContext) { + constructor( + worker: Worker, + sharedData: SharedArrayBuffer, + names: string[], + modules: WebAssembly.Module[], + opts: InternalConfig, + context: CallContext, + ) { this.sharedData = sharedData; this.sharedDataView = new DataView(sharedData); this.hostFlag = new Int32Array(sharedData); @@ -59,13 +80,13 @@ class BackgroundPlugin { this.#context = context; this.hostFlag[0] = SAB_BASE_OFFSET; - this.worker.on('message', (ev) => this.#handleMessage(ev)); + this.worker.on("message", (ev) => this.#handleMessage(ev)); } async #handleTimeout() { // block new requests from coming in & the current request from settling - const request = this.#request - this.#request = [() => { }, () => { }] + const request = this.#request; + this.#request = [() => {}, () => {}]; const timedOut = {}; const failed = {}; @@ -77,9 +98,9 @@ class BackgroundPlugin { this.opts, this.names, this.modules, - this.sharedData - ) - ]) + this.sharedData, + ), + ]), ].filter(Boolean)).catch(() => failed); this.#context[RESET](); @@ -88,30 +109,30 @@ class BackgroundPlugin { // squatting on `this.#request` so the plugin always looks "active". if (result === timedOut) { this.opts.logger.error( - 'EXTISM: Plugin timed out while handling a timeout. Plugin will hang. This Wasm module may have a non-trivial `start` section.' - ) - this.worker = null as unknown as any + "EXTISM: Plugin timed out while handling a timeout. Plugin will hang. This Wasm module may have a non-trivial `start` section.", + ); + this.worker = null as unknown as any; // TODO: expose some way to observe that the plugin is in a "poisoned" state. - return + return; } // The worker failed to start up for some other reason. This is pretty unlikely to happen! if (result === failed) { this.opts.logger.error( - 'EXTISM: Plugin failed to restart during a timeout. Plugin will hang.' - ) - this.worker = null as unknown as any - return + "EXTISM: Plugin failed to restart during a timeout. Plugin will hang.", + ); + this.worker = null as unknown as any; + return; } const [, worker] = result as any[]; this.worker = worker as Worker; if (request) { - request.pop()!(new Error('EXTISM: call canceled due to timeout')) + request.pop()!(new Error("EXTISM: call canceled due to timeout")); } - this.#request = null + this.#request = null; - this.worker.on('message', (ev) => this.#handleMessage(ev)); + this.worker.on("message", (ev) => this.#handleMessage(ev)); } async reset(): Promise { @@ -119,7 +140,7 @@ class BackgroundPlugin { return false; } - await this.#invoke('reset'); + await this.#invoke("reset"); this.#context[RESET](); return true; @@ -131,19 +152,21 @@ class BackgroundPlugin { async #handleMessage(ev: any) { switch (ev?.type) { - case 'invoke': + case "invoke": return this.#handleInvoke(ev); - case 'return': + case "return": return this.#handleReturn(ev); - case 'log': + case "log": return this.#handleLog(ev); } } #handleLog(ev: any) { const fn = (this.opts.logger as any)[ev.level as string]; - if (typeof fn !== 'function') { - this.opts.logger?.error(`failed to find loglevel="${ev.level}" on logger: message=${ev.message}`); + if (typeof fn !== "function") { + this.opts.logger?.error( + `failed to find loglevel="${ev.level}" on logger: message=${ev.message}`, + ); } else { fn.call(this.opts.logger, ev.message); } @@ -172,7 +195,7 @@ class BackgroundPlugin { // host -> guest() invoke async #invoke(handler: string, ...args: any[]): Promise { if (this.#request) { - throw new Error('plugin is not reentrant'); + throw new Error("plugin is not reentrant"); } let resolve, reject; const promise = new Promise((res, rej) => { @@ -183,7 +206,7 @@ class BackgroundPlugin { this.#request = [resolve as any, reject as any]; if (!this.worker) { - throw new Error('worker not initialized'); + throw new Error("worker not initialized"); } const timedOut = {}; @@ -192,15 +215,15 @@ class BackgroundPlugin { // an empty error handler. Promise.race([ timeout(this.opts.timeoutMs, timedOut), - promise - ].filter(Boolean)).then(async v => { + promise, + ].filter(Boolean)).then(async (v) => { if (v === timedOut) { - await this.#handleTimeout() + await this.#handleTimeout(); } - }, () => { }) + }, () => {}); this.worker.postMessage({ - type: 'invoke', + type: "invoke", handler, args, }); @@ -209,11 +232,15 @@ class BackgroundPlugin { } async functionExists(funcName: string): Promise { - return await this.#invoke('functionExists', funcName); + return await this.#invoke("functionExists", funcName); } // host -> guest invoke() - async call(funcName: string, input?: string | Uint8Array, hostContext?: T): Promise { + async call( + funcName: string, + input?: string | Uint8Array, + hostContext?: T, + ): Promise { const index = this.#context[STORE](input); this.#context[SET_HOST_CONTEXT](hostContext); @@ -233,7 +260,9 @@ class BackgroundPlugin { } const buf = new PluginOutput( - CAPABILITIES.allowSharedBufferCodec ? block.buffer : new Uint8Array(block.buffer).slice().buffer, + CAPABILITIES.allowSharedBufferCodec + ? block.buffer + : new Uint8Array(block.buffer).slice().buffer, ); if (shouldThrow) { @@ -244,9 +273,17 @@ class BackgroundPlugin { return buf; } - async callBlock(funcName: string, input: number | null): Promise<[number | null, number | null]> { + async callBlock( + funcName: string, + input: number | null, + ): Promise<[number | null, number | null]> { const exported = this.#context[EXPORT_STATE](); - const { results, state } = await this.#invoke('call', funcName, input, exported); + const { results, state } = await this.#invoke( + "call", + funcName, + input, + exported, + ); this.#context[IMPORT_STATE](state, true); const [err, data] = results; @@ -258,20 +295,20 @@ class BackgroundPlugin { } async getExports(): Promise { - return await this.#invoke('getExports'); + return await this.#invoke("getExports"); } async getImports(): Promise { - return await this.#invoke('getImports'); + return await this.#invoke("getImports"); } async getInstance(): Promise { - throw new Error('todo'); + throw new Error("todo"); } async close(): Promise { if (this.worker) { - await terminateWorker(this.worker) + await terminateWorker(this.worker); this.worker = null as any; } } @@ -286,10 +323,12 @@ class BackgroundPlugin { // // - https://github.com/nodejs/node/pull/44409 // - https://github.com/denoland/deno/issues/14786 - const timer = setInterval(() => { }, 0); + const timer = setInterval(() => {}, 0); try { if (!func) { - throw Error(`Plugin error: host function "${ev.namespace}" "${ev.func}" does not exist`); + throw Error( + `Plugin error: host function "${ev.namespace}" "${ev.func}" does not exist`, + ); } // Fill the shared array buffer with an expected garbage value to make debugging @@ -329,7 +368,7 @@ class BackgroundPlugin { } } - if (typeof data === 'bigint') { + if (typeof data === "bigint") { promise = writer.writeUint8(SharedArrayBufferSection.RetI64); if (promise) { await promise; @@ -339,7 +378,7 @@ class BackgroundPlugin { if (promise) { await promise; } - } else if (typeof data === 'number') { + } else if (typeof data === "number") { promise = writer.writeUint8(SharedArrayBufferSection.RetF64); if (promise) { await promise; @@ -399,9 +438,14 @@ class RingBufferWriter { do { value = Atomics.load(this.flag, 0); if (value === lastKnownValue) { - const { value: result, async } = AtomicsWaitAsync(this.flag, 0, lastKnownValue, MAX_WAIT); + const { value: result, async } = AtomicsWaitAsync( + this.flag, + 0, + lastKnownValue, + MAX_WAIT, + ); if (async) { - if ((await result) === 'timed-out') { + if ((await result) === "timed-out") { continue; } } @@ -411,7 +455,9 @@ class RingBufferWriter { signal() { const old = Atomics.load(this.flag, 0); - while (Atomics.compareExchange(this.flag, 0, old, this.outputOffset) === old) { } + while ( + Atomics.compareExchange(this.flag, 0, old, this.outputOffset) === old + ) {} Atomics.notify(this.flag, 0, 1); } @@ -430,11 +476,19 @@ class RingBufferWriter { async spanningWrite(input: Uint8Array) { let inputOffset = 0; let toWrite = this.output.byteLength - this.outputOffset; - let flushedWriteCount = 1 + Math.floor((input.byteLength - toWrite) / (this.output.byteLength - SAB_BASE_OFFSET)); - const finalWrite = (input.byteLength - toWrite) % (this.output.byteLength - SAB_BASE_OFFSET); + let flushedWriteCount = 1 + + Math.floor( + (input.byteLength - toWrite) / + (this.output.byteLength - SAB_BASE_OFFSET), + ); + const finalWrite = (input.byteLength - toWrite) % + (this.output.byteLength - SAB_BASE_OFFSET); do { - new Uint8Array(this.output).set(input.subarray(inputOffset, inputOffset + toWrite), this.outputOffset); + new Uint8Array(this.output).set( + input.subarray(inputOffset, inputOffset + toWrite), + this.outputOffset, + ); // increment the offset so we know we've written _something_ (and can bypass the "did we not write anything" check in `flush()`) this.outputOffset += toWrite; @@ -488,7 +542,11 @@ class HttpContext { allowedHosts: string[]; memoryOptions: MemoryOptions; - constructor(_fetch: typeof fetch, allowedHosts: string[], memoryOptions: MemoryOptions) { + constructor( + _fetch: typeof fetch, + allowedHosts: string[], + memoryOptions: MemoryOptions, + ) { this.fetch = _fetch; this.allowedHosts = allowedHosts; this.lastStatusCode = 0; @@ -497,19 +555,26 @@ class HttpContext { contribute(functions: Record>) { functions[EXTISM_ENV] ??= {}; - functions[EXTISM_ENV].http_request = (callContext: CallContext, reqaddr: bigint, bodyaddr: bigint) => - this.makeRequest(callContext, reqaddr, bodyaddr); + functions[EXTISM_ENV].http_request = ( + callContext: CallContext, + reqaddr: bigint, + bodyaddr: bigint, + ) => this.makeRequest(callContext, reqaddr, bodyaddr); functions[EXTISM_ENV].http_status_code = () => this.lastStatusCode; } - async makeRequest(callContext: CallContext, reqaddr: bigint, bodyaddr: bigint) { + async makeRequest( + callContext: CallContext, + reqaddr: bigint, + bodyaddr: bigint, + ) { const req = callContext.read(reqaddr); if (req === null) { return 0n; } const { headers, header, url: rawUrl, method: m } = req.json(); - const method = m ?? 'GET'; + const method = m ?? "GET"; const url = new URL(rawUrl); const isAllowed = this.allowedHosts.some((allowedHost) => { @@ -517,10 +582,14 @@ class HttpContext { }); if (!isAllowed) { - throw new Error(`Call error: HTTP request to "${url}" is not allowed (no allowedHosts match "${url.hostname}")`); + throw new Error( + `Call error: HTTP request to "${url}" is not allowed (no allowedHosts match "${url.hostname}")`, + ); } - const body = bodyaddr === 0n || method === 'GET' || method === 'HEAD' ? null : callContext.read(bodyaddr)?.bytes(); + const body = bodyaddr === 0n || method === "GET" || method === "HEAD" + ? null + : callContext.read(bodyaddr)?.bytes(); const fetch = this.fetch; const response = await fetch(rawUrl, { headers: headers || header, @@ -531,9 +600,9 @@ class HttpContext { this.lastStatusCode = response.status; try { - let bytes = this.memoryOptions.maxHttpResponseBytes ? - await readBodyUpTo(response, this.memoryOptions.maxHttpResponseBytes) : - new Uint8Array(await response.arrayBuffer()); + let bytes = this.memoryOptions.maxHttpResponseBytes + ? await readBodyUpTo(response, this.memoryOptions.maxHttpResponseBytes) + : new Uint8Array(await response.arrayBuffer()); const result = callContext.store(bytes); @@ -554,19 +623,33 @@ export async function createBackgroundPlugin( names: string[], modules: WebAssembly.Module[], ): Promise { - const context = new CallContext(SharedArrayBuffer, opts.logger, opts.config, opts.memory); - const httpContext = new HttpContext(opts.fetch, opts.allowedHosts, opts.memory); + const context = new CallContext( + SharedArrayBuffer, + opts.logger, + opts.logLevel, + opts.config, + opts.memory, + ); + const httpContext = new HttpContext( + opts.fetch, + opts.allowedHosts, + opts.memory, + ); httpContext.contribute(opts.functions); // NB(chrisdickinson): In order for the host and guest to have the same "view" of the // variables, forward the guest's var_get/var_set methods up to the host CallContext. opts.functions[EXTISM_ENV] ??= {}; opts.functions[EXTISM_ENV].var_get = (_: CallContext, key: bigint) => { - return context[ENV].var_get(key) - } - opts.functions[EXTISM_ENV].var_set = (_: CallContext, key: bigint, val: bigint) => { - return context[ENV].var_set(key, val) - } + return context[ENV].var_get(key); + }; + opts.functions[EXTISM_ENV].var_set = ( + _: CallContext, + key: bigint, + val: bigint, + ) => { + return context[ENV].var_set(key, val); + }; // NB(chrisdickinson): We *have* to create the SharedArrayBuffer in // the parent context because -- for whatever reason! -- chromium does @@ -579,19 +662,30 @@ export async function createBackgroundPlugin( // If we fail to initialize the worker (because Wasm hangs), we need access to // the partially-initialized worker so that we can terminate its thread. - let earlyWorker: Worker - const onworker = (w: Worker) => { earlyWorker = w }; + let earlyWorker: Worker; + const onworker = (w: Worker) => { + earlyWorker = w; + }; const worker = await Promise.race([ timeout(opts.timeoutMs, timedOut), - createWorker(opts, names, modules, sharedData, onworker) + createWorker(opts, names, modules, sharedData, onworker), ].filter(Boolean)); if (worker === timedOut) { await terminateWorker(earlyWorker!); - throw new Error('EXTISM: timed out while waiting for plugin to instantiate') + throw new Error( + "EXTISM: timed out while waiting for plugin to instantiate", + ); } - return new BackgroundPlugin(worker as Worker, sharedData, names, modules, opts, context); + return new BackgroundPlugin( + worker as Worker, + sharedData, + names, + modules, + opts, + context, + ); } async function createWorker( @@ -599,29 +693,29 @@ async function createWorker( names: string[], modules: WebAssembly.Module[], sharedData: SharedArrayBuffer, - onworker: (_w: Worker) => void = (_w: Worker) => { }, + onworker: (_w: Worker) => void = (_w: Worker) => {}, ): Promise { const worker = new Worker(WORKER_URL); - onworker(worker) + onworker(worker); await new Promise((resolve, reject) => { - worker.on('message', function handler(ev) { - if (ev?.type !== 'initialized') { + worker.on("message", function handler(ev) { + if (ev?.type !== "initialized") { reject(new Error(`received unexpected message (type=${ev?.type})`)); } - worker.removeListener('message', handler); + worker.removeListener("message", handler); resolve(null); }); }); const onready = new Promise((resolve, reject) => { - worker.on('message', function handler(ev) { - if (ev?.type !== 'ready') { + worker.on("message", function handler(ev) { + if (ev?.type !== "ready") { reject(new Error(`received unexpected message (type=${ev?.type})`)); } - worker.removeListener('message', handler); + worker.removeListener("message", handler); resolve(null); }); }); @@ -629,8 +723,10 @@ async function createWorker( const { fetch: _, logger: __, ...rest } = opts; const message = { ...rest, - type: 'init', - functions: Object.fromEntries(Object.entries(opts.functions || {}).map(([k, v]) => [k, Object.keys(v)])), + type: "init", + functions: Object.fromEntries( + Object.entries(opts.functions || {}).map(([k, v]) => [k, Object.keys(v)]), + ), names, modules, sharedData, @@ -646,16 +742,16 @@ function timeout(ms: number | null, sentinel: any) { return ( ms === null ? null - : new Promise(resolve => setTimeout(() => resolve(sentinel), ms)) - ) + : new Promise((resolve) => setTimeout(() => resolve(sentinel), ms)) + ); } async function terminateWorker(w: Worker) { - if (typeof (globalThis as any).Bun !== 'undefined') { - const timer = setTimeout(() => { }, 10); - await w.terminate() + if (typeof (globalThis as any).Bun !== "undefined") { + const timer = setTimeout(() => {}, 10); + await w.terminate(); clearTimeout(timer); } else { - await w.terminate() + await w.terminate(); } } diff --git a/src/call-context.ts b/src/call-context.ts index cf061e1..5a77034 100644 --- a/src/call-context.ts +++ b/src/call-context.ts @@ -1,15 +1,23 @@ -import { type PluginConfig, PluginOutput, MemoryOptions } from './interfaces.ts'; -import { CAPABILITIES } from './polyfills/deno-capabilities.ts'; - -export const BEGIN = Symbol('begin'); -export const END = Symbol('end'); -export const ENV = Symbol('env'); -export const SET_HOST_CONTEXT = Symbol('set-host-context'); -export const GET_BLOCK = Symbol('get-block'); -export const IMPORT_STATE = Symbol('import-state'); -export const EXPORT_STATE = Symbol('export-state'); -export const STORE = Symbol('store-value'); -export const RESET = Symbol('reset'); +import { + LogLevel, + MemoryOptions, + type PluginConfig, + PluginOutput, + LogLevelPriority, + priorityToLogLevel, + logLevelToPriority, +} from "./interfaces.ts"; +import { CAPABILITIES } from "./polyfills/deno-capabilities.ts"; + +export const BEGIN = Symbol("begin"); +export const END = Symbol("end"); +export const ENV = Symbol("env"); +export const SET_HOST_CONTEXT = Symbol("set-host-context"); +export const GET_BLOCK = Symbol("get-block"); +export const IMPORT_STATE = Symbol("import-state"); +export const EXPORT_STATE = Symbol("export-state"); +export const STORE = Symbol("store-value"); +export const RESET = Symbol("reset"); export class Block { buffer: ArrayBufferLike; @@ -49,6 +57,7 @@ export class CallContext { /** @hidden */ #blocks: (Block | null)[] = []; #logger: Console; + #logLevel: LogLevelPriority; #decoder: TextDecoder; #encoder: TextEncoder; #arrayBufferType: { new(size: number): ArrayBufferLike }; @@ -56,12 +65,19 @@ export class CallContext { #vars: Map = new Map(); #varsSize: number; #memoryOptions: MemoryOptions; - #hostContext: any + #hostContext: any; /** @hidden */ - constructor(type: { new(size: number): ArrayBufferLike }, logger: Console, config: PluginConfig, memoryOptions: MemoryOptions) { + constructor( + type: { new(size: number): ArrayBufferLike }, + logger: Console, + logLevel: LogLevelPriority, + config: PluginConfig, + memoryOptions: MemoryOptions, + ) { this.#arrayBufferType = type; this.#logger = logger; + this.#logLevel = logLevel ?? Infinity; this.#decoder = new TextDecoder(); this.#encoder = new TextEncoder(); this.#memoryOptions = memoryOptions; @@ -76,7 +92,7 @@ export class CallContext { } hostContext(): T { - return this.#hostContext as T + return this.#hostContext as T; } /** @@ -90,11 +106,16 @@ export class CallContext { if (this.#memoryOptions.maxPages) { const pageSize = 64 * 1024; - const totalBytes = this.#blocks.reduce((acc, block) => acc + (block?.buffer.byteLength ?? 0), 0) + const totalBytes = this.#blocks.reduce( + (acc, block) => acc + (block?.buffer.byteLength ?? 0), + 0, + ); const totalPages = Math.ceil(totalBytes / pageSize); if (totalPages > this.#memoryOptions.maxPages) { - this.#logger.error(`memory limit exceeded: ${totalPages} pages requested, ${this.#memoryOptions.maxPages} allowed`); + this.#logger.error( + `memory limit exceeded: ${totalPages} pages requested, ${this.#memoryOptions.maxPages} allowed`, + ); return 0n; } } @@ -118,19 +139,20 @@ export class CallContext { * Set a variable to a given string or byte array value. */ setVariable(name: string, value: string | Uint8Array) { - const buffer = ( - typeof value === 'string' - ? this.#encoder.encode(value) - : value - ) + const buffer = typeof value === "string" + ? this.#encoder.encode(value) + : value; - const variable = this.#vars.get(name) + const variable = this.#vars.get(name); - const newSize = this.#varsSize + buffer.byteLength - (variable?.byteLength || 0) + const newSize = this.#varsSize + buffer.byteLength - + (variable?.byteLength || 0); if (newSize > (this.#memoryOptions?.maxVarBytes || Infinity)) { - throw new Error(`var memory limit exceeded: ${newSize} bytes requested, ${this.#memoryOptions.maxVarBytes} allowed`) + throw new Error( + `var memory limit exceeded: ${newSize} bytes requested, ${this.#memoryOptions.maxVarBytes} allowed`, + ); } - this.#varsSize = newSize + this.#varsSize = newSize; this.#vars.set(name, buffer); } @@ -138,12 +160,12 @@ export class CallContext { * Delete a variable if present. */ deleteVariable(name: string) { - const variable = this.#vars.get(name) + const variable = this.#vars.get(name); if (!variable) { - return + return; } - this.#vars.delete(name) - this.#varsSize -= variable.byteLength + this.#vars.delete(name); + this.#varsSize -= variable.byteLength; } /** @@ -159,10 +181,10 @@ export class CallContext { return null; } - const buffer = - !(block.buffer instanceof ArrayBuffer) && !CAPABILITIES.allowSharedBufferCodec - ? new Uint8Array(block.buffer).slice().buffer - : block.buffer; + const buffer = !(block.buffer instanceof ArrayBuffer) && + !CAPABILITIES.allowSharedBufferCodec + ? new Uint8Array(block.buffer).slice().buffer + : block.buffer; return new PluginOutput(buffer); } @@ -175,7 +197,7 @@ export class CallContext { store(input: string | Uint8Array): bigint { const idx = this[STORE](input); if (!idx) { - throw new Error('failed to store output'); + throw new Error("failed to store output"); } return Block.indexToAddress(idx); } @@ -190,14 +212,24 @@ export class CallContext { } setError(err: string | Error | null = null) { - const blockIdx = err ? this[STORE](err instanceof Error ? err.message : err) : 0 + const blockIdx = err + ? this[STORE](err instanceof Error ? err.message : err) + : 0; if (!blockIdx) { - throw new Error('could not store error value') + throw new Error("could not store error value"); } this.#stack[this.#stack.length - 1][2] = blockIdx; } + get logLevel() { + return priorityToLogLevel(this.#logLevel) + } + + set logLevel(v: LogLevel) { + this.#logLevel = logLevelToPriority(v) + } + /** @hidden */ [ENV] = { alloc: (n: bigint): bigint => { @@ -259,11 +291,14 @@ export class CallContext { const blockIdx = Block.addressToIndex(addr); const block = this.#blocks[blockIdx]; if (!block) { - throw new Error(`cannot assign to this block (addr=${addr.toString(16).padStart(16, '0')}; length=${length})`); + throw new Error( + `cannot assign to this block (addr=${addr.toString(16).padStart(16, "0") + }; length=${length})`, + ); } if (length > block.buffer.byteLength) { - throw new Error('length longer than target block'); + throw new Error("length longer than target block"); } this.#stack[this.#stack.length - 1][1] = blockIdx; @@ -273,18 +308,18 @@ export class CallContext { const blockIdx = Block.addressToIndex(addr); const block = this.#blocks[blockIdx]; if (!block) { - throw new Error('cannot assign error to this block'); + throw new Error("cannot assign error to this block"); } this.#stack[this.#stack.length - 1][2] = blockIdx; }, error_get: (): bigint => { - const error = this.#stack[this.#stack.length - 1][2] + const error = this.#stack[this.#stack.length - 1][2]; if (error) { - return Block.indexToAddress(error) + return Block.indexToAddress(error); } - return 0n + return 0n; }, config_get: (addr: bigint): bigint => { @@ -294,12 +329,13 @@ export class CallContext { return 0n; } - const key = item.string(); - - this[ENV].free(addr); - - if (key in this.#config) { - return this.store(this.#config[key]); + try { + const key = item.string(); + if (key in this.#config) { + return this.store(this.#config[key]); + } + } finally { + this[ENV].free(addr); } return 0n; @@ -312,32 +348,41 @@ export class CallContext { return 0n; } - const key = item.string(); - this[ENV].free(addr); + try { + const key = item.string(); - const result = this.getVariable(key); - const stored = result ? this[STORE](result.bytes()) || 0 : 0; - return Block.indexToAddress(stored) + const result = this.getVariable(key); + const stored = result ? this[STORE](result.bytes()) || 0 : 0; + return Block.indexToAddress(stored); + } finally { + this[ENV].free(addr); + } }, var_set: (addr: bigint, valueaddr: bigint): void => { const item = this.read(addr); if (item === null) { - this.#logger.error(`attempted to set variable using invalid key address (addr="${addr.toString(16)}H")`); + this.#logger.error( + `attempted to set variable using invalid key address (addr="${addr.toString(16) + }H")`, + ); return; } const key = item.string(); if (valueaddr === 0n) { - this.deleteVariable(key) + this.deleteVariable(key); return; } const valueBlock = this.#blocks[Block.addressToIndex(valueaddr)]; if (!valueBlock) { - this.#logger.error(`attempted to set variable to invalid address (key="${key}"; addr="${valueaddr.toString(16)}H")`); + this.#logger.error( + `attempted to set variable to invalid address (key="${key}"; addr="${valueaddr.toString(16) + }H")`, + ); return; } @@ -345,23 +390,23 @@ export class CallContext { // Copy the variable value out of the block for TWO reasons: // 1. Variables outlive blocks -- blocks are reset after each invocation. // 2. If the block is backed by a SharedArrayBuffer, we can't read text out of it directly (in many browser contexts.) - const copied = new Uint8Array(valueBlock.buffer.byteLength) - copied.set(new Uint8Array(valueBlock.buffer), 0) + const copied = new Uint8Array(valueBlock.buffer.byteLength); + copied.set(new Uint8Array(valueBlock.buffer), 0); this.setVariable(key, copied); } catch (err: any) { - this.#logger.error(err.message) - this.setError(err) + this.#logger.error(err.message); + this.setError(err); return; } }, http_request: (_requestOffset: bigint, _bodyOffset: bigint): bigint => { - this.#logger.error('http_request is not enabled'); + this.#logger.error("http_request is not enabled"); return 0n; }, http_status_code: (): number => { - this.#logger.error('http_status_code is not enabled'); + this.#logger.error("http_status_code is not enabled"); return 0; }, @@ -373,62 +418,38 @@ export class CallContext { return this.length(addr); }, - log_warn: (addr: bigint) => { - const blockIdx = Block.addressToIndex(addr); - const block = this.#blocks[blockIdx]; - if (!block) { - this.#logger.error( - `failed to log(warn): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`, - ); - return; - } - const text = this.#decoder.decode(block.buffer); - this.#logger.warn(text); - this[ENV].free(addr); - }, - - log_info: (addr: bigint) => { - const blockIdx = Block.addressToIndex(addr); - const block = this.#blocks[blockIdx]; - if (!block) { - this.#logger.error( - `failed to log(info): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`, - ); - return; - } - const text = this.#decoder.decode(block.buffer); - this.#logger.info(text); - this[ENV].free(addr); - }, + log_warn: this.#handleLog.bind(this, logLevelToPriority('warn'), 'warn'), + log_info: this.#handleLog.bind(this, logLevelToPriority('info'), 'info'), + log_debug: this.#handleLog.bind(this, logLevelToPriority('debug'), 'debug'), + log_error: this.#handleLog.bind(this, logLevelToPriority('error'), 'error'), + log_trace: this.#handleLog.bind(this, logLevelToPriority('trace'), 'trace'), - log_debug: (addr: bigint) => { - const blockIdx = Block.addressToIndex(addr); - const block = this.#blocks[blockIdx]; - if (!block) { - this.#logger.error( - `failed to log(debug): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`, - ); - return; - } - const text = this.#decoder.decode(block.buffer); - this.#logger.debug(text); - this[ENV].free(addr); + get_log_level: (): number => { + return isFinite(this.#logLevel) ? this.#logLevel : 0xffff_ffff; }, + }; - log_error: (addr: bigint) => { - const blockIdx = Block.addressToIndex(addr); - const block = this.#blocks[blockIdx]; - if (!block) { - this.#logger.error( - `failed to log(error): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`, - ); - return; - } + /** @hidden */ + #handleLog(threshold: LogLevelPriority, level: LogLevel, addr: bigint) { + if (this.#logLevel > threshold) { + return; + } + const blockIdx = Block.addressToIndex(addr); + const block = this.#blocks[blockIdx]; + if (!block) { + this.#logger.error( + `failed to log(${level}): bad block reference in addr 0x${addr.toString(16).padStart(64, "0") + }`, + ); + return; + } + try { const text = this.#decoder.decode(block.buffer); - this.#logger.error(text); - this[ENV].free(addr); - }, - }; + (this.#logger[level as keyof Console] as any)(text); + } finally { + this.#blocks[blockIdx] = null; + } + } /** @hidden */ get #input(): Block | null { @@ -464,7 +485,9 @@ export class CallContext { // eslint-disable-next-line prefer-const for (let [buf, idx] of state.blocks) { if (buf && copy) { - const dst = new Uint8Array(new this.#arrayBufferType(Number(buf.byteLength))); + const dst = new Uint8Array( + new this.#arrayBufferType(Number(buf.byteLength)), + ); dst.set(new Uint8Array(buf)); buf = dst.buffer; } @@ -495,7 +518,7 @@ export class CallContext { /** @hidden */ [STORE](input?: string | Uint8Array): number | null { - if (typeof input === 'string') { + if (typeof input === "string") { input = this.#encoder.encode(input); } @@ -526,7 +549,7 @@ export class CallContext { /** @hidden */ [SET_HOST_CONTEXT](hostContext: any) { - this.#hostContext = hostContext + this.#hostContext = hostContext; } /** @hidden */ @@ -536,7 +559,7 @@ export class CallContext { /** @hidden */ [END](): [number | null, number | null] { - this.#hostContext = null + this.#hostContext = null; const [, outputIdx, errorIdx] = this.#stack.pop() as (number | null)[]; const outputPosition = errorIdx === null ? 1 : 0; const idx = errorIdx ?? outputIdx; diff --git a/src/foreground-plugin.ts b/src/foreground-plugin.ts index e29c583..e29de78 100644 --- a/src/foreground-plugin.ts +++ b/src/foreground-plugin.ts @@ -1,8 +1,21 @@ -import { CallContext, RESET, GET_BLOCK, BEGIN, END, ENV, STORE, SET_HOST_CONTEXT } from './call-context.ts'; -import { PluginOutput, type InternalConfig, InternalWasi } from './interfaces.ts'; -import { loadWasi } from './polyfills/deno-wasi.ts'; - -export const EXTISM_ENV = 'extism:host/env'; +import { + BEGIN, + CallContext, + END, + ENV, + GET_BLOCK, + RESET, + SET_HOST_CONTEXT, + STORE, +} from "./call-context.ts"; +import { + type InternalConfig, + InternalWasi, + PluginOutput, +} from "./interfaces.ts"; +import { loadWasi } from "./polyfills/deno-wasi.ts"; + +export const EXTISM_ENV = "extism:host/env"; type InstantiatedModule = [WebAssembly.Module, WebAssembly.Instance]; @@ -13,7 +26,12 @@ export class ForegroundPlugin { #wasi: InternalWasi[]; #opts: InternalConfig; - constructor(opts: InternalConfig, context: CallContext, instancePair: InstantiatedModule, wasi: InternalWasi[]) { + constructor( + opts: InternalConfig, + context: CallContext, + instancePair: InstantiatedModule, + wasi: InternalWasi[], + ) { this.#context = context; this.#instancePair = instancePair; this.#wasi = wasi; @@ -34,18 +52,22 @@ export class ForegroundPlugin { } async functionExists(funcName: string): Promise { - return typeof this.#instancePair[1].exports[funcName] === 'function'; + return typeof this.#instancePair[1].exports[funcName] === "function"; } - async callBlock(funcName: string, input: number | null): Promise<[number | null, number | null]> { + async callBlock( + funcName: string, + input: number | null, + ): Promise<[number | null, number | null]> { this.#active = true; - const func: CallableFunction | undefined = this.#instancePair[1].exports[funcName] as CallableFunction; + const func: CallableFunction | undefined = this.#instancePair[1] + .exports[funcName] as CallableFunction; if (!func) { throw Error(`Plugin error: function "${funcName}" does not exist`); } - if (typeof func !== 'function') { + if (typeof func !== "function") { throw Error(`Plugin error: export "${funcName}" is not a function`); } @@ -61,7 +83,11 @@ export class ForegroundPlugin { } } - async call(funcName: string, input?: string | Uint8Array, hostContext?: T): Promise { + async call( + funcName: string, + input?: string | Uint8Array, + hostContext?: T, + ): Promise { this.#context[RESET](); const inputIdx = this.#context[STORE](input); @@ -109,7 +135,13 @@ export async function createForegroundPlugin( opts: InternalConfig, names: string[], modules: WebAssembly.Module[], - context: CallContext = new CallContext(ArrayBuffer, opts.logger, opts.config, opts.memory), + context: CallContext = new CallContext( + ArrayBuffer, + opts.logger, + opts.logLevel, + opts.config, + opts.memory, + ), ): Promise { const imports: Record> = { [EXTISM_ENV]: context[ENV], @@ -119,21 +151,40 @@ export async function createForegroundPlugin( for (const namespace in opts.functions) { imports[namespace] = imports[namespace] || {}; for (const func in opts.functions[namespace]) { - imports[namespace][func] = opts.functions[namespace][func].bind(null, context); + imports[namespace][func] = opts.functions[namespace][func].bind( + null, + context, + ); } } // find the "main" module and try to instantiate it. - const mainIndex = names.indexOf('main'); + const mainIndex = names.indexOf("main"); if (mainIndex === -1) { - throw new Error('Unreachable: manifests must have at least one "main" module. Enforced by "src/manifest.ts")'); + throw new Error( + 'Unreachable: manifests must have at least one "main" module. Enforced by "src/manifest.ts")', + ); } const seen: Map = new Map(); const wasiList: InternalWasi[] = []; - const instance = await instantiateModule(['main'], modules[mainIndex], imports, opts, wasiList, names, modules, seen); - - return new ForegroundPlugin(opts, context, [modules[mainIndex], instance], wasiList); + const instance = await instantiateModule( + ["main"], + modules[mainIndex], + imports, + opts, + wasiList, + names, + modules, + seen, + ); + + return new ForegroundPlugin( + opts, + context, + [modules[mainIndex], instance], + wasiList, + ); } async function instantiateModule( @@ -148,7 +199,10 @@ async function instantiateModule( ) { linked.set(module, null); - const instantiationImports: Record> = {}; + const instantiationImports: Record< + string, + Record + > = {}; const requested = WebAssembly.Module.imports(module); let wasi = null; @@ -156,9 +210,11 @@ async function instantiateModule( const nameIdx = names.indexOf(module); if (nameIdx === -1) { - if (module === 'wasi_snapshot_preview1' && wasi === null) { + if (module === "wasi_snapshot_preview1" && wasi === null) { if (!opts.wasiEnabled) { - throw new Error('WASI is not enabled; see the "wasiEnabled" plugin option'); + throw new Error( + 'WASI is not enabled; see the "wasiEnabled" plugin option', + ); } if (wasi === null) { @@ -171,31 +227,38 @@ async function instantiateModule( // lookup from "imports" if (!Object.hasOwnProperty.call(imports, module)) { throw new Error( - `from module "${current.join( - '"/"', - )}": cannot resolve import "${module}" "${name}": not provided by host imports nor linked manifest items`, + `from module "${ + current.join( + '"/"', + ) + }": cannot resolve import "${module}" "${name}": not provided by host imports nor linked manifest items`, ); } if (!Object.hasOwnProperty.call(imports[module], name)) { throw new Error( - `from module "${current.join( - '"/"', - )}": cannot resolve import "${module}" "${name}" ("${module}" is a host module, but does not contain "${name}")`, + `from module "${ + current.join( + '"/"', + ) + }": cannot resolve import "${module}" "${name}" ("${module}" is a host module, but does not contain "${name}")`, ); } switch (kind) { case `function`: { instantiationImports[module] ??= {}; - instantiationImports[module][name] = imports[module][name] as CallableFunction; + instantiationImports[module][name] = + imports[module][name] as CallableFunction; break; } default: throw new Error( - `from module "${current.join( - '"/"', - )}": in import "${module}" "${name}", "${kind}"-typed host imports are not supported yet`, + `from module "${ + current.join( + '"/"', + ) + }": in import "${module}" "${name}", "${kind}"-typed host imports are not supported yet`, ); } } else { @@ -209,31 +272,55 @@ async function instantiateModule( if (!target) { throw new Error( - `from module "${current.join('"/"')}": cannot import "${module}" "${name}"; no export matched request`, + `from module "${ + current.join('"/"') + }": cannot import "${module}" "${name}"; no export matched request`, ); } // If the dependency provides "_start", treat it as a WASI Command module; instantiate it (and its subtree) directly. - const instance = providerExports.find((xs) => xs.name === '_start') - ? await instantiateModule([...current, module], provider, imports, opts, wasiList, names, modules, new Map()) + const instance = providerExports.find((xs) => xs.name === "_start") + ? await instantiateModule( + [...current, module], + provider, + imports, + opts, + wasiList, + names, + modules, + new Map(), + ) : !linked.has(provider) - ? (await instantiateModule([...current, module], provider, imports, opts, wasiList, names, modules, linked), - linked.get(provider)) - : linked.get(provider); + ? (await instantiateModule( + [...current, module], + provider, + imports, + opts, + wasiList, + names, + modules, + linked, + ), + linked.get(provider)) + : linked.get(provider); if (!instance) { // circular import, either make a trampoline or bail - if (kind === 'function') { + if (kind === "function") { instantiationImports[module] = {}; let cached: CallableFunction | null = null; - instantiationImports[module][name] = (...args: (number | bigint)[]) => { + instantiationImports[module][name] = ( + ...args: (number | bigint)[] + ) => { if (cached) { return cached(...args); } const instance = linked.get(modules[nameIdx]); if (!instance) { throw new Error( - `from module instance "${current.join('"/"')}": target module "${module}" was never instantiated`, + `from module instance "${ + current.join('"/"') + }": target module "${module}" was never instantiated`, ); } cached = instance.exports[name] as CallableFunction; @@ -241,16 +328,19 @@ async function instantiateModule( }; } else { throw new Error( - `from module "${current.join( - '"/"', - )}": cannot import "${module}" "${name}"; circular imports of type="${kind}" are not supported`, + `from module "${ + current.join( + '"/"', + ) + }": cannot import "${module}" "${name}"; circular imports of type="${kind}" are not supported`, ); } } else { // Add each requested import value piecemeal, since we have to validate that _all_ import requests are satisfied by this // module. instantiationImports[module] ??= {}; - instantiationImports[module][name] = instance.exports[name] as WebAssembly.ExportValue; + instantiationImports[module][name] = instance + .exports[name] as WebAssembly.ExportValue; } } } @@ -258,34 +348,35 @@ async function instantiateModule( const instance = await WebAssembly.instantiate(module, instantiationImports); const guestType = instance.exports.hs_init - ? 'haskell' + ? "haskell" : instance.exports._initialize - ? 'reactor' - : instance.exports._start - ? 'command' - : 'none'; + ? "reactor" + : instance.exports._start + ? "command" + : "none"; if (wasi) { await wasi?.initialize(instance); if (instance.exports.hs_init) { (instance.exports.hs_init as CallableFunction)(); } - } else + } else { switch (guestType) { - case 'command': + case "command": if (instance.exports._initialize) { (instance.exports._initialize as CallableFunction)(); } (instance.exports._start as CallableFunction)(); break; - case 'reactor': + case "reactor": (instance.exports._initialize as CallableFunction)(); break; - case 'haskell': + case "haskell": (instance.exports.hs_init as CallableFunction)(); break; } + } linked.set(module, instance); return instance; diff --git a/src/interfaces.ts b/src/interfaces.ts index ecfa544..a7ecc01 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,4 +1,4 @@ -import { CallContext } from './call-context.ts'; +import { CallContext } from "./call-context.ts"; /** * {@link Plugin} Config @@ -46,43 +46,67 @@ export class PluginOutput extends DataView { } setInt8(_byteOffset: number, _value: number): void { - throw new Error('Cannot set values on output'); + throw new Error("Cannot set values on output"); } setInt16(_byteOffset: number, _value: number, _littleEndian?: boolean): void { - throw new Error('Cannot set values on output'); + throw new Error("Cannot set values on output"); } setInt32(_byteOffset: number, _value: number, _littleEndian?: boolean): void { - throw new Error('Cannot set values on output'); + throw new Error("Cannot set values on output"); } setUint8(_byteOffset: number, _value: number): void { - throw new Error('Cannot set values on output'); + throw new Error("Cannot set values on output"); } - setUint16(_byteOffset: number, _value: number, _littleEndian?: boolean): void { - throw new Error('Cannot set values on output'); + setUint16( + _byteOffset: number, + _value: number, + _littleEndian?: boolean, + ): void { + throw new Error("Cannot set values on output"); } - setUint32(_byteOffset: number, _value: number, _littleEndian?: boolean): void { - throw new Error('Cannot set values on output'); + setUint32( + _byteOffset: number, + _value: number, + _littleEndian?: boolean, + ): void { + throw new Error("Cannot set values on output"); } - setFloat32(_byteOffset: number, _value: number, _littleEndian?: boolean): void { - throw new Error('Cannot set values on output'); + setFloat32( + _byteOffset: number, + _value: number, + _littleEndian?: boolean, + ): void { + throw new Error("Cannot set values on output"); } - setFloat64(_byteOffset: number, _value: number, _littleEndian?: boolean): void { - throw new Error('Cannot set values on output'); + setFloat64( + _byteOffset: number, + _value: number, + _littleEndian?: boolean, + ): void { + throw new Error("Cannot set values on output"); } - setBigInt64(_byteOffset: number, _value: bigint, _littleEndian?: boolean): void { - throw new Error('Cannot set values on output'); + setBigInt64( + _byteOffset: number, + _value: bigint, + _littleEndian?: boolean, + ): void { + throw new Error("Cannot set values on output"); } - setBigUint64(_byteOffset: number, _value: bigint, _littleEndian?: boolean): void { - throw new Error('Cannot set values on output'); + setBigUint64( + _byteOffset: number, + _value: bigint, + _littleEndian?: boolean, + ): void { + throw new Error("Cannot set values on output"); } } @@ -106,7 +130,11 @@ export interface Plugin { * @param {T} hostContext Per-call context to make available to host functions * @returns {Promise} The result from the function call */ - call(funcName: string, input?: string | number | Uint8Array, hostContext?: T): Promise; + call( + funcName: string, + input?: string | number | Uint8Array, + hostContext?: T, + ): Promise; getExports(): Promise; getImports(): Promise; getInstance(): Promise; @@ -145,10 +173,15 @@ export interface ExtismPluginOptions { runInWorker?: boolean; /** - * A logger implementation. Must provide `info`, `debug`, `warn`, and `error` methods. + * A logger implementation. Must provide `trace`, `info`, `debug`, `warn`, and `error` methods. */ logger?: Console; + /** + * The log level to use. + */ + logLevel?: LogLevel; + /** * A map of namespaces to function names to host functions. * @@ -165,7 +198,11 @@ export interface ExtismPluginOptions { * } * ``` */ - functions?: { [key: string]: { [key: string]: (callContext: CallContext, ...args: any[]) => any } } | undefined; + functions?: { + [key: string]: { + [key: string]: (callContext: CallContext, ...args: any[]) => any; + }; + } | undefined; allowedPaths?: { [key: string]: string } | undefined; /** @@ -223,21 +260,24 @@ type SnakeCase> = { [K in keyof T as CamelToSnakeCase]: T[K]; }; - -export interface NativeManifestOptions extends Pick< - ExtismPluginOptions, - "allowedPaths" | "allowedHosts" | "memory" | "config" | "timeoutMs" -> { +export interface NativeManifestOptions extends + Pick< + ExtismPluginOptions, + "allowedPaths" | "allowedHosts" | "memory" | "config" | "timeoutMs" + > { } /** * The subset of {@link ExtismPluginOptions} attributes available for configuration via * a {@link Manifest}. If an attribute is specified at both the {@link ExtismPluginOptions} and * `ManifestOptions` level, the plugin option will take precedence. */ -export type ManifestOptions = NativeManifestOptions & SnakeCase; +export type ManifestOptions = + & NativeManifestOptions + & SnakeCase; export interface InternalConfig extends Required { logger: Console; + logLevel: LogLevelPriority; enableWasiOutput: boolean; functions: { [namespace: string]: { [func: string]: any } }; fetch: typeof fetch; @@ -299,18 +339,19 @@ export interface ManifestWasmModule { * * ⚠️ `module` cannot be used in conjunction with `hash`: the Web Platform does not currently provide a way to get source * bytes from a `WebAssembly.Module` in order to hash. - * */ -export type ManifestWasm = ( - | ManifestWasmUrl - | ManifestWasmData - | ManifestWasmPath - | ManifestWasmResponse - | ManifestWasmModule -) & { - name?: string; - hash?: string; -}; +export type ManifestWasm = + & ( + | ManifestWasmUrl + | ManifestWasmData + | ManifestWasmPath + | ManifestWasmResponse + | ManifestWasmModule + ) + & { + name?: string; + hash?: string; + }; /** * The manifest which describes the {@link Plugin} code and runtime constraints. This is passed to {@link createPlugin} @@ -360,7 +401,13 @@ export interface Manifest extends ManifestOptions { * * @see [Extism](https://extism.org/) > [Concepts](https://extism.org/docs/category/concepts) > [Manifest](https://extism.org/docs/concepts/manifest) */ -export type ManifestLike = Manifest | Response | WebAssembly.Module | ArrayBuffer | string | URL; +export type ManifestLike = + | Manifest + | Response + | WebAssembly.Module + | ArrayBuffer + | string + | URL; export interface Capabilities { /** @@ -469,3 +516,43 @@ export enum SharedArrayBufferSection { RetVoid = 3, Block = 4, } + +export type LogLevel = + | 'trace' + | 'debug' + | 'info' + | 'warn' + | 'error' + | 'silent' + +export function logLevelToPriority(level: LogLevel) { + switch (level) { + case 'trace': return 0; + case 'debug': return 1; + case 'info': return 2; + case 'warn': return 3; + case 'error': return 4; + case 'silent': return Infinity; + default: + throw new TypeError( + `unrecognized log level "${level}"; expected one of "trace", "debug", "info", "warn", "error", "silent"` + ) + } +} + +export type LogLevelPriority = ReturnType; + +export function priorityToLogLevel(level: LogLevelPriority) { + switch (level) { + case 0: return 'trace'; + case 1: return 'debug'; + case 2: return 'info'; + case 3: return 'warn'; + case 4: return 'error'; + case Infinity: return 'silent'; + default: + throw new TypeError( + `unrecognized log level "${level}"; expected one of "trace", "debug", "info", "warn", "error", "silent"` + ) + } +} diff --git a/src/mod.test.ts b/src/mod.test.ts index 76f6b1f..97adef3 100644 --- a/src/mod.test.ts +++ b/src/mod.test.ts @@ -1,9 +1,9 @@ -import { test } from 'node:test'; -import assert from 'node:assert'; -import createPlugin, { CallContext, CAPABILITIES } from './mod.ts'; +import { test } from "node:test"; +import assert from "node:assert"; +import createPlugin, { CallContext, CAPABILITIES } from "./mod.ts"; -if (typeof WebAssembly === 'undefined') { - test('this platform lacks WebAssembly support', async () => { +if (typeof WebAssembly === "undefined") { + test("this platform lacks WebAssembly support", async () => { // at the time of writing (2023 Oct 27), playwright webkit builds for windows // do not support webassembly. there's an open PR (https://github.com/WebKit/WebKit/pull/18184) // to fix this though. @@ -12,34 +12,48 @@ if (typeof WebAssembly === 'undefined') { // The presence of `*.test.ts` files adjacent to module files is no mistake, sadly: // we have to be in the same directory in order to preserve the `__dirname` / `import.meta.url` value // between `mod.ts` and the tests in the build output. - test('createPlugin loads a module and provides lookups', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/code.wasm', { useWasi: true }); + test("createPlugin loads a module and provides lookups", async () => { + const plugin = await createPlugin("http://localhost:8124/wasm/code.wasm", { + useWasi: true, + }); try { - assert(await plugin.functionExists('count_vowels'), 'count_vowels should exist'); - assert(!(await plugin.functionExists('count_sheep')), 'count_sheep should not exist'); + assert( + await plugin.functionExists("count_vowels"), + "count_vowels should exist", + ); + assert( + !(await plugin.functionExists("count_sheep")), + "count_sheep should not exist", + ); } finally { await plugin.close(); } }); - test('createPlugin loads a WebAssembly.Module', async () => { - const response = await fetch('http://localhost:8124/wasm/code.wasm'); + test("createPlugin loads a WebAssembly.Module", async () => { + const response = await fetch("http://localhost:8124/wasm/code.wasm"); const arrayBuffer = await response.arrayBuffer(); const module = await WebAssembly.compile(arrayBuffer); const plugin = await createPlugin(module, { useWasi: true }); try { - assert(await plugin.functionExists('count_vowels'), 'count_vowels should exist'); - assert(!(await plugin.functionExists('count_sheep')), 'count_sheep should not exist'); + assert( + await plugin.functionExists("count_vowels"), + "count_vowels should exist", + ); + assert( + !(await plugin.functionExists("count_sheep")), + "count_sheep should not exist", + ); } finally { await plugin.close(); } }); - test('createPlugin loads a WebAssembly.Module from manifest', async () => { - const response = await fetch('http://localhost:8124/wasm/code.wasm'); + test("createPlugin loads a WebAssembly.Module from manifest", async () => { + const response = await fetch("http://localhost:8124/wasm/code.wasm"); const arrayBuffer = await response.arrayBuffer(); const plugin = await createPlugin( { wasm: [{ module: await WebAssembly.compile(arrayBuffer) }] }, @@ -47,18 +61,29 @@ if (typeof WebAssembly === 'undefined') { ); try { - assert(await plugin.functionExists('count_vowels'), 'count_vowels should exist'); - assert(!(await plugin.functionExists('count_sheep')), 'count_sheep should not exist'); + assert( + await plugin.functionExists("count_vowels"), + "count_vowels should exist", + ); + assert( + !(await plugin.functionExists("count_sheep")), + "count_sheep should not exist", + ); } finally { await plugin.close(); } }); - test('createPlugin fails if provided a module and hash', async () => { - const response = await fetch('http://localhost:8124/wasm/code.wasm'); + test("createPlugin fails if provided a module and hash", async () => { + const response = await fetch("http://localhost:8124/wasm/code.wasm"); const arrayBuffer = await response.arrayBuffer(); const [err, plugin] = await createPlugin( - { wasm: [{ module: await WebAssembly.compile(arrayBuffer), hash: 'anything' }] }, + { + wasm: [{ + module: await WebAssembly.compile(arrayBuffer), + hash: "anything", + }], + }, { useWasi: true }, ).then( (plugin) => [null, plugin], @@ -69,46 +94,60 @@ if (typeof WebAssembly === 'undefined') { await plugin.close(); } assert.equal(plugin, null); - assert.equal(err.message, 'Item specified a hash but WebAssembly.Module source data is unavailable for hashing'); + assert.equal( + err.message, + "Item specified a hash but WebAssembly.Module source data is unavailable for hashing", + ); }); - test('createPlugin loads a fetch Response', async () => { - const plugin = await createPlugin(fetch('http://localhost:8124/wasm/code.wasm'), { useWasi: true }); + test("createPlugin loads a fetch Response", async () => { + const plugin = await createPlugin( + fetch("http://localhost:8124/wasm/code.wasm"), + { useWasi: true }, + ); try { - assert(await plugin.functionExists('count_vowels'), 'count_vowels should exist'); - assert(!(await plugin.functionExists('count_sheep')), 'count_sheep should not exist'); + assert( + await plugin.functionExists("count_vowels"), + "count_vowels should exist", + ); + assert( + !(await plugin.functionExists("count_sheep")), + "count_sheep should not exist", + ); } finally { await plugin.close(); } }); if (!CAPABILITIES.crossOriginChecksEnforced) { - test('can create plugin from url with hash check', async () => { + test("can create plugin from url with hash check", async () => { const plugin = await createPlugin({ wasm: [ { - url: 'https://github.com/extism/plugins/releases/download/v0.5.0/count_vowels.wasm', - hash: '93898457953d30d016f712ccf4336ce7e9971db5f7f3aff1edd252764f75d5d7', + url: + "https://github.com/extism/plugins/releases/download/v0.5.0/count_vowels.wasm", + hash: + "93898457953d30d016f712ccf4336ce7e9971db5f7f3aff1edd252764f75d5d7", }, ], }); try { - assert.equal(await plugin.functionExists('count_vowels'), true); + assert.equal(await plugin.functionExists("count_vowels"), true); } finally { await plugin.close(); } }); } - test('createPlugin fails on hash mismatch (bad hash)', async () => { + test("createPlugin fails on hash mismatch (bad hash)", async () => { const [err, plugin] = await createPlugin( { wasm: [ { - url: 'http://localhost:8124/wasm/code.wasm', - hash: 'not a good hash', + url: "http://localhost:8124/wasm/code.wasm", + hash: "not a good hash", }, ], }, @@ -125,13 +164,14 @@ if (typeof WebAssembly === 'undefined') { } }); - test('createPlugin fails on hash mismatch (hash mismatch)', async () => { + test("createPlugin fails on hash mismatch (hash mismatch)", async () => { const [err, plugin] = await createPlugin( { wasm: [ { - url: 'http://localhost:8124/wasm/code.wasm', - hash: '93898457953d30d016f712ccf4336ce7e9971db5f7f3aff1edd252764f75d5d7', + url: "http://localhost:8124/wasm/code.wasm", + hash: + "93898457953d30d016f712ccf4336ce7e9971db5f7f3aff1edd252764f75d5d7", }, ], }, @@ -148,34 +188,36 @@ if (typeof WebAssembly === 'undefined') { } }); - test('createPlugin loads a module and provides access to exports/imports', async () => { - const plugin = await createPlugin({ wasm: [{ url: 'http://localhost:8124/wasm/code.wasm' }] }, { useWasi: true }); + test("createPlugin loads a module and provides access to exports/imports", async () => { + const plugin = await createPlugin({ + wasm: [{ url: "http://localhost:8124/wasm/code.wasm" }], + }, { useWasi: true }); try { const exports = await plugin.getExports(); assert.deepEqual( exports.map((xs) => xs.name).sort(), - ['memory', 'count_vowels', '__data_end', '__heap_base'].sort(), + ["memory", "count_vowels", "__data_end", "__heap_base"].sort(), ); const imports = await plugin.getImports(); assert.deepEqual( imports.map((xs) => xs.name).sort(), [ - 'alloc', - 'config_get', - 'error_set', - 'input_length', - 'input_load_u64', - 'input_load_u8', - 'length', - 'load_u64', - 'load_u8', - 'output_set', - 'store_u64', - 'store_u8', - 'var_get', - 'var_set', + "alloc", + "config_get", + "error_set", + "input_length", + "input_load_u64", + "input_load_u8", + "length", + "load_u64", + "load_u8", + "output_set", + "store_u64", + "store_u8", + "var_get", + "var_set", ].sort(), ); } finally { @@ -183,98 +225,104 @@ if (typeof WebAssembly === 'undefined') { } }); - test('createPlugin returns an interface that can call wasm functions', async () => { - const plugin = await createPlugin({ wasm: [{ url: 'http://localhost:8124/wasm/code.wasm' }] }, { useWasi: true }); + test("createPlugin returns an interface that can call wasm functions", async () => { + const plugin = await createPlugin({ + wasm: [{ url: "http://localhost:8124/wasm/code.wasm" }], + }, { useWasi: true }); try { - const result = await plugin.call('count_vowels', 'hello world'); - assert(result, 'result is not null'); + const result = await plugin.call("count_vowels", "hello world"); + assert(result, "result is not null"); assert.deepEqual(JSON.parse(new TextDecoder().decode(result.buffer)), { count: 3, total: 3, - vowels: 'aeiouAEIOU', + vowels: "aeiouAEIOU", }); } finally { await plugin.close(); } }); - test('logging works as expected', async () => { + test("logging works as expected", async () => { const intercept: Record = {}; - const logLevel = (level: string) => (message: string) => (intercept[level] = message); + const logLevel = + (level: string) => (message: string) => (intercept[level] = message); // FIXME: we're using non-blocking log functions here; to properly preserve behavior we // should invoke these and wait on the host to return. const logger = Object.fromEntries( - ['info', 'debug', 'warn', 'error'].map((lvl) => [lvl, logLevel(lvl)]), + ["info", "debug", "warn", "error", "trace"].map(( + lvl, + ) => [lvl, logLevel(lvl)]), ) as unknown as Console; const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/log.wasm' }] }, - { useWasi: true, logger }, + { wasm: [{ url: "http://localhost:8124/wasm/log.wasm" }] }, + { useWasi: true, logger, logLevel: 'trace' }, ); try { - await plugin.call('run_test', ''); + await plugin.call("run_test", ""); assert.deepEqual(intercept, { - debug: 'this is a debug log', - error: 'this is an erorr log', - info: 'this is an info log', - warn: 'this is a warning log', + trace: "this is a trace log", + debug: "this is a debug log", + error: "this is an error log", + info: "this is an info log", + warn: "this is a warning log", }); } finally { await plugin.close(); } }); - test('host functions may read info from context and return values', async () => { + test("host functions may read info from context and return values", async () => { let executed: any; const functions = { - 'extism:host/user': { + "extism:host/user": { hello_world(context: CallContext, off: bigint) { executed = context.read(off)?.string(); - return context.store('wow okay then'); + return context.store("wow okay then"); }, }, }; const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { wasm: [{ url: "http://localhost:8124/wasm/code-functions.wasm" }] }, { useWasi: true, functions }, ); try { - const output = await plugin.call('count_vowels', 'hello world'); - assert.equal(output?.string(), 'wow okay then'); + const output = await plugin.call("count_vowels", "hello world"); + assert.equal(output?.string(), "wow okay then"); assert.equal(executed, '{"count": 3}'); } finally { await plugin.close(); } }); - test('resetting the plugin unsets all existing pages', async () => { + test("resetting the plugin unsets all existing pages", async () => { const offsets: bigint[] = [0n, 0n]; let callContext: CallContext | null = null; const functions = { - 'extism:host/user': { + "extism:host/user": { hello_world(context: CallContext, off: bigint) { callContext = context; offsets[0] = off; - offsets[1] = context.store('wow okay then'); + offsets[1] = context.store("wow okay then"); return offsets[1]; }, }, }; const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { wasm: [{ url: "http://localhost:8124/wasm/code-functions.wasm" }] }, { useWasi: true, functions }, ); try { - const output = await plugin.call('count_vowels', 'hello world'); - assert.equal(output?.string(), 'wow okay then'); + const output = await plugin.call("count_vowels", "hello world"); + assert.equal(output?.string(), "wow okay then"); await plugin.reset(); @@ -288,22 +336,22 @@ if (typeof WebAssembly === 'undefined') { } }); - test('host functions reject original promise when throwing', async () => { + test("host functions reject original promise when throwing", async () => { const expected = String(Math.random()); const functions = { - 'extism:host/user': { + "extism:host/user": { hello_world(_context: CallContext, _off: bigint) { throw new Error(expected); }, }, }; const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { wasm: [{ url: "http://localhost:8124/wasm/code-functions.wasm" }] }, { useWasi: true, functions }, ); try { - const [err, data] = await plugin.call('count_vowels', 'hello world').then( + const [err, data] = await plugin.call("count_vowels", "hello world").then( (data) => [null, data], (err) => [err, null], ); @@ -315,93 +363,117 @@ if (typeof WebAssembly === 'undefined') { } }); - test('plugin can get/set variables', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/var.wasm', { useWasi: true }); + test("plugin can get/set variables", async () => { + const plugin = await createPlugin("http://localhost:8124/wasm/var.wasm", { + useWasi: true, + }); try { - const [err, data] = await plugin.call('run_test').then( + const [err, data] = await plugin.call("run_test").then( (data) => [null, data], (err) => [err, null], ); assert.equal(err, null); - assert.equal(data.string(), 'a: 0'); + assert.equal(data.string(), "a: 0"); } finally { await plugin.close(); } }); - test('plugins cannot allocate more var bytes than allowed', async () => { + test("plugins cannot allocate more var bytes than allowed", async () => { const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/memory.wasm' }], memory: { maxVarBytes: 100 } }, - { useWasi: true }); + { + wasm: [{ url: "http://localhost:8124/wasm/memory.wasm" }], + memory: { maxVarBytes: 100 }, + }, + { useWasi: true }, + ); try { - const [err, _] = await plugin.call('alloc_var', JSON.stringify({ bytes: 1024 })).then( + const [err, _] = await plugin.call( + "alloc_var", + JSON.stringify({ bytes: 1024 }), + ).then( (data) => [null, data], (err) => [err, null], ); - assert(err) - assert(/var memory limit exceeded: 1024 bytes requested, 100 allowed/.test(err.message)); + assert(err); + assert( + /var memory limit exceeded: 1024 bytes requested, 100 allowed/.test( + err.message, + ), + ); } finally { await plugin.close(); } }); - test('plugins can allocate var bytes if allowed', async () => { + test("plugins can allocate var bytes if allowed", async () => { const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/memory.wasm' }], memory: { maxVarBytes: 1024 } }, - { useWasi: true }); + { + wasm: [{ url: "http://localhost:8124/wasm/memory.wasm" }], + memory: { maxVarBytes: 1024 }, + }, + { useWasi: true }, + ); try { - const [err, _] = await plugin.call('alloc_var', JSON.stringify({ bytes: 1024 })).then( + const [err, _] = await plugin.call( + "alloc_var", + JSON.stringify({ bytes: 1024 }), + ).then( (data) => [null, data], (err) => [err, null], ); - assert(err === null) + assert(err === null); } finally { await plugin.close(); } }); - test('plugins can link', async () => { + test("plugins can link", async () => { const plugin = await createPlugin({ wasm: [ - { name: 'main', url: 'http://localhost:8124/wasm/reflect.wasm' }, - { name: 'extism:host/user', url: 'http://localhost:8124/wasm/upper.wasm' }, + { name: "main", url: "http://localhost:8124/wasm/reflect.wasm" }, + { + name: "extism:host/user", + url: "http://localhost:8124/wasm/upper.wasm", + }, ], }); try { - const [err, data] = await plugin.call('reflect', 'Hello, world!').then( + const [err, data] = await plugin.call("reflect", "Hello, world!").then( (data) => [null, data], (err) => [err, null], ); assert.equal(err, null); - assert.equal(data.string(), 'HELLO, WORLD!'); + assert.equal(data.string(), "HELLO, WORLD!"); } finally { await plugin.close(); } }); - test('plugin linking: circular func deps are supported', async () => { + test("plugin linking: circular func deps are supported", async () => { const plugin = await createPlugin({ wasm: [ // these deps also share a memory - { name: 'lhs', url: 'http://localhost:8124/wasm/circular-lhs.wasm' }, - { name: 'rhs', url: 'http://localhost:8124/wasm/circular-rhs.wasm' }, - { name: 'main', url: 'http://localhost:8124/wasm/circular.wasm' }, + { name: "lhs", url: "http://localhost:8124/wasm/circular-lhs.wasm" }, + { name: "rhs", url: "http://localhost:8124/wasm/circular-rhs.wasm" }, + { name: "main", url: "http://localhost:8124/wasm/circular.wasm" }, ], }); try { // this plugin starts with 1, multiplies by two, adds one, ... recursively, until it's greater than 100. - const [err, data] = await plugin.call('encalculate', 'Hello, world!').then( - (data) => [null, data], - (err) => [err, null], - ); + const [err, data] = await plugin.call("encalculate", "Hello, world!") + .then( + (data) => [null, data], + (err) => [err, null], + ); assert.equal(err, null); assert.equal(data.getBigUint64(0, true), 127); @@ -410,11 +482,11 @@ if (typeof WebAssembly === 'undefined') { } }); - test('plugin linking: missing deps are messaged', async () => { + test("plugin linking: missing deps are messaged", async () => { const [err, plugin] = await createPlugin({ wasm: [ - { name: 'lhs', url: 'http://localhost:8124/wasm/circular-lhs.wasm' }, - { name: 'main', url: 'http://localhost:8124/wasm/circular.wasm' }, + { name: "lhs", url: "http://localhost:8124/wasm/circular-lhs.wasm" }, + { name: "main", url: "http://localhost:8124/wasm/circular.wasm" }, ], }).then( (data) => [null, data], @@ -432,171 +504,184 @@ if (typeof WebAssembly === 'undefined') { } }); - test('input data respects byte offsets and lengths', async () => { + test("input data respects byte offsets and lengths", async () => { const plugin = await createPlugin({ wasm: [ - { name: 'main', url: 'http://localhost:8124/wasm/reflect.wasm' }, - { name: 'extism:host/user', url: 'http://localhost:8124/wasm/upper.wasm' }, + { name: "main", url: "http://localhost:8124/wasm/reflect.wasm" }, + { + name: "extism:host/user", + url: "http://localhost:8124/wasm/upper.wasm", + }, ], }); - const arrayBuffer = new ArrayBuffer(8192) - const view = new Uint8Array(arrayBuffer, 10, "Hello world!".length) + const arrayBuffer = new ArrayBuffer(8192); + const view = new Uint8Array(arrayBuffer, 10, "Hello world!".length); new TextEncoder().encodeInto("Hello world!", view); try { - const [err, data] = await plugin.call('reflect', view).then( + const [err, data] = await plugin.call("reflect", view).then( (data) => [null, data], (err) => [err, null], ); assert.equal(err, null); - assert.equal(data.string(), 'HELLO WORLD!'); + assert.equal(data.string(), "HELLO WORLD!"); } finally { await plugin.close(); } }); if (CAPABILITIES.hasWorkerCapability) { - test('host functions may be async if worker is off-main-thread', async () => { + test("host functions may be async if worker is off-main-thread", async () => { const functions = { - 'extism:host/user': { + "extism:host/user": { async hello_world(context: CallContext, _off: bigint) { await new Promise((resolve) => setTimeout(resolve, 100)); - return context.store('it works'); + return context.store("it works"); }, }, }; const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { wasm: [{ url: "http://localhost:8124/wasm/code-functions.wasm" }] }, { useWasi: true, functions, runInWorker: true }, ); try { - const output = await plugin.call('count_vowels', 'hello world'); - assert.equal(output?.string(), 'it works'); + const output = await plugin.call("count_vowels", "hello world"); + assert.equal(output?.string(), "it works"); } finally { await plugin.close(); } }); - test('plugin callcontext reflects vars set in plugin', async () => { - let seen: string = 'nope nope' - let key: string = 'nope nope' - const plugin = await createPlugin('http://localhost:8124/wasm/02-var-reflected.wasm', { - useWasi: true, - runInWorker: true, - functions: { - user: { - async test(callContext, n) { - key = callContext.read(n)!.text()! - seen = callContext.getVariable(callContext.read(n)!.text())!.text() - } - } - } - }); + test("plugin callcontext reflects vars set in plugin", async () => { + let seen: string = "nope nope"; + let key: string = "nope nope"; + const plugin = await createPlugin( + "http://localhost:8124/wasm/02-var-reflected.wasm", + { + useWasi: true, + runInWorker: true, + functions: { + user: { + async test(callContext, n) { + key = callContext.read(n)!.text()!; + seen = callContext.getVariable(callContext.read(n)!.text())! + .text(); + }, + }, + }, + }, + ); try { // This plugin has a value in memory, "hi there". It writes that variable into // extism memory, then stores that as an extism var -- mapping "hi there" => "hi there". // (This is just out of expedience so we don't have to store another value!) We then // call the host function with the result of 'var_get "hi there"'; so we're testing // that the guest and host have the _same_ view of variables. - const [err, _] = await plugin.call('test').then( + const [err, _] = await plugin.call("test").then( (data) => [null, data], (err) => [err, null], ); - assert(!err) - assert.equal(key, 'hi there') - assert.equal(seen, 'hi there') + assert(!err); + assert.equal(key, "hi there"); + assert.equal(seen, "hi there"); } finally { await plugin.close(); } }); if (CAPABILITIES.supportsTimeouts) { - test('timeout works on call()', async () => { + test("timeout works on call()", async () => { const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/loop-forever.wasm' }] }, + { wasm: [{ url: "http://localhost:8124/wasm/loop-forever.wasm" }] }, { useWasi: true, timeoutMs: 250, runInWorker: true }, ); - try { - const [err, output] = await plugin.call('loop', 'hello world').then( - res => [, res], - err => [err,] + const [err, output] = await plugin.call("loop", "hello world").then( + (res) => [, res], + (err) => [err], ); if (output) { - assert.fail("Expected no output") + assert.fail("Expected no output"); } assert.equal( err!.message, `EXTISM: call canceled due to timeout`, - ) + ); } finally { await plugin.close(); } }); - test('timeout applies to initialization', async () => { + test("timeout applies to initialization", async () => { const [err, plugin] = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/loop-forever-init.wasm' }] }, + { + wasm: [{ + url: "http://localhost:8124/wasm/loop-forever-init.wasm", + }], + }, { useWasi: true, timeoutMs: 250, runInWorker: true }, ).then( - res => [, res], - err => [err,] + (res) => [, res], + (err) => [err], ); if (plugin) { await plugin.close(); - assert.fail("Expected no output") + assert.fail("Expected no output"); } assert.equal( err!.message, `EXTISM: timed out while waiting for plugin to instantiate`, - ) + ); }); } - test('host functions preserve call context', async () => { - const one = { hi: 'there' } - let seen: typeof one | null = null + test("host functions preserve call context", async () => { + const one = { hi: "there" }; + let seen: typeof one | null = null; const functions = { - 'extism:host/user': { + "extism:host/user": { async hello_world(context: CallContext, _off: bigint) { - seen = context.hostContext<{ hi: string }>() + seen = context.hostContext<{ hi: string }>(); await new Promise((resolve) => setTimeout(resolve, 100)); - return context.store('it works'); + return context.store("it works"); }, }, }; const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { wasm: [{ url: "http://localhost:8124/wasm/code-functions.wasm" }] }, { useWasi: true, functions, runInWorker: true }, ); try { - const output = await plugin.call('count_vowels', 'hello world', one); - assert.equal(output?.string(), 'it works'); - assert.strictEqual(seen, one, 'we preserved the host context') + const output = await plugin.call("count_vowels", "hello world", one); + assert.equal(output?.string(), "it works"); + assert.strictEqual(seen, one, "we preserved the host context"); } finally { await plugin.close(); } }); - test('test writes that span multiple blocks (w/small buffer)', async () => { - const value = '9:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ'.repeat(18428 / 34); + test("test writes that span multiple blocks (w/small buffer)", async () => { + const value = "9:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ".repeat(18428 / 34); const functions = { - 'extism:host/user': { + "extism:host/user": { async hello_world(context: CallContext, _off: bigint) { - context.setVariable('hmmm okay storing a variable', 'hello world hello.'); + context.setVariable( + "hmmm okay storing a variable", + "hello world hello.", + ); const result = new TextEncoder().encode(value); const ret = context.store(result); return ret; @@ -605,71 +690,90 @@ if (typeof WebAssembly === 'undefined') { }; const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, - { useWasi: true, functions, runInWorker: true, sharedArrayBufferSize: 1 << 6 }, + { wasm: [{ url: "http://localhost:8124/wasm/code-functions.wasm" }] }, + { + useWasi: true, + functions, + runInWorker: true, + sharedArrayBufferSize: 1 << 6, + }, ); let i = 0; try { for (; i < 10; ++i) { - const output = await plugin.call('count_vowels', 'hello world'); + const output = await plugin.call("count_vowels", "hello world"); assert.equal(output?.string(), value); } - const again = await plugin.call('count_vowels', 'hello world'); + const again = await plugin.call("count_vowels", "hello world"); assert.equal(again?.string(), value); } finally { await plugin.close(); } }); - test('host functions may not be reentrant off-main-thread', async () => { + test("host functions may not be reentrant off-main-thread", async () => { const functions = { - 'extism:host/user': { + "extism:host/user": { async hello_world(context: CallContext, _off: bigint) { - await plugin?.call('count_vowels', 'hello world'); - return context.store('it works'); + await plugin?.call("count_vowels", "hello world"); + return context.store("it works"); }, }, }; const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { wasm: [{ url: "http://localhost:8124/wasm/code-functions.wasm" }] }, { useWasi: true, functions, runInWorker: true }, ); try { - const [err, data] = await plugin.call('count_vowels', 'hello world').then( - (data) => [null, data], - (err) => [err, null], - ); + const [err, data] = await plugin.call("count_vowels", "hello world") + .then( + (data) => [null, data], + (err) => [err, null], + ); assert(data === null); - assert.equal(err?.message, 'plugin is not reentrant'); + assert.equal(err?.message, "plugin is not reentrant"); } finally { await plugin.close(); } }); - if (!CAPABILITIES.crossOriginChecksEnforced) - test('http fails as expected when no allowed hosts match', async () => { + if (!CAPABILITIES.crossOriginChecksEnforced) { + test("http fails as expected when no allowed hosts match", async () => { const functions = { - 'extism:host/user': { + "extism:host/user": { async hello_world(context: CallContext, _off: bigint) { await new Promise((resolve) => setTimeout(resolve, 100)); - return context.store('it works'); + return context.store("it works"); }, }, }; const plugin = await createPlugin( - { wasm: [{ name: 'main', url: 'http://localhost:8124/wasm/http.wasm' }] }, - { useWasi: true, functions, runInWorker: true, allowedHosts: ['*.example.com'] }, + { + wasm: [{ + name: "main", + url: "http://localhost:8124/wasm/http.wasm", + }], + }, + { + useWasi: true, + functions, + runInWorker: true, + allowedHosts: ["*.example.com"], + }, ); try { const [err, data] = await plugin - .call('http_get', '{"url": "https://jsonplaceholder.typicode.com/todos/1"}') + .call( + "http_get", + '{"url": "https://jsonplaceholder.typicode.com/todos/1"}', + ) .then( (data) => [null, data], (err) => [err, null], @@ -684,16 +788,24 @@ if (typeof WebAssembly === 'undefined') { await plugin.close(); } }); + } - test('http works as expected when host is allowed', async () => { + test("http works as expected when host is allowed", async () => { const plugin = await createPlugin( - { wasm: [{ name: 'main', url: 'http://localhost:8124/wasm/http.wasm' }], allowedHosts: ['*.typicode.com'], memory: { maxHttpResponseBytes: 100 * 1024 * 1024 } }, + { + wasm: [{ name: "main", url: "http://localhost:8124/wasm/http.wasm" }], + allowedHosts: ["*.typicode.com"], + memory: { maxHttpResponseBytes: 100 * 1024 * 1024 }, + }, { useWasi: true, functions: {}, runInWorker: true }, ); try { const [err, data] = await plugin - .call('http_get', '{"url": "https://jsonplaceholder.typicode.com/todos/1"}') + .call( + "http_get", + '{"url": "https://jsonplaceholder.typicode.com/todos/1"}', + ) .then( (data) => [null, data], (err) => [err, null], @@ -702,7 +814,7 @@ if (typeof WebAssembly === 'undefined') { assert.deepEqual(data.json(), { userId: 1, id: 1, - title: 'delectus aut autem', + title: "delectus aut autem", completed: false, }); } finally { @@ -710,35 +822,48 @@ if (typeof WebAssembly === 'undefined') { } }); - test('http fails when body is larger than allowed', async () => { + test("http fails when body is larger than allowed", async () => { const plugin = await createPlugin( - { wasm: [{ name: 'main', url: 'http://localhost:8124/wasm/http.wasm' }], allowedHosts: ['*.typicode.com'], memory: { maxHttpResponseBytes: 1 } }, + { + wasm: [{ name: "main", url: "http://localhost:8124/wasm/http.wasm" }], + allowedHosts: ["*.typicode.com"], + memory: { maxHttpResponseBytes: 1 }, + }, { useWasi: true, functions: {}, runInWorker: true }, ); try { const [err, _] = await plugin - .call('http_get', '{"url": "https://jsonplaceholder.typicode.com/todos/1"}') + .call( + "http_get", + '{"url": "https://jsonplaceholder.typicode.com/todos/1"}', + ) .then( (data) => [null, data], (err) => [err, null], ); - assert(err) + assert(err); } finally { await plugin.close(); } }); - test('we fallback to Manifest.allowedHosts if ExtismPluginOptions.allowedHosts is not specified', async () => { + test("we fallback to Manifest.allowedHosts if ExtismPluginOptions.allowedHosts is not specified", async () => { const plugin = await createPlugin( - { wasm: [{ name: 'main', url: 'http://localhost:8124/wasm/http.wasm' }], allowedHosts: ['*.typicode.com'] }, + { + wasm: [{ name: "main", url: "http://localhost:8124/wasm/http.wasm" }], + allowedHosts: ["*.typicode.com"], + }, { useWasi: true, functions: {}, runInWorker: true }, ); try { const [err, data] = await plugin - .call('http_get', '{"url": "https://jsonplaceholder.typicode.com/todos/1"}') + .call( + "http_get", + '{"url": "https://jsonplaceholder.typicode.com/todos/1"}', + ) .then( (data) => [null, data], (err) => [err, null], @@ -747,7 +872,7 @@ if (typeof WebAssembly === 'undefined') { assert.deepEqual(data.json(), { userId: 1, id: 1, - title: 'delectus aut autem', + title: "delectus aut autem", completed: false, }); } finally { @@ -756,128 +881,156 @@ if (typeof WebAssembly === 'undefined') { }); } - test('createPlugin fails as expected when calling unknown function', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/code.wasm', { useWasi: true }); + test("createPlugin fails as expected when calling unknown function", async () => { + const plugin = await createPlugin("http://localhost:8124/wasm/code.wasm", { + useWasi: true, + }); try { - const [err, data] = await plugin.call('reticulate_splines', 'hello world').then( - (data) => [null, data], - (err) => [err, null], - ); + const [err, data] = await plugin.call("reticulate_splines", "hello world") + .then( + (data) => [null, data], + (err) => [err, null], + ); assert(data === null); - assert.equal(err?.message, 'Plugin error: function "reticulate_splines" does not exist'); + assert.equal( + err?.message, + 'Plugin error: function "reticulate_splines" does not exist', + ); } finally { await plugin.close(); } }); - test('plugin can allocate memory', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/alloc.wasm'); + test("plugin can allocate memory", async () => { + const plugin = await createPlugin("http://localhost:8124/wasm/alloc.wasm"); try { - await plugin.call('run_test', ''); + await plugin.call("run_test", ""); } finally { await plugin.close(); } }); - test('plugins cant allocate more memory than allowed', async () => { + test("plugins cant allocate more memory than allowed", async () => { const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/memory.wasm' }], memory: { maxPages: 2 } }, - { useWasi: true }); + { + wasm: [{ url: "http://localhost:8124/wasm/memory.wasm" }], + memory: { maxPages: 2 }, + }, + { useWasi: true }, + ); const pageSize = 64 * 1024; try { - const [err, _] = await plugin.call('alloc_memory', JSON.stringify({ bytes: pageSize * 5 })).then( + const [err, _] = await plugin.call( + "alloc_memory", + JSON.stringify({ bytes: pageSize * 5 }), + ).then( (data) => [null, data], (err) => [err, null], ); - assert(err) + assert(err); } finally { await plugin.close(); } }); - test('plugins can allocate memory if allowed', async () => { + test("plugins can allocate memory if allowed", async () => { const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/memory.wasm' }], memory: { maxPages: 6 } }, - { useWasi: true }); + { + wasm: [{ url: "http://localhost:8124/wasm/memory.wasm" }], + memory: { maxPages: 6 }, + }, + { useWasi: true }, + ); const pageSize = 64 * 1024; try { - const [err, _] = await plugin.call('alloc_memory', JSON.stringify({ bytes: pageSize * 5 })).then( + const [err, _] = await plugin.call( + "alloc_memory", + JSON.stringify({ bytes: pageSize * 5 }), + ).then( (data) => [null, data], (err) => [err, null], ); - assert(err === null) + assert(err === null); } finally { await plugin.close(); } }); - test('plugin can call input_offset', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/input_offset.wasm'); + test("plugin can call input_offset", async () => { + const plugin = await createPlugin( + "http://localhost:8124/wasm/input_offset.wasm", + ); try { - const input = 'hello world'; - const hw = await plugin.call('input_offset_length', input); + const input = "hello world"; + const hw = await plugin.call("input_offset_length", input); assert.equal(hw?.getBigUint64(0, true), input.length); } finally { await plugin.close(); } }); - test('plugin can fail gracefully', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/fail.wasm'); + test("plugin can fail gracefully", async () => { + const plugin = await createPlugin("http://localhost:8124/wasm/fail.wasm"); try { - const [err, data] = await plugin.call('run_test', '').then( + const [err, data] = await plugin.call("run_test", "").then( (data) => [null, data], (err) => [err, null], ); assert(data === null); - assert.equal(err.message, 'Plugin-originated error: Some error message'); + assert.equal(err.message, "Plugin-originated error: Some error message"); } finally { await plugin.close(); } }); if (CAPABILITIES.supportsWasiPreview1) { - test('can initialize Haskell runtime', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/hello_haskell.wasm', { - config: { greeting: 'Howdy' }, - useWasi: true, - }); + test("can initialize Haskell runtime", async () => { + const plugin = await createPlugin( + "http://localhost:8124/wasm/hello_haskell.wasm", + { + config: { greeting: "Howdy" }, + useWasi: true, + }, + ); try { - let output = await plugin.call('testing', 'John'); + let output = await plugin.call("testing", "John"); - assert.equal(output?.string(), 'Howdy, John'); + assert.equal(output?.string(), "Howdy, John"); - output = await plugin.call('testing', 'Ben'); + output = await plugin.call("testing", "Ben"); assert(output !== null); - assert.equal(output?.string(), 'Howdy, Ben'); + assert.equal(output?.string(), "Howdy, Ben"); } finally { await plugin.close(); } }); - test('we fallback to Manifest.config if ExtismPluginOptions.config is not specified', async () => { + test("we fallback to Manifest.config if ExtismPluginOptions.config is not specified", async () => { const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/hello_haskell.wasm' }], config: { greeting: 'Howdy' } }, - { useWasi: true } + { + wasm: [{ url: "http://localhost:8124/wasm/hello_haskell.wasm" }], + config: { greeting: "Howdy" }, + }, + { useWasi: true }, ); try { - let output = await plugin.call('testing', 'John'); + let output = await plugin.call("testing", "John"); - assert.equal(output?.string(), 'Howdy, John'); + assert.equal(output?.string(), "Howdy, John"); - output = await plugin.call('testing', 'Ben'); + output = await plugin.call("testing", "Ben"); assert(output !== null); - assert.equal(output?.string(), 'Howdy, Ben'); + assert.equal(output?.string(), "Howdy, Ben"); } finally { await plugin.close(); } @@ -886,40 +1039,52 @@ if (typeof WebAssembly === 'undefined') { // TODO(chrisdickinson): this turns out to be pretty tricky to test, since // deno and node's wasi bindings bypass JS entirely and write directly to // their respective FDs. I'm settling for tests that exercise both behaviors. - test('when EXTISM_ENABLE_WASI_OUTPUT is not set, WASI output is stifled', async () => { + test("when EXTISM_ENABLE_WASI_OUTPUT is not set, WASI output is stifled", async () => { if ((globalThis as unknown as any).process) { ( - globalThis as unknown as Record }> - ).process.env.EXTISM_ENABLE_WASI_OUTPUT = ''; + globalThis as unknown as Record< + string, + { env: Record } + > + ).process.env.EXTISM_ENABLE_WASI_OUTPUT = ""; } else if ((globalThis as unknown as any).Deno) { - globalThis.Deno.env.set('EXTISM_ENABLE_WASI_OUTPUT', ''); + globalThis.Deno.env.set("EXTISM_ENABLE_WASI_OUTPUT", ""); } - const plugin = await createPlugin('http://localhost:8124/wasm/wasistdout.wasm', { - useWasi: true, - }); + const plugin = await createPlugin( + "http://localhost:8124/wasm/wasistdout.wasm", + { + useWasi: true, + }, + ); try { - await plugin.call('say_hello'); + await plugin.call("say_hello"); } finally { await plugin.close(); } }); - test('respects enableWasiOutput', async () => { + test("respects enableWasiOutput", async () => { if ((globalThis as unknown as any).process) { ( - globalThis as unknown as Record }> - ).process.env.EXTISM_ENABLE_WASI_OUTPUT = ''; + globalThis as unknown as Record< + string, + { env: Record } + > + ).process.env.EXTISM_ENABLE_WASI_OUTPUT = ""; } else if ((globalThis as unknown as any).Deno) { - globalThis.Deno.env.set('EXTISM_ENABLE_WASI_OUTPUT', ''); + globalThis.Deno.env.set("EXTISM_ENABLE_WASI_OUTPUT", ""); } - const plugin = await createPlugin('http://localhost:8124/wasm/wasistdout.wasm', { - useWasi: true, - enableWasiOutput: true, - }); + const plugin = await createPlugin( + "http://localhost:8124/wasm/wasistdout.wasm", + { + useWasi: true, + enableWasiOutput: true, + }, + ); try { - await plugin.call('say_hello'); + await plugin.call("say_hello"); } finally { await plugin.close(); } @@ -927,72 +1092,81 @@ if (typeof WebAssembly === 'undefined') { } if (CAPABILITIES.fsAccess && CAPABILITIES.supportsWasiPreview1) { - test('readonly allowed paths are not supported', async () => { + test("readonly allowed paths are not supported", async () => { try { await createPlugin( - { wasm: [{ name: 'main', url: 'http://localhost:8124/wasm/fs.wasm' }], allowedPaths: { '/mnt': 'ro:tests/data' } }, + { + wasm: [{ name: "main", url: "http://localhost:8124/wasm/fs.wasm" }], + allowedPaths: { "/mnt": "ro:tests/data" }, + }, { useWasi: true, functions: {}, runInWorker: true }, ); - assert.fail('should not reach here'); + assert.fail("should not reach here"); } catch (err) { if (err instanceof Error) { - assert.equal(err.message, 'Readonly dirs are not supported: ro:tests/data'); + assert.equal( + err.message, + "Readonly dirs are not supported: ro:tests/data", + ); } } }); - test('can access fs', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/fs.wasm', { - allowedPaths: { '/mnt': 'tests/data' }, + test("can access fs", async () => { + const plugin = await createPlugin("http://localhost:8124/wasm/fs.wasm", { + allowedPaths: { "/mnt": "tests/data" }, useWasi: true, }); try { - const output = await plugin.call('run_test', ''); + const output = await plugin.call("run_test", ""); assert(output !== null); const result = output.string(); - assert.equal(result, 'hello world!'); + assert.equal(result, "hello world!"); } finally { await plugin.close(); } }); - test('we fallback to Manifest.allowedPaths if ExtismPluginOptions.allowedPaths is not specified', async () => { + test("we fallback to Manifest.allowedPaths if ExtismPluginOptions.allowedPaths is not specified", async () => { const plugin = await createPlugin( - { wasm: [{ url: 'http://localhost:8124/wasm/fs.wasm' }], allowedPaths: { '/mnt': 'tests/data' } }, - { useWasi: true } + { + wasm: [{ url: "http://localhost:8124/wasm/fs.wasm" }], + allowedPaths: { "/mnt": "tests/data" }, + }, + { useWasi: true }, ); try { - const output = await plugin.call('run_test', ''); + const output = await plugin.call("run_test", ""); assert(output !== null); const result = output.string(); - assert.equal(result, 'hello world!'); + assert.equal(result, "hello world!"); } finally { await plugin.close(); } }); - test('linking to a wasi command side-module works', async () => { + test("linking to a wasi command side-module works", async () => { const plugin = await createPlugin( { wasm: [ - { name: 'side', url: 'http://localhost:8124/wasm/fs.wasm' }, - { name: 'main', url: 'http://localhost:8124/wasm/fs-link.wasm' }, + { name: "side", url: "http://localhost:8124/wasm/fs.wasm" }, + { name: "main", url: "http://localhost:8124/wasm/fs-link.wasm" }, ], }, { - allowedPaths: { '/mnt': 'tests/data' }, + allowedPaths: { "/mnt": "tests/data" }, useWasi: true, }, ); try { - const output = await plugin.call('run_test', ''); + const output = await plugin.call("run_test", ""); assert(output !== null); const result = output.string(); - assert.equal(result, 'hello world!'); + assert.equal(result, "hello world!"); } finally { await plugin.close(); } diff --git a/src/mod.ts b/src/mod.ts index 9a638f4..04f23e0 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -1,33 +1,43 @@ -import { CAPABILITIES } from './polyfills/deno-capabilities.ts'; +import { CAPABILITIES } from "./polyfills/deno-capabilities.ts"; -import type { ManifestLike, InternalConfig, ExtismPluginOptions, Plugin } from './interfaces.ts'; +import { + logLevelToPriority, + type ExtismPluginOptions, + type InternalConfig, + type ManifestLike, + type Plugin, +} from "./interfaces.ts"; -import { toWasmModuleData as _toWasmModuleData } from './manifest.ts'; +import { toWasmModuleData as _toWasmModuleData } from "./manifest.ts"; -import { createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; -import { createBackgroundPlugin as _createBackgroundPlugin } from './background-plugin.ts'; +import { createForegroundPlugin as _createForegroundPlugin } from "./foreground-plugin.ts"; +import { createBackgroundPlugin as _createBackgroundPlugin } from "./background-plugin.ts"; -export { CAPABILITIES } from './polyfills/deno-capabilities.ts'; +export { CAPABILITIES } from "./polyfills/deno-capabilities.ts"; export type { Capabilities, ExtismPluginOptions, + LogLevel, + Manifest, ManifestLike, - ManifestWasmResponse, - ManifestWasmModule, + ManifestWasm, ManifestWasmData, - ManifestWasmUrl, + ManifestWasmModule, ManifestWasmPath, - ManifestWasm, - Manifest, + ManifestWasmResponse, + ManifestWasmUrl, MemoryOptions, Plugin, PluginConfig, PluginConfigLike, PluginOutput, -} from './interfaces.ts'; +} from "./interfaces.ts"; -export type { CallContext, CallContext as CurrentPlugin } from './call-context.ts'; +export type { + CallContext, + CallContext as CurrentPlugin, +} from "./call-context.ts"; /** * Create a {@link Plugin} given a {@link ManifestLike} and {@link ExtismPluginOptions}. @@ -72,16 +82,22 @@ export async function createPlugin( ): Promise { opts = { ...opts }; opts.useWasi ??= false; - opts.enableWasiOutput ??= opts.useWasi ? CAPABILITIES.extismStdoutEnvVarSet : false; + opts.enableWasiOutput ??= opts.useWasi + ? CAPABILITIES.extismStdoutEnvVarSet + : false; opts.functions = opts.functions || {}; // TODO(chrisdickinson): reset this to `CAPABILITIES.hasWorkerCapability` once we've fixed https://github.com/extism/js-sdk/issues/46. opts.runInWorker ??= false; opts.logger ??= console; + opts.logLevel ??= 'silent'; opts.fetch ??= fetch; - const [manifestOpts, names, moduleData] = await _toWasmModuleData(await Promise.resolve(manifest), opts.fetch ?? fetch); + const [manifestOpts, names, moduleData] = await _toWasmModuleData( + await Promise.resolve(manifest), + opts.fetch ?? fetch, + ); opts.allowedPaths = opts.allowedPaths || manifestOpts.allowedPaths || {}; opts.allowedHosts = opts.allowedHosts || manifestOpts.allowedHosts || []; @@ -90,24 +106,28 @@ export async function createPlugin( opts.timeoutMs = opts.timeoutMs || manifestOpts.timeoutMs || null; if (opts.allowedHosts.length && !opts.runInWorker) { - throw new TypeError('"allowedHosts" requires "runInWorker: true". HTTP functions are only available to plugins running in a worker.') + throw new TypeError( + '"allowedHosts" requires "runInWorker: true". HTTP functions are only available to plugins running in a worker.', + ); } if (opts.timeoutMs && !opts.runInWorker) { - throw new TypeError('"timeout" requires "runInWorker: true". Call timeouts are only available to plugins running in a worker.') + throw new TypeError( + '"timeout" requires "runInWorker: true". Call timeouts are only available to plugins running in a worker.', + ); } if (opts.runInWorker && !CAPABILITIES.hasWorkerCapability) { throw new Error( - 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)', + "Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)", ); } for (const guest in opts.allowedPaths) { const host = opts.allowedPaths[guest]; - if (host.startsWith('ro:')) { - throw new Error(`Readonly dirs are not supported: ${host}`) + if (host.startsWith("ro:")) { + throw new Error(`Readonly dirs are not supported: ${host}`); } } @@ -118,6 +138,7 @@ export async function createPlugin( fetch: opts.fetch || fetch, wasiEnabled: opts.useWasi, logger: opts.logger, + logLevel: logLevelToPriority(opts.logLevel || 'silent'), config: opts.config, enableWasiOutput: opts.enableWasiOutput, sharedArrayBufferSize: Number(opts.sharedArrayBufferSize) || 1 << 16, @@ -125,7 +146,11 @@ export async function createPlugin( memory: opts.memory, }; - return (opts.runInWorker ? _createBackgroundPlugin : _createForegroundPlugin)(ic, names, moduleData); + return (opts.runInWorker ? _createBackgroundPlugin : _createForegroundPlugin)( + ic, + names, + moduleData, + ); } export { createPlugin as newPlugin }; diff --git a/src/worker.ts b/src/worker.ts index e8c694f..4502e4e 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -1,8 +1,20 @@ -import { parentPort } from 'node:worker_threads'; - -import { ForegroundPlugin, createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; -import { CallContext, EXPORT_STATE, CallState, IMPORT_STATE } from './call-context.ts'; -import { SharedArrayBufferSection, SAB_BASE_OFFSET, type InternalConfig } from './interfaces.ts'; +import { parentPort } from "node:worker_threads"; + +import { + createForegroundPlugin as _createForegroundPlugin, + ForegroundPlugin, +} from "./foreground-plugin.ts"; +import { + CallContext, + CallState, + EXPORT_STATE, + IMPORT_STATE, +} from "./call-context.ts"; +import { + type InternalConfig, + SAB_BASE_OFFSET, + SharedArrayBufferSection, +} from "./interfaces.ts"; class Reactor { hostFlag: Int32Array | null; @@ -15,68 +27,80 @@ class Reactor { constructor(port: typeof parentPort) { if (!port) { - throw new Error('This should be unreachable: this module should only be invoked as a web worker.'); + throw new Error( + "This should be unreachable: this module should only be invoked as a web worker.", + ); } this.sharedData = null; this.sharedDataView = null; this.hostFlag = null; this.port = port; - this.port.on('message', (ev: any) => this.handleMessage(ev)); - this.port.postMessage({ type: 'initialized' }); + this.port.on("message", (ev: any) => this.handleMessage(ev)); + this.port.postMessage({ type: "initialized" }); this.dynamicHandlers = new Map(); - this.dynamicHandlers.set('call', async (transfer: any[], name: string, input: number | null, state: CallState) => { - if (!this.context) { - throw new Error('invalid state: no context available to worker reactor'); - } + this.dynamicHandlers.set( + "call", + async ( + transfer: any[], + name: string, + input: number | null, + state: CallState, + ) => { + if (!this.context) { + throw new Error( + "invalid state: no context available to worker reactor", + ); + } - this.context[IMPORT_STATE](state); + this.context[IMPORT_STATE](state); - const results: any = await this.plugin?.callBlock(name, input).then( - (indices) => [null, indices], - (err) => [err, null], - ); + const results: any = await this.plugin?.callBlock(name, input).then( + (indices) => [null, indices], + (err) => [err, null], + ); - state = this.context[EXPORT_STATE](); - for (const [block] of state.blocks) { - if (block) { - transfer.push(block); + state = this.context[EXPORT_STATE](); + for (const [block] of state.blocks) { + if (block) { + transfer.push(block); + } } - } - if (results[0]) { - results[0] = { - originalStack: results[0]?.stack, - message: results[0]?.message, - }; - } + if (results[0]) { + results[0] = { + originalStack: results[0]?.stack, + message: results[0]?.message, + }; + } - return { results, state }; - }); + return { results, state }; + }, + ); - this.dynamicHandlers.set('reset', async (_txf) => { + this.dynamicHandlers.set("reset", async (_txf) => { return this.plugin?.reset(); }); - this.dynamicHandlers.set('getExports', async (_txf) => { + this.dynamicHandlers.set("getExports", async (_txf) => { return this.plugin?.getExports(); }); - this.dynamicHandlers.set('getImports', async (_txf) => { + this.dynamicHandlers.set("getImports", async (_txf) => { return this.plugin?.getImports(); }); - this.dynamicHandlers.set('functionExists', async (_txf, name) => { + this.dynamicHandlers.set("functionExists", async (_txf, name) => { return this.plugin?.functionExists(name); }); } async handleMessage(ev: any) { switch (ev.type) { - case 'init': + case "init": return await this.handleInit(ev); - case 'invoke': + case "invoke": return await this.handleInvoke(ev); } } @@ -85,7 +109,7 @@ class Reactor { const handler = this.dynamicHandlers.get(ev.handler); if (!handler) { return this.port.postMessage({ - type: 'return', + type: "return", result: [`no handler registered for ${ev.handler}`, null], }); } @@ -105,7 +129,7 @@ class Reactor { return this.port.postMessage( { - type: 'return', + type: "return", results, }, transfer, @@ -133,7 +157,8 @@ class Reactor { funcs.map((funcName) => { return [ funcName, - (context: CallContext, ...args: any[]) => this.callHost(context, namespace, funcName, args), + (context: CallContext, ...args: any[]) => + this.callHost(context, namespace, funcName, args), ]; }), ), @@ -143,15 +168,24 @@ class Reactor { const { type: _, modules, functions: __, ...opts } = ev; - const logLevel = (level: string) => (message: string) => this.port.postMessage({ type: 'log', level, message }); + const logLevel = (level: string) => (message: string) => + this.port.postMessage({ type: "log", level, message }); // TODO: we're using non-blocking log functions here; to properly preserve behavior we // should invoke these and wait on the host to return. const logger = Object.fromEntries( - ['info', 'debug', 'warn', 'error'].map((lvl) => [lvl, logLevel(lvl)]), + ["info", "debug", "warn", "error", "trace"].map(( + lvl, + ) => [lvl, logLevel(lvl)]), ) as unknown as Console; - this.context = new CallContext(ArrayBuffer, logger, ev.config, ev.memory); + this.context = new CallContext( + ArrayBuffer, + logger, + ev.logLevel, + ev.config, + ev.memory, + ); // TODO: replace our internal fetch and logger this.plugin = await _createForegroundPlugin( @@ -161,18 +195,25 @@ class Reactor { this.context, ); - this.port.postMessage({ type: 'ready' }); + this.port.postMessage({ type: "ready" }); } - callHost(context: CallContext, namespace: string, func: string, args: (number | bigint)[]): number | bigint | void { + callHost( + context: CallContext, + namespace: string, + func: string, + args: (number | bigint)[], + ): number | bigint | void { if (!this.hostFlag) { - throw new Error('attempted to call host before receiving shared array buffer'); + throw new Error( + "attempted to call host before receiving shared array buffer", + ); } Atomics.store(this.hostFlag, 0, SAB_BASE_OFFSET); const state = context[EXPORT_STATE](); this.port.postMessage({ - type: 'invoke', + type: "invoke", namespace, func, args, @@ -270,7 +311,7 @@ class RingBufferReader { value = Atomics.load(this.flag, 0); if (value === SAB_BASE_OFFSET) { const result = Atomics.wait(this.flag, 0, SAB_BASE_OFFSET, MAX_WAIT); - if (result === 'timed-out') { + if (result === "timed-out") { continue; } } @@ -298,7 +339,12 @@ class RingBufferReader { read(output: Uint8Array) { this.position += output.byteLength; if (output.byteLength < this.available) { - output.set(new Uint8Array(this.input).subarray(this.inputOffset, this.inputOffset + output.byteLength)); + output.set( + new Uint8Array(this.input).subarray( + this.inputOffset, + this.inputOffset + output.byteLength, + ), + ); this.inputOffset += output.byteLength; return; } @@ -308,7 +354,13 @@ class RingBufferReader { // read ::= [outputoffset, inputoffset, extent] // firstread = [this.outputOffset, 0, this.available - this.outputOffset] do { - output.set(new Uint8Array(this.input).subarray(this.inputOffset, this.inputOffset + extent), outputOffset); + output.set( + new Uint8Array(this.input).subarray( + this.inputOffset, + this.inputOffset + extent, + ), + outputOffset, + ); outputOffset += extent; this.inputOffset += extent; if (outputOffset === output.byteLength) { @@ -320,7 +372,10 @@ class RingBufferReader { } this.pull(); - extent = Math.min(Math.max(this.available, 0), output.byteLength - outputOffset); + extent = Math.min( + Math.max(this.available, 0), + output.byteLength - outputOffset, + ); } while (outputOffset !== output.byteLength); } diff --git a/wasm/log.wasm b/wasm/log.wasm old mode 100644 new mode 100755 index 26598e981e610efdcb4018a92a0a93ae0eb12746..5a7efdfb87c5d70fb0f3cedea8ea9ff87a028e48 GIT binary patch literal 213879 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%0Ae8|yhOBe~ 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 Tx4JM?vvRUwwXFXRLH7Ry@TtU?