@cairn-tool/cairn 3.1.0 → 3.3.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 (91) hide show
  1. package/README.md +74 -28
  2. package/dist/agent/audit/index.d.ts +6 -6
  3. package/dist/agent/audit/index.js +6 -17
  4. package/dist/agent/audit/index.js.map +1 -1
  5. package/dist/archive/run.d.ts +14 -7
  6. package/dist/archive/run.js +4 -1
  7. package/dist/archive/run.js.map +1 -1
  8. package/dist/archive/sets.d.ts +26 -1
  9. package/dist/archive/sets.js +108 -0
  10. package/dist/archive/sets.js.map +1 -1
  11. package/dist/atomic-write.d.ts +22 -0
  12. package/dist/atomic-write.js +42 -0
  13. package/dist/atomic-write.js.map +1 -0
  14. package/dist/binary-kind.d.ts +13 -0
  15. package/dist/binary-kind.js +25 -0
  16. package/dist/binary-kind.js.map +1 -0
  17. package/dist/cli.js +48 -0
  18. package/dist/cli.js.map +1 -1
  19. package/dist/commands/archive.js +11 -5
  20. package/dist/commands/archive.js.map +1 -1
  21. package/dist/commands/jira.js +1 -21
  22. package/dist/commands/jira.js.map +1 -1
  23. package/dist/commands/pdf.d.ts +29 -0
  24. package/dist/commands/pdf.js +345 -0
  25. package/dist/commands/pdf.js.map +1 -0
  26. package/dist/contract/registry.js +65 -1
  27. package/dist/contract/registry.js.map +1 -1
  28. package/dist/contract/schemas/index.js +2 -0
  29. package/dist/contract/schemas/index.js.map +1 -1
  30. package/dist/contract/schemas/pdf.d.ts +2 -0
  31. package/dist/contract/schemas/pdf.js +292 -0
  32. package/dist/contract/schemas/pdf.js.map +1 -0
  33. package/dist/jira/adf/to-markdown.js +2 -32
  34. package/dist/jira/adf/to-markdown.js.map +1 -1
  35. package/dist/markdown-stringify.d.ts +35 -0
  36. package/dist/markdown-stringify.js +44 -0
  37. package/dist/markdown-stringify.js.map +1 -0
  38. package/dist/pdf/attachments.d.ts +50 -0
  39. package/dist/pdf/attachments.js +229 -0
  40. package/dist/pdf/attachments.js.map +1 -0
  41. package/dist/pdf/diagnostics.d.ts +110 -0
  42. package/dist/pdf/diagnostics.js +137 -0
  43. package/dist/pdf/diagnostics.js.map +1 -0
  44. package/dist/pdf/document.d.ts +159 -0
  45. package/dist/pdf/document.js +272 -0
  46. package/dist/pdf/document.js.map +1 -0
  47. package/dist/pdf/forms.d.ts +9 -0
  48. package/dist/pdf/forms.js +101 -0
  49. package/dist/pdf/forms.js.map +1 -0
  50. package/dist/pdf/inspect.d.ts +42 -0
  51. package/dist/pdf/inspect.js +232 -0
  52. package/dist/pdf/inspect.js.map +1 -0
  53. package/dist/pdf/layout.d.ts +78 -0
  54. package/dist/pdf/layout.js +524 -0
  55. package/dist/pdf/layout.js.map +1 -0
  56. package/dist/pdf/outline.d.ts +30 -0
  57. package/dist/pdf/outline.js +127 -0
  58. package/dist/pdf/outline.js.map +1 -0
  59. package/dist/pdf/read.d.ts +62 -0
  60. package/dist/pdf/read.js +193 -0
  61. package/dist/pdf/read.js.map +1 -0
  62. package/dist/pdf/struct.d.ts +35 -0
  63. package/dist/pdf/struct.js +226 -0
  64. package/dist/pdf/struct.js.map +1 -0
  65. package/dist/pdf/text.d.ts +71 -0
  66. package/dist/pdf/text.js +166 -0
  67. package/dist/pdf/text.js.map +1 -0
  68. package/dist/pdf/to-markdown.d.ts +22 -0
  69. package/dist/pdf/to-markdown.js +289 -0
  70. package/dist/pdf/to-markdown.js.map +1 -0
  71. package/dist/pdf/types.d.ts +205 -0
  72. package/dist/pdf/types.js +2 -0
  73. package/dist/pdf/types.js.map +1 -0
  74. package/dist/pdf/validate.d.ts +51 -0
  75. package/dist/pdf/validate.js +136 -0
  76. package/dist/pdf/validate.js.map +1 -0
  77. package/dist/serve/pdf-tools.d.ts +2 -0
  78. package/dist/serve/pdf-tools.js +196 -0
  79. package/dist/serve/pdf-tools.js.map +1 -0
  80. package/dist/serve/tools.d.ts +2 -17
  81. package/dist/serve/tools.js +4 -0
  82. package/dist/serve/tools.js.map +1 -1
  83. package/dist/serve/types.d.ts +22 -0
  84. package/dist/serve/types.js +2 -0
  85. package/dist/serve/types.js.map +1 -0
  86. package/dist/usage/providers/cursor.d.ts +2 -0
  87. package/dist/usage/providers/cursor.js +570 -0
  88. package/dist/usage/providers/cursor.js.map +1 -0
  89. package/dist/usage/providers/index.js +2 -0
  90. package/dist/usage/providers/index.js.map +1 -1
  91. package/package.json +2 -1
