@intactfile/engines 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +68 -0
  2. package/dist/archive/diagnose.d.ts +37 -0
  3. package/dist/archive/diagnose.d.ts.map +1 -0
  4. package/dist/archive/diagnose.js +165 -0
  5. package/dist/archive/diagnose.js.map +1 -0
  6. package/dist/archive/format.d.ts +52 -0
  7. package/dist/archive/format.d.ts.map +1 -0
  8. package/dist/archive/format.js +87 -0
  9. package/dist/archive/format.js.map +1 -0
  10. package/dist/archive/host.d.ts +83 -0
  11. package/dist/archive/host.d.ts.map +1 -0
  12. package/dist/archive/host.js +92 -0
  13. package/dist/archive/host.js.map +1 -0
  14. package/dist/archive/index.d.ts +22 -0
  15. package/dist/archive/index.d.ts.map +1 -0
  16. package/dist/archive/index.js +19 -0
  17. package/dist/archive/index.js.map +1 -0
  18. package/dist/archive/salvage.d.ts +73 -0
  19. package/dist/archive/salvage.d.ts.map +1 -0
  20. package/dist/archive/salvage.js +210 -0
  21. package/dist/archive/salvage.js.map +1 -0
  22. package/dist/byte-source.d.ts +19 -0
  23. package/dist/byte-source.d.ts.map +1 -0
  24. package/dist/byte-source.js +16 -0
  25. package/dist/byte-source.js.map +1 -0
  26. package/dist/bytesource/blob.d.ts +14 -0
  27. package/dist/bytesource/blob.d.ts.map +1 -0
  28. package/dist/bytesource/blob.js +23 -0
  29. package/dist/bytesource/blob.js.map +1 -0
  30. package/dist/crc32.d.ts +2 -0
  31. package/dist/crc32.d.ts.map +1 -0
  32. package/dist/crc32.js +18 -0
  33. package/dist/crc32.js.map +1 -0
  34. package/dist/detect/index.d.ts +16 -0
  35. package/dist/detect/index.d.ts.map +1 -0
  36. package/dist/detect/index.js +159 -0
  37. package/dist/detect/index.js.map +1 -0
  38. package/dist/index.d.ts +24 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +17 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/jpeg/browser-loader.d.ts +21 -0
  43. package/dist/jpeg/browser-loader.d.ts.map +1 -0
  44. package/dist/jpeg/browser-loader.js +28 -0
  45. package/dist/jpeg/browser-loader.js.map +1 -0
  46. package/dist/jpeg/diagnose.d.ts +50 -0
  47. package/dist/jpeg/diagnose.d.ts.map +1 -0
  48. package/dist/jpeg/diagnose.js +124 -0
  49. package/dist/jpeg/diagnose.js.map +1 -0
  50. package/dist/jpeg/exif.d.ts +11 -0
  51. package/dist/jpeg/exif.d.ts.map +1 -0
  52. package/dist/jpeg/exif.js +50 -0
  53. package/dist/jpeg/exif.js.map +1 -0
  54. package/dist/jpeg/format.d.ts +106 -0
  55. package/dist/jpeg/format.d.ts.map +1 -0
  56. package/dist/jpeg/format.js +248 -0
  57. package/dist/jpeg/format.js.map +1 -0
  58. package/dist/jpeg/index.d.ts +18 -0
  59. package/dist/jpeg/index.d.ts.map +1 -0
  60. package/dist/jpeg/index.js +14 -0
  61. package/dist/jpeg/index.js.map +1 -0
  62. package/dist/jpeg/mozjpeg-host.d.ts +60 -0
  63. package/dist/jpeg/mozjpeg-host.d.ts.map +1 -0
  64. package/dist/jpeg/mozjpeg-host.js +138 -0
  65. package/dist/jpeg/mozjpeg-host.js.map +1 -0
  66. package/dist/jpeg/repair.d.ts +89 -0
  67. package/dist/jpeg/repair.d.ts.map +1 -0
  68. package/dist/jpeg/repair.js +216 -0
  69. package/dist/jpeg/repair.js.map +1 -0
  70. package/dist/node.d.ts +11 -0
  71. package/dist/node.d.ts.map +1 -0
  72. package/dist/node.js +29 -0
  73. package/dist/node.js.map +1 -0
  74. package/dist/pdf/browser-loader.d.ts +30 -0
  75. package/dist/pdf/browser-loader.d.ts.map +1 -0
  76. package/dist/pdf/browser-loader.js +21 -0
  77. package/dist/pdf/browser-loader.js.map +1 -0
  78. package/dist/pdf/diagnose.d.ts +23 -0
  79. package/dist/pdf/diagnose.d.ts.map +1 -0
  80. package/dist/pdf/diagnose.js +113 -0
  81. package/dist/pdf/diagnose.js.map +1 -0
  82. package/dist/pdf/format.d.ts +61 -0
  83. package/dist/pdf/format.d.ts.map +1 -0
  84. package/dist/pdf/format.js +150 -0
  85. package/dist/pdf/format.js.map +1 -0
  86. package/dist/pdf/index.d.ts +17 -0
  87. package/dist/pdf/index.d.ts.map +1 -0
  88. package/dist/pdf/index.js +12 -0
  89. package/dist/pdf/index.js.map +1 -0
  90. package/dist/pdf/qpdf-host.d.ts +66 -0
  91. package/dist/pdf/qpdf-host.d.ts.map +1 -0
  92. package/dist/pdf/qpdf-host.js +149 -0
  93. package/dist/pdf/qpdf-host.js.map +1 -0
  94. package/dist/pdf/rebuild.d.ts +32 -0
  95. package/dist/pdf/rebuild.d.ts.map +1 -0
  96. package/dist/pdf/rebuild.js +90 -0
  97. package/dist/pdf/rebuild.js.map +1 -0
  98. package/dist/pdf/repair.d.ts +61 -0
  99. package/dist/pdf/repair.d.ts.map +1 -0
  100. package/dist/pdf/repair.js +167 -0
  101. package/dist/pdf/repair.js.map +1 -0
  102. package/dist/png/diagnose.d.ts +50 -0
  103. package/dist/png/diagnose.d.ts.map +1 -0
  104. package/dist/png/diagnose.js +261 -0
  105. package/dist/png/diagnose.js.map +1 -0
  106. package/dist/png/format.d.ts +133 -0
  107. package/dist/png/format.d.ts.map +1 -0
  108. package/dist/png/format.js +350 -0
  109. package/dist/png/format.js.map +1 -0
  110. package/dist/png/index.d.ts +13 -0
  111. package/dist/png/index.d.ts.map +1 -0
  112. package/dist/png/index.js +10 -0
  113. package/dist/png/index.js.map +1 -0
  114. package/dist/png/repair.d.ts +31 -0
  115. package/dist/png/repair.d.ts.map +1 -0
  116. package/dist/png/repair.js +335 -0
  117. package/dist/png/repair.js.map +1 -0
  118. package/dist/sqlite/diagnose.d.ts +31 -0
  119. package/dist/sqlite/diagnose.d.ts.map +1 -0
  120. package/dist/sqlite/diagnose.js +169 -0
  121. package/dist/sqlite/diagnose.js.map +1 -0
  122. package/dist/sqlite/format.d.ts +103 -0
  123. package/dist/sqlite/format.d.ts.map +1 -0
  124. package/dist/sqlite/format.js +237 -0
  125. package/dist/sqlite/format.js.map +1 -0
  126. package/dist/sqlite/index.d.ts +24 -0
  127. package/dist/sqlite/index.d.ts.map +1 -0
  128. package/dist/sqlite/index.js +19 -0
  129. package/dist/sqlite/index.js.map +1 -0
  130. package/dist/sqlite/recover.d.ts +66 -0
  131. package/dist/sqlite/recover.d.ts.map +1 -0
  132. package/dist/sqlite/recover.js +305 -0
  133. package/dist/sqlite/recover.js.map +1 -0
  134. package/dist/sqlite/scan.d.ts +94 -0
  135. package/dist/sqlite/scan.d.ts.map +1 -0
  136. package/dist/sqlite/scan.js +356 -0
  137. package/dist/sqlite/scan.js.map +1 -0
  138. package/dist/sqlite/sqlite-host.d.ts +37 -0
  139. package/dist/sqlite/sqlite-host.d.ts.map +1 -0
  140. package/dist/sqlite/sqlite-host.js +80 -0
  141. package/dist/sqlite/sqlite-host.js.map +1 -0
  142. package/dist/sqlite/wal.d.ts +14 -0
  143. package/dist/sqlite/wal.d.ts.map +1 -0
  144. package/dist/sqlite/wal.js +79 -0
  145. package/dist/sqlite/wal.js.map +1 -0
  146. package/dist/types.d.ts +2 -0
  147. package/dist/types.d.ts.map +1 -0
  148. package/dist/types.js +2 -0
  149. package/dist/types.js.map +1 -0
  150. package/dist/video/diagnose.d.ts +41 -0
  151. package/dist/video/diagnose.d.ts.map +1 -0
  152. package/dist/video/diagnose.js +188 -0
  153. package/dist/video/diagnose.js.map +1 -0
  154. package/dist/video/format.d.ts +87 -0
  155. package/dist/video/format.d.ts.map +1 -0
  156. package/dist/video/format.js +273 -0
  157. package/dist/video/format.js.map +1 -0
  158. package/dist/video/index.d.ts +21 -0
  159. package/dist/video/index.d.ts.map +1 -0
  160. package/dist/video/index.js +17 -0
  161. package/dist/video/index.js.map +1 -0
  162. package/dist/video/libav-host.d.ts +88 -0
  163. package/dist/video/libav-host.d.ts.map +1 -0
  164. package/dist/video/libav-host.js +66 -0
  165. package/dist/video/libav-host.js.map +1 -0
  166. package/dist/video/mp4-rebuild.d.ts +167 -0
  167. package/dist/video/mp4-rebuild.d.ts.map +1 -0
  168. package/dist/video/mp4-rebuild.js +270 -0
  169. package/dist/video/mp4-rebuild.js.map +1 -0
  170. package/dist/video/mp4box-host.d.ts +62 -0
  171. package/dist/video/mp4box-host.d.ts.map +1 -0
  172. package/dist/video/mp4box-host.js +209 -0
  173. package/dist/video/mp4box-host.js.map +1 -0
  174. package/dist/video/reconstruct.d.ts +97 -0
  175. package/dist/video/reconstruct.d.ts.map +1 -0
  176. package/dist/video/reconstruct.js +271 -0
  177. package/dist/video/reconstruct.js.map +1 -0
  178. package/dist/video/remux.d.ts +85 -0
  179. package/dist/video/remux.d.ts.map +1 -0
  180. package/dist/video/remux.js +350 -0
  181. package/dist/video/remux.js.map +1 -0
  182. package/dist/zip/diagnose.d.ts +22 -0
  183. package/dist/zip/diagnose.d.ts.map +1 -0
  184. package/dist/zip/diagnose.js +126 -0
  185. package/dist/zip/diagnose.js.map +1 -0
  186. package/dist/zip/format.d.ts +79 -0
  187. package/dist/zip/format.d.ts.map +1 -0
  188. package/dist/zip/format.js +129 -0
  189. package/dist/zip/format.js.map +1 -0
  190. package/dist/zip/repair.d.ts +32 -0
  191. package/dist/zip/repair.d.ts.map +1 -0
  192. package/dist/zip/repair.js +82 -0
  193. package/dist/zip/repair.js.map +1 -0
  194. package/dist/zip/salvage.d.ts +23 -0
  195. package/dist/zip/salvage.d.ts.map +1 -0
  196. package/dist/zip/salvage.js +140 -0
  197. package/dist/zip/salvage.js.map +1 -0
  198. package/dist/zip/scan.d.ts +64 -0
  199. package/dist/zip/scan.d.ts.map +1 -0
  200. package/dist/zip/scan.js +386 -0
  201. package/dist/zip/scan.js.map +1 -0
  202. package/dist/zip/writer.d.ts +14 -0
  203. package/dist/zip/writer.d.ts.map +1 -0
  204. package/dist/zip/writer.js +131 -0
  205. package/dist/zip/writer.js.map +1 -0
  206. package/package.json +71 -0
  207. package/src/archive/LICENSE-unrar.txt +53 -0
