vde-open 0.1.7 → 0.1.8

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.
@@ -1,7 +1,6 @@
1
- import { r as __toESM, t as __commonJSMin } from "./rolldown-runtime-DC62tzP2.js";
1
+ import { C as roleAllowed, N as __commonJSMin, P as __toESM, S as isValidLogicalPath, _ as classifyLink, b as encodeLogicalPath, d as analyzeDocument, g as assetTypeOf, u as ParseLimitError, v as classifyReference, x as hasHiddenSegment, y as dirnameOfLogicalPath } from "./html-static-BDV6MgA7.js";
2
2
  import { $ as literal, A as successEnvelope, B as readParamsSchema, C as VdeError, D as ExitCode, E as isVdeError, F as htmlModeChangeParamsSchema, G as sha256Schema, H as reorderParamsSchema, I as htmlModeSchema, J as watchRuleSchema, K as sourceKindSchema, L as linkOpenParamsSchema, M as closeParamsSchema, N as documentFormatSchema, P as documentIdSchema, Q as boolean, R as listParamsSchema, S as validateAnswers, U as revisionSchema, V as renderGrantParamsSchema, W as searchParamsSchema, X as _enum, Y as LIMITS, Z as array, _ as feedbackWaitParamsSchema, a as uniqueTokens, at as canonicalJson, b as submissionSchema, c as feedbackAckParamsSchema, d as feedbackDraftParamsSchema, et as number, f as feedbackForgetParamsSchema, g as feedbackSubmitParamsSchema, h as feedbackStatusSchema, i as codePointLength, it as ZodError, j as assetRecordSchema, k as errorEnvelope, l as feedbackCancelParamsSchema, m as feedbackListParamsSchema, n as batchesOf, nt as strictObject, o as answersSchema, p as feedbackIdParamsSchema, q as sourceStateSchema, r as partsOf, rt as string, s as cancellationSchema, tt as record, u as feedbackCreateParamsSchema, v as questionnaireSchema, w as exitCodeForError, x as utf8Length, y as requestIdSchema, z as openParamsSchema } from "./search-index-B7DqaWh6.js";
3
- import { C as roleAllowed, _ as dirnameOfLogicalPath, a as ParseLimitError, g as classifyReference, h as classifyLink, m as assetTypeOf, o as analyzeDocument, v as encodeLogicalPath, x as isValidLogicalPath, y as hasHiddenSegment } from "./rendering-rules-DiE2jI7I.js";
4
- import { n as scanReferences, r as extractTitle } from "./render-BJnuEfHt.js";
3
+ import { n as scanReferences, r as extractTitle } from "./render-BX3arVZj.js";
5
4
  import { n as measureHeap, t as collectGarbage } from "./heap-C3qHm7SE.js";
6
5
  import { accessSync, constants, existsSync, stat, statSync, unwatchFile, watch, watchFile } from "node:fs";
7
6
  import { appendFile, chmod, link, lstat, mkdir, mkdtemp, open, readFile, readdir, readlink, realpath, rename, rm, rmdir, stat as stat$1, writeFile } from "node:fs/promises";
@@ -155,7 +154,7 @@ function parseStrictJson(text) {
155
154
  }
156
155
  //#endregion
157
156
  //#region package.json
158
- var version = "0.1.7";
157
+ var version = "0.1.8";
159
158
  //#endregion
160
159
  //#region src/documents/cursor.ts