@@ -0,0 +1,272 @@
1
+ import { createRequire } from "node:module";
2
+ import path from "node:path";
3
+ import { CODES, diagnostic } from "./diagnostics.js";
4
+ /** Raised when the wall-clock budget expires. Classified as `AP005`. */
5
+ export class DeadlineExceeded extends Error {
6
+ ms;
7
+ constructor(ms) {
8
+ super(`Exceeded the ${ms}ms budget`);
9
+ this.ms = ms;
10
+ this.name = "DeadlineExceeded";
11
+ }
12
+ }
13
+ /** Raised when the install is broken, rather than the document. */
14
+ export class PdfjsUnavailableError extends Error {
15
+ constructor(cause) {
16
+ super(`The PDF parser could not be loaded (${cause}). Reinstall @cairn-tool/cairn; ` +
17
+ "this is an installation problem rather than a problem with the document.");
18
+ this.name = "PdfjsUnavailableError";
19
+ }
20
+ }
21
+ /**
22
+ * Resolves pdfjs's asset directories.
23
+ *
24
+ * `createRequire(import.meta.url)` is the idiom `src/sqlite.ts` already uses.
25
+ * Resolving `package.json` rather than the entry point lands at the package root
26
+ * with no `..` climbing, and works because pdfjs-dist publishes no `exports`
27
+ * map.
28
+ *
29
+ * These are **runtime resolutions into another package's directory, not files
30
+ * cairn packs**. Nothing here belongs in `package.json` `files`; this looks like
31
+ * the `.markdownlintrc` packaging trap and is not one.
32
+ */
33
+ function pdfjsRoot() {
34
+ try {
35
+ return path.dirname(createRequire(import.meta.url).resolve("pdfjs-dist/package.json"));
36
+ }
37
+ catch (error) {
38
+ throw new PdfjsUnavailableError(error.message);
39
+ }
40
+ }
41
+ /**
42
+ * A trailing separator is load-bearing: pdf.js concatenates
43
+ * `cMapUrl + name + ".bcmap"`, so omitting it produces a wrong path, no cmap,
44
+ * and silently missing CJK text — a degradation indistinguishable from success.
45
+ */
46
+ function assetDir(root, name) {
47
+ return `${path.join(root, name)}${path.sep}`;
48
+ }
49
+ let cached;
50
+ /**
51
+ * Imports the **legacy** build.
52
+ *
53
+ * Not a preference. The package-root entry prints
54
+ * "Please use the `legacy` build in Node.js environments" and then throws
55
+ * `hashOriginal.toHex is not a function`: it assumes a V8 with
56
+ * `Uint8Array.prototype.toHex`, which Node 22 and 23 do not have. The legacy
57
+ * build works across cairn's whole `engines` range.
58
+ *
59
+ * The `process.emitWarning` swap is the same guard `src/sqlite.ts` applies to
60
+ * `node:sqlite`: stderr carries the diagnostics stream, and an experimental
61
+ * warning emitted at import time would land in it.
62
+ */
63
+ async function loadPdfjs() {
64
+ if (cached)
65
+ return cached;
66
+ const emit = process.emitWarning;
67
+ try {
68
+ process.emitWarning = () => { };
69
+ cached = await import("pdfjs-dist/legacy/build/pdf.mjs");
70
+ }
71
+ catch (error) {
72
+ throw new PdfjsUnavailableError(error.message);
73
+ }
74
+ finally {
75
+ process.emitWarning = emit;
76
+ }
77
+ return cached;
78
+ }
79
+ /**
80
+ * A pathological document can emit a warning per object; past this the count is
81
+ * kept and the text is dropped.
82
+ */
83
+ const CAPTURE_CAP = 2000;
84
+ /**
85
+ * Opens a document, runs `body`, and tears down — in that order, always.
86
+ *
87
+ * A scoped-resource function rather than an open/close pair so a caller cannot
88
+ * forget the teardown. On 6.3.289 pdf.js uses the main-thread fake worker in
89
+ * Node (no worker thread is spawned, verified), so a leaked handle does not hang
90
+ * the process — but `destroy()` still releases the parsed object graph, and a
91
+ * future version spawning a real worker would make it load-bearing.
92
+ */
93
+ export async function withDocument(data, options, body) {
94
+ const pdfjs = await loadPdfjs();
95
+ const root = pdfjsRoot();
96
+ // pdf.js refuses a Node `Buffer` outright — "Please provide binary data as
97
+ // `Uint8Array`" — even though a Buffer is one. `read.ts` already hands over a
98
+ // plain array it owns, but normalizing here means no caller can get it wrong,
99
+ // and this is the site whose job is to absorb exactly that.
100
+ const bytes = data.constructor === Uint8Array ? data : Uint8Array.from(data);
101
+ const captured = [];
102
+ let dropped = 0;
103
+ const saved = {
104
+ log: console.log,
105
+ warn: console.warn,
106
+ error: console.error,
107
+ info: console.info,
108
+ debug: console.debug,
109
+ };
110
+ const capture = (...args) => {
111
+ if (captured.length < CAPTURE_CAP)
112
+ captured.push(args.map(String).join(" "));
113
+ else
114
+ dropped += 1;
115
+ };
116
+ // Captured rather than silenced. pdf.js routes warn() to console.warn, which
117
+ // is stderr — the stream that carries this toolset's diagnostics and must be
118
+ // empty on a clean run. Capturing also gives `validate` its only signal for a
119
+ // reconstructed xref, a substituted font, or an unsupported filter; setting
120
+ // verbosity to ERRORS would keep stderr clean and take those away.
121
+ console.log = capture;
122
+ console.warn = capture;
123
+ console.error = capture;
124
+ console.info = capture;
125
+ console.debug = capture;
126
+ const deadline = Date.now() + options.timeoutMs;
127
+ const within = async (work) => {
128
+ const remaining = deadline - Date.now();
129
+ if (remaining <= 0)
130
+ throw new DeadlineExceeded(options.timeoutMs);
131
+ let timer;
132
+ try {
133
+ return await Promise.race([
134
+ work(),
135
+ new Promise((_, reject) => {
136
+ // unref'd, or the timer itself keeps the event loop alive and the CLI
137
+ // waits out the whole budget after it has already written its output.
138
+ timer = setTimeout(() => reject(new DeadlineExceeded(options.timeoutMs)), remaining);
139
+ timer.unref();
140
+ }),
141
+ ]);
142
+ }
143
+ finally {
144
+ if (timer)
145
+ clearTimeout(timer);
146
+ }
147
+ };
148
+ let task;
149
+ try {
150
+ let needsPassword = false;
151
+ task = pdfjs.getDocument({
152
+ data: bytes,
153
+ // No `url` and no `docBaseUrl`: absence is the hardening. Supplying either
154
+ // would make pdf.js the thing performing I/O, and the toolset could no
155
+ // longer claim it does not reach the network.
156
+ verbosity: pdfjs.VerbosityLevel.WARNINGS,
157
+ cMapUrl: assetDir(root, "cmaps"),
158
+ cMapPacked: true,
159
+ standardFontDataUrl: assetDir(root, "standard_fonts"),
160
+ iccUrl: assetDir(root, "iccs"),
161
+ wasmUrl: assetDir(root, "wasm"),
162
+ useWasm: false,
163
+ // The field that actually keeps the no-network claim true: it forces the
164
+ // main thread to supply cmap and font bytes from the paths above instead
165
+ // of letting the worker call fetch().
166
+ useWorkerFetch: false,
167
+ // Probing the host's installed fonts would make output machine-dependent.
168
+ useSystemFonts: false,
169
+ disableFontFace: true,
170
+ // Always false, in every command. Enumerating every damaged page is worth
171
+ // more than dying on the first, and `--strict` changes the exit rule
172
+ // rather than the parse.
173
+ stopAtErrors: false,
174
+ maxImageSize: 32 * 1024 * 1024,
175
+ });
176
+ // Never prompts: a CLI that blocks on a TTY read is unusable in a pipeline.
177
+ // There is no --password flag, so this only ever refuses.
178
+ task.onPassword = (reply) => {
179
+ needsPassword = true;
180
+ reply(new Error("password required"));
181
+ };
182
+ const doc = (await within(() => task.promise));
183
+ if (doc.numPages > options.maxPages)
184
+ throw new PageBudgetExceeded(doc.numPages, options.maxPages);
185
+ void needsPassword;
186
+ const permissions = await within(() => doc.getPermissions());
187
+ const handle = {
188
+ doc,
189
+ notices: captured,
190
+ // getPermissions() returns null on an unencrypted document and a Set on an
191
+ // encrypted one, verified against 6.3.289 — so a non-null result here with
192
+ // no password supplied means the document opened on an empty user
193
+ // password, and its restrictions are advisory only.
194
+ encrypted: permissions !== null,
195
+ within,
196
+ };
197
+ return await body(handle);
198
+ }
199
+ finally {
200
+ // destroy() first, restore second. pdf.js fires callbacks during teardown,
201
+ // and restoring the console before the worker is gone lets a late warning
202
+ // escape onto a stream that is carrying a payload.
203
+ if (task) {
204
+ try {
205
+ await task.destroy();
206
+ }
207
+ catch {
208
+ // Teardown failure cannot change the answer already computed.
209
+ }
210
+ }
211
+ Object.assign(console, saved);
212
+ if (dropped > 0)
213
+ captured.push(`(${dropped} further parser messages dropped)`);
214
+ }
215
+ }
216
+ /** Raised before any page is touched, when the document is larger than allowed. */
217
+ export class PageBudgetExceeded extends Error {
218
+ pages;
219
+ limit;
220
+ constructor(pages, limit) {
221
+ super(`Document has ${pages} pages, more than the ${limit} allowed`);
222
+ this.pages = pages;
223
+ this.limit = limit;
224
+ this.name = "PageBudgetExceeded";
225
+ }
226
+ }
227
+ /**
228
+ * Maps a thrown value onto a finding.
229
+ *
230
+ * Matched by class where pdf.js exports one, never by `error.name`: a name is a
231
+ * string that has drifted before, and the classes are exported precisely so a
232
+ * consumer can branch on them.
233
+ *
234
+ * The distinction between `AP002` and `AP100` is worth keeping: `AP002` means
235
+ * the bytes never had a `%PDF-` header, decided by `read.ts` before pdf.js ran,
236
+ * and `AP100` means it looked like a PDF and could not be parsed. That is the
237
+ * difference between "wrong file" and "damaged file".
238
+ */
239
+ export async function classify(error, page) {
240
+ const pdfjs = await loadPdfjs().catch(() => null);
241
+ const message = error instanceof Error ? error.message : String(error);
242
+ const build = (code, text, remediation) => diagnostic({
243
+ code,
244
+ severity: "error",
245
+ quality: "unsupported",
246
+ message: text,
247
+ ...(page !== undefined ? { page } : {}),
248
+ ...(remediation ? { remediation } : {}),
249
+ });
250
+ if (error instanceof DeadlineExceeded)
251
+ return build(CODES.timedOut, message, "Raise the budget with --timeout.");
252
+ if (error instanceof PageBudgetExceeded)
253
+ return build(CODES.tooManyPages, message, "Raise the bound with --max-pages.");
254
+ if (error instanceof PdfjsUnavailableError)
255
+ return build(CODES.failure, message);
256
+ if (pdfjs) {
257
+ if (error instanceof pdfjs.PasswordException) {
258
+ const incorrect = error.code === pdfjs.PasswordResponses?.INCORRECT_PASSWORD;
259
+ return build(incorrect ? CODES.passwordIncorrect : CODES.passwordRequired, incorrect
260
+ ? "The document was encrypted with a password this tool was not given"
261
+ : "The document is encrypted and requires a password to open", "This toolset takes no password. Decrypt the document with another tool first.");
262
+ }
263
+ if (error instanceof pdfjs.InvalidPDFException)
264
+ return build(CODES.unparseable, `The document could not be parsed even after recovery: ${message}`);
265
+ if (pdfjs.ResponseException && error instanceof pdfjs.ResponseException)
266
+ return build(CODES.failure, `The parser attempted a network request, which should be impossible: ${message}`, "Report this: it means a hardening option stopped taking effect.");
267
+ }
268
+ if (page !== undefined)
269
+ return build(CODES.pageUnreadable, `Page ${page}: ${message}`);
270
+ return build(CODES.failure, message);
271
+ }
272
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/pdf/document.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { OpenDocument } from "./document.js";
2
+ import type { PdfDiagnostic, PdfForm } from "./types.js";
3
+ export interface FormResult {
4
+ form: PdfForm;
5
+ diagnostics: PdfDiagnostic[];
6
+ }
7
+ export declare function readForm(handle: OpenDocument): Promise<FormResult>;
8
+ /** The human and llm rendering: one line per field. */
9
+ export declare function formatForm(form: PdfForm): string;
@@ -0,0 +1,101 @@
1
+ import { CODES, DiagnosticSink } from "./diagnostics.js";
2
+ /** Field values arrive as strings, names, or arrays; anything else is dropped. */
3
+ function text(value) {
4
+ if (typeof value === "string")
5
+ return value;
6
+ if (typeof value === "number" || typeof value === "boolean")
7
+ return String(value);
8
+ if (Array.isArray(value)) {
9
+ const parts = value.filter((item) => typeof item === "string");
10
+ return parts.length ? parts.join(", ") : undefined;
11
+ }
12
+ return undefined;
13
+ }
14
+ function fieldFrom(name, widgets, pageCount) {
15
+ // The widgets of one field agree on everything but position, so the first is
16
+ // representative; the count is what the rest contribute.
17
+ const first = widgets[0] ?? {};
18
+ // A field attached to no page reports -1, not undefined — so the range check
19
+ // below is what turns the sentinel into `page: null` instead of page 0.
20
+ const zeroBased = typeof first.page === "number" ? first.page : null;
21
+ const page = zeroBased === null || zeroBased < 0 || zeroBased >= pageCount ? null : zeroBased + 1;
22
+ return {
23
+ name,
24
+ type: typeof first.type === "string" && first.type ? first.type : "unknown",
25
+ page,
26
+ ...(text(first.value) === undefined ? {} : { value: text(first.value) }),
27
+ ...(text(first.defaultValue) === undefined ? {} : { defaultValue: text(first.defaultValue) }),
28
+ readOnly: first.editable === false,
29
+ hidden: first.hidden === true,
30
+ password: first.password === true,
31
+ ...(typeof first.charLimit === "number" && first.charLimit > 0
32
+ ? { charLimit: first.charLimit }
33
+ : {}),
34
+ ...(text(first.exportValues) === undefined ? {} : { exportValues: text(first.exportValues) }),
35
+ widgets: widgets.length,
36
+ };
37
+ }
38
+ export async function readForm(handle) {
39
+ const sink = new DiagnosticSink();
40
+ // Checked before the field lookup, not after: an XFA-only document has no
41
+ // AcroForm field objects at all, so an empty list here would otherwise be
42
+ // indistinguishable from a document carrying no form.
43
+ if (handle.doc.isPureXfa) {
44
+ sink.add({
45
+ code: CODES.formXfa,
46
+ quality: "approximate",
47
+ message: "This is an XFA form; its field values live in an XML packet that is not read",
48
+ remediation: "Open it in a viewer that supports XFA, or use pdf text for whatever static content the document carries.",
49
+ });
50
+ return { form: { type: "xfa", fieldCount: 0, fields: [] }, diagnostics: sink.all() };
51
+ }
52
+ // Null covers both "no /AcroForm" and "an /AcroForm declaring no fields":
53
+ // pdf.js reports `hasFields: false` for each and there is nothing in the API
54
+ // that separates them, so `none` is the only claim this can honestly make.
55
+ const objects = await handle.within(() => handle.doc.getFieldObjects());
56
+ if (!objects || objects.size === 0)
57
+ return { form: { type: "none", fieldCount: 0, fields: [] }, diagnostics: sink.all() };
58
+ const fields = [];
59
+ for (const [name, widgets] of objects) {
60
+ const list = (Array.isArray(widgets) ? widgets : []);
61
+ if (list.length === 0)
62
+ continue;
63
+ const field = fieldFrom(name, list, handle.doc.numPages);
64
+ if (field.page === null)
65
+ sink.add({
66
+ code: CODES.formFieldPageUnresolved,
67
+ quality: "exact",
68
+ construct: name,
69
+ message: `Field '${name}' does not resolve to a page in this document`,
70
+ });
71
+ fields.push(field);
72
+ }
73
+ // Byte comparison, never localeCompare: a differently configured CI runner
74
+ // would otherwise reorder the payload.
75
+ fields.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
76
+ return {
77
+ form: { type: "acroform", fieldCount: fields.length, fields },
78
+ diagnostics: sink.all(),
79
+ };
80
+ }
81
+ /** The human and llm rendering: one line per field. */
82
+ export function formatForm(form) {
83
+ if (form.type === "xfa")
84
+ return "xfa form: field values are not readable\n";
85
+ if (form.fieldCount === 0)
86
+ return "no form fields\n";
87
+ const width = Math.max(...form.fields.map((field) => field.name.length));
88
+ const lines = form.fields.map((field) => {
89
+ const flags = [
90
+ field.readOnly ? "read-only" : "",
91
+ field.hidden ? "hidden" : "",
92
+ field.password ? "password" : "",
93
+ ].filter(Boolean);
94
+ const page = field.page === null ? " -" : String(field.page).padStart(3);
95
+ const value = field.value === undefined ? "" : ` = ${JSON.stringify(field.value)}`;
96
+ const suffix = flags.length ? ` [${flags.join(", ")}]` : "";
97
+ return `${page} ${field.name.padEnd(width)} ${field.type}${value}${suffix}`;
98
+ });
99
+ return `${lines.join("\n")}\n`;
100
+ }
101
+ //# sourceMappingURL=forms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.js","sourceRoot":"","sources":["../../src/pdf/forms.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { OpenDocument, PdfStructNode } from "./document.js";
2
+ import type { PdfDiagnostic, PdfDocumentInfo, PdfPageInfo, TextLayer } from "./types.js";
3
+ export declare function classifyTextLayer(characters: number, density: number): TextLayer;
4
+ /**
5
+ * Converts a PDF `D:YYYYMMDDHHmmSS` date to ISO 8601 in UTC.
6
+ *
7
+ * Omitted rather than guessed when it does not parse, and never rendered in the
8
+ * host timezone: that would make the payload machine-dependent, which is the
9
+ * same reasoning the ADF converter applies to its own timestamps.
10
+ */
11
+ export declare function parsePdfDate(value: unknown): string | undefined;
12
+ /** True when a structure tree carries at least one marked-content leaf. */
13
+ export declare function hasStructContent(node: PdfStructNode | null | undefined): boolean;
14
+ export interface SummaryResult {
15
+ document: PdfDocumentInfo;
16
+ diagnostics: PdfDiagnostic[];
17
+ }
18
+ /**
19
+ * The document facts that cost nothing: no page is opened.
20
+ *
21
+ * Every command carries this, so a caller can read `document.tagged` off a
22
+ * `to-markdown` payload rather than making a second call. `structured` and
23
+ * `textLayer` are absent here because only a command that walks pages can
24
+ * measure them, and a field nobody measured must not read as a measurement.
25
+ */
26
+ export declare function documentSummary(handle: OpenDocument): Promise<SummaryResult>;
27
+ export interface InspectResult {
28
+ document: PdfDocumentInfo;
29
+ pages: PdfPageInfo[];
30
+ diagnostics: PdfDiagnostic[];
31
+ }
32
+ /**
33
+ * The page inventory, on top of {@link documentSummary}.
34
+ *
35
+ * `probeStructure` drives whether each page's structure tree is opened. It is on
36
+ * for `inspect`, because `structured` is one of the two fields the command
37
+ * exists to report, and off elsewhere.
38
+ */
39
+ export declare function inspectDocument(handle: OpenDocument, options?: {
40
+ pages?: number[];
41
+ probeStructure?: boolean;
42
+ }): Promise<InspectResult>;