@gmickel/gno 1.45.1 → 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.
Files changed (236) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES.md +46 -0
  3. package/assets/skill/SKILL.md +7 -6
  4. package/assets/skill/cli-reference.md +14 -6
  5. package/assets/skill/mcp-reference.md +4 -1
  6. package/assets/spa-production.json.gz +0 -0
  7. package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip +0 -0
  8. package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip.sha256 +1 -0
  9. package/browser-extension/dist/chunk-4tc9v0ja.js +74 -0
  10. package/browser-extension/dist/manifest.json +1 -1
  11. package/browser-extension/dist/preview.html +1 -1
  12. package/browser-extension/dist/service-worker.js +32 -33
  13. package/bunfig.toml +2 -0
  14. package/package.json +40 -26
  15. package/spec/cli.md +30 -11
  16. package/spec/db/schema.sql +146 -1
  17. package/spec/mcp.md +26 -0
  18. package/src/app/context-runtime-types.ts +3 -0
  19. package/src/app/context-runtime.ts +2 -0
  20. package/src/cli/commands/ask.ts +6 -1
  21. package/src/cli/commands/daemon.ts +21 -8
  22. package/src/cli/commands/embed.ts +77 -41
  23. package/src/cli/commands/mcp/install.ts +20 -0
  24. package/src/cli/commands/mcp/paths.ts +25 -0
  25. package/src/cli/commands/mcp/status.ts +6 -0
  26. package/src/cli/detach.ts +3 -2
  27. package/src/cli/program.ts +6 -0
  28. package/src/config/types.ts +3 -3
  29. package/src/converters/adapters/markitdownTs/adapter.ts +1 -2
  30. package/src/converters/adapters/officeparser/adapter.ts +1 -2
  31. package/src/converters/versions.ts +6 -8
  32. package/src/core/context-evidence.ts +8 -4
  33. package/src/core/job-manager.ts +95 -13
  34. package/src/core/network-boundary-inventory.ts +10 -0
  35. package/src/core/shutdown-budget.ts +45 -0
  36. package/src/embed/backlog.ts +107 -4
  37. package/src/embed/batch.ts +42 -2
  38. package/src/embed/fingerprint.ts +16 -0
  39. package/src/embed/retry.ts +113 -5
  40. package/src/embed/variant-backlog.ts +105 -0
  41. package/src/embed/variant-plan.ts +62 -0
  42. package/src/embed/variant-retry.ts +113 -0
  43. package/src/ingestion/graph-reconciliation.ts +327 -0
  44. package/src/ingestion/sync.ts +9 -272
  45. package/src/llm/http-inference.ts +6 -0
  46. package/src/llm/httpEmbedding.ts +37 -6
  47. package/src/llm/httpGeneration.ts +18 -3
  48. package/src/llm/httpRerank.ts +23 -5
  49. package/src/llm/inference-cancellation.ts +168 -0
  50. package/src/llm/inference-scope.ts +202 -0
  51. package/src/llm/lazy-ports.ts +115 -0
  52. package/src/llm/native-worker/client.ts +541 -0
  53. package/src/llm/native-worker/dispatcher.ts +228 -0
  54. package/src/llm/native-worker/embedding-identity.ts +33 -0
  55. package/src/llm/native-worker/entry.ts +173 -0
  56. package/src/llm/native-worker/errors.ts +32 -0
  57. package/src/llm/native-worker/evaluation.ts +16 -0
  58. package/src/llm/native-worker/owned-exit.ts +108 -0
  59. package/src/llm/native-worker/owner.ts +141 -0
  60. package/src/llm/native-worker/ports.ts +317 -0
  61. package/src/llm/native-worker/protocol.ts +442 -0
  62. package/src/llm/native-worker/runtime-config.ts +92 -0
  63. package/src/llm/nodeLlamaCpp/adapter.ts +77 -20
  64. package/src/llm/nodeLlamaCpp/embedding.ts +130 -46
  65. package/src/llm/nodeLlamaCpp/generation.ts +34 -5
  66. package/src/llm/nodeLlamaCpp/lifecycle-options.ts +99 -0
  67. package/src/llm/nodeLlamaCpp/lifecycle.ts +209 -204
  68. package/src/llm/nodeLlamaCpp/rerank-capacity.ts +111 -0
  69. package/src/llm/nodeLlamaCpp/rerank.ts +118 -27
  70. package/src/llm/nodeLlamaCpp/simulator-handle.ts +73 -0
  71. package/src/llm/nodeLlamaCpp/simulator-install.ts +124 -0
  72. package/src/llm/nodeLlamaCpp/simulator-session.ts +240 -0
  73. package/src/llm/nodeLlamaCpp/simulator-types.ts +80 -0
  74. package/src/llm/types.ts +35 -5
  75. package/src/mcp/context.ts +27 -0
  76. package/src/mcp/http-transport.ts +12 -10
  77. package/src/mcp/server.ts +3 -0
  78. package/src/mcp/tool-profile.ts +30 -8
  79. package/src/mcp/tools/context.ts +8 -11
  80. package/src/mcp/tools/embed.ts +1 -1
  81. package/src/mcp/tools/index-cmd.ts +1 -1
  82. package/src/mcp/tools/index.ts +10 -8
  83. package/src/mcp/tools/query.ts +14 -30
  84. package/src/mcp/tools/vsearch.ts +1 -1
  85. package/src/pipeline/answer.ts +23 -3
  86. package/src/pipeline/claim-verifier.ts +6 -0
  87. package/src/pipeline/expansion.ts +43 -40
  88. package/src/pipeline/explain.ts +6 -2
  89. package/src/pipeline/filters.ts +63 -0
  90. package/src/pipeline/fusion.ts +29 -9
  91. package/src/pipeline/graph-retrieval.ts +29 -9
  92. package/src/pipeline/hybrid.ts +198 -55
  93. package/src/pipeline/hydration.ts +161 -0
  94. package/src/pipeline/owner-fusion.ts +87 -0
  95. package/src/pipeline/rerank.ts +35 -11
  96. package/src/pipeline/search.ts +13 -2
  97. package/src/pipeline/types.ts +5 -3
  98. package/src/pipeline/vsearch.ts +87 -7
  99. package/src/sdk/client.ts +47 -3
  100. package/src/sdk/embed.ts +63 -39
  101. package/src/serve/background-runtime.ts +1 -1
  102. package/src/serve/context.ts +41 -56
  103. package/src/serve/embed-scheduler.ts +58 -35
  104. package/src/serve/public/components/IndexingProgress.tsx +46 -60
  105. package/src/serve/public/globals.built.css +1 -1
  106. package/src/serve/public/lib/shiki-language-ids.ts +14 -0
  107. package/src/serve/resident-admission.ts +36 -36
  108. package/src/serve/resident-background-work.ts +20 -2
  109. package/src/serve/resident-request.ts +11 -5
  110. package/src/serve/resident-runtime.ts +97 -61
  111. package/src/serve/resident-shutdown.ts +153 -0
  112. package/src/serve/routes/api.ts +3 -1
  113. package/src/serve/server.ts +47 -26
  114. package/src/store/migrations/028-vector-variants.ts +54 -0
  115. package/src/store/migrations/029-graph-reference-state.ts +77 -0
  116. package/src/store/migrations/index.ts +4 -0
  117. package/src/store/sqlite/adapter.ts +251 -183
  118. package/src/store/sqlite/eligibility.ts +174 -0
  119. package/src/store/sqlite/graph-edge-application.ts +66 -0
  120. package/src/store/sqlite/graph-reference-state.ts +194 -0
  121. package/src/store/sqlite/legacy-vector-ownership.ts +79 -0
  122. package/src/store/types.ts +80 -12
  123. package/src/store/vector/eligibility.ts +36 -0
  124. package/src/store/vector/freshness.ts +33 -6
  125. package/src/store/vector/lazy.ts +81 -0
  126. package/src/store/vector/sqlite-vec.ts +106 -54
  127. package/src/store/vector/stats.ts +14 -3
  128. package/src/store/vector/types.ts +35 -2
  129. package/src/store/vector/variant-search.ts +192 -0
  130. package/src/store/vector/variants.ts +451 -0
  131. package/vendor/converters/markitdown-ts/LICENSE +21 -0
  132. package/vendor/converters/markitdown-ts/dist/index.cjs +1180 -0
  133. package/vendor/converters/markitdown-ts/dist/index.d.cts +46 -0
  134. package/vendor/converters/markitdown-ts/dist/index.d.mts +46 -0
  135. package/vendor/converters/markitdown-ts/dist/index.d.ts +46 -0
  136. package/vendor/converters/markitdown-ts/dist/index.mjs +1152 -0
  137. package/vendor/converters/markitdown-ts/package.json +77 -0
  138. package/vendor/converters/officeparser/LICENSE +21 -0
  139. package/vendor/converters/officeparser/dist/OfficeConverter.d.ts +47 -0
  140. package/vendor/converters/officeparser/dist/OfficeConverter.js +76 -0
  141. package/vendor/converters/officeparser/dist/OfficeGenerator.d.ts +23 -0
  142. package/vendor/converters/officeparser/dist/OfficeGenerator.js +73 -0
  143. package/vendor/converters/officeparser/dist/OfficeParser.d.ts +106 -0
  144. package/vendor/converters/officeparser/dist/OfficeParser.js +332 -0
  145. package/vendor/converters/officeparser/dist/cli.d.ts +28 -0
  146. package/vendor/converters/officeparser/dist/cli.js +381 -0
  147. package/vendor/converters/officeparser/dist/defaults.d.ts +41 -0
  148. package/vendor/converters/officeparser/dist/defaults.js +218 -0
  149. package/vendor/converters/officeparser/dist/generators/BaseGenerator.d.ts +107 -0
  150. package/vendor/converters/officeparser/dist/generators/BaseGenerator.js +248 -0
  151. package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.d.ts +82 -0
  152. package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.js +797 -0
  153. package/vendor/converters/officeparser/dist/generators/CsvGenerator.d.ts +38 -0
  154. package/vendor/converters/officeparser/dist/generators/CsvGenerator.js +245 -0
  155. package/vendor/converters/officeparser/dist/generators/EpubGenerator.d.ts +43 -0
  156. package/vendor/converters/officeparser/dist/generators/EpubGenerator.js +315 -0
  157. package/vendor/converters/officeparser/dist/generators/HtmlGenerator.d.ts +59 -0
  158. package/vendor/converters/officeparser/dist/generators/HtmlGenerator.js +1942 -0
  159. package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.d.ts +96 -0
  160. package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.js +1175 -0
  161. package/vendor/converters/officeparser/dist/generators/PdfGenerator.d.ts +22 -0
  162. package/vendor/converters/officeparser/dist/generators/PdfGenerator.js +194 -0
  163. package/vendor/converters/officeparser/dist/generators/RtfGenerator.d.ts +29 -0
  164. package/vendor/converters/officeparser/dist/generators/RtfGenerator.js +316 -0
  165. package/vendor/converters/officeparser/dist/generators/TextGenerator.d.ts +13 -0
  166. package/vendor/converters/officeparser/dist/generators/TextGenerator.js +201 -0
  167. package/vendor/converters/officeparser/dist/index.d.ts +60 -0
  168. package/vendor/converters/officeparser/dist/index.js +72 -0
  169. package/vendor/converters/officeparser/dist/index.mjs +18 -0
  170. package/vendor/converters/officeparser/dist/officeparser.browser.d.ts +2621 -0
  171. package/vendor/converters/officeparser/dist/officeparser.browser.iife.js +1336 -0
  172. package/vendor/converters/officeparser/dist/officeparser.browser.mjs +1335 -0
  173. package/vendor/converters/officeparser/dist/officeparser.browser.slim.d.ts +2621 -0
  174. package/vendor/converters/officeparser/dist/officeparser.browser.slim.iife.js +1336 -0
  175. package/vendor/converters/officeparser/dist/officeparser.browser.slim.mjs +1335 -0
  176. package/vendor/converters/officeparser/dist/parsers/CsvParser.d.ts +9 -0
  177. package/vendor/converters/officeparser/dist/parsers/CsvParser.js +115 -0
  178. package/vendor/converters/officeparser/dist/parsers/EpubParser.d.ts +8 -0
  179. package/vendor/converters/officeparser/dist/parsers/EpubParser.js +217 -0
  180. package/vendor/converters/officeparser/dist/parsers/ExcelParser.d.ts +32 -0
  181. package/vendor/converters/officeparser/dist/parsers/ExcelParser.js +736 -0
  182. package/vendor/converters/officeparser/dist/parsers/HtmlParser.d.ts +2 -0
  183. package/vendor/converters/officeparser/dist/parsers/HtmlParser.js +1287 -0
  184. package/vendor/converters/officeparser/dist/parsers/MarkdownParser.d.ts +2 -0
  185. package/vendor/converters/officeparser/dist/parsers/MarkdownParser.js +1272 -0
  186. package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.d.ts +31 -0
  187. package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.js +1819 -0
  188. package/vendor/converters/officeparser/dist/parsers/PdfParser.d.ts +67 -0
  189. package/vendor/converters/officeparser/dist/parsers/PdfParser.js +848 -0
  190. package/vendor/converters/officeparser/dist/parsers/PowerPointParser.d.ts +32 -0
  191. package/vendor/converters/officeparser/dist/parsers/PowerPointParser.js +950 -0
  192. package/vendor/converters/officeparser/dist/parsers/RtfParser.d.ts +187 -0
  193. package/vendor/converters/officeparser/dist/parsers/RtfParser.js +1801 -0
  194. package/vendor/converters/officeparser/dist/parsers/WordParser.d.ts +79 -0
  195. package/vendor/converters/officeparser/dist/parsers/WordParser.js +1177 -0
  196. package/vendor/converters/officeparser/dist/sbom.cdx.json +1763 -0
  197. package/vendor/converters/officeparser/dist/types.d.ts +2507 -0
  198. package/vendor/converters/officeparser/dist/types.js +107 -0
  199. package/vendor/converters/officeparser/dist/utils/astUtils.d.ts +16 -0
  200. package/vendor/converters/officeparser/dist/utils/astUtils.js +33 -0
  201. package/vendor/converters/officeparser/dist/utils/chartUtils.d.ts +6 -0
  202. package/vendor/converters/officeparser/dist/utils/chartUtils.js +257 -0
  203. package/vendor/converters/officeparser/dist/utils/configUtils.d.ts +44 -0
  204. package/vendor/converters/officeparser/dist/utils/configUtils.js +315 -0
  205. package/vendor/converters/officeparser/dist/utils/dateUtils.d.ts +17 -0
  206. package/vendor/converters/officeparser/dist/utils/dateUtils.js +69 -0
  207. package/vendor/converters/officeparser/dist/utils/envUtils.d.ts +29 -0
  208. package/vendor/converters/officeparser/dist/utils/envUtils.js +152 -0
  209. package/vendor/converters/officeparser/dist/utils/errorUtils.d.ts +72 -0
  210. package/vendor/converters/officeparser/dist/utils/errorUtils.js +245 -0
  211. package/vendor/converters/officeparser/dist/utils/imageUtils.d.ts +66 -0
  212. package/vendor/converters/officeparser/dist/utils/imageUtils.js +133 -0
  213. package/vendor/converters/officeparser/dist/utils/mathUtils.d.ts +42 -0
  214. package/vendor/converters/officeparser/dist/utils/mathUtils.js +385 -0
  215. package/vendor/converters/officeparser/dist/utils/moduleLoader.d.ts +18 -0
  216. package/vendor/converters/officeparser/dist/utils/moduleLoader.js +106 -0
  217. package/vendor/converters/officeparser/dist/utils/ocrUtils.d.ts +42 -0
  218. package/vendor/converters/officeparser/dist/utils/ocrUtils.js +428 -0
  219. package/vendor/converters/officeparser/dist/utils/sanitize.d.ts +148 -0
  220. package/vendor/converters/officeparser/dist/utils/sanitize.js +344 -0
  221. package/vendor/converters/officeparser/dist/utils/sheetUtils.d.ts +7 -0
  222. package/vendor/converters/officeparser/dist/utils/sheetUtils.js +35 -0
  223. package/vendor/converters/officeparser/dist/utils/styleMapper.d.ts +36 -0
  224. package/vendor/converters/officeparser/dist/utils/styleMapper.js +224 -0
  225. package/vendor/converters/officeparser/dist/utils/xmlUtils.d.ts +163 -0
  226. package/vendor/converters/officeparser/dist/utils/xmlUtils.js +461 -0
  227. package/vendor/converters/officeparser/dist/utils/zipUtils.d.ts +134 -0
  228. package/vendor/converters/officeparser/dist/utils/zipUtils.js +337 -0
  229. package/vendor/converters/officeparser/package.json +147 -0
  230. package/vendor/converters/upstream-manifest.json +124 -0
  231. package/vendor/dependency-fixes/README.md +77 -0
  232. package/vendor/dependency-fixes/vendor-converters.py +83 -0
  233. package/browser-extension/artifacts/gno-browser-clipper-v1.45.1.zip +0 -0
  234. package/browser-extension/artifacts/gno-browser-clipper-v1.45.1.zip.sha256 +0 -1
  235. package/browser-extension/dist/chunk-627emwpj.js +0 -75
  236. /package/browser-extension/dist/{chunk-ydfx5d7p.css → chunk-z74y8n8c.css} +0 -0
