archive-codec 0.0.0 → 1.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/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/index.cjs +80 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +72 -0
- package/package.json +89 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Joseph Mearman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# archive-codec
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ExaDev/archive-codec) [](https://www.npmjs.com/package/archive-codec) [](https://github.com/ExaDev/archive-codec/releases/latest) [](https://github.com/ExaDev/archive-codec/actions)
|
|
4
|
+
|
|
5
|
+
> Recursive archive (ZIP-in-ZIP) detection and walking with depth and cumulative decompressed-size guards — zero document-format knowledge, the archive utility package for the [documents.js family](https://github.com/ExaDev). Worker-isomorphic: the same code runs under Node and inside a Cloudflare Workers isolate.
|
|
6
|
+
|
|
7
|
+
Created for [documents.js#564](https://github.com/ExaDev/documents.js/issues/564): nothing in the ecosystem recursed into a nested archive. Most concretely, OOXML's embedded-object model — a docx/pptx carrying a genuinely separate ZIP blob at `word/embeddings/oleObject1.xlsx` — had no safe handling anywhere, and no package guarded against recursive-archive inputs at all (`byte-codec`'s 512 MiB per-stream inflate cap does not compose across recursion). A new sibling was chosen over extending `byte-codec` (whose charter is byte/image primitives, zero container-format knowledge) or doing it inline in `documents.js` (which would repeat the duplication `byte-codec`'s own extraction was meant to avoid). No family package depends on it yet; `documents.js` consuming it for OOXML embedded objects is planned follow-up work.
|
|
8
|
+
|
|
9
|
+
Scope for v1: **ZIP containers only** — read and write over [`fflate`](https://github.com/101arrowz/fflate), recursive walking of ZIP-in-ZIP entries, and archive-format detection (ZIP vs not-ZIP). **tar and gzip are explicitly out of scope.**
|
|
10
|
+
|
|
11
|
+
## Getting started
|
|
12
|
+
|
|
13
|
+
Requires Node.js `>=20` and pnpm `11.6.0`.
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pnpm install
|
|
17
|
+
pnpm build # tsdown -> dist/ (ESM + CJS + .d.ts)
|
|
18
|
+
pnpm typecheck # tsc -p tsconfig.json && tsc -p tsconfig.node.json (dual tsconfig)
|
|
19
|
+
pnpm lint # eslint . --fix --cache --max-warnings 0
|
|
20
|
+
pnpm test # vitest run
|
|
21
|
+
pnpm test:watch # vitest
|
|
22
|
+
pnpm test:workers # vitest run --config vitest.workers.config.ts, inside a real Cloudflare Workers (workerd) isolate
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
To run a single test file, pass its path to vitest directly, e.g. `pnpm exec vitest run src/zip/walk.test.ts`.
|
|
26
|
+
|
|
27
|
+
## What it provides
|
|
28
|
+
|
|
29
|
+
| Module | Exports |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `zip/container` | `zipPackage` (ordered-entries ZIP write with stored-uncompressed support), `unzipPackage`, `ZipEntry` |
|
|
32
|
+
| `zip/detect` | `detectArchiveFormat` (`'zip' \| 'unknown'`), `isZipArchive`, `ArchiveFormat` |
|
|
33
|
+
| `zip/walk` | `walkArchive` (recursive ZIP-in-ZIP walking), `ArchiveWalkEntry`, `ArchiveWalkLimitError`, `MAX_WALK_DEPTH`, `MAX_WALK_TOTAL_BYTES`, `WalkArchiveOptions` |
|
|
34
|
+
|
|
35
|
+
### Recursive walking
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { walkArchive } from 'archive-codec';
|
|
39
|
+
|
|
40
|
+
// Every entry of every nested ZIP, flattened. Throws ArchiveWalkLimitError if
|
|
41
|
+
// the walk exceeds the depth cap or the cumulative decompressed-bytes budget.
|
|
42
|
+
for (const entry of walkArchive(docxBytes)) {
|
|
43
|
+
entry.path; // e.g. 'xl/workbook.xml', the path within its own archive
|
|
44
|
+
entry.ancestors; // e.g. ['word/embeddings/oleObject1.xlsx'] -- the nested
|
|
45
|
+
// ZIP entries descended through to reach this one
|
|
46
|
+
entry.bytes; // decompressed content
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Both guards throw rather than truncate: an input outside the contract must fail loudly, never return a partial listing that looks complete. The defaults are `MAX_WALK_DEPTH` (8 — real producers bottom out around depth 3; the motivating OOXML embedded-object case is depth 2) and `MAX_WALK_TOTAL_BYTES` (512 MiB cumulative across every nesting level — the same figure `byte-codec` grants a single stream, re-purposed as one shared budget so a bomb's multiplicative nesting leverage becomes bounded addition). Both are overridable per call via `walkArchive(bytes, { maxDepth, maxTotalBytes })`, and each constant's derivation is stated in its source comment.
|
|
51
|
+
|
|
52
|
+
### ZIP container
|
|
53
|
+
|
|
54
|
+
`zipPackage` takes an *ordered* array of `[path, entry]` tuples, not a `Record`, so the caller controls the exact emission order deterministically (the property formats with a fixed-offset first entry — ODF's `mimetype` — depend on), and any entry can be written stored-uncompressed via `stored: true`. `unzipPackage` is the read side; the returned `Record` makes no ordering promise and collapses duplicate paths.
|
|
55
|
+
|
|
56
|
+
## Conventions
|
|
57
|
+
|
|
58
|
+
- Worker-isomorphic (see the [family-wide convention](https://github.com/ExaDev/documents.js/blob/main/README.md#conventions)): runtime `src/` must not import `node:*`, a bare Node builtin, or use the `Buffer` global — enforced by a `no-restricted-imports`/`no-restricted-globals` ESLint rule and exercised in CI by running the test suite inside an actual `workerd` isolate (`pnpm test:workers`). Test files under `src/**/*.test.ts` and `src/test-support/` are exempt and may use Node APIs for fixtures.
|
|
59
|
+
- Only `src/index.ts` may be named `index.*` — a custom ESLint rule (`local/no-non-barrel-index`) rejects any other module using an `index` basename, since that would be a hidden entry point the `exports` map in `package.json` doesn't advertise.
|
|
60
|
+
- Zero document-format knowledge: this package knows bytes and ZIP structure, never that any entry is a document. It depends only on `fflate` — not on `byte-codec`, `ooxml.js`, or `odf.js` (whose ZIP wrappers it deliberately mirrors rather than imports, keeping their branding and release cadences decoupled).
|
|
61
|
+
- Releases are fully automated: a push to `main` runs `semantic-release` in CI, which determines the version from Conventional Commit messages and publishes to npm via OIDC trusted publishing (no local `NPM_TOKEN` needed). There is no manual publish step.
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
pnpm add archive-codec
|
|
67
|
+
# or
|
|
68
|
+
npm install archive-codec
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let fflate = require("fflate");
|
|
3
|
+
//#region src/zip/container.ts
|
|
4
|
+
function unzipPackage(bytes) {
|
|
5
|
+
return (0, fflate.unzipSync)(bytes);
|
|
6
|
+
}
|
|
7
|
+
function zipPackage(entries) {
|
|
8
|
+
const data = {};
|
|
9
|
+
for (const [path, entry] of entries) if (entry.stored === true) data[path] = [entry.bytes, { level: 0 }];
|
|
10
|
+
else data[path] = entry.bytes;
|
|
11
|
+
return (0, fflate.zipSync)(data);
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/zip/detect.ts
|
|
15
|
+
const ZIP_LOCAL_FILE_HEADER_MAGIC = [
|
|
16
|
+
80,
|
|
17
|
+
75,
|
|
18
|
+
3,
|
|
19
|
+
4
|
|
20
|
+
];
|
|
21
|
+
const ZIP_END_OF_CENTRAL_DIRECTORY_MAGIC = [
|
|
22
|
+
80,
|
|
23
|
+
75,
|
|
24
|
+
5,
|
|
25
|
+
6
|
|
26
|
+
];
|
|
27
|
+
function startsWithMagic(bytes, magic) {
|
|
28
|
+
if (bytes.length < magic.length) return false;
|
|
29
|
+
for (let i = 0; i < magic.length; i++) if (bytes[i] !== magic[i]) return false;
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
function isZipArchive(bytes) {
|
|
33
|
+
return startsWithMagic(bytes, ZIP_LOCAL_FILE_HEADER_MAGIC) || startsWithMagic(bytes, ZIP_END_OF_CENTRAL_DIRECTORY_MAGIC);
|
|
34
|
+
}
|
|
35
|
+
function detectArchiveFormat(bytes) {
|
|
36
|
+
return isZipArchive(bytes) ? "zip" : "unknown";
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/zip/walk.ts
|
|
40
|
+
const MAX_WALK_DEPTH = 8;
|
|
41
|
+
const MAX_WALK_TOTAL_BYTES = 536870912;
|
|
42
|
+
var ArchiveWalkLimitError = class extends Error {
|
|
43
|
+
limit;
|
|
44
|
+
constructor(limit, message) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.name = "ArchiveWalkLimitError";
|
|
47
|
+
this.limit = limit;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
function walkArchive(bytes, options = {}) {
|
|
51
|
+
const maxDepth = options.maxDepth ?? 8;
|
|
52
|
+
const maxTotalBytes = options.maxTotalBytes ?? 536870912;
|
|
53
|
+
if (!isZipArchive(bytes)) throw new Error("walkArchive input is not a ZIP archive (leading magic bytes are not PK\\x03\\x04 or PK\\x05\\x06)");
|
|
54
|
+
const entries = [];
|
|
55
|
+
let totalBytes = 0;
|
|
56
|
+
visit(bytes, []);
|
|
57
|
+
return entries;
|
|
58
|
+
function visit(archive, ancestors) {
|
|
59
|
+
if (ancestors.length + 1 > maxDepth) throw new ArchiveWalkLimitError("depth", `archive nesting exceeds the maximum walk depth of ${maxDepth} at ${ancestors.join(" > ")}`);
|
|
60
|
+
for (const [path, entryBytes] of Object.entries(unzipPackage(archive))) {
|
|
61
|
+
totalBytes += entryBytes.length;
|
|
62
|
+
if (totalBytes > maxTotalBytes) throw new ArchiveWalkLimitError("total-bytes", `cumulative decompressed size of the walk exceeded the ${maxTotalBytes}-byte budget at ${path}`);
|
|
63
|
+
entries.push({
|
|
64
|
+
path,
|
|
65
|
+
ancestors,
|
|
66
|
+
bytes: entryBytes
|
|
67
|
+
});
|
|
68
|
+
if (isZipArchive(entryBytes)) visit(entryBytes, [...ancestors, path]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
exports.ArchiveWalkLimitError = ArchiveWalkLimitError;
|
|
74
|
+
exports.MAX_WALK_DEPTH = MAX_WALK_DEPTH;
|
|
75
|
+
exports.MAX_WALK_TOTAL_BYTES = MAX_WALK_TOTAL_BYTES;
|
|
76
|
+
exports.detectArchiveFormat = detectArchiveFormat;
|
|
77
|
+
exports.isZipArchive = isZipArchive;
|
|
78
|
+
exports.unzipPackage = unzipPackage;
|
|
79
|
+
exports.walkArchive = walkArchive;
|
|
80
|
+
exports.zipPackage = zipPackage;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/zip/container.d.ts
|
|
2
|
+
interface ZipEntry {
|
|
3
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
4
|
+
readonly stored?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare function unzipPackage(bytes: Uint8Array<ArrayBuffer>): Record<string, Uint8Array<ArrayBuffer>>;
|
|
7
|
+
declare function zipPackage(entries: readonly (readonly [string, ZipEntry])[]): Uint8Array<ArrayBuffer>;
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/zip/detect.d.ts
|
|
10
|
+
type ArchiveFormat = 'zip' | 'unknown';
|
|
11
|
+
declare function isZipArchive(bytes: Uint8Array): boolean;
|
|
12
|
+
declare function detectArchiveFormat(bytes: Uint8Array): ArchiveFormat;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/zip/walk.d.ts
|
|
15
|
+
declare const MAX_WALK_DEPTH = 8;
|
|
16
|
+
declare const MAX_WALK_TOTAL_BYTES: number;
|
|
17
|
+
interface ArchiveWalkEntry {
|
|
18
|
+
readonly path: string;
|
|
19
|
+
readonly ancestors: readonly string[];
|
|
20
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
21
|
+
}
|
|
22
|
+
type ArchiveWalkLimit = 'depth' | 'total-bytes';
|
|
23
|
+
declare class ArchiveWalkLimitError extends Error {
|
|
24
|
+
readonly limit: ArchiveWalkLimit;
|
|
25
|
+
constructor(limit: ArchiveWalkLimit, message: string);
|
|
26
|
+
}
|
|
27
|
+
interface WalkArchiveOptions {
|
|
28
|
+
readonly maxDepth?: number;
|
|
29
|
+
readonly maxTotalBytes?: number;
|
|
30
|
+
}
|
|
31
|
+
declare function walkArchive(bytes: Uint8Array<ArrayBuffer>, options?: WalkArchiveOptions): ArchiveWalkEntry[];
|
|
32
|
+
//#endregion
|
|
33
|
+
export { ArchiveFormat, ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, WalkArchiveOptions, ZipEntry, detectArchiveFormat, isZipArchive, unzipPackage, walkArchive, zipPackage };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/zip/container.d.ts
|
|
2
|
+
interface ZipEntry {
|
|
3
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
4
|
+
readonly stored?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare function unzipPackage(bytes: Uint8Array<ArrayBuffer>): Record<string, Uint8Array<ArrayBuffer>>;
|
|
7
|
+
declare function zipPackage(entries: readonly (readonly [string, ZipEntry])[]): Uint8Array<ArrayBuffer>;
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/zip/detect.d.ts
|
|
10
|
+
type ArchiveFormat = 'zip' | 'unknown';
|
|
11
|
+
declare function isZipArchive(bytes: Uint8Array): boolean;
|
|
12
|
+
declare function detectArchiveFormat(bytes: Uint8Array): ArchiveFormat;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/zip/walk.d.ts
|
|
15
|
+
declare const MAX_WALK_DEPTH = 8;
|
|
16
|
+
declare const MAX_WALK_TOTAL_BYTES: number;
|
|
17
|
+
interface ArchiveWalkEntry {
|
|
18
|
+
readonly path: string;
|
|
19
|
+
readonly ancestors: readonly string[];
|
|
20
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
21
|
+
}
|
|
22
|
+
type ArchiveWalkLimit = 'depth' | 'total-bytes';
|
|
23
|
+
declare class ArchiveWalkLimitError extends Error {
|
|
24
|
+
readonly limit: ArchiveWalkLimit;
|
|
25
|
+
constructor(limit: ArchiveWalkLimit, message: string);
|
|
26
|
+
}
|
|
27
|
+
interface WalkArchiveOptions {
|
|
28
|
+
readonly maxDepth?: number;
|
|
29
|
+
readonly maxTotalBytes?: number;
|
|
30
|
+
}
|
|
31
|
+
declare function walkArchive(bytes: Uint8Array<ArrayBuffer>, options?: WalkArchiveOptions): ArchiveWalkEntry[];
|
|
32
|
+
//#endregion
|
|
33
|
+
export { ArchiveFormat, ArchiveWalkEntry, ArchiveWalkLimit, ArchiveWalkLimitError, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, WalkArchiveOptions, ZipEntry, detectArchiveFormat, isZipArchive, unzipPackage, walkArchive, zipPackage };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { unzipSync, zipSync } from "fflate";
|
|
2
|
+
//#region src/zip/container.ts
|
|
3
|
+
function unzipPackage(bytes) {
|
|
4
|
+
return unzipSync(bytes);
|
|
5
|
+
}
|
|
6
|
+
function zipPackage(entries) {
|
|
7
|
+
const data = {};
|
|
8
|
+
for (const [path, entry] of entries) if (entry.stored === true) data[path] = [entry.bytes, { level: 0 }];
|
|
9
|
+
else data[path] = entry.bytes;
|
|
10
|
+
return zipSync(data);
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/zip/detect.ts
|
|
14
|
+
const ZIP_LOCAL_FILE_HEADER_MAGIC = [
|
|
15
|
+
80,
|
|
16
|
+
75,
|
|
17
|
+
3,
|
|
18
|
+
4
|
|
19
|
+
];
|
|
20
|
+
const ZIP_END_OF_CENTRAL_DIRECTORY_MAGIC = [
|
|
21
|
+
80,
|
|
22
|
+
75,
|
|
23
|
+
5,
|
|
24
|
+
6
|
|
25
|
+
];
|
|
26
|
+
function startsWithMagic(bytes, magic) {
|
|
27
|
+
if (bytes.length < magic.length) return false;
|
|
28
|
+
for (let i = 0; i < magic.length; i++) if (bytes[i] !== magic[i]) return false;
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
function isZipArchive(bytes) {
|
|
32
|
+
return startsWithMagic(bytes, ZIP_LOCAL_FILE_HEADER_MAGIC) || startsWithMagic(bytes, ZIP_END_OF_CENTRAL_DIRECTORY_MAGIC);
|
|
33
|
+
}
|
|
34
|
+
function detectArchiveFormat(bytes) {
|
|
35
|
+
return isZipArchive(bytes) ? "zip" : "unknown";
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/zip/walk.ts
|
|
39
|
+
const MAX_WALK_DEPTH = 8;
|
|
40
|
+
const MAX_WALK_TOTAL_BYTES = 536870912;
|
|
41
|
+
var ArchiveWalkLimitError = class extends Error {
|
|
42
|
+
limit;
|
|
43
|
+
constructor(limit, message) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = "ArchiveWalkLimitError";
|
|
46
|
+
this.limit = limit;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
function walkArchive(bytes, options = {}) {
|
|
50
|
+
const maxDepth = options.maxDepth ?? 8;
|
|
51
|
+
const maxTotalBytes = options.maxTotalBytes ?? 536870912;
|
|
52
|
+
if (!isZipArchive(bytes)) throw new Error("walkArchive input is not a ZIP archive (leading magic bytes are not PK\\x03\\x04 or PK\\x05\\x06)");
|
|
53
|
+
const entries = [];
|
|
54
|
+
let totalBytes = 0;
|
|
55
|
+
visit(bytes, []);
|
|
56
|
+
return entries;
|
|
57
|
+
function visit(archive, ancestors) {
|
|
58
|
+
if (ancestors.length + 1 > maxDepth) throw new ArchiveWalkLimitError("depth", `archive nesting exceeds the maximum walk depth of ${maxDepth} at ${ancestors.join(" > ")}`);
|
|
59
|
+
for (const [path, entryBytes] of Object.entries(unzipPackage(archive))) {
|
|
60
|
+
totalBytes += entryBytes.length;
|
|
61
|
+
if (totalBytes > maxTotalBytes) throw new ArchiveWalkLimitError("total-bytes", `cumulative decompressed size of the walk exceeded the ${maxTotalBytes}-byte budget at ${path}`);
|
|
62
|
+
entries.push({
|
|
63
|
+
path,
|
|
64
|
+
ancestors,
|
|
65
|
+
bytes: entryBytes
|
|
66
|
+
});
|
|
67
|
+
if (isZipArchive(entryBytes)) visit(entryBytes, [...ancestors, path]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { ArchiveWalkLimitError, MAX_WALK_DEPTH, MAX_WALK_TOTAL_BYTES, detectArchiveFormat, isZipArchive, unzipPackage, walkArchive, zipPackage };
|
package/package.json
CHANGED
|
@@ -1,5 +1,92 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archive-codec",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Recursive archive (ZIP-in-ZIP) detection and walking with depth and cumulative decompressed-size guards - zero document-format knowledge, the archive utility package for the documents.js family.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/ExaDev/archive-codec.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/ExaDev/archive-codec#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/ExaDev/archive-codec/issues"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": {
|
|
18
|
+
"import": "./dist/index.d.ts",
|
|
19
|
+
"require": "./dist/index.d.cts"
|
|
20
|
+
},
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"require": "./dist/index.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./*": {
|
|
25
|
+
"types": {
|
|
26
|
+
"import": "./dist/*.d.ts",
|
|
27
|
+
"require": "./dist/*.d.cts"
|
|
28
|
+
},
|
|
29
|
+
"import": "./dist/*.js",
|
|
30
|
+
"require": "./dist/*.cjs"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/index.cjs",
|
|
34
|
+
"module": "./dist/index.js",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public",
|
|
41
|
+
"provenance": true,
|
|
42
|
+
"registry": "https://registry.npmjs.org/"
|
|
43
|
+
},
|
|
44
|
+
"sideEffects": false,
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=20"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "turbo run _build",
|
|
50
|
+
"_build": "tsdown",
|
|
51
|
+
"prepublishOnly": "pnpm run lint && pnpm run typecheck && tsdown && publint && attw --pack",
|
|
52
|
+
"lint": "turbo run _lint",
|
|
53
|
+
"_lint": "eslint . --fix --cache --max-warnings 0",
|
|
54
|
+
"lint:check": "eslint . --max-warnings 0",
|
|
55
|
+
"typecheck": "turbo run _typecheck",
|
|
56
|
+
"_typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.node.json",
|
|
57
|
+
"test": "turbo run _test",
|
|
58
|
+
"_test": "vitest run",
|
|
59
|
+
"test:watch": "vitest",
|
|
60
|
+
"test:workers": "turbo run _test:workers",
|
|
61
|
+
"_test:workers": "vitest run --config vitest.workers.config.ts",
|
|
62
|
+
"prepare": "husky"
|
|
63
|
+
},
|
|
64
|
+
"packageManager": "pnpm@11.6.0",
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"fflate": "^0.8.2"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
70
|
+
"@cloudflare/vitest-pool-workers": "^0.20.1",
|
|
71
|
+
"@commitlint/cli": "^21.2.1",
|
|
72
|
+
"@commitlint/config-conventional": "^21.2.0",
|
|
73
|
+
"@eslint/js": "^10.0.1",
|
|
74
|
+
"@semantic-release/changelog": "^7.0.0",
|
|
75
|
+
"@semantic-release/git": "^11.0.1",
|
|
76
|
+
"@types/node": "^26.1.2",
|
|
77
|
+
"eslint": "^10.8.0",
|
|
78
|
+
"globals": "^17.8.0",
|
|
79
|
+
"husky": "^9.1.7",
|
|
80
|
+
"lint-staged": "^17.2.0",
|
|
81
|
+
"publint": "^0.3.21",
|
|
82
|
+
"semantic-release": "^25.0.8",
|
|
83
|
+
"tsdown": "^0.22.13",
|
|
84
|
+
"turbo": "^2.10.8",
|
|
85
|
+
"typescript": "^6.0.3",
|
|
86
|
+
"typescript-eslint": "^8.65.0",
|
|
87
|
+
"vitest": "^4.1.10"
|
|
88
|
+
},
|
|
89
|
+
"lint-staged": {
|
|
90
|
+
"*.ts": "eslint --fix"
|
|
91
|
+
}
|
|
5
92
|
}
|