@dragon708/docmind-browser 1.5.1 → 1.7.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/dist/index.d.ts CHANGED
@@ -1,8 +1,11 @@
1
- import { DocMindAnalyzeOptions, AnalysisResult, FileKind, RuntimeDescriptor, DocMindPublicIntent, AnalysisAnalyzer, ProcessingPlanDescriptor, ExplainAnalysisPlanOptions, GetCapabilitiesOptions } from '@dragon708/docmind-shared';
2
- export { AnalysisAnalyzer, AnalysisResult, CapabilityDescriptor, DetectFileKindInput, DocMindPublicIntent, DocxAnalysisCoreResult, ExplainAnalysisPlanOptions, ExplainAnalysisPlanResult, FileKind, FileKindMetadata, GenericAnalysisResult, GetCapabilitiesOptions, GetCapabilitiesResult, ImageAnalysisCoreResult, PdfAnalysisCoreResult, TextAnalysisResult, detectFileKind } from '@dragon708/docmind-shared';
3
- import { OcrOptions, OcrTiffOptions, PreprocessImageOptions } from '@dragon708/docmind-ocr';
1
+ import { DocMindAnalyzeOptions, AnalyzeFileOutputOptions, NormalizeStructuredOptions, AnalysisResult, StructuredDocumentResult, FileKind, RuntimeDescriptor, DocMindPublicIntent, AnalysisAnalyzer, ProcessingPlanDescriptor, ExplainAnalysisPlanOptions, GetCapabilitiesOptions } from '@dragon708/docmind-shared';
2
+ export { AnalysisAnalyzer, AnalysisResult, CapabilityDescriptor, DetectFileKindInput, DocMindPublicIntent, DocumentBlock, DocumentImageRef, DocumentPage, DocumentTable, DocxAnalysisCoreResult, ExplainAnalysisPlanOptions, ExplainAnalysisPlanResult, FileKind, FileKindMetadata, GenericAnalysisResult, GetCapabilitiesOptions, GetCapabilitiesResult, ImageAnalysisCoreResult, PdfAnalysisCoreResult, StructuredDocumentResult, TextAnalysisResult, analyzeFileRequestsStructured, detectFileKind, isStructuredDocumentResult } from '@dragon708/docmind-shared';
3
+ import { RenderLlmTextOptions, RenderMarkdownOptions, RenderMarkdownSectionsOptions, MarkdownSection } from '@dragon708/docmind-markdown';
4
+ export { MarkdownSection } from '@dragon708/docmind-markdown';
4
5
  import { AnalyzeDocxIncludeFlags, DocxToHtmlOptions } from '@dragon708/docmind-docx';
5
- export { AnalyzeDocxIncludeFlags } from '@dragon708/docmind-docx';
6
+ export { AnalyzeDocxIncludeFlags, ExtractStructuredDataFromDocxOptions, extractStructuredDataFromDocx } from '@dragon708/docmind-docx';
7
+ import { OcrOptions, OcrTiffOptions, PreprocessImageOptions } from '@dragon708/docmind-ocr';
8
+ export { ExtractStructuredDataFromImageOptions, extractStructuredDataFromImage } from '@dragon708/docmind-ocr';
6
9
 
7
10
  /**
8
11
  * Opciones DOCX para el facade browser (Mammoth + inclusiones v2 de `@dragon708/docmind-docx`; sin APIs Node-only).
@@ -30,27 +33,51 @@ interface BrowserOcrOptions extends OcrOptions, Pick<OcrTiffOptions, "maxPages"
30
33
  * Options for public browser methods (`analyzeFile`, intent APIs).
31
34
  * There is no PDF pipeline in the browser; {@link BrowserOcrOptions.mode} applies to images only.
32
35
  */
