@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.
- package/LICENSE +21 -0
- package/README.md +318 -0
- package/package.json +55 -0
- package/src/bundles.js +545 -0
- package/src/compiler.js +1164 -0
- package/src/ctan.js +818 -0
- package/src/hash.js +102 -0
- package/src/index.js +32 -0
- package/src/storage.js +642 -0
- package/src/utils.js +33 -0
- package/src/worker.js +2217 -0
- package/types/bundles.d.ts +143 -0
- package/types/compiler.d.ts +288 -0
- package/types/ctan.d.ts +156 -0
- package/types/hash.d.ts +25 -0
- package/types/index.d.ts +5 -0
- package/types/storage.d.ts +124 -0
- package/types/utils.d.ts +16 -0
package/src/hash.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Centralized hashing using BLAKE3 (WASM) for ~10x faster hashing on large documents
|
|
2
|
+
// Falls back to DJB2 for sync contexts before BLAKE3 is initialized
|
|
3
|
+
|
|
4
|
+
let blake3Module = null;
|
|
5
|
+
let blake3LoadPromise = null;
|
|
6
|
+
|
|
7
|
+
// 8 bytes = 64-bit hash, sufficient for change detection (not cryptographic security)
|
|
8
|
+
const HASH_LENGTH = 8;
|
|
9
|
+
// Skip WASM overhead for tiny inputs where DJB2 is faster
|
|
10
|
+
const SMALL_INPUT_THRESHOLD = 128;
|
|
11
|
+
// Pre-allocated options object to avoid allocation on every hash call
|
|
12
|
+
const BLAKE3_OPTIONS = Object.freeze({ length: HASH_LENGTH });
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* DJB2 hash - fast fallback for small inputs or before BLAKE3 loads
|
|
16
|
+
* @param {string} content
|
|
17
|
+
* @returns {string} hex hash
|
|
18
|
+
*/
|
|
19
|
+
function djb2Hash(content) {
|
|
20
|
+
let hash = 5381 >>> 0;
|
|
21
|
+
const len = content.length;
|
|
22
|
+
for (let i = 0; i < len; i++) {
|
|
23
|
+
hash = ((hash * 33) ^ content.charCodeAt(i)) >>> 0;
|
|
24
|
+
}
|
|
25
|
+
return hash.toString(16).padStart(8, '0');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Initialize BLAKE3 module (called once, cached)
|
|
30
|
+
* @returns {Promise<object>}
|
|
31
|
+
*/
|
|
32
|
+
async function loadBlake3() {
|
|
33
|
+
if (blake3Module) return blake3Module;
|
|
34
|
+
if (blake3LoadPromise) return blake3LoadPromise;
|
|
35
|
+
|
|
36
|
+
blake3LoadPromise = import('blake3-wasm/browser.js').then(module => {
|
|
37
|
+
blake3Module = module;
|
|
38
|
+
return module;
|
|
39
|
+
}).catch(err => {
|
|
40
|
+
console.warn('BLAKE3 load failed, using DJB2 fallback:', err.message);
|
|
41
|
+
return null;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return blake3LoadPromise;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Hash content using BLAKE3 (async, preferred for large documents)
|
|
49
|
+
* @param {string} content
|
|
50
|
+
* @returns {Promise<string>} hex hash
|
|
51
|
+
*/
|
|
52
|
+
export async function hashAsync(content) {
|
|
53
|
+
// Use DJB2 for small inputs - WASM overhead not worth it
|
|
54
|
+
if (content.length < SMALL_INPUT_THRESHOLD) {
|
|
55
|
+
return djb2Hash(content);
|
|
56
|
+
}
|
|
57
|
+
const module = await loadBlake3();
|
|
58
|
+
if (module) {
|
|
59
|
+
return module.hash(content, BLAKE3_OPTIONS).toString('hex');
|
|
60
|
+
}
|
|
61
|
+
return djb2Hash(content);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Hash content synchronously - uses BLAKE3 if loaded, else DJB2
|
|
66
|
+
* Call initHash() early to ensure BLAKE3 is available for sync hashing
|
|
67
|
+
* @param {string} content
|
|
68
|
+
* @returns {string} hex hash
|
|
69
|
+
*/
|
|
70
|
+
export function hashSync(content) {
|
|
71
|
+
// Use DJB2 for small inputs - WASM overhead not worth it
|
|
72
|
+
if (content.length < SMALL_INPUT_THRESHOLD) {
|
|
73
|
+
return djb2Hash(content);
|
|
74
|
+
}
|
|
75
|
+
if (blake3Module) {
|
|
76
|
+
return blake3Module.hash(content, BLAKE3_OPTIONS).toString('hex');
|
|
77
|
+
}
|
|
78
|
+
return djb2Hash(content);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Initialize BLAKE3 for use with hashSync
|
|
83
|
+
* Call this early in app startup for best performance
|
|
84
|
+
* @returns {Promise<boolean>} true if BLAKE3 loaded successfully
|
|
85
|
+
*/
|
|
86
|
+
export async function initHash() {
|
|
87
|
+
const module = await loadBlake3();
|
|
88
|
+
return !!module;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Check if BLAKE3 is ready for sync hashing
|
|
93
|
+
* @returns {boolean}
|
|
94
|
+
*/
|
|
95
|
+
export function isBlake3Ready() {
|
|
96
|
+
return !!blake3Module;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Legacy export names for drop-in replacement
|
|
100
|
+
export { hashSync as hashContent };
|
|
101
|
+
export { hashSync as hashDocument };
|
|
102
|
+
export { hashSync as hashPreamble };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @siglum/engine
|
|
3
|
+
* Browser-based LaTeX compilation with lazy bundle loading.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```js
|
|
7
|
+
* import { SiglumCompiler } from '@siglum/engine';
|
|
8
|
+
*
|
|
9
|
+
* const compiler = new SiglumCompiler({
|
|
10
|
+
* bundlesUrl: '/bundles',
|
|
11
|
+
* onLog: console.log
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* await compiler.init();
|
|
15
|
+
* const result = await compiler.compile('\\documentclass{article}...');
|
|
16
|
+
* if (result.pdf) {
|
|
17
|
+
* // Use result.pdf (Uint8Array)
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export { SiglumCompiler, BusyTeXCompiler } from './compiler.js';
|
|
23
|
+
export { BundleManager, detectEngine, extractPreamble, hashPreamble } from './bundles.js';
|
|
24
|
+
export { CTANFetcher, getPackageFromFile, isValidPackageName, forceRefreshPackage } from './ctan.js';
|
|
25
|
+
export {
|
|
26
|
+
clearCTANCache,
|
|
27
|
+
hashDocument,
|
|
28
|
+
getCachedPdf,
|
|
29
|
+
saveCachedPdf,
|
|
30
|
+
listAllCachedPackages,
|
|
31
|
+
} from './storage.js';
|
|
32
|
+
export { createBatchedLogger } from './utils.js';
|