typst-wasm 0.0.1-alpha

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/wasm.js ADDED
@@ -0,0 +1,26 @@
1
+ import { loadWasmModule as loadWasmModule$1, wasmBinaryUrl } from "@typst-wasm/engine-wasm";
2
+
3
+ //#region src/wasm.ts
4
+ var wasm_default = wasmBinaryUrl;
5
+ const toWasmCompileOptions = (options = {}) => ({
6
+ format: options.format ?? "pdf",
7
+ main: options.main,
8
+ root: options.root,
9
+ inputs: options.inputs,
10
+ features: options.features,
11
+ creation_timestamp: options.creationTimestamp,
12
+ jobs: options.jobs,
13
+ diagnostic_format: options.diagnosticFormat,
14
+ pages: options.pages,
15
+ pdf_standards: options.pdfStandards,
16
+ pdf_tags: options.pdfTags,
17
+ ppi: options.ppi,
18
+ deps: options.deps,
19
+ deps_format: options.depsFormat,
20
+ timings: options.timings
21
+ });
22
+ const loadWasmModule = () => loadWasmModule$1();
23
+
24
+ //#endregion
25
+ export { wasm_default as default, loadWasmModule, toWasmCompileOptions, wasmBinaryUrl };
26
+ //# sourceMappingURL=wasm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wasm.js","names":["loadEngineWasmModule"],"sources":["../src/wasm.ts"],"sourcesContent":["import {\n loadWasmModule as loadEngineWasmModule,\n wasmBinaryUrl,\n type BundleFile as WasmBundleFile,\n type CompileOptions as EngineCompileOptions,\n type CompileOutput as EngineCompileOutput,\n type InitOutput,\n type PageOutput as WasmPageOutput,\n type TypstCompilerInstance,\n type WasmDiagnostic,\n type WasmModule,\n} from \"@typst-wasm/engine-wasm\";\nimport type { CompileOptions } from \"./types\";\n\nexport type {\n InitOutput,\n TypstCompilerInstance,\n WasmBundleFile,\n WasmDiagnostic,\n WasmPageOutput,\n WasmModule,\n};\n\nexport type WasmCompileOptions = EngineCompileOptions & {\n main?: string;\n root?: string;\n};\n\nexport type WasmCompileOutput = EngineCompileOutput;\n\nexport { wasmBinaryUrl };\nexport default wasmBinaryUrl;\n\nexport const toWasmCompileOptions = (\n options: CompileOptions = {},\n): WasmCompileOptions => ({\n format: options.format ?? \"pdf\",\n main: options.main,\n root: options.root,\n inputs: options.inputs,\n features: options.features,\n creation_timestamp: options.creationTimestamp,\n jobs: options.jobs,\n diagnostic_format: options.diagnosticFormat,\n pages: options.pages,\n pdf_standards: options.pdfStandards,\n pdf_tags: options.pdfTags,\n ppi: options.ppi,\n deps: options.deps,\n deps_format: options.depsFormat,\n timings: options.timings,\n});\n\nexport const loadWasmModule = (): Promise<WasmModule> => loadEngineWasmModule();\n"],"mappings":";;;AA+BA,mBAAe;AAEf,MAAa,wBACX,UAA0B,EAAE,MACJ;CACxB,QAAQ,QAAQ,UAAU;CAC1B,MAAM,QAAQ;CACd,MAAM,QAAQ;CACd,QAAQ,QAAQ;CAChB,UAAU,QAAQ;CAClB,oBAAoB,QAAQ;CAC5B,MAAM,QAAQ;CACd,mBAAmB,QAAQ;CAC3B,OAAO,QAAQ;CACf,eAAe,QAAQ;CACvB,UAAU,QAAQ;CAClB,KAAK,QAAQ;CACb,MAAM,QAAQ;CACd,aAAa,QAAQ;CACrB,SAAS,QAAQ;CAClB;AAED,MAAa,uBAA4CA,kBAAsB"}
package/dist/worker.js ADDED
@@ -0,0 +1,200 @@
1
+ //#region src/protocol.ts
2
+ const INITIAL_SAB_SIZE = 1024 * 1024;
3
+ const MAX_SAB_SIZE = 4 * 1024 * 1024 * 1024;
4
+ const DEFAULT_FETCH_TIMEOUT = 3e4;
5
+ const SharedMemoryCommunicationStatus = {
6
+ None: 0,
7
+ Pending: 1,
8
+ Error: 2,
9
+ Success: 3
10
+ };
11
+ var SharedMemoryCommunication = class SharedMemoryCommunication {
12
+ dataBuf;
13
+ statusBuf;
14
+ sizeBuf;
15
+ constructor() {
16
+ this.dataBuf = new SharedArrayBuffer(INITIAL_SAB_SIZE, { maxByteLength: MAX_SAB_SIZE });
17
+ this.statusBuf = new SharedArrayBuffer(4);
18
+ this.sizeBuf = new SharedArrayBuffer(4);
19
+ }
20
+ getStatus() {
21
+ return Atomics.load(new Int32Array(this.statusBuf), 0);
22
+ }
23
+ setStatus(status) {
24
+ const statusView = new Int32Array(this.statusBuf);
25
+ Atomics.store(statusView, 0, status);
26
+ Atomics.notify(statusView, 0, 1);
27
+ }
28
+ setBuffer(buf) {
29
+ const needed = buf.byteLength;
30
+ if (needed > MAX_SAB_SIZE) throw new Error(`File too large: ${needed} bytes. Maximum allowed: ${MAX_SAB_SIZE} bytes.`);
31
+ if (needed > this.dataBuf.byteLength) this.dataBuf.grow(needed);
32
+ new Uint8Array(this.dataBuf).set(buf);
33
+ Atomics.store(new Int32Array(this.sizeBuf), 0, needed);
34
+ }
35
+ getBuffer() {
36
+ const size = Atomics.load(new Int32Array(this.sizeBuf), 0);
37
+ return new Uint8Array(this.dataBuf, 0, size);
38
+ }
39
+ waitForStatusChange(expectedStatus, timeoutMs = DEFAULT_FETCH_TIMEOUT) {
40
+ return Atomics.wait(new Int32Array(this.statusBuf), 0, expectedStatus, timeoutMs) === "ok";
41
+ }
42
+ static hydrateObj(obj) {
43
+ const instantiation = new SharedMemoryCommunication();
44
+ instantiation.dataBuf = obj.dataBuf;
45
+ instantiation.statusBuf = obj.statusBuf;
46
+ instantiation.sizeBuf = obj.sizeBuf;
47
+ return instantiation;
48
+ }
49
+ };
50
+ //#endregion
51
+ //#region src/messages.ts
52
+ const isRecord = (value) => typeof value === "object" && value !== null;
53
+ const isMainToWorkerMessage = (value) => {
54
+ if (!isRecord(value)) return false;
55
+ if (typeof value.kind !== "string") return false;
56
+ return typeof value.requestId === "number";
57
+ };
58
+ new URL("./dist/typst_wasm_bg.wasm", import.meta.url);
59
+ const loadWasmModule$1 = () => import("./typst_wasm-Bjm0p5IN.js");
60
+ //#endregion
61
+ //#region src/wasm.ts
62
+ const loadWasmModule = () => loadWasmModule$1();
63
+ //#endregion
64
+ //#region src/worker.ts
65
+ var WorkerCommandError = class extends Error {
66
+ constructor(requestId, code, message, cause) {
67
+ super(message);
68
+ this.requestId = requestId;
69
+ this.code = code;
70
+ this.cause = cause;
71
+ this.name = "WorkerCommandError";
72
+ }
73
+ };
74
+ const toWorkerCommandError = (error, requestId) => error instanceof WorkerCommandError ? error : new WorkerCommandError(requestId, "COMMAND_FAILED", "Unhandled worker command failure", error);
75
+ let compiler = null;
76
+ let sharedMemoryCommunication = null;
77
+ let wasmExports = null;
78
+ const MAX_FETCH_ATTEMPTS = 3;
79
+ const textDecoder = new TextDecoder();
80
+ const writeResultLength = (resultLenPtr, len) => {
81
+ if (!wasmExports) throw new Error("WASM exports not initialized");
82
+ new DataView(wasmExports.memory.buffer).setUint32(resultLenPtr, len, true);
83
+ };
84
+ const pathFromWasm = (pathPtr, pathLen) => {
85
+ if (!wasmExports) throw new Error("WASM exports not initialized");
86
+ return textDecoder.decode(new Uint8Array(wasmExports.memory.buffer, pathPtr, pathLen));
87
+ };
88
+ const copyIntoWasm = (bytes, resultLenPtr) => {
89
+ if (!wasmExports) throw new Error("WASM exports not initialized");
90
+ const resultPtr = wasmExports.__wbindgen_malloc(bytes.length, 1);
91
+ new Uint8Array(wasmExports.memory.buffer, resultPtr, bytes.length).set(bytes);
92
+ writeResultLength(resultLenPtr, bytes.length);
93
+ return resultPtr;
94
+ };
95
+ const hostFetch = (pathPtr, pathLen, resultLenPtr) => {
96
+ if (!sharedMemoryCommunication) throw new Error("Communication buffer not initialized");
97
+ const path = pathFromWasm(pathPtr, pathLen);
98
+ for (let attempt = 0; attempt < MAX_FETCH_ATTEMPTS; attempt += 1) {
99
+ sharedMemoryCommunication.setStatus(SharedMemoryCommunicationStatus.Pending);
100
+ self.postMessage({
101
+ kind: "web_fetch",
102
+ payload: { path }
103
+ });
104
+ if (!sharedMemoryCommunication.waitForStatusChange(SharedMemoryCommunicationStatus.Pending, 3e4)) continue;
105
+ if (sharedMemoryCommunication.getStatus() === SharedMemoryCommunicationStatus.Success) return copyIntoWasm(sharedMemoryCommunication.getBuffer(), resultLenPtr);
106
+ }
107
+ writeResultLength(resultLenPtr, 0);
108
+ return 0;
109
+ };
110
+ const successResponse = (requestId, result) => ({
111
+ requestId,
112
+ result
113
+ });
114
+ const errorResponse = (requestId, code, message, cause) => ({
115
+ requestId,
116
+ error: {
117
+ code,
118
+ message,
119
+ cause
120
+ }
121
+ });
122
+ const ensureCompiler = (requestId) => {
123
+ if (compiler) return compiler;
124
+ throw new WorkerCommandError(requestId, "COMPILER_NOT_INITIALIZED", "Compiler not initialized");
125
+ };
126
+ const runCompilerCommand = (requestId, commandName, run) => {
127
+ const readyCompiler = ensureCompiler(requestId);
128
+ try {
129
+ return run(readyCompiler);
130
+ } catch (cause) {
131
+ throw new WorkerCommandError(requestId, "COMMAND_FAILED", `Worker command failed: ${commandName}`, cause);
132
+ }
133
+ };
134
+ const handleRequest = async (request) => {
135
+ switch (request.kind) {
136
+ case "init": {
137
+ sharedMemoryCommunication = SharedMemoryCommunication.hydrateObj(request.payload.sharedMemoryCommunication);
138
+ let wasmModule;
139
+ try {
140
+ wasmModule = await loadWasmModule();
141
+ } catch (cause) {
142
+ throw new WorkerCommandError(request.requestId, "INIT_FAILED", "Failed to load WASM module", cause);
143
+ }
144
+ try {
145
+ wasmExports = await wasmModule.default({
146
+ module_or_path: request.payload.moduleOrPath,
147
+ imports: { bridge: { host_fetch: hostFetch } }
148
+ });
149
+ compiler = new wasmModule.TypstCompiler();
150
+ } catch (cause) {
151
+ throw new WorkerCommandError(request.requestId, "INIT_FAILED", "Failed to initialize WASM worker", cause);
152
+ }
153
+ return successResponse(request.requestId, void 0);
154
+ }
155
+ case "add_file":
156
+ runCompilerCommand(request.requestId, "add_file", (readyCompiler) => {
157
+ readyCompiler.add_file(request.payload.path, request.payload.data);
158
+ });
159
+ return successResponse(request.requestId, void 0);
160
+ case "add_source":
161
+ runCompilerCommand(request.requestId, "add_source", (readyCompiler) => {
162
+ readyCompiler.add_source(request.payload.path, request.payload.text);
163
+ });
164
+ return successResponse(request.requestId, void 0);
165
+ case "add_font":
166
+ runCompilerCommand(request.requestId, "add_font", (readyCompiler) => {
167
+ readyCompiler.add_font(request.payload.data);
168
+ });
169
+ return successResponse(request.requestId, void 0);
170
+ case "remove_file":
171
+ runCompilerCommand(request.requestId, "remove_file", (readyCompiler) => {
172
+ readyCompiler.remove_file(request.payload.path);
173
+ });
174
+ return successResponse(request.requestId, void 0);
175
+ case "clear_files":
176
+ runCompilerCommand(request.requestId, "clear_files", (readyCompiler) => {
177
+ readyCompiler.clear_files();
178
+ });
179
+ return successResponse(request.requestId, void 0);
180
+ case "set_main":
181
+ runCompilerCommand(request.requestId, "set_main", (readyCompiler) => {
182
+ readyCompiler.set_main(request.payload.path);
183
+ });
184
+ return successResponse(request.requestId, void 0);
185
+ case "compile": return successResponse(request.requestId, runCompilerCommand(request.requestId, "compile", (readyCompiler) => readyCompiler.compile(request.payload.options)));
186
+ case "list_files": return successResponse(request.requestId, runCompilerCommand(request.requestId, "list_files", (readyCompiler) => readyCompiler.list_files()));
187
+ case "has_file": return successResponse(request.requestId, runCompilerCommand(request.requestId, "has_file", (readyCompiler) => readyCompiler.has_file(request.payload.path)));
188
+ }
189
+ };
190
+ self.onmessage = (event) => {
191
+ const data = event.data;
192
+ if (!isMainToWorkerMessage(data)) return;
193
+ handleRequest(data).catch((error) => {
194
+ const commandError = toWorkerCommandError(error, data.requestId);
195
+ return errorResponse(commandError.requestId, commandError.code, commandError.message, commandError.cause);
196
+ }).then((result) => {
197
+ self.postMessage(result);
198
+ });
199
+ };
200
+ //#endregion
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "typst-wasm",
3
+ "version": "0.0.1-alpha",
4
+ "description": "Typst WebAssembly compiler service with worker and JSPI backends",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./fonts": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./wasm": {
19
+ "types": "./dist/wasm.d.ts",
20
+ "default": "./dist/wasm.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "author": "will-lol",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/will-lol/typst-wasm.git"
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/will-lol/typst-wasm/issues"
34
+ },
35
+ "homepage": "https://github.com/will-lol/typst-wasm#readme",
36
+ "scripts": {
37
+ "fix": "pnpm run format:fix && pnpm exec eslint . --fix",
38
+ "lint": "pnpm exec eslint . --max-warnings=0",
39
+ "format": "pnpm exec prettier . --check",
40
+ "format:fix": "pnpm exec prettier . --write",
41
+ "knip": "pnpm exec knip"
42
+ },
43
+ "devDependencies": {
44
+ "@eslint/js": "^10.0.1",
45
+ "@types/bun": "^1.3.9",
46
+ "@types/deno": "^2.5.0",
47
+ "@types/node": "^25.2.0",
48
+ "eslint": "^10.0.0",
49
+ "knip": "^6.17.1",
50
+ "prettier": "^3.8.4",
51
+ "tsdown": "^0.20.3",
52
+ "typescript": "^5.9.3",
53
+ "typescript-eslint": "^8.55.0",
54
+ "vitest": "^4.0.16"
55
+ },
56
+ "dependencies": {
57
+ "@typst-wasm/engine-wasm": "0.0.1-alpha",
58
+ "@typst-wasm/fonts": "0.0.1-alpha",
59
+ "nanotar": "^0.2.0",
60
+ "tsdown-plugin-worker": "^0.1.6"
61
+ }
62
+ }