@heripo/document-processor 0.1.28 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +71 -9
- package/README.md +87 -9
- package/dist/index.cjs +295 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -7
- package/dist/index.d.ts +84 -7
- package/dist/index.js +294 -34
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -41,6 +41,7 @@ __export(index_exports, {
|
|
|
41
41
|
ChapterConverter: () => ChapterConverter,
|
|
42
42
|
DocumentProcessor: () => DocumentProcessor,
|
|
43
43
|
PAGE_NUMBER_PATTERN: () => PAGE_NUMBER_PATTERN,
|
|
44
|
+
PROCESSED_DOCUMENT_SCHEMA_VERSION: () => PROCESSED_DOCUMENT_SCHEMA_VERSION,
|
|
44
45
|
PagePattern: () => PagePattern,
|
|
45
46
|
PageRangeParseError: () => PageRangeParseError,
|
|
46
47
|
PageRangeParser: () => PageRangeParser,
|
|
@@ -619,6 +620,52 @@ var RefResolver = class {
|
|
|
619
620
|
buildIndex(items, _prefix) {
|
|
620
621
|
return new Map(items.map((item) => [item.self_ref, item]));
|
|
621
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* Check whether a Docling self reference exists in the indexed document.
|
|
625
|
+
*/
|
|
626
|
+
hasRef(ref) {
|
|
627
|
+
const match = ref.match(/^#\/(\w+)\//);
|
|
628
|
+
if (!match) {
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
const collection = match[1];
|
|
632
|
+
if (collection === "texts") {
|
|
633
|
+
return this.textMap.has(ref);
|
|
634
|
+
}
|
|
635
|
+
if (collection === "pictures") {
|
|
636
|
+
return this.pictureMap.has(ref);
|
|
637
|
+
}
|
|
638
|
+
if (collection === "tables") {
|
|
639
|
+
return this.tableMap.has(ref);
|
|
640
|
+
}
|
|
641
|
+
if (collection === "groups") {
|
|
642
|
+
return this.groupMap.has(ref);
|
|
643
|
+
}
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Assert that a Docling self reference exists in the indexed document.
|
|
648
|
+
*/
|
|
649
|
+
assertRef(ref, context) {
|
|
650
|
+
if (this.hasRef(ref)) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const message = `[RefResolver] Missing source reference in ${context}: ${ref}`;
|
|
654
|
+
this.logger.warn(message);
|
|
655
|
+
throw new Error(message);
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Assert that every Docling self reference exists in the indexed document.
|
|
659
|
+
*/
|
|
660
|
+
assertRefs(refs, context) {
|
|
661
|
+
const missingRefs = refs.filter((ref) => !this.hasRef(ref));
|
|
662
|
+
if (missingRefs.length === 0) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const message = `[RefResolver] Missing source references in ${context}: ${missingRefs.join(", ")}`;
|
|
666
|
+
this.logger.warn(message);
|
|
667
|
+
throw new Error(message);
|
|
668
|
+
}
|
|
622
669
|
/**
|
|
623
670
|
* Resolve a $ref string to the actual item
|
|
624
671
|
* @param ref - Reference string (e.g., "#/texts/0")
|
|
@@ -710,6 +757,7 @@ var IdGenerator = class {
|
|
|
710
757
|
imageCounter = 0;
|
|
711
758
|
tableCounter = 0;
|
|
712
759
|
footnoteCounter = 0;
|
|
760
|
+
textBlockCounter = 0;
|
|
713
761
|
/**
|
|
714
762
|
* Generate a chapter ID
|
|
715
763
|
* @returns A chapter ID in the format "ch-001"
|
|
@@ -742,6 +790,14 @@ var IdGenerator = class {
|
|
|
742
790
|
this.footnoteCounter++;
|
|
743
791
|
return `ftn-${this.padNumber(this.footnoteCounter)}`;
|
|
744
792
|
}
|
|
793
|
+
/**
|
|
794
|
+
* Generate a text block ID
|
|
795
|
+
* @returns A text block ID in the format "txt-001"
|
|
796
|
+
*/
|
|
797
|
+
generateTextBlockId() {
|
|
798
|
+
this.textBlockCounter++;
|
|
799
|
+
return `txt-${this.padNumber(this.textBlockCounter)}`;
|
|
800
|
+
}
|
|
745
801
|
/**
|
|
746
802
|
* Reset all counters to zero
|
|
747
803
|
*/
|
|
@@ -750,6 +806,7 @@ var IdGenerator = class {
|
|
|
750
806
|
this.imageCounter = 0;
|
|
751
807
|
this.tableCounter = 0;
|
|
752
808
|
this.footnoteCounter = 0;
|
|
809
|
+
this.textBlockCounter = 0;
|
|
753
810
|
}
|
|
754
811
|
/**
|
|
755
812
|
* Get current counter values (for testing/debugging)
|
|
@@ -759,7 +816,8 @@ var IdGenerator = class {
|
|
|
759
816
|
chapter: this.chapterCounter,
|
|
760
817
|
image: this.imageCounter,
|
|
761
818
|
table: this.tableCounter,
|
|
762
|
-
footnote: this.footnoteCounter
|
|
819
|
+
footnote: this.footnoteCounter,
|
|
820
|
+
textBlock: this.textBlockCounter
|
|
763
821
|
};
|
|
764
822
|
}
|
|
765
823
|
/**
|
|
@@ -1065,6 +1123,7 @@ var ChapterConverter = class _ChapterConverter {
|
|
|
1065
1123
|
title: _ChapterConverter.FRONT_MATTER_TITLE,
|
|
1066
1124
|
pageNo: 1,
|
|
1067
1125
|
level: 1,
|
|
1126
|
+
sourceRefs: [],
|
|
1068
1127
|
textBlocks: [],
|
|
1069
1128
|
imageIds: [],
|
|
1070
1129
|
tableIds: [],
|
|
@@ -1084,6 +1143,7 @@ var ChapterConverter = class _ChapterConverter {
|
|
|
1084
1143
|
title: TextCleaner.normalize(entry.title),
|
|
1085
1144
|
pageNo: entry.pageNo,
|
|
1086
1145
|
level: entry.level,
|
|
1146
|
+
sourceRefs: entry.sourceRefs ?? [],
|
|
1087
1147
|
textBlocks: [],
|
|
1088
1148
|
imageIds: [],
|
|
1089
1149
|
tableIds: [],
|
|
@@ -1174,6 +1234,8 @@ var ChapterConverter = class _ChapterConverter {
|
|
|
1174
1234
|
).map((item) => {
|
|
1175
1235
|
const pdfPageNo = item.prov?.[0]?.page_no ?? 1;
|
|
1176
1236
|
return {
|
|
1237
|
+
id: this.idGenerator.generateTextBlockId(),
|
|
1238
|
+
sourceRef: item.self_ref,
|
|
1177
1239
|
text: TextCleaner.normalize(item.text),
|
|
1178
1240
|
pdfPageNo
|
|
1179
1241
|
};
|
|
@@ -1278,6 +1340,84 @@ var ChapterConverter = class _ChapterConverter {
|
|
|
1278
1340
|
};
|
|
1279
1341
|
|
|
1280
1342
|
// src/converters/resource-converter.ts
|
|
1343
|
+
function getFiniteNumber(value) {
|
|
1344
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1345
|
+
}
|
|
1346
|
+
function getRowSpan(cell) {
|
|
1347
|
+
const startRow = getFiniteNumber(cell.start_row_offset_idx);
|
|
1348
|
+
const endRow = getFiniteNumber(cell.end_row_offset_idx);
|
|
1349
|
+
const offsetSpan = startRow !== void 0 && endRow !== void 0 ? endRow - startRow : 0;
|
|
1350
|
+
return Math.max(1, offsetSpan || getFiniteNumber(cell.row_span) || 1);
|
|
1351
|
+
}
|
|
1352
|
+
function getColSpan(cell) {
|
|
1353
|
+
const startCol = getFiniteNumber(cell.start_col_offset_idx);
|
|
1354
|
+
const endCol = getFiniteNumber(cell.end_col_offset_idx);
|
|
1355
|
+
const offsetSpan = startCol !== void 0 && endCol !== void 0 ? endCol - startCol : 0;
|
|
1356
|
+
return Math.max(1, offsetSpan || getFiniteNumber(cell.col_span) || 1);
|
|
1357
|
+
}
|
|
1358
|
+
function convertTableCell(cell) {
|
|
1359
|
+
return {
|
|
1360
|
+
text: cell.text ?? "",
|
|
1361
|
+
rowSpan: getRowSpan(cell),
|
|
1362
|
+
colSpan: getColSpan(cell),
|
|
1363
|
+
isHeader: cell.column_header || cell.row_header || false
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
function getAnchorColumn(cell, rowIndex, colIndex) {
|
|
1367
|
+
const startRow = getFiniteNumber(cell.start_row_offset_idx) ?? rowIndex;
|
|
1368
|
+
const startCol = getFiniteNumber(cell.start_col_offset_idx) ?? colIndex;
|
|
1369
|
+
return startRow === rowIndex ? startCol : void 0;
|
|
1370
|
+
}
|
|
1371
|
+
function buildGridFromRawGrid(rawGrid) {
|
|
1372
|
+
return rawGrid.map((row, rowIndex) => {
|
|
1373
|
+
const cellsByColumn = /* @__PURE__ */ new Map();
|
|
1374
|
+
row.forEach((cell, colIndex) => {
|
|
1375
|
+
const anchorColumn = getAnchorColumn(cell, rowIndex, colIndex);
|
|
1376
|
+
if (anchorColumn === void 0 || cellsByColumn.has(anchorColumn)) {
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
cellsByColumn.set(anchorColumn, convertTableCell(cell));
|
|
1380
|
+
});
|
|
1381
|
+
return Array.from(cellsByColumn.entries()).sort(([left], [right]) => left - right).map(([, cell]) => cell);
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
function buildGridFromTableCells(tableCells, rowCount) {
|
|
1385
|
+
if (!tableCells || tableCells.length === 0) return [];
|
|
1386
|
+
const maxRow = tableCells.reduce(
|
|
1387
|
+
(max, cell) => Math.max(max, getFiniteNumber(cell.start_row_offset_idx) ?? 0),
|
|
1388
|
+
rowCount - 1
|
|
1389
|
+
) + 1;
|
|
1390
|
+
const rows = Array.from({ length: Math.max(0, maxRow) }, () => []);
|
|
1391
|
+
for (const cell of tableCells) {
|
|
1392
|
+
const row = getFiniteNumber(cell.start_row_offset_idx) ?? 0;
|
|
1393
|
+
const col = getFiniteNumber(cell.start_col_offset_idx) ?? 0;
|
|
1394
|
+
if (row < 0 || row >= rows.length) continue;
|
|
1395
|
+
rows[row].push({ col, cell: convertTableCell(cell) });
|
|
1396
|
+
}
|
|
1397
|
+
return rows.map(
|
|
1398
|
+
(row) => row.sort((a, b) => a.col - b.col).map(({ cell }) => cell)
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1401
|
+
function getGridColumnCount(grid) {
|
|
1402
|
+
return grid.reduce(
|
|
1403
|
+
(max, row) => Math.max(
|
|
1404
|
+
max,
|
|
1405
|
+
row.reduce((sum, cell) => sum + Math.max(1, cell.colSpan), 0)
|
|
1406
|
+
),
|
|
1407
|
+
0
|
|
1408
|
+
);
|
|
1409
|
+
}
|
|
1410
|
+
function buildProcessedTableGrid(table) {
|
|
1411
|
+
const rawGrid = table.data.grid ?? [];
|
|
1412
|
+
const rawNumRows = getFiniteNumber(table.data.num_rows);
|
|
1413
|
+
const rawNumCols = getFiniteNumber(table.data.num_cols);
|
|
1414
|
+
const grid = rawGrid.length > 0 ? buildGridFromRawGrid(rawGrid) : buildGridFromTableCells(table.data.table_cells, rawNumRows ?? 0);
|
|
1415
|
+
return {
|
|
1416
|
+
grid,
|
|
1417
|
+
numRows: rawNumRows !== void 0 && rawNumRows > 0 ? rawNumRows : grid.length,
|
|
1418
|
+
numCols: rawNumCols !== void 0 && rawNumCols > 0 ? rawNumCols : getGridColumnCount(grid)
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1281
1421
|
var ResourceConverter = class {
|
|
1282
1422
|
logger;
|
|
1283
1423
|
idGenerator;
|
|
@@ -1313,12 +1453,17 @@ var ResourceConverter = class {
|
|
|
1313
1453
|
this.logger.info(
|
|
1314
1454
|
`[ResourceConverter] Converting ${doclingDoc.pictures.length} images...`
|
|
1315
1455
|
);
|
|
1316
|
-
const
|
|
1317
|
-
(picture) => this.captionProcessingPipeline.
|
|
1456
|
+
const captionSources = doclingDoc.pictures.map(
|
|
1457
|
+
(picture) => this.captionProcessingPipeline.extractCaptionSource(picture.captions)
|
|
1458
|
+
);
|
|
1459
|
+
const captionTexts = captionSources.map(
|
|
1460
|
+
(captionSource) => captionSource.text
|
|
1318
1461
|
);
|
|
1319
1462
|
const images = doclingDoc.pictures.map(
|
|
1320
1463
|
(picture, index) => ({
|
|
1321
1464
|
id: this.idGenerator.generateImageId(),
|
|
1465
|
+
sourceRef: picture.self_ref,
|
|
1466
|
+
captionSourceRefs: captionSources[index].sourceRefs,
|
|
1322
1467
|
path: `${artifactDir}/images/image_${index}.png`,
|
|
1323
1468
|
pdfPageNo: picture.prov?.[0]?.page_no ?? 0
|
|
1324
1469
|
})
|
|
@@ -1341,23 +1486,21 @@ var ResourceConverter = class {
|
|
|
1341
1486
|
this.logger.info(
|
|
1342
1487
|
`[ResourceConverter] Converting ${doclingDoc.tables.length} tables...`
|
|
1343
1488
|
);
|
|
1344
|
-
const
|
|
1345
|
-
(table) => this.captionProcessingPipeline.
|
|
1489
|
+
const captionSources = doclingDoc.tables.map(
|
|
1490
|
+
(table) => this.captionProcessingPipeline.extractCaptionSource(table.captions)
|
|
1346
1491
|
);
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
colSpan: cell.col_span ?? 1,
|
|
1353
|
-
isHeader: cell.column_header || cell.row_header || false
|
|
1354
|
-
}))
|
|
1355
|
-
);
|
|
1492
|
+
const captionTexts = captionSources.map(
|
|
1493
|
+
(captionSource) => captionSource.text
|
|
1494
|
+
);
|
|
1495
|
+
const tables = doclingDoc.tables.map((table, index) => {
|
|
1496
|
+
const { grid, numRows, numCols } = buildProcessedTableGrid(table);
|
|
1356
1497
|
return {
|
|
1357
1498
|
id: this.idGenerator.generateTableId(),
|
|
1499
|
+
sourceRef: table.self_ref,
|
|
1500
|
+
captionSourceRefs: captionSources[index].sourceRefs,
|
|
1358
1501
|
pdfPageNo: table.prov?.[0]?.page_no ?? 0,
|
|
1359
|
-
numRows
|
|
1360
|
-
numCols
|
|
1502
|
+
numRows,
|
|
1503
|
+
numCols,
|
|
1361
1504
|
grid
|
|
1362
1505
|
};
|
|
1363
1506
|
});
|
|
@@ -1384,6 +1527,7 @@ var ResourceConverter = class {
|
|
|
1384
1527
|
);
|
|
1385
1528
|
const footnotes = footnoteItems.filter((item) => TextCleaner.isValidText(item.text)).map((item) => ({
|
|
1386
1529
|
id: this.idGenerator.generateFootnoteId(),
|
|
1530
|
+
sourceRef: item.self_ref,
|
|
1387
1531
|
text: TextCleaner.normalize(item.text),
|
|
1388
1532
|
pdfPageNo: item.prov?.[0]?.page_no ?? 1
|
|
1389
1533
|
}));
|
|
@@ -3683,7 +3827,8 @@ Remember: Look for SMALL numbers in MARGINS only. Ignore figure/table/photo numb
|
|
|
3683
3827
|
};
|
|
3684
3828
|
|
|
3685
3829
|
// src/pipelines/caption-processing-pipeline.ts
|
|
3686
|
-
var CaptionProcessingPipeline = class {
|
|
3830
|
+
var CaptionProcessingPipeline = class _CaptionProcessingPipeline {
|
|
3831
|
+
static CAPTION_JOIN_SEPARATOR = " ";
|
|
3687
3832
|
logger;
|
|
3688
3833
|
captionParser;
|
|
3689
3834
|
captionValidator;
|
|
@@ -3832,23 +3977,51 @@ var CaptionProcessingPipeline = class {
|
|
|
3832
3977
|
return captionsByIndex;
|
|
3833
3978
|
}
|
|
3834
3979
|
/**
|
|
3835
|
-
* Extract caption text from resource
|
|
3980
|
+
* Extract combined caption text from resource captions.
|
|
3981
|
+
*
|
|
3982
|
+
* Returns every caption joined with a single space; empty/whitespace-only
|
|
3983
|
+
* entries are skipped. Returns `undefined` when nothing resolves to text.
|
|
3836
3984
|
*
|
|
3837
|
-
*
|
|
3985
|
+
* @deprecated Use {@link extractCaptionSource} to also receive the
|
|
3986
|
+
* caption `$ref` list. This helper is retained as a thin wrapper for
|
|
3987
|
+
* legacy callers.
|
|
3838
3988
|
*/
|
|
3839
3989
|
extractCaptionText(captions) {
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3990
|
+
return this.extractCaptionSource(captions).text;
|
|
3991
|
+
}
|
|
3992
|
+
/**
|
|
3993
|
+
* Extract caption text and source references from resource captions.
|
|
3994
|
+
*
|
|
3995
|
+
* Iterates captions in order. String captions contribute to text only;
|
|
3996
|
+
* `$ref` captions also push the ref onto `sourceRefs` regardless of whether
|
|
3997
|
+
* the resolver finds text. Resolved/raw text is trimmed, and empty parts
|
|
3998
|
+
* are skipped before joining.
|
|
3999
|
+
*/
|
|
4000
|
+
extractCaptionSource(captions) {
|
|
4001
|
+
const sourceRefs = [];
|
|
4002
|
+
const textParts = [];
|
|
4003
|
+
captions?.forEach((caption) => {
|
|
4004
|
+
if (typeof caption === "string") {
|
|
4005
|
+
const text2 = caption.trim();
|
|
4006
|
+
if (text2.length > 0) {
|
|
4007
|
+
textParts.push(text2);
|
|
4008
|
+
}
|
|
4009
|
+
return;
|
|
4010
|
+
}
|
|
4011
|
+
sourceRefs.push(caption.$ref);
|
|
4012
|
+
const resolved = this.refResolver?.resolveText(caption.$ref);
|
|
4013
|
+
const text = resolved?.text.trim();
|
|
4014
|
+
if (text && text.length > 0) {
|
|
4015
|
+
textParts.push(text);
|
|
4016
|
+
}
|
|
4017
|
+
});
|
|
4018
|
+
if (textParts.length === 0) {
|
|
4019
|
+
return { sourceRefs };
|
|
3850
4020
|
}
|
|
3851
|
-
return
|
|
4021
|
+
return {
|
|
4022
|
+
text: textParts.join(_CaptionProcessingPipeline.CAPTION_JOIN_SEPARATOR),
|
|
4023
|
+
sourceRefs
|
|
4024
|
+
};
|
|
3852
4025
|
}
|
|
3853
4026
|
};
|
|
3854
4027
|
|
|
@@ -4420,6 +4593,7 @@ var CaptionValidationError = class extends Error {
|
|
|
4420
4593
|
};
|
|
4421
4594
|
|
|
4422
4595
|
// src/document-processor.ts
|
|
4596
|
+
var PROCESSED_DOCUMENT_SCHEMA_VERSION = "processed-document.v2";
|
|
4423
4597
|
var DocumentProcessor = class {
|
|
4424
4598
|
logger;
|
|
4425
4599
|
fallbackModel;
|
|
@@ -4570,18 +4744,26 @@ var DocumentProcessor = class {
|
|
|
4570
4744
|
`[DocumentProcessor] Chapter conversion took ${chaptersTime}ms`
|
|
4571
4745
|
);
|
|
4572
4746
|
const startTimeAssemble = Date.now();
|
|
4573
|
-
const processedDoc = this.assembleProcessedDocument(
|
|
4747
|
+
const processedDoc = this.assembleProcessedDocument({
|
|
4574
4748
|
reportId,
|
|
4575
4749
|
pageRangeMap,
|
|
4576
4750
|
chapters,
|
|
4577
4751
|
images,
|
|
4578
4752
|
tables,
|
|
4579
|
-
footnotes
|
|
4580
|
-
|
|
4753
|
+
footnotes,
|
|
4754
|
+
source: processOptions.source
|
|
4755
|
+
});
|
|
4581
4756
|
const assembleTime = Date.now() - startTimeAssemble;
|
|
4582
4757
|
this.logger.info(
|
|
4583
4758
|
`[DocumentProcessor] Document assembly took ${assembleTime}ms`
|
|
4584
4759
|
);
|
|
4760
|
+
const sourceRefValidationMode = this.resolveSourceRefValidationMode(processOptions);
|
|
4761
|
+
if (sourceRefValidationMode !== "off") {
|
|
4762
|
+
this.validateProcessedDocumentSourceRefs(
|
|
4763
|
+
processedDoc,
|
|
4764
|
+
sourceRefValidationMode
|
|
4765
|
+
);
|
|
4766
|
+
}
|
|
4585
4767
|
this.logger.info("[DocumentProcessor] Document processing completed");
|
|
4586
4768
|
return {
|
|
4587
4769
|
document: processedDoc,
|
|
@@ -4720,15 +4902,93 @@ var DocumentProcessor = class {
|
|
|
4720
4902
|
);
|
|
4721
4903
|
return pageRangeMap;
|
|
4722
4904
|
}
|
|
4905
|
+
resolveSourceRefValidationMode(processOptions) {
|
|
4906
|
+
if (processOptions.sourceRefValidationMode !== void 0) {
|
|
4907
|
+
return processOptions.sourceRefValidationMode;
|
|
4908
|
+
}
|
|
4909
|
+
return processOptions.validateSourceRefs === true ? "error" : "off";
|
|
4910
|
+
}
|
|
4911
|
+
validateProcessedDocumentSourceRefs(processedDoc, mode) {
|
|
4912
|
+
if (!this.refResolver) {
|
|
4913
|
+
throw new Error(
|
|
4914
|
+
"[DocumentProcessor] Cannot validate source references before RefResolver initialization"
|
|
4915
|
+
);
|
|
4916
|
+
}
|
|
4917
|
+
const issues = this.collectMissingSourceRefs(
|
|
4918
|
+
processedDoc,
|
|
4919
|
+
this.refResolver
|
|
4920
|
+
);
|
|
4921
|
+
if (issues.length === 0) {
|
|
4922
|
+
this.logger.info(
|
|
4923
|
+
"[DocumentProcessor] Source reference validation passed"
|
|
4924
|
+
);
|
|
4925
|
+
return;
|
|
4926
|
+
}
|
|
4927
|
+
const details = issues.map((issue) => `${issue.context} (${issue.ref})`).join("; ");
|
|
4928
|
+
const message = `[DocumentProcessor] Source reference validation found ${issues.length} missing reference(s): ${details}`;
|
|
4929
|
+
if (mode === "warn") {
|
|
4930
|
+
this.logger.warn(message);
|
|
4931
|
+
return;
|
|
4932
|
+
}
|
|
4933
|
+
this.logger.error(message);
|
|
4934
|
+
throw new Error(message);
|
|
4935
|
+
}
|
|
4936
|
+
collectMissingSourceRefs(processedDoc, refResolver) {
|
|
4937
|
+
const issues = [];
|
|
4938
|
+
const addRef = (ref, context) => {
|
|
4939
|
+
if (ref !== void 0 && !refResolver.hasRef(ref)) {
|
|
4940
|
+
issues.push({ context, ref });
|
|
4941
|
+
}
|
|
4942
|
+
};
|
|
4943
|
+
const addRefs = (refs, context) => {
|
|
4944
|
+
refs?.forEach((ref, index) => {
|
|
4945
|
+
addRef(ref, `${context}[${index}]`);
|
|
4946
|
+
});
|
|
4947
|
+
};
|
|
4948
|
+
const visitChapter = (chapter) => {
|
|
4949
|
+
addRefs(chapter.sourceRefs, `chapter ${chapter.id} sourceRefs`);
|
|
4950
|
+
chapter.textBlocks.forEach((textBlock, index) => {
|
|
4951
|
+
addRef(
|
|
4952
|
+
textBlock.sourceRef,
|
|
4953
|
+
`chapter ${chapter.id} textBlock ${textBlock.id ?? index} sourceRef`
|
|
4954
|
+
);
|
|
4955
|
+
});
|
|
4956
|
+
chapter.children?.forEach(visitChapter);
|
|
4957
|
+
};
|
|
4958
|
+
processedDoc.chapters.forEach(visitChapter);
|
|
4959
|
+
processedDoc.images.forEach((image) => {
|
|
4960
|
+
addRef(image.sourceRef, `image ${image.id} sourceRef`);
|
|
4961
|
+
addRefs(image.captionSourceRefs, `image ${image.id} captionSourceRefs`);
|
|
4962
|
+
});
|
|
4963
|
+
processedDoc.tables.forEach((table) => {
|
|
4964
|
+
addRef(table.sourceRef, `table ${table.id} sourceRef`);
|
|
4965
|
+
addRefs(table.captionSourceRefs, `table ${table.id} captionSourceRefs`);
|
|
4966
|
+
});
|
|
4967
|
+
processedDoc.footnotes.forEach((footnote) => {
|
|
4968
|
+
addRef(footnote.sourceRef, `footnote ${footnote.id} sourceRef`);
|
|
4969
|
+
});
|
|
4970
|
+
return issues;
|
|
4971
|
+
}
|
|
4723
4972
|
/**
|
|
4724
4973
|
* Assemble the final ProcessedDocument
|
|
4725
4974
|
*
|
|
4726
4975
|
* Creates the ProcessedDocument structure with all converted components
|
|
4727
4976
|
*/
|
|
4728
|
-
assembleProcessedDocument(
|
|
4977
|
+
assembleProcessedDocument(input) {
|
|
4729
4978
|
this.logger.info("[DocumentProcessor] Assembling ProcessedDocument...");
|
|
4979
|
+
const {
|
|
4980
|
+
reportId,
|
|
4981
|
+
pageRangeMap,
|
|
4982
|
+
chapters,
|
|
4983
|
+
images,
|
|
4984
|
+
tables,
|
|
4985
|
+
footnotes,
|
|
4986
|
+
source
|
|
4987
|
+
} = input;
|
|
4730
4988
|
const processedDoc = {
|
|
4731
4989
|
reportId,
|
|
4990
|
+
schemaVersion: PROCESSED_DOCUMENT_SCHEMA_VERSION,
|
|
4991
|
+
...source !== void 0 ? { source } : {},
|
|
4732
4992
|
pageRangeMap,
|
|
4733
4993
|
chapters,
|
|
4734
4994
|
images,
|
|
@@ -4780,6 +5040,7 @@ var DocumentProcessor = class {
|
|
|
4780
5040
|
ChapterConverter,
|
|
4781
5041
|
DocumentProcessor,
|
|
4782
5042
|
PAGE_NUMBER_PATTERN,
|
|
5043
|
+
PROCESSED_DOCUMENT_SCHEMA_VERSION,
|
|
4783
5044
|
PagePattern,
|
|
4784
5045
|
PageRangeParseError,
|
|
4785
5046
|
PageRangeParser,
|