@vedivad/typst-web-service 0.7.11 → 0.8.0

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/compiler.ts","../src/formatter.ts","../src/project.ts","../src/renderer.ts"],"sourcesContent":["import * as Comlink from \"comlink\";\nimport { createAnalyzerWorker } from \"./rpc.js\";\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(wasmUrl: string): 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 didChangeMany(\n opens: Array<{ uri: string; content: string }>,\n changes: Array<{ uri: string; version: number; content: string }>,\n ): Promise<void>;\n didCloseMany(uris: 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 * completion and hover for Typst documents.\n *\n * const analyzer = await TypstAnalyzer.create({ wasmUrl: '...' });\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\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(absoluteWasmUrl);\n\n return analyzer;\n }\n\n async didOpen(uri: string, content: string): Promise<void> {\n this.openedUris.add(uri);\n await this.proxy.didOpen(uri, content);\n }\n\n async didClose(uri: string): Promise<void> {\n if (!this.openedUris.delete(uri)) return;\n await this.proxy.didClose(uri);\n }\n\n /**\n * Notify the analyzer that a document has changed.\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 /**\n * Batch document changes. Splits inputs into opens (first-time URIs) and\n * changes (already-open URIs) and sends them in a single worker roundtrip.\n */\n async didChangeMany(docs: Record<string, string>): Promise<void> {\n const opens: Array<{ uri: string; content: string }> = [];\n const changes: Array<{ uri: string; version: number; content: string }> =\n [];\n for (const [uri, content] of Object.entries(docs)) {\n if (this.openedUris.has(uri)) {\n changes.push({ uri, version: ++this.versionCounter, content });\n } else {\n opens.push({ uri, content });\n this.openedUris.add(uri);\n }\n }\n if (opens.length === 0 && changes.length === 0) return;\n await this.proxy.didChangeMany(opens, changes);\n }\n\n /**\n * Batch document closes. Filters to currently-open URIs and sends the set\n * in a single worker roundtrip.\n */\n async didCloseMany(uris: string[]): Promise<void> {\n const toClose: string[] = [];\n for (const uri of uris) {\n if (this.openedUris.delete(uri)) toClose.push(uri);\n }\n if (toClose.length === 0) return;\n await this.proxy.didCloseMany(toClose);\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.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 * 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 entry?: string,\n ): Promise<{ diagnostics: DiagnosticMessage[]; vector?: Uint8Array }>;\n compilePdf(\n files?: Record<string, string>,\n entry?: string,\n ): Promise<Uint8Array>;\n mapShadow(path: string, content: Uint8Array): void;\n mapShadowMany(files: Record<string, 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\nconst DEFAULT_ENTRY = \"/main.typ\";\n\nfunction toFiles(\n source?: string | Record<string, string>,\n entry?: string,\n): Record<string, string> | undefined {\n if (source === undefined) return undefined;\n return typeof source === \"string\"\n ? { [entry ?? DEFAULT_ENTRY]: source }\n : 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 /**\n * Compile. Pass a source string (written to the entry path) or a map of\n * files to register before compiling; omit to compile whatever is currently\n * in the VFS (populated via setText/setBinary/setJson/setMany).\n * Defaults to compiling \"/main.typ\"; override with `entry`.\n */\n async compile(\n source?: string | Record<string, string>,\n entry?: string,\n ): Promise<CompileResult> {\n const result = await this.proxy.compile(toFiles(source, entry), entry);\n if (result.vector) this.lastVector = result.vector;\n return result;\n }\n\n /**\n * Compile to PDF. Pass a source string (written to the entry path) or a map\n * of files to register before compiling; omit to compile whatever is\n * currently in the VFS (populated via setText/setBinary/setJson/setMany).\n * Defaults to compiling \"/main.typ\"; override with `entry`.\n */\n compilePdf(\n source?: string | Record<string, string>,\n entry?: string,\n ): Promise<Uint8Array> {\n return this.proxy.compilePdf(toFiles(source, entry), entry);\n }\n\n /** Add or overwrite a text file in the virtual compiler filesystem. */\n setText(path: string, source: string): Promise<void> {\n return this.proxy.mapShadow(path, this.encoder.encode(source));\n }\n\n /** Add or overwrite a JSON file in the virtual compiler filesystem. */\n setJson(\n path: string,\n value: unknown,\n replacer?: (this: unknown, key: string, value: unknown) => unknown,\n space?: string | number,\n ): Promise<void> {\n return this.setText(path, JSON.stringify(value, replacer, space));\n }\n\n /**\n * Add or overwrite multiple files in the virtual compiler filesystem in a\n * single worker roundtrip. Strings are UTF-8 encoded; Uint8Arrays are passed\n * through.\n */\n setMany(files: Record<string, string | Uint8Array>): Promise<void> {\n const encoded: Record<string, Uint8Array> = {};\n for (const [path, content] of Object.entries(files)) {\n encoded[path] =\n typeof content === \"string\" ? this.encoder.encode(content) : content;\n }\n return this.proxy.mapShadowMany(encoded);\n }\n\n /** Add or overwrite a binary file in the virtual compiler filesystem. */\n 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 return this.proxy.mapShadow(path, bytes);\n }\n\n /** Remove a file from the virtual compiler filesystem. */\n remove(path: string): Promise<void> {\n return this.proxy.unmapShadow(path);\n }\n\n /** Clear all virtual files from the compiler filesystem. */\n clear(): Promise<void> {\n return this.proxy.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","import type { TypstAnalyzer } from \"./analyzer.js\";\nimport type { CompileResult, TypstCompiler } from \"./compiler.js\";\nimport { normalizePath, normalizeRoot } from \"./uri.js\";\n\nexport interface TypstProjectOptions {\n compiler: TypstCompiler;\n /**\n * Optional analyzer. When provided, text file operations also sync with the\n * analyzer so completions / hover reflect the current state.\n */\n analyzer?: TypstAnalyzer;\n /** Default entry file path. Default: \"/main.typ\". */\n entry?: string;\n /**\n * Project root used to build stable analyzer URIs. Default: \"/project\".\n * URIs are formed as `untitled:<root-without-leading-slash><path>` — so\n * `/main.typ` becomes `untitled:project/main.typ`.\n */\n rootPath?: string;\n}\n\nconst DEFAULT_ENTRY = \"/main.typ\";\nconst DEFAULT_ROOT = \"/project\";\n\n/**\n * Coordinates a compiler + analyzer pair for multi-file Typst projects.\n *\n * Owns the project's virtual filesystem state. Editors push incremental\n * `setText` updates as the user types; the project mirrors those edits to\n * both the compiler's shadow VFS and the analyzer's open-document set, then\n * compiles or services LSP requests against the current state.\n *\n * const project = new TypstProject({ compiler, analyzer });\n * await project.setMany({ \"/main.typ\": \"...\", \"/utils.typ\": \"...\" });\n * const result = await project.compile();\n */\nexport class TypstProject {\n private readonly compiler: TypstCompiler;\n private readonly analyzer?: TypstAnalyzer;\n private readonly rootPath: string;\n private readonly trackedTextPaths = new Set<string>();\n /** Last content written via setText/setMany, per path. Used to skip redundant writes to compiler + analyzer. */\n private readonly lastSyncedContent = new Map<string, string>();\n private _entry: string;\n\n constructor(options: TypstProjectOptions) {\n this.compiler = options.compiler;\n this.analyzer = options.analyzer;\n this.rootPath = normalizeRoot(options.rootPath ?? DEFAULT_ROOT);\n this._entry = normalizePath(options.entry ?? DEFAULT_ENTRY);\n }\n\n /** Current entry file path. */\n get entry(): string {\n return this._entry;\n }\n\n /** Whether an analyzer is attached. */\n get hasAnalyzer(): boolean {\n return this.analyzer !== undefined;\n }\n\n /** Change the sticky entry file used by subsequent compile() calls. */\n setEntry(path: string): void {\n this._entry = normalizePath(path);\n }\n\n /**\n * Add or overwrite a text file. Goes to the compiler's VFS and, when an\n * analyzer is attached, to the analyzer as a document change. Redundant\n * calls with unchanged content are skipped.\n */\n async setText(path: string, content: string): Promise<void> {\n const p = normalizePath(path);\n this.trackedTextPaths.add(p);\n if (this.lastSyncedContent.get(p) === content) return;\n this.lastSyncedContent.set(p, content);\n const ops: Array<Promise<void>> = [this.compiler.setText(p, content)];\n if (this.analyzer) {\n ops.push(this.analyzer.didChange(this.toUri(p), content));\n }\n await Promise.all(ops);\n }\n\n /**\n * Add or overwrite a JSON file. Compiler-only — the analyzer does not track\n * data files.\n */\n setJson(path: string, value: unknown): Promise<void> {\n return this.compiler.setJson(normalizePath(path), value);\n }\n\n /** Add or overwrite a binary file. Compiler-only. */\n setBinary(\n path: string,\n content: ArrayBuffer | ArrayBufferView,\n ): Promise<void> {\n return this.compiler.setBinary(normalizePath(path), content);\n }\n\n /**\n * Batch set multiple files. Strings route to both compiler and analyzer;\n * Uint8Array entries go to the compiler only.\n */\n async setMany(files: Record<string, string | Uint8Array>): Promise<void> {\n const normalized: Record<string, string | Uint8Array> = {};\n for (const [path, content] of Object.entries(files)) {\n normalized[normalizePath(path)] = content;\n }\n const compilerOp = this.compiler.setMany(normalized);\n const analyzerOp = (async () => {\n if (!this.analyzer) return;\n const docs: Record<string, string> = {};\n for (const [path, content] of Object.entries(normalized)) {\n if (typeof content !== \"string\") continue;\n this.trackedTextPaths.add(path);\n if (this.lastSyncedContent.get(path) === content) continue;\n this.lastSyncedContent.set(path, content);\n docs[this.toUri(path)] = content;\n }\n if (Object.keys(docs).length > 0) {\n await this.analyzer.didChangeMany(docs);\n }\n })();\n await Promise.all([compilerOp, analyzerOp]);\n }\n\n /**\n * Remove a file. Always removed from the compiler's VFS; also closed on the\n * analyzer when it was previously tracked as text.\n */\n async remove(path: string): Promise<void> {\n const p = normalizePath(path);\n const wasText = this.trackedTextPaths.delete(p);\n this.lastSyncedContent.delete(p);\n const ops: Array<Promise<void>> = [this.compiler.remove(p)];\n if (this.analyzer && wasText) {\n ops.push(this.analyzer.didClose(this.toUri(p)));\n }\n await Promise.all(ops);\n }\n\n /** Clear all files from both compiler VFS and analyzer document set. */\n async clear(): Promise<void> {\n const uris = Array.from(this.trackedTextPaths, (p) => this.toUri(p));\n this.trackedTextPaths.clear();\n this.lastSyncedContent.clear();\n const compilerOp = this.compiler.clear();\n const analyzerOp =\n this.analyzer && uris.length > 0\n ? this.analyzer.didCloseMany(uris)\n : Promise.resolve();\n await Promise.all([compilerOp, analyzerOp]);\n }\n\n /** Compile the current VFS state using the sticky entry. */\n compile(): Promise<CompileResult> {\n return this.compiler.compile(undefined, this._entry);\n }\n\n /** Compile the current VFS state to PDF using the sticky entry. */\n compilePdf(): Promise<Uint8Array> {\n return this.compiler.compilePdf(undefined, this._entry);\n }\n\n private requireAnalyzer(operation: string): TypstAnalyzer {\n if (!this.analyzer) {\n throw new Error(`TypstProject: ${operation} requires an analyzer`);\n }\n return this.analyzer;\n }\n\n /** Request completions at the given position. Throws when no analyzer is attached. */\n completion(path: string, line: number, character: number): Promise<unknown> {\n return this.requireAnalyzer(\"completion\").completion(\n this.toUri(normalizePath(path)),\n line,\n character,\n );\n }\n\n /** Request hover info at the given position. Throws when no analyzer is attached. */\n hover(path: string, line: number, character: number): Promise<unknown> {\n return this.requireAnalyzer(\"hover\").hover(\n this.toUri(normalizePath(path)),\n line,\n character,\n );\n }\n\n private toUri(path: string): string {\n const root = this.rootPath.replace(/^\\//, \"\");\n return `untitled:${root}${normalizePath(path)}`;\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({ module_or_path: 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":["Comlink","DEFAULT_ENTRY"]}
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,115 @@ 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 });
2105
+ // src/diagnostics-sort.ts
2106
+ function sortDiagnosticsByFileAndRange(diagnostics) {
2107
+ return [...diagnostics].sort(
2108
+ (a, b) => a.path.localeCompare(b.path) || a.range.startLine - b.range.startLine || a.range.startCol - b.range.startCol || a.range.endLine - b.range.endLine || a.range.endCol - b.range.endCol || a.message.localeCompare(b.message)
2109
+ );
2134
2110
  }
