@siglum/engine 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Detect the appropriate engine from LaTeX source.
3
+ * @param {string} source - LaTeX source
4
+ * @returns {'pdflatex'|'xelatex'} Detected engine
5
+ */
6
+ export function detectEngine(source: string): "pdflatex" | "xelatex";
7
+ /**
8
+ * Extract preamble from LaTeX source (everything before \begin{document}).
9
+ * @param {string} source - LaTeX source
10
+ * @returns {string} Preamble content
11
+ */
12
+ export function extractPreamble(source: string): string;
13
+ /**
14
+ * @typedef {Object} BundleManagerOptions
15
+ * @property {string} [bundleBase] - Base URL for bundles
16
+ * @property {(msg: string) => void} [onLog] - Logging callback
17
+ * @property {(stage: string, detail: string) => void} [onProgress] - Progress callback
18
+ */
19
+ /**
20
+ * Manages LaTeX package bundles - loading, caching, and resolution.
21
+ */
22
+ export class BundleManager {
23
+ /**
24
+ * @param {BundleManagerOptions} [options] - Manager options
25
+ */
26
+ constructor(options?: BundleManagerOptions);
27
+ bundleBase: string;
28
+ bundleCache: Map<any, any>;
29
+ fileManifest: any;
30
+ packageMap: any;
31
+ bundleDeps: {
32
+ engines: any;
33
+ bundles: {};
34
+ deferred: any;
35
+ };
36
+ packageDeps: any;
37
+ bundleRegistry: Set<string>;
38
+ bytesDownloaded: number;
39
+ cacheHitCount: number;
40
+ onLog: (msg: string) => void;
41
+ onProgress: (stage: string, detail: string) => void;
42
+ /**
43
+ * Compute a hash for bundle versioning based on manifest entries
44
+ * Uses the file paths and sizes to detect changes
45
+ */
46
+ getBundleHash(bundleName: any): string;
47
+ /**
48
+ * Load bundle manifests from cache or server.
49
+ * @returns {Promise<Object>} File manifest
50
+ */
51
+ loadManifest(): Promise<any>;
52
+ _initFromBundlesData(bundlesData: any): void;
53
+ /**
54
+ * Load bundle dependency information.
55
+ * @returns {Promise<Object>} Bundle dependencies
56
+ */
57
+ loadBundleDeps(): Promise<any>;
58
+ /**
59
+ * Check if a bundle exists in the registry.
60
+ * @param {string} bundleName - Bundle name
61
+ * @returns {boolean}
62
+ */
63
+ bundleExists(bundleName: string): boolean;
64
+ /**
65
+ * Resolve packages to their required bundles.
66
+ * @param {string[]} packages - Package names
67
+ * @param {string} [engine] - Engine name
68
+ * @returns {string[]} Bundle names
69
+ */
70
+ resolveBundles(packages: string[], engine?: string): string[];
71
+ /**
72
+ * Extract packages from LaTeX source and resolve to bundles.
73
+ * @param {string} source - LaTeX source
74
+ * @param {string} [engine] - Engine name
75
+ * @returns {{packages: string[], bundles: string[]}}
76
+ */
77
+ checkPackages(source: string, engine?: string): {
78
+ packages: string[];
79
+ bundles: string[];
80
+ };
81
+ /**
82
+ * Pre-scan source to identify packages needing CTAN fetch.
83
+ * Expands detected packages with known dependencies from packageDeps.
84
+ * Scans additionalFiles for multi-file documents.
85
+ *
86
+ * @param {string} source - Main LaTeX source
87
+ * @param {string} engine - Engine (pdflatex, xelatex, etc.)
88
+ * @param {Object} additionalFiles - Optional map of filename → content
89
+ * @returns {{ bundledPackages: string[], ctanPackages: string[] }}
90
+ */
91
+ prescanForCtanPackages(source: string, engine?: string, additionalFiles?: any): {
92
+ bundledPackages: string[];
93
+ ctanPackages: string[];
94
+ };
95
+ /**
96
+ * Load a bundle by name.
97
+ * @param {string} bundleName - Bundle name
98
+ * @returns {Promise<ArrayBuffer|SharedArrayBuffer>} Bundle data
99
+ */
100
+ loadBundle(bundleName: string): Promise<ArrayBuffer | SharedArrayBuffer>;
101
+ /**
102
+ * Load multiple bundles in parallel.
103
+ * @param {string[]} bundleNames - Bundle names
104
+ * @returns {Promise<Object<string, ArrayBuffer|SharedArrayBuffer>>} Map of bundle data
105
+ */
106
+ loadBundles(bundleNames: string[]): Promise<{
107
+ [x: string]: ArrayBuffer | SharedArrayBuffer;
108
+ }>;
109
+ /**
110
+ * Get bundle loading statistics.
111
+ * @returns {{bytesDownloaded: number, cacheHits: number, bundlesCached: number}}
112
+ */
113
+ getStats(): {
114
+ bytesDownloaded: number;
115
+ cacheHits: number;
116
+ bundlesCached: number;
117
+ };
118
+ /**
119
+ * Clear in-memory bundle cache to free RAM. Filesystem cache is preserved.
120
+ */
121
+ clearCache(): void;
122
+ /**
123
+ * Preload all required bundles for an engine.
124
+ * @param {string} [engine] - Engine name
125
+ * @returns {Promise<void>}
126
+ */
127
+ preloadEngine(engine?: string): Promise<void>;
128
+ }
129
+ export { hashPreamble } from "./hash.js";
130
+ export type BundleManagerOptions = {
131
+ /**
132
+ * - Base URL for bundles
133
+ */
134
+ bundleBase?: string;
135
+ /**
136
+ * - Logging callback
137
+ */
138
+ onLog?: (msg: string) => void;
139
+ /**
140
+ * - Progress callback
141
+ */
142
+ onProgress?: (stage: string, detail: string) => void;
143
+ };
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Browser-based LaTeX compiler using WebAssembly.
3
+ * Handles bundle loading, CTAN fetching, and compilation orchestration.
4
+ */
5
+ export class SiglumCompiler {
6
+ /**
7
+ * @param {SiglumCompilerOptions} [options] - Compiler options
8
+ */
9
+ constructor(options?: SiglumCompilerOptions);
10
+ bundlesUrl: string;
11
+ wasmUrl: string;
12
+ jsUrl: string;
13
+ workerUrl: string;
14
+ ctanProxyUrl: string;
15
+ xzwasmUrl: string;
16
+ bundleManager: BundleManager;
17
+ ctanFetcher: CTANFetcher;
18
+ worker: Worker;
19
+ workerReady: boolean;
20
+ wasmModule: WebAssembly.Module;
21
+ _initWorkerPromise: Promise<any>;
22
+ pendingCompile: {
23
+ resolve: (result: any) => Promise<void>;
24
+ reject: (error: any) => void;
25
+ };
26
+ formatGenerationPromise: any;
27
+ onLog: (msg: string) => void;
28
+ onProgress: (stage: string, detail: string) => void;
29
+ enableCtan: boolean;
30
+ enableLazyFS: boolean;
31
+ enableDocCache: boolean;
32
+ maxRetries: number;
33
+ verbose: boolean;
34
+ eagerBundles: string[] | {
35
+ [x: string]: string[];
36
+ };
37
+ _pendingRangeRequests: Map<any, any>;
38
+ _rangeRequestTimer: number;
39
+ _rangeRequestDebounceMs: number;
40
+ _rangeCoalesceGapBytes: number;
41
+ _log(msg: any): void;
42
+ /**
43
+ * Get eager bundles for a specific engine.
44
+ * Eager bundles are loaded immediately instead of being deferred.
45
+ * @param {string} engine - The engine (pdflatex, xelatex, lualatex)
46
+ * @returns {string[]} List of bundle names to load eagerly
47
+ */
48
+ getEagerBundles(engine: string): string[];
49
+ /**
50
+ * Preload bundles into memory cache.
51
+ * Call this to eagerly load bundles before compilation.
52
+ * Useful for loading large deferred bundles (like cm-super) in advance.
53
+ *
54
+ * @example
55
+ * // Preload cm-super before first compile
56
+ * await compiler.preloadBundles(['cm-super']);
57
+ *
58
+ * @param {string[]} bundleNames - Bundles to preload
59
+ * @returns {Promise<{loaded: string[], failed: string[]}>}
60
+ */
61
+ preloadBundles(bundleNames: string[]): Promise<{
62
+ loaded: string[];
63
+ failed: string[];
64
+ }>;
65
+ /**
66
+ * Pre-warm the compiler in the background.
67
+ * Call this early (e.g., on page load) to eliminate cold start latency.
68
+ * The promise resolves when initialization is complete, but you don't need to await it.
69
+ *
70
+ * @example
71
+ * // On app mount, before user starts typing
72
+ * const compiler = new BusyTeXCompiler(options);
73
+ * compiler.prewarm(); // Fire and forget - init happens in background
74
+ *
75
+ * // Later, when user wants to compile:
76
+ * await compiler.compile(source); // Already warmed up!
77
+ *
78
+ * @returns {Promise<void>} Resolves when initialization is complete
79
+ */
80
+ prewarm(): Promise<void>;
81
+ _prewarmPromise: any;
82
+ /**
83
+ * Check if compiler is ready (initialized and warmed up)
84
+ * @returns {boolean}
85
+ */
86
+ isReady(): boolean;
87
+ /**
88
+ * Initialize the compiler. Loads WASM, manifests, and prepares the worker.
89
+ * @returns {Promise<void>}
90
+ */
91
+ init(): Promise<void>;
92
+ _loadManifests(): Promise<void>;
93
+ _loadWasm(): Promise<void>;
94
+ _initWorker(): Promise<any>;
95
+ _doInitWorker(): Promise<any>;
96
+ _handleWorkerMessage(e: any): void;
97
+ pendingFormat: {
98
+ resolve: (result: any) => void;
99
+ reject: (error: any) => void;
100
+ };
101
+ _handleWorkerError(e: any): void;
102
+ _handleCtanFetchRequest(msg: any): Promise<void>;
103
+ _handleBundleFetchRequest(msg: any): Promise<void>;
104
+ /**
105
+ * Queue a range request for batching. Requests are coalesced and fetched
106
+ * together to reduce HTTP overhead.
107
+ */
108
+ _queueRangeRequest(msg: any): void;
109
+ /**
110
+ * Process all pending range requests, coalescing nearby ranges.
111
+ */
112
+ _processRangeRequestBatch(): Promise<void>;
113
+ /**
114
+ * Coalesce nearby ranges to reduce HTTP requests.
115
+ * Returns groups of original requests that can be satisfied by a single fetch.
116
+ */
117
+ _coalesceRanges(requests: any): any[][];
118
+ /**
119
+ * Fetch a coalesced range and distribute data to original requesters.
120
+ */
121
+ _fetchCoalescedRange(bundleName: any, group: any): Promise<void>;
122
+ _handleMemorySnapshot(msg: any): Promise<void>;
123
+ /**
124
+ * Compile LaTeX source to PDF.
125
+ * @param {string} source - LaTeX source code
126
+ * @param {CompileOptions} [options] - Compilation options
127
+ * @returns {Promise<CompileResult>} Compilation result with PDF or error
128
+ */
129
+ compile(source: string, options?: CompileOptions): Promise<CompileResult>;
130
+ _fmtMemCache: any;
131
+ /**
132
+ * Pre-generate a format file for faster subsequent compilations.
133
+ * @param {string} source - LaTeX source with preamble to cache
134
+ * @param {{engine?: string}} [options] - Options
135
+ * @returns {Promise<Uint8Array|null>} Format data or null if not supported
136
+ */
137
+ generateFormat(source: string, options?: {
138
+ engine?: string;
139
+ }): Promise<Uint8Array | null>;
140
+ /**
141
+ * Clear all caches (CTAN packages, mounted files).
142
+ * @returns {Promise<void>}
143
+ */
144
+ clearCache(): Promise<void>;
145
+ /**
146
+ * Get compiler statistics.
147
+ * @returns {{bundles: Object, ctan: Object}} Statistics from bundle manager and CTAN fetcher
148
+ */
149
+ getStats(): {
150
+ bundles: any;
151
+ ctan: any;
152
+ };
153
+ /**
154
+ * Terminate the worker. Call unload() for full cleanup.
155
+ */
156
+ terminate(): void;
157
+ /**
158
+ * Unload compiler to free memory. Clears RAM caches but keeps disk caches.
159
+ * Call init() again to reinitialize.
160
+ */
161
+ unload(): void;
162
+ /**
163
+ * Check if compiler is currently loaded.
164
+ * @returns {boolean}
165
+ */
166
+ isLoaded(): boolean;
167
+ }
168
+ /**
169
+ * Backwards-compatible alias for SiglumCompiler.
170
+ * @type {typeof SiglumCompiler}
171
+ */
172
+ export const BusyTeXCompiler: typeof SiglumCompiler;
173
+ export type SiglumCompilerOptions = {
174
+ /**
175
+ * - URL to bundles directory
176
+ */
177
+ bundlesUrl?: string;
178
+ /**
179
+ * - URL to busytex.wasm
180
+ */
181
+ wasmUrl?: string;
182
+ /**
183
+ * - URL to busytex.js (derived from wasmUrl if not provided)
184
+ */
185
+ jsUrl?: string;
186
+ /**
187
+ * - URL to worker.js or null for embedded worker
188
+ */
189
+ workerUrl?: string | null;
190
+ /**
191
+ * - CTAN proxy URL
192
+ */
193
+ ctanProxyUrl?: string;
194
+ /**
195
+ * - XZ decompression WASM URL
196
+ */
197
+ xzwasmUrl?: string;
198
+ /**
199
+ * - Logging callback
200
+ */
201
+ onLog?: (msg: string) => void;
202
+ /**
203
+ * - Progress callback
204
+ */
205
+ onProgress?: (stage: string, detail: string) => void;
206
+ /**
207
+ * - Enable CTAN fetching (default: true if ctanProxyUrl provided)
208
+ */
209
+ enableCtan?: boolean;
210
+ /**
211
+ * - Enable lazy filesystem (default: true)
212
+ */
213
+ enableLazyFS?: boolean;
214
+ /**
215
+ * - Enable document cache (default: true)
216
+ */
217
+ enableDocCache?: boolean;
218
+ /**
219
+ * - Max fetch retries per compile (default: 15)
220
+ */
221
+ maxRetries?: number;
222
+ /**
223
+ * - Log TeX stdout (default: false)
224
+ */
225
+ verbose?: boolean;
226
+ /**
227
+ * - Bundles to load eagerly
228
+ */
229
+ eagerBundles?: string[] | {
230
+ [x: string]: string[];
231
+ };
232
+ };
233
+ export type CompileOptions = {
234
+ /**
235
+ * - 'pdflatex', 'xelatex', or 'lualatex'
236
+ */
237
+ engine?: string;
238
+ /**
239
+ * - Use document cache
240
+ */
241
+ useCache?: boolean;
242
+ /**
243
+ * - Additional files for compilation
244
+ */
245
+ additionalFiles?: {
246
+ [x: string]: string | Uint8Array;
247
+ };
248
+ };
249
+ export type CompileResult = {
250
+ /**
251
+ * - Whether compilation succeeded
252
+ */
253
+ success: boolean;
254
+ /**
255
+ * - Compiled PDF bytes
256
+ */
257
+ pdf?: Uint8Array;
258
+ /**
259
+ * - True if PDF is in SharedArrayBuffer
260
+ */
261
+ pdfIsShared?: boolean;
262
+ /**
263
+ * - SyncTeX data for source mapping
264
+ */
265
+ syncTexData?: any | null;
266
+ /**
267
+ * - Compilation statistics
268
+ */
269
+ stats?: any;
270
+ /**
271
+ * - TeX compilation log
272
+ */
273
+ log?: string;
274
+ /**
275
+ * - Error message if failed
276
+ */
277
+ error?: string;
278
+ /**
279
+ * - TeX exit code if failed
280
+ */
281
+ exitCode?: number;
282
+ /**
283
+ * - True if result was from cache
284
+ */
285
+ cached?: boolean;
286
+ };
287
+ import { BundleManager } from './bundles.js';
288
+ import { CTANFetcher } from './ctan.js';
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Extract package name from a missing file path.
3
+ * Handles special cases like EC/TC fonts (cm-super).
4
+ * @param {string} filename - File name (e.g., "lingmacros.sty")
5
+ * @returns {string} Package name
6
+ */
7
+ export function getPackageFromFile(filename: string): string;
8
+ /**
9
+ * Check if a string is a valid CTAN package name.
10
+ * @param {string} name - Package name to validate
11
+ * @returns {boolean} True if valid
12
+ */
13
+ export function isValidPackageName(name: string): boolean;
14
+ /**
15
+ * Force clear a specific package from cache (for version refresh).
16
+ * @param {string} packageName - Package name to clear
17
+ * @returns {Promise<boolean>} True if successful
18
+ */
19
+ export function forceRefreshPackage(packageName: string): Promise<boolean>;
20
+ /**
21
+ * @typedef {Object} CTANFetcherOptions
22
+ * @property {string} [proxyUrl] - CTAN proxy URL
23
+ * @property {string} [bundlesUrl] - Bundles URL
24
+ * @property {string} [xzwasmUrl] - XZ decompression WASM URL
25
+ * @property {(msg: string) => void} [onLog] - Logging callback
26
+ */
27
+ /**
28
+ * @typedef {Object} PackageResult
29
+ * @property {Map<string, Uint8Array>} files - Map of file paths to contents
30
+ * @property {string[]} dependencies - Package dependencies
31
+ * @property {boolean} [notFound] - True if package was not found
32
+ */
33
+ /**
34
+ * Fetches LaTeX packages from CTAN/TexLive archives.
35
+ */
36
+ export class CTANFetcher {
37
+ /**
38
+ * @param {CTANFetcherOptions} [options] - Fetcher options
39
+ */
40
+ constructor(options?: CTANFetcherOptions);
41
+ proxyUrl: string;
42
+ bundlesUrl: string;
43
+ mountedFiles: Set<any>;
44
+ fileCache: Map<any, any>;
45
+ fetchCount: number;
46
+ onLog: (msg: string) => void;
47
+ /**
48
+ * Load file-to-package index (maps filename.sty → package-name).
49
+ * @returns {Promise<Object<string, string>>} Index mapping filenames to packages
50
+ */
51
+ loadFileToPackageIndex(): Promise<{
52
+ [x: string]: string;
53
+ }>;
54
+ /**
55
+ * Look up package name for a file.
56
+ * @param {string} fileName - File name (e.g., "lingmacros.sty")
57
+ * @returns {Promise<string|null>} Package name or null
58
+ */
59
+ lookupPackageForFile(fileName: string): Promise<string | null>;
60
+ /**
61
+ * Get all cached file contents.
62
+ * @returns {Object<string, Uint8Array>} Map of file paths to contents
63
+ */
64
+ getCachedFiles(): {
65
+ [x: string]: Uint8Array;
66
+ };
67
+ /**
68
+ * Load a package from cache.
69
+ * @param {string} packageName - Package name
70
+ * @returns {Promise<PackageResult|null>} Package result or null if not cached
71
+ */
72
+ loadPackageFromCache(packageName: string): Promise<PackageResult | null>;
73
+ /**
74
+ * Fetch a package from CTAN/TexLive.
75
+ * @param {string} packageName - Package name
76
+ * @param {{tlYear?: number}} [options] - Options
77
+ * @returns {Promise<PackageResult|null>} Package result or null if not found
78
+ */
79
+ fetchPackage(packageName: string, options?: {
80
+ tlYear?: number;
81
+ }): Promise<PackageResult | null>;
82
+ lookupTexLivePackageName(packageName: any): Promise<any>;
83
+ fetchTexLivePackage(packageName: any, tlYear?: any): Promise<{
84
+ files: Map<any, any>;
85
+ dependencies: any;
86
+ }>;
87
+ fetchCtanPackage(packageName: any, tlYear?: any): Promise<{
88
+ files: Map<any, any>;
89
+ dependencies: any;
90
+ }>;
91
+ fetchWithDependencies(packageName: any, fetched?: Set<any>): Promise<Map<any, any>>;
92
+ /**
93
+ * Batch fetch multiple packages in parallel.
94
+ * Used for pre-fetching detected CTAN packages before compilation.
95
+ * @param {string[]} packageNames - Package names to fetch
96
+ * @param {Object} options
97
+ * @param {number} options.concurrency - Max parallel fetches (default: 1, see note below)
98
+ * @returns {Promise<{fetched: string[], failed: string[], skipped: string[]}>}
99
+ */
100
+ batchFetchPackages(packageNames: string[], options?: {
101
+ concurrency: number;
102
+ }): Promise<{
103
+ fetched: string[];
104
+ failed: string[];
105
+ skipped: string[];
106
+ }>;
107
+ /**
108
+ * Get list of all mounted file paths.
109
+ * @returns {string[]} Array of file paths
110
+ */
111
+ getMountedFiles(): string[];
112
+ /**
113
+ * Get fetcher statistics.
114
+ * @returns {{fetchCount: number, mountedFiles: number}} Stats object
115
+ */
116
+ getStats(): {
117
+ fetchCount: number;
118
+ mountedFiles: number;
119
+ };
120
+ /**
121
+ * Clear the mounted files set.
122
+ */
123
+ clearMountedFiles(): void;
124
+ }
125
+ export type CTANFetcherOptions = {
126
+ /**
127
+ * - CTAN proxy URL
128
+ */
129
+ proxyUrl?: string;
130
+ /**
131
+ * - Bundles URL
132
+ */
133
+ bundlesUrl?: string;
134
+ /**
135
+ * - XZ decompression WASM URL
136
+ */
137
+ xzwasmUrl?: string;
138
+ /**
139
+ * - Logging callback
140
+ */
141
+ onLog?: (msg: string) => void;
142
+ };
143
+ export type PackageResult = {
144
+ /**
145
+ * - Map of file paths to contents
146
+ */
147
+ files: Map<string, Uint8Array>;
148
+ /**
149
+ * - Package dependencies
150
+ */
151
+ dependencies: string[];
152
+ /**
153
+ * - True if package was not found
154
+ */
155
+ notFound?: boolean;
156
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Hash content using BLAKE3 (async, preferred for large documents)
3
+ * @param {string} content
4
+ * @returns {Promise<string>} hex hash
5
+ */
6
+ export function hashAsync(content: string): Promise<string>;
7
+ /**
8
+ * Hash content synchronously - uses BLAKE3 if loaded, else DJB2
9
+ * Call initHash() early to ensure BLAKE3 is available for sync hashing
10
+ * @param {string} content
11
+ * @returns {string} hex hash
12
+ */
13
+ export function hashSync(content: string): string;
14
+ /**
15
+ * Initialize BLAKE3 for use with hashSync
16
+ * Call this early in app startup for best performance
17
+ * @returns {Promise<boolean>} true if BLAKE3 loaded successfully
18
+ */
19
+ export function initHash(): Promise<boolean>;
20
+ /**
21
+ * Check if BLAKE3 is ready for sync hashing
22
+ * @returns {boolean}
23
+ */
24
+ export function isBlake3Ready(): boolean;
25
+ export { hashSync as hashContent, hashSync as hashDocument, hashSync as hashPreamble };
@@ -0,0 +1,5 @@
1
+ export { createBatchedLogger } from "./utils.js";
2
+ export { SiglumCompiler, BusyTeXCompiler } from "./compiler.js";
3
+ export { BundleManager, detectEngine, extractPreamble, hashPreamble } from "./bundles.js";
4
+ export { CTANFetcher, getPackageFromFile, isValidPackageName, forceRefreshPackage } from "./ctan.js";
5
+ export { clearCTANCache, hashDocument, getCachedPdf, saveCachedPdf, listAllCachedPackages } from "./storage.js";