@stll/anonymize-pdf 2.9.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,7 +51,8 @@ appropriate for untrusted files.
51
51
  `anonymizePdfRaster` accepts pages produced by an explicit renderer/OCR
52
52
  provider and creates a brand-new image-only PDF. It requires a prepared stella
53
53
  pipeline, runs native detection over each page's observed text, merges an
54
- optional digest-bound `ExternalDetectionBatch`, and fails if any selected
54
+ optional digest-bound `ExternalDetectionBatch`, applies optional user-reviewed
55
+ `manualRegions` expressed in displayed-page PDF points, and fails if any selected
55
56
  non-whitespace UTF-16 span is not completely mapped to observed glyph geometry.
56
57
  Provider metadata must identify one explicit OCR language pack; mixed or
57
58
  implicit language selection is not part of the contract.
@@ -60,6 +61,8 @@ binds the source and each page, requires complete rendering and OCR assertions,
60
61
  destructively fills the mapped pixels, and verifies both the newly written PDF
61
62
  graph and its decompressed image pixels. Encrypted inputs are rejected.
62
63
 
64
+ Manual regions are validated against the observed page and mapped outward to
65
+ pixels before filling, so partially covered edge pixels cannot retain content.
63
66
  The output never reuses source PDF objects, streams, text layers, metadata,
64
67
  forms, annotations, attachments, actions, signatures, optional content, or
65
68
  incremental revisions. A solid rectangle in this output is destructive: the
@@ -104,6 +107,11 @@ document-profile capability; PDF inspection remains available through the
104
107
  byte-oriented core runtimes. The Poppler/Tesseract adapter is separately named
105
108
  as the Node-only `document.pdf.observe-raster-local` capability.
106
109
 
110
+ Raster certificates use `PDF_RASTER_CERTIFICATE_CONTRACT_VERSION` (2), including
111
+ the required `manualRegionCount` field. The decoder rejects version 1 certificates;
112
+ consumers storing certificates must dispatch by their version. Raster requests
113
+ (`PDF_RASTER_CONTRACT_VERSION`) and inspections remain at version 1.
114
+
107
115
  `rewritePdfRasterFromDetections` is the advanced rewrite-only seam for trusted
108
116
  adapters that already ran detection. Detection spans must be strictly ordered
109
117
  and non-overlapping. Its name and certificate deliberately do not imply
package/dist/index.d.mts CHANGED
@@ -84,6 +84,8 @@ type PdfRasterPage = {
84
84
  pixelSha256: string;
85
85
  /** Strictly ordered, non-overlapping UTF-16 spans. */
86
86
  detections: readonly PdfRasterDetection[];
87
+ /** User-reviewed regions in normalized displayed-page PDF points. */
88
+ manualRegions?: readonly PdfRect[];
87
89
  };
