@vedivad/typst-web-service 0.7.11 → 0.7.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/rpc.ts","../src/uri.ts","../src/analyzer.ts","../src/analyzer-session.ts","../src/compiler.ts","../src/formatter.ts","../src/renderer.ts"],"sourcesContent":["// Injected at build time by tsup (see tsup.config.ts)\ndeclare const __WORKER_CODE__: string;\ndeclare const __ANALYZER_WORKER_CODE__: string;\n\n/** Create a Worker from an inlined code string, auto-revoking the blob URL on terminate. */\nexport function createBlobWorker(code: string): Worker {\n const blob = new Blob([code], { type: \"application/javascript\" });\n const url = URL.createObjectURL(blob);\n const worker = new Worker(url);\n const origTerminate = worker.terminate.bind(worker);\n worker.terminate = () => {\n origTerminate();\n URL.revokeObjectURL(url);\n };\n return worker;\n}\n\n/** Create a blob Worker from the inlined compiler worker code. */\nexport function createWorker(): Worker {\n return createBlobWorker(__WORKER_CODE__);\n}\n\n/** Create a blob Worker from the inlined analyzer worker code. */\nexport function createAnalyzerWorker(): Worker {\n return createBlobWorker(__ANALYZER_WORKER_CODE__);\n}\n\n/** Generic RPC helper: post a message to a worker and await a response matched by id. */\nexport function workerRpc<\n TReq extends { id: number },\n TRes extends { id: number },\n>(\n worker: Worker,\n request: TReq,\n timeoutMs: number = 30_000,\n transfer?: Transferable[],\n): Promise<TRes> {\n return new Promise((resolve, reject) => {\n const handler = (e: MessageEvent<TRes>) => {\n if (e.data.id !== request.id) return;\n clearTimeout(timer);\n worker.removeEventListener(\"message\", handler);\n resolve(e.data);\n };\n const timer = setTimeout(() => {\n worker.removeEventListener(\"message\", handler);\n reject(new Error(`worker timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n worker.addEventListener(\"message\", handler);\n worker.postMessage(request, transfer ?? []);\n });\n}\n\n/** Convenience: send a destroy RPC and terminate the worker. */\nexport function destroyWorker<TReq extends { id: number }>(\n worker: Worker,\n request: TReq,\n timeoutMs: number,\n label: string,\n): void {\n workerRpc(worker, request, timeoutMs)\n .catch((err) => console.error(`${label} destroy failed:`, err))\n .finally(() => worker.terminate());\n}\n","export function normalizeUntitledUri(uri: string): string {\n if (!uri.startsWith(\"untitled:\")) return uri;\n return `untitled:${uri.slice(\"untitled:\".length).replace(/^\\/+/, \"\")}`;\n}\n\nexport function normalizePath(path: string): string {\n return path.startsWith(\"/\") ? path : `/${path}`;\n}\n\nexport function normalizeRoot(rootPath: string): string {\n const root = normalizePath(rootPath);\n return root === \"/\" ? \"\" : root.replace(/\\/+$/, \"\");\n}\n","import type {\n AnalyzerDiagnosticEvent,\n AnalyzerMessage,\n AnalyzerRequest,\n AnalyzerResponse,\n LspDiagnostic,\n} from \"./analyzer-types.js\";\nimport { createAnalyzerWorker, destroyWorker, workerRpc } from \"./rpc.js\";\nimport { normalizeUntitledUri } from \"./uri.js\";\n\nexport type { LspDiagnostic };\n\nexport type DiagnosticsListener = (\n uri: string,\n diagnostics: LspDiagnostic[],\n) => void;\n\nexport interface TypstAnalyzerOptions {\n /**\n * Explicit Worker instance. When omitted, an inlined blob worker is created automatically.\n * Use this for Vite apps:\n * `await TypstAnalyzer.create({ worker: new Worker(new URL('typst-web-service/analyzer-worker', import.meta.url), { type: 'module' }) })`\n */\n worker?: Worker;\n /**\n * URL to the tinymist WASM binary.\n * Required — there is no default CDN URL for tinymist-web.\n */\n wasmUrl: string;\n}\n\nconst TIMEOUT = { INIT: 120_000, REQUEST: 30_000, DESTROY: 5_000 } as const;\n\n/**\n * Manages a tinymist language server in a Web Worker. Provides LSP-based\n * diagnostics, completion, and hover for Typst documents.\n *\n * Diagnostics are push-based: call `didChange()` to notify the analyzer of\n * content changes, and receive diagnostics via `onDiagnostics()` listeners\n * whenever tinymist publishes them.\n *\n * const analyzer = await TypstAnalyzer.create({ wasmUrl: '...' });\n * analyzer.onDiagnostics((uri, diags) => { ... });\n */\nexport class TypstAnalyzer {\n private idCounter: number;\n private versionCounter = 0;\n private worker: Worker;\n private openedUris = new Set<string>();\n private diagnosticsListeners = new Set<DiagnosticsListener>();\n\n private constructor(worker: Worker, idCounter: number) {\n this.worker = worker;\n this.idCounter = idCounter;\n\n // Listen for unsolicited diagnostic push notifications from the worker.\n this.worker.addEventListener(\n \"message\",\n (e: MessageEvent<AnalyzerMessage>) => {\n if (e.data.type === \"diagnostics\" && !(\"id\" in e.data)) {\n const event = e.data as AnalyzerDiagnosticEvent;\n const normalizedUri = normalizeUntitledUri(event.uri);\n for (const listener of this.diagnosticsListeners) {\n listener(normalizedUri, event.diagnostics);\n }\n }\n },\n );\n }\n\n static async create(options: TypstAnalyzerOptions): Promise<TypstAnalyzer> {\n const worker = options.worker ?? createAnalyzerWorker();\n let idCounter = 0;\n const absoluteWasmUrl = new URL(options.wasmUrl, globalThis.location?.href)\n .href;\n\n const res = await workerRpc<AnalyzerRequest, AnalyzerResponse>(\n worker,\n { type: \"init\", id: ++idCounter, wasmUrl: absoluteWasmUrl },\n TIMEOUT.INIT,\n );\n if (res.type === \"error\")\n throw new Error(`TypstAnalyzer init failed: ${res.message}`);\n\n return new TypstAnalyzer(worker, idCounter);\n }\n\n /**\n * Register a listener for push-based diagnostics.\n * Returns an unsubscribe function.\n */\n onDiagnostics(listener: DiagnosticsListener): () => void {\n this.diagnosticsListeners.add(listener);\n return () => this.diagnosticsListeners.delete(listener);\n }\n\n private rpc(\n request: AnalyzerRequest,\n timeoutMs: number = TIMEOUT.REQUEST,\n ): Promise<AnalyzerResponse> {\n return workerRpc(this.worker, request, timeoutMs);\n }\n\n async didOpen(uri: string, content: string): Promise<void> {\n const res = await this.rpc({\n type: \"didOpen\",\n id: ++this.idCounter,\n uri,\n content,\n });\n if (res.type === \"error\") throw new Error(res.message);\n this.openedUris.add(uri);\n }\n\n async didClose(uri: string): Promise<void> {\n if (!this.openedUris.has(uri)) return;\n const res = await this.rpc({\n type: \"didClose\",\n id: ++this.idCounter,\n uri,\n });\n if (res.type === \"error\") throw new Error(res.message);\n this.openedUris.delete(uri);\n }\n\n /**\n * Notify the analyzer that a document has changed.\n * Diagnostics will arrive asynchronously via `onDiagnostics()` listeners.\n */\n async didChange(uri: string, content: string): Promise<void> {\n if (!this.openedUris.has(uri)) {\n await this.didOpen(uri, content);\n return;\n }\n\n const version = ++this.versionCounter;\n const res = await this.rpc({\n type: \"didChange\",\n id: ++this.idCounter,\n uri,\n version,\n content,\n });\n if (res.type === \"error\") throw new Error(res.message);\n }\n\n async completion(\n uri: string,\n line: number,\n character: number,\n ): Promise<unknown> {\n const res = await this.rpc({\n type: \"completion\",\n id: ++this.idCounter,\n uri,\n line,\n character,\n });\n if (res.type === \"error\") throw new Error(res.message);\n if (res.type === \"completionResult\") return res.result;\n return null;\n }\n\n async hover(uri: string, line: number, character: number): Promise<unknown> {\n const res = await this.rpc({\n type: \"hover\",\n id: ++this.idCounter,\n uri,\n line,\n character,\n });\n if (res.type === \"error\") throw new Error(res.message);\n if (res.type === \"hoverResult\") return res.result;\n return null;\n }\n\n destroy(): void {\n this.diagnosticsListeners.clear();\n destroyWorker(\n this.worker,\n { type: \"destroy\" as const, id: ++this.idCounter },\n TIMEOUT.DESTROY,\n \"TypstAnalyzer\",\n );\n }\n}\n","import type { LspDiagnostic } from \"./analyzer-types.js\";\nimport type { TypstAnalyzer } from \"./analyzer.js\";\nimport { normalizePath, normalizeRoot } from \"./uri.js\";\n\nexport type DiagnosticsSubscriber = (diagnostics: LspDiagnostic[]) => void;\n\nexport interface AnalyzerSessionOptions {\n analyzer: Pick<\n TypstAnalyzer,\n \"didOpen\" | \"didChange\" | \"completion\" | \"hover\" | \"onDiagnostics\"\n >;\n /** Project root used to build stable in-memory analyzer URIs. Default: \"/project\". */\n rootPath?: string;\n /** Entry file path within the project. Synced last to ensure dependencies load first. Default: \"/main.typ\". */\n entryPath?: string;\n}\n\n/**\n * Synchronizes an in-memory Typst project with a TypstAnalyzer.\n * Handles multi-file ordering, request queueing, and diagnostic subscriptions.\n *\n * Diagnostics arrive via the analyzer's push mechanism and are forwarded\n * to subscribers registered with `subscribe()`.\n *\n * const session = new AnalyzerSession({ analyzer });\n * session.subscribe(\"/main.typ\", (diags) => { ... });\n * await session.sync(\"/main.typ\", files);\n */\nexport class AnalyzerSession {\n private readonly analyzer: AnalyzerSessionOptions[\"analyzer\"];\n private readonly rootPath: string;\n private readonly entryPath: string;\n private readonly syncedFiles = new Map<string, string>();\n private queue: Promise<void> = Promise.resolve();\n\n // Diagnostic subscription state\n private readonly listenersByUri = new Map<\n string,\n Set<DiagnosticsSubscriber>\n >();\n /** Last push received per URI. Replayed on subscribe() so tab-back shows correct diagnostics instantly. */\n private readonly diagnosticsCache = new Map<string, LspDiagnostic[]>();\n private readonly unsubscribeAnalyzer: () => void;\n\n constructor(options: AnalyzerSessionOptions) {\n this.analyzer = options.analyzer;\n this.rootPath = normalizeRoot(options.rootPath ?? \"/project\");\n this.entryPath = normalizePath(options.entryPath ?? \"/main.typ\");\n\n this.unsubscribeAnalyzer = this.analyzer.onDiagnostics(\n (uri, diagnostics) => {\n this.diagnosticsCache.set(uri, diagnostics);\n const listeners = this.listenersByUri.get(uri);\n if (!listeners) return;\n for (const listener of listeners) listener(diagnostics);\n },\n );\n }\n\n /** Build a tinymist URI from a project-relative path. */\n toUri(path: string): string {\n const root = this.rootPath.replace(/^\\//, \"\");\n return `untitled:${root}${normalizePath(path)}`;\n }\n\n /**\n * Subscribe to push-based diagnostics for a file path.\n * Returns an unsubscribe function.\n */\n subscribe(path: string, listener: DiagnosticsSubscriber): () => void {\n const uri = this.toUri(path);\n\n let listeners = this.listenersByUri.get(uri);\n if (!listeners) {\n listeners = new Set();\n this.listenersByUri.set(uri, listeners);\n }\n listeners.add(listener);\n\n // Replay the last known diagnostics immediately so the UI reflects the\n // correct state without waiting for the next tinymist push.\n const cached = this.diagnosticsCache.get(uri);\n if (cached) listener(cached);\n\n return () => {\n const current = this.listenersByUri.get(uri);\n if (!current) return;\n current.delete(listener);\n if (current.size === 0) this.listenersByUri.delete(uri);\n };\n }\n\n /**\n * Sync all project files with the analyzer.\n * `files` must include the active file's current content under `path`.\n *\n * If the active file's content hasn't changed since the last sync, a\n * lightweight hover is triggered to ensure the analyzer re-analyzes with\n * the current project state and publishes fresh diagnostics.\n */\n async sync(path: string, files: Record<string, string>): Promise<void> {\n await this.enqueue(async () => {\n const changed = await this.syncFiles(path, files);\n\n // When the active file's content is unchanged, the analyzer won't\n // publish fresh diagnostics on its own. A hover request forces it\n // to re-evaluate with the (possibly updated) project context.\n if (!changed) {\n try {\n await this.analyzer.hover(this.toUri(normalizePath(path)), 0, 0);\n } catch {\n /* best-effort — the hover result is unused */\n }\n }\n\n // Clean up files that were removed from the project.\n for (const filePath of this.syncedFiles.keys()) {\n if (!Object.hasOwn(files, filePath)) {\n this.syncedFiles.delete(filePath);\n }\n }\n });\n }\n\n /**\n * Sync files and request completions at the given position.\n * Returns the raw LSP CompletionList/CompletionItem[] from tinymist.\n */\n async completion(\n path: string,\n files: Record<string, string>,\n line: number,\n character: number,\n ): Promise<unknown> {\n return this.enqueue(async () => {\n await this.syncFiles(path, files);\n return this.analyzer.completion(\n this.toUri(normalizePath(path)),\n line,\n character,\n );\n });\n }\n\n /**\n * Sync files and request hover info at the given position.\n * Returns the raw LSP Hover result from tinymist.\n */\n async hover(\n path: string,\n files: Record<string, string>,\n line: number,\n character: number,\n ): Promise<unknown> {\n return this.enqueue(async () => {\n await this.syncFiles(path, files);\n return this.analyzer.hover(\n this.toUri(normalizePath(path)),\n line,\n character,\n );\n });\n }\n\n destroy(): void {\n this.unsubscribeAnalyzer();\n this.listenersByUri.clear();\n this.diagnosticsCache.clear();\n }\n\n /**\n * Sync all project files: dependencies first, active file last.\n * Returns whether the active file's content was actually sent to the analyzer.\n */\n private async syncFiles(\n path: string,\n files: Record<string, string>,\n ): Promise<boolean> {\n const activePath = normalizePath(path);\n for (const filePath of this.orderedPaths(files)) {\n if (filePath === activePath) continue;\n await this.syncFile(filePath, files[filePath]);\n }\n return this.syncFile(activePath, files[activePath]);\n }\n\n /** Sync a single file. Returns true if content was sent to the analyzer. */\n private async syncFile(path: string, content: string): Promise<boolean> {\n const prev = this.syncedFiles.get(path);\n if (prev == null) {\n await this.analyzer.didOpen(this.toUri(path), content);\n } else if (prev !== content) {\n await this.analyzer.didChange(this.toUri(path), content);\n } else {\n return false;\n }\n this.syncedFiles.set(path, content);\n return true;\n }\n\n private orderedPaths(files: Record<string, string>): string[] {\n return Object.keys(files)\n .map((p) => normalizePath(p))\n .sort((a, b) => {\n if (a === this.entryPath) return 1;\n if (b === this.entryPath) return -1;\n return a.localeCompare(b);\n });\n }\n\n private enqueue<T>(task: () => Promise<T>): Promise<T> {\n const run = this.queue.then(task, task);\n this.queue = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n}\n","import { createWorker, destroyWorker, workerRpc } from \"./rpc.js\";\nimport type {\n DiagnosticMessage,\n WorkerRequest,\n WorkerResponse,\n} from \"./types.js\";\n\nexport interface CompileResult {\n diagnostics: DiagnosticMessage[];\n /** Vector artifact bytes from the compiler, usable with TypstRenderer for SVG rendering. */\n vector?: Uint8Array;\n}\n\nexport interface TypstCompilerOptions {\n /**\n * Explicit Worker instance. When omitted, an inlined blob worker is created automatically.\n * Use this for Vite apps to get proper source maps:\n * `await TypstCompiler.create({ worker: new Worker(new URL('typst-web-service/worker', import.meta.url)) })`\n */\n worker?: Worker;\n /**\n * URL to the typst-ts-web-compiler WASM binary.\n * Defaults to the matching version on jsDelivr CDN.\n * Override with a local asset URL for offline support or faster load:\n * `new URL('@myriaddreamin/typst-ts-web-compiler/pkg/typst_ts_web_compiler_bg.wasm', import.meta.url).href`\n */\n wasmUrl?: string;\n /** Font URLs to load into the Typst compiler. Defaults to Roboto from jsDelivr. */\n fonts?: string[];\n /**\n * Enable fetching @preview/ packages from packages.typst.org on demand.\n * Default: true.\n */\n packages?: boolean;\n}\n\nconst DEFAULT_FONTS = [\n \"https://cdn.jsdelivr.net/npm/roboto-font@0.1.0/fonts/Roboto/roboto-regular-webfont.ttf\",\n];\n\nconst DEFAULT_WASM_URL =\n \"https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-web-compiler@0.7.0-rc2/pkg/typst_ts_web_compiler_bg.wasm\";\n\nconst TIMEOUT = { INIT: 60_000, RENDER: 60_000, DESTROY: 5_000 } as const;\n\nfunction toFiles(\n source: string | Record<string, string>,\n): Record<string, string> {\n return typeof source === \"string\" ? { \"/main.typ\": source } : source;\n}\n\n/**\n * Manages a Typst compiler worker. Create one instance and share it across\n * all extensions (linter, autocomplete, preview, etc.).\n *\n * await TypstCompiler.create() // blob worker, defaults\n * await TypstCompiler.create({ wasmUrl: '...' }) // blob worker, custom WASM\n * await TypstCompiler.create({ worker: myWorker }) // explicit Worker (Vite)\n * await TypstCompiler.create({ worker: myWorker, fonts: [...] }) // explicit Worker + options\n */\nexport class TypstCompiler {\n private idCounter: number;\n private worker: Worker;\n\n /** The most recent vector artifact from a compile, if any. */\n lastVector?: Uint8Array;\n\n private constructor(worker: Worker, idCounter: number) {\n this.worker = worker;\n this.idCounter = idCounter;\n }\n\n static async create(\n options: TypstCompilerOptions = {},\n ): Promise<TypstCompiler> {\n const worker = options.worker ?? createWorker();\n let idCounter = 0;\n\n const res = await workerRpc<WorkerRequest, WorkerResponse>(\n worker,\n {\n type: \"init\",\n id: ++idCounter,\n wasmUrl: options.wasmUrl ?? DEFAULT_WASM_URL,\n fonts: options.fonts ?? DEFAULT_FONTS,\n packages: options.packages ?? true,\n },\n TIMEOUT.INIT,\n );\n if (res.type === \"error\")\n throw new Error(`TypstCompiler init failed: ${res.message}`);\n\n return new TypstCompiler(worker, idCounter);\n }\n\n /** Compile a single source string (treated as /main.typ) or a map of files. */\n async compile(\n source: string | Record<string, string>,\n ): Promise<CompileResult> {\n const id = ++this.idCounter;\n const files = toFiles(source);\n const response = await workerRpc<WorkerRequest, WorkerResponse>(\n this.worker,\n {\n type: \"compile\",\n id,\n files,\n },\n );\n if (response.type === \"cancelled\") return { diagnostics: [] };\n if (response.type === \"result\") {\n const vector = response.vector\n ? new Uint8Array(response.vector)\n : undefined;\n if (vector) this.lastVector = vector;\n return { diagnostics: response.diagnostics, vector };\n }\n if (response.type === \"error\") throw new Error(response.message);\n return { diagnostics: [] };\n }\n\n /** Compile to PDF from a single source string (treated as /main.typ) or a map of files. */\n async compilePdf(\n source: string | Record<string, string>,\n ): Promise<Uint8Array> {\n const id = ++this.idCounter;\n const files = toFiles(source);\n const response = await workerRpc<WorkerRequest, WorkerResponse>(\n this.worker,\n { type: \"render\", id, files },\n TIMEOUT.RENDER,\n );\n if (response.type === \"cancelled\") throw new Error(\"Render cancelled\");\n if (response.type === \"pdf\") return new Uint8Array(response.data);\n if (response.type === \"error\") throw new Error(response.message);\n throw new Error(\"Unexpected response type\");\n }\n\n destroy(): void {\n const id = ++this.idCounter;\n destroyWorker(\n this.worker,\n { type: \"destroy\" as const, id },\n TIMEOUT.DESTROY,\n \"TypstCompiler\",\n );\n }\n}\n","export interface FormatConfig {\n /** Number of spaces per indentation level. Default: 2 */\n tab_spaces?: number;\n /** Maximum line width. Default: 80 */\n max_width?: number;\n /** Maximum consecutive blank lines allowed. */\n blank_lines_upper_bound?: number;\n /** Collapse consecutive whitespace in markup to a single space. */\n collapse_markup_spaces?: boolean;\n /** Sort import items alphabetically. */\n reorder_import_items?: boolean;\n /** Wrap text in markup to fit within max_width. Implies collapse_markup_spaces. */\n wrap_text?: boolean;\n}\n\nexport interface FormatRangeResult {\n /** Start index (UTF-16) of the actual formatted range. */\n start: number;\n /** End index (UTF-16) of the actual formatted range. */\n end: number;\n /** The formatted text for the range. */\n text: string;\n}\n\ntype TypstyleModule = typeof import(\"@typstyle/typstyle-wasm-bundler\");\n\nlet typstylePromise: Promise<TypstyleModule> | null = null;\n\nfunction getTypstyle(): Promise<TypstyleModule> {\n if (!typstylePromise) {\n typstylePromise = import(\"@typstyle/typstyle-wasm-bundler\").catch((err) => {\n typstylePromise = null;\n throw err;\n });\n }\n return typstylePromise;\n}\n\n/**\n * Typst code formatter powered by typstyle.\n *\n * Runs on the main thread — typstyle is lightweight and fast.\n * The WASM module is loaded lazily on first format call.\n * Requires a bundler that supports WASM imports (e.g. Vite, webpack).\n *\n * const formatter = TypstFormatter.create({ tab_spaces: 2, max_width: 80 });\n * const formatted = await formatter.format(source);\n */\nexport class TypstFormatter {\n private config: FormatConfig;\n\n private constructor(config: FormatConfig = {}) {\n this.config = config;\n // Eagerly start loading WASM so it's ready by first use.\n // Swallow preload errors — they'll surface on first format() call.\n getTypstyle().catch(() => {});\n }\n\n static create(config: FormatConfig = {}): TypstFormatter {\n return new TypstFormatter(config);\n }\n\n /** Format an entire Typst source string. */\n async format(source: string): Promise<string> {\n const typstyle = await getTypstyle();\n return typstyle.format(source, this.config);\n }\n\n /** Format a range within a Typst source string. Indices are UTF-16 code units. */\n async formatRange(\n source: string,\n start: number,\n end: number,\n ): Promise<FormatRangeResult> {\n const typstyle = await getTypstyle();\n const result = typstyle.format_range(source, start, end, this.config);\n return { start: result.start, end: result.end, text: result.text };\n }\n}\n","/** Minimal interface for the built TypstRenderer instance. */\nexport interface RendererInstance {\n free(): void;\n create_session(): RendererSession;\n manipulate_data(\n session: RendererSession,\n action: string,\n data: Uint8Array,\n ): void;\n svg_data(session: RendererSession): string;\n}\n\n/** Minimal interface for a TypstRenderer session. */\nexport interface RendererSession {\n free(): void;\n}\n\ntype RendererWasmModule = typeof import(\"@myriaddreamin/typst-ts-renderer\");\n\nconst DEFAULT_RENDERER_WASM_URL =\n \"https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-renderer@0.7.0-rc2/pkg/typst_ts_renderer_bg.wasm\";\n\nlet rendererModulePromise: Promise<RendererWasmModule> | null = null;\n\nfunction getRendererModule(): Promise<RendererWasmModule> {\n if (!rendererModulePromise) {\n rendererModulePromise = import(\"@myriaddreamin/typst-ts-renderer\").catch(\n (err) => {\n rendererModulePromise = null;\n throw err;\n },\n );\n }\n return rendererModulePromise;\n}\n\nexport interface TypstRendererOptions {\n /** URL to the typst-ts-renderer WASM binary. Defaults to jsDelivr CDN. */\n wasmUrl?: string;\n}\n\n/**\n * Converts Typst vector artifacts to SVG strings.\n *\n * The renderer WASM module is loaded lazily on first use.\n *\n * const renderer = TypstRenderer.create();\n * const svg = await renderer.renderSvg(vector);\n */\nexport class TypstRenderer {\n private wasmUrl: string;\n private instance: Promise<RendererInstance> | null = null;\n\n private constructor(options: TypstRendererOptions = {}) {\n this.wasmUrl = options.wasmUrl ?? DEFAULT_RENDERER_WASM_URL;\n // Eagerly start loading the WASM module so it's ready by first use.\n getRendererModule().catch(() => {});\n }\n\n static create(options: TypstRendererOptions = {}): TypstRenderer {\n return new TypstRenderer(options);\n }\n\n private getInstance(): Promise<RendererInstance> {\n if (!this.instance) {\n this.instance = this.#init().catch((err) => {\n this.instance = null;\n throw err;\n });\n }\n return this.instance;\n }\n\n async #init(): Promise<RendererInstance> {\n const mod = await getRendererModule();\n await mod.default(this.wasmUrl);\n return new mod.TypstRendererBuilder().build();\n }\n\n /** Free the underlying WASM renderer instance. */\n async destroy(): Promise<void> {\n const instance = this.instance;\n this.instance = null;\n if (instance) {\n (await instance).free();\n }\n }\n\n /** Render a Typst vector artifact to an SVG string. */\n async renderSvg(vector: Uint8Array): Promise<string> {\n const renderer = await this.getInstance();\n const session = renderer.create_session();\n try {\n renderer.manipulate_data(session, \"reset\", vector);\n return renderer.svg_data(session);\n } finally {\n session.free();\n }\n }\n}\n"],"mappings":"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","names":["TIMEOUT"]}
1
+ {"version":3,"sources":["../src/analyzer.ts","../src/rpc.ts","../src/uri.ts","../src/analyzer-session.ts","../src/compiler.ts","../src/formatter.ts","../src/renderer.ts"],"sourcesContent":["import * as Comlink from \"comlink\";\nimport type { LspDiagnostic } from \"./analyzer-types.js\";\nimport { createAnalyzerWorker } from \"./rpc.js\";\nimport { normalizeUntitledUri } from \"./uri.js\";\n\nexport type { LspDiagnostic };\n\nexport type DiagnosticsListener = (\n uri: string,\n diagnostics: LspDiagnostic[],\n) => void;\n\nexport interface TypstAnalyzerOptions {\n /**\n * Explicit Worker instance. When omitted, an inlined blob worker is created automatically.\n * Use this for Vite apps:\n * `await TypstAnalyzer.create({ worker: new Worker(new URL('typst-web-service/analyzer-worker', import.meta.url), { type: 'module' }) })`\n */\n worker?: Worker;\n /**\n * URL to the tinymist WASM binary.\n * Required — there is no default CDN URL for tinymist-web.\n */\n wasmUrl: string;\n}\n\ninterface AnalyzerWorkerAPI {\n init(\n wasmUrl: string,\n onDiagnostics: (uri: string, diagnostics: LspDiagnostic[]) => void,\n ): Promise<void>;\n didOpen(uri: string, content: string): Promise<void>;\n didClose(uri: string): Promise<void>;\n didChange(uri: string, version: number, content: string): Promise<void>;\n completion(uri: string, line: number, character: number): Promise<unknown>;\n hover(uri: string, line: number, character: number): Promise<unknown>;\n destroy(): void;\n}\n\n/**\n * Manages a tinymist language server in a Web Worker. Provides LSP-based\n * diagnostics, completion, and hover for Typst documents.\n *\n * Diagnostics are push-based: call `didChange()` to notify the analyzer of\n * content changes, and receive diagnostics via `onDiagnostics()` listeners\n * whenever tinymist publishes them.\n *\n * const analyzer = await TypstAnalyzer.create({ wasmUrl: '...' });\n * analyzer.onDiagnostics((uri, diags) => { ... });\n */\nexport class TypstAnalyzer {\n private readonly proxy: Comlink.Remote<AnalyzerWorkerAPI>;\n private readonly worker: Worker;\n private versionCounter = 0;\n private openedUris = new Set<string>();\n private diagnosticsListeners = new Set<DiagnosticsListener>();\n\n private constructor(\n worker: Worker,\n proxy: Comlink.Remote<AnalyzerWorkerAPI>,\n ) {\n this.worker = worker;\n this.proxy = proxy;\n }\n\n static async create(options: TypstAnalyzerOptions): Promise<TypstAnalyzer> {\n const worker = options.worker ?? createAnalyzerWorker();\n const proxy = Comlink.wrap<AnalyzerWorkerAPI>(worker);\n const absoluteWasmUrl = new URL(options.wasmUrl, globalThis.location?.href)\n .href;\n\n const analyzer = new TypstAnalyzer(worker, proxy);\n\n await proxy.init(\n absoluteWasmUrl,\n Comlink.proxy((uri: string, diagnostics: LspDiagnostic[]) => {\n const normalizedUri = normalizeUntitledUri(uri);\n for (const listener of analyzer.diagnosticsListeners) {\n listener(normalizedUri, diagnostics);\n }\n }),\n );\n\n return analyzer;\n }\n\n /**\n * Register a listener for push-based diagnostics.\n * Returns an unsubscribe function.\n */\n onDiagnostics(listener: DiagnosticsListener): () => void {\n this.diagnosticsListeners.add(listener);\n return () => this.diagnosticsListeners.delete(listener);\n }\n\n async didOpen(uri: string, content: string): Promise<void> {\n await this.proxy.didOpen(uri, content);\n this.openedUris.add(uri);\n }\n\n async didClose(uri: string): Promise<void> {\n if (!this.openedUris.has(uri)) return;\n await this.proxy.didClose(uri);\n this.openedUris.delete(uri);\n }\n\n /**\n * Notify the analyzer that a document has changed.\n * Diagnostics will arrive asynchronously via `onDiagnostics()` listeners.\n */\n async didChange(uri: string, content: string): Promise<void> {\n if (!this.openedUris.has(uri)) {\n await this.didOpen(uri, content);\n return;\n }\n const version = ++this.versionCounter;\n await this.proxy.didChange(uri, version, content);\n }\n\n async completion(\n uri: string,\n line: number,\n character: number,\n ): Promise<unknown> {\n return this.proxy.completion(uri, line, character);\n }\n\n async hover(uri: string, line: number, character: number): Promise<unknown> {\n return this.proxy.hover(uri, line, character);\n }\n\n destroy(): void {\n this.diagnosticsListeners.clear();\n this.proxy[Comlink.releaseProxy]();\n this.worker.terminate();\n }\n}\n","// Injected at build time by tsup (see tsup.config.ts)\ndeclare const __WORKER_CODE__: string;\ndeclare const __ANALYZER_WORKER_CODE__: string;\n\n/** Create a Worker from an inlined code string, auto-revoking the blob URL on terminate. */\nexport function createBlobWorker(code: string): Worker {\n const blob = new Blob([code], { type: \"application/javascript\" });\n const url = URL.createObjectURL(blob);\n const worker = new Worker(url);\n const origTerminate = worker.terminate.bind(worker);\n worker.terminate = () => {\n origTerminate();\n URL.revokeObjectURL(url);\n };\n return worker;\n}\n\n/** Create a blob Worker from the inlined compiler worker code. */\nexport function createWorker(): Worker {\n return createBlobWorker(__WORKER_CODE__);\n}\n\n/** Create a blob Worker from the inlined analyzer worker code. */\nexport function createAnalyzerWorker(): Worker {\n return createBlobWorker(__ANALYZER_WORKER_CODE__);\n}\n","export function normalizeUntitledUri(uri: string): string {\n if (!uri.startsWith(\"untitled:\")) return uri;\n return `untitled:${uri.slice(\"untitled:\".length).replace(/^\\/+/, \"\")}`;\n}\n\nexport function normalizePath(path: string): string {\n return path.startsWith(\"/\") ? path : `/${path}`;\n}\n\nexport function normalizeRoot(rootPath: string): string {\n const root = normalizePath(rootPath);\n return root === \"/\" ? \"\" : root.replace(/\\/+$/, \"\");\n}\n","import type { LspDiagnostic } from \"./analyzer-types.js\";\nimport type { TypstAnalyzer } from \"./analyzer.js\";\nimport { normalizePath, normalizeRoot } from \"./uri.js\";\n\nexport type DiagnosticsSubscriber = (diagnostics: LspDiagnostic[]) => void;\n\nexport interface AnalyzerSessionOptions {\n analyzer: Pick<\n TypstAnalyzer,\n \"didOpen\" | \"didChange\" | \"completion\" | \"hover\" | \"onDiagnostics\"\n >;\n /** Project root used to build stable in-memory analyzer URIs. Default: \"/project\". */\n rootPath?: string;\n /** Entry file path within the project. Synced last to ensure dependencies load first. Default: \"/main.typ\". */\n entryPath?: string;\n}\n\n/**\n * Synchronizes an in-memory Typst project with a TypstAnalyzer.\n * Handles multi-file ordering, request queueing, and diagnostic subscriptions.\n *\n * Diagnostics arrive via the analyzer's push mechanism and are forwarded\n * to subscribers registered with `subscribe()`.\n *\n * const session = new AnalyzerSession({ analyzer });\n * session.subscribe(\"/main.typ\", (diags) => { ... });\n * await session.sync(\"/main.typ\", files);\n */\nexport class AnalyzerSession {\n private readonly analyzer: AnalyzerSessionOptions[\"analyzer\"];\n private readonly rootPath: string;\n private readonly entryPath: string;\n private readonly syncedFiles = new Map<string, string>();\n private queue: Promise<void> = Promise.resolve();\n\n // Diagnostic subscription state\n private readonly listenersByUri = new Map<\n string,\n Set<DiagnosticsSubscriber>\n >();\n /** Last push received per URI. Replayed on subscribe() so tab-back shows correct diagnostics instantly. */\n private readonly diagnosticsCache = new Map<string, LspDiagnostic[]>();\n private readonly unsubscribeAnalyzer: () => void;\n\n constructor(options: AnalyzerSessionOptions) {\n this.analyzer = options.analyzer;\n this.rootPath = normalizeRoot(options.rootPath ?? \"/project\");\n this.entryPath = normalizePath(options.entryPath ?? \"/main.typ\");\n\n this.unsubscribeAnalyzer = this.analyzer.onDiagnostics(\n (uri, diagnostics) => {\n this.diagnosticsCache.set(uri, diagnostics);\n const listeners = this.listenersByUri.get(uri);\n if (!listeners) return;\n for (const listener of listeners) listener(diagnostics);\n },\n );\n }\n\n /** Build a tinymist URI from a project-relative path. */\n toUri(path: string): string {\n const root = this.rootPath.replace(/^\\//, \"\");\n return `untitled:${root}${normalizePath(path)}`;\n }\n\n /**\n * Subscribe to push-based diagnostics for a file path.\n * Returns an unsubscribe function.\n */\n subscribe(path: string, listener: DiagnosticsSubscriber): () => void {\n const uri = this.toUri(path);\n\n let listeners = this.listenersByUri.get(uri);\n if (!listeners) {\n listeners = new Set();\n this.listenersByUri.set(uri, listeners);\n }\n listeners.add(listener);\n\n // Replay the last known diagnostics immediately so the UI reflects the\n // correct state without waiting for the next tinymist push.\n const cached = this.diagnosticsCache.get(uri);\n if (cached) listener(cached);\n\n return () => {\n const current = this.listenersByUri.get(uri);\n if (!current) return;\n current.delete(listener);\n if (current.size === 0) this.listenersByUri.delete(uri);\n };\n }\n\n /**\n * Sync all project files with the analyzer.\n * `files` must include the active file's current content under `path`.\n *\n * If the active file's content hasn't changed since the last sync, a\n * lightweight hover is triggered to ensure the analyzer re-analyzes with\n * the current project state and publishes fresh diagnostics.\n */\n async sync(path: string, files: Record<string, string>): Promise<void> {\n await this.enqueue(async () => {\n const changed = await this.syncFiles(path, files);\n\n // When the active file's content is unchanged, the analyzer won't\n // publish fresh diagnostics on its own. A hover request forces it\n // to re-evaluate with the (possibly updated) project context.\n if (!changed) {\n try {\n await this.analyzer.hover(this.toUri(normalizePath(path)), 0, 0);\n } catch {\n /* best-effort — the hover result is unused */\n }\n }\n\n // Clean up files that were removed from the project.\n for (const filePath of this.syncedFiles.keys()) {\n if (!Object.hasOwn(files, filePath)) {\n this.syncedFiles.delete(filePath);\n }\n }\n });\n }\n\n /**\n * Sync files and request completions at the given position.\n * Returns the raw LSP CompletionList/CompletionItem[] from tinymist.\n */\n async completion(\n path: string,\n files: Record<string, string>,\n line: number,\n character: number,\n ): Promise<unknown> {\n return this.enqueue(async () => {\n await this.syncFiles(path, files);\n return this.analyzer.completion(\n this.toUri(normalizePath(path)),\n line,\n character,\n );\n });\n }\n\n /**\n * Sync files and request hover info at the given position.\n * Returns the raw LSP Hover result from tinymist.\n */\n async hover(\n path: string,\n files: Record<string, string>,\n line: number,\n character: number,\n ): Promise<unknown> {\n return this.enqueue(async () => {\n await this.syncFiles(path, files);\n return this.analyzer.hover(\n this.toUri(normalizePath(path)),\n line,\n character,\n );\n });\n }\n\n destroy(): void {\n this.unsubscribeAnalyzer();\n this.listenersByUri.clear();\n this.diagnosticsCache.clear();\n }\n\n /**\n * Sync all project files: dependencies first, active file last.\n * Returns whether the active file's content was actually sent to the analyzer.\n */\n private async syncFiles(\n path: string,\n files: Record<string, string>,\n ): Promise<boolean> {\n const activePath = normalizePath(path);\n for (const filePath of this.orderedPaths(files)) {\n if (filePath === activePath) continue;\n await this.syncFile(filePath, files[filePath]);\n }\n return this.syncFile(activePath, files[activePath]);\n }\n\n /** Sync a single file. Returns true if content was sent to the analyzer. */\n private async syncFile(path: string, content: string): Promise<boolean> {\n const prev = this.syncedFiles.get(path);\n if (prev == null) {\n await this.analyzer.didOpen(this.toUri(path), content);\n } else if (prev !== content) {\n await this.analyzer.didChange(this.toUri(path), content);\n } else {\n return false;\n }\n this.syncedFiles.set(path, content);\n return true;\n }\n\n private orderedPaths(files: Record<string, string>): string[] {\n return Object.keys(files)\n .map((p) => normalizePath(p))\n .sort((a, b) => {\n if (a === this.entryPath) return 1;\n if (b === this.entryPath) return -1;\n return a.localeCompare(b);\n });\n }\n\n private enqueue<T>(task: () => Promise<T>): Promise<T> {\n const run = this.queue.then(task, task);\n this.queue = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n}\n","import * as Comlink from \"comlink\";\nimport { createWorker } from \"./rpc.js\";\nimport type { DiagnosticMessage } from \"./types.js\";\n\ninterface CompilerWorkerAPI {\n init(wasmUrl: string, fontUrls: string[], packages: boolean): Promise<void>;\n compile(\n files: Record<string, string>,\n ): Promise<{ diagnostics: DiagnosticMessage[]; vector?: Uint8Array }>;\n compilePdf(files: Record<string, string>): Promise<Uint8Array>;\n addSource(path: string, source: string): void;\n mapShadow(path: string, content: Uint8Array): void;\n unmapShadow(path: string): void;\n resetShadow(): void;\n destroy(): void;\n}\n\nexport interface CompileResult {\n diagnostics: DiagnosticMessage[];\n /** Vector artifact bytes from the compiler, usable with TypstRenderer for SVG rendering. */\n vector?: Uint8Array;\n}\n\nexport interface TypstCompilerOptions {\n /**\n * Explicit Worker instance. When omitted, an inlined blob worker is created automatically.\n * Use this for Vite apps to get proper source maps:\n * `await TypstCompiler.create({ worker: new Worker(new URL('typst-web-service/worker', import.meta.url)) })`\n */\n worker?: Worker;\n /**\n * URL to the typst-ts-web-compiler WASM binary.\n * Defaults to the matching version on jsDelivr CDN.\n * Override with a local asset URL for offline support or faster load:\n * `new URL('@myriaddreamin/typst-ts-web-compiler/pkg/typst_ts_web_compiler_bg.wasm', import.meta.url).href`\n */\n wasmUrl?: string;\n /** Font URLs to load into the Typst compiler. Defaults to Roboto from jsDelivr. */\n fonts?: string[];\n /**\n * Enable fetching @preview/ packages from packages.typst.org on demand.\n * Default: true.\n */\n packages?: boolean;\n}\n\nconst DEFAULT_FONTS = [\n \"https://cdn.jsdelivr.net/npm/roboto-font@0.1.0/fonts/Roboto/roboto-regular-webfont.ttf\",\n];\n\nconst DEFAULT_WASM_URL =\n \"https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-web-compiler@0.7.0-rc2/pkg/typst_ts_web_compiler_bg.wasm\";\n\nfunction toFiles(\n source: string | Record<string, string>,\n): Record<string, string> {\n return typeof source === \"string\" ? { \"/main.typ\": source } : source;\n}\n\n/**\n * Manages a Typst compiler worker. Create one instance and share it across\n * all extensions (linter, autocomplete, preview, etc.).\n *\n * await TypstCompiler.create() // blob worker, defaults\n * await TypstCompiler.create({ wasmUrl: '...' }) // blob worker, custom WASM\n * await TypstCompiler.create({ worker: myWorker }) // explicit Worker (Vite)\n * await TypstCompiler.create({ worker: myWorker, fonts: [...] }) // explicit Worker + options\n */\nexport class TypstCompiler {\n private readonly proxy: Comlink.Remote<CompilerWorkerAPI>;\n private readonly worker: Worker;\n private readonly encoder = new TextEncoder();\n\n /** The most recent vector artifact from a compile, if any. */\n lastVector?: Uint8Array;\n\n private constructor(\n worker: Worker,\n proxy: Comlink.Remote<CompilerWorkerAPI>,\n ) {\n this.worker = worker;\n this.proxy = proxy;\n }\n\n static async create(\n options: TypstCompilerOptions = {},\n ): Promise<TypstCompiler> {\n const worker = options.worker ?? createWorker();\n const proxy = Comlink.wrap<CompilerWorkerAPI>(worker);\n\n await proxy.init(\n options.wasmUrl ?? DEFAULT_WASM_URL,\n options.fonts ?? DEFAULT_FONTS,\n options.packages ?? true,\n );\n\n return new TypstCompiler(worker, proxy);\n }\n\n /** Compile a single source string (treated as /main.typ) or a map of files. */\n async compile(\n source: string | Record<string, string>,\n ): Promise<CompileResult> {\n const result = await this.proxy.compile(toFiles(source));\n if (result.vector) this.lastVector = result.vector;\n return result;\n }\n\n /** Compile to PDF from a single source string (treated as /main.typ) or a map of files. */\n async compilePdf(\n source: string | Record<string, string>,\n ): Promise<Uint8Array> {\n return this.proxy.compilePdf(toFiles(source));\n }\n\n /** Add or overwrite a text source file. */\n async addSource(path: string, source: string): Promise<void> {\n await this.proxy.addSource(path, source);\n }\n\n /** Add or overwrite an in-memory shadow file (text or binary). */\n async mapShadow(path: string, content: Uint8Array): Promise<void> {\n await this.proxy.mapShadow(path, content);\n }\n\n /** Remove an in-memory shadow file by path. */\n async unmapShadow(path: string): Promise<void> {\n await this.proxy.unmapShadow(path);\n }\n\n /**\n * Clear all shadow files held by the compiler.\n * Note: this also clears files previously added via addSource in the compiler runtime.\n */\n async resetShadow(): Promise<void> {\n await this.proxy.resetShadow();\n }\n\n /** Add or overwrite a text file in the virtual compiler filesystem. */\n async setText(path: string, source: string): Promise<void> {\n await this.mapShadow(path, this.encoder.encode(source));\n }\n\n /** Add or overwrite a JSON file in the virtual compiler filesystem. */\n async setJson(\n path: string,\n value: unknown,\n replacer?: (this: unknown, key: string, value: unknown) => unknown,\n space?: string | number,\n ): Promise<void> {\n await this.setText(path, JSON.stringify(value, replacer, space));\n }\n\n /** Add or overwrite a binary file in the virtual compiler filesystem. */\n async setBinary(\n path: string,\n content: ArrayBuffer | ArrayBufferView,\n ): Promise<void> {\n const bytes =\n content instanceof ArrayBuffer\n ? new Uint8Array(content)\n : new Uint8Array(\n content.buffer,\n content.byteOffset,\n content.byteLength,\n );\n await this.mapShadow(path, bytes);\n }\n\n /** Remove a file from the virtual compiler filesystem. */\n async remove(path: string): Promise<void> {\n await this.unmapShadow(path);\n }\n\n /** Clear all virtual files from the compiler filesystem. */\n async clear(): Promise<void> {\n await this.resetShadow();\n }\n\n destroy(): void {\n this.proxy[Comlink.releaseProxy]();\n this.worker.terminate();\n }\n}\n","export interface FormatConfig {\n /** Number of spaces per indentation level. Default: 2 */\n tab_spaces?: number;\n /** Maximum line width. Default: 80 */\n max_width?: number;\n /** Maximum consecutive blank lines allowed. */\n blank_lines_upper_bound?: number;\n /** Collapse consecutive whitespace in markup to a single space. */\n collapse_markup_spaces?: boolean;\n /** Sort import items alphabetically. */\n reorder_import_items?: boolean;\n /** Wrap text in markup to fit within max_width. Implies collapse_markup_spaces. */\n wrap_text?: boolean;\n}\n\nexport interface FormatRangeResult {\n /** Start index (UTF-16) of the actual formatted range. */\n start: number;\n /** End index (UTF-16) of the actual formatted range. */\n end: number;\n /** The formatted text for the range. */\n text: string;\n}\n\ntype TypstyleModule = typeof import(\"@typstyle/typstyle-wasm-bundler\");\n\nlet typstylePromise: Promise<TypstyleModule> | null = null;\n\nfunction getTypstyle(): Promise<TypstyleModule> {\n if (!typstylePromise) {\n typstylePromise = import(\"@typstyle/typstyle-wasm-bundler\").catch((err) => {\n typstylePromise = null;\n throw err;\n });\n }\n return typstylePromise;\n}\n\n/**\n * Typst code formatter powered by typstyle.\n *\n * Runs on the main thread — typstyle is lightweight and fast.\n * The WASM module is loaded lazily on first format call.\n * Requires a bundler that supports WASM imports (e.g. Vite, webpack).\n *\n * const formatter = TypstFormatter.create({ tab_spaces: 2, max_width: 80 });\n * const formatted = await formatter.format(source);\n */\nexport class TypstFormatter {\n private config: FormatConfig;\n\n private constructor(config: FormatConfig = {}) {\n this.config = config;\n // Eagerly start loading WASM so it's ready by first use.\n // Swallow preload errors — they'll surface on first format() call.\n getTypstyle().catch(() => {});\n }\n\n static create(config: FormatConfig = {}): TypstFormatter {\n return new TypstFormatter(config);\n }\n\n /** Format an entire Typst source string. */\n async format(source: string): Promise<string> {\n const typstyle = await getTypstyle();\n return typstyle.format(source, this.config);\n }\n\n /** Format a range within a Typst source string. Indices are UTF-16 code units. */\n async formatRange(\n source: string,\n start: number,\n end: number,\n ): Promise<FormatRangeResult> {\n const typstyle = await getTypstyle();\n const result = typstyle.format_range(source, start, end, this.config);\n return { start: result.start, end: result.end, text: result.text };\n }\n}\n","/** Minimal interface for the built TypstRenderer instance. */\nexport interface RendererInstance {\n free(): void;\n create_session(): RendererSession;\n manipulate_data(\n session: RendererSession,\n action: string,\n data: Uint8Array,\n ): void;\n svg_data(session: RendererSession): string;\n}\n\n/** Minimal interface for a TypstRenderer session. */\nexport interface RendererSession {\n free(): void;\n}\n\ntype RendererWasmModule = typeof import(\"@myriaddreamin/typst-ts-renderer\");\n\nconst DEFAULT_RENDERER_WASM_URL =\n \"https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-renderer@0.7.0-rc2/pkg/typst_ts_renderer_bg.wasm\";\n\nlet rendererModulePromise: Promise<RendererWasmModule> | null = null;\n\nfunction getRendererModule(): Promise<RendererWasmModule> {\n if (!rendererModulePromise) {\n rendererModulePromise = import(\"@myriaddreamin/typst-ts-renderer\").catch(\n (err) => {\n rendererModulePromise = null;\n throw err;\n },\n );\n }\n return rendererModulePromise;\n}\n\nexport interface TypstRendererOptions {\n /** URL to the typst-ts-renderer WASM binary. Defaults to jsDelivr CDN. */\n wasmUrl?: string;\n}\n\n/**\n * Converts Typst vector artifacts to SVG strings.\n *\n * The renderer WASM module is loaded lazily on first use.\n *\n * const renderer = TypstRenderer.create();\n * const svg = await renderer.renderSvg(vector);\n */\nexport class TypstRenderer {\n private wasmUrl: string;\n private instance: Promise<RendererInstance> | null = null;\n\n private constructor(options: TypstRendererOptions = {}) {\n this.wasmUrl = options.wasmUrl ?? DEFAULT_RENDERER_WASM_URL;\n // Eagerly start loading the WASM module so it's ready by first use.\n getRendererModule().catch(() => {});\n }\n\n static create(options: TypstRendererOptions = {}): TypstRenderer {\n return new TypstRenderer(options);\n }\n\n private getInstance(): Promise<RendererInstance> {\n if (!this.instance) {\n this.instance = this.#init().catch((err) => {\n this.instance = null;\n throw err;\n });\n }\n return this.instance;\n }\n\n async #init(): Promise<RendererInstance> {\n const mod = await getRendererModule();\n await mod.default(this.wasmUrl);\n return new mod.TypstRendererBuilder().build();\n }\n\n /** Free the underlying WASM renderer instance. */\n async destroy(): Promise<void> {\n const instance = this.instance;\n this.instance = null;\n if (instance) {\n (await instance).free();\n }\n }\n\n /** Render a Typst vector artifact to an SVG string. */\n async renderSvg(vector: Uint8Array): Promise<string> {\n const renderer = await this.getInstance();\n const session = renderer.create_session();\n try {\n renderer.manipulate_data(session, \"reset\", vector);\n return renderer.svg_data(session);\n } finally {\n session.free();\n }\n }\n}\n"],"mappings":"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","names":["proxy","Comlink","proxy"]}
package/dist/worker.js CHANGED
@@ -1567,6 +1567,9 @@ var init_wasm_pack_shim = __esm({
1567
1567
  }
1568
1568
  });
