dir-archiver 2.2.0 → 3.0.1
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 +94 -0
- package/CONTRACT.md +94 -0
- package/CONTRIBUTING.md +33 -0
- package/README.md +48 -99
- package/SECURITY.md +23 -0
- package/SUPPORT.md +16 -0
- package/dist/cli-args.d.ts +24 -0
- package/dist/cli-args.js +204 -0
- package/dist/cli.js +132 -46
- package/dist/core.d.ts +33 -0
- package/dist/core.js +549 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.js +25 -0
- package/dist/index.d.ts +19 -28
- package/dist/index.js +17 -319
- package/dist/runtime/bun.d.ts +2 -0
- package/dist/runtime/bun.js +17 -0
- package/dist/runtime/deno.d.ts +2 -0
- package/dist/runtime/deno.js +17 -0
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +33 -0
- package/dist/runtime/node.d.ts +2 -0
- package/dist/runtime/node.js +17 -0
- package/dist/runtime/types.d.ts +11 -0
- package/dist/runtime/types.js +1 -0
- package/dist/types.d.ts +186 -0
- package/dist/types.js +1 -0
- package/docs/explanation/index.md +5 -0
- package/docs/explanation/profiles.md +21 -0
- package/docs/how-to/ci-release-artifact.md +32 -0
- package/docs/how-to/ci-usage.md +34 -0
- package/docs/how-to/extract-untrusted.md +57 -0
- package/docs/how-to/index.md +7 -0
- package/docs/index.md +24 -0
- package/docs/reference/cli.md +277 -0
- package/docs/reference/index.md +7 -0
- package/docs/reference/options.md +57 -0
- package/docs/tutorial/bundle-a-plugin.md +42 -0
- package/docs/tutorial/first-archive-flow.md +33 -0
- package/jsr.json +35 -0
- package/package.json +52 -20
package/dist/index.js
CHANGED
|
@@ -1,320 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
1
|
+
/**
|
|
2
|
+
* dir-archiver v3 API surface.
|
|
3
|
+
*
|
|
4
|
+
* v3 is a bytefold-backed orchestration layer that supports Node.js, Deno, and Bun.
|
|
5
|
+
*/
|
|
6
|
+
import { audit, detect, extract, list, normalize, open, write } from './core.js';
|
|
7
|
+
export { audit, detect, extract, list, normalize, open, write };
|
|
8
|
+
export { DirArchiverError } from './errors.js';
|
|
9
|
+
const api = {
|
|
10
|
+
open,
|
|
11
|
+
detect,
|
|
12
|
+
list,
|
|
13
|
+
audit,
|
|
14
|
+
normalize,
|
|
15
|
+
extract,
|
|
16
|
+
write
|
|
37
17
|
};
|
|
38
|
-
|
|
39
|
-
const fs = __importStar(require("fs"));
|
|
40
|
-
const archiver_1 = __importDefault(require("archiver"));
|
|
41
|
-
class DirArchiver {
|
|
42
|
-
/**
|
|
43
|
-
* The constructor.
|
|
44
|
-
* @param directoryPath - the path of the folder to archive.
|
|
45
|
-
* @param zipPath - The path of the zip file to create.
|
|
46
|
-
* @param includeBaseDirectory - Includes a base directory at the root of the archive. For example, if the root folder of your project is named "your-project", setting includeBaseDirectory to true will create an archive that includes this base directory. If this option is set to false the archive created will unzip its content to the current directory.
|
|
47
|
-
* @param excludes - The name of the files and foldes to exclude.
|
|
48
|
-
*/
|
|
49
|
-
constructor(directoryPath, zipPath, includeBaseDirectory = false, excludes = [], followSymlinks = false) {
|
|
50
|
-
this.directoryPath = path.resolve(directoryPath);
|
|
51
|
-
this.zipPath = path.resolve(zipPath);
|
|
52
|
-
this.includeBaseDirectory = includeBaseDirectory;
|
|
53
|
-
this.followSymlinks = followSymlinks;
|
|
54
|
-
this.baseDirectory = path.basename(this.directoryPath);
|
|
55
|
-
this.visitedDirectories = new Set();
|
|
56
|
-
this.caseInsensitiveExcludes = process.platform === 'win32';
|
|
57
|
-
// Contains the excluded files and folders.
|
|
58
|
-
this.excludedPaths = new Set();
|
|
59
|
-
this.excludedNames = new Set();
|
|
60
|
-
for (const excludeRaw of excludes) {
|
|
61
|
-
if (typeof excludeRaw !== 'string') {
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
const trimmedRaw = excludeRaw.trim();
|
|
65
|
-
if (trimmedRaw.length === 0) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
let normalizedExclude = path.normalize(trimmedRaw.replace(/\\/g, path.sep));
|
|
69
|
-
if (normalizedExclude === '.' || normalizedExclude === path.sep) {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
if (path.isAbsolute(normalizedExclude)) {
|
|
73
|
-
const relativeCandidate = path.relative(this.directoryPath, normalizedExclude);
|
|
74
|
-
const isInsideSource = relativeCandidate.length > 0
|
|
75
|
-
&& !relativeCandidate.startsWith('..')
|
|
76
|
-
&& !path.isAbsolute(relativeCandidate);
|
|
77
|
-
if (isInsideSource) {
|
|
78
|
-
normalizedExclude = path.normalize(relativeCandidate);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (normalizedExclude.length === 0) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
const hasSeparator = normalizedExclude.includes('/')
|
|
85
|
-
|| normalizedExclude.includes('\\')
|
|
86
|
-
|| normalizedExclude.includes(path.sep);
|
|
87
|
-
const trimmedExclude = normalizedExclude.replace(/[\\/]+$/g, '');
|
|
88
|
-
if (trimmedExclude.length === 0 || trimmedExclude === '.') {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
const normalizedValue = this.normalizeExcludeValue(trimmedExclude);
|
|
92
|
-
this.excludedPaths.add(normalizedValue);
|
|
93
|
-
if (!hasSeparator) {
|
|
94
|
-
this.excludedNames.add(normalizedValue);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const relativeZipPath = path.relative(this.directoryPath, this.zipPath);
|
|
98
|
-
const isZipInsideSource = relativeZipPath.length > 0
|
|
99
|
-
&& !relativeZipPath.startsWith('..')
|
|
100
|
-
&& !path.isAbsolute(relativeZipPath);
|
|
101
|
-
if (isZipInsideSource) {
|
|
102
|
-
const normalizedZipPath = path.normalize(relativeZipPath);
|
|
103
|
-
this.excludedPaths.add(this.normalizeExcludeValue(normalizedZipPath));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Recursively traverse the directory tree and append the files to the archive.
|
|
108
|
-
* @param directoryPath - The path of the directory being looped through.
|
|
109
|
-
*/
|
|
110
|
-
traverseDirectoryTree(directoryPath, archive) {
|
|
111
|
-
const directoriesToVisit = [directoryPath];
|
|
112
|
-
while (directoriesToVisit.length > 0) {
|
|
113
|
-
const nextDirectory = directoriesToVisit.pop();
|
|
114
|
-
if (!nextDirectory) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
if (this.followSymlinks) {
|
|
118
|
-
try {
|
|
119
|
-
const realPath = fs.realpathSync(nextDirectory);
|
|
120
|
-
if (this.visitedDirectories.has(realPath)) {
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
this.visitedDirectories.add(realPath);
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
const resolvedDirectoryPath = path.resolve(nextDirectory);
|
|
130
|
-
const entries = fs.readdirSync(resolvedDirectoryPath, { withFileTypes: true });
|
|
131
|
-
entries.sort((firstEntry, secondEntry) => {
|
|
132
|
-
if (firstEntry.name < secondEntry.name) {
|
|
133
|
-
return -1;
|
|
134
|
-
}
|
|
135
|
-
if (firstEntry.name > secondEntry.name) {
|
|
136
|
-
return 1;
|
|
137
|
-
}
|
|
138
|
-
return 0;
|
|
139
|
-
});
|
|
140
|
-
for (const entry of entries) {
|
|
141
|
-
const currentPath = path.join(resolvedDirectoryPath, entry.name);
|
|
142
|
-
if (currentPath === this.zipPath) {
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
const relativePath = path.relative(this.directoryPath, currentPath);
|
|
146
|
-
const normalizedRelativePath = path.normalize(relativePath);
|
|
147
|
-
const archiveRelativePath = normalizedRelativePath.replace(/\\/g, '/');
|
|
148
|
-
const baseName = path.basename(normalizedRelativePath);
|
|
149
|
-
const normalizedPathValue = this.normalizeExcludeValue(normalizedRelativePath);
|
|
150
|
-
const normalizedNameValue = this.normalizeExcludeValue(baseName);
|
|
151
|
-
if (this.excludedPaths.has(normalizedPathValue) || this.excludedNames.has(normalizedNameValue)) {
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (entry.isFile()) {
|
|
155
|
-
if (this.includeBaseDirectory) {
|
|
156
|
-
archive.file(currentPath, {
|
|
157
|
-
name: path.posix.join(this.baseDirectory, archiveRelativePath)
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
archive.file(currentPath, {
|
|
162
|
-
name: archiveRelativePath
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
else if (entry.isDirectory()) {
|
|
167
|
-
directoriesToVisit.push(currentPath);
|
|
168
|
-
}
|
|
169
|
-
else if (entry.isSymbolicLink()) {
|
|
170
|
-
if (!this.followSymlinks) {
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
let stats;
|
|
174
|
-
try {
|
|
175
|
-
stats = fs.statSync(currentPath);
|
|
176
|
-
}
|
|
177
|
-
catch {
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
if (stats.isFile()) {
|
|
181
|
-
if (this.includeBaseDirectory) {
|
|
182
|
-
archive.file(currentPath, {
|
|
183
|
-
name: path.posix.join(this.baseDirectory, archiveRelativePath)
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
archive.file(currentPath, {
|
|
188
|
-
name: archiveRelativePath
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
else if (stats.isDirectory()) {
|
|
193
|
-
directoriesToVisit.push(currentPath);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
prettyBytes(bytes) {
|
|
200
|
-
if (bytes > 1000 && bytes < 1000000) {
|
|
201
|
-
const kiloBytes = Math.round(((bytes / 1000) + Number.EPSILON) * 100) / 100;
|
|
202
|
-
return `${kiloBytes} KB`;
|
|
203
|
-
}
|
|
204
|
-
if (bytes > 1000000 && bytes < 1000000000) {
|
|
205
|
-
const megaBytes = Math.round(((bytes / 1000000) + Number.EPSILON) * 100) / 100;
|
|
206
|
-
return `${megaBytes} MB`;
|
|
207
|
-
}
|
|
208
|
-
if (bytes > 1000000000) {
|
|
209
|
-
const gigaBytes = Math.round(((bytes / 1000000000) + Number.EPSILON) * 100) / 100;
|
|
210
|
-
return `${gigaBytes} GB`;
|
|
211
|
-
}
|
|
212
|
-
return `${bytes} bytes`;
|
|
213
|
-
}
|
|
214
|
-
normalizeExcludeValue(value) {
|
|
215
|
-
return this.caseInsensitiveExcludes ? value.toLowerCase() : value;
|
|
216
|
-
}
|
|
217
|
-
createZip() {
|
|
218
|
-
return new Promise((resolve, reject) => {
|
|
219
|
-
let output = null;
|
|
220
|
-
let archive = null;
|
|
221
|
-
let settled = false;
|
|
222
|
-
const safeResolve = (value) => {
|
|
223
|
-
if (settled) {
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
settled = true;
|
|
227
|
-
resolve(value);
|
|
228
|
-
};
|
|
229
|
-
const safeReject = (err) => {
|
|
230
|
-
if (settled) {
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
settled = true;
|
|
234
|
-
try {
|
|
235
|
-
if (archive) {
|
|
236
|
-
archive.abort();
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
catch {
|
|
240
|
-
// Ignore abort errors.
|
|
241
|
-
}
|
|
242
|
-
try {
|
|
243
|
-
if (output) {
|
|
244
|
-
output.destroy();
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
catch {
|
|
248
|
-
// Ignore destroy errors.
|
|
249
|
-
}
|
|
250
|
-
try {
|
|
251
|
-
if (fs.existsSync(this.zipPath)) {
|
|
252
|
-
fs.unlinkSync(this.zipPath);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
catch {
|
|
256
|
-
// Ignore cleanup errors.
|
|
257
|
-
}
|
|
258
|
-
const normalizedError = err instanceof Error ? err : new Error(String(err));
|
|
259
|
-
reject(normalizedError);
|
|
260
|
-
};
|
|
261
|
-
// Remove the destination zip if it exists.
|
|
262
|
-
// see : https://github.com/Ismail-elkorchi/dir-archiver/issues/5
|
|
263
|
-
try {
|
|
264
|
-
if (fs.existsSync(this.zipPath)) {
|
|
265
|
-
fs.unlinkSync(this.zipPath);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
catch (err) {
|
|
269
|
-
safeReject(err);
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
// Create a file to stream archive data to.
|
|
273
|
-
output = fs.createWriteStream(this.zipPath);
|
|
274
|
-
archive = (0, archiver_1.default)('zip', {
|
|
275
|
-
zlib: { level: 9 }
|
|
276
|
-
});
|
|
277
|
-
// Catch warnings during archiving.
|
|
278
|
-
archive.on('warning', (err) => {
|
|
279
|
-
if (err.code === 'ENOENT') {
|
|
280
|
-
// log warning
|
|
281
|
-
console.log(err);
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
safeReject(err);
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
// Catch errors during archiving.
|
|
288
|
-
archive.on('error', (err) => {
|
|
289
|
-
safeReject(err);
|
|
290
|
-
});
|
|
291
|
-
output.on('error', (err) => {
|
|
292
|
-
safeReject(err);
|
|
293
|
-
});
|
|
294
|
-
// Listen for all archive data to be written.
|
|
295
|
-
output.on('close', () => {
|
|
296
|
-
if (settled) {
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
console.log(`Created ${this.zipPath} of ${this.prettyBytes(archive.pointer())}`);
|
|
300
|
-
safeResolve(this.zipPath);
|
|
301
|
-
});
|
|
302
|
-
// Pipe archive data to the file.
|
|
303
|
-
archive.pipe(output);
|
|
304
|
-
// Recursively traverse the directory tree and append the files to the archive.
|
|
305
|
-
this.visitedDirectories.clear();
|
|
306
|
-
try {
|
|
307
|
-
this.traverseDirectoryTree(this.directoryPath, archive);
|
|
308
|
-
}
|
|
309
|
-
catch (err) {
|
|
310
|
-
safeReject(err);
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
// Finalize the archive.
|
|
314
|
-
void Promise.resolve(archive.finalize()).catch((err) => {
|
|
315
|
-
safeReject(err);
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
module.exports = DirArchiver;
|
|
18
|
+
export default api;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
let bunBindingsPromise;
|
|
2
|
+
export const loadBunBindings = async () => {
|
|
3
|
+
bunBindingsPromise !== null && bunBindingsPromise !== void 0 ? bunBindingsPromise : (bunBindingsPromise = import('@ismail-elkorchi/bytefold/bun')
|
|
4
|
+
.then((moduleExports) => {
|
|
5
|
+
const openArchive = moduleExports.openArchive;
|
|
6
|
+
const createArchiveWriter = moduleExports.createArchiveWriter;
|
|
7
|
+
if (typeof openArchive !== 'function' || typeof createArchiveWriter !== 'function') {
|
|
8
|
+
throw new Error('Bytefold bun runtime exports are unavailable.');
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
runtime: 'bun',
|
|
12
|
+
openArchive,
|
|
13
|
+
createArchiveWriter
|
|
14
|
+
};
|
|
15
|
+
}));
|
|
16
|
+
return bunBindingsPromise;
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
let denoBindingsPromise;
|
|
2
|
+
export const loadDenoBindings = async () => {
|
|
3
|
+
denoBindingsPromise !== null && denoBindingsPromise !== void 0 ? denoBindingsPromise : (denoBindingsPromise = import('@ismail-elkorchi/bytefold/deno')
|
|
4
|
+
.then((moduleExports) => {
|
|
5
|
+
const openArchive = moduleExports.openArchive;
|
|
6
|
+
const createArchiveWriter = moduleExports.createArchiveWriter;
|
|
7
|
+
if (typeof openArchive !== 'function' || typeof createArchiveWriter !== 'function') {
|
|
8
|
+
throw new Error('Bytefold deno runtime exports are unavailable.');
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
runtime: 'deno',
|
|
12
|
+
openArchive,
|
|
13
|
+
createArchiveWriter
|
|
14
|
+
};
|
|
15
|
+
}));
|
|
16
|
+
return denoBindingsPromise;
|
|
17
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DirArchiverError } from '../errors.js';
|
|
2
|
+
import { loadBunBindings } from './bun.js';
|
|
3
|
+
import { loadDenoBindings } from './deno.js';
|
|
4
|
+
import { loadNodeBindings } from './node.js';
|
|
5
|
+
const hasDenoGlobal = () => { var _a, _b; return typeof ((_b = (_a = globalThis.Deno) === null || _a === void 0 ? void 0 : _a.version) === null || _b === void 0 ? void 0 : _b.deno) === 'string'; };
|
|
6
|
+
const hasBunGlobal = () => { var _a; return typeof ((_a = globalThis.Bun) === null || _a === void 0 ? void 0 : _a.version) === 'string'; };
|
|
7
|
+
const hasNodeProcess = () => { var _a, _b; return typeof ((_b = (_a = globalThis.process) === null || _a === void 0 ? void 0 : _a.versions) === null || _b === void 0 ? void 0 : _b.node) === 'string'; };
|
|
8
|
+
const detectRuntime = () => {
|
|
9
|
+
if (hasDenoGlobal()) {
|
|
10
|
+
return 'deno';
|
|
11
|
+
}
|
|
12
|
+
if (hasBunGlobal()) {
|
|
13
|
+
return 'bun';
|
|
14
|
+
}
|
|
15
|
+
if (hasNodeProcess()) {
|
|
16
|
+
return 'node';
|
|
17
|
+
}
|
|
18
|
+
throw new DirArchiverError('DIRARCHIVER_RUNTIME_UNSUPPORTED', 'Unsupported runtime. Expected Node.js, Deno, or Bun.');
|
|
19
|
+
};
|
|
20
|
+
let runtimeBindingsPromise;
|
|
21
|
+
export const loadRuntimeBindings = async () => {
|
|
22
|
+
runtimeBindingsPromise !== null && runtimeBindingsPromise !== void 0 ? runtimeBindingsPromise : (runtimeBindingsPromise = (async () => {
|
|
23
|
+
const runtime = detectRuntime();
|
|
24
|
+
if (runtime === 'deno') {
|
|
25
|
+
return loadDenoBindings();
|
|
26
|
+
}
|
|
27
|
+
if (runtime === 'bun') {
|
|
28
|
+
return loadBunBindings();
|
|
29
|
+
}
|
|
30
|
+
return loadNodeBindings();
|
|
31
|
+
})());
|
|
32
|
+
return runtimeBindingsPromise;
|
|
33
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
let nodeBindingsPromise;
|
|
2
|
+
export const loadNodeBindings = async () => {
|
|
3
|
+
nodeBindingsPromise !== null && nodeBindingsPromise !== void 0 ? nodeBindingsPromise : (nodeBindingsPromise = import('@ismail-elkorchi/bytefold/node')
|
|
4
|
+
.then((moduleExports) => {
|
|
5
|
+
const openArchive = moduleExports.openArchive;
|
|
6
|
+
const createArchiveWriter = moduleExports.createArchiveWriter;
|
|
7
|
+
if (typeof openArchive !== 'function' || typeof createArchiveWriter !== 'function') {
|
|
8
|
+
throw new Error('Bytefold node runtime exports are unavailable.');
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
runtime: 'node',
|
|
12
|
+
openArchive,
|
|
13
|
+
createArchiveWriter
|
|
14
|
+
};
|
|
15
|
+
}));
|
|
16
|
+
return nodeBindingsPromise;
|
|
17
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ArchiveFormat, ArchiveOpenOptions, ArchiveReader } from '@ismail-elkorchi/bytefold';
|
|
2
|
+
export type RuntimeKind = 'node' | 'deno' | 'bun';
|
|
3
|
+
export interface ArchiveWriterLike {
|
|
4
|
+
add: (name: string, source: Uint8Array | ArrayBuffer | ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>) => Promise<void>;
|
|
5
|
+
close: () => Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export interface RuntimeBindings {
|
|
8
|
+
runtime: RuntimeKind;
|
|
9
|
+
openArchive: (input: unknown, options?: ArchiveOpenOptions) => Promise<ArchiveReader>;
|
|
10
|
+
createArchiveWriter: (format: ArchiveFormat, writable: WritableStream<Uint8Array>, options?: unknown) => ArchiveWriterLike;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { ArchiveDetectionReport, ArchiveFormat, ArchiveIssue, ArchiveLimits, ArchiveNormalizeReport, ArchiveOpenOptions, ArchiveProfile } from '@ismail-elkorchi/bytefold';
|
|
2
|
+
export type { ArchiveFormat, ArchiveLimits, ArchiveProfile };
|
|
3
|
+
/**
|
|
4
|
+
* Accepted input shapes for archive read operations.
|
|
5
|
+
*/
|
|
6
|
+
export type DirArchiverInput = string | URL | Uint8Array | ArrayBuffer | ReadableStream<Uint8Array> | Blob;
|
|
7
|
+
/**
|
|
8
|
+
* Common options forwarded to bytefold archive-open operations.
|
|
9
|
+
*
|
|
10
|
+
* Used by `open()`, `detect()`, `list()`, and `audit()`.
|
|
11
|
+
*/
|
|
12
|
+
export interface OpenOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Explicit format override when callers already know archive type.
|
|
15
|
+
*/
|
|
16
|
+
format?: ArchiveOpenOptions['format'] | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Safety profile (`compat`, `strict`, `agent`) applied during reads/audits.
|
|
19
|
+
*/
|
|
20
|
+
profile?: ArchiveOpenOptions['profile'] | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Extra strictness toggle forwarded to bytefold parsing.
|
|
23
|
+
*/
|
|
24
|
+
isStrict?: ArchiveOpenOptions['isStrict'] | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Parser/resource limits enforced while opening or auditing archives.
|
|
27
|
+
*/
|
|
28
|
+
limits?: ArchiveOpenOptions['limits'] | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Abort signal for cancelling in-flight async operations.
|
|
31
|
+
*/
|
|
32
|
+
signal?: ArchiveOpenOptions['signal'] | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Password used for encrypted archives when supported by the runtime.
|
|
35
|
+
*/
|
|
36
|
+
password?: ArchiveOpenOptions['password'] | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Filename hint used for extension-based inference with non-path inputs.
|
|
39
|
+
*/
|
|
40
|
+
filename?: ArchiveOpenOptions['filename'] | undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Format detection result.
|
|
44
|
+
*/
|
|
45
|
+
export interface DetectResult {
|
|
46
|
+
format: ArchiveFormat;
|
|
47
|
+
detection: ArchiveDetectionReport | undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Single archive entry projection used by list responses.
|
|
51
|
+
*/
|
|
52
|
+
export interface ListEntry {
|
|
53
|
+
format: ArchiveFormat;
|
|
54
|
+
name: string;
|
|
55
|
+
size: string;
|
|
56
|
+
isDirectory: boolean;
|
|
57
|
+
isSymlink: boolean;
|
|
58
|
+
linkName?: string | undefined;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Archive listing response payload.
|
|
62
|
+
*/
|
|
63
|
+
export interface ListResult {
|
|
64
|
+
format: ArchiveFormat;
|
|
65
|
+
detection: ArchiveDetectionReport | undefined;
|
|
66
|
+
entries: ListEntry[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Options for `audit()`.
|
|
70
|
+
*
|
|
71
|
+
* Alias of `OpenOptions` for stable API typing; CLI-only flags (for example
|
|
72
|
+
* `--json`) are not part of this programmatic surface.
|
|
73
|
+
*/
|
|
74
|
+
export type AuditOptions = OpenOptions;
|
|
75
|
+
/**
|
|
76
|
+
* Normalize operation options.
|
|
77
|
+
*/
|
|
78
|
+
export interface NormalizeOptions extends OpenOptions {
|
|
79
|
+
deterministic?: boolean | undefined;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Normalize operation result payload.
|
|
83
|
+
*/
|
|
84
|
+
export interface NormalizeResult {
|
|
85
|
+
format: ArchiveFormat;
|
|
86
|
+
report: ArchiveNormalizeReport;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Extraction options with explicit safety limits.
|
|
90
|
+
*
|
|
91
|
+
* `extract()` defaults to `profile: 'strict'` when no profile is supplied.
|
|
92
|
+
*/
|
|
93
|
+
export interface ExtractOptions extends OpenOptions {
|
|
94
|
+
/**
|
|
95
|
+
* If `true`, symbolic-link entries are materialized on disk; otherwise they
|
|
96
|
+
* are skipped and counted in `ExtractResult.skippedEntries`.
|
|
97
|
+
*/
|
|
98
|
+
allowSymlinks?: boolean | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Reserved for forward compatibility. Hard-link entries are currently
|
|
101
|
+
* rejected with `DIRARCHIVER_UNSUPPORTED_ENTRY` regardless of this flag.
|
|
102
|
+
*/
|
|
103
|
+
allowHardlinks?: boolean | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Maximum bytes allowed for any single extracted file entry.
|
|
106
|
+
*/
|
|
107
|
+
maxEntryBytes?: number | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* Maximum cumulative bytes allowed across all extracted file entries.
|
|
110
|
+
*/
|
|
111
|
+
maxTotalExtractedBytes?: number | undefined;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Extraction summary result.
|
|
115
|
+
*/
|
|
116
|
+
export interface ExtractResult {
|
|
117
|
+
format: ArchiveFormat;
|
|
118
|
+
destination: string;
|
|
119
|
+
extractedFiles: number;
|
|
120
|
+
extractedDirectories: number;
|
|
121
|
+
skippedEntries: number;
|
|
122
|
+
issues: ArchiveIssue[];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Archive writer options.
|
|
126
|
+
*/
|
|
127
|
+
export interface WriteOptions {
|
|
128
|
+
/**
|
|
129
|
+
* Requested output format. If omitted, inferred from destination extension
|
|
130
|
+
* and falls back to `zip` when inference is not possible.
|
|
131
|
+
*/
|
|
132
|
+
format?: ArchiveFormat | undefined;
|
|
133
|
+
/**
|
|
134
|
+
* Includes the source directory name as a root folder in the archive when
|
|
135
|
+
* source is a directory.
|
|
136
|
+
*/
|
|
137
|
+
includeBaseDirectory?: boolean | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* Follows symbolic links while walking directory sources for `write()`.
|
|
140
|
+
*/
|
|
141
|
+
followSymlinks?: boolean | undefined;
|
|
142
|
+
/**
|
|
143
|
+
* Glob-like exclusion patterns evaluated relative to the source root.
|
|
144
|
+
*/
|
|
145
|
+
exclude?: string[] | undefined;
|
|
146
|
+
/**
|
|
147
|
+
* Writer profile (`compat`, `strict`, `agent`) forwarded to bytefold.
|
|
148
|
+
*/
|
|
149
|
+
profile?: ArchiveProfile | undefined;
|
|
150
|
+
/**
|
|
151
|
+
* Optional writer limits passed through to bytefold operations.
|
|
152
|
+
*/
|
|
153
|
+
limits?: ArchiveLimits | undefined;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Archive writer result payload.
|
|
157
|
+
*/
|
|
158
|
+
export interface WriteResult {
|
|
159
|
+
format: ArchiveFormat;
|
|
160
|
+
source: string;
|
|
161
|
+
destination: string;
|
|
162
|
+
entryCount: number;
|
|
163
|
+
wrappedDirectoryCodec: boolean;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Usage-error shape emitted by CLI parsing.
|
|
167
|
+
*/
|
|
168
|
+
export interface CliUsageError {
|
|
169
|
+
message: string;
|
|
170
|
+
issues: readonly {
|
|
171
|
+
code: string;
|
|
172
|
+
message: string;
|
|
173
|
+
}[];
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Canonical command identifiers supported by the CLI contract.
|
|
177
|
+
*/
|
|
178
|
+
export interface SupportedCommandMap {
|
|
179
|
+
open: 'open';
|
|
180
|
+
detect: 'detect';
|
|
181
|
+
list: 'list';
|
|
182
|
+
audit: 'audit';
|
|
183
|
+
extract: 'extract';
|
|
184
|
+
normalize: 'normalize';
|
|
185
|
+
write: 'write';
|
|
186
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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.
|