@poe-platform/safe-bash 0.1.842 → 0.1.844

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.
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createFfprobeCommand
3
- } from "../safe-bash/command-chunks/chunk-MJB4WWYP.js";
3
+ } from "../safe-bash/command-chunks/chunk-3SWIXYXT.js";
4
4
  import "../safe-bash/command-chunks/chunk-MGBHGZEX.js";
5
- import "../safe-bash/command-chunks/chunk-SBBMNLD6.js";
5
+ import "../safe-bash/command-chunks/chunk-LORSYIAF.js";
6
6
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
7
7
 
8
8
  // packages/safe-bash-command-ffprobe/src/index.ts
@@ -11,9 +11,9 @@ import {
11
11
  loadManifestResources,
12
12
  resolvePath,
13
13
  rethrowRuntimeError
14
- } from "../safe-bash/command-chunks/chunk-MJB4WWYP.js";
14
+ } from "../safe-bash/command-chunks/chunk-3SWIXYXT.js";
15
15
  import "../safe-bash/command-chunks/chunk-MGBHGZEX.js";
16
- import "../safe-bash/command-chunks/chunk-SBBMNLD6.js";
16
+ import "../safe-bash/command-chunks/chunk-LORSYIAF.js";
17
17
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
18
18
  export {
19
19
  createFfprobeCommand,
@@ -1273,10 +1273,10 @@ function* combinePixelSteps(size, lengths) {
1273
1273
  for (let offset = 3; offset < length; offset += 4) data[offset] = 255;
1274
1274
  continue;
1275
1275
  }
1276
- const count = Math.max(0, Math.min(length, lengths[image] - position));
1276
+ const count = Math.max(0, Math.min(length, (lengths[image] ?? 0) - position));
1277
1277
  if (!count) continue;
1278
1278
  const source = yield { kind: "read", image, position, length: count };
1279
- for (let offset = 0; offset < count; offset += 4) data[offset + image] = source[offset];
1279
+ for (let offset = 0; offset < count; offset += 4) data[offset + image] = Math.round(0.212656 * source[offset] + 0.715158 * source[offset + 1] + 0.072186 * source[offset + 2]);
1280
1280
  }
1281
1281
  yield { kind: "write", position, data };
1282
1282
  }
@@ -3651,7 +3651,7 @@ import {
3651
3651
  } from "../safe-bash-contracts/command.js";
3652
3652
  import { readBytes as readBytes2, writeBytes as writeBytes2 } from "../safe-bash-contracts/io.js";
3653
3653
  import { createOutputOperation } from "../safe-bash-contracts/output.js";