1569
1569
 
1570
+ // src/worker.ts
1571
+ import * as Comlink from "comlink";
1572
+
1570
1573
  // ../../node_modules/.bun/@myriaddreamin+typst.ts@0.7.0-rc2+5e675a3c06b42b50/node_modules/@myriaddreamin/typst.ts/dist/esm/init.mjs
1571
1574
  var ComponentBuilder = class {
1572
1575
  loadedFonts = /* @__PURE__ */ new Set();
@@ -1732,35 +1735,35 @@ function loadFonts(userFonts, options) {
1732
1735
  loader._kind = "fontLoader";
1733
1736
  return loader;
1734
1737
  }
1735
- function withPackageRegistry(packageRegistry2) {
1738
+ function withPackageRegistry(packageRegistry) {
1736
1739
  return async (_, { builder }) => {
1737
1740
  return new Promise((resolve) => {
1738
- builder.set_package_registry(packageRegistry2, function(spec) {
1739
- return packageRegistry2.resolve(spec, this);
1741
+ builder.set_package_registry(packageRegistry, function(spec) {
1742
+ return packageRegistry.resolve(spec, this);
1740
1743
  });
1741
1744
  resolve();
1742
1745
  });
1743
1746
  };
1744
1747
  }
1745
- function withAccessModel(accessModel2) {
1748
+ function withAccessModel(accessModel) {
1746
1749
  return async (_, ctx) => {
1747
1750
  if (ctx.alreadySetAccessModel) {
1748
1751
  throw new Error(`already set some assess model before: ${ctx.alreadySetAccessModel.constructor?.name}(${ctx.alreadySetAccessModel})`);
1749
1752
  }
1750
- ctx.alreadySetAccessModel = accessModel2;
1753
+ ctx.alreadySetAccessModel = accessModel;
1751
1754
  return new Promise((resolve) => {
1752
- ctx.builder.set_access_model(accessModel2, (path) => {
1753
- const lastModified = accessModel2.getMTime(path);
1755
+ ctx.builder.set_access_model(accessModel, (path) => {
1756
+ const lastModified = accessModel.getMTime(path);
1754
1757
  if (lastModified) {
1755
1758
  return lastModified.getTime();
1756
1759
  }
1757
1760
  return 0;
1758
1761
  }, (path) => {
1759
- return accessModel2.isFile(path) || false;
1762
+ return accessModel.isFile(path) || false;
1760
1763
  }, (path) => {
1761
- return accessModel2.getRealPath(path) || path;
1764
+ return accessModel.getRealPath(path) || path;
1762
1765
  }, (path) => {
1763
- return accessModel2.readAll(path);
1766
+ return accessModel.readAll(path);
1764
1767
  });
1765
1768
  resolve();
1766
1769
  });
@@ -2099,157 +2102,102 @@ var FetchPackageRegistry = class {
2099
2102
  }
2100
2103
  };
2101
2104
 
2102
- // src/queue.ts
2103
- var yieldToEventLoop = () => new Promise((r) => setTimeout(r, 0));
2104
- function makeQueue(handle, onCancel) {
2105
- let pending = null;
2106
- let processing = false;
2107
- async function drain() {
2108
- processing = true;
2109
- while (pending) {
2110
- const req = pending;
2111
- pending = null;
2112
- await yieldToEventLoop();
2113
- if (pending) {
2114
- onCancel?.(req);
2115
- continue;
2116
- }
2117
- try {
2118
- await handle(req);
2119
- } catch {
2120
- }
2121
- }
2122
- processing = false;
2123
- }
2124
- return (req) => {
2125
- pending = req;
2126
- if (!processing) drain();
2127
- };
2128
- }
2129
-
2130
- // src/worker-utils.ts
2131
- function postError(id, err) {
2132
- const message = err instanceof Error ? err.message : String(err);
2133
- self.postMessage({ type: "error", id, message });
2134
- }
2135
-
2136
2105
  // src/worker.ts
2137
2106
  var MAIN_FILE = "/main.typ";
2138
- var accessModel = new MemoryAccessModel();
2139
- var packageRegistry = new FetchPackageRegistry(accessModel);
2140
- var compiler = null;
2141
- async function initCompiler(wasmUrl, fontUrls, packages) {
2142
- compiler = createTypstCompiler();
2143
- await compiler.init({
2144
- getModule: () => wasmUrl,
2145
- beforeBuild: [
2146
- loadFonts(fontUrls),
2147
- ...packages ? [withAccessModel(accessModel), withPackageRegistry(packageRegistry)] : []
2148
- ]
2149
- });
2150
- }
2151
- function parseRange(range) {
2152
- const m = range.match(/(\d+):(\d+)-(\d+):(\d+)/);
2153
- if (!m) {
2154
- console.warn(
2155
- `[typst-web-service] Skipping diagnostic with unrecognized range format: ${JSON.stringify(range)}`
2156
- );
2157
- return null;
2158
- }
2159
- return { startLine: +m[1], startCol: +m[2], endLine: +m[3], endCol: +m[4] };
2160
- }
2161
- function addSources(files) {
2162
- if (!compiler) throw new Error("Compiler not initialized");
2163
- for (const [path, source] of Object.entries(files)) {
2164
- compiler.addSource(path, source);
2107
+ var CompilerWorker = class {
2108
+ compiler = null;
2109
+ accessModel = new MemoryAccessModel();
2110
+ packageRegistry;
2111
+ constructor() {
2112
+ this.packageRegistry = new FetchPackageRegistry(this.accessModel);
2113
+ }
2114
+ async init(wasmUrl, fontUrls, packages) {
2115
+ this.compiler = createTypstCompiler();
2116
+ await this.compiler.init({
2117
+ getModule: () => wasmUrl,
2118
+ beforeBuild: [
2119
+ loadFonts(fontUrls),
2120
+ ...packages ? [
2121
+ withAccessModel(this.accessModel),
2122
+ withPackageRegistry(this.packageRegistry)
2123
+ ] : []
2124
+ ]
2125
+ });
2165
2126
  }
2166
- }
2167
- async function compile(files) {
2168
- addSources(files);
2169
- const result = await compiler.compile({
2170
- mainFilePath: MAIN_FILE,
2171
- diagnostics: "full"
2172
- });
2173
- const diagnostics = (result.diagnostics ?? []).flatMap(
2174
- (d) => {
2175
- const range = parseRange(d.range);
2176
- if (!range) return [];
2177
- return [
2178
- { ...d, severity: d.severity, range }
2179
- ];
2127
+ async compile(files) {
2128
+ if (!this.compiler) throw new Error("Compiler not initialized");
2129
+ for (const [path, source] of Object.entries(files)) {
2130
+ this.compiler.addSource(path, source);
2180
2131
  }
2181
- );
2182
- return { diagnostics, vector: result.result ?? void 0 };
2183
- }
2184
- function transferBuffer(data) {
2185
- return data.buffer.slice(
2186
- data.byteOffset,
2187
- data.byteOffset + data.byteLength
2188
- );
2189
- }
2190
- function postCancelled(req) {
2191
- self.postMessage({
2192
- type: "cancelled",
2193
- id: req.id
2194
- });
2195
- }
2196
- var enqueueCompile = makeQueue(async (req) => {
2197
- try {
2198
- const { diagnostics, vector: vectorData } = await compile(req.files);
2199
- const vector = vectorData ? transferBuffer(vectorData) : void 0;
2200
- const msg = {
2201
- type: "result",
2202
- id: req.id,
2203
- diagnostics,
2204
- vector
2205
- };
2206
- self.postMessage(msg, vector ? [vector] : []);
2207
- } catch (err) {
2208
- postError(req.id, err);
2132
+ const result = await this.compiler.compile({
2133
+ mainFilePath: MAIN_FILE,
2134
+ diagnostics: "full"
2135
+ });
2136
+ const diagnostics = (result.diagnostics ?? []).flatMap(
2137
+ (d) => {
2138
+ const m = d.range.match(/(\d+):(\d+)-(\d+):(\d+)/);
2139
+ if (!m) {
2140
+ console.warn(
2141
+ `[typst-web-service] Skipping diagnostic with unrecognized range format: ${JSON.stringify(d.range)}`
2142
+ );
2143
+ return [];
2144
+ }
2145
+ return [
2146
+ {
2147
+ ...d,
2148
+ severity: d.severity,
2149
+ range: {
2150
+ startLine: +m[1],
2151
+ startCol: +m[2],
2152
+ endLine: +m[3],
2153
+ endCol: +m[4]
2154
+ }
2155
+ }
2156
+ ];
2157
+ }
2158
+ );
2159
+ const vector = result.result ?? void 0;
2160
+ if (vector) {
2161
+ return Comlink.transfer({ diagnostics, vector }, [
2162
+ vector.buffer
2163
+ ]);
2164
+ }
2165
+ return { diagnostics };
2209
2166
  }
2210
- }, postCancelled);
2211
- var enqueueRender = makeQueue(async (req) => {
2212
- try {
2213
- addSources(req.files);
2214
- const result = await compiler.compile({
2167
+ async compilePdf(files) {
2168
+ if (!this.compiler) throw new Error("Compiler not initialized");
2169
+ for (const [path, source] of Object.entries(files)) {
2170
+ this.compiler.addSource(path, source);
2171
+ }
2172
+ const result = await this.compiler.compile({
2215
2173
  mainFilePath: MAIN_FILE,
2216
2174
  format: CompileFormatEnum.pdf,
2217
2175
  diagnostics: "none"
2218
2176
  });
2219
2177
  if (!result.result) throw new Error("Compilation produced no output");
2220
- const data = transferBuffer(result.result);
2221
- self.postMessage(
2222
- { type: "pdf", id: req.id, data },
2223
- [data]
2224
- );
2225
- } catch (err) {
2226
- postError(req.id, err);
2178
+ return Comlink.transfer(result.result, [
2179
+ result.result.buffer
2180
+ ]);
2227
2181
  }
2228
- }, postCancelled);
2229
- self.onmessage = async (e) => {
2230
- const req = e.data;
2231
- if (req.type === "init") {
2232
- try {
2233
- await initCompiler(req.wasmUrl, req.fonts, req.packages);
2234
- self.postMessage({ type: "ready", id: req.id });
2235
- } catch (err) {
2236
- postError(req.id, err);
2237
- }
2238
- return;
2182
+ addSource(path, source) {
2183
+ if (!this.compiler) throw new Error("Compiler not initialized");
2184
+ this.compiler.addSource(path, source);
2239
2185
  }
2240
- if (req.type === "compile") {
2241
- enqueueCompile(req);
2242
- return;
2186
+ mapShadow(path, content) {
2187
+ if (!this.compiler) throw new Error("Compiler not initialized");
2188
+ this.compiler.mapShadow(path, content);
2243
2189
  }
2244
- if (req.type === "render") {
2245
- enqueueRender(req);
2246
- return;
2190
+ unmapShadow(path) {
2191
+ if (!this.compiler) throw new Error("Compiler not initialized");
2192
+ this.compiler.unmapShadow(path);
2247
2193
  }
2248
- if (req.type === "destroy") {
2249
- self.postMessage({
2250
- type: "destroyed",
2251
- id: req.id
2252
- });
2194
+ resetShadow() {
2195
+ if (!this.compiler) throw new Error("Compiler not initialized");
2196
+ this.compiler.resetShadow();
2197
+ }
2198
+ destroy() {
2199
+ this.compiler = null;
2253
2200
  }
2254
2201
  };
2202
+ Comlink.expose(new CompilerWorker());
2255
2203
  //# sourceMappingURL=worker.js.map