@@ -0,0 +1,22 @@
1
+ import { ConversionResult, GeneratorConfig, OfficeParserAST } from '../types.js';
2
+ import { BaseGenerator } from './BaseGenerator.js';
3
+ /**
4
+ * Generates high-fidelity PDF documents using a headless browser engine.
5
+ *
6
+ * Uses an environment-aware strategy:
7
+ * - Node.js: Uses Puppeteer (peer dependency) for server-side rendering.
8
+ * - Browser: Leverages native browser print capabilities.
9
+ */
10
+ export declare class PdfGenerator extends BaseGenerator<'pdf'> {
11
+ constructor(ast: OfficeParserAST, config?: GeneratorConfig<'pdf'>);
12
+ generate(): Promise<ConversionResult<'pdf'>>;
13
+ /**
14
+ * Node.js implementation using Puppeteer.
15
+ * Uses dynamic import to avoid bundling puppeteer into the library core.
16
+ */
17
+ private generateInNode;
18
+ /**
19
+ * Browser implementation using hidden iframe and native print.
20
+ */
21
+ private generateInBrowser;
22
+ }
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PdfGenerator = void 0;
4
+ const types_js_1 = require("../types.js");
5
+ const envUtils_js_1 = require("../utils/envUtils.js");
6
+ const errorUtils_js_1 = require("../utils/errorUtils.js");
7
+ const BaseGenerator_js_1 = require("./BaseGenerator.js");
8
+ const HtmlGenerator_js_1 = require("./HtmlGenerator.js");
9
+ /**
10
+ * Generates high-fidelity PDF documents using a headless browser engine.
11
+ *
12
+ * Uses an environment-aware strategy:
13
+ * - Node.js: Uses Puppeteer (peer dependency) for server-side rendering.
14
+ * - Browser: Leverages native browser print capabilities.
15
+ */
16
+ class PdfGenerator extends BaseGenerator_js_1.BaseGenerator {
17
+ constructor(ast, config) {
18
+ super('pdf', ast, config);
19
+ }
20
+ async generate() {
21
+ // Step 1: Generate high-fidelity HTML as the source for PDF rendering
22
+ // We reuse the current configuration but ensure standalone mode is on for HTML
23
+ const htmlGenerator = new HtmlGenerator_js_1.HtmlGenerator(this.ast, {
24
+ ...this.config,
25
+ // Force sourceAttributes off: those data-* attributes are wire-format plumbing for
26
+ // structured consumers and change the mermaid shape's rendered appearance, neither of
27
+ // which belongs in a printed PDF.
28
+ htmlConfig: { ...this.config.htmlConfig, standalone: true, sourceAttributes: false },
29
+ });
30
+ const htmlResult = await htmlGenerator.generate();
31
+ const html = typeof htmlResult.value === 'string' ? htmlResult.value : '';
32
+ // Step 2: Render to PDF based on environment
33
+ if (envUtils_js_1.isBrowser) {
34
+ return this.generateInBrowser(html);
35
+ }
36
+ else {
37
+ return this.generateInNode(html);
38
+ }
39
+ }
40
+ /**
41
+ * Node.js implementation using Puppeteer.
42
+ * Uses dynamic import to avoid bundling puppeteer into the library core.
43
+ */
44
+ async generateInNode(html) {
45
+ const signal = this.config.abortSignal;
46
+ if (signal?.aborted) {
47
+ throw (0, errorUtils_js_1.getAbortError)();
48
+ }
49
+ let browser;
50
+ const onAbort = async () => {
51
+ if (browser) {
52
+ try {
53
+ await browser.close();
54
+ }
55
+ catch (e) {
56
+ // ignore
57
+ }
58
+ }
59
+ };
60
+ if (signal) {
61
+ signal.addEventListener('abort', onAbort);
62
+ }
63
+ try {
64
+ // Dynamic import for peer dependency
65
+ // @ts-ignore
66
+ const puppeteerModule = await import('puppeteer');
67
+ const puppeteer = puppeteerModule.default || puppeteerModule;
68
+ const launchOptions = { ...this.config.pdfConfig.launchOptions };
69
+ // Handle Apple Silicon / Rosetta performance warning and binary detection
70
+ const isMac = process.platform === 'darwin';
71
+ const isX64 = process.arch === 'x64';
72
+ let isRosetta = false;
73
+ if (isMac && isX64) {
74
+ try {
75
+ const { execSync } = await import('child_process');
76
+ isRosetta = execSync('sysctl -n hw.optional.arm64', { stdio: 'pipe' }).toString().trim() === '1';
77
+ }
78
+ catch (e) {
79
+ // Ignore errors in detection
80
+ }
81
+ }
82
+ if (isRosetta) {
83
+ this.warn(types_js_1.OfficeWarningType.PERFORMANCE_TIP, "You are running on Apple Silicon using an x64 Node.js installation. PDF generation will be significantly faster (avoiding Rosetta translation) if you switch to a native arm64 Node.js version.");
84
+ }
85
+ // Note: We are no longer suppressing the Puppeteer 'Degraded performance' warning here
86
+ // to ensure transparency about the environment state. Programmatically fixing this
87
+ // would require force-downloading a ~300MB arm64 browser binary or switching to
88
+ // a system-installed Chrome, both of which are too intrusive for a library.
89
+ browser = await puppeteer.launch(launchOptions);
90
+ const page = await browser.newPage();
91
+ // Harden against SSRF: the HTML being rendered is derived from an untrusted
92
+ // document, and `networkidle0` would otherwise fetch every URL it references
93
+ // (external images, stylesheets, etc.) from this host — reaching internal
94
+ // services or a cloud metadata endpoint (169.254.169.254). Intercept requests
95
+ // and allow only inline data/blob URIs and the configured chart CDN; abort every
96
+ // other remote fetch.
97
+ const allowedHosts = new Set();
98
+ try {
99
+ const chartSrc = this.config.htmlConfig?.chartJsSrc;
100
+ if (chartSrc)
101
+ allowedHosts.add(new URL(chartSrc).host);
102
+ }
103
+ catch { /* no or invalid chart CDN configured */ }
104
+ let blockedRemoteResource = false;
105
+ await page.setRequestInterception(true);
106
+ page.on('request', (req) => {
107
+ const url = req.url();
108
+ if (url.startsWith('data:') || url.startsWith('blob:') || url.startsWith('about:')) {
109
+ return req.continue().catch(() => { });
110
+ }
111
+ try {
112
+ if (allowedHosts.has(new URL(url).host)) {
113
+ return req.continue().catch(() => { });
114
+ }
115
+ }
116
+ catch { /* unparseable URL — fall through and block */ }
117
+ blockedRemoteResource = true;
118
+ return req.abort().catch(() => { });
119
+ });
120
+ const pdfConfig = this.config.pdfConfig;
121
+ const timeout = pdfConfig.timeout;
122
+ if (timeout !== undefined && timeout > 0) {
123
+ page.setDefaultTimeout(timeout);
124
+ page.setDefaultNavigationTimeout(timeout);
125
+ }
126
+ // Set content and wait for network/assets to load
127
+ await page.setContent(html, { waitUntil: 'networkidle0' });
128
+ const pdfBuffer = await page.pdf({
129
+ format: pdfConfig.format,
130
+ width: pdfConfig.width,
131
+ height: pdfConfig.height,
132
+ landscape: pdfConfig.landscape,
133
+ printBackground: pdfConfig.printBackground,
134
+ scale: pdfConfig.scale,
135
+ margin: pdfConfig.margin,
136
+ displayHeaderFooter: pdfConfig.displayHeaderFooter,
137
+ headerTemplate: pdfConfig.headerTemplate,
138
+ footerTemplate: pdfConfig.footerTemplate,
139
+ });
140
+ await browser.close();
141
+ browser = null;
142
+ if (blockedRemoteResource) {
143
+ this.warn(types_js_1.OfficeWarningType.BROWSER_GENERATION_LIMITATION, 'One or more remote resources referenced by the document were blocked during PDF rendering to prevent server-side request forgery (SSRF). Only inline images and the configured chart CDN are loaded.');
144
+ }
145
+ return {
146
+ value: new Uint8Array(pdfBuffer),
147
+ messages: this.messages
148
+ };
149
+ }
150
+ catch (err) {
151
+ if (browser) {
152
+ try {
153
+ await browser.close();
154
+ }
155
+ catch (e) {
156
+ // ignore
157
+ }
158
+ browser = null;
159
+ }
160
+ if (signal?.aborted) {
161
+ throw (0, errorUtils_js_1.getAbortError)();
162
+ }
163
+ if (err.message && (err.message.includes('timeout') || err.message.includes('Timeout'))) {
164
+ this.warn(types_js_1.OfficeWarningType.PAGE_LOAD_FAILED, `PDF generation timed out: ${err.message}`);
165
+ }
166
+ else {
167
+ this.warn(types_js_1.OfficeWarningType.DEPENDENCY_LOAD_FAILED, `puppeteer. Please install it with 'npm install puppeteer'. Error: ${err.message}`);
168
+ }
169
+ return {
170
+ value: new Uint8Array(),
171
+ messages: this.messages
172
+ };
173
+ }
174
+ finally {
175
+ if (signal) {
176
+ signal.removeEventListener('abort', onAbort);
177
+ }
178
+ }
179
+ }
180
+ /**
181
+ * Browser implementation using hidden iframe and native print.
182
+ */
183
+ async generateInBrowser(html) {
184
+ this.warn(types_js_1.OfficeWarningType.BROWSER_GENERATION_LIMITATION, "Browser-based PDF generation triggered. For automated 'Save as PDF' without user interaction, we recommend using 'html2pdf.js' as a custom generator hook.");
185
+ // In a browser environment, we return the HTML and suggest using window.print()
186
+ // Or we could trigger a print dialog immediately if desired,
187
+ // but returning the string allows the user to decide where to inject it.
188
+ return {
189
+ value: html,
190
+ messages: this.messages
191
+ };
192
+ }
193
+ }
194
+ exports.PdfGenerator = PdfGenerator;
@@ -0,0 +1,29 @@
1
+ import { ConversionResult, GeneratorConfig, OfficeContentNode, OfficeParserAST } from '../types.js';
2
+ import { BaseGenerator } from './BaseGenerator.js';
3
+ /**
4
+ * Generates high-fidelity RTF (Rich Text Format) from an AST.
5
+ */
6
+ export declare class RtfGenerator extends BaseGenerator<'rtf'> {
7
+ private colorTable;
8
+ private inTable;
9
+ /**
10
+ * Set while rendering a heading's children.
11
+ *
12
+ * A heading emits its own `{\\b\\fs44 ...}` wrapper, so a run inside it that also carries bold
13
+ * and a size - which is now the normal case for ODF, where the heading's paragraph style is
14
+ * inherited by its runs - would emit a nested `\\fs28` that *overrides* the outer `\\fs44`.
15
+ * The heading then renders at the body-text size it was styled with rather than at heading
16
+ * size. Suppressing the inherited weight and size inside a heading keeps the heading's own
17
+ * wrapper authoritative; every other property (colour, font) still comes through.
18
+ */
19
+ private inHeading;
20
+ /** As `inHeading`, but for the inherited font size - see `hasUniformFormatting`. */
21
+ private headingUniformSize;
22
+ constructor(ast: OfficeParserAST, config?: GeneratorConfig<'rtf'>);
23
+ generate(): Promise<ConversionResult<'rtf'>>;
24
+ protected processNodeRecursive(node: OfficeContentNode, processor: (node: OfficeContentNode, childrenOutput: string) => Promise<string>): Promise<string>;
25
+ private renderBody;
26
+ private getColorIndex;
27
+ private isLightColor;
28
+ private escapeRtf;
29
+ }
@@ -0,0 +1,316 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RtfGenerator = void 0;
4
+ const sanitize_js_1 = require("../utils/sanitize.js");
5
+ const BaseGenerator_js_1 = require("./BaseGenerator.js");
6
+ const errorUtils_js_1 = require("../utils/errorUtils.js");
7
+ /**
8
+ * Generates high-fidelity RTF (Rich Text Format) from an AST.
9
+ */
10
+ class RtfGenerator extends BaseGenerator_js_1.BaseGenerator {
11
+ colorTable = [];
12
+ inTable = false;
13
+ /**
14
+ * Set while rendering a heading's children.
15
+ *
16
+ * A heading emits its own `{\\b\\fs44 ...}` wrapper, so a run inside it that also carries bold
17
+ * and a size - which is now the normal case for ODF, where the heading's paragraph style is
18
+ * inherited by its runs - would emit a nested `\\fs28` that *overrides* the outer `\\fs44`.
19
+ * The heading then renders at the body-text size it was styled with rather than at heading
20
+ * size. Suppressing the inherited weight and size inside a heading keeps the heading's own
21
+ * wrapper authoritative; every other property (colour, font) still comes through.
22
+ */
23
+ inHeading = false;
24
+ /** As `inHeading`, but for the inherited font size - see `hasUniformFormatting`. */
25
+ headingUniformSize = false;
26
+ constructor(ast, config) {
27
+ super('rtf', ast, config);
28
+ }
29
+ async generate() {
30
+ this.colorTable = [];
31
+ // We first process all nodes to collect colors and analyze structure
32
+ const bodyContent = await this.renderBody(this.ast);
33
+ let output = '{\\rtf1\\ansi\\uc1\\deff0\n';
34
+ // 1. Info Group (Metadata)
35
+ const meta = this.effectiveMetadata;
36
+ if (this.config.renderMetadata && meta) {
37
+ // RTF's \info group is a fixed set of control words with no slot for caller-defined keys.
38
+ this.warnUnrepresentableCustomMetadata('RTF');
39
+ output += '{\\info';
40
+ if (meta.title)
41
+ output += `{\\title ${this.escapeRtf(meta.title)}}`;
42
+ if (meta.author)
43
+ output += `{\\author ${this.escapeRtf(meta.author)}}`;
44
+ if (meta.description)
45
+ output += `{\\comm ${this.escapeRtf(meta.description)}}`;
46
+ // \subject and \keywords are standard \info destinations, so unlike a caller's
47
+ // arbitrary custom keys these DO have a home in RTF.
48
+ if (meta.subject)
49
+ output += `{\\subject ${this.escapeRtf(meta.subject)}}`;
50
+ if (meta.keywords)
51
+ output += `{\\keywords ${this.escapeRtf(meta.keywords)}}`;
52
+ output += '}\n';
53
+ }
54
+ // 2. Font Table
55
+ output += '{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Times New Roman;}}\n';
56
+ // 3. Color Table
57
+ if (this.colorTable.length > 0) {
58
+ output += '{\\colortbl;';
59
+ for (const hex of this.colorTable) {
60
+ const r = parseInt(hex.substring(1, 3), 16);
61
+ const g = parseInt(hex.substring(3, 5), 16);
62
+ const b = parseInt(hex.substring(5, 7), 16);
63
+ output += `\\red${r}\\green${g}\\blue${b};`;
64
+ }
65
+ output += '}\n';
66
+ }
67
+ // 4. Body
68
+ output += '\\f0\\fs24\n';
69
+ output += bodyContent;
70
+ output += '}';
71
+ return {
72
+ value: output,
73
+ messages: this.messages
74
+ };
75
+ }
76
+ async processNodeRecursive(node, processor) {
77
+ // Mirrors the check in BaseGenerator.processNodeRecursive. This override replaces that
78
+ // method entirely, so without repeating the check here the signal would be silently
79
+ // inert for this generator - which is exactly how it was missed.
80
+ (0, errorUtils_js_1.checkAbortSignal)(this.config.abortSignal);
81
+ const wasInTable = this.inTable;
82
+ if (node.type === 'table')
83
+ this.inTable = true;
84
+ const wasInHeading = this.inHeading;
85
+ const wasHeadingSize = this.headingUniformSize;
86
+ if (node.type === 'heading') {
87
+ this.inHeading = this.hasUniformFormatting(node, f => f?.bold === true);
88
+ this.headingUniformSize = this.hasUniformFormatting(node, f => !!f?.size);
89
+ }
90
+ const result = await super.processNodeRecursive(node, processor);
91
+ this.inTable = wasInTable;
92
+ this.inHeading = wasInHeading;
93
+ this.headingUniformSize = wasHeadingSize;
94
+ return result;
95
+ }
96
+ async renderBody(ast) {
97
+ let body = '';
98
+ this.inTable = false;
99
+ const processor = async (node, childrenOutput) => {
100
+ const mapping = this.getSemanticMapping(node);
101
+ if (mapping) {
102
+ if (mapping.tag === 'blockquote') {
103
+ const pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
104
+ return `${pPr}\\li720\\ri720\\sa120 ${childrenOutput}\\par\n`;
105
+ }
106
+ const hMatch = mapping.tag.match(/^h([1-6])$/);
107
+ if (hMatch) {
108
+ const level = parseInt(hMatch[1]);
109
+ const fontSize = 24 + (6 - level) * 4;
110
+ const pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
111
+ return `${pPr}\\s${level}\\sb240\\sa120{\\b\\fs${fontSize} ${childrenOutput}}\\par\n`;
112
+ }
113
+ }
114
+ switch (node.type) {
115
+ case 'text': {
116
+ let text = this.escapeRtf(node.text || '');
117
+ const f = node.formatting;
118
+ const meta = node.metadata;
119
+ if (this.config.includeFormatting && f) {
120
+ let prefix = '';
121
+ let suffix = '';
122
+ if (f.bold && !this.inHeading) {
123
+ prefix += '\\b ';
124
+ suffix = '\\b0 ' + suffix;
125
+ }
126
+ if (f.italic) {
127
+ prefix += '\\i ';
128
+ suffix = '\\i0 ' + suffix;
129
+ }
130
+ if (f.underline) {
131
+ prefix += '\\ul ';
132
+ suffix = '\\ul0 ' + suffix;
133
+ }
134
+ if (f.strikethrough) {
135
+ prefix += '\\strike ';
136
+ suffix = '\\strike0 ' + suffix;
137
+ }
138
+ if (f.color) {
139
+ // RTF default background is white. Ensure light text without a dark background remains readable.
140
+ const isTextLight = this.isLightColor(f.color);
141
+ const isBgLight = !f.backgroundColor || this.isLightColor(f.backgroundColor);
142
+ if (!(isTextLight && isBgLight)) {
143
+ const idx = this.getColorIndex(f.color);
144
+ prefix += `\\cf${idx + 1} `;
145
+ }
146
+ }
147
+ if (f.backgroundColor) {
148
+ const idx = this.getColorIndex(f.backgroundColor);
149
+ prefix += `\\highlight${idx + 1} `;
150
+ }
151
+ if (f.size && !this.headingUniformSize) {
152
+ let pt = 12; // default
153
+ const val = parseFloat(f.size);
154
+ if (!isNaN(val)) {
155
+ if (f.size.includes('in'))
156
+ pt = val * 72;
157
+ else if (f.size.includes('cm'))
158
+ pt = val * 28.3465;
159
+ else if (f.size.includes('mm'))
160
+ pt = val * 2.83465;
161
+ else if (f.size.includes('px'))
162
+ pt = val * 0.75;
163
+ else
164
+ pt = val;
165
+ }
166
+ prefix += `\\fs${Math.round(pt * 2)} `;
167
+ }
168
+ text = `{\\f0 ${prefix}${text}${suffix}}`;
169
+ }
170
+ if (meta?.link) {
171
+ const isInternal = meta.linkType !== 'external';
172
+ if (!this.config.ignoreInternalLinks || !isInternal) {
173
+ // Scheme-checked, not merely escaped. escapeRtf neutralizes the field
174
+ // metacharacters but says nothing about where the link points, so RTF
175
+ // was the one generator that would emit `javascript:` or a `file://`
176
+ // /UNC target that HTML and Markdown both reject. On rejection, fall
177
+ // through to the bare link text - the same degradation as HTML's
178
+ // href="" and Markdown's [text]().
179
+ const safeLink = (0, sanitize_js_1.sanitizeRtfUrl)(meta.link);
180
+ if (safeLink) {
181
+ return `{\\field{\\*\\fldinst{HYPERLINK "${safeLink}"}}{\\fldrslt ${text}}}`;
182
+ }
183
+ }
184
+ }
185
+ return text;
186
+ }
187
+ case 'heading': {
188
+ const meta = node.metadata;
189
+ const level = meta?.level || 1;
190
+ const fontSize = 24 + (6 - level) * 4;
191
+ const pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
192
+ return `${pPr}\\s${level}\\sb240\\sa120{\\b\\fs${fontSize} ${childrenOutput}}\\par\n`;
193
+ }
194
+ case 'paragraph': {
195
+ let pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
196
+ pPr += '\\sa120';
197
+ if (this.config.includeFormatting && node.metadata) {
198
+ const meta = node.metadata;
199
+ if (meta.alignment) {
200
+ if (meta.alignment === 'center')
201
+ pPr += '\\qc';
202
+ else if (meta.alignment === 'right')
203
+ pPr += '\\qr';
204
+ else if (meta.alignment === 'justify')
205
+ pPr += '\\qj';
206
+ }
207
+ }
208
+ return `${pPr} ${childrenOutput}\\par\n`;
209
+ }
210
+ case 'list': {
211
+ const meta = node.metadata;
212
+ const level = meta?.indentation || 0;
213
+ const indent = (level + 1) * 360;
214
+ const isOrdered = meta?.listType === 'ordered';
215
+ const marker = isOrdered ? `${(meta.itemIndex ?? 0) + 1}. ` : '\\bullet ';
216
+ const listControl = isOrdered ? '\\pndec' : '\\pnbullet';
217
+ const pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
218
+ return `${pPr}\\li${indent}\\fi-360\\ilvl${level}${listControl} ${marker}${childrenOutput}\\par\n`;
219
+ }
220
+ case 'table': {
221
+ return `\\pard\\sa0\n${childrenOutput}`;
222
+ }
223
+ case 'row': {
224
+ const cells = node.children || [];
225
+ const pageWidth = 9000; // Standard twips width
226
+ const cellWidth = Math.floor(pageWidth / (cells.length || 1));
227
+ let cellDefs = '';
228
+ for (let i = 0; i < cells.length; i++) {
229
+ // Add basic cell borders and calculate width
230
+ cellDefs += `\\clbrdrt\\brdrs\\brdrw10\\clbrdrl\\brdrs\\brdrw10\\clbrdrb\\brdrs\\brdrw10\\clbrdrr\\brdrs\\brdrw10\\cellx${(i + 1) * cellWidth}`;
231
+ }
232
+ return `\\trowd\\trgaph108\\trleft-108${cellDefs}\n${childrenOutput}\\row\n`;
233
+ }
234
+ case 'cell': {
235
+ return `\\pard\\intbl\\sb60\\sa60 ${childrenOutput}\\cell\n`;
236
+ }
237
+ case 'image': {
238
+ if (!this.config.includeImages)
239
+ return '';
240
+ const meta = node.metadata;
241
+ const attachmentName = meta?.attachmentName;
242
+ const attachment = this.ast.attachments.find(a => a.name === attachmentName);
243
+ if (attachment && attachment.data) {
244
+ const type = attachment.extension === 'png' ? 'pngblip' : 'jpegblip';
245
+ // Convert base64 to hex
246
+ const binary = atob(attachment.data);
247
+ let hex = '';
248
+ for (let i = 0; i < binary.length; i++) {
249
+ const h = binary.charCodeAt(i).toString(16);
250
+ hex += h.length === 1 ? '0' + h : h;
251
+ if (i % 64 === 63)
252
+ hex += '\n'; // Add newlines for better RTF readability
253
+ }
254
+ // Default goals (approx 3 inches wide at 1440 twips per inch)
255
+ return `{\\pict\\${type}\\picwgoal4320\\pichgoal3240\n${hex}\n}\n`;
256
+ }
257
+ return '';
258
+ }
259
+ case 'break': {
260
+ return node.metadata?.breakType === 'page' ? '\\page\n' : '\\line\n';
261
+ }
262
+ case 'embed': {
263
+ // RTF has no embed concept - degrade to the URL as plain text rather than
264
+ // silently dropping the node (it has no children to fall back to).
265
+ const meta = node.metadata;
266
+ if (!meta?.url)
267
+ return '';
268
+ // Rendered as visible text rather than a field, but still a URL a reader may
269
+ // copy, so it gets the same scheme policy.
270
+ const safeUrl = (0, sanitize_js_1.sanitizeRtfUrl)(meta.url);
271
+ if (!safeUrl)
272
+ return '';
273
+ const pPr = this.inTable ? '\\pard\\intbl' : '\\pard';
274
+ return `${pPr}\\sa120 ${safeUrl}\\par\n`;
275
+ }
276
+ default:
277
+ return childrenOutput;
278
+ }
279
+ };
280
+ for (const node of ast.content) {
281
+ body += await this.processNodeRecursive(node, processor);
282
+ }
283
+ if (this.collectedNotes.length > 0) {
284
+ body += '\\pard\\sb120\\sa120\\keepn{\\b Notes:}\\par\n';
285
+ for (const note of this.collectedNotes) {
286
+ body += await this.processNodeRecursive(note, processor);
287
+ }
288
+ }
289
+ return body;
290
+ }
291
+ getColorIndex(hex) {
292
+ const h = hex.toUpperCase();
293
+ let idx = this.colorTable.indexOf(h);
294
+ if (idx === -1) {
295
+ idx = this.colorTable.length;
296
+ this.colorTable.push(h);
297
+ }
298
+ return idx;
299
+ }
300
+ isLightColor(hex) {
301
+ if (!hex || hex.length !== 7 || !hex.startsWith('#'))
302
+ return false;
303
+ const r = parseInt(hex.substring(1, 3), 16);
304
+ const g = parseInt(hex.substring(3, 5), 16);
305
+ const b = parseInt(hex.substring(5, 7), 16);
306
+ if (isNaN(r) || isNaN(g) || isNaN(b))
307
+ return false;
308
+ // Simple luminance calculation
309
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
310
+ return luminance > 0.8;
311
+ }
312
+ escapeRtf(text) {
313
+ return (0, sanitize_js_1.escapeRtf)(text);
314
+ }
315
+ }
316
+ exports.RtfGenerator = RtfGenerator;
@@ -0,0 +1,13 @@
1
+ import { ConversionResult, GeneratorConfig, OfficeParserAST } from '../types.js';
2
+ import { BaseGenerator } from './BaseGenerator.js';
3
+ /**
4
+ * Generates plain text from an AST.
5
+ */
6
+ export declare class TextGenerator extends BaseGenerator<'text'> {
7
+ constructor(ast: OfficeParserAST, config?: GeneratorConfig<'text'>);
8
+ /**
9
+ * Generates plain text by concatenating text content from nodes.
10
+ */
11
+ generate(): Promise<ConversionResult<'text'>>;
12
+ private renderTable;
13
+ }