dir-archiver 3.0.0 → 3.0.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/CHANGELOG.md +100 -0
- package/CONTRACT.md +94 -0
- package/CONTRIBUTING.md +33 -0
- package/README.md +35 -59
- package/SECURITY.md +23 -0
- package/SUPPORT.md +16 -0
- package/dist/cli.js +1 -0
- package/dist/core.d.ts +76 -7
- package/dist/core.js +76 -7
- package/dist/errors.d.ts +53 -9
- package/dist/errors.js +13 -0
- package/dist/index.d.ts +33 -12
- package/dist/index.js +8 -2
- package/dist/types.d.ts +120 -10
- package/docs/explanation/index.md +5 -0
- package/docs/explanation/profiles.md +21 -0
- package/docs/how-to/cli-json-and-exit-codes.md +78 -0
- package/docs/how-to/extract-untrusted.md +54 -0
- package/docs/how-to/index.md +7 -0
- package/docs/how-to/troubleshoot-common-failures.md +78 -0
- package/docs/index.md +23 -0
- package/docs/maintainers/ci-release-artifact.md +39 -0
- package/docs/maintainers/ci-usage.md +41 -0
- package/docs/reference/cli.md +277 -0
- package/docs/reference/contract.md +13 -0
- package/docs/reference/index.md +7 -0
- package/docs/reference/options.md +57 -0
- package/docs/tutorial/bundle-a-plugin.md +55 -0
- package/docs/tutorial/first-archive-flow.md +52 -0
- package/jsr.json +10 -5
- package/package.json +30 -14
package/dist/errors.d.ts
CHANGED
|
@@ -1,25 +1,69 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Stable
|
|
2
|
+
* Stable machine-readable error codes emitted by `dir-archiver`.
|
|
3
|
+
*
|
|
4
|
+
* - `DIRARCHIVER_INVALID_SOURCE`: input bytes or paths could not be read.
|
|
5
|
+
* - `DIRARCHIVER_INVALID_DESTINATION`: destination path or parent directory is
|
|
6
|
+
* invalid for the requested operation.
|
|
7
|
+
* - `DIRARCHIVER_PATH_TRAVERSAL`: strict extraction rejected a traversal or
|
|
8
|
+
* absolute-path entry.
|
|
9
|
+
* - `DIRARCHIVER_UNSUPPORTED_ENTRY`: the archive contains an entry type or
|
|
10
|
+
* feature that `dir-archiver` does not support safely.
|
|
11
|
+
* - `DIRARCHIVER_RESOURCE_LIMIT`: extraction exceeded configured byte limits.
|
|
12
|
+
* - `DIRARCHIVER_RUNTIME_UNSUPPORTED`: the current runtime cannot satisfy a
|
|
13
|
+
* required bytefold capability.
|
|
14
|
+
* - `DIRARCHIVER_NORMALIZE_UNSUPPORTED`: normalization is unavailable for the
|
|
15
|
+
* selected format/runtime pair.
|
|
16
|
+
* - `DIRARCHIVER_USAGE`: CLI invocation is missing required flags or uses
|
|
17
|
+
* unsupported values.
|
|
3
18
|
*/
|
|
4
19
|
export type DirArchiverErrorCode = 'DIRARCHIVER_INVALID_SOURCE' | 'DIRARCHIVER_INVALID_DESTINATION' | 'DIRARCHIVER_PATH_TRAVERSAL' | 'DIRARCHIVER_UNSUPPORTED_ENTRY' | 'DIRARCHIVER_RESOURCE_LIMIT' | 'DIRARCHIVER_RUNTIME_UNSUPPORTED' | 'DIRARCHIVER_NORMALIZE_UNSUPPORTED' | 'DIRARCHIVER_USAGE';
|
|
20
|
+
/**
|
|
21
|
+
* Stable JSON payload emitted by `DirArchiverError.toJSON()`.
|
|
22
|
+
*
|
|
23
|
+
* This is the machine-readable error shape used by the CLI `--json` surface and
|
|
24
|
+
* by API consumers that persist `DirArchiverError` objects to logs or reports.
|
|
25
|
+
*/
|
|
26
|
+
export interface DirArchiverErrorJson {
|
|
27
|
+
/** Schema version for the serialized error payload. */
|
|
28
|
+
schemaVersion: '1';
|
|
29
|
+
/** Stable error class name used in serialized output. */
|
|
30
|
+
name: 'DirArchiverError';
|
|
31
|
+
/** Stable machine-readable error code. */
|
|
32
|
+
code: DirArchiverErrorCode;
|
|
33
|
+
/** Human-readable summary of the failure. */
|
|
34
|
+
message: string;
|
|
35
|
+
/** Optional remediation hint when the error carries one. */
|
|
36
|
+
hint?: string;
|
|
37
|
+
/** Optional structured context for logs and diagnostics. */
|
|
38
|
+
context?: Record<string, unknown>;
|
|
39
|
+
}
|
|
5
40
|
/**
|
|
6
41
|
* Structured error contract for dir-archiver v3.
|
|
7
42
|
*/
|
|
8
43
|
export declare class DirArchiverError extends Error {
|
|
44
|
+
/** Stable machine-readable error code. */
|
|
9
45
|
readonly code: DirArchiverErrorCode;
|
|
46
|
+
/** Optional operator-facing hint for remediation. */
|
|
10
47
|
readonly hint: string | undefined;
|
|
48
|
+
/** Optional structured context for logs, JSON output, or diagnostics. */
|
|
11
49
|
readonly context: Record<string, unknown> | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a structured error value safe for CLI and API consumers.
|
|
52
|
+
*
|
|
53
|
+
* @param code Stable machine-readable error code.
|
|
54
|
+
* @param message Human-readable summary of the failure.
|
|
55
|
+
* @param options Optional hint, structured context, and nested cause.
|
|
56
|
+
*/
|
|
12
57
|
constructor(code: DirArchiverErrorCode, message: string, options?: {
|
|
13
58
|
hint?: string | undefined;
|
|
14
59
|
context?: Record<string, unknown> | undefined;
|
|
15
60
|
cause?: unknown;
|
|
16
61
|
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
62
|
+
/**
|
|
63
|
+
* Serializes the error into the stable JSON shape used by the CLI.
|
|
64
|
+
*
|
|
65
|
+
* The returned object always includes `schemaVersion`, `name`, `code`, and
|
|
66
|
+
* `message`. Optional `hint` and `context` keys are omitted when unset.
|
|
67
|
+
*/
|
|
68
|
+
toJSON(): DirArchiverErrorJson;
|
|
25
69
|
}
|
package/dist/errors.js
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
* Structured error contract for dir-archiver v3.
|
|
3
3
|
*/
|
|
4
4
|
export class DirArchiverError extends Error {
|
|
5
|
+
/**
|
|
6
|
+
* Creates a structured error value safe for CLI and API consumers.
|
|
7
|
+
*
|
|
8
|
+
* @param code Stable machine-readable error code.
|
|
9
|
+
* @param message Human-readable summary of the failure.
|
|
10
|
+
* @param options Optional hint, structured context, and nested cause.
|
|
11
|
+
*/
|
|
5
12
|
constructor(code, message, options = {}) {
|
|
6
13
|
super(message);
|
|
7
14
|
this.name = 'DirArchiverError';
|
|
@@ -12,6 +19,12 @@ export class DirArchiverError extends Error {
|
|
|
12
19
|
this.cause = options.cause;
|
|
13
20
|
}
|
|
14
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Serializes the error into the stable JSON shape used by the CLI.
|
|
24
|
+
*
|
|
25
|
+
* The returned object always includes `schemaVersion`, `name`, `code`, and
|
|
26
|
+
* `message`. Optional `hint` and `context` keys are omitted when unset.
|
|
27
|
+
*/
|
|
15
28
|
toJSON() {
|
|
16
29
|
return {
|
|
17
30
|
schemaVersion: '1',
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Deterministic directory archiving and extraction over zip, tar, and layered compression.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Supports Node.js, Deno, and Bun through one API surface backed by bytefold.
|
|
5
5
|
*/
|
|
6
6
|
import { audit, detect, extract, list, normalize, open, write } from './core.js';
|
|
7
7
|
export { audit, detect, extract, list, normalize, open, write };
|
|
8
8
|
export { DirArchiverError } from './errors.js';
|
|
9
|
-
export type {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
export type { DirArchiverErrorCode, DirArchiverErrorJson } from './errors.js';
|
|
10
|
+
export type { ArchiveFormat, ArchiveLimits, ArchiveDetectionReport, ArchiveIssue, ArchiveNormalizeReport, ArchiveProfile, CliUsageError, DetectResult, DirArchiverInput, ExtractOptions, ExtractResult, ListEntry, ListResult, NormalizeOptions, NormalizeResult, OpenOptions, SupportedCommandMap, WriteOptions, WriteResult } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Namespace-style API contract mirrored by the default export.
|
|
13
|
+
*
|
|
14
|
+
* Consumers who prefer `import dirArchiver from "dir-archiver"` get the same
|
|
15
|
+
* operations and semantics as the named exports on this interface.
|
|
16
|
+
*/
|
|
17
|
+
export interface DirArchiverNamespace {
|
|
18
|
+
/** Open an archive reader after resolving format, limits, and runtime support. */
|
|
19
|
+
readonly open: typeof open;
|
|
20
|
+
/** Detect an archive format without extracting entries. */
|
|
21
|
+
readonly detect: typeof detect;
|
|
22
|
+
/** Project archive entries into a stable listing payload. */
|
|
23
|
+
readonly list: typeof list;
|
|
24
|
+
/** Audit an archive against the requested safety profile and limits. */
|
|
25
|
+
readonly audit: typeof audit;
|
|
26
|
+
/** Rewrite an archive into its normalized deterministic representation. */
|
|
27
|
+
readonly normalize: typeof normalize;
|
|
28
|
+
/** Extract an archive to disk with explicit safety and size controls. */
|
|
29
|
+
readonly extract: typeof extract;
|
|
30
|
+
/** Write a directory or file tree into an archive. */
|
|
31
|
+
readonly write: typeof write;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Namespace-style default export for consumers who prefer
|
|
35
|
+
* `import dirArchiver from "dir-archiver"`.
|
|
36
|
+
*
|
|
37
|
+
* It mirrors the named exports exactly and does not add extra behavior.
|
|
38
|
+
*/
|
|
39
|
+
declare const api: DirArchiverNamespace;
|
|
19
40
|
export default api;
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Deterministic directory archiving and extraction over zip, tar, and layered compression.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Supports Node.js, Deno, and Bun through one API surface backed by bytefold.
|
|
5
5
|
*/
|
|
6
6
|
import { audit, detect, extract, list, normalize, open, write } from './core.js';
|
|
7
7
|
export { audit, detect, extract, list, normalize, open, write };
|
|
8
8
|
export { DirArchiverError } from './errors.js';
|
|
9
|
+
/**
|
|
10
|
+
* Namespace-style default export for consumers who prefer
|
|
11
|
+
* `import dirArchiver from "dir-archiver"`.
|
|
12
|
+
*
|
|
13
|
+
* It mirrors the named exports exactly and does not add extra behavior.
|
|
14
|
+
*/
|
|
9
15
|
const api = {
|
|
10
16
|
open,
|
|
11
17
|
detect,
|
package/dist/types.d.ts
CHANGED
|
@@ -1,107 +1,210 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export type { ArchiveFormat,
|
|
1
|
+
import type { ArchiveFormat, ArchiveProfile } from '@ismail-elkorchi/bytefold';
|
|
2
|
+
export type { ArchiveFormat, ArchiveProfile, } from '@ismail-elkorchi/bytefold';
|
|
3
|
+
/** Resource limit configuration accepted by `open`, `audit`, and extraction flows. */
|
|
4
|
+
export type ArchiveLimits = Record<string, unknown>;
|
|
5
|
+
/** Issue shape emitted for archive read/normalize/extract failures. */
|
|
6
|
+
export type ArchiveIssue = Record<string, unknown>;
|
|
7
|
+
/** Public detection report shape aligned with runtime diagnostics payloads. */
|
|
8
|
+
export type ArchiveDetectionReport = Record<string, unknown>;
|
|
9
|
+
/** Public normalize report shape for deterministic archive rewrites. */
|
|
10
|
+
export type ArchiveNormalizeReport = Record<string, unknown>;
|
|
3
11
|
/**
|
|
4
12
|
* Accepted input shapes for archive read operations.
|
|
13
|
+
*
|
|
14
|
+
* String paths and `URL` objects are the most common inputs, but callers can
|
|
15
|
+
* also supply raw bytes or web streams when the archive is already in memory.
|
|
5
16
|
*/
|
|
6
17
|
export type DirArchiverInput = string | URL | Uint8Array | ArrayBuffer | ReadableStream<Uint8Array> | Blob;
|
|
7
18
|
/**
|
|
8
|
-
* Common options forwarded to bytefold open operations.
|
|
19
|
+
* Common options forwarded to bytefold archive-open operations.
|
|
20
|
+
*
|
|
21
|
+
* Used by `open()`, `detect()`, `list()`, and `audit()`.
|
|
9
22
|
*/
|
|
10
23
|
export interface OpenOptions {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Explicit format override when callers already know archive type.
|
|
26
|
+
*/
|
|
27
|
+
format?: ArchiveFormat | 'auto' | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Safety profile (`compat`, `strict`, `agent`) applied during reads/audits.
|
|
30
|
+
*/
|
|
31
|
+
profile?: ArchiveProfile | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Extra strictness toggle forwarded to bytefold parsing.
|
|
34
|
+
*/
|
|
35
|
+
isStrict?: boolean | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Parser/resource limits enforced while opening or auditing archives.
|
|
38
|
+
*/
|
|
39
|
+
limits?: ArchiveLimits | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Abort signal for cancelling in-flight async operations.
|
|
42
|
+
*/
|
|
43
|
+
signal?: AbortSignal | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Password used for encrypted archives when supported by the runtime.
|
|
46
|
+
*/
|
|
47
|
+
password?: string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Filename hint used for extension-based inference with non-path inputs.
|
|
50
|
+
*/
|
|
51
|
+
filename?: string | undefined;
|
|
18
52
|
}
|
|
19
53
|
/**
|
|
20
54
|
* Format detection result.
|
|
21
55
|
*/
|
|
22
56
|
export interface DetectResult {
|
|
57
|
+
/** Resolved archive format after detection. */
|
|
23
58
|
format: ArchiveFormat;
|
|
59
|
+
/** Bytefold detection metadata, if the runtime produced it. */
|
|
24
60
|
detection: ArchiveDetectionReport | undefined;
|
|
25
61
|
}
|
|
26
62
|
/**
|
|
27
63
|
* Single archive entry projection used by list responses.
|
|
28
64
|
*/
|
|
29
65
|
export interface ListEntry {
|
|
66
|
+
/** Entry format as exposed by the underlying reader. */
|
|
30
67
|
format: ArchiveFormat;
|
|
68
|
+
/** Entry path inside the archive, normalized to forward slashes. */
|
|
31
69
|
name: string;
|
|
70
|
+
/** Entry size encoded as a string for JSON-safe transport. */
|
|
32
71
|
size: string;
|
|
72
|
+
/** Whether the entry materializes as a directory. */
|
|
33
73
|
isDirectory: boolean;
|
|
74
|
+
/** Whether the entry is a symbolic link. */
|
|
34
75
|
isSymlink: boolean;
|
|
76
|
+
/** Link target when the entry is a symbolic link. */
|
|
35
77
|
linkName?: string | undefined;
|
|
36
78
|
}
|
|
37
79
|
/**
|
|
38
80
|
* Archive listing response payload.
|
|
39
81
|
*/
|
|
40
82
|
export interface ListResult {
|
|
83
|
+
/** Resolved archive format after detection/open completed. */
|
|
41
84
|
format: ArchiveFormat;
|
|
85
|
+
/** Bytefold detection metadata used to choose `format`, when available. */
|
|
42
86
|
detection: ArchiveDetectionReport | undefined;
|
|
87
|
+
/** Projected archive entries in archive iteration order. */
|
|
43
88
|
entries: ListEntry[];
|
|
44
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Options for `audit()`.
|
|
92
|
+
*
|
|
93
|
+
* Alias of `OpenOptions` for stable API typing; CLI-only flags (for example
|
|
94
|
+
* `--json`) are not part of this programmatic surface.
|
|
95
|
+
*/
|
|
45
96
|
export type AuditOptions = OpenOptions;
|
|
46
97
|
/**
|
|
47
98
|
* Normalize operation options.
|
|
48
99
|
*/
|
|
49
100
|
export interface NormalizeOptions extends OpenOptions {
|
|
101
|
+
/** Request deterministic normalization when the runtime supports the knob. */
|
|
50
102
|
deterministic?: boolean | undefined;
|
|
51
103
|
}
|
|
52
104
|
/**
|
|
53
105
|
* Normalize operation result payload.
|
|
54
106
|
*/
|
|
55
107
|
export interface NormalizeResult {
|
|
108
|
+
/** Source archive format that was normalized. */
|
|
56
109
|
format: ArchiveFormat;
|
|
110
|
+
/** Detailed normalization report from bytefold. */
|
|
57
111
|
report: ArchiveNormalizeReport;
|
|
58
112
|
}
|
|
59
113
|
/**
|
|
60
114
|
* Extraction options with explicit safety limits.
|
|
115
|
+
*
|
|
116
|
+
* `extract()` defaults to `profile: 'strict'` when no profile is supplied.
|
|
61
117
|
*/
|
|
62
118
|
export interface ExtractOptions extends OpenOptions {
|
|
119
|
+
/**
|
|
120
|
+
* If `true`, symbolic-link entries are materialized on disk; otherwise they
|
|
121
|
+
* are skipped and counted in `ExtractResult.skippedEntries`.
|
|
122
|
+
*/
|
|
63
123
|
allowSymlinks?: boolean | undefined;
|
|
124
|
+
/**
|
|
125
|
+
* Reserved for forward compatibility. Hard-link entries are currently
|
|
126
|
+
* rejected with `DIRARCHIVER_UNSUPPORTED_ENTRY` regardless of this flag.
|
|
127
|
+
*/
|
|
64
128
|
allowHardlinks?: boolean | undefined;
|
|
129
|
+
/**
|
|
130
|
+
* Maximum bytes allowed for any single extracted file entry.
|
|
131
|
+
*/
|
|
65
132
|
maxEntryBytes?: number | undefined;
|
|
133
|
+
/**
|
|
134
|
+
* Maximum cumulative bytes allowed across all extracted file entries.
|
|
135
|
+
*/
|
|
66
136
|
maxTotalExtractedBytes?: number | undefined;
|
|
67
137
|
}
|
|
68
138
|
/**
|
|
69
139
|
* Extraction summary result.
|
|
70
140
|
*/
|
|
71
141
|
export interface ExtractResult {
|
|
142
|
+
/** Source archive format that was extracted to disk. */
|
|
72
143
|
format: ArchiveFormat;
|
|
144
|
+
/** Absolute destination directory path used for extraction. */
|
|
73
145
|
destination: string;
|
|
146
|
+
/** Number of file entries written to disk. */
|
|
74
147
|
extractedFiles: number;
|
|
148
|
+
/** Number of directory entries created on disk. */
|
|
75
149
|
extractedDirectories: number;
|
|
150
|
+
/** Number of entries skipped due to policy, such as disallowed symlinks. */
|
|
76
151
|
skippedEntries: number;
|
|
152
|
+
/** Audit issues collected before or during extraction. */
|
|
77
153
|
issues: ArchiveIssue[];
|
|
78
154
|
}
|
|
79
155
|
/**
|
|
80
156
|
* Archive writer options.
|
|
81
157
|
*/
|
|
82
158
|
export interface WriteOptions {
|
|
159
|
+
/**
|
|
160
|
+
* Requested output format. If omitted, inferred from destination extension
|
|
161
|
+
* and falls back to `zip` when inference is not possible.
|
|
162
|
+
*/
|
|
83
163
|
format?: ArchiveFormat | undefined;
|
|
164
|
+
/**
|
|
165
|
+
* Includes the source directory name as a root folder in the archive when
|
|
166
|
+
* source is a directory.
|
|
167
|
+
*/
|
|
84
168
|
includeBaseDirectory?: boolean | undefined;
|
|
169
|
+
/**
|
|
170
|
+
* Follows symbolic links while walking directory sources for `write()`.
|
|
171
|
+
*/
|
|
85
172
|
followSymlinks?: boolean | undefined;
|
|
173
|
+
/**
|
|
174
|
+
* Glob-like exclusion patterns evaluated relative to the source root.
|
|
175
|
+
*/
|
|
86
176
|
exclude?: string[] | undefined;
|
|
177
|
+
/**
|
|
178
|
+
* Writer profile (`compat`, `strict`, `agent`) forwarded to bytefold.
|
|
179
|
+
*/
|
|
87
180
|
profile?: ArchiveProfile | undefined;
|
|
181
|
+
/**
|
|
182
|
+
* Optional writer limits passed through to bytefold operations.
|
|
183
|
+
*/
|
|
88
184
|
limits?: ArchiveLimits | undefined;
|
|
89
185
|
}
|
|
90
186
|
/**
|
|
91
187
|
* Archive writer result payload.
|
|
92
188
|
*/
|
|
93
189
|
export interface WriteResult {
|
|
190
|
+
/** Archive format emitted to the destination path. */
|
|
94
191
|
format: ArchiveFormat;
|
|
192
|
+
/** Absolute source path that was archived. */
|
|
95
193
|
source: string;
|
|
194
|
+
/** Absolute destination archive path that was written. */
|
|
96
195
|
destination: string;
|
|
196
|
+
/** Number of archive entries written to the output archive. */
|
|
97
197
|
entryCount: number;
|
|
198
|
+
/** Whether a directory source was wrapped in a tar-based single-file codec. */
|
|
98
199
|
wrappedDirectoryCodec: boolean;
|
|
99
200
|
}
|
|
100
201
|
/**
|
|
101
202
|
* Usage-error shape emitted by CLI parsing.
|
|
102
203
|
*/
|
|
103
204
|
export interface CliUsageError {
|
|
205
|
+
/** Human-readable summary of the CLI validation failure. */
|
|
104
206
|
message: string;
|
|
207
|
+
/** Individual issues returned by the command-line parser. */
|
|
105
208
|
issues: readonly {
|
|
106
209
|
code: string;
|
|
107
210
|
message: string;
|
|
@@ -111,11 +214,18 @@ export interface CliUsageError {
|
|
|
111
214
|
* Canonical command identifiers supported by the CLI contract.
|
|
112
215
|
*/
|
|
113
216
|
export interface SupportedCommandMap {
|
|
217
|
+
/** Literal identifier for the `open` command. */
|
|
114
218
|
open: 'open';
|
|
219
|
+
/** Literal identifier for the `detect` command. */
|
|
115
220
|
detect: 'detect';
|
|
221
|
+
/** Literal identifier for the `list` command. */
|
|
116
222
|
list: 'list';
|
|
223
|
+
/** Literal identifier for the `audit` command. */
|
|
117
224
|
audit: 'audit';
|
|
225
|
+
/** Literal identifier for the `extract` command. */
|
|
118
226
|
extract: 'extract';
|
|
227
|
+
/** Literal identifier for the `normalize` command. */
|
|
119
228
|
normalize: 'normalize';
|
|
229
|
+
/** Literal identifier for the `write` command. */
|
|
120
230
|
write: 'write';
|
|
121
231
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Profile behavior and tradeoffs
|
|
2
|
+
|
|
3
|
+
Profiles configure safety posture for audit and extraction.
|
|
4
|
+
|
|
5
|
+
## compat
|
|
6
|
+
|
|
7
|
+
- Minimal guardrails.
|
|
8
|
+
- Use only for trusted inputs or internal tooling.
|
|
9
|
+
|
|
10
|
+
## strict
|
|
11
|
+
|
|
12
|
+
- Blocks traversal, absolute paths, and unsafe entries.
|
|
13
|
+
- Enforces explicit resource limits.
|
|
14
|
+
|
|
15
|
+
## agent
|
|
16
|
+
|
|
17
|
+
- Same safety posture as strict.
|
|
18
|
+
- Adds additional audit assertions for automation pipelines.
|
|
19
|
+
|
|
20
|
+
Profiles are passed through to bytefold, so bytefold updates may expand the
|
|
21
|
+
checked conditions without changing the profile names.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# How-to: use CLI JSON output and exit codes
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
Drive `dir-archiver` from automation without scraping human-readable command
|
|
5
|
+
output.
|
|
6
|
+
|
|
7
|
+
## Prereqs
|
|
8
|
+
- Node `>=24`
|
|
9
|
+
- `npm install`
|
|
10
|
+
- `npm run build`
|
|
11
|
+
|
|
12
|
+
## Copy/paste
|
|
13
|
+
```sh
|
|
14
|
+
tmpdir="$(mktemp -d)"
|
|
15
|
+
mkdir -p "$tmpdir/src"
|
|
16
|
+
printf 'hello from dir-archiver\n' > "$tmpdir/src/hello.txt"
|
|
17
|
+
|
|
18
|
+
node dist/cli.js write --source "$tmpdir/src" --output "$tmpdir/archive.zip" --json
|
|
19
|
+
node dist/cli.js detect --input "$tmpdir/archive.zip" --json
|
|
20
|
+
|
|
21
|
+
set +e
|
|
22
|
+
node dist/cli.js extract --json
|
|
23
|
+
usage_exit=$?
|
|
24
|
+
set -e
|
|
25
|
+
|
|
26
|
+
printf 'usage_exit=%s\n' "$usage_exit"
|
|
27
|
+
rm -rf "$tmpdir"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## What you should see
|
|
31
|
+
- `write` emits JSON on stdout with fields shaped like:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"format": "zip",
|
|
36
|
+
"source": "/tmp/.../src",
|
|
37
|
+
"destination": "/tmp/.../archive.zip",
|
|
38
|
+
"entryCount": 1,
|
|
39
|
+
"wrappedDirectoryCodec": false
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- `detect` emits JSON on stdout with `format` plus a `detection` object.
|
|
44
|
+
- The invalid `extract --json` invocation emits a usage payload on stdout shaped
|
|
45
|
+
like:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"schemaVersion": "1",
|
|
50
|
+
"code": "DIRARCHIVER_USAGE",
|
|
51
|
+
"message": "Invalid CLI arguments.",
|
|
52
|
+
"issues": [
|
|
53
|
+
{ "code": "REQUIRED", "message": "extract requires --input." },
|
|
54
|
+
{ "code": "REQUIRED", "message": "extract requires --output." }
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- `usage_exit=2` confirms the usage-error exit code.
|
|
60
|
+
|
|
61
|
+
## Exit codes
|
|
62
|
+
|
|
63
|
+
| Exit code | Meaning | Where to read details |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `0` | Command completed successfully. | stdout (`--json`) or normal console output |
|
|
66
|
+
| `1` | Runtime failure or archive-policy failure. | stderr |
|
|
67
|
+
| `2` | CLI usage or validation failure. | stdout with `--json`, stderr otherwise |
|
|
68
|
+
|
|
69
|
+
## Common failure modes
|
|
70
|
+
- Scripts scrape prose output instead of passing `--json`.
|
|
71
|
+
- Exit codes `1` and `2` are collapsed into one generic failure bucket.
|
|
72
|
+
- stdout and stderr are merged, which corrupts JSON parsing.
|
|
73
|
+
- Commands are run before `npm run build`, so `dist/cli.js` is missing.
|
|
74
|
+
|
|
75
|
+
## Related reference
|
|
76
|
+
- [CLI reference](../reference/cli.md)
|
|
77
|
+
- [Options reference](../reference/options.md)
|
|
78
|
+
- [Contract](../../CONTRACT.md)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# How-to: extract untrusted archives safely
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
Prevent path traversal and decompression amplification from turning extraction
|
|
5
|
+
into a filesystem or resource-exhaustion risk.
|
|
6
|
+
|
|
7
|
+
## Prereqs
|
|
8
|
+
- Node `>=24`
|
|
9
|
+
- `npm install`
|
|
10
|
+
- `npm run build`
|
|
11
|
+
|
|
12
|
+
## Copy/paste
|
|
13
|
+
Runnable example file:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
node examples/extract-untrusted.mjs
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Equivalent API pattern (audit first, then extract with limits):
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { audit, extract } from "dir-archiver";
|
|
23
|
+
|
|
24
|
+
const input = "./incoming.zip";
|
|
25
|
+
const report = await audit(input, { profile: "agent" });
|
|
26
|
+
if (!report.ok) {
|
|
27
|
+
console.error(JSON.stringify({ ok: false, issues: report.issues }, null, 2));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await extract(input, "./out", {
|
|
32
|
+
profile: "strict",
|
|
33
|
+
maxEntryBytes: 64 * 1024 * 1024,
|
|
34
|
+
maxTotalExtractedBytes: 512 * 1024 * 1024,
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## What you should see
|
|
39
|
+
- The audit step succeeds before extraction starts.
|
|
40
|
+
- The example intentionally sets a low extraction limit and reports
|
|
41
|
+
`DIRARCHIVER_RESOURCE_LIMIT`.
|
|
42
|
+
|
|
43
|
+
## Common failure modes
|
|
44
|
+
- `profile: "compat"` is used for hostile input, which weakens pre-extract
|
|
45
|
+
safety checks.
|
|
46
|
+
- Limits are left unset, so decompression amplification can consume far more
|
|
47
|
+
disk than expected.
|
|
48
|
+
- Callers treat file creation as success instead of checking the returned issues
|
|
49
|
+
and skipped-entry counts.
|
|
50
|
+
|
|
51
|
+
## Related reference
|
|
52
|
+
- [CLI reference](../reference/cli.md)
|
|
53
|
+
- [Options reference](../reference/options.md)
|
|
54
|
+
- [Contract](../../CONTRACT.md)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# How-to: troubleshoot common failures
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
Map common `dir-archiver` failures to the likely option or input problem
|
|
5
|
+
without guessing from raw shell output.
|
|
6
|
+
|
|
7
|
+
## Prereqs
|
|
8
|
+
- Node `>=24`
|
|
9
|
+
- `npm install`
|
|
10
|
+
- `npm run build`
|
|
11
|
+
|
|
12
|
+
## Copy/paste
|
|
13
|
+
```sh
|
|
14
|
+
tmpdir="$(mktemp -d)"
|
|
15
|
+
mkdir -p "$tmpdir/src"
|
|
16
|
+
printf 'hello world\n' > "$tmpdir/src/hello.txt"
|
|
17
|
+
node dist/cli.js write --source "$tmpdir/src" --output "$tmpdir/archive.zip" --json >/dev/null
|
|
18
|
+
|
|
19
|
+
set +e
|
|
20
|
+
node dist/cli.js extract \
|
|
21
|
+
--input "$tmpdir/archive.zip" \
|
|
22
|
+
--output "$tmpdir/out" \
|
|
23
|
+
--max-entry-bytes 4 \
|
|
24
|
+
--json
|
|
25
|
+
runtime_exit=$?
|
|
26
|
+
|
|
27
|
+
node dist/cli.js extract --json
|
|
28
|
+
usage_exit=$?
|
|
29
|
+
set -e
|
|
30
|
+
|
|
31
|
+
printf 'runtime_exit=%s\n' "$runtime_exit"
|
|
32
|
+
printf 'usage_exit=%s\n' "$usage_exit"
|
|
33
|
+
|
|
34
|
+
node dist/cli.js detect --input "$tmpdir/archive.zip" --json
|
|
35
|
+
node dist/cli.js list --input "$tmpdir/archive.zip" --json
|
|
36
|
+
rm -rf "$tmpdir"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## What you should see
|
|
40
|
+
- The first `extract` fails with a `DirArchiverError` JSON payload on stderr
|
|
41
|
+
shaped like:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"schemaVersion": "1",
|
|
46
|
+
"name": "DirArchiverError",
|
|
47
|
+
"code": "DIRARCHIVER_RESOURCE_LIMIT",
|
|
48
|
+
"message": "..."
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- `runtime_exit=1` confirms a runtime/archive-policy failure.
|
|
53
|
+
- The second `extract --json` emits a `DIRARCHIVER_USAGE` payload on stdout and
|
|
54
|
+
`usage_exit=2`.
|
|
55
|
+
- `detect` and `list` still succeed. Use them to inspect the archive before
|
|
56
|
+
trying a different `extract` policy or limit.
|
|
57
|
+
|
|
58
|
+
## Quick diagnosis table
|
|
59
|
+
|
|
60
|
+
| Symptom | Likely cause | First fix |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| Exit `2` with `DIRARCHIVER_USAGE` | Missing or invalid command flags | Compare the command to the [CLI reference](../reference/cli.md). |
|
|
63
|
+
| Exit `1` with `DIRARCHIVER_RESOURCE_LIMIT` | `maxEntryBytes` or `maxTotalExtractedBytes` is lower than the archive requires | Raise the limit or audit first to size the archive. |
|
|
64
|
+
| Exit `1` with `DIRARCHIVER_PATH_TRAVERSAL` or `DIRARCHIVER_UNSUPPORTED_ENTRY` | Strict/agent safety checks rejected an entry path or link | Run `audit` or `list` first and keep `compat` only for trusted input. |
|
|
65
|
+
| Raw `ENOENT` or a missing-path stack trace | The input or output path is wrong for the current working directory | Re-run with absolute paths or verify the file exists. |
|
|
66
|
+
|
|
67
|
+
## Common failure modes
|
|
68
|
+
- Missing input files or missing output directories.
|
|
69
|
+
- Unsupported archive formats or encrypted inputs without the required
|
|
70
|
+
password/support.
|
|
71
|
+
- Strict extraction rejecting traversal-style or link-based entries.
|
|
72
|
+
- Treating an exit code alone as the diagnosis instead of reading the JSON
|
|
73
|
+
error `code`.
|
|
74
|
+
|
|
75
|
+
## Related reference
|
|
76
|
+
- [CLI reference](../reference/cli.md)
|
|
77
|
+
- [Options reference](../reference/options.md)
|
|
78
|
+
- [Contract](../../CONTRACT.md)
|