33
- interface BrowserAnalyzeOptions extends DocMindAnalyzeOptions {
36
+ interface BrowserAnalyzeOptions extends DocMindAnalyzeOptions, AnalyzeFileOutputOptions {
34
37
  /** Image OCR only; no PDF in this runtime. See {@link BrowserOcrOptions.mode}. */
35
38
  readonly ocr?: BrowserOcrOptions;
36
39
  /** Solo DOCX: ver {@link BrowserAnalyzeDocxOptionsSlice}. */
37
40
  readonly docx?: BrowserAnalyzeDocxOptionsSlice;
38
41
  }
42
+ /** Options for {@link extractStructuredData}: same as {@link BrowserAnalyzeOptions} plus shared normalize knobs. */
43
+ type BrowserExtractStructuredDataOptions = BrowserAnalyzeOptions & {
44
+ readonly normalize?: NormalizeStructuredOptions;
45
+ };
46
+ /**
47
+ * {@link extractMarkdown}: structured options plus `markdown` for `renderMarkdown` (`@dragon708/docmind-markdown`).
48
+ */
49
+ interface BrowserExtractMarkdownOptions extends BrowserExtractStructuredDataOptions {
50
+ readonly markdown?: RenderMarkdownOptions;
51
+ }
52
+ /** {@link extractLlmContent}: optional `llm` passed to `renderLlmText`. */
53
+ interface BrowserExtractLlmContentOptions extends BrowserExtractStructuredDataOptions {
54
+ readonly llm?: RenderLlmTextOptions;
55
+ }
56
+ /** {@link extractStructuredChunks}: optional `chunks` (split / section sizing). */
57
+ interface BrowserExtractStructuredChunksOptions extends BrowserExtractStructuredDataOptions {
58
+ readonly chunks?: RenderMarkdownSectionsOptions;
59
+ }
39
60
 
40
61
  /**
41
62
  * Inputs supported by the browser entry (DOM types only — no `fs`, no Node `Buffer` in the public surface).
42
63
  */
43
64
  type BrowserAnalyzeInput = File | Blob | ArrayBuffer;
44
65
 
66
+ /** PDF is not processed in the browser; use `@dragon708/docmind-node` on the server. */
67
+ declare const BROWSER_PDF_UNSUPPORTED_WARNING = "PDF text extraction is not available in the browser runtime; use @dragon708/docmind-node on the server.";
68
+ /** PDF structured extraction is not available in the browser; use the Node PDF helpers. */
69
+ declare const BROWSER_PDF_STRUCTURED_UNSUPPORTED_WARNING = "PDF text extraction is not available in the browser runtime; use @dragon708/docmind-node on the server. Structured document extraction for PDF is not available in the browser; use @dragon708/docmind-node (e.g. extractStructuredDataFromPdf).";
70
+
45
71
  /**
46
72
  * Browser `analyzeFile` router. Package-level scope and limitations are documented on the package entry (`index.ts`).
47
73
  */
48
74
 
49
- /** PDF is not processed in the browser; use `@dragon708/docmind-node` on the server. */
50
- declare const BROWSER_PDF_UNSUPPORTED_WARNING = "PDF text extraction is not available in the browser runtime; use @dragon708/docmind-node on the server.";
51
75
  /**
52
76
  * Full-document router: DOCX (text + HTML), images (OCR subject to {@link BrowserOcrOptions.mode}),
53
77
  * plain text (UTF-8 decode). PDF yields `not_implemented` with {@link BROWSER_PDF_UNSUPPORTED_WARNING}.
78
+ *
79
+ * When `structuredOutput` is true or `output` includes `"structured"`, successful results may include
80
+ * optional `structured` (see `@dragon708/docmind-shared` DocMindV2Extensions).
54
81
  */
55
82
  declare function analyzeFile(input: BrowserAnalyzeInput, options?: BrowserAnalyzeOptions): Promise<AnalysisResult>;
56
83
 
@@ -77,8 +104,40 @@ declare function convertToHtml(input: BrowserAnalyzeInput, options?: BrowserAnal
77
104
  */
78
105
  declare function runOcr(input: BrowserAnalyzeInput, options?: BrowserAnalyzeOptions): Promise<AnalysisResult>;
79
106
 
107
+ /**
108
+ * Browser-safe structured extraction: DOCX and images delegate to `@dragon708/docmind-docx` /
109
+ * `@dragon708/docmind-ocr`; plain text is normalized via `@dragon708/docmind-shared`.
110
+ * PDF is not supported in this runtime (clear warnings, no PDF package import).
111
+ */
112
+
113
+ /**
114
+ * Returns a {@link StructuredDocumentResult} for inputs the browser runtime actually supports:
115
+ * **DOCX** (`extractStructuredDataFromDocx`), **images** (`extractStructuredDataFromImage` when OCR is not off),
116
+ * **plain text** (UTF-8 decode + `normalizeToStructuredResult`).
117
+ *
118
+ * **PDF** yields an empty structured envelope plus `BROWSER_PDF_STRUCTURED_UNSUPPORTED_WARNING` from the facade (no PDF parser in this runtime).
119
+ */
120
+ declare function extractStructuredData(input: BrowserAnalyzeInput, options?: BrowserExtractStructuredDataOptions): Promise<StructuredDocumentResult>;
121
+
122
+ /**
123
+ * {@link extractStructuredData} → `renderMarkdown` (browser-safe; no Node-only APIs).
124
+ * PDF: empty structured stub → usually empty output; see {@link getCapabilities} capability `markdown`.
125
+ *
126
+ * @param input - `File` / `Blob` / bytes as accepted by the browser facade.
127
+ * @param options - `markdown` render options plus structured/`ocr`/`docx` routing (same as {@link extractStructuredData}).
128
+ */
129
+ declare function extractMarkdown(input: BrowserAnalyzeInput, options?: BrowserExtractMarkdownOptions): Promise<string>;
130
+ /**
131
+ * Same as {@link extractMarkdown} but `renderLlmText` (compact plain text for LLM prompts).
132
+ */
133
+ declare function extractLlmContent(input: BrowserAnalyzeInput, options?: BrowserExtractLlmContentOptions): Promise<string>;
134
+ /**
135
+ * Structured extract → `renderMarkdownSections` for sectioned Markdown in the browser.
136
+ */
137
+ declare function extractStructuredChunks(input: BrowserAnalyzeInput, options?: BrowserExtractStructuredChunksOptions): Promise<readonly MarkdownSection[]>;
138
+
80
139
  /** High-level features the user can ask DocMind for (per input kind and runtime). */
81
- type PublicCapabilityId = "text" | "metadata" | "html" | "ocr" | "pages" | "image-normalization" | "gif-first-frame" | "bmp" | "heic" | "tiff";
140
+ type PublicCapabilityId = "text" | "metadata" | "html" | "ocr" | "pages" | "structured-output" | "markdown" | "llm-text" | "structured-chunks" | "image-normalization" | "gif-first-frame" | "bmp" | "heic" | "tiff";
82
141
  declare function docxIncludeRequested(flags?: AnalyzeDocxIncludeFlags): boolean;
83
142
  /** DOCX `word/media` en runtime browser (JSZip; sin pipeline Node). */
84
143
  interface DocxEmbeddedImageCapabilities {
@@ -150,19 +209,24 @@ interface ExplainAnalysisPlanReport {
150
209
  readonly warnings?: readonly string[];
151
210
  }
152
211
 
212
+ /** Browser facade intents that run `@dragon708/docmind-markdown` after structured extraction. */
213
+ type BrowserMarkdownFacadeIntent = "extractMarkdown" | "extractLlmContent" | "extractStructuredChunks";
153
214
  /** Options for {@link explainAnalysisPlan}: shared fields plus optional `ocr` / `docx` for accurate step preview. */
154
- type BrowserExplainAnalysisPlanOptions = ExplainAnalysisPlanOptions & Pick<BrowserAnalyzeOptions, "ocr" | "docx">;
215
+ type BrowserExplainAnalysisPlanOptions = Omit<ExplainAnalysisPlanOptions, "intent"> & Pick<BrowserAnalyzeOptions, "ocr" | "docx" | "structuredOutput" | "output"> & {
216
+ readonly intent?: DocMindPublicIntent | BrowserMarkdownFacadeIntent;
217
+ };
155
218
 
156
219
  /**
157
220
  * Epic 1 — **Capabilities:** detects kind from the same hints as `analyzeFile`, then lists which of
158
- * `text` | `metadata` | `html` | `ocr` | `pages` and image-specific ids (`image-normalization`, `bmp`, `gif-first-frame`, `heic`, `tiff`) apply in the browser (PDF always unsupported).
221
+ * `text` | `metadata` | `html` | `ocr` | `pages` | `structured-output` | `markdown` | `llm-text` | `structured-chunks`
222
+ * and image-specific ids (`image-normalization`, `bmp`, `gif-first-frame`, `heic`, `tiff`) apply in the browser (PDF always unsupported for meaningful content).
159
223
  * No Mammoth/Tesseract/PDF parsing. For DOCX, {@link GetCapabilitiesReport.docxStructure} / `docxEmbeddedImages` describe v2 opt-in features.
160
224
  */
161
225
  declare function getCapabilities(input: BrowserAnalyzeInput, options?: GetCapabilitiesOptions): Promise<GetCapabilitiesReport>;
162
226
  /**
163
227
  * Epic 1 — **Plan preview:** structured explanation (analyzer, native extraction vs OCR, `limitations`, `plan.steps`)
164
- * for a {@link DocMindPublicIntent}. Optional `ocr` refines image steps; optional `docx.include` adds planned OOXML parallel steps for DOCX. No heavy I/O.
228
+ * for a {@link DocMindPublicIntent} (including `extractStructuredData`). Optional `ocr` refines image steps; optional `docx.include` adds planned OOXML parallel steps for DOCX. No heavy I/O.
165
229
  */
166
230
  declare function explainAnalysisPlan(input: BrowserAnalyzeInput, options?: BrowserExplainAnalysisPlanOptions): Promise<ExplainAnalysisPlanReport>;
167
231
 
168
- export { BROWSER_PDF_UNSUPPORTED_WARNING, type BrowserAnalyzeDocxOptionsSlice, type BrowserAnalyzeInput, type BrowserAnalyzeOptions, type BrowserExplainAnalysisPlanOptions, type BrowserOcrMode, type BrowserOcrOptions, DOCX_EMBEDDED_IMAGE_CAPABILITIES_BROWSER, DOCX_STRUCTURE_CAPABILITIES_BROWSER, type DocxEmbeddedImageCapabilities, type DocxStructuralCapabilities, type ExplainAnalysisPlanReport, type GetCapabilitiesReport, type NativeExtractionPlan, type OcrPlan, type PublicCapabilityId, type PublicCapabilitySupport, analyzeFile, convertToHtml, docxIncludeRequested, explainAnalysisPlan, extractMetadata, extractText, getCapabilities, runOcr };
232
+ export { BROWSER_PDF_STRUCTURED_UNSUPPORTED_WARNING, BROWSER_PDF_UNSUPPORTED_WARNING, type BrowserAnalyzeDocxOptionsSlice, type BrowserAnalyzeInput, type BrowserAnalyzeOptions, type BrowserExplainAnalysisPlanOptions, type BrowserExtractLlmContentOptions, type BrowserExtractMarkdownOptions, type BrowserExtractStructuredChunksOptions, type BrowserExtractStructuredDataOptions, type BrowserMarkdownFacadeIntent, type BrowserOcrMode, type BrowserOcrOptions, DOCX_EMBEDDED_IMAGE_CAPABILITIES_BROWSER, DOCX_STRUCTURE_CAPABILITIES_BROWSER, type DocxEmbeddedImageCapabilities, type DocxStructuralCapabilities, type ExplainAnalysisPlanReport, type GetCapabilitiesReport, type NativeExtractionPlan, type OcrPlan, type PublicCapabilityId, type PublicCapabilitySupport, analyzeFile, convertToHtml, docxIncludeRequested, explainAnalysisPlan, extractLlmContent, extractMarkdown, extractMetadata, extractStructuredChunks, extractStructuredData, extractText, getCapabilities, runOcr };
package/dist/index.js CHANGED
@@ -1,7 +1,10 @@
1
- import { assertValidAnalyzeFileInput, detectFileKind, notImplementedResult, UNKNOWN_FORMAT_WARNING, analyzeText, toUint8Array, InvalidInputError, getMimeType } from '@dragon708/docmind-shared';
2
- export { detectFileKind } from '@dragon708/docmind-shared';
3
- import { analyzeDocx } from '@dragon708/docmind-docx';
4
- import { preprocessHasEffect, resolveImageFormat, normalizeImageForOcr, ocrTiff, ocrImageDetailed } from '@dragon708/docmind-ocr';
1
+ import { detectFileKind, normalizeToStructuredResult, UNKNOWN_FORMAT_WARNING, analyzeText, toUint8Array, assertValidAnalyzeFileInput, notImplementedResult, analyzeFileRequestsStructured, InvalidInputError, getMimeType } from '@dragon708/docmind-shared';
2
+ export { analyzeFileRequestsStructured, detectFileKind, isStructuredDocumentResult } from '@dragon708/docmind-shared';
3
+ import { extractStructuredDataFromDocx, analyzeDocx } from '@dragon708/docmind-docx';
4
+ export { extractStructuredDataFromDocx } from '@dragon708/docmind-docx';
5
+ import { extractStructuredDataFromImage, preprocessHasEffect, resolveImageFormat, normalizeImageForOcr, ocrTiff, ocrImageDetailed } from '@dragon708/docmind-ocr';
6
+ export { extractStructuredDataFromImage } from '@dragon708/docmind-ocr';
7
+ import { renderMarkdown, renderLlmText, renderMarkdownSections } from '@dragon708/docmind-markdown';
5
8
 
6
9
  // src/analyzeFile.ts
7
10
  function assertBrowserInput(input) {
@@ -223,8 +226,125 @@ async function analyzeImageForBrowser(input, options) {
223
226
  return base;
224
227
  }
225
228
 
226
- // src/analyzeFile.ts
229
+ // src/browserPdfWarnings.ts
227
230
  var BROWSER_PDF_UNSUPPORTED_WARNING = "PDF text extraction is not available in the browser runtime; use @dragon708/docmind-node on the server.";
231
+ var BROWSER_PDF_STRUCTURED_UNSUPPORTED_WARNING = `${BROWSER_PDF_UNSUPPORTED_WARNING} Structured document extraction for PDF is not available in the browser; use @dragon708/docmind-node (e.g. extractStructuredDataFromPdf).`;
232
+ var OCR_OFF_STRUCTURED_WARNING = 'Structured image extraction uses OCR; ocr.mode is "off". Use "auto" or "force" to run Tesseract.';
233
+ function resolveOcrMode2(ocr) {
234
+ return ocr?.mode ?? "auto";
235
+ }
236
+ async function extractStructuredData(input, options) {
237
+ throwIfAborted(options?.signal);
238
+ prepareBrowserAnalyzeInput(input);
239
+ const kind = detectFileKind(input);
240
+ const bytesInput = input;
241
+ const norm = options?.normalize;
242
+ switch (kind) {
243
+ case "pdf":
244
+ return normalizeToStructuredResult(
245
+ {
246
+ kind: "pdf",
247
+ text: "",
248
+ blocks: [],
249
+ warnings: [BROWSER_PDF_STRUCTURED_UNSUPPORTED_WARNING],
250
+ metadata: {
251
+ extra: { browserStructuredPdfUnsupported: true }
252
+ }
253
+ },
254
+ norm
255
+ );
256
+ case "docx": {
257
+ const data = await toUint8Array(bytesInput);
258
+ if (data.byteLength === 0) {
259
+ return normalizeToStructuredResult(
260
+ {
261
+ kind: "docx",
262
+ text: "",
263
+ blocks: [],
264
+ warnings: ["No document bytes were provided for analysis."]
265
+ },
266
+ norm
267
+ );
268
+ }
269
+ const docxOpts = analyzeDocxOptionsFromBrowser(options);
270
+ return extractStructuredDataFromDocx(data, {
271
+ ...docxOpts ?? {},
272
+ normalize: norm
273
+ });
274
+ }
275
+ case "image": {
276
+ const data = await toUint8Array(bytesInput);
277
+ if (data.byteLength === 0) {
278
+ return normalizeToStructuredResult(
279
+ {
280
+ kind: "image",
281
+ text: "",
282
+ blocks: [],
283
+ tables: [],
284
+ pages: [],
285
+ images: [],
286
+ warnings: ["No image bytes were provided for analysis."]
287
+ },
288
+ norm
289
+ );
290
+ }
291
+ if (resolveOcrMode2(options?.ocr) === "off") {
292
+ return normalizeToStructuredResult(
293
+ {
294
+ kind: "image",
295
+ text: "",
296
+ blocks: [],
297
+ tables: [],
298
+ pages: [],
299
+ images: [],
300
+ warnings: [OCR_OFF_STRUCTURED_WARNING],
301
+ metadata: {
302
+ extra: { ocrSkipped: true }
303
+ }
304
+ },
305
+ norm
306
+ );
307
+ }
308
+ const ocr = options?.ocr;
309
+ return extractStructuredDataFromImage(input, {
310
+ signal: options?.signal,
311
+ normalize: norm,
312
+ langs: ocr?.langs,
313
+ preprocess: ocr?.preprocess,
314
+ maxPages: ocr?.maxPages,
315
+ pageSeparator: ocr?.pageSeparator
316
+ });
317
+ }
318
+ case "text": {
319
+ const textResult = await analyzeText(bytesInput, { signal: options?.signal });
320
+ const blocks = textResult.text.length > 0 ? [{ type: "paragraph", id: "p-0", text: textResult.text }] : [];
321
+ return normalizeToStructuredResult(
322
+ {
323
+ kind: "text",
324
+ text: textResult.text,
325
+ blocks,
326
+ warnings: [...textResult.warnings],
327
+ metadata: {
328
+ extra: { plainTextUtf8: true }
329
+ }
330
+ },
331
+ norm
332
+ );
333
+ }
334
+ default:
335
+ return normalizeToStructuredResult(
336
+ {
337
+ kind: "unknown",
338
+ text: "",
339
+ blocks: [],
340
+ warnings: [UNKNOWN_FORMAT_WARNING]
341
+ },
342
+ norm
343
+ );
344
+ }
345
+ }
346
+
347
+ // src/analyzeFile.ts
228
348
  async function analyzeFile(input, options) {
229
349
  if (options?.signal?.aborted) {
230
350
  const err = new Error("The operation was aborted");
@@ -235,17 +355,38 @@ async function analyzeFile(input, options) {
235
355
  assertValidAnalyzeFileInput(input);
236
356
  const fileKind = detectFileKind(input);
237
357
  const bytesInput = input;
358
+ let result;
238
359
  switch (fileKind) {
239
360
  case "pdf":
240
- return notImplementedResult("pdf", "pdf", [BROWSER_PDF_UNSUPPORTED_WARNING]);
361
+ result = notImplementedResult("pdf", "pdf", [BROWSER_PDF_UNSUPPORTED_WARNING]);
362
+ break;
241
363
  case "docx":
242
- return analyzeDocxForBrowser(bytesInput, options);
364
+ result = await analyzeDocxForBrowser(bytesInput, options);
365
+ break;
243
366
  case "image":
244
- return analyzeImageForBrowser(bytesInput, options);
367
+ result = await analyzeImageForBrowser(bytesInput, options);
368
+ break;
245
369
  case "text":
246
- return analyzeText(bytesInput, { signal: options?.signal });
370
+ result = await analyzeText(bytesInput, { signal: options?.signal });
371
+ break;
247
372
  default:
248
- return notImplementedResult(fileKind, "none", [UNKNOWN_FORMAT_WARNING]);
373
+ result = notImplementedResult(fileKind, "none", [UNKNOWN_FORMAT_WARNING]);
374
+ }
375
+ if (!analyzeFileRequestsStructured(options) || result.status !== "ok") {
376
+ return result;
377
+ }
378
+ try {
379
+ const structured = await extractStructuredData(input, {
380
+ signal: options?.signal,
381
+ ocr: options?.ocr,
382
+ docx: options?.docx
383
+ });
384
+ return { ...result, structured };
385
+ } catch (e) {
386
+ if (e instanceof Error && e.name === "AbortError") throw e;
387
+ const msg = e instanceof Error ? e.message : String(e);
388
+ const prev = "warnings" in result && Array.isArray(result.warnings) ? [...result.warnings] : [];
389
+ return { ...result, warnings: [...prev, `warning: analyzeFile structured merge failed: ${msg}`] };
249
390
  }
250
391
  }
251
392
  var DOCX_METADATA_STUB = "Structured document metadata for DOCX is not returned by extractMetadata; use analyzeFile, extractText, or convertToHtml with options.docx.include for OOXML structure, headings, tables, blocks, approximate pages, and embedded images.";
@@ -379,6 +520,24 @@ async function runOcr(input, options) {
379
520
  return notImplementedResult(kind, "none", [UNKNOWN_FORMAT_WARNING]);
380
521
  }
381
522
  }
523
+ async function extractMarkdown(input, options) {
524
+ throwIfAborted(options?.signal);
525
+ const { markdown: markdownOpts, ...structuredOpts } = options ?? {};
526
+ const structured = await extractStructuredData(input, structuredOpts);
527
+ return renderMarkdown(structured, markdownOpts);
528
+ }
529
+ async function extractLlmContent(input, options) {
530
+ throwIfAborted(options?.signal);
531
+ const { llm: llmOpts, ...structuredOpts } = options ?? {};
532
+ const structured = await extractStructuredData(input, structuredOpts);
533
+ return renderLlmText(structured, llmOpts);
534
+ }
535
+ async function extractStructuredChunks(input, options) {
536
+ throwIfAborted(options?.signal);
537
+ const { chunks: chunkOpts, ...structuredOpts } = options ?? {};
538
+ const structured = await extractStructuredData(input, structuredOpts);
539
+ return renderMarkdownSections(structured, chunkOpts);
540
+ }
382
541
 
383
542
  // src/capabilityReport.ts
384
543
  function docxIncludeRequested(flags) {
@@ -415,7 +574,10 @@ var IMAGE_META = "Raster images have no document metadata bundle; extractMetadat
415
574
  var IMAGE_HTML = "No layout HTML for raster images; use extractText or runOcr for text.";
416
575
  var TEXT_META_NOTE = "Plain text has no structured document metadata; extractMetadata still returns decoded content.";
417
576
  var OCR_OFF_NOTE = 'Image OCR may be skipped when `ocr.mode` is "off" in analyze options.';
577
+ var STRUCTURED_OCR_OFF = 'Structured image output uses OCR; when `ocr.mode` is "off", `extractStructuredData` returns an empty envelope with a warning.';
418
578
  var UNKNOWN_KIND = "Could not determine file kind from name, MIME, or bytes; all features are reported as unsupported until the kind is known.";
579
+ var MARKDOWN_PDF_BROWSER = "PDF: no parser in-browser \u2014 extractMarkdown / extractLlmContent / extractStructuredChunks only render an empty structured stub; use @dragon708/docmind-node for real PDF \u2192 Markdown / LLM text / chunks.";
580
+ var MARKDOWN_IMAGE_OCR_OFF = 'Same as structured-output: when ocr.mode is "off", structured (and thus Markdown/LLM/chunk exports) are empty aside from warnings.';
419
581
  function slot(id, supported, warnings) {
420
582
  return warnings?.length ? { id, supported, warnings } : { id, supported };
421
583
  }
@@ -431,7 +593,11 @@ function buildBrowserCapabilityReport(kind) {
431
593
  slot("metadata", false, [pdf]),
432
594
  slot("html", false, [pdf]),
433
595
  slot("ocr", false, [pdf]),
434
- slot("pages", false, [pdf])
596
+ slot("pages", false, [pdf]),
597
+ slot("structured-output", false, [BROWSER_PDF_STRUCTURED_UNSUPPORTED_WARNING]),
598
+ slot("markdown", false, [MARKDOWN_PDF_BROWSER]),
599
+ slot("llm-text", false, [MARKDOWN_PDF_BROWSER]),
600
+ slot("structured-chunks", false, [MARKDOWN_PDF_BROWSER])
435
601
  ];
436
602
  break;
437
603
  case "docx":
@@ -448,6 +614,18 @@ function buildBrowserCapabilityReport(kind) {
448
614
  slot("ocr", false, ["OCR does not apply to DOCX in DocMind."]),
449
615
  slot("pages", false, [
450
616
  "No PDF page count; approximate DOCX pages via options.docx.include.pagesApprox (heuristic, not print layout)."
617
+ ]),
618
+ slot("structured-output", true, [
619
+ "`extractStructuredData` uses `@dragon708/docmind-docx` (Mammoth + OOXML) and returns `StructuredDocumentResult`; optional `options.docx` slices are forwarded."
620
+ ]),
621
+ slot("markdown", true, [
622
+ "extractMarkdown: structured DOCX \u2192 Markdown via `@dragon708/docmind-markdown` (browser-safe, no Node APIs)."
623
+ ]),
624
+ slot("llm-text", true, [
625
+ "extractLlmContent: structured \u2192 compact plain text for prompts (same pipeline as Markdown export)."
626
+ ]),
627
+ slot("structured-chunks", true, [
628
+ "extractStructuredChunks: structured \u2192 Markdown sections (`splitStructuredIntoChunks` in markdown package)."
451
629
  ])
452
630
  ];
453
631
  break;
@@ -479,6 +657,23 @@ function buildBrowserCapabilityReport(kind) {
479
657
  ]),
480
658
  slot("pages", true, [
481
659
  "Multipage TIFF may populate `pages` and `textByPage` when OCR succeeds; other formats may expose `pages` when normalization reports it."
660
+ ]),
661
+ slot("structured-output", true, [
662
+ "`extractStructuredData` uses `extractStructuredDataFromImage` (same OCR path as analyzeFile when `ocr.mode` is not off).",
663
+ STRUCTURED_OCR_OFF,
664
+ "HEIC/HEIF and TIFF limitations match `getCapabilities` (`heic`, `tiff`) and OCR warnings."
665
+ ]),
666
+ slot("markdown", true, [
667
+ "extractMarkdown: OCR structured layout \u2192 Markdown when OCR runs; HEIC unsupported; TIFF best-effort.",
668
+ MARKDOWN_IMAGE_OCR_OFF
669
+ ]),
670
+ slot("llm-text", true, [
671
+ "extractLlmContent: OCR structured \u2192 LLM plain text under the same OCR and format limits.",
672
+ MARKDOWN_IMAGE_OCR_OFF
673
+ ]),
674
+ slot("structured-chunks", true, [
675
+ "extractStructuredChunks: OCR structured \u2192 sectioned Markdown; empty when OCR is off or HEIC.",
676
+ MARKDOWN_IMAGE_OCR_OFF
482
677
  ])
483
678
  ];
484
679
  break;
@@ -488,7 +683,17 @@ function buildBrowserCapabilityReport(kind) {
488
683
  slot("metadata", true, [TEXT_META_NOTE]),
489
684
  slot("html", true),
490
685
  slot("ocr", false, ["OCR does not apply to plain text files."]),
491
- slot("pages", false)
686
+ slot("pages", false),
687
+ slot("structured-output", true, [
688
+ "`extractStructuredData` decodes UTF-8 (via `analyzeText`) and normalizes to `StructuredDocumentResult` (paragraph block rollup)."
689
+ ]),
690
+ slot("markdown", true, [
691
+ "extractMarkdown: UTF-8 structured rollup \u2192 Markdown (`@dragon708/docmind-markdown`)."
692
+ ]),
693
+ slot("llm-text", true, ["extractLlmContent: UTF-8 structured \u2192 LLM-oriented plain text."]),
694
+ slot("structured-chunks", true, [
695
+ "extractStructuredChunks: typically a single Markdown section when only paragraph rollup exists."
696
+ ])
492
697
  ];
493
698
  break;
494
699
  default:
@@ -498,7 +703,11 @@ function buildBrowserCapabilityReport(kind) {
498
703
  slot("metadata", false),
499
704
  slot("html", false),
500
705
  slot("ocr", false),
501
- slot("pages", false)
706
+ slot("pages", false),
707
+ slot("structured-output", false, [UNKNOWN_KIND]),
708
+ slot("markdown", false, [UNKNOWN_KIND]),
709
+ slot("llm-text", false, [UNKNOWN_KIND]),
710
+ slot("structured-chunks", false, [UNKNOWN_KIND])
502
711
  ];
503
712
  }
504
713
  return {
@@ -541,7 +750,11 @@ function buildBrowserExplainReport(kind, intent, ocrMode, plan, docxInclude, ocr
541
750
  let limitations = [];
542
751
  const ocrOffNote = ocrMode === "off" ? 'Image OCR is skipped when ocr.mode is "off".' : "";
543
752
  if (kind === "pdf") {
544
- limitations = lim(BROWSER_PDF_UNSUPPORTED_WARNING);
753
+ const structuredLikeIntent = intent === "extractStructuredData" || intent === "extractMarkdown" || intent === "extractLlmContent" || intent === "extractStructuredChunks";
754
+ limitations = lim(
755
+ BROWSER_PDF_UNSUPPORTED_WARNING,
756
+ structuredLikeIntent ? "`extractStructuredData` / extractMarkdown / extractLlmContent / extractStructuredChunks only see an empty structured envelope in-browser for PDF; use @dragon708/docmind-node for real PDF extraction and Markdown/LLM/chunk exports." : ""
757
+ );
545
758
  nativeExtraction = {
546
759
  willAttempt: false,
547
760
  description: "PDF is not processed in the browser runtime; use @dragon708/docmind-node."
@@ -564,8 +777,10 @@ function buildBrowserExplainReport(kind, intent, ocrMode, plan, docxInclude, ocr
564
777
  });
565
778
  }
566
779
  if (kind === "unknown") {
780
+ const structuredLikeIntent = intent === "extractStructuredData" || intent === "extractMarkdown" || intent === "extractLlmContent" || intent === "extractStructuredChunks";
567
781
  limitations = lim(
568
- "Could not classify the file from name, MIME, or bytes; analysis will return not_implemented until hints improve."
782
+ "Could not classify the file from name, MIME, or bytes; analysis will return not_implemented until hints improve.",
783
+ structuredLikeIntent ? "Structured and Markdown/LLM/chunk exports need a known kind (text, DOCX, or image) in the browser runtime." : ""
569
784
  );
570
785
  nativeExtraction = { willAttempt: false, description: "No analyzer selected without a known file kind." };
571
786
  ocr = { mayUse: false, description: "OCR is not used for unknown kinds." };
@@ -677,6 +892,51 @@ function buildBrowserExplainReport(kind, intent, ocrMode, plan, docxInclude, ocr
677
892
  ocr = { mayUse: false, description: "OCR does not apply to text files." };
678
893
  }
679
894
  break;
895
+ case "extractStructuredData":
896
+ case "extractMarkdown":
897
+ case "extractLlmContent":
898
+ case "extractStructuredChunks":
899
+ if (kind === "docx") {
900
+ nativeExtraction = {
901
+ willAttempt: true,
902
+ description: intent === "extractStructuredData" ? "`extractStructuredDataFromDocx`: Mammoth plus required OOXML extractors (structure, headings, tables, blocks, pagesApprox, embeddedImages unless disabled), then `normalizeToStructuredResult`. Optional `options.docx` is forwarded." : `${String(intent)}: same structured DOCX pipeline as extractStructuredData, then \`@dragon708/docmind-markdown\` (browser-safe).`
903
+ };
904
+ ocr = { mayUse: false, description: "DOCX structured path does not use OCR." };
905
+ limitations = lim(DOCX_ZIP_NOTE_BROWSER);
906
+ } else if (kind === "image") {
907
+ nativeExtraction = {
908
+ willAttempt: false,
909
+ description: BROWSER_IMAGE_PIPELINE
910
+ };
911
+ ocr = {
912
+ mayUse: imageOcrActive,
913
+ description: imageOcrActive ? "`extractStructuredDataFromImage` mirrors the OCR pipeline (normalize \u2192 optional preprocess \u2192 Tesseract, or `ocrTiff` for TIFF)." : "OCR skipped while ocr.mode is off; structured output will be empty with a warning."
914
+ };
915
+ limitations = lim(ocrOffNote, BROWSER_TIFF_RASTER_NOTE, BROWSER_HEIC_NOTE);
916
+ } else {
917
+ nativeExtraction = {
918
+ willAttempt: true,
919
+ description: intent === "extractStructuredData" ? "UTF-8 decode via `analyzeText`, then `normalizeToStructuredResult` with a paragraph block rollup." : `${String(intent)}: UTF-8 structured envelope, then \`@dragon708/docmind-markdown\`.`
920
+ };
921
+ ocr = { mayUse: false, description: "OCR does not apply to text files." };
922
+ }
923
+ if (intent === "extractMarkdown") {
924
+ limitations = [
925
+ ...limitations,
926
+ ...lim("Output: Markdown string via renderMarkdown.")
927
+ ];
928
+ } else if (intent === "extractLlmContent") {
929
+ limitations = [
930
+ ...limitations,
931
+ ...lim("Output: compact plain text via renderLlmText.")
932
+ ];
933
+ } else if (intent === "extractStructuredChunks") {
934
+ limitations = [
935
+ ...limitations,
936
+ ...lim("Output: MarkdownSection[] via renderMarkdownSections.")
937
+ ];
938
+ }
939
+ break;
680
940
  default:
681
941
  nativeExtraction = { willAttempt: false, description: "Intent not specialized in this runtime." };
682
942
  ocr = { mayUse: false, description: "See plan steps." };
@@ -701,7 +961,7 @@ function buildBrowserExplainReport(kind, intent, ocrMode, plan, docxInclude, ocr
701
961
  }
702
962
 
703
963
  // src/introspection.ts
704
- function resolveOcrMode2(ocr) {
964
+ function resolveOcrMode3(ocr) {
705
965
  return ocr?.mode ?? "auto";
706
966
  }
707
967
  function imageBrowserPlanSteps(ocrMode, ocr) {
@@ -766,9 +1026,93 @@ function planForAnalyzeFile(kind, ocrMode, docxInclude, ocr) {
766
1026
  };
767
1027
  }
768
1028
  }
769
- function planForIntent(intentOpt, kind, ocrMode, docxInclude, ocr) {
1029
+ function planForIntent(intentOpt, kind, ocrMode, docxInclude, ocr, analyzeFileOutput) {
770
1030
  const intent = intentOpt ?? "analyzeFile";
771
- if (intent === "analyzeFile") return planForAnalyzeFile(kind, ocrMode, docxInclude, ocr);
1031
+ if (intent === "extractStructuredData") {
1032
+ switch (kind) {
1033
+ case "pdf":
1034
+ return {
1035
+ intent: "extractStructuredData",
1036
+ steps: [
1037
+ { id: "detect_kind", status: "done" },
1038
+ { id: "pdf_pipeline", status: "skipped" },
1039
+ { id: "structured_output", status: "skipped" }
1040
+ ]
1041
+ };
1042
+ case "docx":
1043
+ return {
1044
+ intent: "extractStructuredData",
1045
+ steps: [
1046
+ { id: "detect_kind", status: "done" },
1047
+ { id: "docx_mammoth", status: "planned" },
1048
+ { id: "docx_ooxml_parallel", status: "planned" },
1049
+ { id: "structured_normalize", status: "planned" }
1050
+ ]
1051
+ };
1052
+ case "image": {
1053
+ if (ocrMode === "off") {
1054
+ return {
1055
+ intent: "extractStructuredData",
1056
+ steps: [
1057
+ { id: "detect_kind", status: "done" },
1058
+ { id: "image_format_detect", status: "skipped" },
1059
+ { id: "normalize_image_for_ocr", status: "skipped" },
1060
+ { id: "preprocess_image_for_ocr", status: "skipped" },
1061
+ { id: "tesseract_ocr", status: "skipped" },
1062
+ { id: "structured_normalize", status: "skipped" }
1063
+ ]
1064
+ };
1065
+ }
1066
+ const imgSteps = imageBrowserPlanSteps(ocrMode, ocr);
1067
+ return {
1068
+ intent: "extractStructuredData",
1069
+ steps: [...imgSteps, { id: "structured_normalize", status: "planned" }]
1070
+ };
1071
+ }
1072
+ case "text":
1073
+ return {
1074
+ intent: "extractStructuredData",
1075
+ steps: [
1076
+ { id: "detect_kind", status: "done" },
1077
+ { id: "utf8_decode", status: "planned" },
1078
+ { id: "structured_normalize", status: "planned" }
1079
+ ]
1080
+ };
1081
+ default:
1082
+ return {
1083
+ intent: "extractStructuredData",
1084
+ steps: [
1085
+ { id: "detect_kind", status: "done" },
1086
+ { id: "route", status: "failed" }
1087
+ ]
1088
+ };
1089
+ }
1090
+ }
1091
+ if (intent === "extractMarkdown" || intent === "extractLlmContent" || intent === "extractStructuredChunks") {
1092
+ const sub = planForIntent(
1093
+ "extractStructuredData",
1094
+ kind,
1095
+ ocrMode,
1096
+ docxInclude,
1097
+ ocr,
1098
+ analyzeFileOutput
1099
+ );
1100
+ return {
1101
+ intent,
1102
+ steps: [
1103
+ ...sub.steps ?? [],
1104
+ { id: "docmind_markdown_render", status: "planned" }
1105
+ ]
1106
+ };
1107
+ }
1108
+ if (intent === "analyzeFile") {
1109
+ const base = planForAnalyzeFile(kind, ocrMode, docxInclude, ocr);
1110
+ if (!analyzeFileRequestsStructured(analyzeFileOutput)) return base;
1111
+ return {
1112
+ ...base,
1113
+ steps: [...base.steps ?? [], { id: "structured_merge", status: "planned" }]
1114
+ };
1115
+ }
772
1116
  if (intent === "extractText") {
773
1117
  const base = planForAnalyzeFile(kind, ocrMode, docxInclude, ocr);
774
1118
  return { ...base, intent: "extractText" };
@@ -860,13 +1204,16 @@ async function explainAnalysisPlan(input, options) {
860
1204
  prepareBrowserAnalyzeInput(input);
861
1205
  const kind = detectFileKind(input);
862
1206
  const intent = options?.intent ?? "analyzeFile";
863
- const ocrMode = resolveOcrMode2(options?.ocr);
1207
+ const ocrMode = resolveOcrMode3(options?.ocr);
864
1208
  const docxInc = options?.docx?.include;
865
1209
  const ocrSlice = options?.ocr;
866
- const plan = planForIntent(intent, kind, ocrMode, docxInc, ocrSlice);
1210
+ const plan = planForIntent(intent, kind, ocrMode, docxInc, ocrSlice, {
1211
+ structuredOutput: options?.structuredOutput,
1212
+ output: options?.output
1213
+ });
867
1214
  return buildBrowserExplainReport(kind, intent, ocrMode, plan, docxInc, ocrSlice);
868
1215
  }
869
1216
 
870
- export { BROWSER_PDF_UNSUPPORTED_WARNING, DOCX_EMBEDDED_IMAGE_CAPABILITIES_BROWSER, DOCX_STRUCTURE_CAPABILITIES_BROWSER, analyzeFile, convertToHtml, docxIncludeRequested, explainAnalysisPlan, extractMetadata, extractText, getCapabilities, runOcr };
1217
+ export { BROWSER_PDF_STRUCTURED_UNSUPPORTED_WARNING, BROWSER_PDF_UNSUPPORTED_WARNING, DOCX_EMBEDDED_IMAGE_CAPABILITIES_BROWSER, DOCX_STRUCTURE_CAPABILITIES_BROWSER, analyzeFile, convertToHtml, docxIncludeRequested, explainAnalysisPlan, extractLlmContent, extractMarkdown, extractMetadata, extractStructuredChunks, extractStructuredData, extractText, getCapabilities, runOcr };
871
1218
  //# sourceMappingURL=index.js.map
872
1219
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dragon708/docmind-browser",
3
- "version": "1.5.1",
3
+ "version": "1.7.0",
4
4
  "description": "Official DocMind browser facade: analyzeFile and intent APIs (DOCX, image OCR, text). PDF and fs paths use @dragon708/docmind-node.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -33,9 +33,10 @@
33
33
  ],
34
34
  "license": "MIT",
35
35
  "dependencies": {
36
- "@dragon708/docmind-docx": "^1.7.1",
37
- "@dragon708/docmind-ocr": "^1.1.3",
38
- "@dragon708/docmind-shared": "^1.1.1"
36
+ "@dragon708/docmind-docx": "^1.8.0",
37
+ "@dragon708/docmind-markdown": "^1.0.0",
38
+ "@dragon708/docmind-ocr": "^1.1.4",
39
+ "@dragon708/docmind-shared": "^1.2.0"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@types/node": "^20.19.37",