@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,335 @@
1
+ /**
2
+ * PNG repair by deterministic byte surgery plus row-level salvage.
3
+ *
4
+ * The engine is pure TypeScript: pako (MIT) does the incremental inflate and
5
+ * fast-png (MIT) re-encodes salvaged rasters. There is no wasm in the import
6
+ * chain, so this can ship in the browser bundle behind the `./png` subpath.
7
+ *
8
+ * What it does, per damage class:
9
+ * - signature-damage / newline-mangling → rewrite/undo, then decode normally;
10
+ * - garbage-prefix → strip everything before the signature;
11
+ * - crc-error → recompute the corrupt chunk CRC(s) once the data is verified;
12
+ * - zeroed/corrupt IHDR dimensions → brute-force against the stored IHDR CRC,
13
+ * falling back to factorizing the inflated IDAT size;
14
+ * - truncated / mid-IDAT corruption → inflate what survives (pako chunked,
15
+ * Z_SYNC_FLUSH), unfilter the complete scanlines and re-emit a shorter but
16
+ * valid PNG.
17
+ *
18
+ * Honesty rules (docs/03 §4, docs/14): a truncated or mid-stream-corrupted file
19
+ * is never reported as full success — the rows below the damage are physically
20
+ * gone because deflate cannot resync, and the report says exactly that.
21
+ * Decompression bombs are refused before any large allocation.
22
+ */
23
+ import { Inflate, Z_SYNC_FLUSH } from 'pako';
24
+ import { encode as encodePng, decode as decodePng } from 'fast-png';
25
+ import { crc32 } from '../crc32.js';
26
+ import { diagnosePng } from './diagnose.js';
27
+ import { DEFAULT_PNG_CAPS, PNG_SIGNATURE, channelsForColorType, hasSignatureAt, looksLikePngAt, parseIhdr, reconstructDimensions, scanlineStride, unfilterScanlines8, unmangleNewlines, walkChunksInMemory, writeU32BE, } from './format.js';
28
+ const INFLATE_OUTPUT_CHUNK = 32 * 1024;
29
+ /**
30
+ * Compressed input is pushed in small slices, each followed by Z_SYNC_FLUSH, so
31
+ * pako emits the output decoded so far. This is what makes mid-stream salvage
32
+ * work: when a later slice hits corruption, the rows decoded from earlier
33
+ * slices have already been captured. A larger slice would leave that prefix
34
+ * un-flushed inside pako and lost when the stream errors.
35
+ */
36
+ const INFLATE_INPUT_SLICE = 1024;
37
+ /**
38
+ * Inflate an IDAT stream defensively. Uses pako in streaming mode with
39
+ * Z_SYNC_FLUSH so that, when the stream is truncated or corrupt, the bytes
40
+ * decoded before the break are still emitted (that prefix is the salvage). The
41
+ * kept output is capped to guard against decompression bombs.
42
+ */
43
+ function safeInflate(idat, cap) {
44
+ const inflator = new Inflate({ chunkSize: INFLATE_OUTPUT_CHUNK });
45
+ const chunks = [];
46
+ let total = 0;
47
+ let capped = false;
48
+ inflator.onData = (chunk) => {
49
+ if (capped)
50
+ return;
51
+ if (total + chunk.length > cap) {
52
+ const room = cap - total;
53
+ if (room > 0) {
54
+ chunks.push(chunk.subarray(0, room));
55
+ total += room;
56
+ }
57
+ capped = true;
58
+ return;
59
+ }
60
+ chunks.push(chunk);
61
+ total += chunk.length;
62
+ };
63
+ // A corrupt stream makes pako set `err`; it does not throw, but guard anyway.
64
+ try {
65
+ for (let i = 0; i < idat.length; i += INFLATE_INPUT_SLICE) {
66
+ inflator.push(idat.subarray(i, Math.min(i + INFLATE_INPUT_SLICE, idat.length)), Z_SYNC_FLUSH);
67
+ if (inflator.ended || inflator.err !== 0 || capped)
68
+ break;
69
+ }
70
+ }
71
+ catch {
72
+ /* fall through with whatever was decoded */
73
+ }
74
+ const output = concat(chunks);
75
+ return { output, err: inflator.err, clean: inflator.err === 0 && inflator.ended, capped };
76
+ }
77
+ function concat(parts) {
78
+ let total = 0;
79
+ for (const p of parts)
80
+ total += p.length;
81
+ const out = new Uint8Array(total);
82
+ let pos = 0;
83
+ for (const p of parts) {
84
+ out.set(p, pos);
85
+ pos += p.length;
86
+ }
87
+ return out;
88
+ }
89
+ /** Concatenate all IDAT payloads, including the available bytes of a cut chunk. */
90
+ function assembleIdat(work, chunks) {
91
+ const parts = [];
92
+ for (const c of chunks) {
93
+ if (c.type !== 'IDAT')
94
+ continue;
95
+ const end = c.truncated ? c.dataOffset + c.availableData : c.crcOffset;
96
+ if (end > c.dataOffset)
97
+ parts.push(work.subarray(c.dataOffset, end));
98
+ }
99
+ return concat(parts);
100
+ }
101
+ /** Recompute and rewrite a chunk's CRC in place (over its current type+data). */
102
+ function recomputeChunkCrc(work, chunk) {
103
+ const crc = crc32(work.subarray(chunk.offset + 4, chunk.crcOffset));
104
+ writeU32BE(work, chunk.crcOffset, crc);
105
+ }
106
+ const IEND_CHUNK = Uint8Array.of(0x00, 0x00, 0x00, 0x00, // length 0
107
+ 0x49, 0x45, 0x4e, 0x44, // "IEND"
108
+ 0xae, 0x42, 0x60, 0x82);
109
+ /**
110
+ * Rebuild a canonical PNG from the complete chunks parsed out of `work`,
111
+ * dropping any truncated trailing chunk and trailing junk, and appending IEND
112
+ * when it is missing.
113
+ */
114
+ function rebuildFromChunks(work, chunks) {
115
+ const parts = [PNG_SIGNATURE];
116
+ let sawIend = false;
117
+ for (const c of chunks) {
118
+ if (c.truncated)
119
+ continue;
120
+ parts.push(work.subarray(c.offset, c.crcOffset + 4));
121
+ if (c.type === 'IEND')
122
+ sawIend = true;
123
+ }
124
+ if (!sawIend)
125
+ parts.push(IEND_CHUNK);
126
+ return concat(parts);
127
+ }
128
+ function tryDecode(image) {
129
+ try {
130
+ decodePng(image, { checkCrc: false });
131
+ return true;
132
+ }
133
+ catch {
134
+ return false;
135
+ }
136
+ }
137
+ export async function repairPng(src, opts) {
138
+ const caps = opts?.caps ?? DEFAULT_PNG_CAPS;
139
+ const diagnosis = await diagnosePng(src, caps);
140
+ const original = await src.read(0, src.size);
141
+ const actions = [];
142
+ const notes = [...diagnosis.notes];
143
+ const build = (outcome, image, extra) => ({
144
+ image,
145
+ report: {
146
+ outcome,
147
+ damageClass: diagnosis.damageClass,
148
+ actions,
149
+ notes,
150
+ bytesIn: src.size,
151
+ bytesOut: image?.length ?? 0,
152
+ width: extra.width ?? null,
153
+ height: extra.height ?? null,
154
+ rowsRecovered: extra.rowsRecovered ?? null,
155
+ rowsExpected: extra.rowsExpected ?? null,
156
+ verified: extra.verified ?? false,
157
+ },
158
+ });
159
+ if (diagnosis.damageClass === 'not-png') {
160
+ notes.push('input is not recognizable as a PNG; nothing to repair');
161
+ return build('failed', undefined, {});
162
+ }
163
+ // --- 1. Working buffer with a valid signature at offset 0. ---
164
+ let work;
165
+ if (diagnosis.newlineMangled) {
166
+ work = unmangleNewlines(original);
167
+ actions.push('reversed FTP/ASCII newline mangling (removed the CR inserted before every LF)');
168
+ }
169
+ else if (diagnosis.signatureOffset !== null && diagnosis.signatureOffset > 0) {
170
+ work = original.slice(diagnosis.signatureOffset);
171
+ actions.push(`stripped a ${diagnosis.signatureOffset}-byte garbage prefix`);
172
+ }
173
+ else {
174
+ work = original.slice();
175
+ }
176
+ if (!hasSignatureAt(work, 0)) {
177
+ if (work.length >= 8 && (looksLikePngAt(work, 0) || diagnosis.damageClass === 'signature-damage')) {
178
+ work.set(PNG_SIGNATURE, 0);
179
+ actions.push('rewrote the 8-byte PNG signature');
180
+ }
181
+ else {
182
+ notes.push('could not establish a valid PNG signature at the start of the file');
183
+ return build('failed', undefined, {});
184
+ }
185
+ }
186
+ // --- 2. Locate and parse IHDR. ---
187
+ const walk = walkChunksInMemory(work, 8);
188
+ const ihdrChunk = walk.chunks.find((c) => c.type === 'IHDR' && !c.truncated);
189
+ if (!ihdrChunk) {
190
+ notes.push('IHDR chunk is missing or truncated; geometry and pixel format cannot be reconstructed reliably');
191
+ return build('failed', undefined, {});
192
+ }
193
+ const ihdr = parseIhdr(work.subarray(ihdrChunk.dataOffset, ihdrChunk.dataOffset + 13));
194
+ if (!ihdr) {
195
+ notes.push('IHDR data is too short to parse');
196
+ return build('failed', undefined, {});
197
+ }
198
+ const channels = channelsForColorType(ihdr.colorType);
199
+ const ihdrCrcOk = ihdrChunk.crcOk === true;
200
+ // --- 3. Bomb guard: refuse sane-but-oversized declared geometry up front. ---
201
+ if (ihdr.width > 0 &&
202
+ ihdr.height > 0 &&
203
+ (ihdr.width > caps.maxWidth || ihdr.height > caps.maxHeight || ihdr.width * ihdr.height > caps.maxPixels)) {
204
+ notes.push(`refusing to process ${ihdr.width}x${ihdr.height}: exceeds the safety cap (possible decompression bomb)`);
205
+ return build('failed', undefined, { width: ihdr.width, height: ihdr.height });
206
+ }
207
+ // --- 4. Assemble and inflate the IDAT stream (capped). ---
208
+ const idat = assembleIdat(work, walk.chunks);
209
+ if (idat.length === 0) {
210
+ notes.push('no IDAT image data is present; pixels cannot be reconstructed');
211
+ return build('failed', undefined, { width: ihdr.width || null, height: ihdr.height || null });
212
+ }
213
+ const inflated = safeInflate(idat, caps.maxInflatedBytes);
214
+ if (inflated.capped) {
215
+ notes.push('inflated output hit the safety cap; refusing (possible decompression bomb)');
216
+ return build('failed', undefined, {});
217
+ }
218
+ // --- 5. Resolve dimensions. ---
219
+ let width = ihdr.width;
220
+ let height = ihdr.height;
221
+ let dimsRecovered = false;
222
+ let dimsExact = true;
223
+ if (channels !== null) {
224
+ const dimsSane = width > 0 && height > 0 && width <= caps.maxWidth && height <= caps.maxHeight && width * height <= caps.maxPixels;
225
+ const strideNow = dimsSane ? scanlineStride(width, channels, ihdr.bitDepth) : 0;
226
+ const dimsMatchInflate = dimsSane && inflated.clean && inflated.output.length % strideNow === 0 && inflated.output.length / strideNow === height;
227
+ const dimsTrusted = (ihdrCrcOk && dimsSane) || dimsMatchInflate;
228
+ if (!dimsTrusted) {
229
+ if (inflated.clean) {
230
+ const rec = reconstructDimensions({
231
+ tail: ihdr,
232
+ inflatedSize: inflated.output.length,
233
+ storedCrc: ihdrChunk.crcStored,
234
+ caps,
235
+ });
236
+ if (rec) {
237
+ width = rec.width;
238
+ height = rec.height;
239
+ dimsRecovered = true;
240
+ dimsExact = rec.exact;
241
+ writeU32BE(work, ihdrChunk.dataOffset, width);
242
+ writeU32BE(work, ihdrChunk.dataOffset + 4, height);
243
+ actions.push(rec.exact
244
+ ? `reconstructed dimensions ${width}x${height} (matched the stored IHDR CRC)`
245
+ : `reconstructed dimensions ${width}x${height} by factorizing the inflated size ` +
246
+ `(${rec.candidates} candidate(s); the CRC could not disambiguate)`);
247
+ }
248
+ else if (!dimsSane) {
249
+ notes.push('IHDR dimensions are unreadable and could not be reconstructed from the image data');
250
+ return build('failed', undefined, {});
251
+ }
252
+ }
253
+ else if (!dimsSane) {
254
+ notes.push('IHDR dimensions are unreadable and the image data is too damaged to reconstruct them');
255
+ return build('failed', undefined, {});
256
+ }
257
+ }
258
+ }
259
+ if (width <= 0 || height <= 0 || width > caps.maxWidth || height > caps.maxHeight || width * height > caps.maxPixels) {
260
+ notes.push('final dimensions are out of the safe range; refusing to allocate');
261
+ return build('failed', undefined, { width: width || null, height: height || null });
262
+ }
263
+ // --- 6. Decide full recovery vs. row salvage. ---
264
+ const strideFinal = channels !== null ? scanlineStride(width, channels, ihdr.bitDepth) : 0;
265
+ const expected = channels !== null ? height * strideFinal : 0;
266
+ const fullData = channels === null ? inflated.clean : inflated.clean && inflated.output.length === expected;
267
+ // Fix IHDR CRC when we changed the dimensions, or when the CRC field alone
268
+ // was corrupt but the data has proven itself by inflating cleanly and fully.
269
+ if (dimsRecovered) {
270
+ recomputeChunkCrc(work, ihdrChunk);
271
+ }
272
+ else if (!ihdrCrcOk && fullData) {
273
+ recomputeChunkCrc(work, ihdrChunk);
274
+ actions.push('recomputed the IHDR CRC (its CRC field was corrupt; dimensions verified against the image data)');
275
+ }
276
+ if (fullData) {
277
+ // All image data survived: recompute any remaining bad CRCs (the data is
278
+ // proven intact) and re-emit a canonical file.
279
+ let fixed = 0;
280
+ for (const c of walk.chunks) {
281
+ if (c.truncated || c.type === 'IHDR')
282
+ continue;
283
+ if (c.crcOk === false) {
284
+ recomputeChunkCrc(work, c);
285
+ fixed += 1;
286
+ }
287
+ }
288
+ if (fixed > 0)
289
+ actions.push(`recomputed ${fixed} corrupt chunk CRC(s)`);
290
+ const image = rebuildFromChunks(work, walk.chunks);
291
+ const verified = tryDecode(image);
292
+ if (!verified) {
293
+ notes.push('re-emitted file failed to re-decode; not claiming success');
294
+ return build('failed', image, { width, height, rowsRecovered: height, rowsExpected: height, verified });
295
+ }
296
+ if (dimsRecovered && !dimsExact) {
297
+ notes.push('dimensions were reconstructed without CRC confirmation; the aspect ratio is a best guess');
298
+ return build('partial', image, { width, height, rowsRecovered: height, rowsExpected: height, verified });
299
+ }
300
+ return build('success', image, { width, height, rowsRecovered: height, rowsExpected: height, verified });
301
+ }
302
+ // --- 7. Salvage path: only complete 8-bit non-interlaced scanlines. ---
303
+ if (channels === null || ihdr.bitDepth !== 8 || ihdr.interlace !== 0 || ihdr.colorType === 3) {
304
+ notes.push('the surviving image data cannot be safely re-encoded: row salvage supports 8-bit, ' +
305
+ 'non-interlaced, non-indexed images only');
306
+ return build('failed', undefined, { width, height, rowsExpected: height });
307
+ }
308
+ const rows = Math.floor(inflated.output.length / strideFinal);
309
+ if (rows <= 0) {
310
+ notes.push('no complete scanline survived before the damage; nothing to salvage');
311
+ return build('failed', undefined, { width, height, rowsRecovered: 0, rowsExpected: height });
312
+ }
313
+ const samples = unfilterScanlines8(inflated.output, rows, width, channels);
314
+ let image;
315
+ try {
316
+ image = encodePng({ width, height: rows, data: samples, channels, depth: 8 });
317
+ }
318
+ catch {
319
+ notes.push('failed to re-encode the salvaged rows');
320
+ return build('failed', undefined, { width, height, rowsRecovered: rows, rowsExpected: height });
321
+ }
322
+ const verified = tryDecode(image);
323
+ if (inflated.err !== 0 && inflated.output.length >= expected) {
324
+ // Structurally decoded to full size but the zlib checksum failed: pixels
325
+ // near the corruption may be wrong even though every row is present.
326
+ notes.push('the image data decoded to full size but its zlib checksum failed: pixels near the ' +
327
+ 'corruption may be wrong. Reported as partial rather than clean.');
328
+ }
329
+ else {
330
+ notes.push(`${rows} of ${height} rows recovered. Rows below the damage are physically gone — ` +
331
+ 'deflate cannot resync past a truncation or a mid-stream corruption.');
332
+ }
333
+ return build('partial', image, { width, height, rowsRecovered: rows, rowsExpected: height, verified });
334
+ }
335
+ //# sourceMappingURL=repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.js","sourceRoot":"","sources":["../../src/png/repair.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { ByteSource } from '../byte-source.js';
2
+ import type { Confidence } from '../types.js';
3
+ import { decodePageSize, readU32 } from './format.js';
4
+ export type SqliteDamageClass = 'none' | 'malformed' | 'header-damage' | 'truncated' | 'not-sqlite' | 'encrypted' | 'unknown';
5
+ export interface SqliteDiagnosis {
6
+ damageClass: SqliteDamageClass;
7
+ confidence: Confidence;
8
+ notes: string[];
9
+ /** Page size in bytes, when the header is readable. */
10
+ pageSize: number | null;
11
+ /** In-header database size in pages (offset 28), when present. */
12
+ headerPageCount: number | null;
13
+ /** Page count implied by the file size (floor(size / pageSize)). */
14
+ filePageCount: number | null;
15
+ textEncoding: number | null;
16
+ /** True when a `-wal` sidecar was flagged in opts. */
17
+ walPresent: boolean;
18
+ }
19
+ export interface DiagnoseSqliteOptions {
20
+ /** Pass true when a sibling `-wal` file exists next to the database. */
21
+ walPresent?: boolean;
22
+ }
23
+ /**
24
+ * Structural SQLite diagnosis via own byte parsing: header magic, page size,
25
+ * in-header page count vs file size, freelist sanity, text encoding and a light
26
+ * b-tree spot-check of page 1 and the declared table roots. Never opens the
27
+ * database with a real engine, so it is safe on arbitrarily damaged input.
28
+ */
29
+ export declare function diagnoseSqlite(src: ByteSource, opts?: DiagnoseSqliteOptions): Promise<SqliteDiagnosis>;
30
+ export { decodePageSize, readU32 };
31
+ //# sourceMappingURL=diagnose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose.d.ts","sourceRoot":"","sources":["../../src/sqlite/diagnose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAGL,cAAc,EAMd,OAAO,EACR,MAAM,aAAa,CAAC;AAGrB,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,WAAW,GACX,eAAe,GACf,WAAW,GACX,YAAY,GACZ,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,iBAAiB,CAAC;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kEAAkE;IAClE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,oEAAoE;IACpE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,sDAAsD;IACtD,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAKD;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,UAAU,EACf,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,eAAe,CAAC,CA4G1B;AA6ED,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC"}
@@ -0,0 +1,169 @@
1
+ import { HEADER_SIZE, SQLITE_MAGIC, decodePageSize, hasMagic, isValidPageSize, parseBtreePageHeader, parseHeader, readU16, readU32, } from './format.js';
2
+ import { PageReader, parseSchema } from './scan.js';
3
+ /** First page read window — enough for the header and a light page-1 check. */
4
+ const PROBE_WINDOW = 4096;
5
+ /**
6
+ * Structural SQLite diagnosis via own byte parsing: header magic, page size,
7
+ * in-header page count vs file size, freelist sanity, text encoding and a light
8
+ * b-tree spot-check of page 1 and the declared table roots. Never opens the
9
+ * database with a real engine, so it is safe on arbitrarily damaged input.
10
+ */
11
+ export async function diagnoseSqlite(src, opts = {}) {
12
+ const notes = [];
13
+ const walPresent = opts.walPresent === true;
14
+ const base = {
15
+ notes,
16
+ pageSize: null,
17
+ headerPageCount: null,
18
+ filePageCount: null,
19
+ textEncoding: null,
20
+ walPresent,
21
+ };
22
+ const done = (damageClass, confidence) => {
23
+ if (walPresent)
24
+ notes.push('a -wal sidecar was flagged; uncheckpointed changes may exist and will be overlaid during recovery');
25
+ return { damageClass, confidence, ...base };
26
+ };
27
+ if (src.size === 0) {
28
+ notes.push('file is empty');
29
+ return done('not-sqlite', 'high');
30
+ }
31
+ const probe = await src.read(0, Math.min(src.size, PROBE_WINDOW));
32
+ if (!hasMagic(probe)) {
33
+ return classifyNoMagic(probe, src.size, notes, done);
34
+ }
35
+ if (src.size < HEADER_SIZE) {
36
+ notes.push(`file has the SQLite magic but is only ${src.size} bytes (< 100-byte header)`);
37
+ return done('truncated', 'high');
38
+ }
39
+ const rawPageSize = readU16(probe, 16);
40
+ if (!isValidPageSize(rawPageSize)) {
41
+ notes.push(`header page-size field is invalid (raw value ${rawPageSize})`);
42
+ base.pageSize = null;
43
+ return done('header-damage', 'high');
44
+ }
45
+ const header = parseHeader(probe);
46
+ if (!header) {
47
+ notes.push('header magic present but the 100-byte header could not be parsed');
48
+ return done('header-damage', 'high');
49
+ }
50
+ base.pageSize = header.pageSize;
51
+ base.headerPageCount = header.pageCount;
52
+ base.textEncoding = header.textEncoding;
53
+ base.filePageCount = Math.floor(src.size / header.pageSize);
54
+ if (header.textEncoding < 1 || header.textEncoding > 3) {
55
+ notes.push(`header text-encoding field is out of range (${header.textEncoding})`);
56
+ return done('header-damage', 'medium');
57
+ }
58
+ // Truncation: a clean database file size is an exact multiple of the page size.
59
+ if (src.size % header.pageSize !== 0) {
60
+ notes.push(`file size (${src.size}) is not a multiple of the page size (${header.pageSize}): last page is cut`);
61
+ return done('truncated', 'high');
62
+ }
63
+ // In-header page count vs actual file size.
64
+ if (header.pageCountValid && header.pageCount !== base.filePageCount) {
65
+ if (header.pageCount > base.filePageCount) {
66
+ notes.push(`header declares ${header.pageCount} pages but the file holds ${base.filePageCount}: pages are missing (truncated)`);
67
+ return done('truncated', 'high');
68
+ }
69
+ notes.push(`header declares ${header.pageCount} pages but the file holds ${base.filePageCount}: header page-count is wrong`);
70
+ return done('header-damage', 'high');
71
+ }
72
+ // Freelist sanity.
73
+ const pageCount = base.filePageCount;
74
+ if (header.freelistTrunkPage < 0 || header.freelistTrunkPage > pageCount) {
75
+ notes.push(`freelist trunk page (${header.freelistTrunkPage}) is out of range`);
76
+ return done('header-damage', 'medium');
77
+ }
78
+ if (header.freelistPageCount > pageCount) {
79
+ notes.push(`freelist page count (${header.freelistPageCount}) exceeds the file's page count`);
80
+ return done('header-damage', 'medium');
81
+ }
82
+ // Light b-tree spot-check: page 1 must be a table b-tree, and declared table
83
+ // roots must point at real b-tree pages. Catches zeroed interior/root pages.
84
+ const structural = await checkStructure(src, header, notes);
85
+ if (structural === 'page1-bad') {
86
+ notes.push('page 1 is not a valid b-tree page (schema page damaged)');
87
+ return done('malformed', 'high');
88
+ }
89
+ if (structural === 'roots-bad') {
90
+ notes.push('one or more table root pages are damaged; some rows are only recoverable by raw scan');
91
+ return done('malformed', 'medium');
92
+ }
93
+ if (structural === 'schema-unreadable') {
94
+ notes.push('schema table could not be read; database structure is damaged');
95
+ return done('malformed', 'medium');
96
+ }
97
+ if (header.readVersion > 2 || header.writeVersion > 2) {
98
+ notes.push(`header read/write format version is newer than expected (${header.readVersion}/${header.writeVersion})`);
99
+ return done('unknown', 'low');
100
+ }
101
+ return done('none', 'high');
102
+ }
103
+ async function checkStructure(src, header, notes) {
104
+ if (!header)
105
+ return 'schema-unreadable';
106
+ const page1 = await src.read(0, Math.min(src.size, header.pageSize));
107
+ const p1hdr = parseBtreePageHeader(page1, 1);
108
+ if (!p1hdr)
109
+ return 'page1-bad';
110
+ // Read the whole file to spot-check table roots (databases are small here).
111
+ const bytes = await src.read(0, src.size);
112
+ const reader = new PageReader(bytes, header.pageSize, header.reservedBytes);
113
+ const schema = parseSchema(reader, header.textEncoding);
114
+ if (!schema.ok)
115
+ return 'schema-unreadable';
116
+ let rootsBad = 0;
117
+ let tables = 0;
118
+ for (const entry of schema.entries) {
119
+ if (entry.type !== 'table')
120
+ continue;
121
+ if (entry.name.startsWith('sqlite_'))
122
+ continue;
123
+ tables++;
124
+ const page = reader.page(entry.rootpage);
125
+ if (!page || !parseBtreePageHeader(page, entry.rootpage))
126
+ rootsBad++;
127
+ }
128
+ if (tables > 0 && rootsBad > 0) {
129
+ notes.push(`${rootsBad} of ${tables} table root page(s) are unreadable`);
130
+ return 'roots-bad';
131
+ }
132
+ return 'ok';
133
+ }
134
+ /**
135
+ * No magic: decide between plain "not a SQLite file" and "possibly encrypted".
136
+ * SQLCipher/SEE databases have no plaintext magic — their first page is
137
+ * ciphertext — so the best we can do is a high-entropy heuristic, reported with
138
+ * low confidence and an honest note.
139
+ */
140
+ function classifyNoMagic(probe, size, notes, done) {
141
+ // How many leading magic bytes survived (partial header damage vs unrelated file).
142
+ let matched = 0;
143
+ for (let i = 0; i < SQLITE_MAGIC.length && i < probe.length; i++) {
144
+ if (probe[i] === SQLITE_MAGIC[i])
145
+ matched++;
146
+ else
147
+ break;
148
+ }
149
+ if (matched >= 8) {
150
+ notes.push(`header magic is partially corrupted (${matched}/16 bytes match)`);
151
+ return done('header-damage', 'medium');
152
+ }
153
+ const window = probe.subarray(0, Math.min(probe.length, 256));
154
+ const distinct = new Set(window).size;
155
+ let zeros = 0;
156
+ for (const b of window)
157
+ if (b === 0)
158
+ zeros++;
159
+ const looksEncrypted = size >= 512 && size % 512 === 0 && distinct > 200 && zeros <= window.length / 32;
160
+ if (looksEncrypted) {
161
+ notes.push('no SQLite magic, but the file is page-aligned with high-entropy content: it may be an encrypted (SQLCipher/SEE) database, which cannot be recovered without the key');
162
+ return done('encrypted', 'low');
163
+ }
164
+ notes.push('no "SQLite format 3" magic in the first bytes: not a SQLite database');
165
+ return done('not-sqlite', size > PROBE_WINDOW ? 'medium' : 'high');
166
+ }
167
+ // Re-export a couple of helpers used elsewhere so callers need only this module.
168
+ export { decodePageSize, readU32 };
169
+ //# sourceMappingURL=diagnose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose.js","sourceRoot":"","sources":["../../src/sqlite/diagnose.ts"],"names":[],"mappings":"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"}