88
90
  type PdfRasterDetection = {
89
91
  start: number;
@@ -97,13 +99,15 @@ type PdfRasterRewrite = {
97
99
  fillRgb: readonly [number, number, number];
98
100
  pages: readonly PdfRasterPage[];
99
101
  };
102
+ declare const PDF_RASTER_CERTIFICATE_CONTRACT_VERSION: 2;
100
103
  type PdfRasterRewriteCertificate = {
101
- contractVersion: 1;
104
+ contractVersion: typeof PDF_RASTER_CERTIFICATE_CONTRACT_VERSION;
102
105
  pageCount: number;
103
106
  sourceSha256: string;
104
107
  outputSha256: string;
105
108
  provider: PdfRasterProvider;
106
109
  detectionCount: number;
110
+ manualRegionCount: number;
107
111
  mappedRegionCount: number;
108
112
  structurePixelRewriteVerified: true;
109
113
  providerAssertedCoverage: "complete-rendering-and-ocr-observation";
@@ -171,6 +175,7 @@ declare const PDF_INSPECTION_CONTRACT_VERSION: 1;
171
175
  declare const PDF_RASTER_CONTRACT_VERSION: 1;
172
176
  declare const PDF_RASTER_MAX_PAGE_BYTES: number;
173
177
  declare const PDF_RASTER_MAX_TOTAL_BYTES: number;
178
+ declare const PDF_RASTER_MAX_MANUAL_REGIONS = 1000000;
174
179
  declare const PDF_RASTER_MAX_OUTPUT_BYTES: number;
175
180
  declare const PDF_RASTER_REQUEST_JSON_MAX_BYTES: number;
176
181
  declare const PDF_DOCUMENT_MAX_BYTES: number;
@@ -216,6 +221,8 @@ type PdfRasterObservedPageInput = {
216
221
  pixels: Uint8Array;
217
222
  /** Optional digest-bound provider batch merged through stella caller semantics. */
218
223
  externalDetectionBatch?: ExternalDetectionBatch | string | undefined;
224
+ /** User-reviewed regions in normalized displayed-page PDF points. */
225
+ manualRegions?: readonly PdfRect[] | undefined;
219
226
  };
220
227
  type AnonymizePdfRasterOptions = {
221
228
  document: Uint8Array;
@@ -227,5 +234,5 @@ type AnonymizePdfRasterOptions = {
227
234
  /** Run stella detection, merge optional external detections, then rewrite pixels. */
228
235
  declare const anonymizePdfRaster: ({ document, pipeline, provider, pages, fillRgb }: AnonymizePdfRasterOptions) => PdfRasterRewriteResult;
229
236
  //#endregion
230
- export { AnonymizePdfRasterOptions, InspectPdfOptions, type LocalPdfRasterObservation, type LocalPdfRasterPage, PDF_DOCUMENT_MAX_BYTES, PDF_INSPECTION_CONTRACT_VERSION, PDF_INSPECTION_ERROR_CODES, PDF_LOADED_PAYLOAD_MAX_BYTES, PDF_LOCAL_PROVIDER_ERROR_CODES, PDF_MAX_GLYPHS, PDF_MAX_OBJECTS, PDF_MAX_OBJECT_DEPTH, PDF_MAX_OBJECT_NODES, PDF_MAX_OBSERVED_TEXT_UTF8_BYTES, PDF_MAX_PAGES, PDF_MAX_PAGE_TEXT_UTF8_BYTES, PDF_OBSERVATIONS_JSON_MAX_BYTES, PDF_PAGE_DIMENSION_TOLERANCE_POINTS, PDF_RASTER_CONTRACT_VERSION, PDF_RASTER_ERROR_CODES, PDF_RASTER_MAX_OUTPUT_BYTES, PDF_RASTER_MAX_PAGE_BYTES, PDF_RASTER_MAX_TOTAL_BYTES, PDF_RASTER_REQUEST_JSON_MAX_BYTES, PDF_STREAM_DECOMPRESSED_MAX_BYTES, type PdfGlyphObservation, type PdfInspection, PdfInspectionError, type PdfInspectionErrorCode, type PdfInspectionGap, PdfLocalProviderError, type PdfLocalProviderErrorCode, type PdfPageInspection, type PdfPageObservation, type PdfRasterDetection, PdfRasterError, type PdfRasterErrorCode, PdfRasterObservedPageInput, type PdfRasterPage, type PdfRasterProvider, type PdfRasterRewrite, type PdfRasterRewriteCertificate, type PdfRasterRewriteResult, type PdfRect, type PdfRiskInventory, type RenderPdfWithPopplerTesseractOptions, RewritePdfRasterFromDetectionsOptions, anonymizePdfRaster, inspectPdf, renderPdfWithPopplerTesseract, rewritePdfRasterFromDetections };
237
+ export { AnonymizePdfRasterOptions, InspectPdfOptions, type LocalPdfRasterObservation, type LocalPdfRasterPage, PDF_DOCUMENT_MAX_BYTES, PDF_INSPECTION_CONTRACT_VERSION, PDF_INSPECTION_ERROR_CODES, PDF_LOADED_PAYLOAD_MAX_BYTES, PDF_LOCAL_PROVIDER_ERROR_CODES, PDF_MAX_GLYPHS, PDF_MAX_OBJECTS, PDF_MAX_OBJECT_DEPTH, PDF_MAX_OBJECT_NODES, PDF_MAX_OBSERVED_TEXT_UTF8_BYTES, PDF_MAX_PAGES, PDF_MAX_PAGE_TEXT_UTF8_BYTES, PDF_OBSERVATIONS_JSON_MAX_BYTES, PDF_PAGE_DIMENSION_TOLERANCE_POINTS, PDF_RASTER_CERTIFICATE_CONTRACT_VERSION, PDF_RASTER_CONTRACT_VERSION, PDF_RASTER_ERROR_CODES, PDF_RASTER_MAX_MANUAL_REGIONS, PDF_RASTER_MAX_OUTPUT_BYTES, PDF_RASTER_MAX_PAGE_BYTES, PDF_RASTER_MAX_TOTAL_BYTES, PDF_RASTER_REQUEST_JSON_MAX_BYTES, PDF_STREAM_DECOMPRESSED_MAX_BYTES, type PdfGlyphObservation, type PdfInspection, PdfInspectionError, type PdfInspectionErrorCode, type PdfInspectionGap, PdfLocalProviderError, type PdfLocalProviderErrorCode, type PdfPageInspection, type PdfPageObservation, type PdfRasterDetection, PdfRasterError, type PdfRasterErrorCode, PdfRasterObservedPageInput, type PdfRasterPage, type PdfRasterProvider, type PdfRasterRewrite, type PdfRasterRewriteCertificate, type PdfRasterRewriteResult, type PdfRect, type PdfRiskInventory, type RenderPdfWithPopplerTesseractOptions, RewritePdfRasterFromDetectionsOptions, anonymizePdfRaster, inspectPdf, renderPdfWithPopplerTesseract, rewritePdfRasterFromDetections };
231
238
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -5,6 +5,7 @@ import { lstat, mkdtemp, readFile, rm, unlink, writeFile } from "node:fs/promise
5
5
  import { tmpdir } from "node:os";
6
6
  import { join, sep } from "node:path";
7
7
  //#region src/types.ts
8
+ const PDF_RASTER_CERTIFICATE_CONTRACT_VERSION = 2;
8
9
  const PDF_INSPECTION_ERROR_CODES = {
9
10
  documentLimitExceeded: "document-limit-exceeded",
10
11
  invalidDocument: "invalid-document",
@@ -94,6 +95,7 @@ const CERTIFICATE_FIELDS = {
94
95
  outputSha256: true,
95
96
  provider: true,
96
97
  detectionCount: true,
98
+ manualRegionCount: true,
97
99
  mappedRegionCount: true,
98
100
  structurePixelRewriteVerified: true,
99
101
  providerAssertedCoverage: true,
@@ -128,7 +130,7 @@ const isInspectionGap = (value) => typeof value === "string" && Object.hasOwn(IN
128
130
  const isInspection = (value) => isRecord(value) && hasExactFields(value, INSPECTION_FIELDS) && value["contractVersion"] === 1 && typeof value["pdfVersion"] === "string" && isNonNegativeInteger(value["byteLength"]) && isNonNegativeInteger(value["objectCount"]) && isNonNegativeInteger(value["pageCount"]) && typeof value["encrypted"] === "boolean" && Array.isArray(value["pages"]) && value["pages"].length === value["pageCount"] && value["pages"].every(isPage) && value["pages"].every(({ pageIndex }, index) => pageIndex === index) && isRiskInventory(value["risks"]) && isRecord(value["coverage"]) && hasExactFields(value["coverage"], COVERAGE_FIELDS) && (value["coverage"]["status"] === "full" || value["coverage"]["status"] === "partial") && Array.isArray(value["coverage"]["gaps"]) && value["coverage"]["gaps"].every(isInspectionGap);
129
131
  const isProvider = (value) => isRecord(value) && hasExactFields(value, PROVIDER_FIELDS) && typeof value["providerId"] === "string" && typeof value["rendererName"] === "string" && typeof value["rendererVersion"] === "string" && typeof value["ocrName"] === "string" && typeof value["ocrVersion"] === "string" && typeof value["ocrLanguage"] === "string";
130
132
  const isSha256 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/u.test(value);
131
- const isCertificate = (value) => isRecord(value) && hasExactFields(value, CERTIFICATE_FIELDS) && value["contractVersion"] === 1 && isNonNegativeInteger(value["pageCount"]) && isSha256(value["sourceSha256"]) && isSha256(value["outputSha256"]) && isProvider(value["provider"]) && isNonNegativeInteger(value["detectionCount"]) && isNonNegativeInteger(value["mappedRegionCount"]) && value["structurePixelRewriteVerified"] === true && value["providerAssertedCoverage"] === "complete-rendering-and-ocr-observation" && value["piiCleanGuaranteed"] === false && typeof value["limitation"] === "string";
133
+ const isCertificate = (value) => isRecord(value) && hasExactFields(value, CERTIFICATE_FIELDS) && value["contractVersion"] === 2 && isNonNegativeInteger(value["pageCount"]) && isSha256(value["sourceSha256"]) && isSha256(value["outputSha256"]) && isProvider(value["provider"]) && isNonNegativeInteger(value["detectionCount"]) && isNonNegativeInteger(value["manualRegionCount"]) && isNonNegativeInteger(value["mappedRegionCount"]) && value["structurePixelRewriteVerified"] === true && value["providerAssertedCoverage"] === "complete-rendering-and-ocr-observation" && value["piiCleanGuaranteed"] === false && typeof value["limitation"] === "string";
132
134
  const decodePdfInspection = (json) => {
133
135
  const value = JSON.parse(json);
134
136
  if (!isInspection(value)) throw new Error("Native PDF inspection does not match contract version 1");
@@ -136,7 +138,7 @@ const decodePdfInspection = (json) => {
136
138
  };
137
139
  const decodePdfRasterRewriteCertificate = (json) => {
138
140
  const value = JSON.parse(json);
139
- if (!isCertificate(value)) throw new Error("Native PDF raster certificate does not match contract version 1");
141
+ if (!isCertificate(value)) throw new Error(`Native PDF raster certificate does not match contract version 2`);
140
142
  return value;
141
143
  };
142
144
  //#endregion
@@ -464,6 +466,7 @@ const PDF_INSPECTION_CONTRACT_VERSION = 1;
464
466
  const PDF_RASTER_CONTRACT_VERSION = 1;
465
467
  const PDF_RASTER_MAX_PAGE_BYTES = 134217728;
466
468
  const PDF_RASTER_MAX_TOTAL_BYTES = 536870912;
469
+ const PDF_RASTER_MAX_MANUAL_REGIONS = 1e6;
467
470
  const PDF_RASTER_MAX_OUTPUT_BYTES = 536870912;
468
471
  const PDF_RASTER_REQUEST_JSON_MAX_BYTES = 67108864;
469
472
  const PDF_DOCUMENT_MAX_BYTES = 67108864;
@@ -541,6 +544,7 @@ const anonymizePdfRaster = ({ document, pipeline, provider, pages, fillRgb = [
541
544
  if (pages.length > 1e4) throw new PdfRasterError(PDF_RASTER_ERROR_CODES.limitExceeded, "limit-exceeded: PDF raster page count exceeds its limit");
542
545
  let totalPixelBytes = 0;
543
546
  let totalObservedTextBytes = 0;
547
+ let totalManualRegions = 0;
544
548
  const textEncoder = new TextEncoder();
545
549
  for (const page of pages) {
546
550
  if (typeof page.observation?.text !== "string") throw new PdfRasterError(PDF_RASTER_ERROR_CODES.invalidContract, "invalid-contract: PDF raster observed text is invalid");
@@ -554,6 +558,8 @@ const anonymizePdfRaster = ({ document, pipeline, provider, pages, fillRgb = [
554
558
  if (!Number.isSafeInteger(expected) || expected > 134217728 || page.pixels.byteLength !== expected) throw new PdfRasterError(PDF_RASTER_ERROR_CODES.limitExceeded, "limit-exceeded: PDF raster page pixels exceed limits or have an invalid RGB8 length");
555
559
  totalPixelBytes += expected;
556
560
  if (!Number.isSafeInteger(totalPixelBytes) || totalPixelBytes > 536870912) throw new PdfRasterError(PDF_RASTER_ERROR_CODES.limitExceeded, "limit-exceeded: PDF raster pixels exceed their aggregate limit");
561
+ totalManualRegions += page.manualRegions?.length ?? 0;
562
+ if (totalManualRegions > 1e6) throw new PdfRasterError(PDF_RASTER_ERROR_CODES.limitExceeded, "limit-exceeded: PDF raster manual regions exceed their aggregate limit");
557
563
  }
558
564
  const binding = loadNativeAnonymizeBinding();
559
565
  let requestPages;
@@ -569,7 +575,8 @@ const anonymizePdfRaster = ({ document, pipeline, provider, pages, fillRgb = [
569
575
  detections: result.resolvedEntities.map(({ start, end }) => ({
570
576
  start,
571
577
  end
572
- }))
578
+ })),
579
+ manualRegions: page.manualRegions ?? []
573
580
  };
574
581
  });
575
582
  } catch {
@@ -588,6 +595,6 @@ const anonymizePdfRaster = ({ document, pipeline, provider, pages, fillRgb = [
588
595
  });
589
596
  };
590
597
  //#endregion
591
- export { PDF_DOCUMENT_MAX_BYTES, PDF_INSPECTION_CONTRACT_VERSION, PDF_INSPECTION_ERROR_CODES, PDF_LOADED_PAYLOAD_MAX_BYTES, PDF_LOCAL_PROVIDER_ERROR_CODES, PDF_MAX_GLYPHS, PDF_MAX_OBJECTS, PDF_MAX_OBJECT_DEPTH, PDF_MAX_OBJECT_NODES, PDF_MAX_OBSERVED_TEXT_UTF8_BYTES, PDF_MAX_PAGES, PDF_MAX_PAGE_TEXT_UTF8_BYTES, PDF_OBSERVATIONS_JSON_MAX_BYTES, PDF_PAGE_DIMENSION_TOLERANCE_POINTS, PDF_RASTER_CONTRACT_VERSION, PDF_RASTER_ERROR_CODES, PDF_RASTER_MAX_OUTPUT_BYTES, PDF_RASTER_MAX_PAGE_BYTES, PDF_RASTER_MAX_TOTAL_BYTES, PDF_RASTER_REQUEST_JSON_MAX_BYTES, PDF_STREAM_DECOMPRESSED_MAX_BYTES, PdfInspectionError, PdfLocalProviderError, PdfRasterError, anonymizePdfRaster, inspectPdf, renderPdfWithPopplerTesseract, rewritePdfRasterFromDetections };
598
+ export { PDF_DOCUMENT_MAX_BYTES, PDF_INSPECTION_CONTRACT_VERSION, PDF_INSPECTION_ERROR_CODES, PDF_LOADED_PAYLOAD_MAX_BYTES, PDF_LOCAL_PROVIDER_ERROR_CODES, PDF_MAX_GLYPHS, PDF_MAX_OBJECTS, PDF_MAX_OBJECT_DEPTH, PDF_MAX_OBJECT_NODES, PDF_MAX_OBSERVED_TEXT_UTF8_BYTES, PDF_MAX_PAGES, PDF_MAX_PAGE_TEXT_UTF8_BYTES, PDF_OBSERVATIONS_JSON_MAX_BYTES, PDF_PAGE_DIMENSION_TOLERANCE_POINTS, PDF_RASTER_CERTIFICATE_CONTRACT_VERSION, PDF_RASTER_CONTRACT_VERSION, PDF_RASTER_ERROR_CODES, PDF_RASTER_MAX_MANUAL_REGIONS, PDF_RASTER_MAX_OUTPUT_BYTES, PDF_RASTER_MAX_PAGE_BYTES, PDF_RASTER_MAX_TOTAL_BYTES, PDF_RASTER_REQUEST_JSON_MAX_BYTES, PDF_STREAM_DECOMPRESSED_MAX_BYTES, PdfInspectionError, PdfLocalProviderError, PdfRasterError, anonymizePdfRaster, inspectPdf, renderPdfWithPopplerTesseract, rewritePdfRasterFromDetections };
592
599
 
593
600
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["PDF_DOCUMENT_MAX_BYTES","PDF_RASTER_MAX_PAGE_BYTES","PDF_RASTER_MAX_TOTAL_BYTES","PDF_MAX_PAGES","PDF_MAX_GLYPHS","PDF_MAX_OBSERVED_TEXT_UTF8_BYTES"],"sources":["../src/types.ts","../src/native-codec.ts","../src/local-provider.ts","../src/index.ts"],"sourcesContent":["export type PdfRect = {\n left: number;\n bottom: number;\n right: number;\n top: number;\n};\n\nexport type PdfGlyphObservation = {\n start: number;\n end: number;\n /** Bounds in normalized displayed-page PDF points, origin bottom-left. */\n bounds: PdfRect;\n source: \"embedded-text\" | \"ocr\";\n};\n\nexport type PdfPageObservation = {\n pageIndex: number;\n /** Effective visible width after page boxes, rotation, and UserUnit scaling. */\n widthPoints: number;\n /** Effective visible height after page boxes, rotation, and UserUnit scaling. */\n heightPoints: number;\n text: string;\n glyphs: readonly PdfGlyphObservation[];\n rendered: boolean;\n textLayer: \"absent\" | \"partial\" | \"complete\";\n ocr: \"not-run\" | \"partial\" | \"complete\";\n imageCount: number;\n};\n\nexport type PdfRiskInventory = {\n acroFormFieldCount: number;\n annotationCount: number;\n documentInfoEntryCount: number;\n embeddedFileCount: number;\n externalActionCount: number;\n formXObjectCount: number;\n imageObjectCount: number;\n incrementalRevisionCount: number;\n javascriptActionCount: number;\n metadataStreamCount: number;\n optionalContentGroupCount: number;\n signatureCount: number;\n trailingNonWhitespaceByteCount: number;\n unsupportedActionCount: number;\n xfaEntryCount: number;\n};\n\nexport type PdfInspectionGap =\n | \"encrypted-document\"\n | \"page-content-not-observed\"\n | \"page-not-rendered\"\n | \"partial-text-layer\"\n | \"retained-document-bytes\"\n | \"unobserved-visual-content\";\n\nexport type PdfPageInspection = {\n pageIndex: number;\n /** Effective visible width after page boxes, rotation, and UserUnit scaling. */\n widthPoints: number;\n /** Effective visible height after page boxes, rotation, and UserUnit scaling. */\n heightPoints: number;\n annotationCount: number;\n observation: PdfPageObservation | null;\n};\n\nexport type PdfInspection = {\n contractVersion: 1;\n pdfVersion: string;\n byteLength: number;\n objectCount: number;\n pageCount: number;\n encrypted: boolean;\n pages: readonly PdfPageInspection[];\n risks: PdfRiskInventory;\n coverage: {\n status: \"full\" | \"partial\";\n gaps: readonly PdfInspectionGap[];\n };\n};\n\nexport type PdfRasterProvider = {\n providerId: string;\n rendererName: string;\n rendererVersion: string;\n ocrName: string;\n ocrVersion: string;\n /** One explicit provider language pack; never an implicit mixed-language set. */\n ocrLanguage: string;\n};\n\nexport type PdfRasterPage = {\n observation: PdfPageObservation;\n widthPixels: number;\n heightPixels: number;\n /** Lowercase SHA-256 of this page's exact opaque, row-packed RGB8 bytes. */\n pixelSha256: string;\n /** Strictly ordered, non-overlapping UTF-16 spans. */\n detections: readonly PdfRasterDetection[];\n};\n\nexport type PdfRasterDetection = { start: number; end: number };\n\nexport type PdfRasterRewrite = {\n contractVersion: 1;\n /** Lowercase SHA-256 of the exact source PDF bytes supplied to the call. */\n sourceSha256: string;\n provider: PdfRasterProvider;\n fillRgb: readonly [number, number, number];\n pages: readonly PdfRasterPage[];\n};\n\nexport type PdfRasterRewriteCertificate = {\n contractVersion: 1;\n pageCount: number;\n sourceSha256: string;\n outputSha256: string;\n provider: PdfRasterProvider;\n detectionCount: number;\n mappedRegionCount: number;\n structurePixelRewriteVerified: true;\n providerAssertedCoverage: \"complete-rendering-and-ocr-observation\";\n piiCleanGuaranteed: false;\n limitation: string;\n};\n\nexport type PdfRasterRewriteResult = {\n document: Uint8Array;\n certificate: PdfRasterRewriteCertificate;\n};\n\nexport const PDF_INSPECTION_ERROR_CODES = {\n documentLimitExceeded: \"document-limit-exceeded\",\n invalidDocument: \"invalid-document\",\n invalidObservation: \"invalid-observation\",\n observationLimitExceeded: \"observation-limit-exceeded\",\n providerFailed: \"provider-failed\",\n} as const;\n\nexport type PdfInspectionErrorCode =\n (typeof PDF_INSPECTION_ERROR_CODES)[keyof typeof PDF_INSPECTION_ERROR_CODES];\n\nexport const PDF_RASTER_ERROR_CODES = {\n detectionFailed: \"detection-failed\",\n invalidContract: \"invalid-contract\",\n limitExceeded: \"limit-exceeded\",\n sourceRejected: \"source-rejected\",\n verificationFailed: \"verification-failed\",\n} as const;\n\nexport type PdfRasterErrorCode =\n (typeof PDF_RASTER_ERROR_CODES)[keyof typeof PDF_RASTER_ERROR_CODES];\n","import type {\n PdfGlyphObservation,\n PdfInspection,\n PdfInspectionGap,\n PdfPageInspection,\n PdfPageObservation,\n PdfRasterProvider,\n PdfRasterRewriteCertificate,\n PdfRect,\n PdfRiskInventory,\n} from \"./types\";\n\ntype RequiredFields<T> = { [Key in keyof T]-?: true };\n\nconst RECT_FIELDS = {\n left: true,\n bottom: true,\n right: true,\n top: true,\n} as const satisfies RequiredFields<PdfRect>;\n\nconst GLYPH_FIELDS = {\n start: true,\n end: true,\n bounds: true,\n source: true,\n} as const satisfies RequiredFields<PdfGlyphObservation>;\n\nconst OBSERVATION_FIELDS = {\n pageIndex: true,\n widthPoints: true,\n heightPoints: true,\n text: true,\n glyphs: true,\n rendered: true,\n textLayer: true,\n ocr: true,\n imageCount: true,\n} as const satisfies RequiredFields<PdfPageObservation>;\n\nconst PAGE_FIELDS = {\n pageIndex: true,\n widthPoints: true,\n heightPoints: true,\n annotationCount: true,\n observation: true,\n} as const satisfies RequiredFields<PdfPageInspection>;\n\nconst RISK_FIELDS = {\n acroFormFieldCount: true,\n annotationCount: true,\n documentInfoEntryCount: true,\n embeddedFileCount: true,\n externalActionCount: true,\n formXObjectCount: true,\n imageObjectCount: true,\n incrementalRevisionCount: true,\n javascriptActionCount: true,\n metadataStreamCount: true,\n optionalContentGroupCount: true,\n signatureCount: true,\n trailingNonWhitespaceByteCount: true,\n unsupportedActionCount: true,\n xfaEntryCount: true,\n} as const satisfies RequiredFields<PdfRiskInventory>;\n\nconst INSPECTION_FIELDS = {\n contractVersion: true,\n pdfVersion: true,\n byteLength: true,\n objectCount: true,\n pageCount: true,\n encrypted: true,\n pages: true,\n risks: true,\n coverage: true,\n} as const satisfies RequiredFields<PdfInspection>;\n\nconst PROVIDER_FIELDS = {\n providerId: true,\n rendererName: true,\n rendererVersion: true,\n ocrName: true,\n ocrVersion: true,\n ocrLanguage: true,\n} as const satisfies RequiredFields<PdfRasterProvider>;\n\nconst CERTIFICATE_FIELDS = {\n contractVersion: true,\n pageCount: true,\n sourceSha256: true,\n outputSha256: true,\n provider: true,\n detectionCount: true,\n mappedRegionCount: true,\n structurePixelRewriteVerified: true,\n providerAssertedCoverage: true,\n piiCleanGuaranteed: true,\n limitation: true,\n} as const satisfies RequiredFields<PdfRasterRewriteCertificate>;\n\nconst COVERAGE_FIELDS = {\n status: true,\n gaps: true,\n} as const satisfies RequiredFields<PdfInspection[\"coverage\"]>;\n\nconst INSPECTION_GAPS = {\n \"encrypted-document\": true,\n \"page-content-not-observed\": true,\n \"page-not-rendered\": true,\n \"partial-text-layer\": true,\n \"retained-document-bytes\": true,\n \"unobserved-visual-content\": true,\n} as const satisfies Record<PdfInspectionGap, true>;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst hasExactFields = (\n value: Record<string, unknown>,\n fields: Record<string, true>,\n): boolean => {\n const keys = Object.keys(value);\n return (\n keys.length === Object.keys(fields).length &&\n keys.every((key) => Object.hasOwn(fields, key))\n );\n};\n\nconst isFiniteNumber = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isFinite(value);\n\nconst isNonNegativeInteger = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n\nconst isRect = (value: unknown): value is PdfRect =>\n isRecord(value) &&\n hasExactFields(value, RECT_FIELDS) &&\n isFiniteNumber(value[\"left\"]) &&\n isFiniteNumber(value[\"bottom\"]) &&\n isFiniteNumber(value[\"right\"]) &&\n isFiniteNumber(value[\"top\"]);\n\nconst isGlyph = (value: unknown): value is PdfGlyphObservation =>\n isRecord(value) &&\n hasExactFields(value, GLYPH_FIELDS) &&\n isNonNegativeInteger(value[\"start\"]) &&\n isNonNegativeInteger(value[\"end\"]) &&\n value[\"end\"] >= value[\"start\"] &&\n isRect(value[\"bounds\"]) &&\n (value[\"source\"] === \"embedded-text\" || value[\"source\"] === \"ocr\");\n\nconst isObservation = (value: unknown): value is PdfPageObservation =>\n isRecord(value) &&\n hasExactFields(value, OBSERVATION_FIELDS) &&\n isNonNegativeInteger(value[\"pageIndex\"]) &&\n isFiniteNumber(value[\"widthPoints\"]) &&\n value[\"widthPoints\"] > 0 &&\n isFiniteNumber(value[\"heightPoints\"]) &&\n value[\"heightPoints\"] > 0 &&\n typeof value[\"text\"] === \"string\" &&\n Array.isArray(value[\"glyphs\"]) &&\n value[\"glyphs\"].every(isGlyph) &&\n typeof value[\"rendered\"] === \"boolean\" &&\n (value[\"textLayer\"] === \"absent\" ||\n value[\"textLayer\"] === \"partial\" ||\n value[\"textLayer\"] === \"complete\") &&\n (value[\"ocr\"] === \"not-run\" ||\n value[\"ocr\"] === \"partial\" ||\n value[\"ocr\"] === \"complete\") &&\n isNonNegativeInteger(value[\"imageCount\"]);\n\nconst isPage = (value: unknown): value is PdfPageInspection =>\n isRecord(value) &&\n hasExactFields(value, PAGE_FIELDS) &&\n isNonNegativeInteger(value[\"pageIndex\"]) &&\n isFiniteNumber(value[\"widthPoints\"]) &&\n value[\"widthPoints\"] > 0 &&\n isFiniteNumber(value[\"heightPoints\"]) &&\n value[\"heightPoints\"] > 0 &&\n isNonNegativeInteger(value[\"annotationCount\"]) &&\n (value[\"observation\"] === null || isObservation(value[\"observation\"]));\n\nconst isRiskInventory = (value: unknown): value is PdfRiskInventory =>\n isRecord(value) &&\n hasExactFields(value, RISK_FIELDS) &&\n Object.keys(RISK_FIELDS).every((field) => isNonNegativeInteger(value[field]));\n\nconst isInspectionGap = (value: unknown): value is PdfInspectionGap =>\n typeof value === \"string\" && Object.hasOwn(INSPECTION_GAPS, value);\n\nconst isInspection = (value: unknown): value is PdfInspection =>\n isRecord(value) &&\n hasExactFields(value, INSPECTION_FIELDS) &&\n value[\"contractVersion\"] === 1 &&\n typeof value[\"pdfVersion\"] === \"string\" &&\n isNonNegativeInteger(value[\"byteLength\"]) &&\n isNonNegativeInteger(value[\"objectCount\"]) &&\n isNonNegativeInteger(value[\"pageCount\"]) &&\n typeof value[\"encrypted\"] === \"boolean\" &&\n Array.isArray(value[\"pages\"]) &&\n value[\"pages\"].length === value[\"pageCount\"] &&\n value[\"pages\"].every(isPage) &&\n value[\"pages\"].every(({ pageIndex }, index) => pageIndex === index) &&\n isRiskInventory(value[\"risks\"]) &&\n isRecord(value[\"coverage\"]) &&\n hasExactFields(value[\"coverage\"], COVERAGE_FIELDS) &&\n (value[\"coverage\"][\"status\"] === \"full\" ||\n value[\"coverage\"][\"status\"] === \"partial\") &&\n Array.isArray(value[\"coverage\"][\"gaps\"]) &&\n value[\"coverage\"][\"gaps\"].every(isInspectionGap);\n\nconst isProvider = (value: unknown): value is PdfRasterProvider =>\n isRecord(value) &&\n hasExactFields(value, PROVIDER_FIELDS) &&\n typeof value[\"providerId\"] === \"string\" &&\n typeof value[\"rendererName\"] === \"string\" &&\n typeof value[\"rendererVersion\"] === \"string\" &&\n typeof value[\"ocrName\"] === \"string\" &&\n typeof value[\"ocrVersion\"] === \"string\" &&\n typeof value[\"ocrLanguage\"] === \"string\";\n\nconst isSha256 = (value: unknown): value is string =>\n typeof value === \"string\" && /^[a-f0-9]{64}$/u.test(value);\n\nconst isCertificate = (value: unknown): value is PdfRasterRewriteCertificate =>\n isRecord(value) &&\n hasExactFields(value, CERTIFICATE_FIELDS) &&\n value[\"contractVersion\"] === 1 &&\n isNonNegativeInteger(value[\"pageCount\"]) &&\n isSha256(value[\"sourceSha256\"]) &&\n isSha256(value[\"outputSha256\"]) &&\n isProvider(value[\"provider\"]) &&\n isNonNegativeInteger(value[\"detectionCount\"]) &&\n isNonNegativeInteger(value[\"mappedRegionCount\"]) &&\n value[\"structurePixelRewriteVerified\"] === true &&\n value[\"providerAssertedCoverage\"] ===\n \"complete-rendering-and-ocr-observation\" &&\n value[\"piiCleanGuaranteed\"] === false &&\n typeof value[\"limitation\"] === \"string\";\n\nexport const decodePdfInspection = (json: string): PdfInspection => {\n const value: unknown = JSON.parse(json);\n if (!isInspection(value)) {\n throw new Error(\"Native PDF inspection does not match contract version 1\");\n }\n return value;\n};\n\nexport const decodePdfRasterRewriteCertificate = (\n json: string,\n): PdfRasterRewriteCertificate => {\n const value: unknown = JSON.parse(json);\n if (!isCertificate(value)) {\n throw new Error(\n \"Native PDF raster certificate does not match contract version 1\",\n );\n }\n return value;\n};\n","import { execFile } from \"node:child_process\";\nimport {\n lstat,\n mkdtemp,\n readFile,\n rm,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, sep } from \"node:path\";\n\nimport { loadNativeAnonymizeBinding } from \"@stll/anonymize\";\n\nimport { decodePdfInspection } from \"./native-codec\";\nimport type {\n PdfGlyphObservation,\n PdfPageObservation,\n PdfRasterProvider,\n} from \"./types\";\n\nconst LOCAL_PROVIDER_ID = \"poppler-tesseract-local\";\nconst PROCESS_OUTPUT_MAX_BYTES = 64 * 1024 * 1024;\nconst PDF_DOCUMENT_MAX_BYTES = 64 * 1024 * 1024;\nconst PDF_RASTER_MAX_PAGE_BYTES = 128 * 1024 * 1024;\nconst PDF_RASTER_MAX_TOTAL_BYTES = 512 * 1024 * 1024;\nconst PDF_MAX_PAGES = 10_000;\nconst PDF_MAX_GLYPHS = 5_000_000;\nconst PDF_MAX_OBSERVED_TEXT_UTF8_BYTES = 64 * 1024 * 1024;\nconst DEFAULT_DPI = 300;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst MIN_DPI = 72;\nconst MAX_DPI = 600;\nconst MIN_TIMEOUT_MS = 100;\nconst MAX_TIMEOUT_MS = 300_000;\nconst LANGUAGE_PACK_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/u;\nconst TEMP_PREFIX = \"stella-anonymize-pdf-\";\n\nexport const PDF_LOCAL_PROVIDER_ERROR_CODES = {\n cleanupFailed: \"cleanup-failed\",\n executableFailed: \"executable-failed\",\n invalidOptions: \"invalid-options\",\n invalidOutput: \"invalid-output\",\n limitExceeded: \"limit-exceeded\",\n sourceRejected: \"source-rejected\",\n} as const;\n\nexport type PdfLocalProviderErrorCode =\n (typeof PDF_LOCAL_PROVIDER_ERROR_CODES)[keyof typeof PDF_LOCAL_PROVIDER_ERROR_CODES];\n\nexport class PdfLocalProviderError extends Error {\n readonly code: PdfLocalProviderErrorCode;\n\n constructor(code: PdfLocalProviderErrorCode, message: string) {\n super(message);\n this.name = \"PdfLocalProviderError\";\n this.code = code;\n }\n}\n\nexport type RenderPdfWithPopplerTesseractOptions = {\n document: Uint8Array;\n /** One explicit installed Tesseract traineddata name, for example `eng`. */\n ocrLanguage: string;\n dpi?: number | undefined;\n pdftoppmPath?: string | undefined;\n tesseractPath?: string | undefined;\n timeoutMs?: number | undefined;\n};\n\nexport type LocalPdfRasterPage = {\n observation: PdfPageObservation;\n widthPixels: number;\n heightPixels: number;\n pixels: Uint8Array;\n};\n\nexport type LocalPdfRasterObservation = {\n provider: PdfRasterProvider;\n pages: readonly LocalPdfRasterPage[];\n};\n\ntype ResolvedLocalProviderOptions = {\n dpi: number;\n ocrLanguage: string;\n pdftoppmPath: string;\n tesseractPath: string;\n timeoutMs: number;\n};\n\ntype ProcessResult = { stdout: Buffer; stderr: Buffer };\n\nconst providerError = (\n code: PdfLocalProviderErrorCode,\n message: string,\n): PdfLocalProviderError =>\n new PdfLocalProviderError(code, `${code}: ${message}`);\n\nconst validateOptions = (\n options: RenderPdfWithPopplerTesseractOptions,\n): ResolvedLocalProviderOptions => {\n const dpi = options.dpi ?? DEFAULT_DPI;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const pdftoppmPath = options.pdftoppmPath ?? \"pdftoppm\";\n const tesseractPath = options.tesseractPath ?? \"tesseract\";\n if (\n !Number.isInteger(dpi) ||\n dpi < MIN_DPI ||\n dpi > MAX_DPI ||\n !Number.isInteger(timeoutMs) ||\n timeoutMs < MIN_TIMEOUT_MS ||\n timeoutMs > MAX_TIMEOUT_MS ||\n !LANGUAGE_PACK_PATTERN.test(options.ocrLanguage) ||\n !pdftoppmPath ||\n pdftoppmPath.includes(\"\\0\") ||\n !tesseractPath ||\n tesseractPath.includes(\"\\0\")\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOptions,\n \"local PDF provider options are invalid\",\n );\n }\n return {\n dpi,\n ocrLanguage: options.ocrLanguage,\n pdftoppmPath,\n tesseractPath,\n timeoutMs,\n };\n};\n\nconst runExecutable = (\n executable: string,\n args: readonly string[],\n timeoutMs: number,\n stage: \"OCR\" | \"renderer\" | \"version probe\",\n): Promise<ProcessResult> =>\n new Promise((resolve, reject) => {\n execFile(\n executable,\n args,\n {\n encoding: \"buffer\",\n maxBuffer: PROCESS_OUTPUT_MAX_BYTES,\n timeout: timeoutMs,\n windowsHide: true,\n },\n (error, stdout, stderr) => {\n if (error !== null) {\n reject(\n providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.executableFailed,\n `local PDF ${stage} failed`,\n ),\n );\n return;\n }\n resolve({ stdout: Buffer.from(stdout), stderr: Buffer.from(stderr) });\n },\n );\n });\n\nconst decodeUtf8 = (value: Uint8Array, context: string): string => {\n try {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(value);\n } catch {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n `local PDF ${context} is not valid UTF-8`,\n );\n }\n};\n\nconst versionFrom = (\n value: Uint8Array,\n prefix: string,\n context: string,\n): string => {\n const line = decodeUtf8(value, context).split(/\\r?\\n/u, 1)[0]?.trim();\n const version = line?.startsWith(prefix)\n ? line.slice(prefix.length).trim()\n : \"\";\n if (!version || version.length > 256 || /\\p{Cc}/u.test(version)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n `local PDF ${context} is invalid`,\n );\n }\n return version;\n};\n\ntype InspectedPageGeometry = {\n heightPoints: number;\n widthPoints: number;\n};\n\nconst inspectPageGeometry = (\n document: Uint8Array,\n): readonly InspectedPageGeometry[] => {\n if (document.byteLength > PDF_DOCUMENT_MAX_BYTES) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"PDF source exceeds its byte limit\",\n );\n }\n const inspect = loadNativeAnonymizeBinding().inspectPdfJson;\n if (inspect === undefined) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.sourceRejected,\n \"native PDF inspection is unavailable\",\n );\n }\n try {\n const inspection = decodePdfInspection(inspect(document));\n if (\n inspection.encrypted === true ||\n inspection.pageCount < 1 ||\n inspection.pageCount > PDF_MAX_PAGES\n ) {\n throw new Error(\"source rejected\");\n }\n return inspection.pages.map(({ heightPoints, widthPoints }) => ({\n heightPoints,\n widthPoints,\n }));\n } catch {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.sourceRejected,\n \"PDF source is unsupported by the local raster provider\",\n );\n }\n};\n\nconst assertProjectedRasterLimits = (\n pages: readonly InspectedPageGeometry[],\n dpi: number,\n): void => {\n let totalBytes = 0;\n for (const { heightPoints, widthPoints } of pages) {\n const widthPixels = Math.ceil((widthPoints * dpi) / 72);\n const heightPixels = Math.ceil((heightPoints * dpi) / 72);\n const pageBytes = widthPixels * heightPixels * 3;\n totalBytes += pageBytes;\n if (\n !Number.isSafeInteger(widthPixels) ||\n !Number.isSafeInteger(heightPixels) ||\n widthPixels <= 0 ||\n heightPixels <= 0 ||\n !Number.isSafeInteger(pageBytes) ||\n pageBytes > PDF_RASTER_MAX_PAGE_BYTES ||\n !Number.isSafeInteger(totalBytes) ||\n totalBytes > PDF_RASTER_MAX_TOTAL_BYTES\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"PDF page geometry exceeds the selected DPI raster limits\",\n );\n }\n }\n};\n\ntype PpmPage = {\n width: number;\n height: number;\n pixels: Uint8Array;\n};\n\nconst isWhitespace = (byte: number): boolean =>\n byte === 0x09 ||\n byte === 0x0a ||\n byte === 0x0b ||\n byte === 0x0c ||\n byte === 0x0d ||\n byte === 0x20;\n\nconst ppmToken = (bytes: Uint8Array, start: number): [string, number] => {\n let offset = start;\n while (offset < bytes.length) {\n if (isWhitespace(bytes[offset] ?? -1)) {\n offset += 1;\n continue;\n }\n if (bytes[offset] === 0x23) {\n while (offset < bytes.length && bytes[offset] !== 0x0a) offset += 1;\n continue;\n }\n break;\n }\n const tokenStart = offset;\n while (\n offset < bytes.length &&\n !isWhitespace(bytes[offset] ?? -1) &&\n bytes[offset] !== 0x23\n ) {\n offset += 1;\n }\n if (tokenStart === offset) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler emitted an invalid PPM header\",\n );\n }\n const token = Buffer.from(bytes.subarray(tokenStart, offset)).toString(\n \"ascii\",\n );\n if (!/^[\\x21-\\x7e]+$/u.test(token)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler emitted an invalid PPM header\",\n );\n }\n return [token, offset];\n};\n\nconst parsePositiveInteger = (value: string): number => {\n if (!/^[1-9][0-9]*$/u.test(value)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler emitted invalid PPM dimensions\",\n );\n }\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Poppler PPM dimensions exceed their limit\",\n );\n }\n return parsed;\n};\n\nconst parsePpm = (bytes: Uint8Array): PpmPage => {\n const [magic, afterMagic] = ppmToken(bytes, 0);\n const [widthValue, afterWidth] = ppmToken(bytes, afterMagic);\n const [heightValue, afterHeight] = ppmToken(bytes, afterWidth);\n const [maximumValue, afterMaximum] = ppmToken(bytes, afterHeight);\n if (magic !== \"P6\" || maximumValue !== \"255\") {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler must emit binary RGB8 PPM pages\",\n );\n }\n const width = parsePositiveInteger(widthValue);\n const height = parsePositiveInteger(heightValue);\n const expected = width * height * 3;\n if (!Number.isSafeInteger(expected) || expected > PDF_RASTER_MAX_PAGE_BYTES) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Poppler page pixels exceed their byte limit\",\n );\n }\n let pixelOffset = afterMaximum;\n if (!isWhitespace(bytes[pixelOffset] ?? -1)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler emitted an invalid PPM pixel separator\",\n );\n }\n const separator = bytes[pixelOffset];\n pixelOffset += 1;\n if (separator === 0x0d && bytes[pixelOffset] === 0x0a) pixelOffset += 1;\n if (bytes.length - pixelOffset !== expected) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler emitted a truncated or trailing PPM page\",\n );\n }\n return { width, height, pixels: bytes.subarray(pixelOffset) };\n};\n\nconst boundedRenderedPage = async (path: string): Promise<PpmPage> => {\n const metadata = await lstat(path);\n if (!metadata.isFile() || metadata.isSymbolicLink()) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler page output must be a regular file\",\n );\n }\n if (metadata.size > PDF_RASTER_MAX_PAGE_BYTES + 1024 * 1024) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Poppler page output exceeds its byte limit\",\n );\n }\n return parsePpm(await readFile(path));\n};\n\nconst integerTsvField = (value: string | undefined): number => {\n if (value === undefined || !/^(?:0|[1-9][0-9]*)$/u.test(value)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Tesseract emitted invalid TSV geometry\",\n );\n }\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Tesseract TSV geometry exceeds its limit\",\n );\n }\n return parsed;\n};\n\nconst parseTesseractTsv = (\n bytes: Uint8Array,\n page: PpmPage,\n pageGeometry: InspectedPageGeometry,\n pageIndex: number,\n): PdfPageObservation => {\n const lines = decodeUtf8(bytes, \"Tesseract TSV\").split(/\\r?\\n/u);\n const header = lines.shift()?.split(\"\\t\");\n if (\n header?.slice(0, 12).join(\"\\t\") !==\n \"level\\tpage_num\\tblock_num\\tpar_num\\tline_num\\tword_num\\tleft\\ttop\\twidth\\theight\\tconf\\ttext\"\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Tesseract emitted an unsupported TSV header\",\n );\n }\n const { heightPoints, widthPoints } = pageGeometry;\n const glyphs: PdfGlyphObservation[] = [];\n let text = \"\";\n let previousLine = \"\";\n for (const line of lines) {\n if (!line) continue;\n const fields = line.split(\"\\t\");\n if (fields[0] !== \"5\") continue;\n const word = fields.slice(11).join(\"\\t\").trim();\n if (!word) continue;\n if (/\\p{Cc}/u.test(word)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Tesseract emitted control characters in OCR text\",\n );\n }\n const lineKey = `${fields[2] ?? \"\"}/${fields[3] ?? \"\"}/${fields[4] ?? \"\"}`;\n if (text) text += lineKey === previousLine ? \" \" : \"\\n\";\n previousLine = lineKey;\n const start = text.length;\n text += word;\n const end = text.length;\n const left = integerTsvField(fields[6]);\n const top = integerTsvField(fields[7]);\n const width = integerTsvField(fields[8]);\n const height = integerTsvField(fields[9]);\n const right = left + width;\n const bottom = top + height;\n if (\n width === 0 ||\n height === 0 ||\n !Number.isSafeInteger(right) ||\n !Number.isSafeInteger(bottom) ||\n right > page.width ||\n bottom > page.height\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Tesseract emitted out-of-page OCR geometry\",\n );\n }\n glyphs.push({\n start,\n end,\n bounds: {\n left: (left * widthPoints) / page.width,\n bottom: heightPoints - (bottom * heightPoints) / page.height,\n right: (right * widthPoints) / page.width,\n top: heightPoints - (top * heightPoints) / page.height,\n },\n source: \"ocr\",\n });\n if (glyphs.length > PDF_MAX_GLYPHS) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Tesseract OCR glyph count exceeds its limit\",\n );\n }\n if (Buffer.byteLength(text, \"utf8\") > PDF_MAX_OBSERVED_TEXT_UTF8_BYTES) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Tesseract OCR text exceeds its byte limit\",\n );\n }\n }\n return {\n pageIndex,\n widthPoints,\n heightPoints,\n text,\n glyphs,\n rendered: true,\n textLayer: \"absent\",\n ocr: \"complete\",\n imageCount: 0,\n };\n};\n\nconst safeCleanup = async (directory: string): Promise<void> => {\n const expectedPrefix = `${tmpdir()}${sep}${TEMP_PREFIX}`;\n if (!directory.startsWith(expectedPrefix)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.cleanupFailed,\n \"local PDF temporary directory could not be verified\",\n );\n }\n try {\n const metadata = await lstat(directory);\n if (!metadata.isDirectory() || metadata.isSymbolicLink()) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.cleanupFailed,\n \"local PDF temporary directory could not be verified\",\n );\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return;\n if (error instanceof PdfLocalProviderError) throw error;\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.cleanupFailed,\n \"local PDF temporary directory could not be inspected\",\n );\n }\n try {\n await rm(directory, { force: true, recursive: true });\n } catch {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.cleanupFailed,\n \"local PDF temporary files could not be removed\",\n );\n }\n};\n\nexport const renderPdfWithPopplerTesseract = async (\n options: RenderPdfWithPopplerTesseractOptions,\n): Promise<LocalPdfRasterObservation> => {\n const resolved = validateOptions(options);\n const pageGeometry = inspectPageGeometry(options.document);\n assertProjectedRasterLimits(pageGeometry, resolved.dpi);\n const pageCount = pageGeometry.length;\n const popplerVersionResult = await runExecutable(\n resolved.pdftoppmPath,\n [\"-v\"],\n resolved.timeoutMs,\n \"version probe\",\n );\n const tesseractVersionResult = await runExecutable(\n resolved.tesseractPath,\n [\"--version\"],\n resolved.timeoutMs,\n \"version probe\",\n );\n const rendererVersion = versionFrom(\n popplerVersionResult.stderr.length > 0\n ? popplerVersionResult.stderr\n : popplerVersionResult.stdout,\n \"pdftoppm version \",\n \"Poppler version\",\n );\n const ocrVersion = versionFrom(\n tesseractVersionResult.stdout.length > 0\n ? tesseractVersionResult.stdout\n : tesseractVersionResult.stderr,\n \"tesseract \",\n \"Tesseract version\",\n );\n const directory = await mkdtemp(join(tmpdir(), TEMP_PREFIX));\n try {\n const sourcePath = join(directory, \"source.pdf\");\n const outputPrefix = join(directory, \"page\");\n await writeFile(sourcePath, options.document, { flag: \"wx\", mode: 0o600 });\n const pages: LocalPdfRasterPage[] = [];\n let totalPixels = 0;\n let totalGlyphs = 0;\n let totalObservedTextUtf8Bytes = 0;\n for (let pageNumber = 1; pageNumber <= pageCount; pageNumber += 1) {\n const inspectedGeometry = pageGeometry.at(pageNumber - 1);\n if (inspectedGeometry === undefined) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.sourceRejected,\n \"PDF page geometry became unavailable\",\n );\n }\n const pageOutputPrefix = `${outputPrefix}-${pageNumber}`;\n const pagePath = `${pageOutputPrefix}.ppm`;\n await runExecutable(\n resolved.pdftoppmPath,\n [\n \"-cropbox\",\n \"-f\",\n String(pageNumber),\n \"-l\",\n String(pageNumber),\n \"-singlefile\",\n \"-r\",\n String(resolved.dpi),\n sourcePath,\n pageOutputPrefix,\n ],\n resolved.timeoutMs,\n \"renderer\",\n );\n try {\n const page = await boundedRenderedPage(pagePath);\n totalPixels += page.pixels.byteLength;\n if (\n !Number.isSafeInteger(totalPixels) ||\n totalPixels > PDF_RASTER_MAX_TOTAL_BYTES\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Poppler page pixels exceed their aggregate limit\",\n );\n }\n const ocr = await runExecutable(\n resolved.tesseractPath,\n [pagePath, \"stdout\", \"-l\", resolved.ocrLanguage, \"tsv\"],\n resolved.timeoutMs,\n \"OCR\",\n );\n const observation = parseTesseractTsv(\n ocr.stdout,\n page,\n inspectedGeometry,\n pageNumber - 1,\n );\n totalGlyphs += observation.glyphs.length;\n totalObservedTextUtf8Bytes += Buffer.byteLength(\n observation.text,\n \"utf8\",\n );\n if (\n !Number.isSafeInteger(totalGlyphs) ||\n totalGlyphs > PDF_MAX_GLYPHS ||\n !Number.isSafeInteger(totalObservedTextUtf8Bytes) ||\n totalObservedTextUtf8Bytes > PDF_MAX_OBSERVED_TEXT_UTF8_BYTES\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Tesseract OCR document output exceeds its aggregate limit\",\n );\n }\n pages.push({\n observation,\n widthPixels: page.width,\n heightPixels: page.height,\n pixels: page.pixels,\n });\n } finally {\n await unlink(pagePath).catch((error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") return;\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.cleanupFailed,\n \"local PDF page raster could not be removed\",\n );\n });\n }\n }\n return {\n provider: {\n providerId: LOCAL_PROVIDER_ID,\n rendererName: \"Poppler pdftoppm\",\n rendererVersion,\n ocrName: \"Tesseract OCR\",\n ocrVersion,\n ocrLanguage: resolved.ocrLanguage,\n },\n pages,\n };\n } finally {\n await safeCleanup(directory);\n }\n};\n","import { createHash } from \"node:crypto\";\n\nimport {\n convert_external_detection_batch,\n loadNativeAnonymizeBinding,\n type ExternalDetectionBatch,\n type PreparedNativePipeline,\n} from \"@stll/anonymize\";\n\nimport {\n PDF_INSPECTION_ERROR_CODES,\n PDF_RASTER_ERROR_CODES,\n type PdfInspection,\n type PdfInspectionErrorCode,\n type PdfPageObservation,\n type PdfRasterProvider,\n type PdfRasterErrorCode,\n type PdfRasterRewrite,\n type PdfRasterRewriteResult,\n} from \"./types\";\nimport {\n decodePdfInspection,\n decodePdfRasterRewriteCertificate,\n} from \"./native-codec\";\n\nexport const PDF_INSPECTION_CONTRACT_VERSION = 1 as const;\nexport const PDF_RASTER_CONTRACT_VERSION = 1 as const;\nexport const PDF_RASTER_MAX_PAGE_BYTES = 128 * 1024 * 1024;\nexport const PDF_RASTER_MAX_TOTAL_BYTES = 512 * 1024 * 1024;\nexport const PDF_RASTER_MAX_OUTPUT_BYTES = 512 * 1024 * 1024;\nexport const PDF_RASTER_REQUEST_JSON_MAX_BYTES = 64 * 1024 * 1024;\nexport const PDF_DOCUMENT_MAX_BYTES = 64 * 1024 * 1024;\nexport const PDF_STREAM_DECOMPRESSED_MAX_BYTES = 32 * 1024 * 1024;\nexport const PDF_LOADED_PAYLOAD_MAX_BYTES = 128 * 1024 * 1024;\nexport const PDF_MAX_OBJECTS = 200_000;\nexport const PDF_MAX_OBJECT_NODES = 1_000_000;\nexport const PDF_MAX_OBJECT_DEPTH = 128;\nexport const PDF_MAX_PAGES = 10_000;\nexport const PDF_MAX_GLYPHS = 5_000_000;\nexport const PDF_MAX_PAGE_TEXT_UTF8_BYTES = 16 * 1024 * 1024;\nexport const PDF_MAX_OBSERVED_TEXT_UTF8_BYTES = 64 * 1024 * 1024;\nexport const PDF_OBSERVATIONS_JSON_MAX_BYTES = 64 * 1024 * 1024;\n/** Renderer dimensions may differ by this many points due to numeric rounding. */\nexport const PDF_PAGE_DIMENSION_TOLERANCE_POINTS = 0.25;\n\nexport class PdfInspectionError extends Error {\n readonly code: PdfInspectionErrorCode;\n\n constructor(code: PdfInspectionErrorCode, message: string) {\n super(message);\n this.name = \"PdfInspectionError\";\n this.code = code;\n }\n}\n\nexport class PdfRasterError extends Error {\n readonly code: PdfRasterErrorCode;\n\n constructor(code: PdfRasterErrorCode, message: string) {\n super(message);\n this.name = \"PdfRasterError\";\n this.code = code;\n }\n}\n\nconst errorCode = (message: string): PdfInspectionErrorCode => {\n const code = Object.values(PDF_INSPECTION_ERROR_CODES).find((candidate) =>\n message.startsWith(`${candidate}:`),\n );\n return code ?? PDF_INSPECTION_ERROR_CODES.invalidDocument;\n};\n\nconst rasterErrorCode = (message: string): PdfRasterErrorCode => {\n const code = Object.values(PDF_RASTER_ERROR_CODES).find((candidate) =>\n message.startsWith(`${candidate}:`),\n );\n return code ?? PDF_RASTER_ERROR_CODES.verificationFailed;\n};\n\nexport type InspectPdfOptions = {\n pageObservations?: readonly PdfPageObservation[];\n};\n\nexport const inspectPdf = (\n document: Uint8Array,\n options: InspectPdfOptions = {},\n): PdfInspection => {\n const inspect = loadNativeAnonymizeBinding().inspectPdfJson;\n if (inspect === undefined) {\n throw new PdfInspectionError(\n PDF_INSPECTION_ERROR_CODES.invalidDocument,\n \"Native anonymize binding does not expose PDF inspection\",\n );\n }\n try {\n const observations = options.pageObservations;\n return decodePdfInspection(\n inspect(\n document,\n observations === undefined ? undefined : JSON.stringify(observations),\n ),\n );\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"PDF inspection failed\";\n throw new PdfInspectionError(errorCode(message), message);\n }\n};\n\nexport type RewritePdfRasterFromDetectionsOptions = {\n document: Uint8Array;\n request: PdfRasterRewrite;\n /** One opaque, row-packed RGB8 buffer per request page, in page order. */\n pagePixels: readonly Uint8Array[];\n};\n\n/**\n * Advanced rewrite-only surface. It verifies structure and destructive pixels but\n * does not run detection or certify that the result is PII-free.\n */\nexport const rewritePdfRasterFromDetections = ({\n document,\n request,\n pagePixels,\n}: RewritePdfRasterFromDetectionsOptions): PdfRasterRewriteResult => {\n const rewrite =\n loadNativeAnonymizeBinding().rewritePdfRasterFromDetectionsJson;\n if (rewrite === undefined) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.verificationFailed,\n \"Native anonymize binding does not expose PDF raster rewriting\",\n );\n }\n try {\n const result = rewrite(document, JSON.stringify(request), pagePixels);\n return {\n document: result.document,\n certificate: decodePdfRasterRewriteCertificate(result.certificateJson),\n };\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"PDF raster rewrite failed\";\n throw new PdfRasterError(rasterErrorCode(message), message);\n }\n};\n\nexport type PdfRasterObservedPageInput = {\n observation: PdfPageObservation;\n widthPixels: number;\n heightPixels: number;\n pixels: Uint8Array;\n /** Optional digest-bound provider batch merged through stella caller semantics. */\n externalDetectionBatch?: ExternalDetectionBatch | string | undefined;\n};\n\nexport type AnonymizePdfRasterOptions = {\n document: Uint8Array;\n pipeline: Pick<\n PreparedNativePipeline,\n \"redactText\" | \"redactTextWithCallerDetections\"\n >;\n provider: PdfRasterProvider;\n pages: readonly PdfRasterObservedPageInput[];\n fillRgb?: readonly [number, number, number] | undefined;\n};\n\nconst sha256 = (value: Uint8Array): string =>\n createHash(\"sha256\").update(value).digest(\"hex\");\n\n/** Run stella detection, merge optional external detections, then rewrite pixels. */\nexport const anonymizePdfRaster = ({\n document,\n pipeline,\n provider,\n pages,\n fillRgb = [0, 0, 0],\n}: AnonymizePdfRasterOptions): PdfRasterRewriteResult => {\n if (document.byteLength > PDF_DOCUMENT_MAX_BYTES) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF source exceeds its byte limit\",\n );\n }\n if (pages.length > PDF_MAX_PAGES) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster page count exceeds its limit\",\n );\n }\n let totalPixelBytes = 0;\n let totalObservedTextBytes = 0;\n const textEncoder = new TextEncoder();\n for (const page of pages) {\n if (typeof page.observation?.text !== \"string\") {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.invalidContract,\n \"invalid-contract: PDF raster observed text is invalid\",\n );\n }\n const observedTextBytes = textEncoder.encode(\n page.observation.text,\n ).byteLength;\n if (observedTextBytes > PDF_MAX_PAGE_TEXT_UTF8_BYTES) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster page text exceeds its byte limit\",\n );\n }\n totalObservedTextBytes += observedTextBytes;\n if (totalObservedTextBytes > PDF_MAX_OBSERVED_TEXT_UTF8_BYTES) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster observed text exceeds its aggregate byte limit\",\n );\n }\n const { heightPixels, widthPixels } = page;\n if (\n !Number.isSafeInteger(widthPixels) ||\n !Number.isSafeInteger(heightPixels) ||\n widthPixels <= 0 ||\n heightPixels <= 0\n ) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.invalidContract,\n \"invalid-contract: PDF raster pixel dimensions are invalid\",\n );\n }\n const expected = widthPixels * heightPixels * 3;\n if (\n !Number.isSafeInteger(expected) ||\n expected > PDF_RASTER_MAX_PAGE_BYTES ||\n page.pixels.byteLength !== expected\n ) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster page pixels exceed limits or have an invalid RGB8 length\",\n );\n }\n totalPixelBytes += expected;\n if (\n !Number.isSafeInteger(totalPixelBytes) ||\n totalPixelBytes > PDF_RASTER_MAX_TOTAL_BYTES\n ) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster pixels exceed their aggregate limit\",\n );\n }\n }\n const binding = loadNativeAnonymizeBinding();\n let requestPages: PdfRasterRewrite[\"pages\"];\n try {\n requestPages = pages.map((page) => {\n const external =\n page.externalDetectionBatch === undefined\n ? undefined\n : convert_external_detection_batch(\n new TextEncoder().encode(page.observation.text),\n page.externalDetectionBatch,\n { binding },\n );\n const result =\n external === undefined\n ? pipeline.redactText(page.observation.text)\n : pipeline.redactTextWithCallerDetections(page.observation.text, {\n detections: external,\n });\n return {\n observation: page.observation,\n widthPixels: page.widthPixels,\n heightPixels: page.heightPixels,\n pixelSha256: sha256(page.pixels),\n detections: result.resolvedEntities.map(({ start, end }) => ({\n start,\n end,\n })),\n };\n });\n } catch {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.detectionFailed,\n \"detection-failed: PDF raster detection failed\",\n );\n }\n return rewritePdfRasterFromDetections({\n document,\n request: {\n contractVersion: PDF_RASTER_CONTRACT_VERSION,\n sourceSha256: sha256(document),\n provider,\n fillRgb,\n pages: requestPages,\n },\n pagePixels: pages.map(({ pixels }) => pixels),\n });\n};\n\nexport { PDF_INSPECTION_ERROR_CODES } from \"./types\";\nexport { PDF_RASTER_ERROR_CODES } from \"./types\";\nexport {\n PDF_LOCAL_PROVIDER_ERROR_CODES,\n PdfLocalProviderError,\n renderPdfWithPopplerTesseract,\n} from \"./local-provider\";\nexport type {\n LocalPdfRasterObservation,\n LocalPdfRasterPage,\n PdfLocalProviderErrorCode,\n RenderPdfWithPopplerTesseractOptions,\n} from \"./local-provider\";\nexport type {\n PdfGlyphObservation,\n PdfInspection,\n PdfInspectionErrorCode,\n PdfInspectionGap,\n PdfPageInspection,\n PdfPageObservation,\n PdfRect,\n PdfRiskInventory,\n PdfRasterDetection,\n PdfRasterErrorCode,\n PdfRasterPage,\n PdfRasterProvider,\n PdfRasterRewrite,\n PdfRasterRewriteCertificate,\n PdfRasterRewriteResult,\n} from \"./types\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["PDF_DOCUMENT_MAX_BYTES","PDF_RASTER_MAX_PAGE_BYTES","PDF_RASTER_MAX_TOTAL_BYTES","PDF_MAX_PAGES","PDF_MAX_GLYPHS","PDF_MAX_OBSERVED_TEXT_UTF8_BYTES"],"sources":["../src/types.ts","../src/native-codec.ts","../src/local-provider.ts","../src/index.ts"],"sourcesContent":["export type PdfRect = {\n left: number;\n bottom: number;\n right: number;\n top: number;\n};\n\nexport type PdfGlyphObservation = {\n start: number;\n end: number;\n /** Bounds in normalized displayed-page PDF points, origin bottom-left. */\n bounds: PdfRect;\n source: \"embedded-text\" | \"ocr\";\n};\n\nexport type PdfPageObservation = {\n pageIndex: number;\n /** Effective visible width after page boxes, rotation, and UserUnit scaling. */\n widthPoints: number;\n /** Effective visible height after page boxes, rotation, and UserUnit scaling. */\n heightPoints: number;\n text: string;\n glyphs: readonly PdfGlyphObservation[];\n rendered: boolean;\n textLayer: \"absent\" | \"partial\" | \"complete\";\n ocr: \"not-run\" | \"partial\" | \"complete\";\n imageCount: number;\n};\n\nexport type PdfRiskInventory = {\n acroFormFieldCount: number;\n annotationCount: number;\n documentInfoEntryCount: number;\n embeddedFileCount: number;\n externalActionCount: number;\n formXObjectCount: number;\n imageObjectCount: number;\n incrementalRevisionCount: number;\n javascriptActionCount: number;\n metadataStreamCount: number;\n optionalContentGroupCount: number;\n signatureCount: number;\n trailingNonWhitespaceByteCount: number;\n unsupportedActionCount: number;\n xfaEntryCount: number;\n};\n\nexport type PdfInspectionGap =\n | \"encrypted-document\"\n | \"page-content-not-observed\"\n | \"page-not-rendered\"\n | \"partial-text-layer\"\n | \"retained-document-bytes\"\n | \"unobserved-visual-content\";\n\nexport type PdfPageInspection = {\n pageIndex: number;\n /** Effective visible width after page boxes, rotation, and UserUnit scaling. */\n widthPoints: number;\n /** Effective visible height after page boxes, rotation, and UserUnit scaling. */\n heightPoints: number;\n annotationCount: number;\n observation: PdfPageObservation | null;\n};\n\nexport type PdfInspection = {\n contractVersion: 1;\n pdfVersion: string;\n byteLength: number;\n objectCount: number;\n pageCount: number;\n encrypted: boolean;\n pages: readonly PdfPageInspection[];\n risks: PdfRiskInventory;\n coverage: {\n status: \"full\" | \"partial\";\n gaps: readonly PdfInspectionGap[];\n };\n};\n\nexport type PdfRasterProvider = {\n providerId: string;\n rendererName: string;\n rendererVersion: string;\n ocrName: string;\n ocrVersion: string;\n /** One explicit provider language pack; never an implicit mixed-language set. */\n ocrLanguage: string;\n};\n\nexport type PdfRasterPage = {\n observation: PdfPageObservation;\n widthPixels: number;\n heightPixels: number;\n /** Lowercase SHA-256 of this page's exact opaque, row-packed RGB8 bytes. */\n pixelSha256: string;\n /** Strictly ordered, non-overlapping UTF-16 spans. */\n detections: readonly PdfRasterDetection[];\n /** User-reviewed regions in normalized displayed-page PDF points. */\n manualRegions?: readonly PdfRect[];\n};\n\nexport type PdfRasterDetection = { start: number; end: number };\n\nexport type PdfRasterRewrite = {\n contractVersion: 1;\n /** Lowercase SHA-256 of the exact source PDF bytes supplied to the call. */\n sourceSha256: string;\n provider: PdfRasterProvider;\n fillRgb: readonly [number, number, number];\n pages: readonly PdfRasterPage[];\n};\n\nexport const PDF_RASTER_CERTIFICATE_CONTRACT_VERSION = 2 as const;\n\nexport type PdfRasterRewriteCertificate = {\n contractVersion: typeof PDF_RASTER_CERTIFICATE_CONTRACT_VERSION;\n pageCount: number;\n sourceSha256: string;\n outputSha256: string;\n provider: PdfRasterProvider;\n detectionCount: number;\n manualRegionCount: number;\n mappedRegionCount: number;\n structurePixelRewriteVerified: true;\n providerAssertedCoverage: \"complete-rendering-and-ocr-observation\";\n piiCleanGuaranteed: false;\n limitation: string;\n};\n\nexport type PdfRasterRewriteResult = {\n document: Uint8Array;\n certificate: PdfRasterRewriteCertificate;\n};\n\nexport const PDF_INSPECTION_ERROR_CODES = {\n documentLimitExceeded: \"document-limit-exceeded\",\n invalidDocument: \"invalid-document\",\n invalidObservation: \"invalid-observation\",\n observationLimitExceeded: \"observation-limit-exceeded\",\n providerFailed: \"provider-failed\",\n} as const;\n\nexport type PdfInspectionErrorCode =\n (typeof PDF_INSPECTION_ERROR_CODES)[keyof typeof PDF_INSPECTION_ERROR_CODES];\n\nexport const PDF_RASTER_ERROR_CODES = {\n detectionFailed: \"detection-failed\",\n invalidContract: \"invalid-contract\",\n limitExceeded: \"limit-exceeded\",\n sourceRejected: \"source-rejected\",\n verificationFailed: \"verification-failed\",\n} as const;\n\nexport type PdfRasterErrorCode =\n (typeof PDF_RASTER_ERROR_CODES)[keyof typeof PDF_RASTER_ERROR_CODES];\n","import { PDF_RASTER_CERTIFICATE_CONTRACT_VERSION } from \"./types\";\nimport type {\n PdfGlyphObservation,\n PdfInspection,\n PdfInspectionGap,\n PdfPageInspection,\n PdfPageObservation,\n PdfRasterProvider,\n PdfRasterRewriteCertificate,\n PdfRect,\n PdfRiskInventory,\n} from \"./types\";\n\ntype RequiredFields<T> = { [Key in keyof T]-?: true };\n\nconst RECT_FIELDS = {\n left: true,\n bottom: true,\n right: true,\n top: true,\n} as const satisfies RequiredFields<PdfRect>;\n\nconst GLYPH_FIELDS = {\n start: true,\n end: true,\n bounds: true,\n source: true,\n} as const satisfies RequiredFields<PdfGlyphObservation>;\n\nconst OBSERVATION_FIELDS = {\n pageIndex: true,\n widthPoints: true,\n heightPoints: true,\n text: true,\n glyphs: true,\n rendered: true,\n textLayer: true,\n ocr: true,\n imageCount: true,\n} as const satisfies RequiredFields<PdfPageObservation>;\n\nconst PAGE_FIELDS = {\n pageIndex: true,\n widthPoints: true,\n heightPoints: true,\n annotationCount: true,\n observation: true,\n} as const satisfies RequiredFields<PdfPageInspection>;\n\nconst RISK_FIELDS = {\n acroFormFieldCount: true,\n annotationCount: true,\n documentInfoEntryCount: true,\n embeddedFileCount: true,\n externalActionCount: true,\n formXObjectCount: true,\n imageObjectCount: true,\n incrementalRevisionCount: true,\n javascriptActionCount: true,\n metadataStreamCount: true,\n optionalContentGroupCount: true,\n signatureCount: true,\n trailingNonWhitespaceByteCount: true,\n unsupportedActionCount: true,\n xfaEntryCount: true,\n} as const satisfies RequiredFields<PdfRiskInventory>;\n\nconst INSPECTION_FIELDS = {\n contractVersion: true,\n pdfVersion: true,\n byteLength: true,\n objectCount: true,\n pageCount: true,\n encrypted: true,\n pages: true,\n risks: true,\n coverage: true,\n} as const satisfies RequiredFields<PdfInspection>;\n\nconst PROVIDER_FIELDS = {\n providerId: true,\n rendererName: true,\n rendererVersion: true,\n ocrName: true,\n ocrVersion: true,\n ocrLanguage: true,\n} as const satisfies RequiredFields<PdfRasterProvider>;\n\nconst CERTIFICATE_FIELDS = {\n contractVersion: true,\n pageCount: true,\n sourceSha256: true,\n outputSha256: true,\n provider: true,\n detectionCount: true,\n manualRegionCount: true,\n mappedRegionCount: true,\n structurePixelRewriteVerified: true,\n providerAssertedCoverage: true,\n piiCleanGuaranteed: true,\n limitation: true,\n} as const satisfies RequiredFields<PdfRasterRewriteCertificate>;\n\nconst COVERAGE_FIELDS = {\n status: true,\n gaps: true,\n} as const satisfies RequiredFields<PdfInspection[\"coverage\"]>;\n\nconst INSPECTION_GAPS = {\n \"encrypted-document\": true,\n \"page-content-not-observed\": true,\n \"page-not-rendered\": true,\n \"partial-text-layer\": true,\n \"retained-document-bytes\": true,\n \"unobserved-visual-content\": true,\n} as const satisfies Record<PdfInspectionGap, true>;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst hasExactFields = (\n value: Record<string, unknown>,\n fields: Record<string, true>,\n): boolean => {\n const keys = Object.keys(value);\n return (\n keys.length === Object.keys(fields).length &&\n keys.every((key) => Object.hasOwn(fields, key))\n );\n};\n\nconst isFiniteNumber = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isFinite(value);\n\nconst isNonNegativeInteger = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n\nconst isRect = (value: unknown): value is PdfRect =>\n isRecord(value) &&\n hasExactFields(value, RECT_FIELDS) &&\n isFiniteNumber(value[\"left\"]) &&\n isFiniteNumber(value[\"bottom\"]) &&\n isFiniteNumber(value[\"right\"]) &&\n isFiniteNumber(value[\"top\"]);\n\nconst isGlyph = (value: unknown): value is PdfGlyphObservation =>\n isRecord(value) &&\n hasExactFields(value, GLYPH_FIELDS) &&\n isNonNegativeInteger(value[\"start\"]) &&\n isNonNegativeInteger(value[\"end\"]) &&\n value[\"end\"] >= value[\"start\"] &&\n isRect(value[\"bounds\"]) &&\n (value[\"source\"] === \"embedded-text\" || value[\"source\"] === \"ocr\");\n\nconst isObservation = (value: unknown): value is PdfPageObservation =>\n isRecord(value) &&\n hasExactFields(value, OBSERVATION_FIELDS) &&\n isNonNegativeInteger(value[\"pageIndex\"]) &&\n isFiniteNumber(value[\"widthPoints\"]) &&\n value[\"widthPoints\"] > 0 &&\n isFiniteNumber(value[\"heightPoints\"]) &&\n value[\"heightPoints\"] > 0 &&\n typeof value[\"text\"] === \"string\" &&\n Array.isArray(value[\"glyphs\"]) &&\n value[\"glyphs\"].every(isGlyph) &&\n typeof value[\"rendered\"] === \"boolean\" &&\n (value[\"textLayer\"] === \"absent\" ||\n value[\"textLayer\"] === \"partial\" ||\n value[\"textLayer\"] === \"complete\") &&\n (value[\"ocr\"] === \"not-run\" ||\n value[\"ocr\"] === \"partial\" ||\n value[\"ocr\"] === \"complete\") &&\n isNonNegativeInteger(value[\"imageCount\"]);\n\nconst isPage = (value: unknown): value is PdfPageInspection =>\n isRecord(value) &&\n hasExactFields(value, PAGE_FIELDS) &&\n isNonNegativeInteger(value[\"pageIndex\"]) &&\n isFiniteNumber(value[\"widthPoints\"]) &&\n value[\"widthPoints\"] > 0 &&\n isFiniteNumber(value[\"heightPoints\"]) &&\n value[\"heightPoints\"] > 0 &&\n isNonNegativeInteger(value[\"annotationCount\"]) &&\n (value[\"observation\"] === null || isObservation(value[\"observation\"]));\n\nconst isRiskInventory = (value: unknown): value is PdfRiskInventory =>\n isRecord(value) &&\n hasExactFields(value, RISK_FIELDS) &&\n Object.keys(RISK_FIELDS).every((field) => isNonNegativeInteger(value[field]));\n\nconst isInspectionGap = (value: unknown): value is PdfInspectionGap =>\n typeof value === \"string\" && Object.hasOwn(INSPECTION_GAPS, value);\n\nconst isInspection = (value: unknown): value is PdfInspection =>\n isRecord(value) &&\n hasExactFields(value, INSPECTION_FIELDS) &&\n value[\"contractVersion\"] === 1 &&\n typeof value[\"pdfVersion\"] === \"string\" &&\n isNonNegativeInteger(value[\"byteLength\"]) &&\n isNonNegativeInteger(value[\"objectCount\"]) &&\n isNonNegativeInteger(value[\"pageCount\"]) &&\n typeof value[\"encrypted\"] === \"boolean\" &&\n Array.isArray(value[\"pages\"]) &&\n value[\"pages\"].length === value[\"pageCount\"] &&\n value[\"pages\"].every(isPage) &&\n value[\"pages\"].every(({ pageIndex }, index) => pageIndex === index) &&\n isRiskInventory(value[\"risks\"]) &&\n isRecord(value[\"coverage\"]) &&\n hasExactFields(value[\"coverage\"], COVERAGE_FIELDS) &&\n (value[\"coverage\"][\"status\"] === \"full\" ||\n value[\"coverage\"][\"status\"] === \"partial\") &&\n Array.isArray(value[\"coverage\"][\"gaps\"]) &&\n value[\"coverage\"][\"gaps\"].every(isInspectionGap);\n\nconst isProvider = (value: unknown): value is PdfRasterProvider =>\n isRecord(value) &&\n hasExactFields(value, PROVIDER_FIELDS) &&\n typeof value[\"providerId\"] === \"string\" &&\n typeof value[\"rendererName\"] === \"string\" &&\n typeof value[\"rendererVersion\"] === \"string\" &&\n typeof value[\"ocrName\"] === \"string\" &&\n typeof value[\"ocrVersion\"] === \"string\" &&\n typeof value[\"ocrLanguage\"] === \"string\";\n\nconst isSha256 = (value: unknown): value is string =>\n typeof value === \"string\" && /^[a-f0-9]{64}$/u.test(value);\n\nconst isCertificate = (value: unknown): value is PdfRasterRewriteCertificate =>\n isRecord(value) &&\n hasExactFields(value, CERTIFICATE_FIELDS) &&\n value[\"contractVersion\"] === PDF_RASTER_CERTIFICATE_CONTRACT_VERSION &&\n isNonNegativeInteger(value[\"pageCount\"]) &&\n isSha256(value[\"sourceSha256\"]) &&\n isSha256(value[\"outputSha256\"]) &&\n isProvider(value[\"provider\"]) &&\n isNonNegativeInteger(value[\"detectionCount\"]) &&\n isNonNegativeInteger(value[\"manualRegionCount\"]) &&\n isNonNegativeInteger(value[\"mappedRegionCount\"]) &&\n value[\"structurePixelRewriteVerified\"] === true &&\n value[\"providerAssertedCoverage\"] ===\n \"complete-rendering-and-ocr-observation\" &&\n value[\"piiCleanGuaranteed\"] === false &&\n typeof value[\"limitation\"] === \"string\";\n\nexport const decodePdfInspection = (json: string): PdfInspection => {\n const value: unknown = JSON.parse(json);\n if (!isInspection(value)) {\n throw new Error(\"Native PDF inspection does not match contract version 1\");\n }\n return value;\n};\n\nexport const decodePdfRasterRewriteCertificate = (\n json: string,\n): PdfRasterRewriteCertificate => {\n const value: unknown = JSON.parse(json);\n if (!isCertificate(value)) {\n throw new Error(\n `Native PDF raster certificate does not match contract version ${PDF_RASTER_CERTIFICATE_CONTRACT_VERSION}`,\n );\n }\n return value;\n};\n","import { execFile } from \"node:child_process\";\nimport {\n lstat,\n mkdtemp,\n readFile,\n rm,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, sep } from \"node:path\";\n\nimport { loadNativeAnonymizeBinding } from \"@stll/anonymize\";\n\nimport { decodePdfInspection } from \"./native-codec\";\nimport type {\n PdfGlyphObservation,\n PdfPageObservation,\n PdfRasterProvider,\n} from \"./types\";\n\nconst LOCAL_PROVIDER_ID = \"poppler-tesseract-local\";\nconst PROCESS_OUTPUT_MAX_BYTES = 64 * 1024 * 1024;\nconst PDF_DOCUMENT_MAX_BYTES = 64 * 1024 * 1024;\nconst PDF_RASTER_MAX_PAGE_BYTES = 128 * 1024 * 1024;\nconst PDF_RASTER_MAX_TOTAL_BYTES = 512 * 1024 * 1024;\nconst PDF_MAX_PAGES = 10_000;\nconst PDF_MAX_GLYPHS = 5_000_000;\nconst PDF_MAX_OBSERVED_TEXT_UTF8_BYTES = 64 * 1024 * 1024;\nconst DEFAULT_DPI = 300;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst MIN_DPI = 72;\nconst MAX_DPI = 600;\nconst MIN_TIMEOUT_MS = 100;\nconst MAX_TIMEOUT_MS = 300_000;\nconst LANGUAGE_PACK_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/u;\nconst TEMP_PREFIX = \"stella-anonymize-pdf-\";\n\nexport const PDF_LOCAL_PROVIDER_ERROR_CODES = {\n cleanupFailed: \"cleanup-failed\",\n executableFailed: \"executable-failed\",\n invalidOptions: \"invalid-options\",\n invalidOutput: \"invalid-output\",\n limitExceeded: \"limit-exceeded\",\n sourceRejected: \"source-rejected\",\n} as const;\n\nexport type PdfLocalProviderErrorCode =\n (typeof PDF_LOCAL_PROVIDER_ERROR_CODES)[keyof typeof PDF_LOCAL_PROVIDER_ERROR_CODES];\n\nexport class PdfLocalProviderError extends Error {\n readonly code: PdfLocalProviderErrorCode;\n\n constructor(code: PdfLocalProviderErrorCode, message: string) {\n super(message);\n this.name = \"PdfLocalProviderError\";\n this.code = code;\n }\n}\n\nexport type RenderPdfWithPopplerTesseractOptions = {\n document: Uint8Array;\n /** One explicit installed Tesseract traineddata name, for example `eng`. */\n ocrLanguage: string;\n dpi?: number | undefined;\n pdftoppmPath?: string | undefined;\n tesseractPath?: string | undefined;\n timeoutMs?: number | undefined;\n};\n\nexport type LocalPdfRasterPage = {\n observation: PdfPageObservation;\n widthPixels: number;\n heightPixels: number;\n pixels: Uint8Array;\n};\n\nexport type LocalPdfRasterObservation = {\n provider: PdfRasterProvider;\n pages: readonly LocalPdfRasterPage[];\n};\n\ntype ResolvedLocalProviderOptions = {\n dpi: number;\n ocrLanguage: string;\n pdftoppmPath: string;\n tesseractPath: string;\n timeoutMs: number;\n};\n\ntype ProcessResult = { stdout: Buffer; stderr: Buffer };\n\nconst providerError = (\n code: PdfLocalProviderErrorCode,\n message: string,\n): PdfLocalProviderError =>\n new PdfLocalProviderError(code, `${code}: ${message}`);\n\nconst validateOptions = (\n options: RenderPdfWithPopplerTesseractOptions,\n): ResolvedLocalProviderOptions => {\n const dpi = options.dpi ?? DEFAULT_DPI;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const pdftoppmPath = options.pdftoppmPath ?? \"pdftoppm\";\n const tesseractPath = options.tesseractPath ?? \"tesseract\";\n if (\n !Number.isInteger(dpi) ||\n dpi < MIN_DPI ||\n dpi > MAX_DPI ||\n !Number.isInteger(timeoutMs) ||\n timeoutMs < MIN_TIMEOUT_MS ||\n timeoutMs > MAX_TIMEOUT_MS ||\n !LANGUAGE_PACK_PATTERN.test(options.ocrLanguage) ||\n !pdftoppmPath ||\n pdftoppmPath.includes(\"\\0\") ||\n !tesseractPath ||\n tesseractPath.includes(\"\\0\")\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOptions,\n \"local PDF provider options are invalid\",\n );\n }\n return {\n dpi,\n ocrLanguage: options.ocrLanguage,\n pdftoppmPath,\n tesseractPath,\n timeoutMs,\n };\n};\n\nconst runExecutable = (\n executable: string,\n args: readonly string[],\n timeoutMs: number,\n stage: \"OCR\" | \"renderer\" | \"version probe\",\n): Promise<ProcessResult> =>\n new Promise((resolve, reject) => {\n execFile(\n executable,\n args,\n {\n encoding: \"buffer\",\n maxBuffer: PROCESS_OUTPUT_MAX_BYTES,\n timeout: timeoutMs,\n windowsHide: true,\n },\n (error, stdout, stderr) => {\n if (error !== null) {\n reject(\n providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.executableFailed,\n `local PDF ${stage} failed`,\n ),\n );\n return;\n }\n resolve({ stdout: Buffer.from(stdout), stderr: Buffer.from(stderr) });\n },\n );\n });\n\nconst decodeUtf8 = (value: Uint8Array, context: string): string => {\n try {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(value);\n } catch {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n `local PDF ${context} is not valid UTF-8`,\n );\n }\n};\n\nconst versionFrom = (\n value: Uint8Array,\n prefix: string,\n context: string,\n): string => {\n const line = decodeUtf8(value, context).split(/\\r?\\n/u, 1)[0]?.trim();\n const version = line?.startsWith(prefix)\n ? line.slice(prefix.length).trim()\n : \"\";\n if (!version || version.length > 256 || /\\p{Cc}/u.test(version)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n `local PDF ${context} is invalid`,\n );\n }\n return version;\n};\n\ntype InspectedPageGeometry = {\n heightPoints: number;\n widthPoints: number;\n};\n\nconst inspectPageGeometry = (\n document: Uint8Array,\n): readonly InspectedPageGeometry[] => {\n if (document.byteLength > PDF_DOCUMENT_MAX_BYTES) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"PDF source exceeds its byte limit\",\n );\n }\n const inspect = loadNativeAnonymizeBinding().inspectPdfJson;\n if (inspect === undefined) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.sourceRejected,\n \"native PDF inspection is unavailable\",\n );\n }\n try {\n const inspection = decodePdfInspection(inspect(document));\n if (\n inspection.encrypted === true ||\n inspection.pageCount < 1 ||\n inspection.pageCount > PDF_MAX_PAGES\n ) {\n throw new Error(\"source rejected\");\n }\n return inspection.pages.map(({ heightPoints, widthPoints }) => ({\n heightPoints,\n widthPoints,\n }));\n } catch {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.sourceRejected,\n \"PDF source is unsupported by the local raster provider\",\n );\n }\n};\n\nconst assertProjectedRasterLimits = (\n pages: readonly InspectedPageGeometry[],\n dpi: number,\n): void => {\n let totalBytes = 0;\n for (const { heightPoints, widthPoints } of pages) {\n const widthPixels = Math.ceil((widthPoints * dpi) / 72);\n const heightPixels = Math.ceil((heightPoints * dpi) / 72);\n const pageBytes = widthPixels * heightPixels * 3;\n totalBytes += pageBytes;\n if (\n !Number.isSafeInteger(widthPixels) ||\n !Number.isSafeInteger(heightPixels) ||\n widthPixels <= 0 ||\n heightPixels <= 0 ||\n !Number.isSafeInteger(pageBytes) ||\n pageBytes > PDF_RASTER_MAX_PAGE_BYTES ||\n !Number.isSafeInteger(totalBytes) ||\n totalBytes > PDF_RASTER_MAX_TOTAL_BYTES\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"PDF page geometry exceeds the selected DPI raster limits\",\n );\n }\n }\n};\n\ntype PpmPage = {\n width: number;\n height: number;\n pixels: Uint8Array;\n};\n\nconst isWhitespace = (byte: number): boolean =>\n byte === 0x09 ||\n byte === 0x0a ||\n byte === 0x0b ||\n byte === 0x0c ||\n byte === 0x0d ||\n byte === 0x20;\n\nconst ppmToken = (bytes: Uint8Array, start: number): [string, number] => {\n let offset = start;\n while (offset < bytes.length) {\n if (isWhitespace(bytes[offset] ?? -1)) {\n offset += 1;\n continue;\n }\n if (bytes[offset] === 0x23) {\n while (offset < bytes.length && bytes[offset] !== 0x0a) offset += 1;\n continue;\n }\n break;\n }\n const tokenStart = offset;\n while (\n offset < bytes.length &&\n !isWhitespace(bytes[offset] ?? -1) &&\n bytes[offset] !== 0x23\n ) {\n offset += 1;\n }\n if (tokenStart === offset) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler emitted an invalid PPM header\",\n );\n }\n const token = Buffer.from(bytes.subarray(tokenStart, offset)).toString(\n \"ascii\",\n );\n if (!/^[\\x21-\\x7e]+$/u.test(token)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler emitted an invalid PPM header\",\n );\n }\n return [token, offset];\n};\n\nconst parsePositiveInteger = (value: string): number => {\n if (!/^[1-9][0-9]*$/u.test(value)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler emitted invalid PPM dimensions\",\n );\n }\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Poppler PPM dimensions exceed their limit\",\n );\n }\n return parsed;\n};\n\nconst parsePpm = (bytes: Uint8Array): PpmPage => {\n const [magic, afterMagic] = ppmToken(bytes, 0);\n const [widthValue, afterWidth] = ppmToken(bytes, afterMagic);\n const [heightValue, afterHeight] = ppmToken(bytes, afterWidth);\n const [maximumValue, afterMaximum] = ppmToken(bytes, afterHeight);\n if (magic !== \"P6\" || maximumValue !== \"255\") {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler must emit binary RGB8 PPM pages\",\n );\n }\n const width = parsePositiveInteger(widthValue);\n const height = parsePositiveInteger(heightValue);\n const expected = width * height * 3;\n if (!Number.isSafeInteger(expected) || expected > PDF_RASTER_MAX_PAGE_BYTES) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Poppler page pixels exceed their byte limit\",\n );\n }\n let pixelOffset = afterMaximum;\n if (!isWhitespace(bytes[pixelOffset] ?? -1)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler emitted an invalid PPM pixel separator\",\n );\n }\n const separator = bytes[pixelOffset];\n pixelOffset += 1;\n if (separator === 0x0d && bytes[pixelOffset] === 0x0a) pixelOffset += 1;\n if (bytes.length - pixelOffset !== expected) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler emitted a truncated or trailing PPM page\",\n );\n }\n return { width, height, pixels: bytes.subarray(pixelOffset) };\n};\n\nconst boundedRenderedPage = async (path: string): Promise<PpmPage> => {\n const metadata = await lstat(path);\n if (!metadata.isFile() || metadata.isSymbolicLink()) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Poppler page output must be a regular file\",\n );\n }\n if (metadata.size > PDF_RASTER_MAX_PAGE_BYTES + 1024 * 1024) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Poppler page output exceeds its byte limit\",\n );\n }\n return parsePpm(await readFile(path));\n};\n\nconst integerTsvField = (value: string | undefined): number => {\n if (value === undefined || !/^(?:0|[1-9][0-9]*)$/u.test(value)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Tesseract emitted invalid TSV geometry\",\n );\n }\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Tesseract TSV geometry exceeds its limit\",\n );\n }\n return parsed;\n};\n\nconst parseTesseractTsv = (\n bytes: Uint8Array,\n page: PpmPage,\n pageGeometry: InspectedPageGeometry,\n pageIndex: number,\n): PdfPageObservation => {\n const lines = decodeUtf8(bytes, \"Tesseract TSV\").split(/\\r?\\n/u);\n const header = lines.shift()?.split(\"\\t\");\n if (\n header?.slice(0, 12).join(\"\\t\") !==\n \"level\\tpage_num\\tblock_num\\tpar_num\\tline_num\\tword_num\\tleft\\ttop\\twidth\\theight\\tconf\\ttext\"\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Tesseract emitted an unsupported TSV header\",\n );\n }\n const { heightPoints, widthPoints } = pageGeometry;\n const glyphs: PdfGlyphObservation[] = [];\n let text = \"\";\n let previousLine = \"\";\n for (const line of lines) {\n if (!line) continue;\n const fields = line.split(\"\\t\");\n if (fields[0] !== \"5\") continue;\n const word = fields.slice(11).join(\"\\t\").trim();\n if (!word) continue;\n if (/\\p{Cc}/u.test(word)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Tesseract emitted control characters in OCR text\",\n );\n }\n const lineKey = `${fields[2] ?? \"\"}/${fields[3] ?? \"\"}/${fields[4] ?? \"\"}`;\n if (text) text += lineKey === previousLine ? \" \" : \"\\n\";\n previousLine = lineKey;\n const start = text.length;\n text += word;\n const end = text.length;\n const left = integerTsvField(fields[6]);\n const top = integerTsvField(fields[7]);\n const width = integerTsvField(fields[8]);\n const height = integerTsvField(fields[9]);\n const right = left + width;\n const bottom = top + height;\n if (\n width === 0 ||\n height === 0 ||\n !Number.isSafeInteger(right) ||\n !Number.isSafeInteger(bottom) ||\n right > page.width ||\n bottom > page.height\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.invalidOutput,\n \"Tesseract emitted out-of-page OCR geometry\",\n );\n }\n glyphs.push({\n start,\n end,\n bounds: {\n left: (left * widthPoints) / page.width,\n bottom: heightPoints - (bottom * heightPoints) / page.height,\n right: (right * widthPoints) / page.width,\n top: heightPoints - (top * heightPoints) / page.height,\n },\n source: \"ocr\",\n });\n if (glyphs.length > PDF_MAX_GLYPHS) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Tesseract OCR glyph count exceeds its limit\",\n );\n }\n if (Buffer.byteLength(text, \"utf8\") > PDF_MAX_OBSERVED_TEXT_UTF8_BYTES) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Tesseract OCR text exceeds its byte limit\",\n );\n }\n }\n return {\n pageIndex,\n widthPoints,\n heightPoints,\n text,\n glyphs,\n rendered: true,\n textLayer: \"absent\",\n ocr: \"complete\",\n imageCount: 0,\n };\n};\n\nconst safeCleanup = async (directory: string): Promise<void> => {\n const expectedPrefix = `${tmpdir()}${sep}${TEMP_PREFIX}`;\n if (!directory.startsWith(expectedPrefix)) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.cleanupFailed,\n \"local PDF temporary directory could not be verified\",\n );\n }\n try {\n const metadata = await lstat(directory);\n if (!metadata.isDirectory() || metadata.isSymbolicLink()) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.cleanupFailed,\n \"local PDF temporary directory could not be verified\",\n );\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return;\n if (error instanceof PdfLocalProviderError) throw error;\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.cleanupFailed,\n \"local PDF temporary directory could not be inspected\",\n );\n }\n try {\n await rm(directory, { force: true, recursive: true });\n } catch {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.cleanupFailed,\n \"local PDF temporary files could not be removed\",\n );\n }\n};\n\nexport const renderPdfWithPopplerTesseract = async (\n options: RenderPdfWithPopplerTesseractOptions,\n): Promise<LocalPdfRasterObservation> => {\n const resolved = validateOptions(options);\n const pageGeometry = inspectPageGeometry(options.document);\n assertProjectedRasterLimits(pageGeometry, resolved.dpi);\n const pageCount = pageGeometry.length;\n const popplerVersionResult = await runExecutable(\n resolved.pdftoppmPath,\n [\"-v\"],\n resolved.timeoutMs,\n \"version probe\",\n );\n const tesseractVersionResult = await runExecutable(\n resolved.tesseractPath,\n [\"--version\"],\n resolved.timeoutMs,\n \"version probe\",\n );\n const rendererVersion = versionFrom(\n popplerVersionResult.stderr.length > 0\n ? popplerVersionResult.stderr\n : popplerVersionResult.stdout,\n \"pdftoppm version \",\n \"Poppler version\",\n );\n const ocrVersion = versionFrom(\n tesseractVersionResult.stdout.length > 0\n ? tesseractVersionResult.stdout\n : tesseractVersionResult.stderr,\n \"tesseract \",\n \"Tesseract version\",\n );\n const directory = await mkdtemp(join(tmpdir(), TEMP_PREFIX));\n try {\n const sourcePath = join(directory, \"source.pdf\");\n const outputPrefix = join(directory, \"page\");\n await writeFile(sourcePath, options.document, { flag: \"wx\", mode: 0o600 });\n const pages: LocalPdfRasterPage[] = [];\n let totalPixels = 0;\n let totalGlyphs = 0;\n let totalObservedTextUtf8Bytes = 0;\n for (let pageNumber = 1; pageNumber <= pageCount; pageNumber += 1) {\n const inspectedGeometry = pageGeometry.at(pageNumber - 1);\n if (inspectedGeometry === undefined) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.sourceRejected,\n \"PDF page geometry became unavailable\",\n );\n }\n const pageOutputPrefix = `${outputPrefix}-${pageNumber}`;\n const pagePath = `${pageOutputPrefix}.ppm`;\n await runExecutable(\n resolved.pdftoppmPath,\n [\n \"-cropbox\",\n \"-f\",\n String(pageNumber),\n \"-l\",\n String(pageNumber),\n \"-singlefile\",\n \"-r\",\n String(resolved.dpi),\n sourcePath,\n pageOutputPrefix,\n ],\n resolved.timeoutMs,\n \"renderer\",\n );\n try {\n const page = await boundedRenderedPage(pagePath);\n totalPixels += page.pixels.byteLength;\n if (\n !Number.isSafeInteger(totalPixels) ||\n totalPixels > PDF_RASTER_MAX_TOTAL_BYTES\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Poppler page pixels exceed their aggregate limit\",\n );\n }\n const ocr = await runExecutable(\n resolved.tesseractPath,\n [pagePath, \"stdout\", \"-l\", resolved.ocrLanguage, \"tsv\"],\n resolved.timeoutMs,\n \"OCR\",\n );\n const observation = parseTesseractTsv(\n ocr.stdout,\n page,\n inspectedGeometry,\n pageNumber - 1,\n );\n totalGlyphs += observation.glyphs.length;\n totalObservedTextUtf8Bytes += Buffer.byteLength(\n observation.text,\n \"utf8\",\n );\n if (\n !Number.isSafeInteger(totalGlyphs) ||\n totalGlyphs > PDF_MAX_GLYPHS ||\n !Number.isSafeInteger(totalObservedTextUtf8Bytes) ||\n totalObservedTextUtf8Bytes > PDF_MAX_OBSERVED_TEXT_UTF8_BYTES\n ) {\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.limitExceeded,\n \"Tesseract OCR document output exceeds its aggregate limit\",\n );\n }\n pages.push({\n observation,\n widthPixels: page.width,\n heightPixels: page.height,\n pixels: page.pixels,\n });\n } finally {\n await unlink(pagePath).catch((error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") return;\n throw providerError(\n PDF_LOCAL_PROVIDER_ERROR_CODES.cleanupFailed,\n \"local PDF page raster could not be removed\",\n );\n });\n }\n }\n return {\n provider: {\n providerId: LOCAL_PROVIDER_ID,\n rendererName: \"Poppler pdftoppm\",\n rendererVersion,\n ocrName: \"Tesseract OCR\",\n ocrVersion,\n ocrLanguage: resolved.ocrLanguage,\n },\n pages,\n };\n } finally {\n await safeCleanup(directory);\n }\n};\n","import { createHash } from \"node:crypto\";\n\nimport {\n convert_external_detection_batch,\n loadNativeAnonymizeBinding,\n type ExternalDetectionBatch,\n type PreparedNativePipeline,\n} from \"@stll/anonymize\";\n\nimport {\n PDF_INSPECTION_ERROR_CODES,\n PDF_RASTER_ERROR_CODES,\n type PdfInspection,\n type PdfInspectionErrorCode,\n type PdfPageObservation,\n type PdfRect,\n type PdfRasterProvider,\n type PdfRasterErrorCode,\n type PdfRasterRewrite,\n type PdfRasterRewriteResult,\n} from \"./types\";\nimport {\n decodePdfInspection,\n decodePdfRasterRewriteCertificate,\n} from \"./native-codec\";\n\nexport const PDF_INSPECTION_CONTRACT_VERSION = 1 as const;\nexport const PDF_RASTER_CONTRACT_VERSION = 1 as const;\nexport const PDF_RASTER_MAX_PAGE_BYTES = 128 * 1024 * 1024;\nexport const PDF_RASTER_MAX_TOTAL_BYTES = 512 * 1024 * 1024;\nexport const PDF_RASTER_MAX_MANUAL_REGIONS = 1_000_000;\nexport const PDF_RASTER_MAX_OUTPUT_BYTES = 512 * 1024 * 1024;\nexport const PDF_RASTER_REQUEST_JSON_MAX_BYTES = 64 * 1024 * 1024;\nexport const PDF_DOCUMENT_MAX_BYTES = 64 * 1024 * 1024;\nexport const PDF_STREAM_DECOMPRESSED_MAX_BYTES = 32 * 1024 * 1024;\nexport const PDF_LOADED_PAYLOAD_MAX_BYTES = 128 * 1024 * 1024;\nexport const PDF_MAX_OBJECTS = 200_000;\nexport const PDF_MAX_OBJECT_NODES = 1_000_000;\nexport const PDF_MAX_OBJECT_DEPTH = 128;\nexport const PDF_MAX_PAGES = 10_000;\nexport const PDF_MAX_GLYPHS = 5_000_000;\nexport const PDF_MAX_PAGE_TEXT_UTF8_BYTES = 16 * 1024 * 1024;\nexport const PDF_MAX_OBSERVED_TEXT_UTF8_BYTES = 64 * 1024 * 1024;\nexport const PDF_OBSERVATIONS_JSON_MAX_BYTES = 64 * 1024 * 1024;\n/** Renderer dimensions may differ by this many points due to numeric rounding. */\nexport const PDF_PAGE_DIMENSION_TOLERANCE_POINTS = 0.25;\n\nexport class PdfInspectionError extends Error {\n readonly code: PdfInspectionErrorCode;\n\n constructor(code: PdfInspectionErrorCode, message: string) {\n super(message);\n this.name = \"PdfInspectionError\";\n this.code = code;\n }\n}\n\nexport class PdfRasterError extends Error {\n readonly code: PdfRasterErrorCode;\n\n constructor(code: PdfRasterErrorCode, message: string) {\n super(message);\n this.name = \"PdfRasterError\";\n this.code = code;\n }\n}\n\nconst errorCode = (message: string): PdfInspectionErrorCode => {\n const code = Object.values(PDF_INSPECTION_ERROR_CODES).find((candidate) =>\n message.startsWith(`${candidate}:`),\n );\n return code ?? PDF_INSPECTION_ERROR_CODES.invalidDocument;\n};\n\nconst rasterErrorCode = (message: string): PdfRasterErrorCode => {\n const code = Object.values(PDF_RASTER_ERROR_CODES).find((candidate) =>\n message.startsWith(`${candidate}:`),\n );\n return code ?? PDF_RASTER_ERROR_CODES.verificationFailed;\n};\n\nexport type InspectPdfOptions = {\n pageObservations?: readonly PdfPageObservation[];\n};\n\nexport const inspectPdf = (\n document: Uint8Array,\n options: InspectPdfOptions = {},\n): PdfInspection => {\n const inspect = loadNativeAnonymizeBinding().inspectPdfJson;\n if (inspect === undefined) {\n throw new PdfInspectionError(\n PDF_INSPECTION_ERROR_CODES.invalidDocument,\n \"Native anonymize binding does not expose PDF inspection\",\n );\n }\n try {\n const observations = options.pageObservations;\n return decodePdfInspection(\n inspect(\n document,\n observations === undefined ? undefined : JSON.stringify(observations),\n ),\n );\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"PDF inspection failed\";\n throw new PdfInspectionError(errorCode(message), message);\n }\n};\n\nexport type RewritePdfRasterFromDetectionsOptions = {\n document: Uint8Array;\n request: PdfRasterRewrite;\n /** One opaque, row-packed RGB8 buffer per request page, in page order. */\n pagePixels: readonly Uint8Array[];\n};\n\n/**\n * Advanced rewrite-only surface. It verifies structure and destructive pixels but\n * does not run detection or certify that the result is PII-free.\n */\nexport const rewritePdfRasterFromDetections = ({\n document,\n request,\n pagePixels,\n}: RewritePdfRasterFromDetectionsOptions): PdfRasterRewriteResult => {\n const rewrite =\n loadNativeAnonymizeBinding().rewritePdfRasterFromDetectionsJson;\n if (rewrite === undefined) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.verificationFailed,\n \"Native anonymize binding does not expose PDF raster rewriting\",\n );\n }\n try {\n const result = rewrite(document, JSON.stringify(request), pagePixels);\n return {\n document: result.document,\n certificate: decodePdfRasterRewriteCertificate(result.certificateJson),\n };\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"PDF raster rewrite failed\";\n throw new PdfRasterError(rasterErrorCode(message), message);\n }\n};\n\nexport type PdfRasterObservedPageInput = {\n observation: PdfPageObservation;\n widthPixels: number;\n heightPixels: number;\n pixels: Uint8Array;\n /** Optional digest-bound provider batch merged through stella caller semantics. */\n externalDetectionBatch?: ExternalDetectionBatch | string | undefined;\n /** User-reviewed regions in normalized displayed-page PDF points. */\n manualRegions?: readonly PdfRect[] | undefined;\n};\n\nexport type AnonymizePdfRasterOptions = {\n document: Uint8Array;\n pipeline: Pick<\n PreparedNativePipeline,\n \"redactText\" | \"redactTextWithCallerDetections\"\n >;\n provider: PdfRasterProvider;\n pages: readonly PdfRasterObservedPageInput[];\n fillRgb?: readonly [number, number, number] | undefined;\n};\n\nconst sha256 = (value: Uint8Array): string =>\n createHash(\"sha256\").update(value).digest(\"hex\");\n\n/** Run stella detection, merge optional external detections, then rewrite pixels. */\nexport const anonymizePdfRaster = ({\n document,\n pipeline,\n provider,\n pages,\n fillRgb = [0, 0, 0],\n}: AnonymizePdfRasterOptions): PdfRasterRewriteResult => {\n if (document.byteLength > PDF_DOCUMENT_MAX_BYTES) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF source exceeds its byte limit\",\n );\n }\n if (pages.length > PDF_MAX_PAGES) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster page count exceeds its limit\",\n );\n }\n let totalPixelBytes = 0;\n let totalObservedTextBytes = 0;\n let totalManualRegions = 0;\n const textEncoder = new TextEncoder();\n for (const page of pages) {\n if (typeof page.observation?.text !== \"string\") {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.invalidContract,\n \"invalid-contract: PDF raster observed text is invalid\",\n );\n }\n const observedTextBytes = textEncoder.encode(\n page.observation.text,\n ).byteLength;\n if (observedTextBytes > PDF_MAX_PAGE_TEXT_UTF8_BYTES) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster page text exceeds its byte limit\",\n );\n }\n totalObservedTextBytes += observedTextBytes;\n if (totalObservedTextBytes > PDF_MAX_OBSERVED_TEXT_UTF8_BYTES) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster observed text exceeds its aggregate byte limit\",\n );\n }\n const { heightPixels, widthPixels } = page;\n if (\n !Number.isSafeInteger(widthPixels) ||\n !Number.isSafeInteger(heightPixels) ||\n widthPixels <= 0 ||\n heightPixels <= 0\n ) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.invalidContract,\n \"invalid-contract: PDF raster pixel dimensions are invalid\",\n );\n }\n const expected = widthPixels * heightPixels * 3;\n if (\n !Number.isSafeInteger(expected) ||\n expected > PDF_RASTER_MAX_PAGE_BYTES ||\n page.pixels.byteLength !== expected\n ) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster page pixels exceed limits or have an invalid RGB8 length\",\n );\n }\n totalPixelBytes += expected;\n if (\n !Number.isSafeInteger(totalPixelBytes) ||\n totalPixelBytes > PDF_RASTER_MAX_TOTAL_BYTES\n ) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster pixels exceed their aggregate limit\",\n );\n }\n totalManualRegions += page.manualRegions?.length ?? 0;\n if (totalManualRegions > PDF_RASTER_MAX_MANUAL_REGIONS) {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.limitExceeded,\n \"limit-exceeded: PDF raster manual regions exceed their aggregate limit\",\n );\n }\n }\n const binding = loadNativeAnonymizeBinding();\n let requestPages: PdfRasterRewrite[\"pages\"];\n try {\n requestPages = pages.map((page) => {\n const external =\n page.externalDetectionBatch === undefined\n ? undefined\n : convert_external_detection_batch(\n new TextEncoder().encode(page.observation.text),\n page.externalDetectionBatch,\n { binding },\n );\n const result =\n external === undefined\n ? pipeline.redactText(page.observation.text)\n : pipeline.redactTextWithCallerDetections(page.observation.text, {\n detections: external,\n });\n return {\n observation: page.observation,\n widthPixels: page.widthPixels,\n heightPixels: page.heightPixels,\n pixelSha256: sha256(page.pixels),\n detections: result.resolvedEntities.map(({ start, end }) => ({\n start,\n end,\n })),\n manualRegions: page.manualRegions ?? [],\n };\n });\n } catch {\n throw new PdfRasterError(\n PDF_RASTER_ERROR_CODES.detectionFailed,\n \"detection-failed: PDF raster detection failed\",\n );\n }\n return rewritePdfRasterFromDetections({\n document,\n request: {\n contractVersion: PDF_RASTER_CONTRACT_VERSION,\n sourceSha256: sha256(document),\n provider,\n fillRgb,\n pages: requestPages,\n },\n pagePixels: pages.map(({ pixels }) => pixels),\n });\n};\n\nexport { PDF_INSPECTION_ERROR_CODES } from \"./types\";\nexport { PDF_RASTER_ERROR_CODES } from \"./types\";\nexport { PDF_RASTER_CERTIFICATE_CONTRACT_VERSION } from \"./types\";\nexport {\n PDF_LOCAL_PROVIDER_ERROR_CODES,\n PdfLocalProviderError,\n renderPdfWithPopplerTesseract,\n} from \"./local-provider\";\nexport type {\n LocalPdfRasterObservation,\n LocalPdfRasterPage,\n PdfLocalProviderErrorCode,\n RenderPdfWithPopplerTesseractOptions,\n} from \"./local-provider\";\nexport type {\n PdfGlyphObservation,\n PdfInspection,\n PdfInspectionErrorCode,\n PdfInspectionGap,\n PdfPageInspection,\n PdfPageObservation,\n PdfRect,\n PdfRiskInventory,\n PdfRasterDetection,\n PdfRasterErrorCode,\n PdfRasterPage,\n PdfRasterProvider,\n PdfRasterRewrite,\n PdfRasterRewriteCertificate,\n PdfRasterRewriteResult,\n} from \"./types\";\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/anonymize-pdf",
3
- "version": "2.9.1",
3
+ "version": "3.0.0",
4
4
  "description": "Fail-closed PDF coverage and risk inspection contracts for stella anonymization",
5
5
  "type": "module",
6
6
  "exports": {
@@ -32,11 +32,11 @@
32
32
  "format": "oxfmt ."
33
33
  },
34
34
  "dependencies": {
35
- "@stll/anonymize": "2.9.1"
35
+ "@stll/anonymize": "3.0.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^26.2.0",
39
- "bun-types": "1.4.0",
39
+ "bun-types": "1.4.1",
40
40
  "tsdown": "0.22.14",
41
41
  "typescript": "7.0.2"
42
42
  }