@takazudo/zudo-image-tweaker 0.1.2 → 0.1.3

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 CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Features
6
+
7
+ - b4push: queue heavy steps through the machine-wide heavy-guard (4b15f51)
8
+
9
+ ### Fixed
10
+
11
+ - product-photo: pass Buffer input to imgly as a MIME-typed Blob, so `removeBackground()` no longer fails with "Unsupported format" ([#126](https://github.com/zudolab/zudo-image-tweaker/pull/126), closes [#123](https://github.com/zudolab/zudo-image-tweaker/issues/123)) (140168c)
12
+ - deps: resolve root-workspace pnpm audit advisories ([#116](https://github.com/zudolab/zudo-image-tweaker/issues/116)) (993351d)
13
+ - deps: limit audit overrides to their current major/minor lines (87c3255)
14
+
15
+ ### Other Changes
16
+
17
+ - ci: bump actions/checkout v7.0.1, actions/setup-node v7.1.0, pnpm/action-setup v6.1.0 (8998428)
18
+ - docs(doc): re-scaffold the doc site onto create-zudo-doc 8.1.0 / zfb 5.1.2 (f92c5d6, 7fde2a5, ff0c72d)
19
+ - ci: gate the doc build, links, wind audit and dependency audit; doc jobs move to Node 24 (4651d4e)
20
+ - build(doc): clear the high doc audit advisories (wrangler 4.152.0, zod 4.6.7, undici override) (aa5f7cf)
21
+ - chore: follow-ups from the code review (8bdd50e)
22
+ - chore(ci): record the NPM_TOKEN rotation on 2026-10-26 (760b1d1)
23
+
3
24
  ## 0.1.2
4
25
 
5
26
  ### Fixed
@@ -1,110 +1,110 @@
1
1
  import P from "node:path";
2
2
  import { pathToFileURL as L } from "node:url";
3
- import m from "sharp";
4
- const Y = { r: 0, g: 0, b: 0, alpha: 0 }, D = { lightX: 0.3, lightY: 0.25, strength: 0.12, spread: 1.3 }, j = [
3
+ import i from "sharp";
4
+ const Y = { r: 0, g: 0, b: 0, alpha: 0 }, j = { lightX: 0.3, lightY: 0.25, strength: 0.12, spread: 1.3 }, D = [
5
5
  { blur: 100, opacity: 0.25, offsetX: 10, offsetY: 8, fadeStartRatio: -0.1 },
6
6
  { blur: 40, opacity: 0.35, offsetX: 8, offsetY: 6, fadeStartRatio: 0 }
7
- ], x = { blur: 8, opacity: 0.55, offsetX: 4, offsetY: 3 }, F = {
7
+ ], F = { blur: 8, opacity: 0.55, offsetX: 4, offsetY: 3 }, G = {
8
8
  squash: 0.25,
9
9
  offsetX: 65,
10
10
  offsetY: 35,
11
11
  blur: 75,
12
12
  opacity: 0.45
13
- }, G = [
13
+ }, x = [
14
14
  { blur: 120, opacity: 0.3, offsetX: 62, offsetY: 55, fadeStartRatio: -0.1 },
15
15
  { blur: 52, opacity: 0.45, offsetX: 58, offsetY: 50, fadeStartRatio: 0 }
16
16
  ], X = { blur: 12, opacity: 0.5, offsetX: 55, offsetY: 45 };
17
- async function q(o, t = {}) {
18
- const { mode: n = "grounded", vignette: e, contactShadow: f, bottomShadows: s, projectedShadow: u } = t, { width: r, height: a } = await m(o).metadata();
19
- if (!r || !a)
17
+ async function U(o, t = {}) {
18
+ const { mode: n = "grounded", vignette: e, contactShadow: f, bottomShadows: s, projectedShadow: u } = t, { width: a, height: r } = await i(o).metadata();
19
+ if (!a || !r)
20
20
  throw new Error("generateShadowLayers: could not read alphaImage dimensions.");
21
- const c = await m(o).extractChannel(3).toColourspace("b-w").raw().toBuffer(), l = U(c, r, a), w = { ...D, ...e }, d = W(r, a, w);
21
+ const c = await i(o).extractChannel(3).toColourspace("b-w").raw().toBuffer(), l = q(c, a, r), b = { ...j, ...e }, d = W(a, r, b);
22
22
  if (n === "floating") {
23
- const p = { ...F, ...u }, [g, B] = _(G, s), i = { ...X, ...f };
23
+ const m = { ...G, ...u }, [B, g] = _(x, s), p = { ...X, ...f };
24
24
  return Promise.all([
25
25
  d,
26
- H(c, r, a, l, p),
27
- h(c, r, a, l, g),
28
- h(c, r, a, l, B),
29
- M(c, r, a, i)
26
+ H(c, a, r, l, m),
27
+ A(c, a, r, l, B),
28
+ A(c, a, r, l, g),
29
+ N(c, a, r, p)
30
30
  ]);
31
31
  }
32
- const [y, b] = _(j, s), S = { ...x, ...f };
32
+ const [y, w] = _(D, s), S = { ...F, ...f };
33
33
  return Promise.all([
34
34
  d,
35
- h(c, r, a, l, y),
36
- h(c, r, a, l, b),
37
- M(c, r, a, S)
35
+ A(c, a, r, l, y),
36
+ A(c, a, r, l, w),
37
+ N(c, a, r, S)
38
38
  ]);
39
39
  }
40
40
  function _(o, t) {
41
41
  return o.map((n, e) => ({ ...n, ...t?.[e] }));
42
42
  }
43
- function A(o) {
43
+ function h(o) {
44
44
  return Number.isFinite(o) ? Math.min(1, Math.max(0, o)) : 0;
45
45
  }
46
- function U(o, t, n) {
46
+ function q(o, t, n) {
47
47
  let e = n, f = 0, s = t, u = 0;
48
- for (let r = 0; r < n; r++)
49
- for (let a = 0; a < t; a++)
50
- o[r * t + a] > 0 && (r < e && (e = r), r > f && (f = r), a < s && (s = a), a > u && (u = a));
48
+ for (let a = 0; a < n; a++)
49
+ for (let r = 0; r < t; r++)
50
+ o[a * t + r] > 0 && (a < e && (e = a), a > f && (f = a), r < s && (s = r), r > u && (u = r));
51
51
  return e > f ? null : { top: e, bottom: f + 1, left: s, right: u + 1 };
52
52
  }
53
- async function M(o, t, n, { blur: e, opacity: f, offsetX: s, offsetY: u }) {
54
- const r = k(o, t, n, s, u), a = await m(r, { raw: { width: t, height: n, channels: 1 } }).toColourspace("b-w").blur(Math.max(e, 0.3)).linear(A(f), 0).png().toBuffer();
55
- return C(a, t, n);
56
- }
57
- async function H(o, t, n, e, { squash: f, offsetX: s, offsetY: u, blur: r, opacity: a }) {
58
- if (!e) return I(t, n);
59
- const c = e.right - e.left, l = e.bottom - e.top, w = Buffer.alloc(c * l);
60
- for (let B = 0; B < l; B++)
61
- for (let i = 0; i < c; i++)
62
- w[B * c + i] = o[(e.top + B) * t + (e.left + i)];
63
- const d = Math.max(1, Math.round(l * f)), y = await m(w, { raw: { width: c, height: l, channels: 1 } }).toColourspace("b-w").resize(c, d, { fit: "fill" }).raw().toBuffer(), b = Buffer.alloc(t * n, 0), S = e.left + s, p = e.bottom + u;
64
- for (let B = 0; B < d; B++) {
65
- const i = p + B;
66
- if (!(i < 0 || i >= n))
53
+ async function N(o, t, n, { blur: e, opacity: f, offsetX: s, offsetY: u }) {
54
+ const a = I(o, t, n, s, u), r = await i(a, { raw: { width: t, height: n, channels: 1 } }).toColourspace("b-w").blur(Math.max(e, 0.3)).linear(h(f), 0).png().toBuffer();
55
+ return E(r, t, n);
56
+ }
57
+ async function H(o, t, n, e, { squash: f, offsetX: s, offsetY: u, blur: a, opacity: r }) {
58
+ if (!e) return k(t, n);
59
+ const c = e.right - e.left, l = e.bottom - e.top, b = Buffer.alloc(c * l);
60
+ for (let g = 0; g < l; g++)
61
+ for (let p = 0; p < c; p++)
62
+ b[g * c + p] = o[(e.top + g) * t + (e.left + p)];
63
+ const d = Math.max(1, Math.round(l * f)), y = await i(b, { raw: { width: c, height: l, channels: 1 } }).toColourspace("b-w").resize(c, d, { fit: "fill" }).raw().toBuffer(), w = Buffer.alloc(t * n, 0), S = e.left + s, m = e.bottom + u;
64
+ for (let g = 0; g < d; g++) {
65
+ const p = m + g;
66
+ if (!(p < 0 || p >= n))
67
67
  for (let T = 0; T < c; T++) {
68
- const E = S + T;
69
- E < 0 || E >= t || (b[i * t + E] = y[B * c + T]);
68
+ const C = S + T;
69
+ C < 0 || C >= t || (w[p * t + C] = y[g * c + T]);
70
70
  }
71
71
  }
72
- const g = await m(b, { raw: { width: t, height: n, channels: 1 } }).toColourspace("b-w").blur(Math.max(r, 0.3)).linear(A(a), 0).png().toBuffer();
73
- return C(g, t, n);
74
- }
75
- async function h(o, t, n, e, { blur: f, opacity: s, offsetX: u, offsetY: r, fadeStartRatio: a }) {
76
- if (!e) return I(t, n);
77
- const c = A(s), l = k(o, t, n, u, r), w = await m(l, { raw: { width: t, height: n, channels: 1 } }).toColourspace("b-w").blur(Math.max(f, 0.3)).raw().toBuffer(), d = e.top + (e.bottom - e.top) * (0.5 + a), y = (e.bottom - d) * 0.5, b = Buffer.alloc(t * n, 0);
78
- for (let p = 0; p < n; p++) {
79
- let g;
80
- p <= d ? g = 0 : y > 0 && p <= d + y ? g = (p - d) / y : g = 1;
81
- const B = g * c;
82
- for (let i = 0; i < t; i++)
83
- b[p * t + i] = Math.round(w[p * t + i] * B);
72
+ const B = await i(w, { raw: { width: t, height: n, channels: 1 } }).toColourspace("b-w").blur(Math.max(a, 0.3)).linear(h(r), 0).png().toBuffer();
73
+ return E(B, t, n);
74
+ }
75
+ async function A(o, t, n, e, { blur: f, opacity: s, offsetX: u, offsetY: a, fadeStartRatio: r }) {
76
+ if (!e) return k(t, n);
77
+ const c = h(s), l = I(o, t, n, u, a), b = await i(l, { raw: { width: t, height: n, channels: 1 } }).toColourspace("b-w").blur(Math.max(f, 0.3)).raw().toBuffer(), d = e.top + (e.bottom - e.top) * (0.5 + r), y = (e.bottom - d) * 0.5, w = Buffer.alloc(t * n, 0);
78
+ for (let m = 0; m < n; m++) {
79
+ let B;
80
+ m <= d ? B = 0 : y > 0 && m <= d + y ? B = (m - d) / y : B = 1;
81
+ const g = B * c;
82
+ for (let p = 0; p < t; p++)
83
+ w[m * t + p] = Math.round(b[m * t + p] * g);
84
84
  }
85
- const S = await m(b, { raw: { width: t, height: n, channels: 1 } }).toColourspace("b-w").png().toBuffer();
86
- return C(S, t, n);
85
+ const S = await i(w, { raw: { width: t, height: n, channels: 1 } }).toColourspace("b-w").png().toBuffer();
86
+ return E(S, t, n);
87
87
  }
88
88
  async function W(o, t, { lightX: n, lightY: e, strength: f, spread: s }) {
89
- const u = A(f), r = Buffer.alloc(o * t, 0), a = o * n, c = t * e, l = Math.min(o, t) / 2, w = s * s;
89
+ const u = h(f), a = Buffer.alloc(o * t, 0), r = o * n, c = t * e, l = Math.min(o, t) / 2, b = s * s;
90
90
  for (let y = 0; y < t; y++) {
91
- const b = (y - c) / l, S = b * b;
92
- for (let p = 0; p < o; p++) {
93
- const g = (p - a) / l, B = g * g + S, i = Math.min(1, (B / w) ** 0.75) * u * 255;
94
- r[y * o + p] = Math.round(i);
91
+ const w = (y - c) / l, S = w * w;
92
+ for (let m = 0; m < o; m++) {
93
+ const B = (m - r) / l, g = B * B + S, p = Math.min(1, (g / b) ** 0.75) * u * 255;
94
+ a[y * o + m] = Math.round(p);
95
95
  }
96
96
  }
97
- const d = await m(r, { raw: { width: o, height: t, channels: 1 } }).toColourspace("b-w").png().toBuffer();
98
- return C(d, o, t);
97
+ const d = await i(a, { raw: { width: o, height: t, channels: 1 } }).toColourspace("b-w").png().toBuffer();
98
+ return E(d, o, t);
99
99
  }
100
- function k(o, t, n, e, f) {
100
+ function I(o, t, n, e, f) {
101
101
  const s = Buffer.alloc(t * n, 0);
102
102
  for (let u = 0; u < n; u++) {
103
- const r = u + f;
104
- if (!(r < 0 || r >= n))
105
- for (let a = 0; a < t; a++) {
106
- const c = a + e;
107
- c < 0 || c >= t || (s[r * t + c] = o[u * t + a]);
103
+ const a = u + f;
104
+ if (!(a < 0 || a >= n))
105
+ for (let r = 0; r < t; r++) {
106
+ const c = r + e;
107
+ c < 0 || c >= t || (s[a * t + c] = o[u * t + r]);
108
108
  }
109
109
  }
110
110
  return s;
@@ -113,18 +113,18 @@ const R = /* @__PURE__ */ new Map();
113
113
  function $(o, t) {
114
114
  const n = `${o}x${t}`;
115
115
  let e = R.get(n);
116
- return e || (e = m({ create: { width: o, height: t, channels: 3, background: { r: 0, g: 0, b: 0 } } }).png().toBuffer(), R.set(n, e)), e;
116
+ return e || (e = i({ create: { width: o, height: t, channels: 3, background: { r: 0, g: 0, b: 0 } } }).png().toBuffer(), R.set(n, e)), e;
117
117
  }
118
- async function C(o, t, n) {
118
+ async function E(o, t, n) {
119
119
  const e = await $(t, n);
120
- return { buffer: await m(e).joinChannel(o).png().toBuffer(), blend: "multiply", offset: { x: 0, y: 0 } };
120
+ return { buffer: await i(e).joinChannel(o).png().toBuffer(), blend: "multiply", offset: { x: 0, y: 0 } };
121
121
  }
122
- async function I(o, t) {
123
- return { buffer: await m({ create: { width: o, height: t, channels: 4, background: Y } }).png().toBuffer(), blend: "multiply", offset: { x: 0, y: 0 } };
122
+ async function k(o, t) {
123
+ return { buffer: await i({ create: { width: o, height: t, channels: 4, background: Y } }).png().toBuffer(), blend: "multiply", offset: { x: 0, y: 0 } };
124
124
  }
125
- const v = 1600, V = 1440, Z = 90, N = { r: 0, g: 0, b: 0, alpha: 0 }, O = "@imgly/background-removal-node";
126
- async function ct(o) {
127
- const t = await z(), n = Q(o), f = await (await t.removeBackground(n)).arrayBuffer();
125
+ const v = 1600, V = 1440, Z = 90, M = { r: 0, g: 0, b: 0, alpha: 0 }, O = "@imgly/background-removal-node";
126
+ async function ft(o) {
127
+ const t = await z(), n = await tt(o), f = await (await t.removeBackground(n)).arrayBuffer();
128
128
  return Buffer.from(f);
129
129
  }
130
130
  async function z() {
@@ -145,45 +145,53 @@ function K(o) {
145
145
  }
146
146
  return !1;
147
147
  }
148
- function Q(o) {
149
- return Buffer.isBuffer(o) || o instanceof URL ? o : L(P.resolve(o)).href;
148
+ const Q = {
149
+ png: "image/png",
150
+ jpeg: "image/jpeg",
151
+ webp: "image/webp"
152
+ };
153
+ async function tt(o) {
154
+ if (o instanceof URL) return o;
155
+ if (!Buffer.isBuffer(o)) return L(P.resolve(o)).href;
156
+ const { format: t } = await i(o).metadata(), n = t ? Q[t] : void 0;
157
+ return n ? new Blob([new Uint8Array(o)], { type: n }) : new Blob([new Uint8Array(await i(o).png().toBuffer())], { type: "image/png" });
150
158
  }
151
- async function ft(o, t = {}) {
159
+ async function st(o, t = {}) {
152
160
  const { threshold: n = 10 } = t;
153
- return m(o).trim({ background: N, threshold: n }).png().toBuffer();
161
+ return i(o).trim({ background: M, threshold: n }).png().toBuffer();
154
162
  }
155
- async function st(o, t) {
163
+ async function ut(o, t) {
156
164
  const { background: n, size: e = v, quality: f = Z, shadow: s = !1 } = t, u = t.fit ?? Math.round(e * (V / v));
157
165
  if (n == null)
158
166
  throw new Error(
159
167
  "composeProductPhoto requires a `background`: an image path/Buffer, or a flat `{ color }` fill."
160
168
  );
161
- const r = Math.round((e - u) / 2), a = await m(o).resize(u, u, { fit: "contain", background: N }).png().toBuffer(), c = nt(n, e);
169
+ const a = Math.round((e - u) / 2), r = await i(o).resize(u, u, { fit: "contain", background: M }).png().toBuffer(), c = et(n, e);
162
170
  let l;
163
171
  if (s) {
164
- const y = await m({
165
- create: { width: e, height: e, channels: 4, background: N }
166
- }).composite([{ input: a, left: r, top: r }]).png().toBuffer();
167
- l = [...(await q(y, s === !0 ? {} : s)).map(tt), { input: y, left: 0, top: 0 }];
172
+ const y = await i({
173
+ create: { width: e, height: e, channels: 4, background: M }
174
+ }).composite([{ input: r, left: a, top: a }]).png().toBuffer();
175
+ l = [...(await U(y, s === !0 ? {} : s)).map(ot), { input: y, left: 0, top: 0 }];
168
176
  } else
169
- l = [{ input: a, left: r, top: r }];
170
- const { data: w, info: d } = await c.composite(l).jpeg({ quality: f }).toBuffer({ resolveWithObject: !0 });
171
- return { buffer: w, width: d.width, height: d.height, format: "jpeg" };
177
+ l = [{ input: r, left: a, top: a }];
178
+ const { data: b, info: d } = await c.composite(l).jpeg({ quality: f }).toBuffer({ resolveWithObject: !0 });
179
+ return { buffer: b, width: d.width, height: d.height, format: "jpeg" };
172
180
  }
173
- function tt(o) {
181
+ function ot(o) {
174
182
  return { input: o.buffer, left: o.offset.x, top: o.offset.y, blend: o.blend };
175
183
  }
176
- function ot(o) {
184
+ function nt(o) {
177
185
  return typeof o == "object" && !Buffer.isBuffer(o) && "color" in o;
178
186
  }
179
- function nt(o, t) {
180
- return ot(o) ? m({
187
+ function et(o, t) {
188
+ return nt(o) ? i({
181
189
  create: { width: t, height: t, channels: 3, background: o.color }
182
- }) : m(o).resize(t, t, { fit: "cover" });
190
+ }) : i(o).resize(t, t, { fit: "cover" });
183
191
  }
184
192
  export {
185
- ft as alphaTrim,
186
- st as composeProductPhoto,
187
- q as generateShadowLayers,
188
- ct as removeBackground
193
+ st as alphaTrim,
194
+ ut as composeProductPhoto,
195
+ U as generateShadowLayers,
196
+ ft as removeBackground
189
197
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takazudo/zudo-image-tweaker",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "sharp-based image-tweaking toolkit: variants, HEIF conversion, OGP cards, byte-budget encoding, square crops, product-photo background removal, calibration, compositing, blurhash, EXIF, and browser upload prep.",
5
5
  "private": false,
6
6
  "license": "MIT",