@vedivad/typst-web-service 0.17.0 → 0.17.2
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.d.ts +114 -4
- package/dist/index.js +38 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { CompileResult, CompletionResponse, Hover, HlSpan } from 'typsten';
|
|
2
|
-
export { CompileResult, Completion, CompletionKind, CompletionResponse, Diagnostic, HlSpan, Hover, HoverKind, Location, PageInfo, Severity } from 'typsten';
|
|
3
|
-
|
|
4
1
|
/** UTF-16 string offset (CodeMirror position) -> UTF-8 byte offset (typsten). */
|
|
5
2
|
declare function cmOffsetToByte(text: string, offset: number): number;
|
|
6
3
|
/** UTF-8 byte offset (typsten) -> UTF-16 string offset (CodeMirror position). */
|
|
@@ -15,6 +12,119 @@ type Path = string;
|
|
|
15
12
|
/** Ensure a path starts with a leading slash. Idempotent. */
|
|
16
13
|
declare function normalizePath(path: string): Path;
|
|
17
14
|
|
|
15
|
+
/* tslint:disable */
|
|
16
|
+
/* eslint-disable */
|
|
17
|
+
/**
|
|
18
|
+
* A highlighted source span: a half-open byte range and its CSS class.
|
|
19
|
+
*/
|
|
20
|
+
interface HlSpan {
|
|
21
|
+
from: number;
|
|
22
|
+
to: number;
|
|
23
|
+
/**
|
|
24
|
+
* Typst\'s own stable highlight class, e.g. `typ-key`, `typ-func`, `typ-str`
|
|
25
|
+
* (see `Tag::css_class`). The editor uses it verbatim as the decoration class.
|
|
26
|
+
*/
|
|
27
|
+
tag: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A hover tooltip.
|
|
32
|
+
*/
|
|
33
|
+
interface Hover {
|
|
34
|
+
kind: HoverKind;
|
|
35
|
+
value: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A single compiler diagnostic, ready for an editor.
|
|
40
|
+
*/
|
|
41
|
+
interface Diagnostic {
|
|
42
|
+
severity: Severity;
|
|
43
|
+
message: string;
|
|
44
|
+
hints: string[];
|
|
45
|
+
/**
|
|
46
|
+
* `None` when the span is detached or cannot be resolved.
|
|
47
|
+
*/
|
|
48
|
+
location: Location | undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A single completion candidate.
|
|
53
|
+
*/
|
|
54
|
+
interface Completion {
|
|
55
|
+
kind: CompletionKind;
|
|
56
|
+
label: string;
|
|
57
|
+
/**
|
|
58
|
+
* Insertion text, possibly with snippet placeholders like `${body}`.
|
|
59
|
+
* Falls back to `label` when `None`.
|
|
60
|
+
*/
|
|
61
|
+
apply: string | undefined;
|
|
62
|
+
detail: string | undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The completions at a cursor, plus where the replacement starts.
|
|
67
|
+
*/
|
|
68
|
+
interface CompletionResponse {
|
|
69
|
+
/**
|
|
70
|
+
* Byte offset where the replacement begins; the editor replaces `from..cursor`.
|
|
71
|
+
*/
|
|
72
|
+
from: number;
|
|
73
|
+
completions: Completion[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The kind of thing a completion inserts.
|
|
78
|
+
*/
|
|
79
|
+
type CompletionKind = "syntax" | "func" | "type" | "param" | "constant" | "path" | "package" | "label" | "font" | "symbol";
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The outcome of a compile: one `PageInfo` per renderable page plus all
|
|
83
|
+
* diagnostics. `compile()` does NOT render, pages are drawn on demand via
|
|
84
|
+
* `render_page`/`render_pages`. Diagnostics are DATA, not errors (the editor
|
|
85
|
+
* needs them on success as warnings and on failure as errors), so this is never
|
|
86
|
+
* a `Result` and never throws. On a failed compile the `pages` describe the
|
|
87
|
+
* last successful document (the last-good preview is kept), so the presence of
|
|
88
|
+
* an error diagnostic means `pages` is stale.
|
|
89
|
+
*/
|
|
90
|
+
interface CompileResult {
|
|
91
|
+
pages: PageInfo[];
|
|
92
|
+
diagnostics: Diagnostic[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The width and height of a rendered page, in points.
|
|
97
|
+
*/
|
|
98
|
+
interface PageInfo {
|
|
99
|
+
width: number;
|
|
100
|
+
height: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Where a diagnostic points, when its span resolves to a real location.
|
|
105
|
+
*
|
|
106
|
+
* Offsets are UTF-8 byte indices (Typst-native); `line`/`column` are 1-based
|
|
107
|
+
* (editor convention). Note these are code-point based; exact UTF-16 mapping
|
|
108
|
+
* for JS editors is a later refinement.
|
|
109
|
+
*/
|
|
110
|
+
interface Location {
|
|
111
|
+
file: string;
|
|
112
|
+
start: number;
|
|
113
|
+
end: number;
|
|
114
|
+
line: number;
|
|
115
|
+
column: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Whether a diagnostic is a fatal error or a non-fatal warning.
|
|
120
|
+
*/
|
|
121
|
+
type Severity = "error" | "warning";
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Whether a hover tooltip is prose or Typst code.
|
|
125
|
+
*/
|
|
126
|
+
type HoverKind = "text" | "code";
|
|
127
|
+
|
|
18
128
|
/** A rendered page: its index, dimensions (in points), and standalone SVG. */
|
|
19
129
|
interface RenderedSvgPage {
|
|
20
130
|
index: number;
|
|
@@ -145,4 +255,4 @@ declare class TypstProject {
|
|
|
145
255
|
destroy(): void;
|
|
146
256
|
}
|
|
147
257
|
|
|
148
|
-
export { type AutoCompileOptions, type CompileListener, type Path, type RenderedSvgPage, TypstProject, type TypstProjectCreateOptions, byteToCmOffset, cmOffsetToByte, normalizePath };
|
|
258
|
+
export { type AutoCompileOptions, type CompileListener, type CompileResult, type Completion, type CompletionKind, type CompletionResponse, type Diagnostic, type HlSpan, type Hover, type HoverKind, type Location, type PageInfo, type Path, type RenderedSvgPage, type Severity, TypstProject, type TypstProjectCreateOptions, byteToCmOffset, cmOffsetToByte, normalizePath };
|
package/dist/index.js
CHANGED
|
@@ -121,47 +121,61 @@ import { gunzipSync } from "fflate";
|
|
|
121
121
|
import { parseTar } from "nanotar";
|
|
122
122
|
var REGISTRY = "https://packages.typst.org/preview";
|
|
123
123
|
var PREVIEW_IMPORT = /@preview\/([a-zA-Z0-9_-]+):(\d+\.\d+\.\d+)/g;
|
|
124
|
+
function specsIn(text) {
|
|
125
|
+
const specs = [];
|
|
126
|
+
for (const m of text.matchAll(PREVIEW_IMPORT)) specs.push(`${m[1]}:${m[2]}`);
|
|
127
|
+
return specs;
|
|
128
|
+
}
|
|
124
129
|
function createPackageLoader(setFile) {
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
async function fetchPackage(
|
|
128
|
-
const
|
|
130
|
+
const decoder2 = new TextDecoder();
|
|
131
|
+
const fetched = /* @__PURE__ */ new Map();
|
|
132
|
+
async function fetchPackage(spec) {
|
|
133
|
+
const [name, version] = spec.split(":");
|
|
134
|
+
const qualified = `@preview/${spec}`;
|
|
129
135
|
const res = await fetch(`${REGISTRY}/${name}-${version}.tar.gz`);
|
|
130
136
|
if (!res.ok) {
|
|
131
137
|
throw new Error(
|
|
132
|
-
`failed to fetch ${
|
|
138
|
+
`failed to fetch ${qualified}: ${res.status} ${res.statusText}`
|
|
133
139
|
);
|
|
134
140
|
}
|
|
135
141
|
const tar = gunzipSync(new Uint8Array(await res.arrayBuffer()));
|
|
142
|
+
const nested = /* @__PURE__ */ new Set();
|
|
136
143
|
for (const entry of parseTar(tar)) {
|
|
137
144
|
if (!entry.data || entry.name.endsWith("/")) continue;
|
|
138
|
-
await setFile(`${
|
|
145
|
+
await setFile(`${qualified}/${entry.name}`, entry.data);
|
|
146
|
+
if (entry.name.endsWith(".typ")) {
|
|
147
|
+
for (const s of specsIn(decoder2.decode(entry.data))) nested.add(s);
|
|
148
|
+
}
|
|
139
149
|
}
|
|
150
|
+
return [...nested];
|
|
151
|
+
}
|
|
152
|
+
function fetchOnce(spec) {
|
|
153
|
+
let task = fetched.get(spec);
|
|
154
|
+
if (!task) {
|
|
155
|
+
task = fetchPackage(spec).catch((err) => {
|
|
156
|
+
fetched.delete(spec);
|
|
157
|
+
throw err;
|
|
158
|
+
});
|
|
159
|
+
fetched.set(spec, task);
|
|
160
|
+
}
|
|
161
|
+
return task;
|
|
140
162
|
}
|
|
141
163
|
return {
|
|
142
164
|
async ensure(sources) {
|
|
143
|
-
const
|
|
165
|
+
const seen = /* @__PURE__ */ new Set();
|
|
166
|
+
let frontier = /* @__PURE__ */ new Set();
|
|
144
167
|
for (const text of sources) {
|
|
145
|
-
for (const
|
|
146
|
-
specs.add(`${m[1]}:${m[2]}`);
|
|
147
|
-
}
|
|
168
|
+
for (const s of specsIn(text)) frontier.add(s);
|
|
148
169
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
loaded.add(spec);
|
|
157
|
-
}).finally(() => {
|
|
158
|
-
inflight.delete(spec);
|
|
159
|
-
});
|
|
160
|
-
inflight.set(spec, task);
|
|
170
|
+
while (frontier.size > 0) {
|
|
171
|
+
const batch = [...frontier].filter((s) => !seen.has(s));
|
|
172
|
+
for (const s of batch) seen.add(s);
|
|
173
|
+
frontier = /* @__PURE__ */ new Set();
|
|
174
|
+
const nestedLists = await Promise.all(batch.map(fetchOnce));
|
|
175
|
+
for (const list of nestedLists) {
|
|
176
|
+
for (const s of list) if (!seen.has(s)) frontier.add(s);
|
|
161
177
|
}
|
|
162
|
-
tasks.push(task);
|
|
163
178
|
}
|
|
164
|
-
await Promise.all(tasks);
|
|
165
179
|
}
|
|
166
180
|
};
|
|
167
181
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/coords.ts","../src/identifiers.ts","../src/project.ts","../src/compile-scheduler.ts","../src/packages.ts"],"sourcesContent":["// typsten speaks UTF-8 byte offsets; CodeMirror/JS strings speak UTF-16 code\n// units. These convert between the two at code-point boundaries (where all\n// engine offsets land).\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/** UTF-16 string offset (CodeMirror position) -> UTF-8 byte offset (typsten). */\nexport function cmOffsetToByte(text: string, offset: number): number {\n return encoder.encode(text.slice(0, offset)).length;\n}\n\n/** UTF-8 byte offset (typsten) -> UTF-16 string offset (CodeMirror position). */\nexport function byteToCmOffset(text: string, byte: number): number {\n if (byte <= 0) return 0;\n const bytes = encoder.encode(text);\n if (byte >= bytes.length) return text.length;\n return decoder.decode(bytes.subarray(0, byte)).length;\n}\n\n/** Whether `text` has any non-ASCII code unit (byte offset != UTF-16 offset). */\nfunction hasNonAscii(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) > 0x7f) return true;\n }\n return false;\n}\n\n/** UTF-8 length in bytes of a Unicode code point. */\nfunction utf8Len(codePoint: number): number {\n if (codePoint < 0x80) return 1;\n if (codePoint < 0x800) return 2;\n if (codePoint < 0x10000) return 3;\n return 4;\n}\n\n/**\n * Map a batch of offsets from one coordinate space to the other in a single pass\n * over `text`, walking both running positions (`key` = input space, `val` =\n * output space) one code point at a time. `keyStep`/`valStep` give each space's\n * advance per code point. Input may be in any order (nested highlight spans\n * produce non-monotonic boundaries); the result is aligned with the input, and\n * offsets past the end clamp to the output length. Pure-ASCII text (where the\n * two spaces coincide) short-circuits to identity.\n */\nfunction mapOffsets(\n text: string,\n offsets: number[],\n keyStep: (codePoint: number, ch: string) => number,\n valStep: (codePoint: number, ch: string) => number,\n): number[] {\n if (offsets.length === 0 || !hasNonAscii(text)) return offsets;\n\n // Walk in ascending input order, but remember each offset's original slot.\n const order = offsets.map((offset, i) => [offset, i] as const);\n order.sort((a, b) => a[0] - b[0]);\n\n const out = new Array<number>(offsets.length);\n let key = 0;\n let val = 0;\n let next = 0;\n const flush = () => {\n while (next < order.length && order[next][0] <= key)\n out[order[next++][1]] = val;\n };\n flush(); // offsets at position 0\n for (const ch of text) {\n if (next >= order.length) break;\n const cp = ch.codePointAt(0)!;\n key += keyStep(cp, ch);\n val += valStep(cp, ch);\n flush();\n }\n // Past the end: the loop ran to completion, so `val` is the full output length.\n while (next < order.length) out[order[next++][1]] = val;\n return out;\n}\n\n/**\n * Convert a batch of UTF-8 byte offsets to UTF-16 (CodeMirror) offsets in one\n * pass. Unlike `byteToCmOffset` (which re-encodes a prefix per call, so N\n * offsets cost O(N * len)), this is O(len + N log N). Used to map highlight span\n * boundaries back to editor coordinates.\n */\nexport function byteOffsetsToCm(text: string, offsets: number[]): number[] {\n return mapOffsets(\n text,\n offsets,\n (cp) => utf8Len(cp),\n (_cp, ch) => ch.length,\n );\n}\n\n/**\n * Convert a batch of UTF-16 (CodeMirror) offsets to UTF-8 byte offsets in one\n * pass. The batched, ASCII-fast-path counterpart to `cmOffsetToByte` (which\n * re-encodes a prefix per call). Used for the highlight viewport bounds.\n */\nexport function cmOffsetsToByte(text: string, offsets: number[]): number[] {\n return mapOffsets(\n text,\n offsets,\n (_cp, ch) => ch.length,\n (cp) => utf8Len(cp),\n );\n}\n","/**\n * Project file paths. `/main.typ` form, leading slash, forward slashes only.\n * `@preview/...` package paths bypass this (they are pushed to the VFS verbatim).\n */\n\n/** `/path/to/file.typ`, leading-slash, forward slashes only. */\nexport type Path = 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","import * as Comlink from \"comlink\";\nimport type { Remote } from \"comlink\";\nimport { CompileScheduler } from \"./compile-scheduler.js\";\nimport { byteOffsetsToCm, cmOffsetsToByte, cmOffsetToByte } from \"./coords.js\";\nimport { normalizePath, type Path } from \"./identifiers.js\";\nimport { createPackageLoader, type PackageLoader } from \"./packages.js\";\nimport type {\n CompileResult,\n CompletionResponse,\n HlSpan,\n Hover,\n RenderedSvgPage,\n} from \"./types.js\";\nimport type { TypstenWorkerApi } from \"./typsten-worker.js\";\n\nexport interface TypstProjectCreateOptions {\n /** Default entry file path. Default: \"/main.typ\". */\n entry?: string;\n /** Auto-compile scheduling after VFS mutations. */\n autoCompile?: AutoCompileOptions;\n}\n\nexport interface AutoCompileOptions {\n /** Idle time (ms) after the last mutation before a compile fires. Default: 0. */\n debounceMs?: number;\n /** Max time (ms) the debounce may defer during sustained edits. Default: 0. */\n maxWaitMs?: number;\n}\n\nexport type CompileListener = (result: CompileResult) => void;\n\nconst DEFAULT_ENTRY = \"/main.typ\";\nconst encoder = new TextEncoder();\n\nfunction errorAsCompileResult(err: unknown): CompileResult {\n const message = err instanceof Error ? err.message : String(err);\n return {\n pages: [],\n diagnostics: [\n { severity: \"error\", message, hints: [], location: undefined },\n ],\n };\n}\n\nfunction toBytes(content: ArrayBuffer | ArrayBufferView): Uint8Array {\n if (content instanceof Uint8Array) return content;\n if (ArrayBuffer.isView(content)) {\n return new Uint8Array(\n content.buffer,\n content.byteOffset,\n content.byteLength,\n );\n }\n return new Uint8Array(content);\n}\n\n/**\n * Multi-file Typst project backed by a single typsten wasm worker. Owns the\n * VFS; editors push `setText` edits as the user types; the project mirrors them\n * to the worker, fetches `@preview` packages on demand, and compiles or renders\n * against the current state.\n *\n * const project = await TypstProject.create();\n * await project.setMany({ \"/main.typ\": \"...\" });\n * const { pages } = await project.compile();\n * const svg = await project.renderPage(0);\n */\nexport class TypstProject {\n /**\n * Tracked project files: the text content for a text file, or `null` for a\n * binary one. Drives dedup, `getText`, `files`, and `clear`. (Cached `@preview`\n * package files live only in the worker VFS, never here.)\n */\n private readonly tracked = new Map<Path, string | null>();\n private readonly compileListeners = new Set<CompileListener>();\n private readonly scheduler: CompileScheduler;\n private readonly packageLoader: PackageLoader;\n private compileVersion = 0;\n private _lastResult: CompileResult | undefined;\n private _entry: Path;\n private destroyed = false;\n\n private constructor(\n private readonly engine: Remote<TypstenWorkerApi>,\n private readonly worker: Worker,\n options: TypstProjectCreateOptions,\n ) {\n this._entry = normalizePath(options.entry ?? DEFAULT_ENTRY);\n this.scheduler = new CompileScheduler({\n debounceMs: options.autoCompile?.debounceMs,\n maxWaitMs: options.autoCompile?.maxWaitMs,\n });\n this.packageLoader = createPackageLoader((path, bytes) =>\n this.engine.setFile(path, bytes),\n );\n }\n\n /** Create a project: spin up the worker, init the wasm, set the entry. */\n static async create(\n options: TypstProjectCreateOptions = {},\n ): Promise<TypstProject> {\n const worker = new Worker(new URL(\"./typsten-worker.js\", import.meta.url), {\n type: \"module\",\n });\n const engine = Comlink.wrap<TypstenWorkerApi>(worker);\n\n const wasmUrl = new URL(\"./typsten_bg.wasm\", import.meta.url).href;\n await engine.init(wasmUrl);\n\n const project = new TypstProject(engine, worker, options);\n await engine.setEntry(project._entry);\n\n return project;\n }\n\n private scheduleCompile(): void {\n if (this.destroyed) return;\n this.scheduler.schedule(() => {\n this.compile().catch((err) => console.error(\"[typst]\", err));\n });\n }\n\n /**\n * Mirror text into the VFS without scheduling a compile. Returns the in-flight\n * write to await, or `null` if the content is unchanged (deduped to a no-op).\n */\n private writeText(p: Path, content: string): Promise<unknown> | null {\n if (this.tracked.get(p) === content) return null;\n this.tracked.set(p, content);\n return this.engine.setFile(p, encoder.encode(content));\n }\n\n /** Mirror binary bytes into the VFS (always writes; binaries are not deduped). */\n private writeBinary(p: Path, bytes: Uint8Array): Promise<unknown> {\n this.tracked.set(p, null);\n return this.engine.setFile(p, bytes);\n }\n\n get entry(): Path {\n return this._entry;\n }\n\n set entry(path: Path) {\n const next = normalizePath(path);\n if (next === this._entry) return;\n this._entry = next;\n void this.engine.setEntry(next);\n this.scheduleCompile();\n }\n\n /** Most recent compile result, or `undefined` before the first compile. */\n get lastResult(): CompileResult | undefined {\n return this._lastResult;\n }\n\n /** Tracked text file paths, in insertion order (fresh array). */\n get files(): Path[] {\n return [...this.tracked]\n .filter(([, content]) => content !== null)\n .map(([path]) => path);\n }\n\n /** Current text for a tracked file, or `undefined` (binary or absent). */\n getText(path: Path): string | undefined {\n return this.tracked.get(normalizePath(path)) ?? undefined;\n }\n\n /** Add or overwrite a text file. No-op if unchanged. */\n async setText(path: Path, content: string): Promise<void> {\n const write = this.writeText(normalizePath(path), content);\n if (write) {\n await write;\n this.scheduleCompile();\n }\n }\n\n /** Add or overwrite a binary file (retires any text tracking for the path). */\n async setBinary(\n path: Path,\n content: ArrayBuffer | ArrayBufferView,\n ): Promise<void> {\n await this.writeBinary(normalizePath(path), toBytes(content));\n this.scheduleCompile();\n }\n\n /** Batch set files. Strings dedup against tracked content; binaries always write. */\n async setMany(files: Record<Path, string | Uint8Array>): Promise<void> {\n const writes: Promise<unknown>[] = [];\n for (const [path, content] of Object.entries(files)) {\n const p = normalizePath(path);\n const write =\n typeof content === \"string\"\n ? this.writeText(p, content)\n : this.writeBinary(p, content);\n if (write) writes.push(write);\n }\n if (writes.length === 0) return;\n await Promise.all(writes);\n this.scheduleCompile();\n }\n\n /** Remove a file from the VFS. */\n async remove(path: Path): Promise<void> {\n const p = normalizePath(path);\n await this.engine.remove(p);\n this.tracked.delete(p);\n this.scheduleCompile();\n }\n\n /** Remove all tracked project files (cached `@preview` packages are kept). */\n async clear(): Promise<void> {\n await Promise.all(\n [...this.tracked.keys()].map((p) => this.engine.remove(p)),\n );\n this.tracked.clear();\n this.scheduleCompile();\n }\n\n /**\n * Register a font (TTF/OTF, or TTC collection bytes) so compilation can use\n * it, then recompile. The engine bundles default body, math, and monospace\n * fonts; use this to add families it does not ship (e.g. CJK or a custom\n * font). Fonts persist for the project's lifetime.\n */\n async addFont(bytes: Uint8Array): Promise<void> {\n await this.engine.addFont(bytes);\n this.scheduleCompile();\n }\n\n /**\n * Subscribe to compile results. Late subscribers get `lastResult` synchronously.\n * 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. Fetches any referenced `@preview` packages\n * first. Errors become a synthetic diagnostic. Listeners fire only for the\n * most recent compile (stale results are dropped).\n */\n async compile(): Promise<CompileResult> {\n this.scheduler.cancel();\n const version = ++this.compileVersion;\n let result: CompileResult;\n try {\n await this.packageLoader.ensure(\n [...this.tracked.values()].filter((v): v is string => v !== null),\n );\n result = await this.engine.compile();\n } catch (err) {\n result = errorAsCompileResult(err);\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 /** Render a single page of the last compile to SVG, or `undefined`. */\n renderPage(index: number): Promise<string | undefined> {\n return this.engine.renderPage(index);\n }\n\n /**\n * Render pages `[start, end)` as `RenderedSvgPage`s (index + dims + svg),\n * zipping the SVG strings with the page metadata from the last compile.\n */\n async renderedPages(start: number, end: number): Promise<RenderedSvgPage[]> {\n const pages = this._lastResult?.pages ?? [];\n const svgs = await this.engine.renderPages(start, end);\n return svgs.map((svg, i) => {\n const index = start + i;\n const dims = pages[index];\n return {\n index,\n width: dims?.width ?? 0,\n height: dims?.height ?? 0,\n svg,\n };\n });\n }\n\n /**\n * Export the last compile as a PDF, or `undefined` if nothing has compiled\n * yet. The bytes are a fresh `Uint8Array` (copied across the worker boundary).\n */\n exportPdf(): Promise<Uint8Array | undefined> {\n return this.engine.exportPdf();\n }\n\n /** Completions at a CodeMirror `offset` in `path`, using `source` as the live buffer. */\n async completion(\n path: Path,\n source: string,\n offset: number,\n explicit = false,\n ): Promise<CompletionResponse | undefined> {\n const p = normalizePath(path);\n await this.writeText(p, source);\n return this.engine.complete(p, cmOffsetToByte(source, offset), explicit);\n }\n\n /** Hover tooltip at a CodeMirror `offset` in `path`, using `source` as the live buffer. */\n async hover(\n path: Path,\n source: string,\n offset: number,\n ): Promise<Hover | undefined> {\n const p = normalizePath(path);\n await this.writeText(p, source);\n return this.engine.hover(p, cmOffsetToByte(source, offset));\n }\n\n /** Format `source` (the live buffer for `path`); returns the formatted text or `undefined`. */\n async format(path: Path, source: string): Promise<string | undefined> {\n const p = normalizePath(path);\n await this.writeText(p, source);\n return this.engine.format(p);\n }\n\n /**\n * Syntax-highlight `source` over the CodeMirror window `[from, to)` (UTF-16\n * offsets; defaults to the whole string). Returns spans whose `from`/`to` are\n * CodeMirror offsets, ready to drive decorations. Stateless: the worker parses\n * `source` directly via typst-syntax, so this neither reads nor mutates the\n * VFS and works for any text (the live buffer, a hover code snippet).\n */\n async highlight(\n source: string,\n from = 0,\n to = source.length,\n ): Promise<HlSpan[]> {\n const [byteFrom, byteTo] = cmOffsetsToByte(source, [from, to]);\n const spans = await this.engine.highlight(source, byteFrom, byteTo);\n if (spans.length === 0) return spans;\n // The worker speaks UTF-8 bytes; map every span boundary back to UTF-16\n // (CodeMirror) offsets in one pass over the source. Boundaries are not\n // monotonic (nested spans wrap inner ones), so byteOffsetsToCm sorts them.\n const cm = byteOffsetsToCm(\n source,\n spans.flatMap((s) => [s.from, s.to]),\n );\n return spans.map((s, i) => ({\n from: cm[i * 2],\n to: cm[i * 2 + 1],\n tag: s.tag,\n }));\n }\n\n /**\n * Syntax-highlight `source` to nested `<span class=\"typ-*\">` HTML for a static\n * context (e.g. a hover tooltip). Stateless, like `highlight`.\n */\n highlightHtml(source: string): Promise<string> {\n return this.engine.highlightHtml(source);\n }\n\n /** Tear down the worker and drop all state. Idempotent. */\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.scheduler.cancel();\n this.compileListeners.clear();\n this.tracked.clear();\n this._lastResult = undefined;\n this.engine[Comlink.releaseProxy]();\n this.worker.terminate();\n }\n}\n","interface CompileSchedulerOptions {\n /** Minimum idle time (ms) after the last request before firing. Default: 0. */\n debounceMs?: number;\n /**\n * Maximum time (ms) the debounce may keep deferring during continuous\n * requests. Guarantees a fire at least this often so users see progress\n * while typing. Default: 0 (no cap).\n */\n maxWaitMs?: number;\n}\n\n/**\n * Debounce helper with a max-wait ceiling for coalescing compile requests. A\n * burst of `schedule(cb)` calls within `debounceMs` collapses into one fire;\n * during sustained bursts, `maxWaitMs` caps how long the debounce may keep\n * deferring.\n */\nexport class CompileScheduler {\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private maxWaitTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(private readonly options: CompileSchedulerOptions = {}) {}\n\n schedule(callback: () => void): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n\n const delay = Math.max(0, this.options.debounceMs ?? 0);\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null;\n this.clearMaxWait();\n callback();\n }, delay);\n\n const maxWait = this.options.maxWaitMs;\n if (maxWait != null && maxWait > 0 && !this.maxWaitTimer) {\n this.maxWaitTimer = setTimeout(() => {\n this.maxWaitTimer = null;\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n callback();\n }\n }, maxWait);\n }\n }\n\n /** Cancel any pending scheduled fire without calling the callback. */\n cancel(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.clearMaxWait();\n }\n\n private clearMaxWait(): void {\n if (this.maxWaitTimer) {\n clearTimeout(this.maxWaitTimer);\n this.maxWaitTimer = null;\n }\n }\n}\n","// typsten resolves `@preview` packages from its in-memory VFS but does no I/O.\n// This is the JS side: scan sources for `@preview` imports, fetch the tarball\n// from the registry, unpack it, and push every member into the VFS. (The eager\n// import-scanner approach; a lazy JSPI fetch is a future typsten milestone.)\nimport { gunzipSync } from \"fflate\";\nimport { parseTar } from \"nanotar\";\n\nconst REGISTRY = \"https://packages.typst.org/preview\";\nconst PREVIEW_IMPORT = /@preview\\/([a-zA-Z0-9_-]+):(\\d+\\.\\d+\\.\\d+)/g;\n\nexport type SetFile = (path: string, bytes: Uint8Array) => Promise<void> | void;\n\nexport interface PackageLoader {\n /**\n * Ensure every `@preview` package referenced in `sources` is present in the\n * VFS, fetching any that are missing.\n */\n ensure(sources: Iterable<string>): Promise<void>;\n}\n\nexport function createPackageLoader(setFile: SetFile): PackageLoader {\n const loaded = new Set<string>();\n const inflight = new Map<string, Promise<void>>();\n\n async function fetchPackage(name: string, version: string): Promise<void> {\n const spec = `@preview/${name}:${version}`;\n const res = await fetch(`${REGISTRY}/${name}-${version}.tar.gz`);\n if (!res.ok) {\n throw new Error(\n `failed to fetch ${spec}: ${res.status} ${res.statusText}`,\n );\n }\n const tar = gunzipSync(new Uint8Array(await res.arrayBuffer()));\n for (const entry of parseTar(tar)) {\n // Skip directories and anything without bytes.\n if (!entry.data || entry.name.endsWith(\"/\")) continue;\n await setFile(`${spec}/${entry.name}`, entry.data);\n }\n }\n\n return {\n async ensure(sources) {\n const specs = new Set<string>();\n for (const text of sources) {\n for (const m of text.matchAll(PREVIEW_IMPORT)) {\n specs.add(`${m[1]}:${m[2]}`);\n }\n }\n\n const tasks: Promise<void>[] = [];\n for (const spec of specs) {\n if (loaded.has(spec)) continue;\n let task = inflight.get(spec);\n if (!task) {\n const [name, version] = spec.split(\":\");\n task = fetchPackage(name, version)\n .then(() => {\n loaded.add(spec);\n })\n .finally(() => {\n inflight.delete(spec);\n });\n inflight.set(spec, task);\n }\n tasks.push(task);\n }\n await Promise.all(tasks);\n },\n };\n}\n"],"mappings":"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","names":["encoder"]}
|
|
1
|
+
{"version":3,"sources":["../src/coords.ts","../src/identifiers.ts","../src/project.ts","../src/compile-scheduler.ts","../src/packages.ts"],"sourcesContent":["// typsten speaks UTF-8 byte offsets; CodeMirror/JS strings speak UTF-16 code\n// units. These convert between the two at code-point boundaries (where all\n// engine offsets land).\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/** UTF-16 string offset (CodeMirror position) -> UTF-8 byte offset (typsten). */\nexport function cmOffsetToByte(text: string, offset: number): number {\n return encoder.encode(text.slice(0, offset)).length;\n}\n\n/** UTF-8 byte offset (typsten) -> UTF-16 string offset (CodeMirror position). */\nexport function byteToCmOffset(text: string, byte: number): number {\n if (byte <= 0) return 0;\n const bytes = encoder.encode(text);\n if (byte >= bytes.length) return text.length;\n return decoder.decode(bytes.subarray(0, byte)).length;\n}\n\n/** Whether `text` has any non-ASCII code unit (byte offset != UTF-16 offset). */\nfunction hasNonAscii(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) > 0x7f) return true;\n }\n return false;\n}\n\n/** UTF-8 length in bytes of a Unicode code point. */\nfunction utf8Len(codePoint: number): number {\n if (codePoint < 0x80) return 1;\n if (codePoint < 0x800) return 2;\n if (codePoint < 0x10000) return 3;\n return 4;\n}\n\n/**\n * Map a batch of offsets from one coordinate space to the other in a single pass\n * over `text`, walking both running positions (`key` = input space, `val` =\n * output space) one code point at a time. `keyStep`/`valStep` give each space's\n * advance per code point. Input may be in any order (nested highlight spans\n * produce non-monotonic boundaries); the result is aligned with the input, and\n * offsets past the end clamp to the output length. Pure-ASCII text (where the\n * two spaces coincide) short-circuits to identity.\n */\nfunction mapOffsets(\n text: string,\n offsets: number[],\n keyStep: (codePoint: number, ch: string) => number,\n valStep: (codePoint: number, ch: string) => number,\n): number[] {\n if (offsets.length === 0 || !hasNonAscii(text)) return offsets;\n\n // Walk in ascending input order, but remember each offset's original slot.\n const order = offsets.map((offset, i) => [offset, i] as const);\n order.sort((a, b) => a[0] - b[0]);\n\n const out = new Array<number>(offsets.length);\n let key = 0;\n let val = 0;\n let next = 0;\n const flush = () => {\n while (next < order.length && order[next][0] <= key)\n out[order[next++][1]] = val;\n };\n flush(); // offsets at position 0\n for (const ch of text) {\n if (next >= order.length) break;\n const cp = ch.codePointAt(0)!;\n key += keyStep(cp, ch);\n val += valStep(cp, ch);\n flush();\n }\n // Past the end: the loop ran to completion, so `val` is the full output length.\n while (next < order.length) out[order[next++][1]] = val;\n return out;\n}\n\n/**\n * Convert a batch of UTF-8 byte offsets to UTF-16 (CodeMirror) offsets in one\n * pass. Unlike `byteToCmOffset` (which re-encodes a prefix per call, so N\n * offsets cost O(N * len)), this is O(len + N log N). Used to map highlight span\n * boundaries back to editor coordinates.\n */\nexport function byteOffsetsToCm(text: string, offsets: number[]): number[] {\n return mapOffsets(\n text,\n offsets,\n (cp) => utf8Len(cp),\n (_cp, ch) => ch.length,\n );\n}\n\n/**\n * Convert a batch of UTF-16 (CodeMirror) offsets to UTF-8 byte offsets in one\n * pass. The batched, ASCII-fast-path counterpart to `cmOffsetToByte` (which\n * re-encodes a prefix per call). Used for the highlight viewport bounds.\n */\nexport function cmOffsetsToByte(text: string, offsets: number[]): number[] {\n return mapOffsets(\n text,\n offsets,\n (_cp, ch) => ch.length,\n (cp) => utf8Len(cp),\n );\n}\n","/**\n * Project file paths. `/main.typ` form, leading slash, forward slashes only.\n * `@preview/...` package paths bypass this (they are pushed to the VFS verbatim).\n */\n\n/** `/path/to/file.typ`, leading-slash, forward slashes only. */\nexport type Path = 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","import * as Comlink from \"comlink\";\nimport type { Remote } from \"comlink\";\nimport { CompileScheduler } from \"./compile-scheduler.js\";\nimport { byteOffsetsToCm, cmOffsetsToByte, cmOffsetToByte } from \"./coords.js\";\nimport { normalizePath, type Path } from \"./identifiers.js\";\nimport { createPackageLoader, type PackageLoader } from \"./packages.js\";\nimport type {\n CompileResult,\n CompletionResponse,\n HlSpan,\n Hover,\n RenderedSvgPage,\n} from \"./types.js\";\nimport type { TypstenWorkerApi } from \"./typsten-worker.js\";\n\nexport interface TypstProjectCreateOptions {\n /** Default entry file path. Default: \"/main.typ\". */\n entry?: string;\n /** Auto-compile scheduling after VFS mutations. */\n autoCompile?: AutoCompileOptions;\n}\n\nexport interface AutoCompileOptions {\n /** Idle time (ms) after the last mutation before a compile fires. Default: 0. */\n debounceMs?: number;\n /** Max time (ms) the debounce may defer during sustained edits. Default: 0. */\n maxWaitMs?: number;\n}\n\nexport type CompileListener = (result: CompileResult) => void;\n\nconst DEFAULT_ENTRY = \"/main.typ\";\nconst encoder = new TextEncoder();\n\nfunction errorAsCompileResult(err: unknown): CompileResult {\n const message = err instanceof Error ? err.message : String(err);\n return {\n pages: [],\n diagnostics: [\n { severity: \"error\", message, hints: [], location: undefined },\n ],\n };\n}\n\nfunction toBytes(content: ArrayBuffer | ArrayBufferView): Uint8Array {\n if (content instanceof Uint8Array) return content;\n if (ArrayBuffer.isView(content)) {\n return new Uint8Array(\n content.buffer,\n content.byteOffset,\n content.byteLength,\n );\n }\n return new Uint8Array(content);\n}\n\n/**\n * Multi-file Typst project backed by a single typsten wasm worker. Owns the\n * VFS; editors push `setText` edits as the user types; the project mirrors them\n * to the worker, fetches `@preview` packages on demand, and compiles or renders\n * against the current state.\n *\n * const project = await TypstProject.create();\n * await project.setMany({ \"/main.typ\": \"...\" });\n * const { pages } = await project.compile();\n * const svg = await project.renderPage(0);\n */\nexport class TypstProject {\n /**\n * Tracked project files: the text content for a text file, or `null` for a\n * binary one. Drives dedup, `getText`, `files`, and `clear`. (Cached `@preview`\n * package files live only in the worker VFS, never here.)\n */\n private readonly tracked = new Map<Path, string | null>();\n private readonly compileListeners = new Set<CompileListener>();\n private readonly scheduler: CompileScheduler;\n private readonly packageLoader: PackageLoader;\n private compileVersion = 0;\n private _lastResult: CompileResult | undefined;\n private _entry: Path;\n private destroyed = false;\n\n private constructor(\n private readonly engine: Remote<TypstenWorkerApi>,\n private readonly worker: Worker,\n options: TypstProjectCreateOptions,\n ) {\n this._entry = normalizePath(options.entry ?? DEFAULT_ENTRY);\n this.scheduler = new CompileScheduler({\n debounceMs: options.autoCompile?.debounceMs,\n maxWaitMs: options.autoCompile?.maxWaitMs,\n });\n this.packageLoader = createPackageLoader((path, bytes) =>\n this.engine.setFile(path, bytes),\n );\n }\n\n /** Create a project: spin up the worker, init the wasm, set the entry. */\n static async create(\n options: TypstProjectCreateOptions = {},\n ): Promise<TypstProject> {\n const worker = new Worker(new URL(\"./typsten-worker.js\", import.meta.url), {\n type: \"module\",\n });\n const engine = Comlink.wrap<TypstenWorkerApi>(worker);\n\n const wasmUrl = new URL(\"./typsten_bg.wasm\", import.meta.url).href;\n await engine.init(wasmUrl);\n\n const project = new TypstProject(engine, worker, options);\n await engine.setEntry(project._entry);\n\n return project;\n }\n\n private scheduleCompile(): void {\n if (this.destroyed) return;\n this.scheduler.schedule(() => {\n this.compile().catch((err) => console.error(\"[typst]\", err));\n });\n }\n\n /**\n * Mirror text into the VFS without scheduling a compile. Returns the in-flight\n * write to await, or `null` if the content is unchanged (deduped to a no-op).\n */\n private writeText(p: Path, content: string): Promise<unknown> | null {\n if (this.tracked.get(p) === content) return null;\n this.tracked.set(p, content);\n return this.engine.setFile(p, encoder.encode(content));\n }\n\n /** Mirror binary bytes into the VFS (always writes; binaries are not deduped). */\n private writeBinary(p: Path, bytes: Uint8Array): Promise<unknown> {\n this.tracked.set(p, null);\n return this.engine.setFile(p, bytes);\n }\n\n get entry(): Path {\n return this._entry;\n }\n\n set entry(path: Path) {\n const next = normalizePath(path);\n if (next === this._entry) return;\n this._entry = next;\n void this.engine.setEntry(next);\n this.scheduleCompile();\n }\n\n /** Most recent compile result, or `undefined` before the first compile. */\n get lastResult(): CompileResult | undefined {\n return this._lastResult;\n }\n\n /** Tracked text file paths, in insertion order (fresh array). */\n get files(): Path[] {\n return [...this.tracked]\n .filter(([, content]) => content !== null)\n .map(([path]) => path);\n }\n\n /** Current text for a tracked file, or `undefined` (binary or absent). */\n getText(path: Path): string | undefined {\n return this.tracked.get(normalizePath(path)) ?? undefined;\n }\n\n /** Add or overwrite a text file. No-op if unchanged. */\n async setText(path: Path, content: string): Promise<void> {\n const write = this.writeText(normalizePath(path), content);\n if (write) {\n await write;\n this.scheduleCompile();\n }\n }\n\n /** Add or overwrite a binary file (retires any text tracking for the path). */\n async setBinary(\n path: Path,\n content: ArrayBuffer | ArrayBufferView,\n ): Promise<void> {\n await this.writeBinary(normalizePath(path), toBytes(content));\n this.scheduleCompile();\n }\n\n /** Batch set files. Strings dedup against tracked content; binaries always write. */\n async setMany(files: Record<Path, string | Uint8Array>): Promise<void> {\n const writes: Promise<unknown>[] = [];\n for (const [path, content] of Object.entries(files)) {\n const p = normalizePath(path);\n const write =\n typeof content === \"string\"\n ? this.writeText(p, content)\n : this.writeBinary(p, content);\n if (write) writes.push(write);\n }\n if (writes.length === 0) return;\n await Promise.all(writes);\n this.scheduleCompile();\n }\n\n /** Remove a file from the VFS. */\n async remove(path: Path): Promise<void> {\n const p = normalizePath(path);\n await this.engine.remove(p);\n this.tracked.delete(p);\n this.scheduleCompile();\n }\n\n /** Remove all tracked project files (cached `@preview` packages are kept). */\n async clear(): Promise<void> {\n await Promise.all(\n [...this.tracked.keys()].map((p) => this.engine.remove(p)),\n );\n this.tracked.clear();\n this.scheduleCompile();\n }\n\n /**\n * Register a font (TTF/OTF, or TTC collection bytes) so compilation can use\n * it, then recompile. The engine bundles default body, math, and monospace\n * fonts; use this to add families it does not ship (e.g. CJK or a custom\n * font). Fonts persist for the project's lifetime.\n */\n async addFont(bytes: Uint8Array): Promise<void> {\n await this.engine.addFont(bytes);\n this.scheduleCompile();\n }\n\n /**\n * Subscribe to compile results. Late subscribers get `lastResult` synchronously.\n * 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. Fetches any referenced `@preview` packages\n * first. Errors become a synthetic diagnostic. Listeners fire only for the\n * most recent compile (stale results are dropped).\n */\n async compile(): Promise<CompileResult> {\n this.scheduler.cancel();\n const version = ++this.compileVersion;\n let result: CompileResult;\n try {\n await this.packageLoader.ensure(\n [...this.tracked.values()].filter((v): v is string => v !== null),\n );\n result = await this.engine.compile();\n } catch (err) {\n result = errorAsCompileResult(err);\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 /** Render a single page of the last compile to SVG, or `undefined`. */\n renderPage(index: number): Promise<string | undefined> {\n return this.engine.renderPage(index);\n }\n\n /**\n * Render pages `[start, end)` as `RenderedSvgPage`s (index + dims + svg),\n * zipping the SVG strings with the page metadata from the last compile.\n */\n async renderedPages(start: number, end: number): Promise<RenderedSvgPage[]> {\n const pages = this._lastResult?.pages ?? [];\n const svgs = await this.engine.renderPages(start, end);\n return svgs.map((svg, i) => {\n const index = start + i;\n const dims = pages[index];\n return {\n index,\n width: dims?.width ?? 0,\n height: dims?.height ?? 0,\n svg,\n };\n });\n }\n\n /**\n * Export the last compile as a PDF, or `undefined` if nothing has compiled\n * yet. The bytes are a fresh `Uint8Array` (copied across the worker boundary).\n */\n exportPdf(): Promise<Uint8Array | undefined> {\n return this.engine.exportPdf();\n }\n\n /** Completions at a CodeMirror `offset` in `path`, using `source` as the live buffer. */\n async completion(\n path: Path,\n source: string,\n offset: number,\n explicit = false,\n ): Promise<CompletionResponse | undefined> {\n const p = normalizePath(path);\n await this.writeText(p, source);\n return this.engine.complete(p, cmOffsetToByte(source, offset), explicit);\n }\n\n /** Hover tooltip at a CodeMirror `offset` in `path`, using `source` as the live buffer. */\n async hover(\n path: Path,\n source: string,\n offset: number,\n ): Promise<Hover | undefined> {\n const p = normalizePath(path);\n await this.writeText(p, source);\n return this.engine.hover(p, cmOffsetToByte(source, offset));\n }\n\n /** Format `source` (the live buffer for `path`); returns the formatted text or `undefined`. */\n async format(path: Path, source: string): Promise<string | undefined> {\n const p = normalizePath(path);\n await this.writeText(p, source);\n return this.engine.format(p);\n }\n\n /**\n * Syntax-highlight `source` over the CodeMirror window `[from, to)` (UTF-16\n * offsets; defaults to the whole string). Returns spans whose `from`/`to` are\n * CodeMirror offsets, ready to drive decorations. Stateless: the worker parses\n * `source` directly via typst-syntax, so this neither reads nor mutates the\n * VFS and works for any text (the live buffer, a hover code snippet).\n */\n async highlight(\n source: string,\n from = 0,\n to = source.length,\n ): Promise<HlSpan[]> {\n const [byteFrom, byteTo] = cmOffsetsToByte(source, [from, to]);\n const spans = await this.engine.highlight(source, byteFrom, byteTo);\n if (spans.length === 0) return spans;\n // The worker speaks UTF-8 bytes; map every span boundary back to UTF-16\n // (CodeMirror) offsets in one pass over the source. Boundaries are not\n // monotonic (nested spans wrap inner ones), so byteOffsetsToCm sorts them.\n const cm = byteOffsetsToCm(\n source,\n spans.flatMap((s) => [s.from, s.to]),\n );\n return spans.map((s, i) => ({\n from: cm[i * 2],\n to: cm[i * 2 + 1],\n tag: s.tag,\n }));\n }\n\n /**\n * Syntax-highlight `source` to nested `<span class=\"typ-*\">` HTML for a static\n * context (e.g. a hover tooltip). Stateless, like `highlight`.\n */\n highlightHtml(source: string): Promise<string> {\n return this.engine.highlightHtml(source);\n }\n\n /** Tear down the worker and drop all state. Idempotent. */\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.scheduler.cancel();\n this.compileListeners.clear();\n this.tracked.clear();\n this._lastResult = undefined;\n this.engine[Comlink.releaseProxy]();\n this.worker.terminate();\n }\n}\n","interface CompileSchedulerOptions {\n /** Minimum idle time (ms) after the last request before firing. Default: 0. */\n debounceMs?: number;\n /**\n * Maximum time (ms) the debounce may keep deferring during continuous\n * requests. Guarantees a fire at least this often so users see progress\n * while typing. Default: 0 (no cap).\n */\n maxWaitMs?: number;\n}\n\n/**\n * Debounce helper with a max-wait ceiling for coalescing compile requests. A\n * burst of `schedule(cb)` calls within `debounceMs` collapses into one fire;\n * during sustained bursts, `maxWaitMs` caps how long the debounce may keep\n * deferring.\n */\nexport class CompileScheduler {\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private maxWaitTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(private readonly options: CompileSchedulerOptions = {}) {}\n\n schedule(callback: () => void): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n\n const delay = Math.max(0, this.options.debounceMs ?? 0);\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null;\n this.clearMaxWait();\n callback();\n }, delay);\n\n const maxWait = this.options.maxWaitMs;\n if (maxWait != null && maxWait > 0 && !this.maxWaitTimer) {\n this.maxWaitTimer = setTimeout(() => {\n this.maxWaitTimer = null;\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n callback();\n }\n }, maxWait);\n }\n }\n\n /** Cancel any pending scheduled fire without calling the callback. */\n cancel(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.clearMaxWait();\n }\n\n private clearMaxWait(): void {\n if (this.maxWaitTimer) {\n clearTimeout(this.maxWaitTimer);\n this.maxWaitTimer = null;\n }\n }\n}\n","// typsten resolves `@preview` packages from its in-memory VFS but does no I/O.\n// This is the JS side: scan sources for `@preview` imports, fetch the tarball\n// from the registry, unpack it, and push every member into the VFS. A package's\n// own sources may import further `@preview` packages, so after unpacking we scan\n// the fetched `.typ` files and follow those transitive imports to a fixed point.\n// (The eager import-scanner approach; a lazy JSPI fetch is a future typsten\n// milestone that would let the engine drive resolution instead.)\nimport { gunzipSync } from \"fflate\";\nimport { parseTar } from \"nanotar\";\n\nconst REGISTRY = \"https://packages.typst.org/preview\";\nconst PREVIEW_IMPORT = /@preview\\/([a-zA-Z0-9_-]+):(\\d+\\.\\d+\\.\\d+)/g;\n\nexport type SetFile = (path: string, bytes: Uint8Array) => Promise<void> | void;\n\nexport interface PackageLoader {\n /**\n * Ensure every `@preview` package referenced in `sources`, and transitively\n * by the packages they pull in, is present in the VFS, fetching any missing.\n */\n ensure(sources: Iterable<string>): Promise<void>;\n}\n\n/** Collect the `name:version` spec of every `@preview` import in `text`. */\nfunction specsIn(text: string): string[] {\n const specs: string[] = [];\n for (const m of text.matchAll(PREVIEW_IMPORT)) specs.push(`${m[1]}:${m[2]}`);\n return specs;\n}\n\nexport function createPackageLoader(setFile: SetFile): PackageLoader {\n const decoder = new TextDecoder();\n // spec (\"name:version\") -> the transitive specs found inside that package.\n // Doubles as the fetch cache: a settled entry means the package is resident.\n const fetched = new Map<string, Promise<string[]>>();\n\n async function fetchPackage(spec: string): Promise<string[]> {\n const [name, version] = spec.split(\":\");\n const qualified = `@preview/${spec}`;\n const res = await fetch(`${REGISTRY}/${name}-${version}.tar.gz`);\n if (!res.ok) {\n throw new Error(\n `failed to fetch ${qualified}: ${res.status} ${res.statusText}`,\n );\n }\n const tar = gunzipSync(new Uint8Array(await res.arrayBuffer()));\n const nested = new Set<string>();\n for (const entry of parseTar(tar)) {\n // Skip directories and anything without bytes.\n if (!entry.data || entry.name.endsWith(\"/\")) continue;\n await setFile(`${qualified}/${entry.name}`, entry.data);\n // A package's own `.typ` sources may import further `@preview` packages.\n if (entry.name.endsWith(\".typ\")) {\n for (const s of specsIn(decoder.decode(entry.data))) nested.add(s);\n }\n }\n return [...nested];\n }\n\n function fetchOnce(spec: string): Promise<string[]> {\n let task = fetched.get(spec);\n if (!task) {\n task = fetchPackage(spec).catch((err: unknown) => {\n // Drop failed fetches so a later compile can retry (transient network).\n fetched.delete(spec);\n throw err;\n });\n fetched.set(spec, task);\n }\n return task;\n }\n\n return {\n async ensure(sources) {\n const seen = new Set<string>();\n let frontier = new Set<string>();\n for (const text of sources) {\n for (const s of specsIn(text)) frontier.add(s);\n }\n // Breadth-first fetch to a fixed point; each package can reveal more.\n while (frontier.size > 0) {\n const batch = [...frontier].filter((s) => !seen.has(s));\n for (const s of batch) seen.add(s);\n frontier = new Set();\n const nestedLists = await Promise.all(batch.map(fetchOnce));\n for (const list of nestedLists) {\n for (const s of list) if (!seen.has(s)) frontier.add(s);\n }\n }\n },\n };\n}\n"],"mappings":"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","names":["decoder","encoder"]}
|