161
160
  function invalidCursor(reason) {
@@ -5659,6 +5658,12 @@ async function printPage(cdp, url) {
5659
5658
  if (navigation.errorText !== void 0 || navigation.loaderId === void 0) throw exportFailed("load");
5660
5659
  loaderId = navigation.loaderId;
5661
5660
  if (!seen.has(loaderId)) await loaded;
5661
+ await cdp.send("Emulation.setEmulatedMedia", { media: "print" }, sessionId);
5662
+ if ((await cdp.send("Runtime.evaluate", {
5663
+ expression: "(async () => { document.body.getBoundingClientRect(); await document.fonts.ready; })()",
5664
+ awaitPromise: true,
5665
+ returnByValue: true
5666
+ }, sessionId)).exceptionDetails !== void 0) throw exportFailed("load");
5662
5667
  const { stream } = await cdp.send("Page.printToPDF", {
5663
5668
  printBackground: true,
5664
5669
  preferCSSPageSize: true,
@@ -5716,12 +5721,11 @@ function createPdfService(options) {
5716
5721
  process.kill(-child.pid, "SIGKILL");
5717
5722
  } catch {}
5718
5723
  };
5719
- const writePage = async (path, html, slot, images) => {
5724
+ const writePage = async (path, html, slot, assets, budget) => {
5720
5725
  const file = await open(path, "wx", 384);
5721
- let written = 0;
5722
5726
  const write = async (text) => {
5723
- written += Buffer.byteLength(text);
5724
- if (written > maxPageBytes) throw new VdeError("E_LIMIT_EXCEEDED", "The document is too large to print.", {
5727
+ budget.written += Buffer.byteLength(text);
5728
+ if (budget.written > maxPageBytes) throw new VdeError("E_LIMIT_EXCEEDED", "The document is too large to print.", {
5725
5729
  reason: "page-size",
5726
5730
  limit: maxPageBytes
5727
5731
  });
@@ -5731,10 +5735,10 @@ function createPdfService(options) {
5731
5735
  let last = 0;
5732
5736
  for (const match of html.matchAll(new RegExp(`${slot}-(\\d+)`, "g"))) {
5733
5737
  await write(html.slice(last, match.index));
5734
- const image = images[Number(match[1])];
5735
- if (image !== void 0) {
5736
- await write(`data:${image.mime};base64,`);
5737
- await write((await store.readBlob(image.sha256)).toString("base64"));
5738
+ const asset = assets[Number(match[1])];
5739
+ if (asset !== void 0) {
5740
+ await write(`data:${asset.mime};base64,`);
5741
+ await write((await store.readBlob(asset.sha256)).toString("base64"));
5738
5742
  }
5739
5743
  last = match.index + match[0].length;
5740
5744
  }
@@ -5821,22 +5825,53 @@ function createPdfService(options) {
5821
5825
  const run = async (documentId, revision, signal) => {
5822
5826
  const started = Date.now();
5823
5827
  const { record, entry } = documents.describeRevision(documentId, revision);
5824
- if (entry.format !== "markdown") throw new VdeError("E_UNSUPPORTED_FORMAT", "Only Markdown documents can be exported as PDF.", { format: entry.format });
5825
5828
  const source = (await store.readBlob(entry.sourceSha256)).toString("utf8");
5826
- const images = entry.assets.filter((asset) => asset.role === "image" || asset.role === "svg");
5827
5829
  const slot = randomBytes(16).toString("hex");
5828
- const html = await printer.print({
5830
+ let assets = entry.assets.filter((asset) => asset.role === "image" || asset.role === "svg");
5831
+ let html;
5832
+ let stylesheets = [];
5833
+ if (entry.format === "markdown") html = await printer.print({
5829
5834
  source,
5830
5835
  title: record.title,
5831
5836
  documentLogicalPath: entry.documentLogicalPath,
5832
- images: images.map((asset) => asset.logicalPath),
5837
+ images: assets.map((asset) => asset.logicalPath),
5833
5838
  imageSlot: slot
5834
5839
  });
5840
+ else {
5841
+ const sheets = [];
5842
+ for (const asset of entry.assets) {
5843
+ if (asset.role !== "style") continue;
5844
+ const bytes = await store.readBlob(asset.sha256);
5845
+ try {
5846
+ sheets.push({
5847
+ logicalPath: asset.logicalPath,
5848
+ text: new TextDecoder("utf-8", { fatal: true }).decode(bytes)
5849
+ });
5850
+ } catch {}
5851
+ }
5852
+ const readable = new Set(sheets.map((sheet) => sheet.logicalPath));
5853
+ assets = entry.assets.filter((asset) => asset.role === "image" || asset.role === "svg" || asset.role === "font" || asset.role === "style" && readable.has(asset.logicalPath));
5854
+ const output = await printer.printHtml({
5855
+ source,
5856
+ title: record.title,
5857
+ documentLogicalPath: entry.documentLogicalPath,
5858
+ assets: assets.map(({ logicalPath, role }) => ({
5859
+ logicalPath,
5860
+ role
5861
+ })),
5862
+ stylesheets: sheets,
5863
+ assetSlot: slot
5864
+ });
5865
+ html = output.html;
5866
+ stylesheets = output.stylesheets;
5867
+ }
5835
5868
  const browser = options.findBrowser();
5836
5869
  const directory = await mkdtemp(join(tempRoot, TEMP_PREFIX$1));
5837
5870
  try {
5838
5871
  const page = join(directory, "document.html");
5839
- await writePage(page, html, slot, images);
5872
+ const budget = { written: 0 };
5873
+ await writePage(page, html, slot, assets, budget);
5874
+ for (const sheet of stylesheets) await writePage(join(directory, sheet.name), sheet.css, slot, assets, budget);
5840
5875
  const { product, pdf } = await print(browser, directory, pathToFileURL(page).href, signal);
5841
5876
  if (!isPdf(pdf)) throw exportFailed("output");
5842
5877
  options.onEvent?.("pdf.exported", {
@@ -5857,7 +5892,7 @@ function createPdfService(options) {
5857
5892
  }
5858
5893
  };
5859
5894
  return {
5860
- exportMarkdown(documentId, revision, signal) {
5895
+ exportPdf(documentId, revision, signal) {
5861
5896
  if (closed) return Promise.reject(stopping());
5862
5897
  if (waiting >= MAX_WAITING) return Promise.reject(new VdeError("E_LIMIT_EXCEEDED", "Too many PDF exports are waiting.", {
5863
5898
  reason: "waiting",
@@ -11257,7 +11292,7 @@ async function startManagementServer(deps, options = {}) {
11257
11292
  api.post("/documents/:id/refresh", async (c) => ok(c, "documents.refresh", await deps.documents.refresh({ documentId: c.req.param("id") })));
11258
11293
  api.post("/documents/:id/pdf", async (c) => {
11259
11294
  const body = strictObject({ revision: revisionSchema }).parse(await c.req.json());
11260
- const pdf = await deps.pdf.exportMarkdown(c.req.param("id"), body.revision, c.req.raw.signal);
11295
+ const pdf = await deps.pdf.exportPdf(c.req.param("id"), body.revision, c.req.raw.signal);
11261
11296
  return c.body(new Uint8Array(pdf), 200, {
11262
11297
  "Content-Type": "application/pdf",
11263
11298
  "Content-Disposition": "attachment"
@@ -13807,6 +13842,10 @@ function createParseService(options = {}) {
13807
13842
  op: "print",
13808
13843
  input
13809
13844
  }),
13845
+ printHtml: (input) => request({
13846
+ op: "printHtml",
13847
+ input
13848
+ }),
13810
13849
  async close() {
13811
13850
  closed = true;
13812
13851
  collector.close(new VdeError("E_DAEMON_STOPPING", "The daemon is stopping."));
@@ -1,4 +1,4 @@
1
- import { D as escapeAttr, E as parseMarkdown, O as escapeHtml$2, k as footnoteReferenceId, n as renderedLinkOf, p as parseMarkdownDocument, r as MARKDOWN_PARSE_OPTIONS, t as localImagePath } from "./rendering-rules-DiE2jI7I.js";
1
+ import { A as escapeAttr, D as defaultTreeAdapter, E as serialize, M as footnoteReferenceId, O as NS, T as parseFragment, c as renderedLinkOf, h as parseMarkdownDocument, i as transformStaticHtml, j as escapeHtml$2, k as parseMarkdown, l as MARKDOWN_PARSE_OPTIONS, n as resolveAssetUrl, o as transformCss, s as localImagePath, t as DiagnosticLog, w as parse, y as dirnameOfLogicalPath } from "./html-static-BDV6MgA7.js";
2
2
  //#region ../../node_modules/.pnpm/@tanstack+markdown@1.0.0_react@19.3.0/node_modules/@tanstack/markdown/dist/html.js
3
3
  function renderHtml(input, options = {}) {
4
4
  return (typeof input === "string" ? parseMarkdown(input, options) : input).children.map((node) => renderBlock(node, options)).join("\n");
@@ -1708,6 +1708,50 @@ figure.tm-code-frame > pre {
1708
1708
  }
1709
1709
  `;
1710
1710
  //#endregion
1711
+ //#region ../../packages/document/src/html-print.ts
1712
+ const PRINT_CSP$1 = "default-src 'none'; img-src data:; font-src data:; style-src 'unsafe-inline' file:; base-uri 'none'; form-action 'none'";
1713
+ function renderHtmlPrintDocument(input) {
1714
+ if (!/^[0-9a-f]{32,}$/.test(input.assetSlot)) throw new Error("The asset slot must be random hexadecimal.");
1715
+ const assets = new Map(input.assets.map(({ logicalPath, role }) => [logicalPath, role]));
1716
+ const readable = new Set(input.stylesheets.map(({ logicalPath }) => logicalPath));
1717
+ const urls = /* @__PURE__ */ new Map();
1718
+ input.assets.forEach(({ logicalPath, role }, index) => {
1719
+ if (role === "style" && readable.has(logicalPath)) urls.set(logicalPath, `asset-${String(index)}.css`);
1720
+ else if (role === "image" || role === "svg" || role === "font") urls.set(logicalPath, `${input.assetSlot}-${String(index)}`);
1721
+ });
1722
+ const log = new DiagnosticLog();
1723
+ const transformed = transformStaticHtml({
1724
+ source: input.source,
1725
+ documentLogicalPath: input.documentLogicalPath,
1726
+ assets,
1727
+ assetUrls: urls
1728
+ }, log);
1729
+ const stylesheets = input.stylesheets.flatMap(({ logicalPath, text }) => {
1730
+ const name = urls.get(logicalPath);
1731
+ if (name === void 0 || assets.get(logicalPath) !== "style") return [];
1732
+ return [{
1733
+ name,
1734
+ css: transformCss(text, "stylesheet", (url, context) => resolveAssetUrl(url, context, dirnameOfLogicalPath(logicalPath), assets, log, { assetUrls: urls })).css
1735
+ }];
1736
+ });
1737
+ const document = parse(transformed.html, { scriptingEnabled: false });
1738
+ const root = document.childNodes.find((node) => "tagName" in node && node.tagName === "html");
1739
+ const head = root && "childNodes" in root ? root.childNodes.find((node) => "tagName" in node && node.tagName === "head") : void 0;
1740
+ if (!head || !("tagName" in head)) throw new Error("The HTML document has no head.");
1741
+ const prefix = parseFragment(head, `<meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="${PRINT_CSP$1}"><style>@page { size: A4; margin: 20mm; }</style>`, {}).childNodes;
1742
+ for (const node of prefix) node.parentNode = head;
1743
+ head.childNodes.unshift(...prefix);
1744
+ if (!head.childNodes.some((node) => "tagName" in node && node.tagName === "title")) {
1745
+ const title = defaultTreeAdapter.createElement("title", NS.HTML, []);
1746
+ defaultTreeAdapter.insertText(title, input.title);
1747
+ defaultTreeAdapter.appendChild(head, title);
1748
+ }
1749
+ return {
1750
+ html: serialize(document),
1751
+ stylesheets
1752
+ };
1753
+ }
1754
+ //#endregion
1711
1755
  //#region ../../packages/document/src/print.ts
1712
1756
  const PRINT_CSP = "default-src 'none'; img-src data:; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'";
1713
1757
  const KANA = /[\u3041-\u3096\u30a1-\u30fa]/;
@@ -1776,4 +1820,4 @@ function renderPrintDocument(input) {
1776
1820
  ].join("\n");
1777
1821
  }
1778
1822
  //#endregion
1779
- export { cssString, headerTitle, renderPrintDocument };
1823
+ export { cssString, headerTitle, renderHtmlPrintDocument, renderPrintDocument };
@@ -0,0 +1,181 @@
1
+ import { _ as classifyLink, a as scanCssReferences, f as assertWithinLimits, i as transformStaticHtml, k as parseMarkdown, l as MARKDOWN_PARSE_OPTIONS, m as markdownUrlTransform, n as resolveAssetUrl, o as transformCss, p as inlineText$1, r as scanHtmlReferences, t as DiagnosticLog, w as parse, y as dirnameOfLogicalPath } from "./html-static-BDV6MgA7.js";
2
+ //#region ../../packages/document/src/title.ts
3
+ const TITLE_SCAN_CHARS = 65536;
4
+ const MAX_TITLE_LENGTH = 160;
5
+ function inlineText(nodes) {
6
+ let text = "";
7
+ for (const node of nodes) switch (node.type) {
8
+ case "text":
9
+ case "inlineCode":
10
+ text += node.value;
11
+ break;
12
+ case "strong":
13
+ case "emphasis":
14
+ case "strike":
15
+ case "link":
16
+ case "inlineComponent":
17
+ text += inlineText(node.children);
18
+ break;
19
+ case "image":
20
+ text += node.alt;
21
+ break;
22
+ case "break": text += " ";
23
+ }
24
+ return text;
25
+ }
26
+ function normalize(text) {
27
+ const collapsed = text.replace(/\s+/g, " ").trim();
28
+ if (collapsed === "") return null;
29
+ return Array.from(collapsed).slice(0, MAX_TITLE_LENGTH).join("");
30
+ }
31
+ function markdownTitle(source) {
32
+ const document = parseMarkdown(source, {
33
+ allowHtml: false,
34
+ frontmatter: true
35
+ });
36
+ for (const node of document.children) if (node.type === "heading") return normalize(inlineText(node.children));
37
+ return null;
38
+ }
39
+ function findElement(node, tagName) {
40
+ if ("tagName" in node && node.tagName === tagName) return node;
41
+ if (!("childNodes" in node)) return null;
42
+ for (const child of node.childNodes) {
43
+ const found = findElement(child, tagName);
44
+ if (found) return found;
45
+ }
46
+ return null;
47
+ }
48
+ function htmlTitle(source) {
49
+ const title = findElement(parse(source), "title");
50
+ if (!title || !("childNodes" in title)) return null;
51
+ let text = "";
52
+ for (const child of title.childNodes) if (child.nodeName === "#text" && "value" in child) text += child.value;
53
+ return normalize(text);
54
+ }
55
+ function extractTitle(source, format) {
56
+ const head = source.slice(0, TITLE_SCAN_CHARS);
57
+ return format === "markdown" ? markdownTitle(head) : htmlTitle(head);
58
+ }
59
+ //#endregion
60
+ //#region ../../packages/document/src/markdown-render.ts
61
+ const MAX_LINKS = 2e3;
62
+ const MAX_LINK_TEXT = 120;
63
+ function parseWithImages(source) {
64
+ const imageUrls = [];
65
+ const document = parseMarkdown(source, {
66
+ ...MARKDOWN_PARSE_OPTIONS,
67
+ urlTransform: (url, kind, defaultUrl) => {
68
+ if (kind === "image") imageUrls.push(url);
69
+ return markdownUrlTransform(url, kind, defaultUrl);
70
+ }
71
+ });
72
+ assertWithinLimits(document);
73
+ return {
74
+ blocks: document.children,
75
+ imageUrls
76
+ };
77
+ }
78
+ function visitInline(nodes, visit) {
79
+ for (const node of nodes) {
80
+ visit(node);
81
+ if ("children" in node) visitInline(node.children, visit);
82
+ }
83
+ }
84
+ function visitBlocks(nodes, visit) {
85
+ for (const node of nodes) switch (node.type) {
86
+ case "heading":
87
+ case "paragraph":
88
+ visitInline(node.children, visit);
89
+ break;
90
+ case "blockquote":
91
+ case "callout":
92
+ case "component":
93
+ visitBlocks(node.children, visit);
94
+ break;
95
+ case "list":
96
+ case "footnotes":
97
+ for (const item of node.items) visitBlocks(item.children, visit);
98
+ break;
99
+ case "table":
100
+ for (const cell of node.header) visitInline(cell.children, visit);
101
+ for (const row of node.rows) for (const cell of row) visitInline(cell.children, visit);
102
+ }
103
+ }
104
+ function scanMarkdownReferences(source) {
105
+ return parseWithImages(source).imageUrls.map((url) => ({
106
+ url,
107
+ context: "image"
108
+ }));
109
+ }
110
+ function analyzeMarkdownRender(input, log = new DiagnosticLog()) {
111
+ const baseDir = dirnameOfLogicalPath(input.documentLogicalPath);
112
+ const parsed = parseWithImages(input.source);
113
+ for (const url of parsed.imageUrls) resolveAssetUrl(url, "image", baseDir, input.assets, log);
114
+ const links = [];
115
+ const seen = /* @__PURE__ */ new Set();
116
+ visitBlocks(parsed.blocks, (node) => {
117
+ if (node.type !== "link" || links.length >= MAX_LINKS || seen.has(node.href)) return;
118
+ const target = classifyLink(node.href);
119
+ if (target.kind === "fragment") return;
120
+ seen.add(node.href);
121
+ links.push({
122
+ linkId: `lnk_${String(links.length + 1).padStart(4, "0")}`,
123
+ href: node.href,
124
+ text: inlineText$1(node.children).replace(/\s+/g, " ").trim().slice(0, MAX_LINK_TEXT),
125
+ kind: target.kind
126
+ });
127
+ });
128
+ return {
129
+ links,
130
+ diagnostics: log.list()
131
+ };
132
+ }
133
+ //#endregion
134
+ //#region ../../packages/document/src/render-document.ts
135
+ function renderDocument(input) {
136
+ const assets = new Map(input.assets.map((asset) => [asset.logicalPath, asset.role]));
137
+ const log = new DiagnosticLog();
138
+ const staticInput = {
139
+ source: input.source,
140
+ documentLogicalPath: input.documentLogicalPath,
141
+ assets
142
+ };
143
+ const stylesheets = input.stylesheets.map(({ logicalPath, text }) => {
144
+ const baseDir = dirnameOfLogicalPath(logicalPath);
145
+ const result = transformCss(text, "stylesheet", (url, context) => resolveAssetUrl(url, context, baseDir, assets, log));
146
+ if (result.invalid > 0) log.note("css-invalid", logicalPath);
147
+ return {
148
+ logicalPath,
149
+ css: result.css
150
+ };
151
+ });
152
+ if (input.format === "markdown") {
153
+ const info = analyzeMarkdownRender(staticInput, log);
154
+ return {
155
+ html: null,
156
+ stylesheets,
157
+ links: info.links,
158
+ diagnostics: info.diagnostics,
159
+ headingTargets: []
160
+ };
161
+ }
162
+ const result = transformStaticHtml({
163
+ ...staticInput,
164
+ interactive: input.mode === "interactive",
165
+ ...input.mode === "interactive" && input.sdkScript !== null ? { sdkScript: input.sdkScript } : {}
166
+ }, log);
167
+ return {
168
+ html: result.html,
169
+ stylesheets,
170
+ links: result.links,
171
+ diagnostics: result.diagnostics,
172
+ headingTargets: result.headingTargets
173
+ };
174
+ }
175
+ function scanReferences(kind, source) {
176
+ if (kind === "markdown") return scanMarkdownReferences(source);
177
+ if (kind === "html") return scanHtmlReferences(source);
178
+ return scanCssReferences(source, "stylesheet");
179
+ }
180
+ //#endregion
181
+ export { scanReferences as n, extractTitle as r, renderDocument as t };