markstream-solid 0.0.0-stage → 2.0.23
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/README.md +120 -2
- package/dist/client/extended-BTbixd9o.js +38 -0
- package/dist/client/index.js +5640 -0
- package/dist/client/katexWorkerClient-ZLzkPVv_.js +260 -0
- package/dist/client/languageIcon-CDTPjsAQ.js +280 -0
- package/dist/client/mermaidWorkerClient-BAvGYsCO.js +243 -0
- package/dist/client/utils/index.js +31 -0
- package/dist/client/utils/katex-threshold.js +32 -0
- package/dist/client/utils/performance-monitor.js +73 -0
- package/dist/client/utils/safeRaf.js +27 -0
- package/dist/client/workers/katexCdnWorker.js +177 -0
- package/dist/client/workers/katexRenderer.worker.js +45 -0
- package/dist/client/workers/katexWorkerClient.js +16 -0
- package/dist/client/workers/mermaidCdnWorker.js +226 -0
- package/dist/client/workers/mermaidParser.worker.js +73 -0
- package/dist/client/workers/mermaidWorkerClient.js +13 -0
- package/dist/components/BuiltinNodes.d.ts +42 -0
- package/dist/components/CodeBlockNode.d.ts +10 -0
- package/dist/components/DiagramBlockNode.d.ts +27 -0
- package/dist/components/HtmlPreviewFrame.d.ts +10 -0
- package/dist/components/MarkstreamProvider.d.ts +5 -0
- package/dist/components/MarkstreamVirtualTimeline.d.ts +35 -0
- package/dist/components/MathNode.d.ts +6 -0
- package/dist/components/NodeRenderer.d.ts +27 -0
- package/dist/components/NodeSlot.d.ts +11 -0
- package/dist/components/PreCodeBlock.d.ts +3 -0
- package/dist/components/PreCodeNode.d.ts +9 -0
- package/dist/components/RenderNode.d.ts +11 -0
- package/dist/components/StreamingTextNode.d.ts +2 -0
- package/dist/components/ToolbarButton.d.ts +14 -0
- package/dist/components/ToolbarIcon.d.ts +18 -0
- package/dist/components/Tooltip.d.ts +13 -0
- package/dist/components/lifecycle.d.ts +6 -0
- package/dist/components/mathHeightCache.d.ts +5 -0
- package/dist/components/rendererModeDefaults.d.ts +4 -0
- package/dist/components/shared/diagram-height.d.ts +16 -0
- package/dist/components/shared/node-helpers.d.ts +78 -0
- package/dist/components/shared/node-outlet-helpers.d.ts +10 -0
- package/dist/components/shared/render-window.d.ts +18 -0
- package/dist/components/shared/reuseParsedNodes.d.ts +3 -0
- package/dist/components/viewport.d.ts +5 -0
- package/dist/components/virtualLayoutKey.d.ts +13 -0
- package/dist/configuration.d.ts +9 -0
- package/dist/customComponents.d.ts +36 -0
- package/dist/enhanceRenderedHtml.d.ts +25 -0
- package/dist/hydrateCustomTagContent.d.ts +2 -0
- package/dist/i18n/useSafeI18n.d.ts +4 -0
- package/dist/icon-themes/index.d.ts +2 -0
- package/dist/icon-themes/material/core.d.ts +2 -0
- package/dist/icon-themes/material/extended.d.ts +2 -0
- package/dist/icon-themes/material/index.d.ts +2 -0
- package/dist/icon-themes/registry.d.ts +9 -0
- package/dist/icon-themes/types.d.ts +11 -0
- package/dist/index.css +3279 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.px.css +3279 -0
- package/dist/index.tailwind.css +3279 -0
- package/dist/optional/d2.d.ts +17 -0
- package/dist/optional/infographic.d.ts +31 -0
- package/dist/optional/katex.d.ts +9 -0
- package/dist/optional/mermaid.d.ts +27 -0
- package/dist/optional/streamDiffs.d.ts +29 -0
- package/dist/renderMarkdownHtml.d.ts +30 -0
- package/dist/sanitizeHtmlContent.d.ts +2 -0
- package/dist/server/extended-CKyCqQt3.js +69 -0
- package/dist/server/index.js +6264 -0
- package/dist/server/languageIcon-TXkIEBPf.js +353 -0
- package/dist/server/utils/index.js +31 -0
- package/dist/server/utils/katex-threshold.js +41 -0
- package/dist/server/utils/performance-monitor.js +120 -0
- package/dist/server/utils/safeRaf.js +28 -0
- package/dist/server/workers/katexCdnWorker.js +190 -0
- package/dist/server/workers/katexWorkerClient.js +373 -0
- package/dist/server/workers/mermaidCdnWorker.js +238 -0
- package/dist/server/workers/mermaidWorkerClient.js +333 -0
- package/dist/streamDiffsWorker.d.ts +72 -0
- package/dist/tailwind.d.ts +2 -0
- package/dist/tailwind.js +3 -0
- package/dist/tooltip/singletonTooltip.d.ts +7 -0
- package/dist/types/component-props.d.ts +253 -0
- package/dist/types/node-renderer-props.d.ts +333 -0
- package/dist/useSmoothMarkdownStream.d.ts +22 -0
- package/dist/utils/clipboard.d.ts +1 -0
- package/dist/utils/d2RenderSalt.d.ts +1 -0
- package/dist/utils/footnoteTarget.d.ts +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/katex-threshold.d.ts +20 -0
- package/dist/utils/languageIcon.d.ts +8 -0
- package/dist/utils/mermaidRenderId.d.ts +1 -0
- package/dist/utils/normalizeKaTeXRenderInput.d.ts +1 -0
- package/dist/utils/performance-monitor.d.ts +121 -0
- package/dist/utils/resolveLanguageIcon.d.ts +2 -0
- package/dist/utils/safeRaf.d.ts +2 -0
- package/dist/utils/virtualItemMeasurement.d.ts +5 -0
- package/dist/virtual-adapter.d.ts +165 -0
- package/dist/workers/katexCdnWorker.d.ts +20 -0
- package/dist/workers/katexRenderer.worker.d.ts +1 -0
- package/dist/workers/katexWorkerClient.d.ts +35 -0
- package/dist/workers/mermaidCdnWorker.d.ts +15 -0
- package/dist/workers/mermaidParser.worker.d.ts +1 -0
- package/dist/workers/mermaidWorkerClient.d.ts +15 -0
- package/package.json +149 -3
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
function stringifyForWorker(value) {
|
|
2
|
+
return JSON.stringify(value);
|
|
3
|
+
}
|
|
4
|
+
function buildKaTeXCDNWorkerSource(options) {
|
|
5
|
+
const mode = options.mode ?? "classic";
|
|
6
|
+
const renderOptions = {
|
|
7
|
+
throwOnError: true,
|
|
8
|
+
displayMode: true,
|
|
9
|
+
output: "html",
|
|
10
|
+
strict: "ignore",
|
|
11
|
+
...options.renderOptions || {}
|
|
12
|
+
};
|
|
13
|
+
const renderOptionsLiteral = stringifyForWorker(renderOptions);
|
|
14
|
+
const katexUrlLiteral = stringifyForWorker(options.katexUrl);
|
|
15
|
+
const mhchemUrlLiteral = options.mhchemUrl ? stringifyForWorker(options.mhchemUrl) : '""';
|
|
16
|
+
if (mode === "module") {
|
|
17
|
+
return `
|
|
18
|
+
let DEBUG = false
|
|
19
|
+
let katex = null
|
|
20
|
+
let katexLoadError = null
|
|
21
|
+
let loadPromise = null
|
|
22
|
+
|
|
23
|
+
function normalizeKaTeX(mod) {
|
|
24
|
+
const resolved = mod && mod.default ? mod.default : mod
|
|
25
|
+
if (resolved && typeof resolved.renderToString === 'function')
|
|
26
|
+
return resolved
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function loadKaTeX() {
|
|
31
|
+
if (katex || katexLoadError)
|
|
32
|
+
return
|
|
33
|
+
if (!loadPromise) {
|
|
34
|
+
loadPromise = (async () => {
|
|
35
|
+
try {
|
|
36
|
+
const mod = await import(${katexUrlLiteral})
|
|
37
|
+
katex = normalizeKaTeX(mod) || null
|
|
38
|
+
const mhchemUrl = ${mhchemUrlLiteral}
|
|
39
|
+
if (mhchemUrl) {
|
|
40
|
+
try {
|
|
41
|
+
await import(mhchemUrl)
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
if (DEBUG)
|
|
45
|
+
console.warn('[markstream-solid:katex-cdn-worker] failed to load mhchem', e)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
katexLoadError = e
|
|
51
|
+
}
|
|
52
|
+
})()
|
|
53
|
+
}
|
|
54
|
+
await loadPromise
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
globalThis.addEventListener('message', async (ev) => {
|
|
58
|
+
const data = ev.data || {}
|
|
59
|
+
if (data.type === 'init') {
|
|
60
|
+
DEBUG = !!data.debug
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const id = data.id ?? ''
|
|
65
|
+
const content = data.content ?? ''
|
|
66
|
+
const displayMode = data.displayMode ?? true
|
|
67
|
+
|
|
68
|
+
await loadKaTeX()
|
|
69
|
+
|
|
70
|
+
if (!katex) {
|
|
71
|
+
const reason = katexLoadError ? String(katexLoadError?.message || katexLoadError) : 'KaTeX is not available in worker'
|
|
72
|
+
globalThis.postMessage({ id, error: reason, content, displayMode })
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const opts = ${renderOptionsLiteral}
|
|
78
|
+
const html = katex.renderToString(content, { ...opts, displayMode: !!displayMode })
|
|
79
|
+
globalThis.postMessage({ id, html, content, displayMode })
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
globalThis.postMessage({ id, error: String(err?.message || err), content, displayMode })
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
`.trimStart();
|
|
86
|
+
}
|
|
87
|
+
return `
|
|
88
|
+
let DEBUG = false
|
|
89
|
+
let katex = null
|
|
90
|
+
let katexLoadError = null
|
|
91
|
+
|
|
92
|
+
function normalizeKaTeX(val) {
|
|
93
|
+
const resolved = val && val.default ? val.default : val
|
|
94
|
+
if (resolved && typeof resolved.renderToString === 'function')
|
|
95
|
+
return resolved
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function loadKaTeXClassic() {
|
|
100
|
+
if (katex || katexLoadError)
|
|
101
|
+
return
|
|
102
|
+
try {
|
|
103
|
+
importScripts(${katexUrlLiteral})
|
|
104
|
+
const mhchemUrl = ${mhchemUrlLiteral}
|
|
105
|
+
if (mhchemUrl) {
|
|
106
|
+
try {
|
|
107
|
+
importScripts(mhchemUrl)
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
if (DEBUG)
|
|
111
|
+
console.warn('[markstream-solid:katex-cdn-worker] failed to load mhchem', e)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
katex = normalizeKaTeX(globalThis.katex)
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
katexLoadError = e
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
loadKaTeXClassic()
|
|
122
|
+
|
|
123
|
+
globalThis.addEventListener('message', (ev) => {
|
|
124
|
+
const data = ev.data || {}
|
|
125
|
+
if (data.type === 'init') {
|
|
126
|
+
DEBUG = !!data.debug
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const id = data.id ?? ''
|
|
131
|
+
const content = data.content ?? ''
|
|
132
|
+
const displayMode = data.displayMode ?? true
|
|
133
|
+
|
|
134
|
+
if (!katex && !katexLoadError)
|
|
135
|
+
loadKaTeXClassic()
|
|
136
|
+
|
|
137
|
+
if (!katex) {
|
|
138
|
+
const reason = katexLoadError ? String(katexLoadError?.message || katexLoadError) : 'KaTeX is not available in worker'
|
|
139
|
+
globalThis.postMessage({ id, error: reason, content, displayMode })
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const opts = ${renderOptionsLiteral}
|
|
145
|
+
const html = katex.renderToString(content, { ...opts, displayMode: !!displayMode })
|
|
146
|
+
globalThis.postMessage({ id, html, content, displayMode })
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
globalThis.postMessage({ id, error: String(err?.message || err), content, displayMode })
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
`.trimStart();
|
|
153
|
+
}
|
|
154
|
+
function createKaTeXWorkerFromCDN(options) {
|
|
155
|
+
const source = buildKaTeXCDNWorkerSource(options);
|
|
156
|
+
if (typeof Worker === "undefined" || typeof URL === "undefined" || typeof Blob === "undefined") {
|
|
157
|
+
return {
|
|
158
|
+
worker: null,
|
|
159
|
+
dispose: () => {
|
|
160
|
+
},
|
|
161
|
+
source
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const blob = new Blob([source], { type: "text/javascript" });
|
|
165
|
+
const url = URL.createObjectURL(blob);
|
|
166
|
+
let revoked = false;
|
|
167
|
+
const dispose = () => {
|
|
168
|
+
if (revoked)
|
|
169
|
+
return;
|
|
170
|
+
revoked = true;
|
|
171
|
+
try {
|
|
172
|
+
URL.revokeObjectURL(url);
|
|
173
|
+
} catch {
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const mode = options.mode ?? "classic";
|
|
177
|
+
const workerOptions = mode === "module" ? { ...options.workerOptions ?? {}, type: "module" } : options.workerOptions;
|
|
178
|
+
const worker = new Worker(url, workerOptions);
|
|
179
|
+
if (options.debug) {
|
|
180
|
+
try {
|
|
181
|
+
worker.postMessage({ type: "init", debug: true });
|
|
182
|
+
} catch {
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return { worker, dispose, source };
|
|
186
|
+
}
|
|
187
|
+
export {
|
|
188
|
+
buildKaTeXCDNWorkerSource,
|
|
189
|
+
createKaTeXWorkerFromCDN
|
|
190
|
+
};
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
let katex = null;
|
|
2
|
+
let importAttempted = false;
|
|
3
|
+
let pendingImport = null;
|
|
4
|
+
let katexLoader = defaultKatexLoader;
|
|
5
|
+
function normalizeKatexModule(mod) {
|
|
6
|
+
const resolved = mod?.default ?? mod;
|
|
7
|
+
if (resolved && typeof resolved.renderToString === "function")
|
|
8
|
+
return resolved;
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
function getGlobalKatex() {
|
|
12
|
+
try {
|
|
13
|
+
const globalStore = globalThis;
|
|
14
|
+
return normalizeKatexModule(globalStore?.katex);
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function defaultKatexLoader() {
|
|
20
|
+
return (async () => {
|
|
21
|
+
const globalKatex = getGlobalKatex();
|
|
22
|
+
if (globalKatex)
|
|
23
|
+
return globalKatex;
|
|
24
|
+
const mod = await import("katex");
|
|
25
|
+
try {
|
|
26
|
+
await import("katex/contrib/mhchem");
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
return normalizeKatexModule(mod);
|
|
30
|
+
})();
|
|
31
|
+
}
|
|
32
|
+
function resetCache() {
|
|
33
|
+
katex = null;
|
|
34
|
+
importAttempted = false;
|
|
35
|
+
pendingImport = null;
|
|
36
|
+
}
|
|
37
|
+
function setKatexLoader(loader) {
|
|
38
|
+
katexLoader = loader;
|
|
39
|
+
resetCache();
|
|
40
|
+
}
|
|
41
|
+
function enableKatex(loader) {
|
|
42
|
+
setKatexLoader(loader ?? defaultKatexLoader);
|
|
43
|
+
}
|
|
44
|
+
function disableKatex() {
|
|
45
|
+
setKatexLoader(null);
|
|
46
|
+
}
|
|
47
|
+
function isKatexEnabled() {
|
|
48
|
+
return typeof katexLoader === "function";
|
|
49
|
+
}
|
|
50
|
+
async function getKatex() {
|
|
51
|
+
const globalKatex = getGlobalKatex();
|
|
52
|
+
if (globalKatex) {
|
|
53
|
+
katex = globalKatex;
|
|
54
|
+
return katex;
|
|
55
|
+
}
|
|
56
|
+
if (katex)
|
|
57
|
+
return katex;
|
|
58
|
+
if (pendingImport)
|
|
59
|
+
return await pendingImport;
|
|
60
|
+
if (importAttempted)
|
|
61
|
+
return null;
|
|
62
|
+
const loader = katexLoader;
|
|
63
|
+
if (!loader) {
|
|
64
|
+
importAttempted = true;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
pendingImport = (async () => {
|
|
68
|
+
try {
|
|
69
|
+
const result = await loader();
|
|
70
|
+
const normalized = normalizeKatexModule(result) ?? result;
|
|
71
|
+
if (normalized) {
|
|
72
|
+
katex = normalized;
|
|
73
|
+
return katex;
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
importAttempted = true;
|
|
78
|
+
return null;
|
|
79
|
+
})();
|
|
80
|
+
try {
|
|
81
|
+
return await pendingImport;
|
|
82
|
+
} finally {
|
|
83
|
+
pendingImport = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function normalizeKaTeXRenderInput(content) {
|
|
87
|
+
if (!content)
|
|
88
|
+
return "";
|
|
89
|
+
return content.replace(/·/g, "⋅").replace(/℃/g, "°C");
|
|
90
|
+
}
|
|
91
|
+
const WORKER_BUSY_CODE = "WORKER_BUSY";
|
|
92
|
+
let worker = null;
|
|
93
|
+
let workerInitError = null;
|
|
94
|
+
let debugKatexWorker = false;
|
|
95
|
+
const pending = /* @__PURE__ */ new Map();
|
|
96
|
+
const cache = /* @__PURE__ */ new Map();
|
|
97
|
+
const cacheMax = 200;
|
|
98
|
+
const drainWaiters = /* @__PURE__ */ new Set();
|
|
99
|
+
let maxConcurrency = 5;
|
|
100
|
+
function notifyDrainIfBelowCap() {
|
|
101
|
+
if (pending.size >= maxConcurrency || drainWaiters.size === 0)
|
|
102
|
+
return;
|
|
103
|
+
const currentWaiters = Array.from(drainWaiters);
|
|
104
|
+
drainWaiters.clear();
|
|
105
|
+
for (const waiter of currentWaiters) {
|
|
106
|
+
try {
|
|
107
|
+
waiter();
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function rememberCache(content, displayMode, html) {
|
|
113
|
+
cache.set(`${displayMode ? "d" : "i"}:${content}`, html);
|
|
114
|
+
if (cache.size > cacheMax) {
|
|
115
|
+
const firstKey = cache.keys().next().value;
|
|
116
|
+
if (firstKey)
|
|
117
|
+
cache.delete(firstKey);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function setKaTeXWorker(nextWorker) {
|
|
121
|
+
worker = nextWorker;
|
|
122
|
+
workerInitError = null;
|
|
123
|
+
worker.onmessage = (event) => {
|
|
124
|
+
const { id, html, error, content, displayMode } = event.data || {};
|
|
125
|
+
const active = pending.get(id);
|
|
126
|
+
if (!active)
|
|
127
|
+
return;
|
|
128
|
+
pending.delete(id);
|
|
129
|
+
clearTimeout(active.timeoutId);
|
|
130
|
+
notifyDrainIfBelowCap();
|
|
131
|
+
if (error) {
|
|
132
|
+
active.reject(new Error(error));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (content)
|
|
136
|
+
rememberCache(String(content), Boolean(displayMode), String(html || ""));
|
|
137
|
+
active.resolve(String(html || ""));
|
|
138
|
+
};
|
|
139
|
+
worker.onerror = (event) => {
|
|
140
|
+
for (const [, active] of pending.entries()) {
|
|
141
|
+
clearTimeout(active.timeoutId);
|
|
142
|
+
active.reject(new Error(`Worker error: ${event.message}`));
|
|
143
|
+
}
|
|
144
|
+
pending.clear();
|
|
145
|
+
notifyDrainIfBelowCap();
|
|
146
|
+
};
|
|
147
|
+
if (debugKatexWorker)
|
|
148
|
+
worker.postMessage({ type: "init", debug: true });
|
|
149
|
+
}
|
|
150
|
+
function clearKaTeXWorker() {
|
|
151
|
+
if (worker)
|
|
152
|
+
worker.terminate?.();
|
|
153
|
+
worker = null;
|
|
154
|
+
workerInitError = null;
|
|
155
|
+
}
|
|
156
|
+
function ensureWorker() {
|
|
157
|
+
if (worker)
|
|
158
|
+
return worker;
|
|
159
|
+
workerInitError = new Error("[markstream-solid:katexWorkerClient] No worker instance set. Please inject a Worker via setKaTeXWorker().");
|
|
160
|
+
workerInitError.name = "WorkerInitError";
|
|
161
|
+
workerInitError.code = "WORKER_INIT_ERROR";
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
function setKaTeXWorkerDebug(enabled) {
|
|
165
|
+
debugKatexWorker = !!enabled;
|
|
166
|
+
if (worker)
|
|
167
|
+
worker.postMessage({ type: "init", debug: debugKatexWorker });
|
|
168
|
+
}
|
|
169
|
+
async function renderKaTeXInWorker(content, displayMode = true, timeout = 2e3, signal) {
|
|
170
|
+
const normalizedContent = normalizeKaTeXRenderInput(content);
|
|
171
|
+
if (!isKatexEnabled()) {
|
|
172
|
+
const error = new Error("KaTeX rendering disabled");
|
|
173
|
+
error.name = "KaTeXDisabled";
|
|
174
|
+
error.code = "KATEX_DISABLED";
|
|
175
|
+
return Promise.reject(error);
|
|
176
|
+
}
|
|
177
|
+
if (workerInitError)
|
|
178
|
+
return Promise.reject(workerInitError);
|
|
179
|
+
const cacheKey = `${displayMode ? "d" : "i"}:${normalizedContent}`;
|
|
180
|
+
const cached = cache.get(cacheKey);
|
|
181
|
+
if (cached)
|
|
182
|
+
return cached;
|
|
183
|
+
const activeWorker = ensureWorker();
|
|
184
|
+
if (!activeWorker)
|
|
185
|
+
return Promise.reject(workerInitError);
|
|
186
|
+
if (pending.size >= maxConcurrency) {
|
|
187
|
+
const error = new Error("Worker busy");
|
|
188
|
+
error.name = "WorkerBusy";
|
|
189
|
+
error.code = WORKER_BUSY_CODE;
|
|
190
|
+
error.busy = true;
|
|
191
|
+
error.inFlight = pending.size;
|
|
192
|
+
error.max = maxConcurrency;
|
|
193
|
+
return Promise.reject(error);
|
|
194
|
+
}
|
|
195
|
+
return new Promise((resolve, reject) => {
|
|
196
|
+
if (signal?.aborted) {
|
|
197
|
+
const error = new Error("Aborted");
|
|
198
|
+
error.name = "AbortError";
|
|
199
|
+
reject(error);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const id = Math.random().toString(36).slice(2);
|
|
203
|
+
const timeoutId = globalThis.setTimeout(() => {
|
|
204
|
+
pending.delete(id);
|
|
205
|
+
notifyDrainIfBelowCap();
|
|
206
|
+
const error = new Error("Worker render timed out");
|
|
207
|
+
error.name = "WorkerTimeout";
|
|
208
|
+
error.code = "WORKER_TIMEOUT";
|
|
209
|
+
reject(error);
|
|
210
|
+
}, timeout);
|
|
211
|
+
const onAbort = () => {
|
|
212
|
+
globalThis.clearTimeout(timeoutId);
|
|
213
|
+
if (pending.delete(id))
|
|
214
|
+
notifyDrainIfBelowCap();
|
|
215
|
+
const error = new Error("Aborted");
|
|
216
|
+
error.name = "AbortError";
|
|
217
|
+
reject(error);
|
|
218
|
+
};
|
|
219
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
220
|
+
pending.set(id, {
|
|
221
|
+
timeoutId,
|
|
222
|
+
resolve: (html) => {
|
|
223
|
+
signal?.removeEventListener("abort", onAbort);
|
|
224
|
+
resolve(html);
|
|
225
|
+
},
|
|
226
|
+
reject: (error) => {
|
|
227
|
+
signal?.removeEventListener("abort", onAbort);
|
|
228
|
+
reject(error);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
activeWorker.postMessage({
|
|
232
|
+
id,
|
|
233
|
+
type: "render",
|
|
234
|
+
content: normalizedContent,
|
|
235
|
+
displayMode
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
function setKaTeXCache(content, displayMode = true, html) {
|
|
240
|
+
rememberCache(normalizeKaTeXRenderInput(content), displayMode, html);
|
|
241
|
+
}
|
|
242
|
+
function getKaTeXWorkerLoad() {
|
|
243
|
+
return {
|
|
244
|
+
inFlight: pending.size,
|
|
245
|
+
max: maxConcurrency
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function setKaTeXWorkerMaxConcurrency(value) {
|
|
249
|
+
if (Number.isFinite(value) && value > 0)
|
|
250
|
+
maxConcurrency = Math.floor(value);
|
|
251
|
+
}
|
|
252
|
+
function isKaTeXWorkerBusy() {
|
|
253
|
+
return pending.size >= maxConcurrency;
|
|
254
|
+
}
|
|
255
|
+
function waitForKaTeXWorkerSlot(timeout = 2e3, signal) {
|
|
256
|
+
if (pending.size < maxConcurrency)
|
|
257
|
+
return Promise.resolve();
|
|
258
|
+
return new Promise((resolve, reject) => {
|
|
259
|
+
let settled = false;
|
|
260
|
+
let timer = null;
|
|
261
|
+
const onDrain = () => {
|
|
262
|
+
if (settled)
|
|
263
|
+
return;
|
|
264
|
+
settled = true;
|
|
265
|
+
if (timer)
|
|
266
|
+
clearTimeout(timer);
|
|
267
|
+
drainWaiters.delete(onDrain);
|
|
268
|
+
resolve();
|
|
269
|
+
};
|
|
270
|
+
drainWaiters.add(onDrain);
|
|
271
|
+
timer = setTimeout(() => {
|
|
272
|
+
if (settled)
|
|
273
|
+
return;
|
|
274
|
+
settled = true;
|
|
275
|
+
drainWaiters.delete(onDrain);
|
|
276
|
+
const error = new Error("Wait for worker slot timed out");
|
|
277
|
+
error.name = "WorkerBusyTimeout";
|
|
278
|
+
error.code = "WORKER_BUSY_TIMEOUT";
|
|
279
|
+
reject(error);
|
|
280
|
+
}, timeout);
|
|
281
|
+
queueMicrotask(() => notifyDrainIfBelowCap());
|
|
282
|
+
if (signal) {
|
|
283
|
+
const onAbort = () => {
|
|
284
|
+
if (settled)
|
|
285
|
+
return;
|
|
286
|
+
settled = true;
|
|
287
|
+
if (timer)
|
|
288
|
+
clearTimeout(timer);
|
|
289
|
+
drainWaiters.delete(onDrain);
|
|
290
|
+
const error = new Error("Aborted");
|
|
291
|
+
error.name = "AbortError";
|
|
292
|
+
reject(error);
|
|
293
|
+
};
|
|
294
|
+
if (signal.aborted)
|
|
295
|
+
onAbort();
|
|
296
|
+
else
|
|
297
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
const defaultBackpressure = {
|
|
302
|
+
timeout: 2e3,
|
|
303
|
+
waitTimeout: 1500,
|
|
304
|
+
backoffMs: 30,
|
|
305
|
+
maxRetries: 1
|
|
306
|
+
};
|
|
307
|
+
const maxBackpressureRetries = 8;
|
|
308
|
+
function setKaTeXBackpressureDefaults(options) {
|
|
309
|
+
if (options.timeout != null)
|
|
310
|
+
defaultBackpressure.timeout = Math.max(0, Math.floor(options.timeout));
|
|
311
|
+
if (options.waitTimeout != null)
|
|
312
|
+
defaultBackpressure.waitTimeout = Math.max(0, Math.floor(options.waitTimeout));
|
|
313
|
+
if (options.backoffMs != null)
|
|
314
|
+
defaultBackpressure.backoffMs = Math.max(0, Math.floor(options.backoffMs));
|
|
315
|
+
if (options.maxRetries != null)
|
|
316
|
+
defaultBackpressure.maxRetries = Math.max(0, Math.floor(options.maxRetries));
|
|
317
|
+
}
|
|
318
|
+
function getKaTeXBackpressureDefaults() {
|
|
319
|
+
return { ...defaultBackpressure };
|
|
320
|
+
}
|
|
321
|
+
async function renderKaTeXWithBackpressure(content, displayMode = true, options = {}) {
|
|
322
|
+
if (!isKatexEnabled()) {
|
|
323
|
+
const error = new Error("KaTeX rendering disabled");
|
|
324
|
+
error.name = "KaTeXDisabled";
|
|
325
|
+
error.code = "KATEX_DISABLED";
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
const timeout = options.timeout ?? defaultBackpressure.timeout;
|
|
329
|
+
const waitTimeout = options.waitTimeout ?? defaultBackpressure.waitTimeout;
|
|
330
|
+
const backoffMs = options.backoffMs ?? defaultBackpressure.backoffMs;
|
|
331
|
+
const rawMaxRetries = options.maxRetries ?? defaultBackpressure.maxRetries;
|
|
332
|
+
const maxRetries = Number.isFinite(rawMaxRetries) ? Math.max(0, Math.min(Math.floor(rawMaxRetries), maxBackpressureRetries)) : defaultBackpressure.maxRetries;
|
|
333
|
+
let attempt = 0;
|
|
334
|
+
for (; ; ) {
|
|
335
|
+
if (options.signal?.aborted) {
|
|
336
|
+
const error = new Error("Aborted");
|
|
337
|
+
error.name = "AbortError";
|
|
338
|
+
throw error;
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
return await renderKaTeXInWorker(content, displayMode, timeout, options.signal);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
if (error?.code !== WORKER_BUSY_CODE || attempt >= maxRetries)
|
|
344
|
+
throw error;
|
|
345
|
+
attempt += 1;
|
|
346
|
+
await waitForKaTeXWorkerSlot(waitTimeout, options.signal).catch(() => {
|
|
347
|
+
});
|
|
348
|
+
if (backoffMs > 0)
|
|
349
|
+
await new Promise((resolve) => globalThis.setTimeout(resolve, backoffMs * attempt));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
export {
|
|
354
|
+
WORKER_BUSY_CODE,
|
|
355
|
+
clearKaTeXWorker,
|
|
356
|
+
disableKatex as d,
|
|
357
|
+
enableKatex as e,
|
|
358
|
+
getKatex as g,
|
|
359
|
+
getKaTeXBackpressureDefaults,
|
|
360
|
+
getKaTeXWorkerLoad,
|
|
361
|
+
isKatexEnabled as i,
|
|
362
|
+
isKaTeXWorkerBusy,
|
|
363
|
+
normalizeKaTeXRenderInput as n,
|
|
364
|
+
renderKaTeXInWorker,
|
|
365
|
+
renderKaTeXWithBackpressure,
|
|
366
|
+
setKatexLoader as s,
|
|
367
|
+
setKaTeXBackpressureDefaults,
|
|
368
|
+
setKaTeXCache,
|
|
369
|
+
setKaTeXWorker,
|
|
370
|
+
setKaTeXWorkerDebug,
|
|
371
|
+
setKaTeXWorkerMaxConcurrency,
|
|
372
|
+
waitForKaTeXWorkerSlot
|
|
373
|
+
};
|