3654
- import { readImageMetadataFromSource as readImageMetadataFromSource3, decodeImageToStorage as decodeImageToStorage2, transformStoredImage as transformStoredImage2, UnsupportedStoredResource as UnsupportedStoredResource4, decodeImage as decodeImage3, decodePngToCanvas, FONT_5X7, encodeImage, readImageMetadata as readImageMetadata3, parseColor as baseParseColor, applyExifOrientationSteps, compositeImageSteps, computeImageStatsSteps as computeImageStatsSteps2, ensureAlphaImageSteps, extendImageSteps, extractChannelImageSteps, extractImageSteps as extractImageSteps2, flattenImageSteps, flipImageSteps, flopImageSteps, grayscaleImageSteps, removeAlphaImageSteps, resizeImageSteps, rotateImageSteps, thresholdImageSteps } from "../image-ast/portable.js";
3654
+ import { readImageMetadataFromSource as readImageMetadataFromSource3, decodeImageToStorage as decodeImageToStorage2, transformStoredImage as transformStoredImage2, UnsupportedStoredResource as UnsupportedStoredResource4, decodeImage as decodeImage3, decodePngToCanvas, FONT_5X7, encodeImage, readImageMetadata as readImageMetadata3, parseColor as baseParseColor, applyExifOrientationSteps, compositeImageSteps, computeImageStatsSteps as computeImageStatsSteps2, ensureAlphaImageSteps, extendImageSteps, extractChannelImageSteps, extractImageSteps as extractImageSteps2, flipImageSteps, flopImageSteps, grayscaleImageSteps, resizeImageSteps, rotateImageSteps, thresholdImageSteps } from "../image-ast/portable.js";
3655
3655
  var X11_NAMED_COLORS = {
3656
3656
  aliceblue: [240, 248, 255, 255],
3657
3657
  antiquewhite: [250, 235, 215, 255],
@@ -4805,11 +4805,10 @@ function* applyMagickSpliceSteps(img, geomStr, bg, gravity = "northwest", signal
4805
4805
  const g = parseMagickGeometry(geomStr);
4806
4806
  const sw = Math.max(0, Math.round(g.width ?? 0));
4807
4807
  const sh = Math.max(0, Math.round(g.height ?? 0));
4808
- const base = resolveGravityOffset(img.width, img.height, gravity);
4809
- const isEast = gravity === "east" || gravity === "northeast" || gravity === "southeast";
4810
- const isSouth = gravity === "south" || gravity === "southwest" || gravity === "southeast";
4811
- const sx = Math.max(0, Math.min(img.width, Math.round(base.left + (isEast ? -g.x : g.x))));
4812
- const sy = Math.max(0, Math.min(img.height, Math.round(base.top + (isSouth ? -g.y : g.y))));
4808
+ const box = gravityAdjustBox(img.width, img.height, sw, sh, g.x, g.y, gravity);
4809
+ const anchor = resolveGravityOffset(sw, sh, gravity);
4810
+ const sx = Math.max(0, Math.min(img.width, box.x + anchor.left));
4811
+ const sy = Math.max(0, Math.min(img.height, box.y + anchor.top));
4813
4812
  const outW = img.width + sw;
4814
4813
  const outH = img.height + sh;
4815
4814
  const out = new Uint8Array(new ArrayBuffer(outW * outH * 4 + outH), 0, outW * outH * 4);
@@ -4841,8 +4840,8 @@ function* applyMagickSpliceSteps(img, geomStr, bg, gravity = "northwest", signal
4841
4840
  function* applyMagickChopSteps(img, geomStr, gravity = "northwest", signal) {
4842
4841
  let cooperativeWork = 0;
4843
4842
  const g = parseMagickGeometry(geomStr);
4844
- const cw = Math.max(0, Math.round(g.width ?? 0));
4845
- const ch = Math.max(0, Math.round(g.height ?? 0));
4843
+ const cw = Math.max(0, Math.round((g.width ?? 0) * (g.isPercent ? img.width / 100 : 1)));
4844
+ const ch = Math.max(0, Math.round((g.height ?? 0) * (g.isPercent ? img.height / 100 : 1)));
4846
4845
  const { x: x0, y: y0 } = gravityAdjustBox(img.width, img.height, cw, ch, g.x, g.y, gravity);
4847
4846
  if (x0 + cw < 0 || y0 + ch < 0 || x0 > img.width || y0 > img.height) {
4848
4847
  return img;
@@ -4880,6 +4879,7 @@ function* applyMagickChopSteps(img, geomStr, gravity = "northwest", signal) {
4880
4879
  }
4881
4880
  function parseMagickRollGeometry(raw) {
4882
4881
  const geometry = parseMagickGeometry(raw);
4882
+ if (geometry.isPercent) return { ...geometry, x: 0, y: 0 };
4883
4883
  if (geometry.hasOffset && geometry.width !== void 0 && !raw.toLowerCase().includes("x")) {
4884
4884
  throw new Error(`invalid argument for option '-roll': ${raw}`);
4885
4885
  }
@@ -7798,32 +7798,37 @@ function runIdentifyCliSync(argv, files, stdinBytes, signal) {
7798
7798
  }
7799
7799
  return next.value;
7800
7800
  }
7801
- function* parseMagickIndexSpec(spec, length) {
7801
+ function parseMagickIndices(spec, ranges = true) {
7802
+ if (!spec.trim() || spec.trimStart().startsWith(",")) throw new Error(`invalid argument ${spec}`);
7803
+ return spec.split(",").filter((part) => part.trim()).map((part) => {
7804
+ const value = part.trim();
7805
+ const separator = ranges ? value.indexOf("-", 1) : -1;
7806
+ const values = separator > 0 ? [value.slice(0, separator), value.slice(separator + 1)] : [value];
7807
+ return values.map((value2) => {
7808
+ const digits = value2.startsWith("-") ? value2.slice(1) : value2;
7809
+ if (!digits.length || !Array.from(digits).every((c) => c >= "0" && c <= "9") || !Number.isSafeInteger(Number(value2))) {
7810
+ throw new Error(`invalid argument ${spec}`);
7811
+ }
7812
+ return Number(value2);
7813
+ });
7814
+ });
7815
+ }
7816
+ function* parseMagickIndexSpec(spec, length, operation) {
7817
+ const parts = parseMagickIndices(spec);
7802
7818
  if (length <= 0) return;
7803
7819
  const resolve = (n) => n < 0 ? Math.max(0, length + n) : Math.min(Math.max(0, n), length - 1);
7804
- for (const part of spec.split(",")) {
7805
- const trimmed = part.trim();
7806
- if (!trimmed) continue;
7807
- const separator = trimmed.indexOf("-", 1);
7808
- const integer = (value) => {
7809
- const digits = value.startsWith("-") ? value.slice(1) : value;
7810
- return digits.length > 0 && Array.from(digits).every((c) => c >= "0" && c <= "9");
7811
- };
7812
- const rangeMatch = separator > 0 && integer(trimmed.slice(0, separator)) && integer(trimmed.slice(separator + 1)) ? [trimmed, trimmed.slice(0, separator), trimmed.slice(separator + 1)] : void 0;
7813
- if (rangeMatch) {
7814
- const s = resolve(parseInt(rangeMatch[1], 10));
7815
- const e = resolve(parseInt(rangeMatch[2], 10));
7816
- if (s <= e) {
7817
- for (let k = s; k <= e; k++) yield k;
7818
- } else {
7819
- for (let k = s; k >= e; k--) yield k;
7820
+ for (const [num, end] of parts) {
7821
+ if (end !== void 0) {
7822
+ const s = resolve(num), e = resolve(end), step = s <= e ? 1 : -1;
7823
+ if (operation === "delete" && s > e) continue;
7824
+ for (let k = s; ; k += step) {
7825
+ yield k;
7826
+ if (k === e) break;
7820
7827
  }
7821
7828
  } else {
7822
- const num = parseInt(trimmed, 10);
7823
- if (!Number.isNaN(num)) {
7824
- const idx = num < 0 ? length + num : num;
7825
- if (idx >= 0 && idx < length) yield idx;
7826
- }
7829
+ const idx = num < 0 ? length + num : num;
7830
+ if (idx >= 0 && idx < length) yield idx;
7831
+ else if (operation === "clone") throw new Error(`invalid image index ${num}`);
7827
7832
  }
7828
7833
  }
7829
7834
  }
@@ -8061,7 +8066,7 @@ function parseMagickPixelOperation(tokens, settings, start, signal) {
8061
8066
  return { ...metadata, data: out, space: "b-w", channels: image.hasAlpha ? 2 : 1 };
8062
8067
  };
8063
8068
  } else if (token === "-gamma") {
8064
- const gamma = Math.max(0.1, Number(tokens[++i] ?? 1));
8069
+ const gamma = Math.max(0.1, parseFloat(tokens[++i] ?? "1"));
8065
8070
  const channels = state.channelExplicit ? state.channels : { r: true, g: true, b: true, a: true };
8066
8071
  const lookup = [channels.r, channels.g, channels.b, channels.a].map((active) => Uint8Array.from({ length: 256 }, (_, value) => active ? clampByteVal(255 * Math.pow(value / 255, 1 / gamma)) : value));
8067
8072
  apply = (image) => applyMagickLookup(image, lookup);
@@ -8069,7 +8074,19 @@ function parseMagickPixelOperation(tokens, settings, start, signal) {
8069
8074
  const mode = (tokens[++i] ?? "on").toLowerCase();
8070
8075
  if (mode === "remove") {
8071
8076
  apply = function* (im) {
8072
- return yield* removeAlphaImageSteps(yield* flattenImageSteps(im, state.background));
8077
+ const out = new Uint8Array(im.data.length);
8078
+ const background = [state.background.r, state.background.g, state.background.b];
8079
+ for (let idx = 0; idx < out.length; idx += 4) {
8080
+ if (idx % 65536 === 0) yield;
8081
+ const alpha = im.data[idx + 3] / 255;
8082
+ for (let channel = 0; channel < 3; channel++) {
8083
+ const bg = background[im.channels === 2 ? 0 : channel];
8084
+ out[idx + channel] = clampByteVal(im.data[idx + channel] * alpha + bg * (1 - alpha));
8085
+ }
8086
+ out[idx + 3] = 255;
8087
+ }
8088
+ const { data16: ignored, ...metadata } = im;
8089
+ return { ...metadata, data: out, hasAlpha: false, channels: im.channels === 4 ? 3 : im.channels === 2 ? 1 : im.channels };
8073
8090
  };
8074
8091
  } else if (mode === "associate" || mode === "disassociate") {
8075
8092
  apply = function* (im) {
@@ -8608,7 +8625,7 @@ function applyMagickReadSetting(tokens, state, start) {
8608
8625
  } else return void 0;
8609
8626
  return i;
8610
8627
  }
8611
- function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], stdinBytes, signal, analysis = { text: "", warnings: "" }) {
8628
+ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBytes, signal, analysis = { text: "", warnings: "" }) {
8612
8629
  let cooperativeWork = 63;
8613
8630
  let stack = [];
8614
8631
  let operandsOnly = false;
@@ -9066,14 +9083,11 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
9066
9083
  }
9067
9084
  stack = nextStack;
9068
9085
  } else if (t === "-combine") {
9069
- if (stack.length >= 3) {
9086
+ if (stack.length > 0) {
9070
9087
  const rIm = stack[0];
9071
- const gIm = stack[1] ?? rIm;
9072
- const bIm = stack[2] ?? rIm;
9073
- const aIm = stack[3];
9074
- const sources = [rIm, gIm, bIm, ...aIm ? [aIm] : []];
9088
+ const sources = stack.slice(0, 4);
9075
9089
  const result = yield* applyMagickRasterSteps(rIm, combinePixelSteps(rIm.width * rIm.height * 4, sources.map((image) => image.data.length)), signal, rIm, sources);
9076
- stack = [{ ...result, space: "srgb", channels: 4, hasAlpha: Boolean(aIm) }];
9090
+ stack = [{ ...result, space: "srgb", channels: 4, hasAlpha: sources.length === 4 }];
9077
9091
  }
9078
9092
  } else if (t === "-draw") {
9079
9093
  const drawSpec = tokens[++i] ?? "";
@@ -9104,9 +9118,10 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
9104
9118
  return yield* applyMagickRasterSteps(im, annotatePixelSteps(im, offset, text, state), signal);
9105
9119
  });
9106
9120
  } else if (t === "+clone" || t === "-clone") {
9107
- const sourcePool = stack.length > 0 ? stack : parentStack;
9121
+ const sourcePool = parentStack ?? stack;
9122
+ if (sourcePool.length === 0) throw new Error(`UnableToCloneImage ${t}`);
9108
9123
  const spec = t === "+clone" ? "-1" : tokens[++i] ?? "-1";
9109
- for (const idx of parseMagickIndexSpec(spec, sourcePool.length)) {
9124
+ for (const idx of parseMagickIndexSpec(spec, sourcePool.length, "clone")) {
9110
9125
  if (++cooperativeWork % 65536 === 0)
9111
9126
  yield;
9112
9127
  const chosen = sourcePool[idx];
@@ -9114,27 +9129,23 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
9114
9129
  stack.push({ ...chosen, data: new Uint8Array(chosen.data) });
9115
9130
  }
9116
9131
  } else if (t === "-duplicate" || t === "+duplicate") {
9132
+ if (stack.length === 0) throw new Error(`no images found for operation ${t}`);
9117
9133
  let count = 1;
9118
9134
  let idxSpec = "-1";
9119
9135
  if (t === "-duplicate") {
9120
- const nextTok = tokens[i + 1];
9121
- if (nextTok && /^\d+/.test(nextTok)) {
9122
- i++;
9123
- const commaIdx = nextTok.indexOf(",");
9124
- if (commaIdx >= 0) {
9125
- count = Math.max(0, parseInt(nextTok.slice(0, commaIdx), 10));
9126
- idxSpec = nextTok.slice(commaIdx + 1) || "-1";
9127
- } else {
9128
- count = Math.max(0, parseInt(nextTok, 10));
9129
- }
9130
- }
9131
- }
9132
- const sourcePool = stack.length > 0 ? stack : parentStack;
9136
+ const value = tokens[++i] ?? "", comma = value.indexOf(",");
9137
+ const countSpec = comma < 0 ? value : value.slice(0, comma);
9138
+ count = parseInt(countSpec, 10);
9139
+ if (!Number.isSafeInteger(count) || count < 0) throw new Error(`invalid argument ${value}`);
9140
+ idxSpec = comma < 0 ? "-1" : value.slice(comma + 1) || "-1";
9141
+ parseMagickIndices(idxSpec);
9142
+ }
9143
+ const sourcePool = stack;
9133
9144
  const sourceLength = sourcePool.length;
9134
9145
  for (let k = 0; k < count; k++) {
9135
9146
  if (++cooperativeWork % 65536 === 0)
9136
9147
  yield;
9137
- for (const idx of parseMagickIndexSpec(idxSpec, sourceLength)) {
9148
+ for (const idx of parseMagickIndexSpec(idxSpec, sourceLength, "clone")) {
9138
9149
  if (++cooperativeWork % 65536 === 0)
9139
9150
  yield;
9140
9151
  const chosen = sourcePool[idx];
@@ -9144,30 +9155,30 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
9144
9155
  }
9145
9156
  } else if (t === "+insert" || t === "-insert") {
9146
9157
  const rawIdx = t === "+insert" ? 0 : Number(tokens[++i] ?? 0);
9158
+ const target = rawIdx < 0 ? stack.length - 1 + rawIdx : rawIdx;
9159
+ if (!Number.isInteger(target) || target < 0 || target >= stack.length || rawIdx < 0 && target === 0) throw new Error(`invalid image index ${t}`);
9147
9160
  if (stack.length > 1) {
9148
9161
  const last = stack.pop();
9149
- const target = rawIdx < 0 ? Math.max(0, stack.length + 1 + rawIdx) : Math.min(stack.length, Math.max(0, rawIdx));
9150
9162
  stack.splice(target, 0, last);
9151
9163
  }
9152
9164
  } else if (t === "+swap" || t === "-swap") {
9153
9165
  let i1 = stack.length - 2;
9154
9166
  let i2 = stack.length - 1;
9155
9167
  if (t === "-swap") {
9156
- const [s1, s2] = (tokens[++i] ?? "-2,-1").split(",").map((n) => Number(n));
9168
+ const [s1, s2] = parseMagickIndices(tokens[++i] ?? "-2,-1", false).flat();
9157
9169
  i1 = (s1 ?? -2) < 0 ? stack.length + (s1 ?? -2) : s1 ?? 0;
9158
9170
  i2 = (s2 ?? -1) < 0 ? stack.length + (s2 ?? -1) : s2 ?? 0;
9159
9171
  }
9160
- if (stack[i1] && stack[i2]) {
9161
- const tmp = stack[i1];
9162
- stack[i1] = stack[i2];
9163
- stack[i2] = tmp;
9164
- }
9172
+ if (i1 === i2 || !stack[i1] || !stack[i2]) throw new Error(`invalid image index ${t}`);
9173
+ const tmp = stack[i1];
9174
+ stack[i1] = stack[i2];
9175
+ stack[i2] = tmp;
9165
9176
  } else if (t === "+delete" || t === "-delete") {
9166
9177
  if (t === "+delete") {
9167
9178
  const popped = stack.pop();
9168
9179
  if (popped) detachRgbaBuffer(popped.data);
9169
9180
  } else {
9170
- const toDelete = new Set(parseMagickIndexSpec(tokens[++i] ?? "-1", stack.length));
9181
+ const toDelete = new Set(parseMagickIndexSpec(tokens[++i] ?? "-1", stack.length, "delete"));
9171
9182
  stack = stack.filter((im, idx) => {
9172
9183
  if (toDelete.has(idx)) {
9173
9184
  detachRgbaBuffer(im.data);
@@ -9267,6 +9278,8 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
9267
9278
  if (composed !== overlay) detachRgbaBuffer(overlay.data);
9268
9279
  }
9269
9280
  stack = [composed, ...stack.slice(2)];
9281
+ } else {
9282
+ throw new Error("image sequence is required '-composite'");
9270
9283
  }
9271
9284
  } else {
9272
9285
  if (t.startsWith("-") && t !== "-" || t.startsWith("+")) throw new Error(`unrecognized option '${t}'`);
@@ -9322,7 +9335,7 @@ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = {
9322
9335
  const pipelineTokens = argv.slice(0, -1);
9323
9336
  const state = createDefaultState();
9324
9337
  try {
9325
- const stack = yield* evaluatePipelineTokensSteps(pipelineTokens, files, state, [], stdinBytes, signal, analysis);
9338
+ const stack = yield* evaluatePipelineTokensSteps(pipelineTokens, files, state, void 0, stdinBytes, signal, analysis);
9326
9339
  if (stack.length === 0) {
9327
9340
  return {
9328
9341
  exitCode: 1,
@@ -9513,17 +9526,17 @@ async function transformStoredMagickCoordinates(image, backend, token, geometry,
9513
9526
  ys = [{ source: h - dy, target: 0, length: dy }, { source: 0, target: dy, length: h - dy }];
9514
9527
  } else if (token === "-splice") {
9515
9528
  const sw = Math.max(0, Math.round(g.width ?? 0)), sh = Math.max(0, Math.round(g.height ?? 0));
9516
- const base = resolveGravityOffset(w, h, state.gravity);
9517
- const east = state.gravity === "east" || state.gravity === "northeast" || state.gravity === "southeast";
9518
- const south = state.gravity === "south" || state.gravity === "southwest" || state.gravity === "southeast";
9519
- const x = Math.max(0, Math.min(w, Math.round(base.left + (east ? -g.x : g.x))));
9520
- const y = Math.max(0, Math.min(h, Math.round(base.top + (south ? -g.y : g.y))));
9529
+ const box = gravityAdjustBox(w, h, sw, sh, g.x, g.y, state.gravity);
9530
+ const anchor = resolveGravityOffset(sw, sh, state.gravity);
9531
+ const x = Math.max(0, Math.min(w, box.x + anchor.left));
9532
+ const y = Math.max(0, Math.min(h, box.y + anchor.top));
9521
9533
  width += sw;
9522
9534
  height += sh;
9523
9535
  xs = [{ source: 0, target: 0, length: x }, { source: x, target: x + sw, length: w - x }];
9524
9536
  ys = [{ source: 0, target: 0, length: y }, { source: y, target: y + sh, length: h - y }];
9525
9537
  } else {
9526
- const cw = Math.max(0, Math.round(g.width ?? 0)), ch = Math.max(0, Math.round(g.height ?? 0));
9538
+ const cw = Math.max(0, Math.round((g.width ?? 0) * (g.isPercent ? w / 100 : 1)));
9539
+ const ch = Math.max(0, Math.round((g.height ?? 0) * (g.isPercent ? h / 100 : 1)));
9527
9540
  const { x, y } = gravityAdjustBox(w, h, cw, ch, g.x, g.y, state.gravity);
9528
9541
  if (x + cw < 0 || y + ch < 0 || x > w || y > h) return image;
9529
9542
  const x0 = Math.max(0, Math.min(w, x)), x1 = Math.max(x0, Math.min(w, x + cw));
@@ -9674,34 +9687,42 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9674
9687
  if (!operandsOnly && ["+clone", "-clone", "+duplicate", "-duplicate", "+delete", "-delete", "+swap", "-swap", "-reverse", "+insert", "-insert"].includes(token)) {
9675
9688
  let spec = "-1", count = 1;
9676
9689
  if (["-clone", "-delete", "-swap", "-insert"].includes(token)) spec = tokens[++i] ?? (token === "-swap" ? "-2,-1" : token === "-insert" ? "0" : "-1");
9677
- if (token === "-duplicate" && tokens[i + 1]?.[0] && tokens[i + 1][0] >= "0" && tokens[i + 1][0] <= "9") {
9678
- const value = tokens[++i], comma = value.indexOf(",");
9679
- count = Math.max(0, parseInt(value, 10));
9690
+ if (token === "-duplicate") {
9691
+ const value = tokens[++i] ?? "", comma = value.indexOf(",");
9692
+ count = parseInt(value, 10);
9693
+ if (!Number.isSafeInteger(count) || count < 0) throw new Error(`invalid argument ${value}`);
9680
9694
  spec = comma < 0 ? "-1" : value.slice(comma + 1) || "-1";
9695
+ parseMagickIndices(spec);
9681
9696
  }
9682
9697
  stackStep(async (stack, _backend, parents) => {
9698
+ if (token.endsWith("duplicate") && stack.length === 0) throw new Error(`no images found for operation ${token}`);
9683
9699
  if (token.endsWith("clone") || token.endsWith("duplicate")) {
9684
- const source = stack.length ? stack : parents.at(-1) ?? stack, length = source.length;
9685
- for (let repeat = 0; repeat < count; repeat++) for (const index of parseMagickIndexSpec(spec, length)) {
9700
+ const source = token.endsWith("clone") ? parents.at(-1) ?? stack : stack;
9701
+ const length = source.length;
9702
+ if (token.endsWith("clone") && length === 0) throw new Error(`UnableToCloneImage ${token}`);
9703
+ for (let repeat = 0; repeat < count; repeat++) for (const index of parseMagickIndexSpec(spec, length, "clone")) {
9686
9704
  await yieldTurn11(signal);
9687
9705
  await stack.push(await source.get(index));
9688
9706
  }
9689
- } else if (token.endsWith("delete")) await stack.remove(parseMagickIndexSpec(spec, stack.length));
9707
+ } else if (token.endsWith("delete")) await stack.remove(parseMagickIndexSpec(spec, stack.length, "delete"));
9690
9708
  else if (token === "-reverse") {
9691
9709
  for (let n = 0; n < Math.floor(stack.length / 2); n++) {
9692
9710
  await yieldTurn11(signal);
9693
9711
  await stack.swap(n, stack.length - 1 - n);
9694
9712
  }
9695
9713
  } else if (token.endsWith("insert")) {
9696
- const raw = token === "+insert" ? 0 : Number(spec), target = raw < 0 ? Math.max(0, stack.length + raw) : Math.min(stack.length - 1, Math.max(0, raw));
9714
+ const raw = token === "+insert" ? 0 : Number(spec), target = raw < 0 ? stack.length - 1 + raw : raw;
9715
+ if (!Number.isInteger(target) || target < 0 || target >= stack.length || raw < 0 && target === 0) throw new Error(`invalid image index ${token}`);
9697
9716
  for (let n = stack.length - 1; n > target; n--) {
9698
9717
  await yieldTurn11(signal);
9699
9718
  await stack.swap(n, n - 1);
9700
9719
  }
9701
9720
  } else {
9702
- const values = (token === "+swap" ? "-2,-1" : spec).split(",").map(Number);
9721
+ const values = parseMagickIndices(token === "+swap" ? "-2,-1" : spec, false).flat();
9703
9722
  const a = values[0] ?? -2, b = values[1] ?? -1;
9704
- await stack.swap(a < 0 ? stack.length + a : a, b < 0 ? stack.length + b : b);
9723
+ const i1 = a < 0 ? stack.length + a : a, i2 = b < 0 ? stack.length + b : b;
9724
+ if (i1 === i2 || i1 < 0 || i2 < 0 || i1 >= stack.length || i2 >= stack.length) throw new Error(`invalid image index ${token}`);
9725
+ await stack.swap(i1, i2);
9705
9726
  }
9706
9727
  return stack;
9707
9728
  });
@@ -9731,7 +9752,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9731
9752
  }
9732
9753
  return result;
9733
9754
  } else if (token === "-combine") {
9734
- if (stack.length < 3) return stack;
9755
+ if (stack.length === 0) return stack;
9735
9756
  const sources = [];
9736
9757
  for (let index = 0; index < Math.min(4, stack.length); index++) sources.push(await stack.get(index));
9737
9758
  const base = sources[0], result = await transformStoredMagickRaster(base, backend, combinePixelSteps(base.width * base.height * 4, sources.map((image) => image.width * image.height * 4)), signal, base, sources);
@@ -9888,7 +9909,8 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9888
9909
  if (token === "-layers") i++;
9889
9910
  const settings = { ...state };
9890
9911
  stackStep(async (stack, backend) => {
9891
- if (!stack.length || token === "-composite" && stack.length < 2) return stack;
9912
+ if (token === "-composite" && stack.length < 2) throw new Error("image sequence is required '-composite'");
9913
+ if (!stack.length) return stack;
9892
9914
  let result;
9893
9915
  if (token === "-composite") {
9894
9916
  const base = await stack.get(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-bash",
3
- "version": "0.1.842",
3
+ "version": "0.1.844",
4
4
  "description": "Extensible virtual shell with pluggable filesystems and streaming commands",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1415,7 +1415,7 @@
1415
1415
  "access": "public"
1416
1416
  },
1417
1417
  "dependencies": {
1418
- "@poe-platform/safe-fs": "0.1.842",
1418
+ "@poe-platform/safe-fs": "0.1.844",
1419
1419
  "pdf-lib": "1.17.1",
1420
1420
  "@types/node": "^25.2.2",
1421
1421
  "@formatjs/intl-numberformat": "9.4.0",