@@ -0,0 +1,61 @@
1
+ import type { ByteSource } from '../byte-source.js';
2
+ import { type PdfDamageClass, type PdfDiagnosis } from './diagnose.js';
3
+ import { type XrefRebuildResult } from './rebuild.js';
4
+ export type PdfRepairOutcome = 'success' | 'partial' | 'failed';
5
+ export interface PdfRepairAttempt {
6
+ /** Candidate input the attempt ran on: original | strip-prefix | rebuild-xref. */
7
+ candidate: string;
8
+ args: string[];
9
+ exitCode: number;
10
+ produced: boolean;
11
+ stderr: string[];
12
+ }
13
+ export interface PdfVerification {
14
+ headerAtZero: boolean;
15
+ hasEof: boolean;
16
+ checkExitCode: number;
17
+ /** --check exit 0 (clean) or 3 (warnings only). */
18
+ checkPassed: boolean;
19
+ checkWarnings: string[];
20
+ }
21
+ export interface PdfRepairReport {
22
+ outcome: PdfRepairOutcome;
23
+ damageClass: PdfDamageClass;
24
+ /** Winning candidate label, or null when everything failed. */
25
+ strategy: string | null;
26
+ attempts: PdfRepairAttempt[];
27
+ /** qpdf warnings from the winning run + verification. */
28
+ warnings: string[];
29
+ notes: string[];
30
+ bytesIn: number;
31
+ bytesOut: number;
32
+ verification: PdfVerification | null;
33
+ }
34
+ /**
35
+ * The qpdf CLI retry ladder: each rung relaxes the rewrite a little more.
36
+ * Pure function so tests can pin the exact argument building.
37
+ */
38
+ export declare function qpdfArgLadder(input: string, output: string): string[][];
39
+ export interface RepairCandidate {
40
+ label: 'original' | 'strip-prefix' | 'rebuild-xref';
41
+ bytes: Uint8Array;
42
+ rebuild?: XrefRebuildResult;
43
+ }
44
+ /**
45
+ * Candidate inputs in the order they are worth trying. Pure given the bytes
46
+ * and diagnosis: original first, prefix-stripped when a garbage prefix was
47
+ * found, and our rebuilt-xref skeleton for structural tail damage.
48
+ */
49
+ export declare function buildRepairCandidates(bytes: Uint8Array, diagnosis: PdfDiagnosis): RepairCandidate[];
50
+ /**
51
+ * Repair a PDF by driving the qpdf CLI (wasm) over progressively more
52
+ * aggressive candidates, then verify the result with header/%%EOF checks and
53
+ * `qpdf --check`. Honesty rules: a truncated input is never reported as full
54
+ * success (data past the cut is unrecoverable by any tool), and outputs that
55
+ * fail verification are reported as failed even when qpdf exited cleanly.
56
+ */
57
+ export declare function repairPdf(src: ByteSource): Promise<{
58
+ pdf: Uint8Array;
59
+ report: PdfRepairReport;
60
+ }>;
61
+ //# sourceMappingURL=repair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.d.ts","sourceRoot":"","sources":["../../src/pdf/repair.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAe,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAEpF,OAAO,EAAuB,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAG3E,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEhE,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,WAAW,EAAE,cAAc,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,yDAAyD;IACzD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;CACtC;AAKD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAMvE;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,UAAU,GAAG,cAAc,GAAG,cAAc,CAAC;IACpD,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,GAAG,eAAe,EAAE,CAoBnG;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAAC,CA2HtG"}
@@ -0,0 +1,167 @@
1
+ import { MemorySource } from '../byte-source.js';
2
+ import { diagnosePdf } from './diagnose.js';
3
+ import { indexOfBytes } from './format.js';
4
+ import { rebuildXrefSkeleton } from './rebuild.js';
5
+ import { runQpdf } from './qpdf-host.js';
6
+ const IN_PATH = '/in.pdf';
7
+ const OUT_PATH = '/out.pdf';
8
+ /**
9
+ * The qpdf CLI retry ladder: each rung relaxes the rewrite a little more.
10
+ * Pure function so tests can pin the exact argument building.
11
+ */
12
+ export function qpdfArgLadder(input, output) {
13
+ return [
14
+ ['--deterministic-id', input, output],
15
+ ['--deterministic-id', '--object-streams=disable', input, output],
16
+ ['--deterministic-id', '--object-streams=disable', '--stream-data=preserve', input, output],
17
+ ];
18
+ }
19
+ /**
20
+ * Candidate inputs in the order they are worth trying. Pure given the bytes
21
+ * and diagnosis: original first, prefix-stripped when a garbage prefix was
22
+ * found, and our rebuilt-xref skeleton for structural tail damage.
23
+ */
24
+ export function buildRepairCandidates(bytes, diagnosis) {
25
+ const candidates = [{ label: 'original', bytes }];
26
+ let structural = bytes;
27
+ if (diagnosis.headerOffset !== null && diagnosis.headerOffset > 0) {
28
+ structural = bytes.subarray(diagnosis.headerOffset);
29
+ candidates.push({ label: 'strip-prefix', bytes: structural });
30
+ }
31
+ const needsRebuild = diagnosis.damageClass === 'broken-xref' ||
32
+ diagnosis.damageClass === 'truncated' ||
33
+ diagnosis.damageClass === 'unknown' ||
34
+ diagnosis.xrefValid === false ||
35
+ !diagnosis.hasEof;
36
+ if (needsRebuild) {
37
+ const rebuild = rebuildXrefSkeleton(structural);
38
+ if (rebuild)
39
+ candidates.push({ label: 'rebuild-xref', bytes: rebuild.bytes, rebuild });
40
+ }
41
+ return candidates;
42
+ }
43
+ /**
44
+ * Repair a PDF by driving the qpdf CLI (wasm) over progressively more
45
+ * aggressive candidates, then verify the result with header/%%EOF checks and
46
+ * `qpdf --check`. Honesty rules: a truncated input is never reported as full
47
+ * success (data past the cut is unrecoverable by any tool), and outputs that
48
+ * fail verification are reported as failed even when qpdf exited cleanly.
49
+ */
50
+ export async function repairPdf(src) {
51
+ const diagnosis = await diagnosePdf(src);
52
+ const bytes = await src.read(0, src.size);
53
+ const attempts = [];
54
+ const notes = [...diagnosis.notes];
55
+ const warnings = [];
56
+ const report = (outcome, pdf, strategy, verification) => ({
57
+ pdf,
58
+ report: {
59
+ outcome,
60
+ damageClass: diagnosis.damageClass,
61
+ strategy,
62
+ attempts,
63
+ warnings,
64
+ notes,
65
+ bytesIn: src.size,
66
+ bytesOut: pdf.length,
67
+ verification,
68
+ },
69
+ });
70
+ if (diagnosis.damageClass === 'not-pdf') {
71
+ notes.push('input is not recognizable as a PDF; qpdf not attempted');
72
+ return report('failed', new Uint8Array(0), null, null);
73
+ }
74
+ let winner = null;
75
+ const tryCandidate = async (candidate) => {
76
+ for (const args of qpdfArgLadder(IN_PATH, OUT_PATH)) {
77
+ const r = await runQpdf({ inputs: { [IN_PATH]: candidate.bytes }, args, readBack: [OUT_PATH] });
78
+ const output = r.outputs.get(OUT_PATH);
79
+ const produced = output !== undefined && output.length > 0;
80
+ attempts.push({
81
+ candidate: candidate.label,
82
+ args,
83
+ exitCode: r.exitCode,
84
+ produced,
85
+ stderr: r.stderr.map(stripProgramPrefix),
86
+ });
87
+ if ((r.exitCode === 0 || r.exitCode === 3) && produced) {
88
+ winner = { candidate, output: output, exitCode: r.exitCode, stderr: r.stderr };
89
+ return;
90
+ }
91
+ }
92
+ };
93
+ const candidates = buildRepairCandidates(bytes, diagnosis);
94
+ for (const candidate of candidates) {
95
+ await tryCandidate(candidate);
96
+ if (winner)
97
+ break;
98
+ }
99
+ // Last resort: even when the structure diagnosed as intact, qpdf may choke
100
+ // on localized corruption (its wasm build cannot self-recover, see
101
+ // qpdf-host.ts). Rebuild the xref skeleton from the object scan and retry.
102
+ if (!winner && !candidates.some((c) => c.label === 'rebuild-xref')) {
103
+ const structural = diagnosis.headerOffset !== null && diagnosis.headerOffset > 0 ? bytes.subarray(diagnosis.headerOffset) : bytes;
104
+ const rebuild = rebuildXrefSkeleton(structural);
105
+ if (rebuild) {
106
+ notes.push('structure looked intact but qpdf failed; retried with a rebuilt xref skeleton');
107
+ await tryCandidate({ label: 'rebuild-xref', bytes: rebuild.bytes, rebuild });
108
+ }
109
+ }
110
+ const win = winner;
111
+ if (!win) {
112
+ notes.push('no qpdf invocation produced an output file');
113
+ const lastErrors = attempts.at(-1)?.stderr ?? [];
114
+ if (lastErrors.length > 0)
115
+ notes.push(...lastErrors);
116
+ if (diagnosis.damageClass === 'encrypted') {
117
+ notes.push('document is encrypted; repair without the password is not possible');
118
+ }
119
+ return report('failed', new Uint8Array(0), null, null);
120
+ }
121
+ warnings.push(...win.stderr.map(stripProgramPrefix));
122
+ if (win.candidate.rebuild) {
123
+ notes.push(`xref table rebuilt from a forward object scan (${win.candidate.rebuild.objectsIndexed} object(s) indexed)`);
124
+ notes.push(...win.candidate.rebuild.notes);
125
+ }
126
+ // Verification: structural asserts + qpdf --check.
127
+ const check = await runQpdf({ inputs: { [IN_PATH]: win.output }, args: ['--check', IN_PATH] });
128
+ const checkWarnings = [...check.stdout, ...check.stderr]
129
+ .map(stripProgramPrefix)
130
+ .filter((l) => /warning/i.test(l));
131
+ const verification = {
132
+ headerAtZero: indexOfBytes(win.output.subarray(0, 5), '%PDF-') === 0,
133
+ hasEof: indexOfBytes(win.output.subarray(Math.max(0, win.output.length - 1024), win.output.length), '%%EOF') !== -1,
134
+ checkExitCode: check.exitCode,
135
+ checkPassed: check.exitCode === 0 || check.exitCode === 3,
136
+ checkWarnings,
137
+ };
138
+ warnings.push(...checkWarnings);
139
+ if (!verification.headerAtZero || !verification.hasEof || !verification.checkPassed) {
140
+ notes.push('repaired output failed verification; not claiming success');
141
+ return report('failed', win.output, win.candidate.label, verification);
142
+ }
143
+ let outcome = 'success';
144
+ if (diagnosis.damageClass === 'truncated') {
145
+ outcome = 'partial';
146
+ notes.push('input was truncated: data beyond the cut is physically gone; recovered what survived');
147
+ }
148
+ else if (win.candidate.rebuild && (win.candidate.rebuild.objectsDropped > 0 || win.candidate.rebuild.missingNumbers.length > 0)) {
149
+ outcome = 'partial';
150
+ notes.push('some objects were incomplete or missing during the xref rebuild');
151
+ }
152
+ else if (win.exitCode === 3 || warnings.length > 0) {
153
+ outcome = 'partial';
154
+ notes.push('qpdf reported warnings; the document was recovered but may have minor problems');
155
+ }
156
+ return report(outcome, win.output, win.candidate.label, verification);
157
+ }
158
+ function stripProgramPrefix(line) {
159
+ // qpdf prefixes fatal messages with argv[0] (the host program in wasm
160
+ // builds); warning lines keep their "WARNING:" prefix untouched.
161
+ const m = /^([^\s:]+): /.exec(line);
162
+ if (m && (m[1].includes('.') || m[1].includes('/') || m[1] === 'qpdf') && m[1] !== 'WARNING') {
163
+ return line.slice(m[0].length).trim();
164
+ }
165
+ return line.trim();
166
+ }
167
+ //# sourceMappingURL=repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.js","sourceRoot":"","sources":["../../src/pdf/repair.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * PNG structural diagnosis via pure own parsing — no wasm anywhere in this
3
+ * import chain, so it is safe in the browser-facing root export.
4
+ *
5
+ * It checks the 8-byte signature (including FTP/ASCII newline mangling and a
6
+ * garbage prefix), walks the chunk stream verifying every CRC, and sanity-
7
+ * checks the IHDR geometry. The chunk walk streams the file chunk-by-chunk and
8
+ * CRCs large chunk data in windows, so diagnosis never buffers the whole file.
9
+ *
10
+ * Honesty rules (docs/03 §4): the diagnosis names the concrete on-disk problem
11
+ * before any repair is attempted, and never inflates the image data (that is
12
+ * repair's job) so it cannot over-promise on IDAT recoverability.
13
+ */
14
+ import type { ByteSource } from '../byte-source.js';
15
+ import type { Confidence } from '../types.js';
16
+ import { type PngSafetyCaps } from './format.js';
17
+ export type PngDamageClass = 'none' | 'signature-damage' | 'crc-error' | 'missing-ihdr' | 'truncated' | 'idat-corruption' | 'garbage-prefix' | 'not-png' | 'unknown';
18
+ export interface PngChunkInfo {
19
+ type: string;
20
+ offset: number;
21
+ length: number;
22
+ /** null when the chunk is truncated and its CRC cannot be verified. */
23
+ crcOk: boolean | null;
24
+ truncated: boolean;
25
+ }
26
+ export interface PngDiagnosis {
27
+ damageClass: PngDamageClass;
28
+ confidence: Confidence;
29
+ notes: string[];
30
+ /** Offset where a valid PNG signature starts (0 normally, >0 = prefix). null = none. */
31
+ signatureOffset: number | null;
32
+ /** The 8-byte signature at offset 0 is exactly correct. */
33
+ signatureValid: boolean;
34
+ /** FTP/ASCII newline mangling detected (LF expanded to CRLF). */
35
+ newlineMangled: boolean;
36
+ hasIhdr: boolean;
37
+ hasIdat: boolean;
38
+ hasIend: boolean;
39
+ width: number | null;
40
+ height: number | null;
41
+ bitDepth: number | null;
42
+ colorType: number | null;
43
+ /** IHDR CRC matches its data. null = IHDR absent or too short to check. */
44
+ ihdrCrcOk: boolean | null;
45
+ /** Count of non-IHDR chunks whose CRC failed. */
46
+ badCrcChunks: number;
47
+ chunks: PngChunkInfo[];
48
+ }
49
+ export declare function diagnosePng(src: ByteSource, caps?: PngSafetyCaps): Promise<PngDiagnosis>;
50
+ //# sourceMappingURL=diagnose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose.d.ts","sourceRoot":"","sources":["../../src/png/diagnose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAEL,KAAK,aAAa,EAWnB,MAAM,aAAa,CAAC;AAGrB,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,kBAAkB,GAClB,WAAW,GACX,cAAc,GACd,WAAW,GACX,iBAAiB,GACjB,gBAAgB,GAChB,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,cAAc,CAAC;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wFAAwF;IACxF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,2DAA2D;IAC3D,cAAc,EAAE,OAAO,CAAC;IACxB,iEAAiE;IACjE,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,2EAA2E;IAC3E,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAmFD,wBAAsB,WAAW,CAC/B,GAAG,EAAE,UAAU,EACf,IAAI,GAAE,aAAgC,GACrC,OAAO,CAAC,YAAY,CAAC,CAyIvB"}
@@ -0,0 +1,261 @@
1
+ import { DEFAULT_PNG_CAPS, ascii4, findSignature, hasSignatureAt, isValidColorConfig, looksLikePngAt, looksNewlineMangled, parseIhdr, readU32BE, unmangleNewlines, walkChunksInMemory, } from './format.js';
2
+ import { crc32 } from '../crc32.js';
3
+ const CRC_WINDOW = 256 * 1024;
4
+ const SIGNATURE_SCAN_WINDOW = 64 * 1024;
5
+ /** Stream the chunk list from a ByteSource, CRC-checking data in windows. */
6
+ async function walkChunksFromSource(src, start) {
7
+ const chunks = [];
8
+ let pos = start;
9
+ let hasIhdr = false;
10
+ let hasIdat = false;
11
+ let hasIend = false;
12
+ let truncated = false;
13
+ let badCrcChunks = 0;
14
+ let idatCrcBad = false;
15
+ let ihdrFirst = false;
16
+ let ihdr = null;
17
+ let first = true;
18
+ while (pos + 8 <= src.size) {
19
+ const header = await src.read(pos, 8);
20
+ if (header.length < 8)
21
+ break;
22
+ const length = readU32BE(header, 0);
23
+ const type = ascii4(header, 4);
24
+ const dataOffset = pos + 8;
25
+ const crcOffset = dataOffset + length;
26
+ if (crcOffset + 4 > src.size) {
27
+ chunks.push({ type, offset: pos, length, crcOk: null, truncated: true });
28
+ truncated = true;
29
+ if (type === 'IDAT')
30
+ hasIdat = true;
31
+ break;
32
+ }
33
+ // CRC over type + data, windowed so a huge chunk never lands in memory.
34
+ let crc = crc32(header.subarray(4, 8));
35
+ let remaining = length;
36
+ let dataPos = dataOffset;
37
+ while (remaining > 0) {
38
+ const win = await src.read(dataPos, Math.min(CRC_WINDOW, remaining));
39
+ if (win.length === 0)
40
+ break;
41
+ crc = crc32(win, crc);
42
+ dataPos += win.length;
43
+ remaining -= win.length;
44
+ }
45
+ const crcBytes = await src.read(crcOffset, 4);
46
+ const crcStored = crcBytes.length === 4 ? readU32BE(crcBytes, 0) : -1;
47
+ const crcOk = crcStored === crc;
48
+ chunks.push({ type, offset: pos, length, crcOk, truncated: false });
49
+ if (type === 'IHDR') {
50
+ hasIhdr = true;
51
+ ihdrFirst = first;
52
+ const data = await src.read(dataOffset, Math.min(length, 13));
53
+ ihdr = { fields: parseIhdr(data), crcOk };
54
+ }
55
+ else if (type === 'IDAT') {
56
+ hasIdat = true;
57
+ if (!crcOk)
58
+ idatCrcBad = true;
59
+ }
60
+ if (type !== 'IHDR' && !crcOk)
61
+ badCrcChunks += 1;
62
+ if (type === 'IEND')
63
+ hasIend = true;
64
+ first = false;
65
+ pos = crcOffset + 4;
66
+ if (type === 'IEND')
67
+ break;
68
+ }
69
+ return { chunks, ihdr, hasIhdr, hasIdat, hasIend, truncated, badCrcChunks, idatCrcBad, ihdrFirst };
70
+ }
71
+ export async function diagnosePng(src, caps = DEFAULT_PNG_CAPS) {
72
+ const notes = [];
73
+ const base = {
74
+ notes,
75
+ signatureOffset: null,
76
+ signatureValid: false,
77
+ newlineMangled: false,
78
+ hasIhdr: false,
79
+ hasIdat: false,
80
+ hasIend: false,
81
+ width: null,
82
+ height: null,
83
+ bitDepth: null,
84
+ colorType: null,
85
+ ihdrCrcOk: null,
86
+ badCrcChunks: 0,
87
+ chunks: [],
88
+ };
89
+ const done = (damageClass, confidence) => ({
90
+ damageClass,
91
+ confidence,
92
+ ...base,
93
+ });
94
+ if (src.size === 0) {
95
+ notes.push('file is empty');
96
+ return done('not-png', 'high');
97
+ }
98
+ if (src.size < 8) {
99
+ notes.push(`file is only ${src.size} bytes: too small to be a PNG`);
100
+ return done('not-png', 'high');
101
+ }
102
+ const head = await src.read(0, Math.min(src.size, 16));
103
+ // --- FTP/ASCII newline mangling (check before generic signature damage,
104
+ // because a mangled signature still spells "PNG" at bytes 1..3). ---
105
+ if (!hasSignatureAt(head, 0) && looksNewlineMangled(head)) {
106
+ base.newlineMangled = true;
107
+ base.signatureValid = false;
108
+ notes.push('signature shows FTP/ASCII-mode newline mangling (every LF expanded to CRLF); ' +
109
+ 'the whole file was corrupted this way and can be un-mangled losslessly');
110
+ // Un-mangle the whole file to report the true underlying structure.
111
+ const whole = await src.read(0, src.size);
112
+ const fixed = unmangleNewlines(whole);
113
+ const walk = walkChunksInMemory(fixed, 8);
114
+ absorbInMemoryWalk(walk, fixed, base, notes);
115
+ base.signatureOffset = 0;
116
+ return done('signature-damage', 'high');
117
+ }
118
+ // --- Signature at offset 0? ---
119
+ if (hasSignatureAt(head, 0)) {
120
+ base.signatureValid = true;
121
+ base.signatureOffset = 0;
122
+ }
123
+ else {
124
+ // Look for a real signature behind a garbage prefix.
125
+ const scan = await src.read(0, Math.min(src.size, SIGNATURE_SCAN_WINDOW));
126
+ const at = findSignature(scan, SIGNATURE_SCAN_WINDOW);
127
+ if (at > 0) {
128
+ base.signatureOffset = at;
129
+ notes.push(`valid PNG signature found at offset ${at} (garbage prefix)`);
130
+ const walk = await walkChunksFromSource(src, at + 8);
131
+ absorbSourceWalk(walk, base, notes);
132
+ if (!walk.hasIhdr) {
133
+ notes.push('no IHDR after the prefix: the signature match may be coincidental');
134
+ return done('unknown', 'low');
135
+ }
136
+ return done('garbage-prefix', walk.truncated ? 'medium' : 'high');
137
+ }
138
+ // Recognizable but broken signature at 0 (some of the 8 bytes are wrong).
139
+ if (looksLikePngAt(head, 0)) {
140
+ base.signatureOffset = 0;
141
+ notes.push('the 8-byte signature is damaged but the "PNG" marker is present');
142
+ const walk = await walkChunksFromSource(src, 8);
143
+ absorbSourceWalk(walk, base, notes);
144
+ return done('signature-damage', walk.hasIhdr ? 'high' : 'medium');
145
+ }
146
+ notes.push('no PNG signature at the start and none found within the scan window');
147
+ return done('not-png', src.size > SIGNATURE_SCAN_WINDOW ? 'medium' : 'high');
148
+ }
149
+ // --- Valid signature at 0: walk the chunk stream. ---
150
+ const walk = await walkChunksFromSource(src, 8);
151
+ absorbSourceWalk(walk, base, notes);
152
+ if (!walk.hasIhdr) {
153
+ notes.push('no IHDR chunk: image geometry and pixel format are missing');
154
+ return done('missing-ihdr', 'high');
155
+ }
156
+ if (!walk.ihdrFirst) {
157
+ notes.push('IHDR is present but is not the first chunk (spec violation)');
158
+ }
159
+ // Dimension sanity (bomb / zeroed geometry) — evaluated before truncation so
160
+ // a suspicious IHDR is always surfaced.
161
+ const dimVerdict = classifyDimensions(base, caps, notes);
162
+ if (base.ihdrCrcOk === false) {
163
+ notes.push('IHDR CRC mismatch: the header (dimensions or pixel format) is corrupt');
164
+ if (dimVerdict === 'zero')
165
+ notes.push('declared dimensions are zero — likely blanked in transit');
166
+ return done('crc-error', 'high');
167
+ }
168
+ if (dimVerdict === 'bomb') {
169
+ return done('unknown', 'high');
170
+ }
171
+ if (dimVerdict === 'zero') {
172
+ return done('unknown', 'medium');
173
+ }
174
+ if (walk.truncated || !walk.hasIend) {
175
+ if (!walk.hasIdat) {
176
+ notes.push('file ends before any IDAT: image data is missing');
177
+ }
178
+ else {
179
+ notes.push('chunk stream is cut short (missing IEND / truncated chunk): image data is truncated');
180
+ notes.push('rows decoded before the cut are recoverable; rows after it are physically gone');
181
+ }
182
+ return done('truncated', walk.hasIdat ? 'high' : 'medium');
183
+ }
184
+ if (walk.idatCrcBad) {
185
+ notes.push('an IDAT chunk failed its CRC: the compressed image data is corrupt');
186
+ notes.push('repair will inflate what it can; rows above the damage may be recoverable');
187
+ return done('idat-corruption', 'high');
188
+ }
189
+ if (walk.badCrcChunks > 0) {
190
+ notes.push(`${walk.badCrcChunks} ancillary chunk(s) failed CRC; the image data itself is intact`);
191
+ return done('crc-error', 'high');
192
+ }
193
+ if (!walk.hasIdat) {
194
+ notes.push('no IDAT chunk: the file carries no image data');
195
+ return done('idat-corruption', 'medium');
196
+ }
197
+ return done('none', 'high');
198
+ }
199
+ function classifyDimensions(base, caps, notes) {
200
+ const w = base.width;
201
+ const h = base.height;
202
+ if (w === null || h === null)
203
+ return 'ok';
204
+ if (w <= 0 || h <= 0)
205
+ return 'zero';
206
+ if (w > caps.maxWidth || h > caps.maxHeight || w * h > caps.maxPixels) {
207
+ notes.push(`declared dimensions ${w}x${h} exceed the safety cap (${caps.maxPixels} px): ` +
208
+ 'possible decompression bomb — repair will refuse to allocate');
209
+ return 'bomb';
210
+ }
211
+ return 'ok';
212
+ }
213
+ /** Fold a streamed walk into the diagnosis base. */
214
+ function absorbSourceWalk(walk, base, notes) {
215
+ base.chunks = walk.chunks;
216
+ base.hasIhdr = walk.hasIhdr;
217
+ base.hasIdat = walk.hasIdat;
218
+ base.hasIend = walk.hasIend;
219
+ base.badCrcChunks = walk.badCrcChunks;
220
+ if (walk.ihdr) {
221
+ base.ihdrCrcOk = walk.ihdr.crcOk;
222
+ const f = walk.ihdr.fields;
223
+ if (f) {
224
+ base.width = f.width;
225
+ base.height = f.height;
226
+ base.bitDepth = f.bitDepth;
227
+ base.colorType = f.colorType;
228
+ if (!isValidColorConfig(f.bitDepth, f.colorType)) {
229
+ notes.push(`unusual bit depth/colour type combination (${f.bitDepth}/${f.colorType})`);
230
+ }
231
+ }
232
+ }
233
+ }
234
+ /** Fold an in-memory walk (used for the un-mangled newline case) into the base. */
235
+ function absorbInMemoryWalk(walk, fixed, base, notes) {
236
+ base.chunks = walk.chunks.map((c) => ({
237
+ type: c.type,
238
+ offset: c.offset,
239
+ length: c.length,
240
+ crcOk: c.crcOk,
241
+ truncated: c.truncated,
242
+ }));
243
+ base.hasIhdr = walk.chunks.some((c) => c.type === 'IHDR');
244
+ base.hasIdat = walk.chunks.some((c) => c.type === 'IDAT');
245
+ base.hasIend = walk.sawIend;
246
+ const ihdrChunk = walk.chunks.find((c) => c.type === 'IHDR');
247
+ if (ihdrChunk && !ihdrChunk.truncated) {
248
+ base.ihdrCrcOk = ihdrChunk.crcOk;
249
+ const f = parseIhdr(fixed.subarray(ihdrChunk.dataOffset, ihdrChunk.dataOffset + 13));
250
+ if (f) {
251
+ base.width = f.width;
252
+ base.height = f.height;
253
+ base.bitDepth = f.bitDepth;
254
+ base.colorType = f.colorType;
255
+ if (!isValidColorConfig(f.bitDepth, f.colorType)) {
256
+ notes.push(`unusual bit depth/colour type combination (${f.bitDepth}/${f.colorType})`);
257
+ }
258
+ }
259
+ }
260
+ }
261
+ //# sourceMappingURL=diagnose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose.js","sourceRoot":"","sources":["../../src/png/diagnose.ts"],"names":[],"mappings":"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"}