2135
2111
 
2136
2112
  // src/worker.ts
2137
2113
  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);
2114
+ var CompilerWorker = class {
2115
+ compiler = null;
2116
+ accessModel = new MemoryAccessModel();
2117
+ packageRegistry;
2118
+ constructor() {
2119
+ this.packageRegistry = new FetchPackageRegistry(this.accessModel);
2120
+ }
2121
+ async init(wasmUrl, fontUrls, packages) {
2122
+ this.compiler = createTypstCompiler();
2123
+ await this.compiler.init({
2124
+ getModule: () => wasmUrl,
2125
+ beforeBuild: [
2126
+ loadFonts(fontUrls),
2127
+ ...packages ? [
2128
+ withAccessModel(this.accessModel),
2129
+ withPackageRegistry(this.packageRegistry)
2130
+ ] : []
2131
+ ]
2132
+ });
2165
2133
  }
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
- ];
2134
+ async compile(files, entry) {
2135
+ if (!this.compiler) throw new Error("Compiler not initialized");
2136
+ if (files) {
2137
+ for (const [path, source] of Object.entries(files)) {
2138
+ this.compiler.addSource(path, source);
2139
+ }
2180
2140
  }
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);
2141
+ const result = await this.compiler.compile({
2142
+ mainFilePath: entry ?? MAIN_FILE,
2143
+ diagnostics: "full"
2144
+ });
2145
+ const diagnostics = sortDiagnosticsByFileAndRange(
2146
+ (result.diagnostics ?? []).flatMap((d) => {
2147
+ const m = d.range.match(/(\d+):(\d+)-(\d+):(\d+)/);
2148
+ if (!m) {
2149
+ console.warn(
2150
+ `[typst-web-service] Skipping diagnostic with unrecognized range format: ${JSON.stringify(d.range)}`
2151
+ );
2152
+ return [];
2153
+ }
2154
+ return [
2155
+ {
2156
+ ...d,
2157
+ severity: d.severity,
2158
+ range: {
2159
+ startLine: +m[1],
2160
+ startCol: +m[2],
2161
+ endLine: +m[3],
2162
+ endCol: +m[4]
2163
+ }
2164
+ }
2165
+ ];
2166
+ })
2167
+ );
2168
+ const vector = result.result ?? void 0;
2169
+ if (vector) {
2170
+ return Comlink.transfer({ diagnostics, vector }, [
2171
+ vector.buffer
2172
+ ]);
2173
+ }
2174
+ return { diagnostics };
2209
2175
  }
