@vedivad/typst-web-service 0.7.12 → 0.8.1

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/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"]}
1
+ {"version":3,"sources":["../src/analyzer.ts","../src/rpc.ts","../src/identifiers.ts","../src/compiler.ts","../src/formatter.ts","../src/project.ts","../src/renderer.ts"],"sourcesContent":["import * as Comlink from \"comlink\";\nimport type { LspCompletionResponse, LspHover } from \"./analyzer-types.js\";\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(\n uri: string,\n line: number,\n character: number,\n ): Promise<LspCompletionResponse>;\n hover(uri: string, line: number, character: number): Promise<LspHover | null>;\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<LspCompletionResponse> {\n return this.proxy.completion(uri, line, character);\n }\n\n async hover(\n uri: string,\n line: number,\n character: number,\n ): Promise<LspHover | null> {\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","/**\n * Two addressing schemes live in this codebase. This file is the only place\n * that knows how they relate. Keeping the vocabulary and conversions colocated\n * means a new contributor can read one file and understand the model.\n *\n * 1. Path — \"/main.typ\". Always leading-slash, always forward slashes.\n * Used by the compiler VFS, the `TypstProject` public API,\n * and the `typstFilePath` facet. Addresses a file within\n * the project.\n *\n * 2. AnalyzerUri — \"untitled:project/main.typ\". What tinymist's LSP expects.\n * Derived from a Path plus the analyzer URI root.\n *\n * The types below are type aliases, not branded/opaque types. That's\n * deliberate: callers can pass string literals where a Path is expected\n * without wrapping, and the aliases only serve as self-documenting\n * signatures. Ingress points still need to call `normalizePath` to defend\n * against un-normalized input — the alias does not imply the string has\n * been normalized.\n */\n\n/** `/path/to/file.typ` — leading-slash, forward slashes only. */\nexport type Path = string;\n\n/** `untitled:project/path/to/file.typ` — what tinymist's LSP consumes. */\nexport type AnalyzerUri = string;\n\n/** Ensure a path starts with a leading slash. Idempotent. */\nexport function normalizePath(path: string): Path {\n return path.startsWith(\"/\") ? path : `/${path}`;\n}\n\n/**\n * Normalize an analyzer URI root. Ensures leading slash, strips trailing\n * slashes. Special-cases \"/\" → \"\" so URIs don't end up with a leading\n * `untitled://`.\n */\nexport function normalizeRoot(rootPath: string): string {\n const root = normalizePath(rootPath);\n return root === \"/\" ? \"\" : root.replace(/\\/+$/, \"\");\n}\n\n/**\n * Build the analyzer URI for a given project path. `root` is expected to\n * already be normalized (see `normalizeRoot`).\n *\n * pathToAnalyzerUri(\"/main.typ\", \"/project\") -> \"untitled:project/main.typ\"\n */\nexport function pathToAnalyzerUri(path: Path, root: string): AnalyzerUri {\n const bare = root.replace(/^\\//, \"\");\n return `untitled:${bare}${normalizePath(path)}`;\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 entry?: string,\n ): Promise<{ diagnostics: DiagnosticMessage[]; vector?: Uint8Array }>;\n compilePdf(entry?: string): 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\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 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 whatever is currently in the VFS (populated via\n * setText/setBinary/setJson/setMany). Defaults to compiling \"/main.typ\";\n * override with `entry`.\n */\n compile(entry?: string): Promise<CompileResult> {\n return this.proxy.compile(entry);\n }\n\n /**\n * Compile to PDF. Operates on whatever is currently in the VFS (populated\n * via setText/setBinary/setJson/setMany). Defaults to compiling \"/main.typ\";\n * override with `entry`.\n */\n compilePdf(entry?: string): Promise<Uint8Array> {\n return this.proxy.compilePdf(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 { LspCompletionResponse, LspHover } from \"./analyzer-types.js\";\nimport type { CompileResult, TypstCompiler } from \"./compiler.js\";\nimport {\n normalizePath,\n normalizeRoot,\n type Path,\n pathToAnalyzerUri,\n} from \"./identifiers.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 * Prefix used to build the `untitled:` URIs handed to the analyzer.\n * Default: \"/project\". A path of `/main.typ` becomes\n * `untitled:project/main.typ`. Only affects URI construction — the compiler\n * and project VFS use the raw paths unchanged.\n */\n analyzerUriRoot?: 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 type CompileListener = (result: CompileResult) => void;\n\nfunction errorAsCompileResult(err: unknown, path: string): CompileResult {\n return {\n diagnostics: [\n {\n package: \"\",\n path,\n severity: \"Error\",\n range: { startLine: 0, startCol: 0, endLine: 0, endCol: 1 },\n message: err instanceof Error ? err.message : String(err),\n },\n ],\n };\n}\n\nexport class TypstProject {\n private readonly compiler: TypstCompiler;\n private readonly analyzer?: TypstAnalyzer;\n private readonly analyzerUriRoot: string;\n private readonly trackedTextPaths = new Set<Path>();\n /** Last content written via setText/setMany, per path. Used to skip redundant writes to compiler + analyzer. */\n private readonly lastSyncedContent = new Map<Path, string>();\n private readonly compileListeners = new Set<CompileListener>();\n private compileVersion = 0;\n private _lastResult: CompileResult | undefined;\n private _entry: Path;\n private destroyed = false;\n\n constructor(options: TypstProjectOptions) {\n this.compiler = options.compiler;\n this.analyzer = options.analyzer;\n this.analyzerUriRoot = normalizeRoot(\n options.analyzerUriRoot ?? DEFAULT_ROOT,\n );\n this._entry = normalizePath(options.entry ?? DEFAULT_ENTRY);\n }\n\n /** Current entry file path. Assign to change the sticky entry used by subsequent `compile()` calls. */\n get entry(): Path {\n return this._entry;\n }\n\n set entry(path: Path) {\n this._entry = normalizePath(path);\n }\n\n /** Whether an analyzer is attached. */\n get hasAnalyzer(): boolean {\n return this.analyzer !== undefined;\n }\n\n /**\n * Most recent compile result, or `undefined` before the first compile has\n * settled. Useful for lazy-mounted UI that subscribes after boot and needs\n * an initial value.\n */\n get lastResult(): CompileResult | undefined {\n return this._lastResult;\n }\n\n /**\n * Snapshot of tracked text file paths, in insertion order. Updated by\n * `setText`, `setMany`, `remove`, and `clear`. Returns a fresh array — mutate\n * freely without affecting project state.\n */\n get files(): Path[] {\n return [...this.trackedTextPaths];\n }\n\n /**\n * Current text content for a tracked file, or `undefined` if the path was\n * never written via `setText`/`setMany` (or was removed). Read-through to the\n * project's sync cache — lets consumers avoid shadowing the VFS themselves.\n */\n getText(path: Path): string | undefined {\n return this.lastSyncedContent.get(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: Path, 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(\n this.analyzer.didChange(\n pathToAnalyzerUri(p, this.analyzerUriRoot),\n content,\n ),\n );\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: Path, 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(path: Path, content: ArrayBuffer | ArrayBufferView): 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<Path, string | Uint8Array>): Promise<void> {\n const normalized: Record<Path, string | Uint8Array> = {};\n for (const [path, content] of Object.entries(files)) {\n normalized[normalizePath(path)] = content;\n }\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[pathToAnalyzerUri(path, this.analyzerUriRoot)] = content;\n }\n const compilerOp = this.compiler.setMany(normalized);\n const analyzerOp =\n this.analyzer && Object.keys(docs).length > 0\n ? this.analyzer.didChangeMany(docs)\n : Promise.resolve();\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: Path): 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(\n this.analyzer.didClose(pathToAnalyzerUri(p, this.analyzerUriRoot)),\n );\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) =>\n pathToAnalyzerUri(p, this.analyzerUriRoot),\n );\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 /**\n * Subscribe to compile results. Fires after every `compile()` whose result is\n * still current (stale results from out-of-order concurrent compiles are\n * dropped). If a compile has already settled, the most recent result is\n * delivered synchronously so late-mounted listeners aren't stuck blank until\n * the next compile. Returns an unsubscribe function.\n */\n onCompile(listener: CompileListener): () => void {\n this.compileListeners.add(listener);\n if (this._lastResult !== undefined) {\n try {\n listener(this._lastResult);\n } catch (err) {\n console.error(\"[typst] compile listener threw:\", err);\n }\n }\n return () => {\n this.compileListeners.delete(listener);\n };\n }\n\n /**\n * Compile the current VFS state using the sticky entry. Errors from the\n * underlying compiler are converted into a synthetic error diagnostic so\n * callers and listeners always receive a `CompileResult`. Listeners are\n * notified only for the most recent compile — results from an earlier call\n * that resolves after a later one are suppressed.\n */\n async compile(): Promise<CompileResult> {\n const version = ++this.compileVersion;\n let result: CompileResult;\n try {\n result = await this.compiler.compile(this._entry);\n } catch (err) {\n result = errorAsCompileResult(err, this._entry);\n }\n if (version === this.compileVersion) {\n this._lastResult = result;\n for (const listener of this.compileListeners) {\n try {\n listener(result);\n } catch (err) {\n console.error(\"[typst] compile listener threw:\", err);\n }\n }\n }\n return result;\n }\n\n /** Compile the current VFS state to PDF using the sticky entry. */\n compilePdf(): Promise<Uint8Array> {\n return this.compiler.compilePdf(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(\n path: Path,\n line: number,\n character: number,\n ): Promise<LspCompletionResponse> {\n return this.requireAnalyzer(\"completion\").completion(\n pathToAnalyzerUri(normalizePath(path), this.analyzerUriRoot),\n line,\n character,\n );\n }\n\n /** Request hover info at the given position. Throws when no analyzer is attached. */\n hover(path: Path, line: number, character: number): Promise<LspHover | null> {\n return this.requireAnalyzer(\"hover\").hover(\n pathToAnalyzerUri(normalizePath(path), this.analyzerUriRoot),\n line,\n character,\n );\n }\n\n /**\n * Tear down the project and the services it owns. Destroys the attached\n * compiler and analyzer, drops all listeners, and clears VFS tracking state.\n * Idempotent — calling twice is a no-op. After destruction, further calls on\n * the project are not supported; construct a new one.\n *\n * If you need to share a compiler or analyzer across projects, destroy them\n * yourself and don't call this method — the project does not provide an\n * ownership toggle.\n */\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.compileListeners.clear();\n this.trackedTextPaths.clear();\n this.lastSyncedContent.clear();\n this._lastResult = undefined;\n this.compiler.destroy();\n this.analyzer?.destroy();\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"]}
package/dist/worker.js CHANGED
@@ -2102,6 +2102,13 @@ var FetchPackageRegistry = class {
2102
2102
  }
