@gmickel/gno 1.46.0 → 2.0.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/README.md +1 -1
- package/THIRD_PARTY_NOTICES.md +46 -0
- package/assets/skill/SKILL.md +7 -6
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip.sha256 +1 -0
- package/browser-extension/dist/chunk-4tc9v0ja.js +74 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/browser-extension/dist/preview.html +1 -1
- package/browser-extension/dist/service-worker.js +32 -33
- package/bunfig.toml +2 -0
- package/package.json +40 -26
- package/spec/cli.md +21 -4
- package/spec/db/schema.sql +146 -1
- package/spec/mcp.md +26 -0
- package/src/app/context-runtime-types.ts +3 -0
- package/src/app/context-runtime.ts +2 -0
- package/src/cli/commands/ask.ts +6 -1
- package/src/cli/commands/daemon.ts +21 -8
- package/src/cli/commands/embed.ts +77 -41
- package/src/cli/detach.ts +3 -2
- package/src/config/types.ts +3 -3
- package/src/converters/adapters/markitdownTs/adapter.ts +1 -2
- package/src/converters/adapters/officeparser/adapter.ts +1 -2
- package/src/converters/versions.ts +6 -8
- package/src/core/context-evidence.ts +8 -4
- package/src/core/job-manager.ts +95 -13
- package/src/core/network-boundary-inventory.ts +10 -0
- package/src/core/shutdown-budget.ts +45 -0
- package/src/embed/backlog.ts +107 -4
- package/src/embed/batch.ts +42 -2
- package/src/embed/fingerprint.ts +16 -0
- package/src/embed/retry.ts +113 -5
- package/src/embed/variant-backlog.ts +105 -0
- package/src/embed/variant-plan.ts +62 -0
- package/src/embed/variant-retry.ts +113 -0
- package/src/ingestion/graph-reconciliation.ts +327 -0
- package/src/ingestion/sync.ts +9 -272
- package/src/llm/http-inference.ts +6 -0
- package/src/llm/httpEmbedding.ts +37 -6
- package/src/llm/httpGeneration.ts +18 -3
- package/src/llm/httpRerank.ts +23 -5
- package/src/llm/inference-cancellation.ts +168 -0
- package/src/llm/inference-scope.ts +202 -0
- package/src/llm/lazy-ports.ts +115 -0
- package/src/llm/native-worker/client.ts +541 -0
- package/src/llm/native-worker/dispatcher.ts +228 -0
- package/src/llm/native-worker/embedding-identity.ts +33 -0
- package/src/llm/native-worker/entry.ts +173 -0
- package/src/llm/native-worker/errors.ts +32 -0
- package/src/llm/native-worker/evaluation.ts +16 -0
- package/src/llm/native-worker/owned-exit.ts +108 -0
- package/src/llm/native-worker/owner.ts +141 -0
- package/src/llm/native-worker/ports.ts +317 -0
- package/src/llm/native-worker/protocol.ts +442 -0
- package/src/llm/native-worker/runtime-config.ts +92 -0
- package/src/llm/nodeLlamaCpp/adapter.ts +77 -20
- package/src/llm/nodeLlamaCpp/embedding.ts +130 -46
- package/src/llm/nodeLlamaCpp/generation.ts +34 -5
- package/src/llm/nodeLlamaCpp/lifecycle-options.ts +99 -0
- package/src/llm/nodeLlamaCpp/lifecycle.ts +209 -204
- package/src/llm/nodeLlamaCpp/rerank-capacity.ts +111 -0
- package/src/llm/nodeLlamaCpp/rerank.ts +118 -27
- package/src/llm/nodeLlamaCpp/simulator-handle.ts +73 -0
- package/src/llm/nodeLlamaCpp/simulator-install.ts +124 -0
- package/src/llm/nodeLlamaCpp/simulator-session.ts +240 -0
- package/src/llm/nodeLlamaCpp/simulator-types.ts +80 -0
- package/src/llm/types.ts +35 -5
- package/src/mcp/context.ts +27 -0
- package/src/mcp/http-transport.ts +12 -10
- package/src/mcp/server.ts +3 -0
- package/src/mcp/tool-profile.ts +30 -8
- package/src/mcp/tools/context.ts +8 -11
- package/src/mcp/tools/embed.ts +1 -1
- package/src/mcp/tools/index-cmd.ts +1 -1
- package/src/mcp/tools/index.ts +10 -8
- package/src/mcp/tools/query.ts +14 -30
- package/src/mcp/tools/vsearch.ts +1 -1
- package/src/pipeline/answer.ts +23 -3
- package/src/pipeline/claim-verifier.ts +6 -0
- package/src/pipeline/expansion.ts +43 -40
- package/src/pipeline/explain.ts +6 -2
- package/src/pipeline/filters.ts +63 -0
- package/src/pipeline/fusion.ts +29 -9
- package/src/pipeline/graph-retrieval.ts +29 -9
- package/src/pipeline/hybrid.ts +198 -55
- package/src/pipeline/hydration.ts +161 -0
- package/src/pipeline/owner-fusion.ts +87 -0
- package/src/pipeline/rerank.ts +35 -11
- package/src/pipeline/search.ts +13 -2
- package/src/pipeline/types.ts +5 -3
- package/src/pipeline/vsearch.ts +87 -7
- package/src/sdk/client.ts +47 -3
- package/src/sdk/embed.ts +63 -39
- package/src/serve/background-runtime.ts +1 -1
- package/src/serve/context.ts +41 -56
- package/src/serve/embed-scheduler.ts +58 -35
- package/src/serve/public/components/IndexingProgress.tsx +46 -60
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/shiki-language-ids.ts +14 -0
- package/src/serve/resident-admission.ts +36 -36
- package/src/serve/resident-background-work.ts +20 -2
- package/src/serve/resident-request.ts +11 -5
- package/src/serve/resident-runtime.ts +97 -61
- package/src/serve/resident-shutdown.ts +153 -0
- package/src/serve/routes/api.ts +3 -1
- package/src/serve/server.ts +47 -26
- package/src/store/migrations/028-vector-variants.ts +54 -0
- package/src/store/migrations/029-graph-reference-state.ts +77 -0
- package/src/store/migrations/index.ts +4 -0
- package/src/store/sqlite/adapter.ts +251 -183
- package/src/store/sqlite/eligibility.ts +174 -0
- package/src/store/sqlite/graph-edge-application.ts +66 -0
- package/src/store/sqlite/graph-reference-state.ts +194 -0
- package/src/store/sqlite/legacy-vector-ownership.ts +79 -0
- package/src/store/types.ts +80 -12
- package/src/store/vector/eligibility.ts +36 -0
- package/src/store/vector/freshness.ts +33 -6
- package/src/store/vector/lazy.ts +81 -0
- package/src/store/vector/sqlite-vec.ts +106 -54
- package/src/store/vector/stats.ts +14 -3
- package/src/store/vector/types.ts +35 -2
- package/src/store/vector/variant-search.ts +192 -0
- package/src/store/vector/variants.ts +451 -0
- package/vendor/converters/markitdown-ts/LICENSE +21 -0
- package/vendor/converters/markitdown-ts/dist/index.cjs +1180 -0
- package/vendor/converters/markitdown-ts/dist/index.d.cts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.mts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.ts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.mjs +1152 -0
- package/vendor/converters/markitdown-ts/package.json +77 -0
- package/vendor/converters/officeparser/LICENSE +21 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.d.ts +47 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.js +76 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.d.ts +23 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.js +73 -0
- package/vendor/converters/officeparser/dist/OfficeParser.d.ts +106 -0
- package/vendor/converters/officeparser/dist/OfficeParser.js +332 -0
- package/vendor/converters/officeparser/dist/cli.d.ts +28 -0
- package/vendor/converters/officeparser/dist/cli.js +381 -0
- package/vendor/converters/officeparser/dist/defaults.d.ts +41 -0
- package/vendor/converters/officeparser/dist/defaults.js +218 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.d.ts +107 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.js +248 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.d.ts +82 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.js +797 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.d.ts +38 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.js +245 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.d.ts +43 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.js +315 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.d.ts +59 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.js +1942 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.d.ts +96 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.js +1175 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.d.ts +22 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.js +194 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.d.ts +29 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.js +316 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.d.ts +13 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.js +201 -0
- package/vendor/converters/officeparser/dist/index.d.ts +60 -0
- package/vendor/converters/officeparser/dist/index.js +72 -0
- package/vendor/converters/officeparser/dist/index.mjs +18 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.mjs +1335 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.mjs +1335 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.d.ts +9 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.js +115 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.d.ts +8 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.js +217 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.js +736 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.js +1287 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.js +1272 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.d.ts +31 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.js +1819 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.d.ts +67 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.js +848 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.js +950 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.d.ts +187 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.js +1801 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.d.ts +79 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.js +1177 -0
- package/vendor/converters/officeparser/dist/sbom.cdx.json +1763 -0
- package/vendor/converters/officeparser/dist/types.d.ts +2507 -0
- package/vendor/converters/officeparser/dist/types.js +107 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.d.ts +16 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.js +33 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.d.ts +6 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.js +257 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.d.ts +44 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.js +315 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.d.ts +17 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.js +69 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.d.ts +29 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.js +152 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.d.ts +72 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.js +245 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.d.ts +66 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.js +133 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.js +385 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.d.ts +18 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.js +106 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.js +428 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.d.ts +148 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.js +344 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.d.ts +7 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.js +35 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.d.ts +36 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.js +224 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.d.ts +163 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.js +461 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.d.ts +134 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.js +337 -0
- package/vendor/converters/officeparser/package.json +147 -0
- package/vendor/converters/upstream-manifest.json +124 -0
- package/vendor/dependency-fixes/README.md +77 -0
- package/vendor/dependency-fixes/vendor-converters.py +83 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip.sha256 +0 -1
- package/browser-extension/dist/chunk-627emwpj.js +0 -75
- /package/browser-extension/dist/{chunk-ydfx5d7p.css → chunk-z74y8n8c.css} +0 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OCR (Optical Character Recognition) Utilities
|
|
4
|
+
*
|
|
5
|
+
* This module provides functions for extracting text from images using Tesseract.js.
|
|
6
|
+
* Used when `config.ocr` is enabled to extract text from embedded images in documents.
|
|
7
|
+
*
|
|
8
|
+
* Includes a worker pool via OcrSchedulerManager to improve performance when
|
|
9
|
+
* processing multiple images.
|
|
10
|
+
*
|
|
11
|
+
* @module ocrUtils
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.terminateOcr = exports.performOcr = void 0;
|
|
15
|
+
const envUtils_js_1 = require("./envUtils.js");
|
|
16
|
+
const errorUtils_js_1 = require("./errorUtils.js");
|
|
17
|
+
/**
|
|
18
|
+
* Wraps a promise in a timeout.
|
|
19
|
+
*
|
|
20
|
+
* @param promise - The promise to wrap
|
|
21
|
+
* @param ms - Timeout duration in milliseconds
|
|
22
|
+
* @param errMsg - Error message to throw if timeout occurs
|
|
23
|
+
* @returns The wrapped promise
|
|
24
|
+
*/
|
|
25
|
+
function withTimeout(promise, ms, errMsg) {
|
|
26
|
+
let id;
|
|
27
|
+
const timeout = new Promise((_, reject) => {
|
|
28
|
+
id = setTimeout(() => {
|
|
29
|
+
reject(new Error(errMsg));
|
|
30
|
+
}, ms);
|
|
31
|
+
});
|
|
32
|
+
return Promise.race([promise, timeout]).then((res) => { clearTimeout(id); return res; }, (err) => { clearTimeout(id); throw err; });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Manages a pool of Tesseract workers with "Smart Affinity".
|
|
36
|
+
*
|
|
37
|
+
* Instead of a simple scheduler, this manager allows workers to persist with
|
|
38
|
+
* a specific language affinity. If a new language is requested and the pool
|
|
39
|
+
* is at capacity, it re-initializes the Least Recently Used (LRU) idle worker
|
|
40
|
+
* rather than resetting the entire pool.
|
|
41
|
+
*
|
|
42
|
+
* Implements lazy loading of tesseract.js to ensure no background processes
|
|
43
|
+
* are spawned unless OCR is explicitly used.
|
|
44
|
+
*/
|
|
45
|
+
class OcrSchedulerManager {
|
|
46
|
+
static instance;
|
|
47
|
+
pool = [];
|
|
48
|
+
queue = [];
|
|
49
|
+
MAX_WORKERS = 4;
|
|
50
|
+
idleTimeout = 10000; // 10s default
|
|
51
|
+
timeoutId = null;
|
|
52
|
+
isProcessing = false;
|
|
53
|
+
constructor() { }
|
|
54
|
+
/**
|
|
55
|
+
* Returns the singleton instance of the manager.
|
|
56
|
+
*/
|
|
57
|
+
static getInstance() {
|
|
58
|
+
if (!OcrSchedulerManager.instance) {
|
|
59
|
+
OcrSchedulerManager.instance = new OcrSchedulerManager();
|
|
60
|
+
}
|
|
61
|
+
return OcrSchedulerManager.instance;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Checks if the singleton instance has been initialized.
|
|
65
|
+
*/
|
|
66
|
+
static hasInstance() {
|
|
67
|
+
return !!OcrSchedulerManager.instance;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Resets the inactivity timer. If the timer reaches its duration,
|
|
71
|
+
* all workers are terminated automatically.
|
|
72
|
+
*/
|
|
73
|
+
resetIdleTimer() {
|
|
74
|
+
if (this.timeoutId) {
|
|
75
|
+
clearTimeout(this.timeoutId);
|
|
76
|
+
}
|
|
77
|
+
if (this.idleTimeout > 0) {
|
|
78
|
+
this.timeoutId = setTimeout(async () => {
|
|
79
|
+
await this.terminate();
|
|
80
|
+
}, this.idleTimeout);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Performs OCR on an image using the smart worker pool.
|
|
85
|
+
*
|
|
86
|
+
* @param image - Image data (Buffer, string path, or Blob)
|
|
87
|
+
* @param config - OCR configuration (language, custom paths, timeouts, signal)
|
|
88
|
+
* @returns Recognized text
|
|
89
|
+
*/
|
|
90
|
+
async recognize(image, config) {
|
|
91
|
+
const signal = config?.abortSignal;
|
|
92
|
+
if (signal?.aborted) {
|
|
93
|
+
return Promise.reject((0, errorUtils_js_1.getAbortError)());
|
|
94
|
+
}
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
// Update idle timeout if provided.
|
|
97
|
+
// Priority: timeout.autoTerminate (new) > autoTerminateTimeout (deprecated) > built-in default.
|
|
98
|
+
const effectiveAutoTerminate = config?.timeout?.autoTerminate ?? config?.autoTerminateTimeout;
|
|
99
|
+
if (effectiveAutoTerminate !== undefined) {
|
|
100
|
+
this.idleTimeout = effectiveAutoTerminate;
|
|
101
|
+
}
|
|
102
|
+
// Reset the inactivity timer every time a new job is requested
|
|
103
|
+
this.resetIdleTimer();
|
|
104
|
+
let abortListener = null;
|
|
105
|
+
let finished = false;
|
|
106
|
+
let job;
|
|
107
|
+
const cleanResolve = (val) => {
|
|
108
|
+
if (finished)
|
|
109
|
+
return;
|
|
110
|
+
finished = true;
|
|
111
|
+
if (job)
|
|
112
|
+
job.isFinished = true;
|
|
113
|
+
if (abortListener && signal) {
|
|
114
|
+
signal.removeEventListener('abort', abortListener);
|
|
115
|
+
}
|
|
116
|
+
resolve(val);
|
|
117
|
+
};
|
|
118
|
+
const cleanReject = (err) => {
|
|
119
|
+
if (finished)
|
|
120
|
+
return;
|
|
121
|
+
finished = true;
|
|
122
|
+
if (job)
|
|
123
|
+
job.isFinished = true;
|
|
124
|
+
if (abortListener && signal) {
|
|
125
|
+
signal.removeEventListener('abort', abortListener);
|
|
126
|
+
}
|
|
127
|
+
reject(err);
|
|
128
|
+
};
|
|
129
|
+
// Priority: timeout.recognition (new) > 30 s default.
|
|
130
|
+
const recogTimeout = config?.timeout?.recognition ?? 30000;
|
|
131
|
+
// Create job
|
|
132
|
+
job = {
|
|
133
|
+
image,
|
|
134
|
+
config: config || {},
|
|
135
|
+
resolve: cleanResolve,
|
|
136
|
+
reject: cleanReject,
|
|
137
|
+
startTime: Date.now(),
|
|
138
|
+
timeoutMs: recogTimeout
|
|
139
|
+
};
|
|
140
|
+
if (signal) {
|
|
141
|
+
abortListener = () => {
|
|
142
|
+
if (finished)
|
|
143
|
+
return;
|
|
144
|
+
const err = (0, errorUtils_js_1.getAbortError)();
|
|
145
|
+
cleanReject(err);
|
|
146
|
+
// 1. Remove job from queue if it hasn't run yet
|
|
147
|
+
const idx = this.queue.indexOf(job);
|
|
148
|
+
if (idx !== -1) {
|
|
149
|
+
this.queue.splice(idx, 1);
|
|
150
|
+
}
|
|
151
|
+
// 2. Find if any worker is currently running this job and terminate/remove it
|
|
152
|
+
const workerIndex = this.pool.findIndex(mw => mw.activeJob === job);
|
|
153
|
+
if (workerIndex !== -1) {
|
|
154
|
+
const managedWorker = this.pool[workerIndex];
|
|
155
|
+
// Remove from pool immediately to prevent reuse
|
|
156
|
+
this.pool.splice(workerIndex, 1);
|
|
157
|
+
// Terminate the worker process
|
|
158
|
+
try {
|
|
159
|
+
managedWorker.worker.terminate();
|
|
160
|
+
}
|
|
161
|
+
catch (e) { }
|
|
162
|
+
// Trigger queue processing for subsequent tasks
|
|
163
|
+
this.processQueue();
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
signal.addEventListener('abort', abortListener);
|
|
167
|
+
}
|
|
168
|
+
// Add job to queue and trigger processing
|
|
169
|
+
this.queue.push(job);
|
|
170
|
+
this.processQueue();
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Attempts to process the next job in the queue using an available worker.
|
|
175
|
+
* Designed to be race-free and support concurrent/parallel job execution.
|
|
176
|
+
*/
|
|
177
|
+
async processQueue() {
|
|
178
|
+
if (this.isProcessing)
|
|
179
|
+
return;
|
|
180
|
+
this.isProcessing = true;
|
|
181
|
+
try {
|
|
182
|
+
while (this.queue.length > 0) {
|
|
183
|
+
const nextJob = this.queue[0];
|
|
184
|
+
if (nextJob.isFinished) {
|
|
185
|
+
this.queue.shift();
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const requestedLanguage = nextJob.config.language || 'eng';
|
|
189
|
+
// 1. Find an idle worker with the EXACT language affinity
|
|
190
|
+
let managed = this.pool.find(mw => !mw.isBusy && mw.language === requestedLanguage);
|
|
191
|
+
// 2. If not found and we have room, create a new worker
|
|
192
|
+
if (!managed && this.pool.length < this.MAX_WORKERS) {
|
|
193
|
+
const job = this.queue.shift();
|
|
194
|
+
if (!job)
|
|
195
|
+
continue;
|
|
196
|
+
this.createAndRunWorker(job, requestedLanguage);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
// 3. If still not found and we are at capacity, find the LRU idle worker and re-initialize it
|
|
200
|
+
if (!managed) {
|
|
201
|
+
const idleWorkers = this.pool.filter(mw => !mw.isBusy);
|
|
202
|
+
if (idleWorkers.length > 0) {
|
|
203
|
+
const job = this.queue.shift();
|
|
204
|
+
if (!job)
|
|
205
|
+
continue;
|
|
206
|
+
managed = idleWorkers.reduce((prev, curr) => (prev.lastUsed < curr.lastUsed ? prev : curr));
|
|
207
|
+
this.reinitializeAndRunWorker(managed, job, requestedLanguage);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// 4. If we have a worker ready, execute the job
|
|
212
|
+
if (managed) {
|
|
213
|
+
const job = this.queue.shift();
|
|
214
|
+
if (!job)
|
|
215
|
+
continue;
|
|
216
|
+
this.runWorker(managed, job);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
// No workers can be allocated right now (all busy and pool at capacity). Break work loop.
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
this.isProcessing = false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Helper to dynamically instantiate a Tesseract worker, register it to the pool, and run the job.
|
|
229
|
+
*/
|
|
230
|
+
async createAndRunWorker(job, requestedLanguage) {
|
|
231
|
+
// Priority: timeout.workerLoad (new) > 60 s default.
|
|
232
|
+
const loadTimeout = job.config.timeout?.workerLoad ?? 60000;
|
|
233
|
+
let managed = null;
|
|
234
|
+
try {
|
|
235
|
+
const { createWorker } = await import('tesseract.js');
|
|
236
|
+
const options = { logger: () => { } };
|
|
237
|
+
if (job.config.workerPath)
|
|
238
|
+
options.workerPath = job.config.workerPath;
|
|
239
|
+
if (job.config.corePath)
|
|
240
|
+
options.corePath = job.config.corePath;
|
|
241
|
+
if (job.config.langPath)
|
|
242
|
+
options.langPath = job.config.langPath;
|
|
243
|
+
const workerPromise = createWorker(requestedLanguage, 1, options);
|
|
244
|
+
// To prevent dangling worker threads on timeout or abort, we register a post-resolution hook
|
|
245
|
+
// that terminates the worker if the promise finishes after the timeout has fired or the job is finished.
|
|
246
|
+
let hasTimedOutOrAborted = false;
|
|
247
|
+
workerPromise.then(async (worker) => {
|
|
248
|
+
if (hasTimedOutOrAborted || job.isFinished) {
|
|
249
|
+
try {
|
|
250
|
+
await worker.terminate();
|
|
251
|
+
}
|
|
252
|
+
catch (e) { }
|
|
253
|
+
}
|
|
254
|
+
}, () => { });
|
|
255
|
+
const worker = loadTimeout > 0
|
|
256
|
+
? await withTimeout(workerPromise, loadTimeout, `OCR worker initialization timed out after ${loadTimeout}ms`).catch(err => {
|
|
257
|
+
hasTimedOutOrAborted = true;
|
|
258
|
+
throw err;
|
|
259
|
+
})
|
|
260
|
+
: await workerPromise;
|
|
261
|
+
// If the job finished/aborted while loading, clean up the worker and skip execution.
|
|
262
|
+
if (job.isFinished) {
|
|
263
|
+
hasTimedOutOrAborted = true;
|
|
264
|
+
try {
|
|
265
|
+
await worker.terminate();
|
|
266
|
+
}
|
|
267
|
+
catch (e) { }
|
|
268
|
+
this.processQueue();
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
managed = {
|
|
272
|
+
worker,
|
|
273
|
+
language: requestedLanguage,
|
|
274
|
+
lastUsed: Date.now(),
|
|
275
|
+
isBusy: false
|
|
276
|
+
};
|
|
277
|
+
this.pool.push(managed);
|
|
278
|
+
await this.runWorker(managed, job);
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
job.reject(err);
|
|
282
|
+
this.processQueue();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Helper to reinitialize an existing idle worker with a different language affinity and run the job.
|
|
287
|
+
*/
|
|
288
|
+
async reinitializeAndRunWorker(managed, job, requestedLanguage) {
|
|
289
|
+
// Priority: timeout.workerLoad (new) > 60 s default.
|
|
290
|
+
const loadTimeout = job.config.timeout?.workerLoad ?? 60000;
|
|
291
|
+
managed.isBusy = true;
|
|
292
|
+
managed.lastUsed = Date.now();
|
|
293
|
+
managed.activeJob = job;
|
|
294
|
+
try {
|
|
295
|
+
const reinitPromise = managed.worker.reinitialize(requestedLanguage);
|
|
296
|
+
if (loadTimeout > 0) {
|
|
297
|
+
await withTimeout(reinitPromise, loadTimeout, `OCR worker re-initialization timed out after ${loadTimeout}ms`);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
await reinitPromise;
|
|
301
|
+
}
|
|
302
|
+
managed.language = requestedLanguage;
|
|
303
|
+
// If the job finished/aborted while reinitializing, clean up and skip execution.
|
|
304
|
+
if (job.isFinished) {
|
|
305
|
+
const index = this.pool.indexOf(managed);
|
|
306
|
+
if (index !== -1) {
|
|
307
|
+
this.pool.splice(index, 1);
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
await managed.worker.terminate();
|
|
311
|
+
}
|
|
312
|
+
catch (e) { }
|
|
313
|
+
this.processQueue();
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
await this.runWorker(managed, job);
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
// Re-initialization failed/timed out, remove worker from pool and terminate
|
|
320
|
+
const index = this.pool.indexOf(managed);
|
|
321
|
+
if (index !== -1) {
|
|
322
|
+
this.pool.splice(index, 1);
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
await managed.worker.terminate();
|
|
326
|
+
}
|
|
327
|
+
catch (e) { }
|
|
328
|
+
job.reject(err);
|
|
329
|
+
this.processQueue();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Helper to execute OCR text recognition on the worker and return the results.
|
|
334
|
+
*/
|
|
335
|
+
async runWorker(managed, job) {
|
|
336
|
+
// Priority: timeout.recognition (new) > 30 s default.
|
|
337
|
+
const recogTimeout = job.config.timeout?.recognition ?? 30000;
|
|
338
|
+
managed.isBusy = true;
|
|
339
|
+
managed.lastUsed = Date.now();
|
|
340
|
+
managed.activeJob = job;
|
|
341
|
+
try {
|
|
342
|
+
const recognizePromise = managed.worker.recognize(job.image);
|
|
343
|
+
const { data: { text } } = recogTimeout > 0
|
|
344
|
+
? await withTimeout(recognizePromise, recogTimeout, `OCR recognition timed out after ${recogTimeout}ms`)
|
|
345
|
+
: await recognizePromise;
|
|
346
|
+
job.resolve(text);
|
|
347
|
+
}
|
|
348
|
+
catch (err) {
|
|
349
|
+
// If it timed out, terminate and remove worker to avoid reusing a stuck process
|
|
350
|
+
if (err.message?.includes('timed out')) {
|
|
351
|
+
const index = this.pool.indexOf(managed);
|
|
352
|
+
if (index !== -1) {
|
|
353
|
+
this.pool.splice(index, 1);
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
await managed.worker.terminate();
|
|
357
|
+
}
|
|
358
|
+
catch (e) { }
|
|
359
|
+
}
|
|
360
|
+
job.reject(err);
|
|
361
|
+
}
|
|
362
|
+
finally {
|
|
363
|
+
if (this.pool.includes(managed)) {
|
|
364
|
+
managed.isBusy = false;
|
|
365
|
+
managed.activeJob = undefined;
|
|
366
|
+
managed.lastUsed = Date.now();
|
|
367
|
+
}
|
|
368
|
+
this.processQueue();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Terminates all workers in the pool and resets the state.
|
|
373
|
+
*/
|
|
374
|
+
async terminate() {
|
|
375
|
+
if (this.timeoutId) {
|
|
376
|
+
clearTimeout(this.timeoutId);
|
|
377
|
+
this.timeoutId = null;
|
|
378
|
+
}
|
|
379
|
+
const workersToTerminate = this.pool.map(mw => mw.worker.terminate());
|
|
380
|
+
await Promise.all(workersToTerminate);
|
|
381
|
+
this.pool = [];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Performs Optical Character Recognition (OCR) on an image to extract text.
|
|
386
|
+
*
|
|
387
|
+
* Uses Tesseract.js to recognize text in the provided image buffer.
|
|
388
|
+
* This is useful for extracting text from screenshots, scanned documents,
|
|
389
|
+
* charts with labels, or any image containing text.
|
|
390
|
+
*
|
|
391
|
+
* This function uses a shared worker pool to minimize initialization overhead.
|
|
392
|
+
*
|
|
393
|
+
* @param image - The image data as a Buffer, file path, or Blob
|
|
394
|
+
* @param config - Optional configuration for language and custom worker paths
|
|
395
|
+
* @returns A promise that resolves to the recognized text as a string
|
|
396
|
+
* @throws {Error} If the image cannot be processed or Tesseract initialization fails
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```typescript
|
|
400
|
+
* // Extract text from an English image
|
|
401
|
+
* const text = await performOcr(imageBuffer, { language: 'eng' });
|
|
402
|
+
* ```
|
|
403
|
+
*
|
|
404
|
+
* @see https://github.com/naptha/tesseract.js for supported languages and options
|
|
405
|
+
*/
|
|
406
|
+
const performOcr = async (image, config) => {
|
|
407
|
+
// Prepare image data
|
|
408
|
+
let inputImage = image;
|
|
409
|
+
// In browser environment, convert Buffer to Blob for better compatibility
|
|
410
|
+
// @ts-ignore
|
|
411
|
+
if (envUtils_js_1.isBrowser && typeof Blob !== 'undefined' && Buffer.isBuffer(image)) {
|
|
412
|
+
inputImage = new Blob([image], { type: 'image/bmp' });
|
|
413
|
+
}
|
|
414
|
+
return await OcrSchedulerManager.getInstance().recognize(inputImage, config);
|
|
415
|
+
};
|
|
416
|
+
exports.performOcr = performOcr;
|
|
417
|
+
/**
|
|
418
|
+
* Terminates all OCR workers and cleans up resources.
|
|
419
|
+
*
|
|
420
|
+
* Should be called when the application is shutting down or OCR is no longer needed
|
|
421
|
+
* to prevent memory leaks and dangling worker processes.
|
|
422
|
+
*/
|
|
423
|
+
const terminateOcr = async () => {
|
|
424
|
+
if (OcrSchedulerManager.hasInstance()) {
|
|
425
|
+
await OcrSchedulerManager.getInstance().terminate();
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
exports.terminateOcr = terminateOcr;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared output-sanitization helpers.
|
|
3
|
+
*
|
|
4
|
+
* Every string in the parsed AST originates from an untrusted document, so any
|
|
5
|
+
* value interpolated into generated output (HTML, XHTML, CSS, URLs, inline
|
|
6
|
+
* scripts, CSV, RTF, Markdown) must be escaped for its destination context.
|
|
7
|
+
* These are the single source of truth — each generator delegates to them so
|
|
8
|
+
* escaping stays consistent and a gap fixed here is fixed everywhere.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Whether a string is a plain HTML attribute *name*, safe to interpolate before `="..."`.
|
|
12
|
+
*
|
|
13
|
+
* Escaping the value is not enough on its own: a key containing a quote or `=` closes the
|
|
14
|
+
* attribute and opens another, so `x" onmouseover="alert(1)" z` yields a real event handler no
|
|
15
|
+
* matter how carefully the value is escaped. This is the shape an attribute-injection payload
|
|
16
|
+
* takes, and rejecting it outright is simpler and safer than trying to escape a name.
|
|
17
|
+
*
|
|
18
|
+
* The predicate is shared rather than restated because it is now applied at four independent
|
|
19
|
+
* points (the parser's attribute collection, the generator's attribute bag, and two
|
|
20
|
+
* styleMap-driven paths). Each of those still keeps its own skip-list inline: the lists are the
|
|
21
|
+
* same policy expressed for different layers, and collapsing them would erase the defence in
|
|
22
|
+
* depth the surrounding comments describe.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isSafeHtmlAttributeName(name: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Whether a `styleMap` `output.tag` may be emitted as an element name.
|
|
27
|
+
*
|
|
28
|
+
* Callers must fall back to their default tag when this returns false, never emit the value.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isSafeStyleMapTag(tag: unknown): tag is string;
|
|
31
|
+
/**
|
|
32
|
+
* Escapes text for an HTML text node or a double-quoted attribute value.
|
|
33
|
+
* Includes the single quote so the result is also safe inside single-quoted
|
|
34
|
+
* attributes.
|
|
35
|
+
*/
|
|
36
|
+
export declare function escapeHtml(text: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Escapes text for an XML text node or attribute (XHTML/OPF/NCX). Same as
|
|
39
|
+
* escapeHtml but emits the XML-canonical `'` for the single quote.
|
|
40
|
+
*/
|
|
41
|
+
export declare function escapeXml(text: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Sanitizes a single CSS value (e.g. a color/size/font/alignment pulled from a
|
|
44
|
+
* document) for placement inside a `style="prop: VALUE"` attribute.
|
|
45
|
+
*
|
|
46
|
+
* - Drops the whole value if it contains a resource-fetching or executing
|
|
47
|
+
* construct (`url()`, `expression()`, `@import`, `image-set()`, `javascript:`)
|
|
48
|
+
* or angle brackets that could break out of the attribute/tag.
|
|
49
|
+
* - Strips characters that break out of `prop: value` (`;`, quotes), out of a
|
|
50
|
+
* `<style>` rule (`{}`), CSS escapes (`\`), and control characters.
|
|
51
|
+
*
|
|
52
|
+
* `rgb()/hsl()` and hex/named colors, lengths, and (unquoted) font names all
|
|
53
|
+
* survive; the trade-off is that legitimately quoted font names lose their
|
|
54
|
+
* quotes, which browsers tolerate.
|
|
55
|
+
*/
|
|
56
|
+
export declare function sanitizeCssValue(value: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Escapes a document-supplied URL for use in an href/src attribute. Beyond the
|
|
59
|
+
* usual attribute escaping, this rejects script-executing schemes (javascript:,
|
|
60
|
+
* vbscript:, data:, etc.) so a hyperlink extracted from an untrusted document
|
|
61
|
+
* can't run code when clicked — only http(s)/mailto/tel and relative/fragment
|
|
62
|
+
* URLs are passed through.
|
|
63
|
+
*/
|
|
64
|
+
export declare function sanitizeUrl(url: string): string;
|
|
65
|
+
/**
|
|
66
|
+
* Decide whether a non-provider `<iframe>` should be preserved, per
|
|
67
|
+
* `HtmlParserConfig.preserveIframes`. `true` allows any src; an array is a hostname allowlist,
|
|
68
|
+
* where an entry matches the src's host exactly or as a `.`-suffix (so `"vimeo.com"` also matches
|
|
69
|
+
* `player.vimeo.com`). A relative or unparseable src is allowed only under `true`. This is a
|
|
70
|
+
* preservation gate, not a sanitizer - the src is still scheme-checked with `sanitizeUrl` on
|
|
71
|
+
* generation.
|
|
72
|
+
*/
|
|
73
|
+
export declare function iframeAllowed(src: string, preserve: boolean | string[] | undefined): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Like sanitizeUrl but for an <img>/<source> src: additionally permits
|
|
76
|
+
* `data:image/*` URIs (embedded document images) while still rejecting
|
|
77
|
+
* script-executing schemes and non-image data URIs (e.g. data:text/html).
|
|
78
|
+
*/
|
|
79
|
+
export declare function sanitizeImageUrl(url: string): string;
|
|
80
|
+
/**
|
|
81
|
+
* Serializes data for embedding inside an inline <script> block. JSON.stringify
|
|
82
|
+
* alone doesn't escape "<", so a value containing "</script>" (e.g. a chart
|
|
83
|
+
* label from attacker-controlled document XML) would close the script early and
|
|
84
|
+
* inject markup. Also escapes the U+2028/U+2029 line separators, which are
|
|
85
|
+
* invalid in JS string literals.
|
|
86
|
+
*/
|
|
87
|
+
export declare function serializeForInlineScript(data: unknown): string;
|
|
88
|
+
/**
|
|
89
|
+
* Formats a value for a CSV field: guards against spreadsheet formula/DDE
|
|
90
|
+
* injection (CWE-1236) and applies RFC 4180 quoting.
|
|
91
|
+
*
|
|
92
|
+
* A cell beginning with `= + - @` (or a tab/CR that some apps treat as a
|
|
93
|
+
* formula start) is prefixed with a single quote so Excel/Sheets render it as
|
|
94
|
+
* literal text rather than executing it. Genuine numbers (including negatives)
|
|
95
|
+
* are exempt so numeric columns are preserved.
|
|
96
|
+
*/
|
|
97
|
+
export declare function csvSafeCell(value: string, delimiter: string): string;
|
|
98
|
+
/**
|
|
99
|
+
* Validates and escapes a document-supplied URL for an RTF `HYPERLINK` field argument.
|
|
100
|
+
*
|
|
101
|
+
* Mirrors `sanitizeUrl`'s contract (validate the scheme, then encode for the destination, else
|
|
102
|
+
* return `''`) but cannot reuse it: `sanitizeUrl` returns `escapeHtml(...)`, which would emit
|
|
103
|
+
* `&` into an RTF field. The scheme allowlist is deliberately identical to `sanitizeUrl`'s
|
|
104
|
+
* and `sanitizeMarkdownUrl`'s, so all three text generators agree on what a hyperlink may point at.
|
|
105
|
+
*
|
|
106
|
+
* **Additionally rejects UNC paths (`\\host\share`), which the HTML allowlist does not.** In a
|
|
107
|
+
* browser `\\evil.com\share` is an inert relative path; in Word it is a live UNC reference that
|
|
108
|
+
* triggers an SMB fetch and an NTLM handshake on click, which is a credential-leak vector rather
|
|
109
|
+
* than a rendering quirk. That asymmetry is why this is a separate function and not a flag on
|
|
110
|
+
* `sanitizeUrl` - the HTML helper must NOT gain this behaviour, since there the path is harmless
|
|
111
|
+
* and rejecting it would break legitimate relative links.
|
|
112
|
+
*
|
|
113
|
+
* Returns `''` for a rejected URL; callers emit the link text without the field wrapper, matching
|
|
114
|
+
* how HTML degrades to `href=""` and Markdown to `[text]()`.
|
|
115
|
+
*/
|
|
116
|
+
export declare function sanitizeRtfUrl(url: string): string;
|
|
117
|
+
/**
|
|
118
|
+
* Escapes text for RTF: neutralizes the control/group metacharacters `\ { }`
|
|
119
|
+
* (which would otherwise inject RTF control words or groups), encodes the double
|
|
120
|
+
* quote (so a hyperlink field argument can't be terminated early), and hex/unicode
|
|
121
|
+
* encodes non-ASCII characters.
|
|
122
|
+
*/
|
|
123
|
+
export declare function escapeRtf(text: string): string;
|
|
124
|
+
/**
|
|
125
|
+
* Escapes document text for a Markdown text position. Markdown passes raw HTML
|
|
126
|
+
* through to the renderer, so a `<` that begins an HTML tag or comment must be
|
|
127
|
+
* neutralized to prevent `<script>`/`<img onerror>` injection when the Markdown
|
|
128
|
+
* is later rendered to HTML.
|
|
129
|
+
*
|
|
130
|
+
* Deliberately narrow — only a `<` immediately followed by a letter, `/`, `!` or
|
|
131
|
+
* `?` (i.e. one that actually opens a tag/comment/PI, matching how browsers
|
|
132
|
+
* detect tags) is encoded. A bare `<` (e.g. `a < b`), `>`, `&`, `[]` and other
|
|
133
|
+
* Markdown metacharacters are left untouched: they can't start a tag, and
|
|
134
|
+
* MarkdownParser round-trips this output without decoding entities, so encoding
|
|
135
|
+
* them would corrupt re-parsed content. URL schemes are handled by
|
|
136
|
+
* sanitizeMarkdownUrl.
|
|
137
|
+
*/
|
|
138
|
+
export declare function markdownEscapeText(text: string): string;
|
|
139
|
+
/**
|
|
140
|
+
* Sanitizes a document-supplied URL for a Markdown `[text](url)` / ``
|
|
141
|
+
* target. Rejects script-executing schemes (returning '' → a dead link) and
|
|
142
|
+
* percent-encodes the characters that would break out of the `(...)` or inject
|
|
143
|
+
* markup. `&` is preserved so query strings survive; set `allowDataImage` for
|
|
144
|
+
* image targets so embedded `data:image/*` URIs are permitted.
|
|
145
|
+
*/
|
|
146
|
+
export declare function sanitizeMarkdownUrl(url: string, opts?: {
|
|
147
|
+
allowDataImage?: boolean;
|
|
148
|
+
}): string;
|