@poe-platform/safe-bash 0.1.612 → 0.1.614
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.
|
@@ -27406,9 +27406,13 @@ async function parse12(scope, limits) {
|
|
|
27406
27406
|
for (const value2 of argumentsValue.values) text2(shellValueBytes(value2));
|
|
27407
27407
|
}
|
|
27408
27408
|
let archive;
|
|
27409
|
+
let action = "add";
|
|
27409
27410
|
let recursive = false;
|
|
27410
27411
|
let quiet = false;
|
|
27411
27412
|
let junkPaths = false;
|
|
27413
|
+
let omitDirectories = false;
|
|
27414
|
+
let storeLinks = false;
|
|
27415
|
+
let test = false;
|
|
27412
27416
|
let level = 6;
|
|
27413
27417
|
let stdinNames = false;
|
|
27414
27418
|
let literal = false;
|
|
@@ -27426,8 +27430,15 @@ async function parse12(scope, limits) {
|
|
|
27426
27430
|
for (let offset = 1; offset < argument2.length; offset++) {
|
|
27427
27431
|
const flag = argument2[offset];
|
|
27428
27432
|
if (flag === "r") recursive = true;
|
|
27429
|
-
else if (flag === "
|
|
27433
|
+
else if (flag === "d" || flag === "u" || flag === "f") {
|
|
27434
|
+
const next = flag === "d" ? "delete" : flag === "u" ? "update" : "freshen";
|
|
27435
|
+
if (action !== "add" && action !== next) throw new ZipFailure(16, "Invalid command arguments", "specify just one action");
|
|
27436
|
+
action = next;
|
|
27437
|
+
} else if (flag === "q") quiet = true;
|
|
27430
27438
|
else if (flag === "j") junkPaths = true;
|
|
27439
|
+
else if (flag === "D") omitDirectories = true;
|
|
27440
|
+
else if (flag === "y") storeLinks = true;
|
|
27441
|
+
else if (flag === "T") test = true;
|
|
27431
27442
|
else if (flag === "@") stdinNames = true;
|
|
27432
27443
|
else if (flag !== void 0 && flag >= "0" && flag <= "9") level = Number(flag);
|
|
27433
27444
|
else if (flag === "i" || flag === "x") {
|
|
@@ -27485,7 +27496,7 @@ async function parse12(scope, limits) {
|
|
|
27485
27496
|
start = end + 1;
|
|
27486
27497
|
}
|
|
27487
27498
|
}
|
|
27488
|
-
return { archive, recursive, quiet, junkPaths, includes, excludes, level, operands: [...names, ...operands], firstOperand };
|
|
27499
|
+
return { action, archive, recursive, quiet, junkPaths, omitDirectories, storeLinks, test, includes, excludes, level, operands: [...names, ...operands], firstOperand };
|
|
27489
27500
|
}
|
|
27490
27501
|
function memberName(path, limits) {
|
|
27491
27502
|
if (path.startsWith("//") && !path.startsWith("///")) fail("UNC source paths are unsupported");
|
|
@@ -27500,6 +27511,30 @@ function memberName(path, limits) {
|
|
|
27500
27511
|
function unchanged4(before, after) {
|
|
27501
27512
|
return before.type === after.type && before.size === after.size && before.mode === after.mode && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs && before.nlink === after.nlink && (!hasIdentity(before) || sameIdentity2(before, after));
|
|
27502
27513
|
}
|
|
27514
|
+
async function inspectSource(scope, path, storeLinks) {
|
|
27515
|
+
const { fs, signal } = scope.context;
|
|
27516
|
+
if (storeLinks) {
|
|
27517
|
+
const stat2 = await scope.operation(() => fs.lstat(path, { signal }));
|
|
27518
|
+
if (stat2.type === "symlink") {
|
|
27519
|
+
const parent = await scope.operation(() => fs.realpath(dirname(path), { signal }));
|
|
27520
|
+
const canonical2 = `${parent === "/" ? "" : parent}/${path.slice(path.lastIndexOf("/") + 1)}`;
|
|
27521
|
+
return { canonical: canonical2, stat: stat2 };
|
|
27522
|
+
}
|
|
27523
|
+
}
|
|
27524
|
+
const canonical = await scope.operation(() => fs.realpath(path, { signal }));
|
|
27525
|
+
const stat = await scope.operation(() => fs.stat(path, { signal }));
|
|
27526
|
+
return { canonical, stat };
|
|
27527
|
+
}
|
|
27528
|
+
async function filterName(name2, selection, includeCount) {
|
|
27529
|
+
selection.matched.clear();
|
|
27530
|
+
await selection.matches(name2);
|
|
27531
|
+
let included = includeCount === 0;
|
|
27532
|
+
for (const pattern of selection.matched) {
|
|
27533
|
+
if (pattern >= includeCount) return false;
|
|
27534
|
+
included = true;
|
|
27535
|
+
}
|
|
27536
|
+
return included;
|
|
27537
|
+
}
|
|
27503
27538
|
async function prepare(scope, parsed, budget) {
|
|
27504
27539
|
const context2 = scope.context;
|
|
27505
27540
|
const limits = budget.limits;
|
|
@@ -27521,7 +27556,10 @@ async function prepare(scope, parsed, budget) {
|
|
|
27521
27556
|
if (!current || !unchanged4(existing2, current)) fail("archive changed while reading");
|
|
27522
27557
|
}
|
|
27523
27558
|
const old = new Map(archive.entries.map((entry) => [entry.name, entry]));
|
|
27559
|
+
if (!archive.entries.length && !parsed.quiet && (parsed.action === "update" || parsed.action === "freshen")) await budget.output(` zip warning: ${parsed.archive} not found or empty
|
|
27560
|
+
`);
|
|
27524
27561
|
const selected = /* @__PURE__ */ new Map();
|
|
27562
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
27525
27563
|
const selection = new Selection([...parsed.includes, ...parsed.excludes], limits, context2.signal);
|
|
27526
27564
|
const ancestors = [];
|
|
27527
27565
|
let visits = 0;
|
|
@@ -27538,8 +27576,7 @@ async function prepare(scope, parsed, budget) {
|
|
|
27538
27576
|
let canonical;
|
|
27539
27577
|
let stat;
|
|
27540
27578
|
try {
|
|
27541
|
-
canonical = await scope
|
|
27542
|
-
stat = await scope.operation(() => context2.fs.stat(path, { signal: context2.signal }));
|
|
27579
|
+
({ canonical, stat } = await inspectSource(scope, path, parsed.storeLinks));
|
|
27543
27580
|
} catch (error) {
|
|
27544
27581
|
context2.signal.throwIfAborted();
|
|
27545
27582
|
if (typeof error !== "object" || error === null || !("code" in error) || error.code !== "ENOENT") throw error;
|
|
@@ -27549,7 +27586,8 @@ async function prepare(scope, parsed, budget) {
|
|
|
27549
27586
|
}
|
|
27550
27587
|
checkPath(canonical, limits);
|
|
27551
27588
|
if (canonical === output2 || existing2 && sameIdentity2(existing2, stat)) return;
|
|
27552
|
-
|
|
27589
|
+
const symlink = parsed.storeLinks && stat.type === "symlink";
|
|
27590
|
+
if (stat.type !== "file" && stat.type !== "directory" && !symlink) {
|
|
27553
27591
|
if (!parsed.quiet) await budget.output(` zip warning: ignoring special file: ${source2}
|
|
27554
27592
|
`);
|
|
27555
27593
|
return;
|
|
@@ -27557,17 +27595,12 @@ async function prepare(scope, parsed, budget) {
|
|
|
27557
27595
|
if (existing2 && !hasIdentity(stat)) fail("cannot exclude archive aliases when source backing identity is unknown");
|
|
27558
27596
|
const directory2 = stat.type === "directory";
|
|
27559
27597
|
if (directory2 && name2 && !name2.endsWith("/")) name2 += "/";
|
|
27560
|
-
selection.
|
|
27561
|
-
await selection.matches(name2);
|
|
27562
|
-
let included = parsed.includes.length === 0;
|
|
27563
|
-
let excluded = false;
|
|
27564
|
-
for (const pattern of selection.matched) {
|
|
27565
|
-
if (pattern < parsed.includes.length) included = true;
|
|
27566
|
-
else excluded = true;
|
|
27567
|
-
}
|
|
27598
|
+
const included = await filterName(name2, selection, parsed.includes.length);
|
|
27568
27599
|
const sourceName = name2;
|
|
27569
27600
|
if (parsed.junkPaths) name2 = directory2 ? "" : name2.slice(name2.lastIndexOf("/") + 1);
|
|
27570
|
-
|
|
27601
|
+
const prior = old.get(name2);
|
|
27602
|
+
const eligible = parsed.action !== "update" && parsed.action !== "freshen" || (prior ? Math.floor(stat.mtimeMs / 1e3) > Math.floor(prior.modified.getTime() / 1e3) : parsed.action === "update");
|
|
27603
|
+
if (name2 && included && eligible && !(directory2 && parsed.omitDirectories)) {
|
|
27571
27604
|
checkPath(name2, limits);
|
|
27572
27605
|
const previous = selected.get(name2);
|
|
27573
27606
|
if (previous && previous.source !== source2) {
|
|
@@ -27579,14 +27612,19 @@ async function prepare(scope, parsed, budget) {
|
|
|
27579
27612
|
}
|
|
27580
27613
|
if (!previous) {
|
|
27581
27614
|
await budget.member(directory2 ? 0 : stat.size);
|
|
27582
|
-
|
|
27615
|
+
let bytes4;
|
|
27616
|
+
if (directory2) bytes4 = new Uint8Array();
|
|
27617
|
+
else if (symlink) {
|
|
27618
|
+
if (!context2.fs.readlink) fail("filesystem does not support reading symbolic links");
|
|
27619
|
+
const target = await scope.operation(() => context2.fs.readlink(path, { signal: context2.signal }));
|
|
27620
|
+
if (import_buffer.Buffer.byteLength(target) > stat.size) fail(`source changed while reading: ${source2}`);
|
|
27621
|
+
bytes4 = import_buffer.Buffer.from(target);
|
|
27622
|
+
} else bytes4 = await collectBytes(scope.input(path), { maxBytes: stat.size, signal: context2.signal });
|
|
27583
27623
|
if (!directory2 && bytes4.length !== stat.size) fail(`source changed while reading: ${source2}`);
|
|
27584
|
-
const current = await scope
|
|
27585
|
-
|
|
27586
|
-
|
|
27587
|
-
let entry = await makeZipEntry(name2, bytes4, { modified: new Date(stat.mtimeMs), mode: stat.mode, directory: directory2, symlink: false }, limits, context2.signal, parsed.level);
|
|
27624
|
+
const current = await inspectSource(scope, path, parsed.storeLinks);
|
|
27625
|
+
if (current.canonical !== canonical || !unchanged4(stat, current.stat)) fail(`source changed while reading: ${source2}`);
|
|
27626
|
+
let entry = await makeZipEntry(name2, bytes4, { modified: new Date(stat.mtimeMs), mode: stat.mode, directory: directory2, symlink }, limits, context2.signal, parsed.level);
|
|
27588
27627
|
if ([".z", ".zip", ".zoo", ".arc", ".lzh", ".arj"].some((suffix2) => name2.toLowerCase().endsWith(suffix2))) entry = { ...entry, method: 0, data: bytes4 };
|
|
27589
|
-
const prior = old.get(name2);
|
|
27590
27628
|
if (prior?.comment) entry = { ...entry, comment: prior.comment };
|
|
27591
27629
|
if (entry.data.length > limits.maxArchiveBytes - compressedBytes) fail("archive byte limit exceeded");
|
|
27592
27630
|
compressedBytes += entry.data.length;
|
|
@@ -27609,26 +27647,53 @@ async function prepare(scope, parsed, budget) {
|
|
|
27609
27647
|
}
|
|
27610
27648
|
}
|
|
27611
27649
|
};
|
|
27612
|
-
|
|
27613
|
-
|
|
27614
|
-
|
|
27650
|
+
if (parsed.action === "delete") {
|
|
27651
|
+
if (!parsed.quiet && parsed.recursive) await budget.output(" zip warning: invalid option(s) used with -d; ignored.\n");
|
|
27652
|
+
if (!parsed.quiet && !archive.entries.length) await budget.output(` zip warning: ${parsed.archive} not found or empty
|
|
27653
|
+
`);
|
|
27654
|
+
const operands = new Selection(parsed.operands, limits, context2.signal);
|
|
27655
|
+
if (parsed.operands.length) {
|
|
27656
|
+
for (const entry of archive.entries) {
|
|
27657
|
+
if (await operands.matches(entry.name) && await filterName(entry.name, selection, parsed.includes.length)) deleted.add(entry.name);
|
|
27658
|
+
}
|
|
27659
|
+
}
|
|
27660
|
+
if (!parsed.quiet) {
|
|
27661
|
+
for (const [index, operand] of parsed.operands.entries()) {
|
|
27662
|
+
if (!operands.matched.has(index)) await budget.output(` zip warning: name not matched: ${operand}
|
|
27663
|
+
`);
|
|
27664
|
+
}
|
|
27665
|
+
}
|
|
27666
|
+
} else {
|
|
27667
|
+
const operands = parsed.operands.length || parsed.action === "add" ? parsed.operands : archive.entries.map((entry) => entry.name);
|
|
27668
|
+
for (const operand of operands) await visit(operand, memberName(operand, limits), 0);
|
|
27669
|
+
}
|
|
27670
|
+
if (!selected.size && (parsed.action === "freshen" || parsed.action === "update" && (existing2 || !parsed.includes.length))) return void 0;
|
|
27671
|
+
if (!selected.size && !deleted.size && (parsed.action === "delete" || !parsed.includes.length)) {
|
|
27672
|
+
const detail = parsed.action !== "delete" && parsed.recursive && parsed.firstOperand >= 0 ? `try: zip ${context2.args.slice(0, parsed.firstOperand).join(" ")} . -i ${context2.args.slice(parsed.firstOperand).join(" ")}` : parsed.archive;
|
|
27615
27673
|
throw new ZipFailure(12, "Nothing to do!", detail);
|
|
27616
27674
|
}
|
|
27617
27675
|
const entries2 = [];
|
|
27618
27676
|
const progress = [];
|
|
27619
27677
|
let progressBytes = 0;
|
|
27620
|
-
const
|
|
27678
|
+
const queue = (message3) => {
|
|
27621
27679
|
if (parsed.quiet) return;
|
|
27622
|
-
const percentage = entry.size ? Math.trunc((Math.trunc(200 * (entry.size - entry.data.length) / entry.size) + 1) / 2) : 0;
|
|
27623
|
-
const message3 = `${update ? "updating:" : " adding:"} ${entry.name} (${entry.method === 8 ? `deflated ${percentage}%` : "stored 0%"})
|
|
27624
|
-
`;
|
|
27625
27680
|
const size = import_buffer.Buffer.byteLength(message3);
|
|
27626
27681
|
if (size > limits.maxTextBytes - budget.textBytes - progressBytes) fail("text output limit exceeded");
|
|
27627
27682
|
progressBytes += size;
|
|
27628
27683
|
progress.push(message3);
|
|
27629
27684
|
};
|
|
27685
|
+
const append = (entry, update) => {
|
|
27686
|
+
const percentage = entry.size ? Math.trunc((Math.trunc(200 * (entry.size - entry.data.length) / entry.size) + 1) / 2) : 0;
|
|
27687
|
+
queue(`${update ? parsed.action === "freshen" ? "freshening:" : "updating:" : " adding:"} ${entry.name} (${entry.method === 8 ? `deflated ${percentage}%` : "stored 0%"})
|
|
27688
|
+
`);
|
|
27689
|
+
};
|
|
27630
27690
|
for (const entry of archive.entries) {
|
|
27631
27691
|
if (++work > limits.maxPatternSteps) fail("archive work limit exceeded");
|
|
27692
|
+
if (deleted.has(entry.name)) {
|
|
27693
|
+
queue(`deleting: ${entry.name}
|
|
27694
|
+
`);
|
|
27695
|
+
continue;
|
|
27696
|
+
}
|
|
27632
27697
|
const replacement = selected.get(entry.name);
|
|
27633
27698
|
if (replacement) {
|
|
27634
27699
|
entries2.push(replacement.entry);
|
|
@@ -27643,8 +27708,33 @@ async function prepare(scope, parsed, budget) {
|
|
|
27643
27708
|
entries2.push(entry);
|
|
27644
27709
|
append(entry, false);
|
|
27645
27710
|
}
|
|
27646
|
-
if (!entries2.length
|
|
27711
|
+
if (!entries2.length) queue(" zip warning: zip file empty\n");
|
|
27647
27712
|
const bytes3 = await writeZipArchive({ entries: entries2, comment: archive.comment }, limits, context2.signal);
|
|
27713
|
+
if (parsed.test) {
|
|
27714
|
+
for (const message3 of progress) await budget.output(message3);
|
|
27715
|
+
progress.length = 0;
|
|
27716
|
+
progressBytes = 0;
|
|
27717
|
+
try {
|
|
27718
|
+
const tested = await readZipArchive(bytes3, limits, context2.signal);
|
|
27719
|
+
if (!tested.entries.length) fail("empty ZIP archive");
|
|
27720
|
+
let decoded = 0;
|
|
27721
|
+
for (const entry of tested.entries) {
|
|
27722
|
+
for await (const chunk of decodeZipEntry(entry, limits, context2.signal)) {
|
|
27723
|
+
if (chunk.length > limits.maxTotalBytes - decoded) fail("actual decompressed byte limit exceeded");
|
|
27724
|
+
decoded += chunk.length;
|
|
27725
|
+
}
|
|
27726
|
+
}
|
|
27727
|
+
} catch {
|
|
27728
|
+
context2.signal.throwIfAborted();
|
|
27729
|
+
if (!parsed.quiet) await budget.output(`test of ${parsed.archive} FAILED
|
|
27730
|
+
`);
|
|
27731
|
+
throw new ZipFailure(8, "Zip file invalid, could not spawn unzip, or wrong unzip", "original files unmodified");
|
|
27732
|
+
}
|
|
27733
|
+
if (!parsed.quiet) {
|
|
27734
|
+
queue(`test of ${parsed.archive} OK
|
|
27735
|
+
`);
|
|
27736
|
+
}
|
|
27737
|
+
}
|
|
27648
27738
|
return { output: output2, parentName, parent, parentStat, existing: existing2, bytes: bytes3, progress };
|
|
27649
27739
|
}
|
|
27650
27740
|
function createZipCommand(options2 = {}) {
|
|
@@ -27657,6 +27747,7 @@ function createZipCommand(options2 = {}) {
|
|
|
27657
27747
|
try {
|
|
27658
27748
|
const parsed = await parse12(scope, limits);
|
|
27659
27749
|
const prepared = await prepare(scope, parsed, budget);
|
|
27750
|
+
if (!prepared) return { exitCode: 12 };
|
|
27660
27751
|
await writeFileOutput(context2, prepared.bytes, () => scope.operation(() => publishZip(scope, prepared)));
|
|
27661
27752
|
for (const message3 of prepared.progress) await budget.output(message3);
|
|
27662
27753
|
return { exitCode: 0 };
|