@fluixi/start 0.1.0-alpha.73 → 0.1.0-alpha.75
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/adapter.cjs +750 -0
- package/dist/adapter.d.ts +9 -9
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +10 -10
- package/dist/adapter.mjs +719 -0
- package/dist/adapters/entry.cjs +122 -0
- package/dist/adapters/entry.js +1 -1
- package/dist/adapters/entry.mjs +95 -0
- package/dist/adapters/platforms.cjs +311 -0
- package/dist/adapters/platforms.d.ts +2 -2
- package/dist/adapters/platforms.js +8 -8
- package/dist/adapters/platforms.mjs +284 -0
- package/dist/api-HD7KU3BS.mjs +114 -0
- package/dist/api.cjs +145 -0
- package/dist/api.js +1 -1
- package/dist/api.mjs +116 -0
- package/dist/commands/api-HD7KU3BS.mjs +114 -0
- package/dist/commands/api-PBT4QZ34.mjs +16 -0
- package/dist/commands/api-PJL37LV3.mjs +17 -0
- package/dist/commands/build.cjs +877 -0
- package/dist/commands/build.js +6 -6
- package/dist/commands/build.mjs +717 -0
- package/dist/commands/chunk-GFQ4MUIC.mjs +117 -0
- package/dist/commands/chunk-MTM6M3TC.mjs +115 -0
- package/dist/commands/dev.cjs +670 -0
- package/dist/commands/dev.js +3 -3
- package/dist/commands/dev.mjs +512 -0
- package/dist/commands/index.cjs +1145 -0
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.js +1 -1
- package/dist/commands/index.mjs +982 -0
- package/dist/commands/prerender.cjs +404 -0
- package/dist/commands/prerender.d.ts +1 -1
- package/dist/commands/prerender.js +2 -2
- package/dist/commands/prerender.mjs +377 -0
- package/dist/commands/start.cjs +463 -0
- package/dist/commands/start.d.ts +1 -1
- package/dist/commands/start.js +2 -2
- package/dist/commands/start.mjs +428 -0
- package/dist/config.cjs +150 -0
- package/dist/config.d.ts +24 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -6
- package/dist/config.mjs +114 -0
- package/dist/define-config.cjs +32 -0
- package/dist/define-config.d.ts +12 -0
- package/dist/define-config.d.ts.map +1 -0
- package/dist/define-config.js +3 -0
- package/dist/define-config.mjs +7 -0
- package/dist/di.cjs +200 -0
- package/dist/di.d.ts +4 -4
- package/dist/di.d.ts.map +1 -1
- package/dist/di.js +9 -9
- package/dist/di.mjs +169 -0
- package/dist/document.cjs +115 -0
- package/dist/document.d.ts +1 -1
- package/dist/document.js +1 -1
- package/dist/document.mjs +86 -0
- package/dist/generated-api.cjs +43 -0
- package/dist/generated-api.js +1 -1
- package/dist/generated-api.mjs +18 -0
- package/dist/generated-server-fns.cjs +43 -0
- package/dist/generated-server-fns.mjs +18 -0
- package/dist/handler-core.cjs +211 -0
- package/dist/handler-core.d.ts +2 -2
- package/dist/handler-core.js +2 -2
- package/dist/handler-core.mjs +185 -0
- package/dist/handler.cjs +182 -0
- package/dist/handler.d.ts +3 -3
- package/dist/handler.js +3 -3
- package/dist/handler.mjs +152 -0
- package/dist/head.cjs +25 -0
- package/dist/head.d.ts +1 -1
- package/dist/head.js +1 -1
- package/dist/head.mjs +4 -0
- package/dist/image-loader.cjs +37 -0
- package/dist/image-loader.d.ts +25 -0
- package/dist/image-loader.d.ts.map +1 -0
- package/dist/image-loader.js +18 -0
- package/dist/image-loader.mjs +12 -0
- package/dist/image.cjs +151 -0
- package/dist/image.d.ts +2 -19
- package/dist/image.d.ts.map +1 -1
- package/dist/image.js +5 -21
- package/dist/image.mjs +126 -0
- package/dist/index.cjs +303 -0
- package/dist/index.d.ts +28 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -17
- package/dist/index.mjs +262 -0
- package/dist/interceptors.cjs +72 -0
- package/dist/interceptors.js +1 -1
- package/dist/interceptors.mjs +44 -0
- package/dist/internal.cjs +393 -0
- package/dist/internal.d.ts +6 -6
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +23 -15
- package/dist/internal.mjs +223 -0
- package/dist/middleware.cjs +58 -0
- package/dist/middleware.d.ts +3 -3
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +1 -1
- package/dist/middleware.mjs +31 -0
- package/dist/preload.cjs +42 -0
- package/dist/preload.js +1 -1
- package/dist/preload.mjs +18 -0
- package/dist/router.cjs +25 -0
- package/dist/router.d.ts +3 -3
- package/dist/router.js +3 -3
- package/dist/router.mjs +4 -0
- package/dist/server-fn-setup.cjs +46 -0
- package/dist/server-fn-setup.d.ts +1 -1
- package/dist/server-fn-setup.js +2 -2
- package/dist/server-fn-setup.mjs +22 -0
- package/dist/server-fn.cjs +109 -0
- package/dist/server-fn.d.ts +2 -2
- package/dist/server-fn.js +2 -2
- package/dist/server-fn.mjs +79 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/ui.cjs +123 -0
- package/dist/ui.js +1 -1
- package/dist/ui.mjs +93 -0
- package/package.json +63 -25
package/dist/handler.mjs
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/*! @fluixi/start v0.1.0-alpha.75 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
|
+
|
|
3
|
+
// src/handler.ts
|
|
4
|
+
import { createServer as createHttpServer } from "node:http";
|
|
5
|
+
import { stat } from "node:fs/promises";
|
|
6
|
+
import { createReadStream } from "node:fs";
|
|
7
|
+
import { Readable } from "node:stream";
|
|
8
|
+
import { join, extname } from "node:path";
|
|
9
|
+
function streamDocument(head, body, tail) {
|
|
10
|
+
const enc = new TextEncoder();
|
|
11
|
+
const stream = new ReadableStream({
|
|
12
|
+
async start(controller) {
|
|
13
|
+
try {
|
|
14
|
+
controller.enqueue(enc.encode(head));
|
|
15
|
+
const reader = body.getReader();
|
|
16
|
+
for (; ; ) {
|
|
17
|
+
const { done, value } = await reader.read();
|
|
18
|
+
if (done) break;
|
|
19
|
+
controller.enqueue(value);
|
|
20
|
+
}
|
|
21
|
+
controller.enqueue(enc.encode(tail));
|
|
22
|
+
controller.close();
|
|
23
|
+
} catch (err) {
|
|
24
|
+
controller.error(err);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return new Response(stream, {
|
|
29
|
+
status: 200,
|
|
30
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function createRequestHandler(render) {
|
|
34
|
+
return async (request) => {
|
|
35
|
+
const url = new URL(request.url);
|
|
36
|
+
try {
|
|
37
|
+
const html = await render(url.pathname + url.search, request);
|
|
38
|
+
return new Response(html, {
|
|
39
|
+
status: 200,
|
|
40
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
41
|
+
});
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return new Response(String(e?.stack || e), {
|
|
44
|
+
status: 500,
|
|
45
|
+
headers: { "content-type": "text/plain; charset=utf-8" }
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function nodeToRequest(req) {
|
|
51
|
+
const host = req.headers.host ?? "localhost";
|
|
52
|
+
const url = `http://${host}${req.url ?? "/"}`;
|
|
53
|
+
const headers = new Headers();
|
|
54
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
55
|
+
if (Array.isArray(v)) v.forEach((x) => headers.append(k, x));
|
|
56
|
+
else if (v != null) headers.set(k, v);
|
|
57
|
+
}
|
|
58
|
+
const method = req.method ?? "GET";
|
|
59
|
+
const init = { method, headers };
|
|
60
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
61
|
+
init.body = req;
|
|
62
|
+
init.duplex = "half";
|
|
63
|
+
}
|
|
64
|
+
return new Request(url, init);
|
|
65
|
+
}
|
|
66
|
+
async function sendResponse(res, response) {
|
|
67
|
+
res.statusCode = response.status;
|
|
68
|
+
response.headers.forEach((value, key) => res.setHeader(key, value));
|
|
69
|
+
const body = response.body;
|
|
70
|
+
if (!body) {
|
|
71
|
+
res.end(await response.text().catch(() => ""));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const reader = body.getReader();
|
|
75
|
+
try {
|
|
76
|
+
for (; ; ) {
|
|
77
|
+
const { done, value } = await reader.read();
|
|
78
|
+
if (done) break;
|
|
79
|
+
res.write(value);
|
|
80
|
+
}
|
|
81
|
+
} finally {
|
|
82
|
+
res.end();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function toNodeHandler(handler) {
|
|
86
|
+
return async (req, res) => {
|
|
87
|
+
const response = await handler(nodeToRequest(req));
|
|
88
|
+
await sendResponse(res, response);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function serveNode(handler, opts = {}) {
|
|
92
|
+
const port = opts.port ?? 3e3;
|
|
93
|
+
const node = toNodeHandler(handler);
|
|
94
|
+
const server = createHttpServer((req, res) => {
|
|
95
|
+
node(req, res).catch((e) => {
|
|
96
|
+
res.statusCode = 500;
|
|
97
|
+
res.end(String(e?.stack || e));
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
server.listen(
|
|
101
|
+
port,
|
|
102
|
+
() => opts.onListen ? opts.onListen(port) : console.log(` fluixi → http://localhost:${port}`)
|
|
103
|
+
);
|
|
104
|
+
return server;
|
|
105
|
+
}
|
|
106
|
+
var MIME = {
|
|
107
|
+
".js": "text/javascript",
|
|
108
|
+
".mjs": "text/javascript",
|
|
109
|
+
".css": "text/css",
|
|
110
|
+
".html": "text/html",
|
|
111
|
+
".json": "application/json",
|
|
112
|
+
".svg": "image/svg+xml",
|
|
113
|
+
".png": "image/png",
|
|
114
|
+
".jpg": "image/jpeg",
|
|
115
|
+
".jpeg": "image/jpeg",
|
|
116
|
+
".webp": "image/webp",
|
|
117
|
+
".ico": "image/x-icon",
|
|
118
|
+
".woff": "font/woff",
|
|
119
|
+
".woff2": "font/woff2",
|
|
120
|
+
".map": "application/json"
|
|
121
|
+
};
|
|
122
|
+
function withStaticFiles(handler, dir) {
|
|
123
|
+
return async (request) => {
|
|
124
|
+
const { pathname } = new URL(request.url);
|
|
125
|
+
if (pathname !== "/" && extname(pathname)) {
|
|
126
|
+
const file = join(dir, decodeURIComponent(pathname));
|
|
127
|
+
try {
|
|
128
|
+
const s = await stat(file);
|
|
129
|
+
if (s.isFile()) {
|
|
130
|
+
const body = Readable.toWeb(createReadStream(file));
|
|
131
|
+
return new Response(body, {
|
|
132
|
+
headers: {
|
|
133
|
+
"content-type": MIME[extname(file).toLowerCase()] ?? "application/octet-stream",
|
|
134
|
+
"content-length": String(s.size)
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return handler(request);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export {
|
|
145
|
+
createRequestHandler,
|
|
146
|
+
nodeToRequest,
|
|
147
|
+
sendResponse,
|
|
148
|
+
serveNode,
|
|
149
|
+
streamDocument,
|
|
150
|
+
toNodeHandler,
|
|
151
|
+
withStaticFiles
|
|
152
|
+
};
|
package/dist/head.cjs
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*! @fluixi/start v0.1.0-alpha.75 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __copyProps = (to, from, except, desc) => {
|
|
8
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
9
|
+
for (let key of __getOwnPropNames(from))
|
|
10
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
11
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
12
|
+
}
|
|
13
|
+
return to;
|
|
14
|
+
};
|
|
15
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
16
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
17
|
+
|
|
18
|
+
// src/head.ts
|
|
19
|
+
var head_exports = {};
|
|
20
|
+
module.exports = __toCommonJS(head_exports);
|
|
21
|
+
__reExport(head_exports, require("@fluixi/head"), module.exports);
|
|
22
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
23
|
+
0 && (module.exports = {
|
|
24
|
+
...require("@fluixi/head")
|
|
25
|
+
});
|
package/dist/head.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@fluixi/start/head
|
|
2
|
+
* `@fluixi/start/head`: the app-facing SEO & document-head surface, re-exported from the
|
|
3
3
|
* framework-agnostic `@fluixi/head` engine. Importing from the meta-framework gives apps a single,
|
|
4
4
|
* stable dependency (like `@fluixi/start/router`) instead of reaching into the engine directly:
|
|
5
5
|
*
|
package/dist/head.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@fluixi/start/head
|
|
2
|
+
* `@fluixi/start/head`: the app-facing SEO & document-head surface, re-exported from the
|
|
3
3
|
* framework-agnostic `@fluixi/head` engine. Importing from the meta-framework gives apps a single,
|
|
4
4
|
* stable dependency (like `@fluixi/start/router`) instead of reaching into the engine directly:
|
|
5
5
|
*
|
package/dist/head.mjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/image-loader.ts
|
|
21
|
+
var image_loader_exports = {};
|
|
22
|
+
__export(image_loader_exports, {
|
|
23
|
+
imageLoader: () => imageLoader
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(image_loader_exports);
|
|
26
|
+
function imageLoader(options = {}) {
|
|
27
|
+
const path = options.path ?? "/api/image";
|
|
28
|
+
return ({ src, width, quality }) => {
|
|
29
|
+
const params = new URLSearchParams({ url: src, w: String(width) });
|
|
30
|
+
if (quality != null) params.set("q", String(quality));
|
|
31
|
+
return `${path}?${params.toString()}`;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
35
|
+
0 && (module.exports = {
|
|
36
|
+
imageLoader
|
|
37
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client half of the image support.
|
|
3
|
+
*
|
|
4
|
+
* `imageLoader` builds a URL; `createImageHandler` reads files and transforms them. Only
|
|
5
|
+
* the first belongs in a page, so it lives apart from the half that imports `node:fs`.
|
|
6
|
+
*/
|
|
7
|
+
export interface ImageLoaderOptions {
|
|
8
|
+
/** Path the image handler route is mounted at. Default `/api/image`. */
|
|
9
|
+
path?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A loader for `@fluixi-ui/image` that targets a `createImageHandler` route.
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { imageLoader } from '@fluixi/start';
|
|
16
|
+
* const loader = imageLoader({ path: '/api/image' });
|
|
17
|
+
* <Image src="/mug.jpg" loader={loader} widths={[120, 240, 480]} />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function imageLoader(options?: ImageLoaderOptions): (args: {
|
|
21
|
+
src: string;
|
|
22
|
+
width: number;
|
|
23
|
+
quality?: number;
|
|
24
|
+
}) => string;
|
|
25
|
+
//# sourceMappingURL=image-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-loader.d.ts","sourceRoot":"","sources":["../src/image-loader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,OAAO,GAAE,kBAAuB,GAC/B,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,CAOpE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A loader for `@fluixi-ui/image` that targets a `createImageHandler` route.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* import { imageLoader } from '@fluixi/start';
|
|
6
|
+
* const loader = imageLoader({ path: '/api/image' });
|
|
7
|
+
* <Image src="/mug.jpg" loader={loader} widths={[120, 240, 480]} />
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export function imageLoader(options = {}) {
|
|
11
|
+
const path = options.path ?? '/api/image';
|
|
12
|
+
return ({ src, width, quality }) => {
|
|
13
|
+
const params = new URLSearchParams({ url: src, w: String(width) });
|
|
14
|
+
if (quality != null)
|
|
15
|
+
params.set('q', String(quality));
|
|
16
|
+
return `${path}?${params.toString()}`;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/image-loader.ts
|
|
2
|
+
function imageLoader(options = {}) {
|
|
3
|
+
const path = options.path ?? "/api/image";
|
|
4
|
+
return ({ src, width, quality }) => {
|
|
5
|
+
const params = new URLSearchParams({ url: src, w: String(width) });
|
|
6
|
+
if (quality != null) params.set("q", String(quality));
|
|
7
|
+
return `${path}?${params.toString()}`;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
imageLoader
|
|
12
|
+
};
|
package/dist/image.cjs
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/*! @fluixi/start v0.1.0-alpha.75 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/image.ts
|
|
22
|
+
var image_exports = {};
|
|
23
|
+
__export(image_exports, {
|
|
24
|
+
createImageHandler: () => createImageHandler,
|
|
25
|
+
imageLoader: () => imageLoader
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(image_exports);
|
|
28
|
+
var import_promises = require("node:fs/promises");
|
|
29
|
+
var import_node_path = require("node:path");
|
|
30
|
+
|
|
31
|
+
// src/image-loader.ts
|
|
32
|
+
function imageLoader(options = {}) {
|
|
33
|
+
const path = options.path ?? "/api/image";
|
|
34
|
+
return ({ src, width, quality }) => {
|
|
35
|
+
const params = new URLSearchParams({ url: src, w: String(width) });
|
|
36
|
+
if (quality != null) params.set("q", String(quality));
|
|
37
|
+
return `${path}?${params.toString()}`;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/image.ts
|
|
42
|
+
var DEFAULT_SIZES = [16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840];
|
|
43
|
+
var MIME = {
|
|
44
|
+
".jpg": "image/jpeg",
|
|
45
|
+
".jpeg": "image/jpeg",
|
|
46
|
+
".png": "image/png",
|
|
47
|
+
".webp": "image/webp",
|
|
48
|
+
".avif": "image/avif",
|
|
49
|
+
".gif": "image/gif",
|
|
50
|
+
".svg": "image/svg+xml"
|
|
51
|
+
};
|
|
52
|
+
var sharpModule;
|
|
53
|
+
async function loadSharp() {
|
|
54
|
+
if (sharpModule !== void 0) return sharpModule;
|
|
55
|
+
try {
|
|
56
|
+
const name = "sharp";
|
|
57
|
+
sharpModule = (await import(
|
|
58
|
+
/* @vite-ignore */
|
|
59
|
+
name
|
|
60
|
+
)).default;
|
|
61
|
+
} catch {
|
|
62
|
+
sharpModule = null;
|
|
63
|
+
}
|
|
64
|
+
return sharpModule;
|
|
65
|
+
}
|
|
66
|
+
var defaultTransform = async (input, { width, quality, format }) => {
|
|
67
|
+
const sharp = await loadSharp();
|
|
68
|
+
if (!sharp) return null;
|
|
69
|
+
const pipe = sharp(input).rotate().resize({ width, withoutEnlargement: true });
|
|
70
|
+
if (format === "avif") return { data: new Uint8Array(await pipe.avif({ quality }).toBuffer()), contentType: "image/avif" };
|
|
71
|
+
if (format === "webp") return { data: new Uint8Array(await pipe.webp({ quality }).toBuffer()), contentType: "image/webp" };
|
|
72
|
+
if (format === "png") return { data: new Uint8Array(await pipe.png().toBuffer()), contentType: "image/png" };
|
|
73
|
+
return { data: new Uint8Array(await pipe.jpeg({ quality, mozjpeg: true }).toBuffer()), contentType: "image/jpeg" };
|
|
74
|
+
};
|
|
75
|
+
function imageResponse(body, contentType, maxAge) {
|
|
76
|
+
return new Response(body, {
|
|
77
|
+
headers: {
|
|
78
|
+
"content-type": contentType,
|
|
79
|
+
"cache-control": `public, max-age=${maxAge}, immutable`,
|
|
80
|
+
vary: "Accept"
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function createImageHandler(options = {}) {
|
|
85
|
+
const root = (0, import_node_path.resolve)(options.root ?? (0, import_node_path.join)(process.cwd(), "public"));
|
|
86
|
+
const domains = new Set((options.domains ?? []).map((d) => d.toLowerCase()));
|
|
87
|
+
const sizes = options.sizes ?? DEFAULT_SIZES;
|
|
88
|
+
const maxQuality = options.quality ?? 80;
|
|
89
|
+
const maxAge = options.cacheMaxAge ?? 60 * 60 * 24 * 365;
|
|
90
|
+
const transform = options.transform ?? defaultTransform;
|
|
91
|
+
const cacheMax = options.cache === false ? 0 : typeof options.cache === "number" ? options.cache : 100;
|
|
92
|
+
const cache = /* @__PURE__ */ new Map();
|
|
93
|
+
return async (request) => {
|
|
94
|
+
const url = new URL(request.url);
|
|
95
|
+
const src = url.searchParams.get("url") ?? url.searchParams.get("src");
|
|
96
|
+
const width = Number(url.searchParams.get("w"));
|
|
97
|
+
const quality = Math.min(Math.max(Number(url.searchParams.get("q")) || maxQuality, 1), 100);
|
|
98
|
+
if (!src) return new Response('missing "url"', { status: 400 });
|
|
99
|
+
if (!Number.isInteger(width) || !sizes.includes(width)) return new Response('invalid "w"', { status: 400 });
|
|
100
|
+
const accept = request.headers.get("accept") ?? "";
|
|
101
|
+
const format = accept.includes("image/avif") ? "avif" : accept.includes("image/webp") ? "webp" : "original";
|
|
102
|
+
const key = `${src}|${width}|${quality}|${format}`;
|
|
103
|
+
const hit = cache.get(key);
|
|
104
|
+
if (hit) return imageResponse(hit.body, hit.contentType, maxAge);
|
|
105
|
+
let input;
|
|
106
|
+
let sourceType;
|
|
107
|
+
if (/^https?:\/\//i.test(src)) {
|
|
108
|
+
let host;
|
|
109
|
+
try {
|
|
110
|
+
host = new URL(src).hostname.toLowerCase();
|
|
111
|
+
} catch {
|
|
112
|
+
return new Response("bad url", { status: 400 });
|
|
113
|
+
}
|
|
114
|
+
if (!domains.has(host)) return new Response("domain not allowed", { status: 403 });
|
|
115
|
+
const res = await fetch(src);
|
|
116
|
+
if (!res.ok) return new Response("upstream error", { status: 502 });
|
|
117
|
+
input = new Uint8Array(await res.arrayBuffer());
|
|
118
|
+
sourceType = res.headers.get("content-type") ?? "application/octet-stream";
|
|
119
|
+
} else {
|
|
120
|
+
const filePath = (0, import_node_path.resolve)(root, src.startsWith("/") ? src.slice(1) : src);
|
|
121
|
+
if (filePath !== root && !filePath.startsWith(root + import_node_path.sep)) return new Response("forbidden", { status: 403 });
|
|
122
|
+
try {
|
|
123
|
+
input = new Uint8Array(await (0, import_promises.readFile)(filePath));
|
|
124
|
+
} catch {
|
|
125
|
+
return new Response("not found", { status: 404 });
|
|
126
|
+
}
|
|
127
|
+
sourceType = MIME[(0, import_node_path.extname)(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
128
|
+
}
|
|
129
|
+
if (sourceType === "image/svg+xml" || sourceType === "image/gif") {
|
|
130
|
+
return imageResponse(input, sourceType, maxAge);
|
|
131
|
+
}
|
|
132
|
+
let out = null;
|
|
133
|
+
try {
|
|
134
|
+
out = await transform(input, { width, quality, format, contentType: sourceType });
|
|
135
|
+
} catch {
|
|
136
|
+
out = null;
|
|
137
|
+
}
|
|
138
|
+
const body = out?.data ?? input;
|
|
139
|
+
const contentType = out?.contentType ?? sourceType;
|
|
140
|
+
if (cacheMax > 0) {
|
|
141
|
+
if (cache.size >= cacheMax) cache.delete(cache.keys().next().value);
|
|
142
|
+
cache.set(key, { body, contentType });
|
|
143
|
+
}
|
|
144
|
+
return imageResponse(body, contentType, maxAge);
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
148
|
+
0 && (module.exports = {
|
|
149
|
+
createImageHandler,
|
|
150
|
+
imageLoader
|
|
151
|
+
});
|
package/dist/image.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export interface ImageHandlerOptions {
|
|
|
35
35
|
*
|
|
36
36
|
* ```ts
|
|
37
37
|
* // src/api/image.ts
|
|
38
|
-
* import { createImageHandler } from '@fluixi/start';
|
|
38
|
+
* import { createImageHandler } from '@fluixi/start/image';
|
|
39
39
|
* export const GET = createImageHandler({ root: 'public', domains: ['cdn.example.com'] });
|
|
40
40
|
* ```
|
|
41
41
|
*
|
|
@@ -43,22 +43,5 @@ export interface ImageHandlerOptions {
|
|
|
43
43
|
* through unchanged (so it's safe to ship either way).
|
|
44
44
|
*/
|
|
45
45
|
export declare function createImageHandler(options?: ImageHandlerOptions): (request: Request) => Promise<Response>;
|
|
46
|
-
export
|
|
47
|
-
/** Path the {@link createImageHandler} route is mounted at. Default `/api/image`. */
|
|
48
|
-
path?: string;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* A loader for `@fluixi-ui/image` that targets a {@link createImageHandler} route.
|
|
52
|
-
*
|
|
53
|
-
* ```tsx
|
|
54
|
-
* import { imageLoader } from '@fluixi/start';
|
|
55
|
-
* const loader = imageLoader({ path: '/api/image' });
|
|
56
|
-
* <Image src="/mug.jpg" loader={loader} widths={[120, 240, 480]} />
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
export declare function imageLoader(options?: ImageLoaderOptions): (args: {
|
|
60
|
-
src: string;
|
|
61
|
-
width: number;
|
|
62
|
-
quality?: number;
|
|
63
|
-
}) => string;
|
|
46
|
+
export { imageLoader, type ImageLoaderOptions } from './image-loader.js';
|
|
64
47
|
//# sourceMappingURL=image.d.ts.map
|
package/dist/image.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;IACtD,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,sFAAsF;AACtF,MAAM,MAAM,cAAc,GAAG,CAC3B,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,kBAAkB,KACrB,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAAC;AAE/D,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,2FAA2F;IAC3F,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B;AAyCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAoE7G;
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;IACtD,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,sFAAsF;AACtF,MAAM,MAAM,cAAc,GAAG,CAC3B,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,kBAAkB,KACrB,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAAC;AAE/D,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,2FAA2F;IAC3F,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B;AAyCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAoE7G;AAGD,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/image.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// On-demand image optimization: a Web `Request` → `Response` handler that resizes
|
|
2
2
|
// and re-encodes images (to AVIF/WebP by content negotiation), plus a matching
|
|
3
|
-
// `imageLoader` for `@fluixi-ui/image`. The actual codec is pluggable
|
|
3
|
+
// `imageLoader` for `@fluixi-ui/image`. The actual codec is pluggable, the
|
|
4
4
|
// default uses `sharp` if it's installed, otherwise it streams the source through
|
|
5
5
|
// unchanged (so the framework never hard-depends on a native module).
|
|
6
6
|
import { readFile } from 'node:fs/promises';
|
|
@@ -52,7 +52,7 @@ function imageResponse(body, contentType, maxAge) {
|
|
|
52
52
|
*
|
|
53
53
|
* ```ts
|
|
54
54
|
* // src/api/image.ts
|
|
55
|
-
* import { createImageHandler } from '@fluixi/start';
|
|
55
|
+
* import { createImageHandler } from '@fluixi/start/image';
|
|
56
56
|
* export const GET = createImageHandler({ root: 'public', domains: ['cdn.example.com'] });
|
|
57
57
|
* ```
|
|
58
58
|
*
|
|
@@ -113,7 +113,7 @@ export function createImageHandler(options = {}) {
|
|
|
113
113
|
}
|
|
114
114
|
sourceType = MIME[extname(filePath).toLowerCase()] ?? 'application/octet-stream';
|
|
115
115
|
}
|
|
116
|
-
// Vector / animated sources aren't rasterized
|
|
116
|
+
// Vector / animated sources aren't rasterized, stream them through.
|
|
117
117
|
if (sourceType === 'image/svg+xml' || sourceType === 'image/gif') {
|
|
118
118
|
return imageResponse(input, sourceType, maxAge);
|
|
119
119
|
}
|
|
@@ -134,21 +134,5 @@ export function createImageHandler(options = {}) {
|
|
|
134
134
|
return imageResponse(body, contentType, maxAge);
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
*
|
|
140
|
-
* ```tsx
|
|
141
|
-
* import { imageLoader } from '@fluixi/start';
|
|
142
|
-
* const loader = imageLoader({ path: '/api/image' });
|
|
143
|
-
* <Image src="/mug.jpg" loader={loader} widths={[120, 240, 480]} />
|
|
144
|
-
* ```
|
|
145
|
-
*/
|
|
146
|
-
export function imageLoader(options = {}) {
|
|
147
|
-
const path = options.path ?? '/api/image';
|
|
148
|
-
return ({ src, width, quality }) => {
|
|
149
|
-
const params = new URLSearchParams({ url: src, w: String(width) });
|
|
150
|
-
if (quality != null)
|
|
151
|
-
params.set('q', String(quality));
|
|
152
|
-
return `${path}?${params.toString()}`;
|
|
153
|
-
};
|
|
154
|
-
}
|
|
137
|
+
// The loader lives apart, so a page can import it without this module's `node:fs`.
|
|
138
|
+
export { imageLoader } from './image-loader.js';
|
package/dist/image.mjs
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*! @fluixi/start v0.1.0-alpha.75 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
|
+
|
|
3
|
+
// src/image.ts
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { resolve as resolvePath, join, extname, sep } from "node:path";
|
|
6
|
+
|
|
7
|
+
// src/image-loader.ts
|
|
8
|
+
function imageLoader(options = {}) {
|
|
9
|
+
const path = options.path ?? "/api/image";
|
|
10
|
+
return ({ src, width, quality }) => {
|
|
11
|
+
const params = new URLSearchParams({ url: src, w: String(width) });
|
|
12
|
+
if (quality != null) params.set("q", String(quality));
|
|
13
|
+
return `${path}?${params.toString()}`;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/image.ts
|
|
18
|
+
var DEFAULT_SIZES = [16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840];
|
|
19
|
+
var MIME = {
|
|
20
|
+
".jpg": "image/jpeg",
|
|
21
|
+
".jpeg": "image/jpeg",
|
|
22
|
+
".png": "image/png",
|
|
23
|
+
".webp": "image/webp",
|
|
24
|
+
".avif": "image/avif",
|
|
25
|
+
".gif": "image/gif",
|
|
26
|
+
".svg": "image/svg+xml"
|
|
27
|
+
};
|
|
28
|
+
var sharpModule;
|
|
29
|
+
async function loadSharp() {
|
|
30
|
+
if (sharpModule !== void 0) return sharpModule;
|
|
31
|
+
try {
|
|
32
|
+
const name = "sharp";
|
|
33
|
+
sharpModule = (await import(
|
|
34
|
+
/* @vite-ignore */
|
|
35
|
+
name
|
|
36
|
+
)).default;
|
|
37
|
+
} catch {
|
|
38
|
+
sharpModule = null;
|
|
39
|
+
}
|
|
40
|
+
return sharpModule;
|
|
41
|
+
}
|
|
42
|
+
var defaultTransform = async (input, { width, quality, format }) => {
|
|
43
|
+
const sharp = await loadSharp();
|
|
44
|
+
if (!sharp) return null;
|
|
45
|
+
const pipe = sharp(input).rotate().resize({ width, withoutEnlargement: true });
|
|
46
|
+
if (format === "avif") return { data: new Uint8Array(await pipe.avif({ quality }).toBuffer()), contentType: "image/avif" };
|
|
47
|
+
if (format === "webp") return { data: new Uint8Array(await pipe.webp({ quality }).toBuffer()), contentType: "image/webp" };
|
|
48
|
+
if (format === "png") return { data: new Uint8Array(await pipe.png().toBuffer()), contentType: "image/png" };
|
|
49
|
+
return { data: new Uint8Array(await pipe.jpeg({ quality, mozjpeg: true }).toBuffer()), contentType: "image/jpeg" };
|
|
50
|
+
};
|
|
51
|
+
function imageResponse(body, contentType, maxAge) {
|
|
52
|
+
return new Response(body, {
|
|
53
|
+
headers: {
|
|
54
|
+
"content-type": contentType,
|
|
55
|
+
"cache-control": `public, max-age=${maxAge}, immutable`,
|
|
56
|
+
vary: "Accept"
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function createImageHandler(options = {}) {
|
|
61
|
+
const root = resolvePath(options.root ?? join(process.cwd(), "public"));
|
|
62
|
+
const domains = new Set((options.domains ?? []).map((d) => d.toLowerCase()));
|
|
63
|
+
const sizes = options.sizes ?? DEFAULT_SIZES;
|
|
64
|
+
const maxQuality = options.quality ?? 80;
|
|
65
|
+
const maxAge = options.cacheMaxAge ?? 60 * 60 * 24 * 365;
|
|
66
|
+
const transform = options.transform ?? defaultTransform;
|
|
67
|
+
const cacheMax = options.cache === false ? 0 : typeof options.cache === "number" ? options.cache : 100;
|
|
68
|
+
const cache = /* @__PURE__ */ new Map();
|
|
69
|
+
return async (request) => {
|
|
70
|
+
const url = new URL(request.url);
|
|
71
|
+
const src = url.searchParams.get("url") ?? url.searchParams.get("src");
|
|
72
|
+
const width = Number(url.searchParams.get("w"));
|
|
73
|
+
const quality = Math.min(Math.max(Number(url.searchParams.get("q")) || maxQuality, 1), 100);
|
|
74
|
+
if (!src) return new Response('missing "url"', { status: 400 });
|
|
75
|
+
if (!Number.isInteger(width) || !sizes.includes(width)) return new Response('invalid "w"', { status: 400 });
|
|
76
|
+
const accept = request.headers.get("accept") ?? "";
|
|
77
|
+
const format = accept.includes("image/avif") ? "avif" : accept.includes("image/webp") ? "webp" : "original";
|
|
78
|
+
const key = `${src}|${width}|${quality}|${format}`;
|
|
79
|
+
const hit = cache.get(key);
|
|
80
|
+
if (hit) return imageResponse(hit.body, hit.contentType, maxAge);
|
|
81
|
+
let input;
|
|
82
|
+
let sourceType;
|
|
83
|
+
if (/^https?:\/\//i.test(src)) {
|
|
84
|
+
let host;
|
|
85
|
+
try {
|
|
86
|
+
host = new URL(src).hostname.toLowerCase();
|
|
87
|
+
} catch {
|
|
88
|
+
return new Response("bad url", { status: 400 });
|
|
89
|
+
}
|
|
90
|
+
if (!domains.has(host)) return new Response("domain not allowed", { status: 403 });
|
|
91
|
+
const res = await fetch(src);
|
|
92
|
+
if (!res.ok) return new Response("upstream error", { status: 502 });
|
|
93
|
+
input = new Uint8Array(await res.arrayBuffer());
|
|
94
|
+
sourceType = res.headers.get("content-type") ?? "application/octet-stream";
|
|
95
|
+
} else {
|
|
96
|
+
const filePath = resolvePath(root, src.startsWith("/") ? src.slice(1) : src);
|
|
97
|
+
if (filePath !== root && !filePath.startsWith(root + sep)) return new Response("forbidden", { status: 403 });
|
|
98
|
+
try {
|
|
99
|
+
input = new Uint8Array(await readFile(filePath));
|
|
100
|
+
} catch {
|
|
101
|
+
return new Response("not found", { status: 404 });
|
|
102
|
+
}
|
|
103
|
+
sourceType = MIME[extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
104
|
+
}
|
|
105
|
+
if (sourceType === "image/svg+xml" || sourceType === "image/gif") {
|
|
106
|
+
return imageResponse(input, sourceType, maxAge);
|
|
107
|
+
}
|
|
108
|
+
let out = null;
|
|
109
|
+
try {
|
|
110
|
+
out = await transform(input, { width, quality, format, contentType: sourceType });
|
|
111
|
+
} catch {
|
|
112
|
+
out = null;
|
|
113
|
+
}
|
|
114
|
+
const body = out?.data ?? input;
|
|
115
|
+
const contentType = out?.contentType ?? sourceType;
|
|
116
|
+
if (cacheMax > 0) {
|
|
117
|
+
if (cache.size >= cacheMax) cache.delete(cache.keys().next().value);
|
|
118
|
+
cache.set(key, { body, contentType });
|
|
119
|
+
}
|
|
120
|
+
return imageResponse(body, contentType, maxAge);
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
export {
|
|
124
|
+
createImageHandler,
|
|
125
|
+
imageLoader
|
|
126
|
+
};
|