modern-tar 0.3.0 → 0.3.2
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 +10 -4
- package/dist/fs/index.d.ts +1 -1
- package/dist/fs/index.js +2 -2
- package/dist/{index-C8X7IkYR.d.ts → index-1Ec89lu7.d.ts} +17 -3
- package/dist/web/index.d.ts +2 -2
- package/dist/web/index.js +1 -1
- package/dist/{web-LcCN87Qy.js → web-Daso6SHn.js} +22 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -246,7 +246,7 @@ const entries = [
|
|
|
246
246
|
const tarBuffer = await packTar(entries);
|
|
247
247
|
```
|
|
248
248
|
|
|
249
|
-
#### `unpackTar(archive: ArrayBuffer | Uint8Array
|
|
249
|
+
#### `unpackTar(archive: ArrayBuffer | Uint8Array | ReadableStream<Uint8Array>, options?: UnpackOptions): Promise<ParsedTarEntryWithData[]>`
|
|
250
250
|
|
|
251
251
|
Extract all entries from a tar archive buffer with optional filtering and transformation.
|
|
252
252
|
|
|
@@ -287,7 +287,7 @@ const stream2 = controller.add({ name: "file2.txt", size: 4 });
|
|
|
287
287
|
controller.finalize();
|
|
288
288
|
```
|
|
289
289
|
|
|
290
|
-
#### `createTarDecoder(): TransformStream<Uint8Array, ParsedTarEntry>`
|
|
290
|
+
#### `createTarDecoder(options?: DecoderOptions): TransformStream<Uint8Array, ParsedTarEntry>`
|
|
291
291
|
|
|
292
292
|
Create a transform stream that parses tar bytes into entries.
|
|
293
293
|
|
|
@@ -387,6 +387,7 @@ const tarStream = createReadStream('backup.tar');
|
|
|
387
387
|
const extractStream = unpackTar('/restore/location', {
|
|
388
388
|
strip: 1,
|
|
389
389
|
fmode: 0o644, // Set consistent file permissions
|
|
390
|
+
strict: true, // Enable strict validation
|
|
390
391
|
});
|
|
391
392
|
await pipeline(tarStream, extractStream);
|
|
392
393
|
```
|
|
@@ -455,8 +456,13 @@ interface ParsedTarEntryWithData {
|
|
|
455
456
|
}
|
|
456
457
|
|
|
457
458
|
// Platform-neutral configuration for unpacking
|
|
458
|
-
interface
|
|
459
|
-
/**
|
|
459
|
+
interface DecoderOptions {
|
|
460
|
+
/** Enable strict validation (e.g., throw on invalid checksums) */
|
|
461
|
+
strict?: boolean;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
interface UnpackOptions extends DecoderOptions {
|
|
465
|
+
/** Number of leading path components to strip from entry names (e.g., strip: 1 removes first directory) */
|
|
460
466
|
strip?: number;
|
|
461
467
|
/** Filter function to include/exclude entries (return false to skip) */
|
|
462
468
|
filter?: (header: TarHeader) => boolean;
|
package/dist/fs/index.d.ts
CHANGED
package/dist/fs/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BLOCK_SIZE, createTarDecoder, createTarHeader, createTarOptionsTransformer, createTarPacker, encoder, generatePax } from "../web-
|
|
1
|
+
import { BLOCK_SIZE, createTarDecoder, createTarHeader, createTarOptionsTransformer, createTarPacker, encoder, generatePax } from "../web-Daso6SHn.js";
|
|
2
2
|
import { createReadStream, createWriteStream } from "node:fs";
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import * as path from "node:path";
|
|
@@ -269,7 +269,7 @@ function unpackTar(directoryPath, options = {}) {
|
|
|
269
269
|
const resolvedDestDir = normalizePath(path.resolve(directoryPath));
|
|
270
270
|
const validatedDirs = new Set([resolvedDestDir]);
|
|
271
271
|
await fs.mkdir(resolvedDestDir, { recursive: true });
|
|
272
|
-
const reader = readable.pipeThrough(createTarDecoder()).pipeThrough(createTarOptionsTransformer(options)).getReader();
|
|
272
|
+
const reader = readable.pipeThrough(createTarDecoder(options)).pipeThrough(createTarOptionsTransformer(options)).getReader();
|
|
273
273
|
try {
|
|
274
274
|
const maxDepth = options.maxDepth ?? 1024;
|
|
275
275
|
while (true) {
|
|
@@ -142,13 +142,26 @@ interface ParsedTarEntryWithData {
|
|
|
142
142
|
header: TarHeader;
|
|
143
143
|
data: Uint8Array;
|
|
144
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Configuration options for creating a tar decoder stream.
|
|
147
|
+
*/
|
|
148
|
+
interface DecoderOptions {
|
|
149
|
+
/**
|
|
150
|
+
* Enable strict validation of the tar archive.
|
|
151
|
+
* When true, the decoder will throw errors for data corruption issues:
|
|
152
|
+
* - Invalid checksums (indicates header corruption)
|
|
153
|
+
* - Invalid USTAR magic string (format violation)
|
|
154
|
+
* @default false
|
|
155
|
+
*/
|
|
156
|
+
strict?: boolean;
|
|
157
|
+
}
|
|
145
158
|
/**
|
|
146
159
|
* Platform-neutral configuration options for extracting tar archives.
|
|
147
160
|
*
|
|
148
161
|
* These options work with any tar extraction implementation and are not tied
|
|
149
162
|
* to specific platforms like Node.js filesystem APIs.
|
|
150
163
|
*/
|
|
151
|
-
interface UnpackOptions {
|
|
164
|
+
interface UnpackOptions extends DecoderOptions {
|
|
152
165
|
/** Number of leading path components to strip from entry names (e.g., strip: 1 removes first directory) */
|
|
153
166
|
strip?: number;
|
|
154
167
|
/** Filter function to include/exclude entries (return false to skip) */
|
|
@@ -372,6 +385,7 @@ declare function createTarPacker(): {
|
|
|
372
385
|
/**
|
|
373
386
|
* Create a transform stream that parses tar bytes into entries.
|
|
374
387
|
*
|
|
388
|
+
* @param options - Optional configuration for the decoder using {@link DecoderOptions}.
|
|
375
389
|
* @returns `TransformStream` that converts tar archive bytes to {@link ParsedTarEntry} objects.
|
|
376
390
|
* @example
|
|
377
391
|
* ```typescript
|
|
@@ -385,6 +399,6 @@ declare function createTarPacker(): {
|
|
|
385
399
|
* // Process entry.body stream as needed
|
|
386
400
|
* }
|
|
387
401
|
*/
|
|
388
|
-
declare function createTarDecoder(): TransformStream<Uint8Array, ParsedTarEntry>;
|
|
402
|
+
declare function createTarDecoder(options?: DecoderOptions): TransformStream<Uint8Array, ParsedTarEntry>;
|
|
389
403
|
//#endregion
|
|
390
|
-
export { type ParsedTarEntry, type ParsedTarEntryWithData, type TarEntry, type TarEntryData, type TarHeader, type TarPackController, type UnpackOptions, createGzipDecoder, createGzipEncoder, createTarDecoder, createTarOptionsTransformer, createTarPacker, packTar, unpackTar };
|
|
404
|
+
export { type DecoderOptions, type ParsedTarEntry, type ParsedTarEntryWithData, type TarEntry, type TarEntryData, type TarHeader, type TarPackController, type UnpackOptions, createGzipDecoder, createGzipEncoder, createTarDecoder, createTarOptionsTransformer, createTarPacker, packTar, unpackTar };
|
package/dist/web/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { ParsedTarEntry, ParsedTarEntryWithData, TarEntry, TarEntryData, TarHeader, TarPackController, UnpackOptions, createGzipDecoder, createGzipEncoder, createTarDecoder, createTarOptionsTransformer, createTarPacker, packTar, unpackTar } from "../index-
|
|
2
|
-
export { ParsedTarEntry, ParsedTarEntryWithData, TarEntry, TarEntryData, TarHeader, TarPackController, UnpackOptions, createGzipDecoder, createGzipEncoder, createTarDecoder, createTarOptionsTransformer, createTarPacker, packTar, unpackTar };
|
|
1
|
+
import { DecoderOptions, ParsedTarEntry, ParsedTarEntryWithData, TarEntry, TarEntryData, TarHeader, TarPackController, UnpackOptions, createGzipDecoder, createGzipEncoder, createTarDecoder, createTarOptionsTransformer, createTarPacker, packTar, unpackTar } from "../index-1Ec89lu7.js";
|
|
2
|
+
export { DecoderOptions, ParsedTarEntry, ParsedTarEntryWithData, TarEntry, TarEntryData, TarHeader, TarPackController, UnpackOptions, createGzipDecoder, createGzipEncoder, createTarDecoder, createTarOptionsTransformer, createTarPacker, packTar, unpackTar };
|
package/dist/web/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { createGzipDecoder, createGzipEncoder, createTarDecoder, createTarOptionsTransformer, createTarPacker, packTar, unpackTar } from "../web-
|
|
1
|
+
import { createGzipDecoder, createGzipEncoder, createTarDecoder, createTarOptionsTransformer, createTarPacker, packTar, unpackTar } from "../web-Daso6SHn.js";
|
|
2
2
|
|
|
3
3
|
export { createGzipDecoder, createGzipEncoder, createTarDecoder, createTarOptionsTransformer, createTarPacker, packTar, unpackTar };
|
|
@@ -291,7 +291,7 @@ function readString(view, offset, size) {
|
|
|
291
291
|
*/
|
|
292
292
|
function readOctal(view, offset, size) {
|
|
293
293
|
const octalString = readString(view, offset, size).trim();
|
|
294
|
-
return octalString ?
|
|
294
|
+
return octalString ? parseInt(octalString, 8) : 0;
|
|
295
295
|
}
|
|
296
296
|
/**
|
|
297
297
|
* Reads a numeric field that can be octal or POSIX base-256.
|
|
@@ -557,6 +557,7 @@ const metaEntryParsers = {
|
|
|
557
557
|
/**
|
|
558
558
|
* Create a transform stream that parses tar bytes into entries.
|
|
559
559
|
*
|
|
560
|
+
* @param options - Optional configuration for the decoder using {@link DecoderOptions}.
|
|
560
561
|
* @returns `TransformStream` that converts tar archive bytes to {@link ParsedTarEntry} objects.
|
|
561
562
|
* @example
|
|
562
563
|
* ```typescript
|
|
@@ -570,7 +571,8 @@ const metaEntryParsers = {
|
|
|
570
571
|
* // Process entry.body stream as needed
|
|
571
572
|
* }
|
|
572
573
|
*/
|
|
573
|
-
function createTarDecoder() {
|
|
574
|
+
function createTarDecoder(options = {}) {
|
|
575
|
+
const strict = options.strict ?? false;
|
|
574
576
|
let buffer = new Uint8Array(0);
|
|
575
577
|
let currentEntry = null;
|
|
576
578
|
let paxGlobals = {};
|
|
@@ -607,7 +609,7 @@ function createTarDecoder() {
|
|
|
607
609
|
return;
|
|
608
610
|
}
|
|
609
611
|
}
|
|
610
|
-
const header = parseUstarHeader(headerBlock);
|
|
612
|
+
const header = parseUstarHeader(headerBlock, strict);
|
|
611
613
|
const metaParser = metaEntryParsers[header.type];
|
|
612
614
|
if (metaParser) {
|
|
613
615
|
const dataSize = header.size;
|
|
@@ -650,18 +652,22 @@ function createTarDecoder() {
|
|
|
650
652
|
if (offset > 0) buffer = buffer.slice(offset);
|
|
651
653
|
},
|
|
652
654
|
flush(controller) {
|
|
653
|
-
if (currentEntry) {
|
|
654
|
-
const error = /* @__PURE__ */ new Error(
|
|
655
|
+
if (currentEntry) if (strict) {
|
|
656
|
+
const error = /* @__PURE__ */ new Error(`Tar archive is truncated. Expected ${currentEntry.header.size} bytes but received ${currentEntry.header.size - currentEntry.bytesLeft}.`);
|
|
655
657
|
currentEntry.controller.error(error);
|
|
656
658
|
controller.error(error);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
+
} else try {
|
|
660
|
+
currentEntry.controller.close();
|
|
661
|
+
} catch {}
|
|
662
|
+
if (strict && buffer.some((b) => b !== 0)) controller.error(/* @__PURE__ */ new Error("Unexpected data at end of archive."));
|
|
659
663
|
}
|
|
660
664
|
});
|
|
661
665
|
}
|
|
662
|
-
function parseUstarHeader(block) {
|
|
663
|
-
if (!validateChecksum(block)) throw new Error("Invalid tar header checksum.");
|
|
666
|
+
function parseUstarHeader(block, strict) {
|
|
667
|
+
if (strict && !validateChecksum(block)) throw new Error("Invalid tar header checksum.");
|
|
664
668
|
const typeflag = readString(block, USTAR.typeflag.offset, USTAR.typeflag.size);
|
|
669
|
+
const magic = readString(block, USTAR.magic.offset, USTAR.magic.size);
|
|
670
|
+
if (strict && magic !== "ustar") throw new Error(`Invalid USTAR magic: expected "ustar", got "${magic}"`);
|
|
665
671
|
return {
|
|
666
672
|
name: readString(block, USTAR.name.offset, USTAR.name.size),
|
|
667
673
|
mode: readOctal(block, USTAR.mode.offset, USTAR.mode.size),
|
|
@@ -672,7 +678,7 @@ function parseUstarHeader(block) {
|
|
|
672
678
|
checksum: readOctal(block, USTAR.checksum.offset, USTAR.checksum.size),
|
|
673
679
|
type: FLAGTYPE[typeflag] || "file",
|
|
674
680
|
linkname: readString(block, USTAR.linkname.offset, USTAR.linkname.size),
|
|
675
|
-
magic
|
|
681
|
+
magic,
|
|
676
682
|
uname: readString(block, USTAR.uname.offset, USTAR.uname.size),
|
|
677
683
|
gname: readString(block, USTAR.gname.offset, USTAR.gname.size),
|
|
678
684
|
prefix: readString(block, USTAR.prefix.offset, USTAR.prefix.size)
|
|
@@ -685,7 +691,7 @@ function parsePax(buffer) {
|
|
|
685
691
|
while (offset < buffer.length) {
|
|
686
692
|
const spaceIndex = buffer.indexOf(32, offset);
|
|
687
693
|
if (spaceIndex === -1) break;
|
|
688
|
-
const length =
|
|
694
|
+
const length = parseInt(decoder.decode(buffer.subarray(offset, spaceIndex)), 10);
|
|
689
695
|
if (Number.isNaN(length) || length === 0) break;
|
|
690
696
|
const recordEnd = offset + length;
|
|
691
697
|
const [key, value] = decoder.decode(buffer.subarray(spaceIndex + 1, recordEnd - 1)).split("=", 2);
|
|
@@ -699,16 +705,16 @@ function parsePax(buffer) {
|
|
|
699
705
|
overrides.linkname = value;
|
|
700
706
|
break;
|
|
701
707
|
case "size":
|
|
702
|
-
overrides.size =
|
|
708
|
+
overrides.size = parseInt(value, 10);
|
|
703
709
|
break;
|
|
704
710
|
case "mtime":
|
|
705
|
-
overrides.mtime =
|
|
711
|
+
overrides.mtime = parseFloat(value);
|
|
706
712
|
break;
|
|
707
713
|
case "uid":
|
|
708
|
-
overrides.uid =
|
|
714
|
+
overrides.uid = parseInt(value, 10);
|
|
709
715
|
break;
|
|
710
716
|
case "gid":
|
|
711
|
-
overrides.gid =
|
|
717
|
+
overrides.gid = parseInt(value, 10);
|
|
712
718
|
break;
|
|
713
719
|
case "uname":
|
|
714
720
|
overrides.uname = value;
|
|
@@ -853,7 +859,7 @@ async function unpackTar(archive, options = {}) {
|
|
|
853
859
|
controller.close();
|
|
854
860
|
} });
|
|
855
861
|
const results = [];
|
|
856
|
-
const reader = sourceStream.pipeThrough(createTarDecoder()).pipeThrough(createTarOptionsTransformer(options)).getReader();
|
|
862
|
+
const reader = sourceStream.pipeThrough(createTarDecoder(options)).pipeThrough(createTarOptionsTransformer(options)).getReader();
|
|
857
863
|
try {
|
|
858
864
|
while (true) {
|
|
859
865
|
const { done, value: entry } = await reader.read();
|