@poe-platform/safe-bash 0.1.843 → 0.1.845

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-4KM2VJRB.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-CMGVQM4Y.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-4KM2VJRB.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-CMGVQM4Y.js";
17
17
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
18
18
  export {
19
19
  createFfprobeCommand,
@@ -36,6 +36,7 @@ export interface MagickGeometry {
36
36
  readonly percentY?: number;
37
37
  readonly areaLimit?: number;
38
38
  readonly isSubdivide?: boolean;
39
+ readonly aspectRatio?: number;
39
40
  }
40
41
  export declare function parseMagickGeometry(raw: string): MagickGeometry;
41
42
  export declare function runIdentifyCli(argv: readonly string[], files: Map<string, Uint8Array> | IdentifyFileInput, stdinBytes?: Uint8Array, signal?: AbortSignal): Promise<ImageMagickCliResult>;
@@ -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
  }
@@ -3947,6 +3947,13 @@ function parseMagickGeometry(raw) {
3947
3947
  break;
3948
3948
  }
3949
3949
  }
3950
+ if (s.includes(":")) {
3951
+ const parts = s.split(":");
3952
+ const ratio = Number(parts[0]) / Number(parts[1]);
3953
+ if (parts.length === 2 && Number(parts[0]) > 0 && Number(parts[1]) > 0 && Number.isFinite(ratio)) {
3954
+ return { aspectRatio: ratio, x, y, hasOffset, forceExact, shrinkOnly, enlargeOnly, fillArea, isPercent: false };
3955
+ }
3956
+ }
3950
3957
  if (isArea) {
3951
3958
  if (s.includes("x") || s.includes("X")) {
3952
3959
  const [wStr, hStr] = s.split(/[xX]/);
@@ -4803,14 +4810,16 @@ function* createCheckerboardImageSteps(width, height) {
4803
4810
  function* applyMagickSpliceSteps(img, geomStr, bg, gravity = "northwest", signal) {
4804
4811
  let cooperativeWork = 0;
4805
4812
  const g = parseMagickGeometry(geomStr);
4806
- const sw = Math.max(0, Math.round(g.width ?? 0));
4807
- const sh = Math.max(0, Math.round(g.height ?? 0));
4813
+ const sw = Math.max(0, Math.round((g.width ?? 0) * (g.isPercent ? img.width / 100 : 1)));
4814
+ const sh = Math.max(0, Math.round((g.height ?? 0) * (g.isPercent ? img.height / 100 : 1)));
4808
4815
  const box = gravityAdjustBox(img.width, img.height, sw, sh, g.x, g.y, gravity);
4809
4816
  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));
4812
4817
  const outW = img.width + sw;
4813
4818
  const outH = img.height + sh;
4819
+ const xOffset = box.x + anchor.left;
4820
+ const sx = xOffset < 0 || xOffset > img.width ? outW : xOffset;
4821
+ if (box.y + anchor.top > outH) throw new Error("pixels are not authentic");
4822
+ const sy = Math.max(0, Math.min(img.height, box.y + anchor.top));
4814
4823
  const out = new Uint8Array(new ArrayBuffer(outW * outH * 4 + outH), 0, outW * outH * 4);
4815
4824
  for (let y = 0; y < outH; y++) {
4816
4825
  if (++cooperativeWork % 65536 === 0)
@@ -4825,7 +4834,7 @@ function* applyMagickSpliceSteps(img, geomStr, bg, gravity = "northwest", signal
4825
4834
  out[dstIdx + 2] = bg.b;
4826
4835
  out[dstIdx + 3] = bg.a;
4827
4836
  } else {
4828
- const origX = x < sx ? x : x - sw;
4837
+ const origX = Math.min(img.width - 1, x < sx ? x : x - sw);
4829
4838
  const origY = y < sy ? y : y - sh;
4830
4839
  const srcIdx = (origY * img.width + origX) * 4;
4831
4840
  out[dstIdx] = img.data[srcIdx];
@@ -4840,8 +4849,8 @@ function* applyMagickSpliceSteps(img, geomStr, bg, gravity = "northwest", signal
4840
4849
  function* applyMagickChopSteps(img, geomStr, gravity = "northwest", signal) {
4841
4850
  let cooperativeWork = 0;
4842
4851
  const g = parseMagickGeometry(geomStr);
4843
- const cw = Math.max(0, Math.round(g.width ?? 0));
4844
- const ch = Math.max(0, Math.round(g.height ?? 0));
4852
+ const cw = Math.max(0, Math.round((g.width ?? 0) * (g.isPercent ? img.width / 100 : 1)));
4853
+ const ch = Math.max(0, Math.round((g.height ?? 0) * (g.isPercent ? img.height / 100 : 1)));
4845
4854
  const { x: x0, y: y0 } = gravityAdjustBox(img.width, img.height, cw, ch, g.x, g.y, gravity);
4846
4855
  if (x0 + cw < 0 || y0 + ch < 0 || x0 > img.width || y0 > img.height) {
4847
4856
  return img;
@@ -4879,6 +4888,7 @@ function* applyMagickChopSteps(img, geomStr, gravity = "northwest", signal) {
4879
4888
  }
4880
4889
  function parseMagickRollGeometry(raw) {
4881
4890
  const geometry = parseMagickGeometry(raw);
4891
+ if (geometry.isPercent) return { ...geometry, x: 0, y: 0 };
4882
4892
  if (geometry.hasOffset && geometry.width !== void 0 && !raw.toLowerCase().includes("x")) {
4883
4893
  throw new Error(`invalid argument for option '-roll': ${raw}`);
4884
4894
  }
@@ -6897,13 +6907,12 @@ function magickCropArea(img, geomStr, gravity) {
6897
6907
  }
6898
6908
  function magickExtentLayout(img, geomStr, state) {
6899
6909
  const g = parseMagickGeometry(geomStr);
6900
- const targetW = Math.max(1, Math.round(g.width ?? img.width));
6901
- const targetH = Math.max(1, Math.round(g.height ?? img.height));
6902
- const offset = resolveGravityOffset(targetW - img.width, targetH - img.height, state.gravity);
6903
- const isEast = state.gravity === "east" || state.gravity === "northeast" || state.gravity === "southeast";
6904
- const isSouth = state.gravity === "south" || state.gravity === "southwest" || state.gravity === "southeast";
6905
- const left = Math.round(offset.left - (isEast ? -g.x : g.x));
6906
- const top = Math.round(offset.top - (isSouth ? -g.y : g.y));
6910
+ const targetW = Math.max(1, Math.round(g.aspectRatio !== void 0 ? Math.min(img.width, img.height * g.aspectRatio) : g.isPercent ? img.width * (g.percentX ?? 100) / 100 : g.width ?? img.width));
6911
+ const targetH = Math.max(1, Math.round(g.aspectRatio !== void 0 ? Math.min(img.height, img.width / g.aspectRatio) : g.isPercent ? img.height * (g.percentY ?? 100) / 100 : g.height ?? img.height));
6912
+ if (g.enlargeOnly && img.width > targetW && img.height > targetH || g.shrinkOnly && img.width < targetW && img.height < targetH) return void 0;
6913
+ const offset = gravityAdjustBox(img.width, img.height, targetW, targetH, g.x, g.y, state.gravity);
6914
+ const left = -offset.x;
6915
+ const top = -offset.y;
6907
6916
  const dstX0 = Math.max(0, left);
6908
6917
  const dstY0 = Math.max(0, top);
6909
6918
  const dstX1 = Math.min(targetW, left + img.width);
@@ -6913,6 +6922,7 @@ function magickExtentLayout(img, geomStr, state) {
6913
6922
  function* applyMagickExtentSteps(img, geomStr, state, signal) {
6914
6923
  yield;
6915
6924
  const layout = magickExtentLayout(img, geomStr, state);
6925
+ if (!layout) return img;
6916
6926
  const virtualCanvas = magickResizeCanvas(img, layout.width, layout.height);
6917
6927
  const source = img[magickInput];
6918
6928
  const canvas = {
@@ -7797,32 +7807,37 @@ function runIdentifyCliSync(argv, files, stdinBytes, signal) {
7797
7807
  }
7798
7808
  return next.value;
7799
7809
  }
7800
- function* parseMagickIndexSpec(spec, length) {
7810
+ function parseMagickIndices(spec, ranges = true) {
7811
+ if (!spec.trim() || spec.trimStart().startsWith(",")) throw new Error(`invalid argument ${spec}`);
7812
+ return spec.split(",").filter((part) => part.trim()).map((part) => {
7813
+ const value = part.trim();
7814
+ const separator = ranges ? value.indexOf("-", 1) : -1;
7815
+ const values = separator > 0 ? [value.slice(0, separator), value.slice(separator + 1)] : [value];
7816
+ return values.map((value2) => {
7817
+ const digits = value2.startsWith("-") ? value2.slice(1) : value2;
7818
+ if (!digits.length || !Array.from(digits).every((c) => c >= "0" && c <= "9") || !Number.isSafeInteger(Number(value2))) {
7819
+ throw new Error(`invalid argument ${spec}`);
7820
+ }
7821
+ return Number(value2);
7822
+ });
7823
+ });
7824
+ }
7825
+ function* parseMagickIndexSpec(spec, length, operation) {
7826
+ const parts = parseMagickIndices(spec);
7801
7827
  if (length <= 0) return;
7802
7828
  const resolve = (n) => n < 0 ? Math.max(0, length + n) : Math.min(Math.max(0, n), length - 1);
7803
- for (const part of spec.split(",")) {
7804
- const trimmed = part.trim();
7805
- if (!trimmed) continue;
7806
- const separator = trimmed.indexOf("-", 1);
7807
- const integer = (value) => {
7808
- const digits = value.startsWith("-") ? value.slice(1) : value;
7809
- return digits.length > 0 && Array.from(digits).every((c) => c >= "0" && c <= "9");
7810
- };
7811
- 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;
7812
- if (rangeMatch) {
7813
- const s = resolve(parseInt(rangeMatch[1], 10));
7814
- const e = resolve(parseInt(rangeMatch[2], 10));
7815
- if (s <= e) {
7816
- for (let k = s; k <= e; k++) yield k;
7817
- } else {
7818
- for (let k = s; k >= e; k--) yield k;
7829
+ for (const [num, end] of parts) {
7830
+ if (end !== void 0) {
7831
+ const s = resolve(num), e = resolve(end), step = s <= e ? 1 : -1;
7832
+ if (operation === "delete" && s > e) continue;
7833
+ for (let k = s; ; k += step) {
7834
+ yield k;
7835
+ if (k === e) break;
7819
7836
  }
7820
7837
  } else {
7821
- const num = parseInt(trimmed, 10);
7822
- if (!Number.isNaN(num)) {
7823
- const idx = num < 0 ? length + num : num;
7824
- if (idx >= 0 && idx < length) yield idx;
7825
- }
7838
+ const idx = num < 0 ? length + num : num;
7839
+ if (idx >= 0 && idx < length) yield idx;
7840
+ else if (operation === "clone") throw new Error(`invalid image index ${num}`);
7826
7841
  }
7827
7842
  }
7828
7843
  }
@@ -8619,7 +8634,7 @@ function applyMagickReadSetting(tokens, state, start) {
8619
8634
  } else return void 0;
8620
8635
  return i;
8621
8636
  }
8622
- function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], stdinBytes, signal, analysis = { text: "", warnings: "" }) {
8637
+ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBytes, signal, analysis = { text: "", warnings: "" }) {
8623
8638
  let cooperativeWork = 63;
8624
8639
  let stack = [];
8625
8640
  let operandsOnly = false;
@@ -8944,18 +8959,22 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
8944
8959
  return { ...canvas, virtualCanvas: magickBorderCanvas(im, bw, bh) };
8945
8960
  });
8946
8961
  } else if (t === "-shave") {
8947
- const g = parseMagickGeometry(tokens[++i] ?? "0x0");
8962
+ const geometry = tokens[++i] ?? "0x0";
8963
+ const g = parseMagickGeometry(geometry);
8948
8964
  const sw = Math.max(0, Math.round(g.width ?? 0));
8949
8965
  const sh = Math.max(0, Math.round(g.height ?? sw));
8950
8966
  stack = yield* mapSteps(stack, function* (im) {
8951
- const clampedSw = Math.min(Math.floor((im.width - 1) / 2), sw);
8952
- const clampedSh = Math.min(Math.floor((im.height - 1) / 2), sh);
8967
+ if (sw * 2 >= im.width || sh * 2 >= im.height) {
8968
+ analysis.warnings += `magick: geometry does not contain image '${geometry}'
8969
+ `;
8970
+ return im;
8971
+ }
8953
8972
  return { ...yield* extractImageSteps2(im, {
8954
- left: clampedSw,
8955
- top: clampedSh,
8956
- width: Math.max(1, im.width - clampedSw * 2),
8957
- height: Math.max(1, im.height - clampedSh * 2)
8958
- }), virtualCanvas: magickBorderCanvas(im, -clampedSw, -clampedSh) };
8973
+ left: sw,
8974
+ top: sh,
8975
+ width: im.width - sw * 2,
8976
+ height: im.height - sh * 2
8977
+ }), virtualCanvas: magickBorderCanvas(im, -sw, -sh) };
8959
8978
  });
8960
8979
  } else if (t === "-trim") {
8961
8980
  stack = yield* mapSteps(stack, function* (im) {
@@ -9077,14 +9096,11 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
9077
9096
  }
9078
9097
  stack = nextStack;
9079
9098
  } else if (t === "-combine") {
9080
- if (stack.length >= 3) {
9099
+ if (stack.length > 0) {
9081
9100
  const rIm = stack[0];
9082
- const gIm = stack[1] ?? rIm;
9083
- const bIm = stack[2] ?? rIm;
9084
- const aIm = stack[3];
9085
- const sources = [rIm, gIm, bIm, ...aIm ? [aIm] : []];
9101
+ const sources = stack.slice(0, 4);
9086
9102
  const result = yield* applyMagickRasterSteps(rIm, combinePixelSteps(rIm.width * rIm.height * 4, sources.map((image) => image.data.length)), signal, rIm, sources);
9087
- stack = [{ ...result, space: "srgb", channels: 4, hasAlpha: Boolean(aIm) }];
9103
+ stack = [{ ...result, space: "srgb", channels: 4, hasAlpha: sources.length === 4 }];
9088
9104
  }
9089
9105
  } else if (t === "-draw") {
9090
9106
  const drawSpec = tokens[++i] ?? "";
@@ -9115,9 +9131,10 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
9115
9131
  return yield* applyMagickRasterSteps(im, annotatePixelSteps(im, offset, text, state), signal);
9116
9132
  });
9117
9133
  } else if (t === "+clone" || t === "-clone") {
9118
- const sourcePool = stack.length > 0 ? stack : parentStack;
9134
+ const sourcePool = parentStack ?? stack;
9135
+ if (sourcePool.length === 0) throw new Error(`UnableToCloneImage ${t}`);
9119
9136
  const spec = t === "+clone" ? "-1" : tokens[++i] ?? "-1";
9120
- for (const idx of parseMagickIndexSpec(spec, sourcePool.length)) {
9137
+ for (const idx of parseMagickIndexSpec(spec, sourcePool.length, "clone")) {
9121
9138
  if (++cooperativeWork % 65536 === 0)
9122
9139
  yield;
9123
9140
  const chosen = sourcePool[idx];
@@ -9125,27 +9142,23 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
9125
9142
  stack.push({ ...chosen, data: new Uint8Array(chosen.data) });
9126
9143
  }
9127
9144
  } else if (t === "-duplicate" || t === "+duplicate") {
9145
+ if (stack.length === 0) throw new Error(`no images found for operation ${t}`);
9128
9146
  let count = 1;
9129
9147
  let idxSpec = "-1";
9130
9148
  if (t === "-duplicate") {
9131
- const nextTok = tokens[i + 1];
9132
- if (nextTok && /^\d+/.test(nextTok)) {
9133
- i++;
9134
- const commaIdx = nextTok.indexOf(",");
9135
- if (commaIdx >= 0) {
9136
- count = Math.max(0, parseInt(nextTok.slice(0, commaIdx), 10));
9137
- idxSpec = nextTok.slice(commaIdx + 1) || "-1";
9138
- } else {
9139
- count = Math.max(0, parseInt(nextTok, 10));
9140
- }
9141
- }
9142
- }
9143
- const sourcePool = stack.length > 0 ? stack : parentStack;
9149
+ const value = tokens[++i] ?? "", comma = value.indexOf(",");
9150
+ const countSpec = comma < 0 ? value : value.slice(0, comma);
9151
+ count = parseInt(countSpec, 10);
9152
+ if (!Number.isSafeInteger(count) || count < 0) throw new Error(`invalid argument ${value}`);
9153
+ idxSpec = comma < 0 ? "-1" : value.slice(comma + 1) || "-1";
9154
+ parseMagickIndices(idxSpec);
9155
+ }
9156
+ const sourcePool = stack;
9144
9157
  const sourceLength = sourcePool.length;
9145
9158
  for (let k = 0; k < count; k++) {
9146
9159
  if (++cooperativeWork % 65536 === 0)
9147
9160
  yield;
9148
- for (const idx of parseMagickIndexSpec(idxSpec, sourceLength)) {
9161
+ for (const idx of parseMagickIndexSpec(idxSpec, sourceLength, "clone")) {
9149
9162
  if (++cooperativeWork % 65536 === 0)
9150
9163
  yield;
9151
9164
  const chosen = sourcePool[idx];
@@ -9155,30 +9168,30 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
9155
9168
  }
9156
9169
  } else if (t === "+insert" || t === "-insert") {
9157
9170
  const rawIdx = t === "+insert" ? 0 : Number(tokens[++i] ?? 0);
9171
+ const target = rawIdx < 0 ? stack.length - 1 + rawIdx : rawIdx;
9172
+ if (!Number.isInteger(target) || target < 0 || target >= stack.length || rawIdx < 0 && target === 0) throw new Error(`invalid image index ${t}`);
9158
9173
  if (stack.length > 1) {
9159
9174
  const last = stack.pop();
9160
- const target = rawIdx < 0 ? Math.max(0, stack.length + 1 + rawIdx) : Math.min(stack.length, Math.max(0, rawIdx));
9161
9175
  stack.splice(target, 0, last);
9162
9176
  }
9163
9177
  } else if (t === "+swap" || t === "-swap") {
9164
9178
  let i1 = stack.length - 2;
9165
9179
  let i2 = stack.length - 1;
9166
9180
  if (t === "-swap") {
9167
- const [s1, s2] = (tokens[++i] ?? "-2,-1").split(",").map((n) => Number(n));
9181
+ const [s1, s2] = parseMagickIndices(tokens[++i] ?? "-2,-1", false).flat();
9168
9182
  i1 = (s1 ?? -2) < 0 ? stack.length + (s1 ?? -2) : s1 ?? 0;
9169
9183
  i2 = (s2 ?? -1) < 0 ? stack.length + (s2 ?? -1) : s2 ?? 0;
9170
9184
  }
9171
- if (stack[i1] && stack[i2]) {
9172
- const tmp = stack[i1];
9173
- stack[i1] = stack[i2];
9174
- stack[i2] = tmp;
9175
- }
9185
+ if (i1 === i2 || !stack[i1] || !stack[i2]) throw new Error(`invalid image index ${t}`);
9186
+ const tmp = stack[i1];
9187
+ stack[i1] = stack[i2];
9188
+ stack[i2] = tmp;
9176
9189
  } else if (t === "+delete" || t === "-delete") {
9177
9190
  if (t === "+delete") {
9178
9191
  const popped = stack.pop();
9179
9192
  if (popped) detachRgbaBuffer(popped.data);
9180
9193
  } else {
9181
- const toDelete = new Set(parseMagickIndexSpec(tokens[++i] ?? "-1", stack.length));
9194
+ const toDelete = new Set(parseMagickIndexSpec(tokens[++i] ?? "-1", stack.length, "delete"));
9182
9195
  stack = stack.filter((im, idx) => {
9183
9196
  if (toDelete.has(idx)) {
9184
9197
  detachRgbaBuffer(im.data);
@@ -9335,7 +9348,7 @@ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = {
9335
9348
  const pipelineTokens = argv.slice(0, -1);
9336
9349
  const state = createDefaultState();
9337
9350
  try {
9338
- const stack = yield* evaluatePipelineTokensSteps(pipelineTokens, files, state, [], stdinBytes, signal, analysis);
9351
+ const stack = yield* evaluatePipelineTokensSteps(pipelineTokens, files, state, void 0, stdinBytes, signal, analysis);
9339
9352
  if (stack.length === 0) {
9340
9353
  return {
9341
9354
  exitCode: 1,
@@ -9525,17 +9538,21 @@ async function transformStoredMagickCoordinates(image, backend, token, geometry,
9525
9538
  xs = [{ source: w - dx, target: 0, length: dx }, { source: 0, target: dx, length: w - dx }];
9526
9539
  ys = [{ source: h - dy, target: 0, length: dy }, { source: 0, target: dy, length: h - dy }];
9527
9540
  } else if (token === "-splice") {
9528
- const sw = Math.max(0, Math.round(g.width ?? 0)), sh = Math.max(0, Math.round(g.height ?? 0));
9541
+ const sw = Math.max(0, Math.round((g.width ?? 0) * (g.isPercent ? w / 100 : 1)));
9542
+ const sh = Math.max(0, Math.round((g.height ?? 0) * (g.isPercent ? h / 100 : 1)));
9529
9543
  const box = gravityAdjustBox(w, h, sw, sh, g.x, g.y, state.gravity);
9530
9544
  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));
9533
9545
  width += sw;
9534
9546
  height += sh;
9535
- xs = [{ source: 0, target: 0, length: x }, { source: x, target: x + sw, length: w - x }];
9547
+ const xOffset = box.x + anchor.left;
9548
+ const x = xOffset < 0 || xOffset > w ? width : xOffset;
9549
+ if (box.y + anchor.top > height) throw new Error("pixels are not authentic");
9550
+ const y = Math.max(0, Math.min(h, box.y + anchor.top));
9551
+ xs = [{ source: 0, target: 0, length: x }, { source: x, target: x + sw, length: Math.max(0, w - x) }];
9536
9552
  ys = [{ source: 0, target: 0, length: y }, { source: y, target: y + sh, length: h - y }];
9537
9553
  } else {
9538
- const cw = Math.max(0, Math.round(g.width ?? 0)), ch = Math.max(0, Math.round(g.height ?? 0));
9554
+ const cw = Math.max(0, Math.round((g.width ?? 0) * (g.isPercent ? w / 100 : 1)));
9555
+ const ch = Math.max(0, Math.round((g.height ?? 0) * (g.isPercent ? h / 100 : 1)));
9539
9556
  const { x, y } = gravityAdjustBox(w, h, cw, ch, g.x, g.y, state.gravity);
9540
9557
  if (x + cw < 0 || y + ch < 0 || x > w || y > h) return image;
9541
9558
  const x0 = Math.max(0, Math.min(w, x)), x1 = Math.max(x0, Math.min(w, x + cw));
@@ -9552,7 +9569,16 @@ async function transformStoredMagickCoordinates(image, backend, token, geometry,
9552
9569
  for (let offset = 0; offset < x.length; offset += 4096) {
9553
9570
  if (work++ % 64 === 0) await yieldTurn11(signal);
9554
9571
  const length = Math.min(4096, x.length - offset) * 4;
9555
- const bytes = await backend.storage.read(image.position + ((y.source + row) * w + x.source + offset) * 4, length);
9572
+ const sourceX = Math.min(w - 1, x.source + offset);
9573
+ const sourceLength = Math.min(length, (w - sourceX) * 4);
9574
+ let bytes = await backend.storage.read(image.position + ((y.source + row) * w + sourceX) * 4, sourceLength);
9575
+ if (sourceLength < length) {
9576
+ const extended = new Uint8Array(length);
9577
+ extended.set(bytes);
9578
+ const edge = bytes.subarray(sourceLength - 4);
9579
+ for (let i = sourceLength; i < length; i += 4) extended.set(edge, i);
9580
+ bytes = extended;
9581
+ }
9556
9582
  await backend.storage.write(position + ((y.target + row) * width + x.target + offset) * 4, bytes);
9557
9583
  }
9558
9584
  }
@@ -9686,34 +9712,42 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9686
9712
  if (!operandsOnly && ["+clone", "-clone", "+duplicate", "-duplicate", "+delete", "-delete", "+swap", "-swap", "-reverse", "+insert", "-insert"].includes(token)) {
9687
9713
  let spec = "-1", count = 1;
9688
9714
  if (["-clone", "-delete", "-swap", "-insert"].includes(token)) spec = tokens[++i] ?? (token === "-swap" ? "-2,-1" : token === "-insert" ? "0" : "-1");
9689
- if (token === "-duplicate" && tokens[i + 1]?.[0] && tokens[i + 1][0] >= "0" && tokens[i + 1][0] <= "9") {
9690
- const value = tokens[++i], comma = value.indexOf(",");
9691
- count = Math.max(0, parseInt(value, 10));
9715
+ if (token === "-duplicate") {
9716
+ const value = tokens[++i] ?? "", comma = value.indexOf(",");
9717
+ count = parseInt(value, 10);
9718
+ if (!Number.isSafeInteger(count) || count < 0) throw new Error(`invalid argument ${value}`);
9692
9719
  spec = comma < 0 ? "-1" : value.slice(comma + 1) || "-1";
9720
+ parseMagickIndices(spec);
9693
9721
  }
9694
9722
  stackStep(async (stack, _backend, parents) => {
9723
+ if (token.endsWith("duplicate") && stack.length === 0) throw new Error(`no images found for operation ${token}`);
9695
9724
  if (token.endsWith("clone") || token.endsWith("duplicate")) {
9696
- const source = stack.length ? stack : parents.at(-1) ?? stack, length = source.length;
9697
- for (let repeat = 0; repeat < count; repeat++) for (const index of parseMagickIndexSpec(spec, length)) {
9725
+ const source = token.endsWith("clone") ? parents.at(-1) ?? stack : stack;
9726
+ const length = source.length;
9727
+ if (token.endsWith("clone") && length === 0) throw new Error(`UnableToCloneImage ${token}`);
9728
+ for (let repeat = 0; repeat < count; repeat++) for (const index of parseMagickIndexSpec(spec, length, "clone")) {
9698
9729
  await yieldTurn11(signal);
9699
9730
  await stack.push(await source.get(index));
9700
9731
  }
9701
- } else if (token.endsWith("delete")) await stack.remove(parseMagickIndexSpec(spec, stack.length));
9732
+ } else if (token.endsWith("delete")) await stack.remove(parseMagickIndexSpec(spec, stack.length, "delete"));
9702
9733
  else if (token === "-reverse") {
9703
9734
  for (let n = 0; n < Math.floor(stack.length / 2); n++) {
9704
9735
  await yieldTurn11(signal);
9705
9736
  await stack.swap(n, stack.length - 1 - n);
9706
9737
  }
9707
9738
  } else if (token.endsWith("insert")) {
9708
- 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));
9739
+ const raw = token === "+insert" ? 0 : Number(spec), target = raw < 0 ? stack.length - 1 + raw : raw;
9740
+ if (!Number.isInteger(target) || target < 0 || target >= stack.length || raw < 0 && target === 0) throw new Error(`invalid image index ${token}`);
9709
9741
  for (let n = stack.length - 1; n > target; n--) {
9710
9742
  await yieldTurn11(signal);
9711
9743
  await stack.swap(n, n - 1);
9712
9744
  }
9713
9745
  } else {
9714
- const values = (token === "+swap" ? "-2,-1" : spec).split(",").map(Number);
9746
+ const values = parseMagickIndices(token === "+swap" ? "-2,-1" : spec, false).flat();
9715
9747
  const a = values[0] ?? -2, b = values[1] ?? -1;
9716
- await stack.swap(a < 0 ? stack.length + a : a, b < 0 ? stack.length + b : b);
9748
+ const i1 = a < 0 ? stack.length + a : a, i2 = b < 0 ? stack.length + b : b;
9749
+ if (i1 === i2 || i1 < 0 || i2 < 0 || i1 >= stack.length || i2 >= stack.length) throw new Error(`invalid image index ${token}`);
9750
+ await stack.swap(i1, i2);
9717
9751
  }
9718
9752
  return stack;
9719
9753
  });
@@ -9743,7 +9777,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9743
9777
  }
9744
9778
  return result;
9745
9779
  } else if (token === "-combine") {
9746
- if (stack.length < 3) return stack;
9780
+ if (stack.length === 0) return stack;
9747
9781
  const sources = [];
9748
9782
  for (let index = 0; index < Math.min(4, stack.length); index++) sources.push(await stack.get(index));
9749
9783
  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);
@@ -10281,13 +10315,18 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10281
10315
  return frames;
10282
10316
  });
10283
10317
  } else if (!operandsOnly && token === "-shave") {
10284
- const geometry = parseMagickGeometry(tokens[++i] ?? "0x0"), width = Math.max(0, Math.round(geometry.width ?? 0)), height = Math.max(0, Math.round(geometry.height ?? width));
10318
+ const geometry = tokens[++i] ?? "0x0", parsed = parseMagickGeometry(geometry);
10319
+ const width = Math.max(0, Math.round(parsed.width ?? 0)), height = Math.max(0, Math.round(parsed.height ?? width));
10285
10320
  steps.push(async (image, backend) => {
10286
10321
  if (!image) return;
10287
- const left = Math.min(Math.floor((image.width - 1) / 2), width), top = Math.min(Math.floor((image.height - 1) / 2), height);
10322
+ if (width * 2 >= image.width || height * 2 >= image.height) {
10323
+ analysis.warnings += `magick: geometry does not contain image '${geometry}'
10324
+ `;
10325
+ return image;
10326
+ }
10288
10327
  return {
10289
- ...await transformStoredImage2(image, backend.storage, { kind: "extract", left, top, width: Math.max(1, image.width - left * 2), height: Math.max(1, image.height - top * 2) }, signal),
10290
- virtualCanvas: magickBorderCanvas(image, -left, -top)
10328
+ ...await transformStoredImage2(image, backend.storage, { kind: "extract", left: width, top: height, width: image.width - width * 2, height: image.height - height * 2 }, signal),
10329
+ virtualCanvas: magickBorderCanvas(image, -width, -height)
10291
10330
  };
10292
10331
  });
10293
10332
  } else if (!operandsOnly && token === "-trim") {
@@ -10301,6 +10340,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10301
10340
  steps.push(async (image, backend) => {
10302
10341
  if (!image) return;
10303
10342
  const layout = magickExtentLayout(image, geometry, captured);
10343
+ if (!layout) return image;
10304
10344
  const virtualCanvas = magickResizeCanvas(image, layout.width, layout.height);
10305
10345
  const source = image[magickInput];
10306
10346
  const canvas = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-bash",
3
- "version": "0.1.843",
3
+ "version": "0.1.845",
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.843",
1418
+ "@poe-platform/safe-fs": "0.1.845",
1419
1419
  "pdf-lib": "1.17.1",
1420
1420
  "@types/node": "^25.2.2",
1421
1421
  "@formatjs/intl-numberformat": "9.4.0",