2103
2103
  };
2104
2104
 
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
+ );
2110
+ }
2111
+
2105
2112
  // src/worker.ts
2106
2113
  var MAIN_FILE = "/main.typ";
2107
2114
  var CompilerWorker = class {
@@ -2124,17 +2131,14 @@ var CompilerWorker = class {
2124
2131
  ]
2125
2132
  });
2126
2133
  }
2127
- async compile(files) {
2134
+ async compile(entry) {
2128
2135
  if (!this.compiler) throw new Error("Compiler not initialized");
2129
- for (const [path, source] of Object.entries(files)) {
2130
- this.compiler.addSource(path, source);
2131
- }
2132
2136
  const result = await this.compiler.compile({
2133
- mainFilePath: MAIN_FILE,
2137
+ mainFilePath: entry ?? MAIN_FILE,
2134
2138
  diagnostics: "full"
2135
2139
  });
2136
- const diagnostics = (result.diagnostics ?? []).flatMap(
2137
- (d) => {
2140
+ const diagnostics = sortDiagnosticsByFileAndRange(
2141
+ (result.diagnostics ?? []).flatMap((d) => {
2138
2142
  const m = d.range.match(/(\d+):(\d+)-(\d+):(\d+)/);
2139
2143
  if (!m) {
2140
2144
  console.warn(
@@ -2154,7 +2158,7 @@ var CompilerWorker = class {
2154
2158
  }
2155
2159
  }
2156
2160
  ];
2157
- }
2161
+ })
2158
2162
  );
2159
2163
  const vector = result.result ?? void 0;
2160
2164
  if (vector) {
@@ -2164,13 +2168,10 @@ var CompilerWorker = class {
2164
2168
  }
2165
2169
  return { diagnostics };
2166
2170
  }
2167
- async compilePdf(files) {
2171
+ async compilePdf(entry) {
2168
2172
  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
2173
  const result = await this.compiler.compile({
2173
- mainFilePath: MAIN_FILE,
2174
+ mainFilePath: entry ?? MAIN_FILE,
2174
2175
  format: CompileFormatEnum.pdf,
2175
2176
  diagnostics: "none"
2176
2177
  });
@@ -2179,14 +2180,16 @@ var CompilerWorker = class {
2179
2180
  result.result.buffer
2180
2181
  ]);
2181
2182
  }
2182
- addSource(path, source) {
2183
- if (!this.compiler) throw new Error("Compiler not initialized");
2184
- this.compiler.addSource(path, source);
2185
- }
2186
2183
  mapShadow(path, content) {
2187
2184
  if (!this.compiler) throw new Error("Compiler not initialized");
2188
2185
  this.compiler.mapShadow(path, content);
2189
2186
  }
2187
+ mapShadowMany(files) {
2188
+ if (!this.compiler) throw new Error("Compiler not initialized");
2189
+ for (const [path, content] of Object.entries(files)) {
2190
+ this.compiler.mapShadow(path, content);
2191
+ }
2192
+ }
2190
2193
  unmapShadow(path) {
2191
2194
  if (!this.compiler) throw new Error("Compiler not initialized");
2192
2195
  this.compiler.unmapShadow(path);