1import initPikchrModule from '../../artifacts/pikchr/pikchr-backend.js'2import wasmModule from '../../artifacts/pikchr/pikchr.wasm'3import { RenderError } from '../runtime/errors'4import type { RendererAdapter } from '../runtime/types'56const VERSION = 'pikchr@85e65b9686/edge-wasm-1'7const MAX_SOURCE = 256 * 10248let modulePromise: Promise<any> | undefined9let renderTail: Promise<void> = Promise.resolve()1011function pikchrModule(): Promise<any> {12 modulePromise ??= initPikchrModule({13 instantiateWasm(imports: WebAssembly.Imports, receive: (instance: WebAssembly.Instance) => void) {14 const instance = new WebAssembly.Instance(wasmModule, imports)15 receive(instance)16 return instance.exports17 },18 })19 return modulePromise!20}2122async function withPikchrLock<T>(operation: () => T): Promise<T> {23 const previous = renderTail24 let release!: () => void25 renderTail = new Promise(resolve => { release = resolve })26 await previous27 try { return operation() } finally { release() }28}2930export const pikchrAdapter: RendererAdapter = {31 id: 'pikchr', runtime: 'edge-wasm', version: VERSION,32 async render(request, signal) {33 if (signal.aborted) throw signal.reason34 if (new TextEncoder().encode(request.source).byteLength > MAX_SOURCE) throw new RenderError(413, 'source_too_large', 'Pikchr source exceeds the 256 KiB limit.')35 if (Object.keys(request.options).length > 0) throw new RenderError(400, 'unsupported_options', 'Pikchr options are not supported by the edge renderer.')36 try {37 const body = await withPikchrLock(async () => {38 if (signal.aborted) throw signal.reason39 const module = await pikchrModule()40 const dimensions = module.ccall('malloc', 'number', ['number'], [8])41 if (!dimensions) throw new RenderError(500, 'renderer_unavailable', 'Pikchr could not allocate render state.')42 let pointer = 043 try {44 pointer = module.ccall('pikchr', 'number', ['string', 'string', 'number', 'number', 'number'], [request.source, 'pikchr', 1, dimensions, dimensions + 4])45 if (!pointer) throw new RenderError(422, 'render_failed', 'Pikchr returned no SVG.')46 const width = module.getValue(dimensions, 'i32')47 const height = module.getValue(dimensions + 4, 'i32')48 const body = module.UTF8ToString(pointer)49 if (width < 0 || height < 0 || /<pre\b/i.test(body)) throw new RenderError(422, 'render_failed', body.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 500) || 'Pikchr could not parse the source.')50 return body51 } finally {52 if (pointer) module.ccall('free', 'number', ['number'], [pointer])53 module.ccall('free', 'number', ['number'], [dimensions])54 }55 })56 if (signal.aborted) throw signal.reason57 if (!body.includes('<svg')) throw new RenderError(422, 'render_failed', 'Pikchr could not parse the source.')58 return { body, contentType: 'image/svg+xml', engineVersion: VERSION, runtime: 'edge-wasm' }59 } catch (error) {60 if (error instanceof RenderError) throw error61 const message = error instanceof Error ? error.message : String(error)62 throw new RenderError(422, 'render_failed', message.slice(0, 500) || 'Pikchr could not render this source.')63 }64 },65}