next-image-transformer 0.2.2 → 0.2.4
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/dist/index-C4FbrW_M.js +10252 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +2 -14459
- package/dist/server.d.ts +1 -0
- package/dist/server.js +198 -0
- package/package.json +8 -3
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createImageTransformRouteHandler } from "./createImageTransformRouteHandler";
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import h from "node:path";
|
|
2
|
+
import { z as C, c as B } from "./index-C4FbrW_M.js";
|
|
3
|
+
import { createHash as U } from "node:crypto";
|
|
4
|
+
import m from "node:fs/promises";
|
|
5
|
+
import b from "sharp";
|
|
6
|
+
function j({
|
|
7
|
+
canonicalUrl: r
|
|
8
|
+
}) {
|
|
9
|
+
return U("sha256").update(r).digest("hex");
|
|
10
|
+
}
|
|
11
|
+
function P({
|
|
12
|
+
cacheKey: r,
|
|
13
|
+
cacheDir: n
|
|
14
|
+
}) {
|
|
15
|
+
const s = r.slice(0, 2), a = r.slice(2), e = h.join(n, s);
|
|
16
|
+
return {
|
|
17
|
+
dir: e,
|
|
18
|
+
bodyPath: h.join(e, `${a}.bin`),
|
|
19
|
+
metaPath: h.join(e, `${a}.json`)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async function q({
|
|
23
|
+
cacheKey: r,
|
|
24
|
+
cacheDir: n
|
|
25
|
+
}) {
|
|
26
|
+
const { bodyPath: s, metaPath: a } = P({ cacheKey: r, cacheDir: n });
|
|
27
|
+
try {
|
|
28
|
+
const [e, u] = await Promise.all([
|
|
29
|
+
m.readFile(s),
|
|
30
|
+
m.readFile(a, "utf8")
|
|
31
|
+
]), o = (() => {
|
|
32
|
+
try {
|
|
33
|
+
const i = JSON.parse(u), t = C.object({
|
|
34
|
+
contentType: C.string()
|
|
35
|
+
}).loose().safeParse(i);
|
|
36
|
+
return t.success ? t.data : null;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
})();
|
|
41
|
+
return o ? { body: new Uint8Array(e), contentType: o.contentType } : null;
|
|
42
|
+
} catch (e) {
|
|
43
|
+
if (e instanceof Error && "code" in e && e.code === "ENOENT")
|
|
44
|
+
return null;
|
|
45
|
+
throw e;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function x(r, n, s, a, e, u, o, i, t) {
|
|
49
|
+
switch (arguments.length) {
|
|
50
|
+
case 1:
|
|
51
|
+
return r;
|
|
52
|
+
case 2:
|
|
53
|
+
return n(r);
|
|
54
|
+
case 3:
|
|
55
|
+
return s(n(r));
|
|
56
|
+
case 4:
|
|
57
|
+
return a(s(n(r)));
|
|
58
|
+
case 5:
|
|
59
|
+
return e(a(s(n(r))));
|
|
60
|
+
case 6:
|
|
61
|
+
return u(e(a(s(n(r)))));
|
|
62
|
+
case 7:
|
|
63
|
+
return o(u(e(a(s(n(r))))));
|
|
64
|
+
case 8:
|
|
65
|
+
return i(o(u(e(a(s(n(r)))))));
|
|
66
|
+
case 9:
|
|
67
|
+
return t(i(o(u(e(a(s(n(r))))))));
|
|
68
|
+
default: {
|
|
69
|
+
for (var c = arguments[0], f = 1; f < arguments.length; f++)
|
|
70
|
+
c = arguments[f](c);
|
|
71
|
+
return c;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function A({
|
|
76
|
+
cacheKey: r,
|
|
77
|
+
body: n,
|
|
78
|
+
meta: s,
|
|
79
|
+
cacheDir: a
|
|
80
|
+
}) {
|
|
81
|
+
const { dir: e, bodyPath: u, metaPath: o } = P({
|
|
82
|
+
cacheKey: r,
|
|
83
|
+
cacheDir: a
|
|
84
|
+
});
|
|
85
|
+
await m.mkdir(e, { recursive: !0 });
|
|
86
|
+
const i = `${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`, t = `${u}.${i}.tmp`, c = `${o}.${i}.tmp`;
|
|
87
|
+
await Promise.all([
|
|
88
|
+
m.writeFile(t, n),
|
|
89
|
+
m.writeFile(c, JSON.stringify(s), "utf8")
|
|
90
|
+
]), await Promise.all([
|
|
91
|
+
m.rename(t, u),
|
|
92
|
+
m.rename(c, o)
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
const O = ({
|
|
96
|
+
apiRouteUrl: r,
|
|
97
|
+
cacheDir: n = h.join(process.cwd(), ".transform-cache"),
|
|
98
|
+
cacheControl: s = "public, max-age=31536000, immutable",
|
|
99
|
+
allowedHosts: a
|
|
100
|
+
}) => {
|
|
101
|
+
const e = B({ apiRouteUrl: r }), u = (o) => {
|
|
102
|
+
if (!a) return !0;
|
|
103
|
+
const i = o.hostname.toLowerCase(), t = o.host.toLowerCase();
|
|
104
|
+
return a.some((c) => {
|
|
105
|
+
if (c instanceof RegExp)
|
|
106
|
+
return c.test(i) || c.test(t);
|
|
107
|
+
const f = c.toLowerCase();
|
|
108
|
+
return f.includes(":") ? t === f : i === f;
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
return async function(i) {
|
|
112
|
+
const { data: t, error: c } = e.safeDecode(i.url);
|
|
113
|
+
if (c)
|
|
114
|
+
return new Response("Bad Request", { status: 400 });
|
|
115
|
+
const f = e.encode(
|
|
116
|
+
// encode() accepts the decoded transform config shape; we only call this
|
|
117
|
+
// after decoding has succeeded.
|
|
118
|
+
t
|
|
119
|
+
), y = t.q ?? 100, g = j({ canonicalUrl: f }), d = await q({ cacheKey: g, cacheDir: n });
|
|
120
|
+
if (d)
|
|
121
|
+
return new Response(d.body, {
|
|
122
|
+
headers: {
|
|
123
|
+
"Content-Type": d.contentType,
|
|
124
|
+
"Cache-Control": s
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
let l;
|
|
128
|
+
try {
|
|
129
|
+
l = new URL(t.source);
|
|
130
|
+
} catch {
|
|
131
|
+
return new Response("Bad Request", { status: 400 });
|
|
132
|
+
}
|
|
133
|
+
if (l.protocol !== "http:" && l.protocol !== "https:")
|
|
134
|
+
return new Response("Bad Request", { status: 400 });
|
|
135
|
+
if (l.username || l.password)
|
|
136
|
+
return new Response("Bad Request", { status: 400 });
|
|
137
|
+
if (!u(l))
|
|
138
|
+
return new Response("Forbidden", { status: 403 });
|
|
139
|
+
const w = await fetch(l);
|
|
140
|
+
if (!w.ok)
|
|
141
|
+
return new Response("Upstream fetch failed", { status: 502 });
|
|
142
|
+
const v = b(Buffer.from(await w.arrayBuffer())), R = await x(
|
|
143
|
+
v,
|
|
144
|
+
/**
|
|
145
|
+
* auto-orient: read's the image's EXIF data and rotates it to the correct
|
|
146
|
+
* orientation.
|
|
147
|
+
*/
|
|
148
|
+
(p) => p.rotate(),
|
|
149
|
+
/**
|
|
150
|
+
* Resize
|
|
151
|
+
*/
|
|
152
|
+
(p) => t.w || t.h ? p.resize({
|
|
153
|
+
width: t.w,
|
|
154
|
+
height: t.h,
|
|
155
|
+
fit: "inside",
|
|
156
|
+
withoutEnlargement: !0
|
|
157
|
+
}) : p,
|
|
158
|
+
/**
|
|
159
|
+
* Change format
|
|
160
|
+
*/
|
|
161
|
+
(p) => {
|
|
162
|
+
switch (t.fmt) {
|
|
163
|
+
case "preserve":
|
|
164
|
+
return p;
|
|
165
|
+
case "avif":
|
|
166
|
+
return p.avif({ quality: y });
|
|
167
|
+
case "webp":
|
|
168
|
+
return p.webp({ quality: y });
|
|
169
|
+
default:
|
|
170
|
+
throw new Error(`Unreachable case: ${t.fmt}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
).toBuffer(), $ = new Uint8Array(R), T = (() => {
|
|
174
|
+
switch (t.fmt) {
|
|
175
|
+
case "preserve":
|
|
176
|
+
return w.headers.get("content-type") ?? "application/octet-stream";
|
|
177
|
+
case "avif":
|
|
178
|
+
return "image/avif";
|
|
179
|
+
case "webp":
|
|
180
|
+
return "image/webp";
|
|
181
|
+
}
|
|
182
|
+
})();
|
|
183
|
+
return await A({
|
|
184
|
+
cacheKey: g,
|
|
185
|
+
body: R,
|
|
186
|
+
meta: { contentType: T },
|
|
187
|
+
cacheDir: n
|
|
188
|
+
}), new Response($, {
|
|
189
|
+
headers: {
|
|
190
|
+
"Content-Type": T,
|
|
191
|
+
"Cache-Control": s
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
export {
|
|
197
|
+
O as createImageTransformRouteHandler
|
|
198
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-image-transformer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"import": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./server": {
|
|
12
|
+
"types": "./dist/server.d.ts",
|
|
13
|
+
"import": "./dist/server.js"
|
|
10
14
|
}
|
|
11
15
|
},
|
|
12
16
|
"type": "module",
|
|
@@ -25,16 +29,17 @@
|
|
|
25
29
|
"@types/node": "24",
|
|
26
30
|
"np": "^10.2.0",
|
|
27
31
|
"prettier": "^3.7.4",
|
|
32
|
+
"sharp": "^0.34.5",
|
|
28
33
|
"typescript": "^5.9.3",
|
|
29
34
|
"vite": "^7.3.0"
|
|
30
35
|
},
|
|
31
36
|
"dependencies": {
|
|
32
37
|
"fp-ts": "^2.16.11",
|
|
33
|
-
"sharp": "^0.34.5",
|
|
34
38
|
"zod": "^4.3.5"
|
|
35
39
|
},
|
|
36
40
|
"peerDependencies": {
|
|
37
|
-
"next": ">=13.4 <17"
|
|
41
|
+
"next": ">=13.4 <17",
|
|
42
|
+
"sharp": "<1"
|
|
38
43
|
},
|
|
39
44
|
"scripts": {
|
|
40
45
|
"build": "vite build && tsc",
|