2210
- }, postCancelled);
2211
- var enqueueRender = makeQueue(async (req) => {
2212
- try {
2213
- addSources(req.files);
2214
- const result = await compiler.compile({
2215
- mainFilePath: MAIN_FILE,
2176
+ async compilePdf(files, entry) {
2177
+ if (!this.compiler) throw new Error("Compiler not initialized");
2178
+ if (files) {
2179
+ for (const [path, source] of Object.entries(files)) {
2180
+ this.compiler.addSource(path, source);
2181
+ }
2182
+ }
2183
+ const result = await this.compiler.compile({
2184
+ mainFilePath: entry ?? MAIN_FILE,
2216
2185
  format: CompileFormatEnum.pdf,
2217
2186
  diagnostics: "none"
2218
2187
  });
2219
2188
  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);
2189
+ return Comlink.transfer(result.result, [
2190
+ result.result.buffer
2191
+ ]);
2227
2192
  }
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);
2193
+ mapShadow(path, content) {
2194
+ if (!this.compiler) throw new Error("Compiler not initialized");
2195
+ this.compiler.mapShadow(path, content);
2196
+ }
2197
+ mapShadowMany(files) {
2198
+ if (!this.compiler) throw new Error("Compiler not initialized");
2199
+ for (const [path, content] of Object.entries(files)) {
2200
+ this.compiler.mapShadow(path, content);
2237
2201
  }
2238
- return;
2239
2202
  }
2240
- if (req.type === "compile") {
2241
- enqueueCompile(req);
2242
- return;
2203
+ unmapShadow(path) {
2204
+ if (!this.compiler) throw new Error("Compiler not initialized");
2205
+ this.compiler.unmapShadow(path);
2243
2206
  }
2244
- if (req.type === "render") {
2245
- enqueueRender(req);
2246
- return;
2207
+ resetShadow() {
2208
+ if (!this.compiler) throw new Error("Compiler not initialized");
2209
+ this.compiler.resetShadow();
2247
2210
  }
2248
- if (req.type === "destroy") {
2249
- self.postMessage({
2250
- type: "destroyed",
2251
- id: req.id
2252
- });
2211
+ destroy() {
2212
+ this.compiler = null;
2253
2213
  }
2254
2214
  };
2215
+ Comlink.expose(new CompilerWorker());
2255
2216
  //# sourceMappingURL=worker.js.map