weifuwu 0.27.24 → 0.27.26
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/README.md +34 -25
- package/dist/cli.js +6 -23
- package/dist/core/router.d.ts +16 -0
- package/dist/index.d.ts +1 -19
- package/dist/index.js +49 -600
- package/dist/types.d.ts +27 -7
- package/package.json +5 -9
- package/dist/docs/ssr/ui.md +0 -472
- package/dist/middleware/csrf.d.ts +0 -47
- package/dist/middleware/flash.d.ts +0 -90
- package/dist/middleware/i18n.d.ts +0 -39
- package/dist/middleware/theme.d.ts +0 -31
- package/dist/ssr/assets.d.ts +0 -5
- package/dist/ssr/compile.d.ts +0 -21
- package/dist/ssr/css.d.ts +0 -16
- package/dist/ssr/html.d.ts +0 -53
- package/dist/ssr/layout.d.ts +0 -2
- package/dist/ssr/ui/assets.d.ts +0 -4
- package/dist/ssr/view.d.ts +0 -36
- package/dist/template/AGENTS.md +0 -30
- package/dist/template/app.ts +0 -20
- package/dist/template/index.ts +0 -6
- package/dist/template/locales/en.json +0 -7
- package/dist/template/locales/zh-CN.json +0 -7
- package/dist/template/package.json +0 -11
- package/dist/template/tsconfig.json +0 -15
- package/dist/template/ui/app/globals.css +0 -2
- package/dist/template/ui/app/layout.ts +0 -39
- package/dist/template/ui/app/page.ts +0 -39
- package/dist/weifuwu-ui.css +0 -646
- package/dist/weifuwu-ui.js +0 -998
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var HttpError = class extends Error {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
// core/trace.ts
|
|
12
|
-
import
|
|
12
|
+
import crypto from "node:crypto";
|
|
13
13
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
14
14
|
var als = new AsyncLocalStorage();
|
|
15
15
|
function currentTraceId() {
|
|
@@ -19,7 +19,7 @@ function currentTrace() {
|
|
|
19
19
|
return als.getStore();
|
|
20
20
|
}
|
|
21
21
|
function runWithTrace(incomingTraceId, fn) {
|
|
22
|
-
const traceId = incomingTraceId ||
|
|
22
|
+
const traceId = incomingTraceId || crypto.randomUUID();
|
|
23
23
|
const startTime = Date.now();
|
|
24
24
|
return als.run({ traceId, startTime }, fn);
|
|
25
25
|
}
|
|
@@ -30,7 +30,7 @@ function traceElapsed() {
|
|
|
30
30
|
}
|
|
31
31
|
function trace(options) {
|
|
32
32
|
const header = options?.header ?? "X-Request-ID";
|
|
33
|
-
const gen = options?.generator ?? (() =>
|
|
33
|
+
const gen = options?.generator ?? (() => crypto.randomUUID());
|
|
34
34
|
return async (req, ctx, next) => {
|
|
35
35
|
const existing = req.headers.get(header);
|
|
36
36
|
const requestId2 = existing ?? gen();
|
|
@@ -293,14 +293,14 @@ function serve(handler, options) {
|
|
|
293
293
|
if (!server.listening) return;
|
|
294
294
|
server.close();
|
|
295
295
|
server.closeIdleConnections();
|
|
296
|
-
return new Promise((
|
|
296
|
+
return new Promise((resolve3) => {
|
|
297
297
|
const timer = setTimeout(() => {
|
|
298
298
|
server.closeAllConnections();
|
|
299
|
-
|
|
299
|
+
resolve3();
|
|
300
300
|
}, timeoutMs);
|
|
301
301
|
server.on("close", () => {
|
|
302
302
|
clearTimeout(timer);
|
|
303
|
-
|
|
303
|
+
resolve3();
|
|
304
304
|
});
|
|
305
305
|
});
|
|
306
306
|
}
|
|
@@ -345,7 +345,7 @@ function createHub(opts) {
|
|
|
345
345
|
}
|
|
346
346
|
});
|
|
347
347
|
}
|
|
348
|
-
function
|
|
348
|
+
function join2(key, ws) {
|
|
349
349
|
if (!channels.has(key)) {
|
|
350
350
|
channels.set(key, /* @__PURE__ */ new Set());
|
|
351
351
|
redisSub?.subscribe(`${prefix}${key}`);
|
|
@@ -407,7 +407,7 @@ function createHub(opts) {
|
|
|
407
407
|
redisPub = void 0;
|
|
408
408
|
redisSub = null;
|
|
409
409
|
}
|
|
410
|
-
return { join:
|
|
410
|
+
return { join: join2, leave, broadcast, close };
|
|
411
411
|
}
|
|
412
412
|
|
|
413
413
|
// core/router.ts
|
|
@@ -919,7 +919,7 @@ function upgradeSocket(wss, req, socket, head, handler, ctx, hub) {
|
|
|
919
919
|
hub.leave(ws);
|
|
920
920
|
},
|
|
921
921
|
sendRoom(room, data) {
|
|
922
|
-
hub.broadcast(room,
|
|
922
|
+
hub.broadcast(room, data);
|
|
923
923
|
}
|
|
924
924
|
};
|
|
925
925
|
if (handler.open) {
|
|
@@ -1082,13 +1082,13 @@ function serveStatic(root, options) {
|
|
|
1082
1082
|
let fileHandle;
|
|
1083
1083
|
try {
|
|
1084
1084
|
fileHandle = await open(filePath, "r");
|
|
1085
|
-
let
|
|
1085
|
+
let stat = await fileHandle.stat();
|
|
1086
1086
|
const realPath = await realpath(filePath);
|
|
1087
1087
|
if (!realPath.startsWith(rootDir + sep) && realPath !== rootDir) {
|
|
1088
1088
|
await fileHandle.close();
|
|
1089
1089
|
return new Response("Forbidden", { status: 403 });
|
|
1090
1090
|
}
|
|
1091
|
-
if (
|
|
1091
|
+
if (stat.isDirectory()) {
|
|
1092
1092
|
await fileHandle.close();
|
|
1093
1093
|
const indexFile = opts.index ?? "index.html";
|
|
1094
1094
|
filePath = resolve2(filePath, indexFile);
|
|
@@ -1096,29 +1096,29 @@ function serveStatic(root, options) {
|
|
|
1096
1096
|
return new Response("Forbidden", { status: 403 });
|
|
1097
1097
|
}
|
|
1098
1098
|
fileHandle = await open(filePath, "r");
|
|
1099
|
-
|
|
1100
|
-
if (!
|
|
1099
|
+
stat = await fileHandle.stat();
|
|
1100
|
+
if (!stat.isFile()) {
|
|
1101
1101
|
await fileHandle.close();
|
|
1102
1102
|
return new Response("Not Found", { status: 404 });
|
|
1103
1103
|
}
|
|
1104
1104
|
}
|
|
1105
1105
|
const mimeType = MIME_TYPES[extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
1106
|
-
const etag = `"${
|
|
1106
|
+
const etag = `"${stat.ino}-${stat.size}-${stat.mtimeMs}"`;
|
|
1107
1107
|
const ifNoneMatch = req.headers.get("if-none-match");
|
|
1108
1108
|
if (ifNoneMatch === etag) {
|
|
1109
1109
|
await fileHandle.close();
|
|
1110
1110
|
return new Response(null, { status: 304 });
|
|
1111
1111
|
}
|
|
1112
1112
|
const ifModifiedSince = req.headers.get("if-modified-since");
|
|
1113
|
-
if (ifModifiedSince &&
|
|
1113
|
+
if (ifModifiedSince && stat.mtimeMs <= new Date(ifModifiedSince).getTime()) {
|
|
1114
1114
|
await fileHandle.close();
|
|
1115
1115
|
return new Response(null, { status: 304 });
|
|
1116
1116
|
}
|
|
1117
1117
|
const headers = {
|
|
1118
1118
|
"Content-Type": mimeType,
|
|
1119
|
-
"Content-Length": String(
|
|
1119
|
+
"Content-Length": String(stat.size),
|
|
1120
1120
|
ETag: etag,
|
|
1121
|
-
"Last-Modified":
|
|
1121
|
+
"Last-Modified": stat.mtime.toUTCString(),
|
|
1122
1122
|
"Cache-Control": opts.immutable ? `public, max-age=${opts.maxAge ?? 31536e3}, immutable` : `public, max-age=${opts.maxAge ?? 0}`
|
|
1123
1123
|
};
|
|
1124
1124
|
const readStream = fileHandle.createReadStream();
|
|
@@ -1656,10 +1656,10 @@ var DEFAULTS = {
|
|
|
1656
1656
|
};
|
|
1657
1657
|
|
|
1658
1658
|
// middleware/request-id.ts
|
|
1659
|
-
import
|
|
1659
|
+
import crypto2 from "node:crypto";
|
|
1660
1660
|
function requestId(options) {
|
|
1661
1661
|
const header = options?.header ?? "X-Request-ID";
|
|
1662
|
-
const gen = options?.generator ?? (() =>
|
|
1662
|
+
const gen = options?.generator ?? (() => crypto2.randomUUID());
|
|
1663
1663
|
const mw = async (req, ctx, next) => {
|
|
1664
1664
|
const existing = req.headers.get(header);
|
|
1665
1665
|
const id = existing ?? gen();
|
|
@@ -1960,7 +1960,7 @@ var TestWSRequest = class {
|
|
|
1960
1960
|
const baseUrl = await this.app._ensureServer();
|
|
1961
1961
|
const wsUrl = baseUrl.replace(/^http/, "ws") + this.path;
|
|
1962
1962
|
const ws = new WSWebSocket(wsUrl, { handshakeTimeout: this._timeout });
|
|
1963
|
-
return new Promise((
|
|
1963
|
+
return new Promise((resolve3, reject) => {
|
|
1964
1964
|
const timer = setTimeout(() => {
|
|
1965
1965
|
reject(new Error(`WebSocket connection timed out after ${this._timeout}ms`));
|
|
1966
1966
|
ws.close();
|
|
@@ -1969,7 +1969,7 @@ var TestWSRequest = class {
|
|
|
1969
1969
|
clearTimeout(timer);
|
|
1970
1970
|
const conn = new TestWSConnection(ws, this._timeout);
|
|
1971
1971
|
this.app._trackConnection(conn);
|
|
1972
|
-
|
|
1972
|
+
resolve3(conn);
|
|
1973
1973
|
});
|
|
1974
1974
|
ws.on("error", (err) => {
|
|
1975
1975
|
clearTimeout(timer);
|
|
@@ -2000,8 +2000,8 @@ var TestWSConnection = class {
|
|
|
2000
2000
|
ws.on("message", (data) => {
|
|
2001
2001
|
const str = data.toString();
|
|
2002
2002
|
if (this.resolveQueue.length > 0) {
|
|
2003
|
-
const
|
|
2004
|
-
|
|
2003
|
+
const resolve3 = this.resolveQueue.shift();
|
|
2004
|
+
resolve3(str);
|
|
2005
2005
|
} else {
|
|
2006
2006
|
this.messageQueue.push(str);
|
|
2007
2007
|
}
|
|
@@ -2031,15 +2031,15 @@ var TestWSConnection = class {
|
|
|
2031
2031
|
if (this._closed) {
|
|
2032
2032
|
throw new Error("WebSocket connection closed");
|
|
2033
2033
|
}
|
|
2034
|
-
return new Promise((
|
|
2034
|
+
return new Promise((resolve3, reject) => {
|
|
2035
2035
|
const timer = setTimeout(() => {
|
|
2036
|
-
const idx = this.resolveQueue.indexOf(
|
|
2036
|
+
const idx = this.resolveQueue.indexOf(resolve3);
|
|
2037
2037
|
if (idx !== -1) this.resolveQueue.splice(idx, 1);
|
|
2038
2038
|
reject(new Error(`WebSocket receive timed out after ${timeout ?? this._timeout}ms`));
|
|
2039
2039
|
}, timeout ?? this._timeout);
|
|
2040
2040
|
this.resolveQueue.push((msg) => {
|
|
2041
2041
|
clearTimeout(timer);
|
|
2042
|
-
|
|
2042
|
+
resolve3(msg);
|
|
2043
2043
|
});
|
|
2044
2044
|
});
|
|
2045
2045
|
}
|
|
@@ -2053,12 +2053,12 @@ var TestWSConnection = class {
|
|
|
2053
2053
|
* Useful for verifying that something did NOT happen.
|
|
2054
2054
|
*/
|
|
2055
2055
|
async expectSilent(ms) {
|
|
2056
|
-
return new Promise((
|
|
2056
|
+
return new Promise((resolve3, reject) => {
|
|
2057
2057
|
if (this.messageQueue.length > 0) {
|
|
2058
2058
|
reject(new Error(`Expected silence but got message: ${this.messageQueue[0].slice(0, 100)}`));
|
|
2059
2059
|
return;
|
|
2060
2060
|
}
|
|
2061
|
-
const timer = setTimeout(() =>
|
|
2061
|
+
const timer = setTimeout(() => resolve3(), ms);
|
|
2062
2062
|
const origPush = this.resolveQueue.push.bind(this.resolveQueue);
|
|
2063
2063
|
this.resolveQueue.push = (_fn) => {
|
|
2064
2064
|
clearTimeout(timer);
|
|
@@ -2460,8 +2460,8 @@ function postgres(opts) {
|
|
|
2460
2460
|
const stmtTimeout = options.statementTimeout ?? 3e4;
|
|
2461
2461
|
let connStr = typeof connection === "string" ? connection : "";
|
|
2462
2462
|
if (stmtTimeout > 0 && typeof connection === "string") {
|
|
2463
|
-
const
|
|
2464
|
-
connStr = `${connStr}${
|
|
2463
|
+
const sep2 = connStr.includes("?") ? "&" : "?";
|
|
2464
|
+
connStr = `${connStr}${sep2}options=-c%20statement_timeout%3D${stmtTimeout}`;
|
|
2465
2465
|
}
|
|
2466
2466
|
const sql = postgresFactory(connStr, {
|
|
2467
2467
|
max: options.max,
|
|
@@ -2554,7 +2554,7 @@ function redis(opts) {
|
|
|
2554
2554
|
|
|
2555
2555
|
// queue/index.ts
|
|
2556
2556
|
import { Redis as IORedis2 } from "ioredis";
|
|
2557
|
-
import
|
|
2557
|
+
import crypto3 from "node:crypto";
|
|
2558
2558
|
|
|
2559
2559
|
// queue/cron.ts
|
|
2560
2560
|
function parseField(field, min, max) {
|
|
@@ -2633,7 +2633,7 @@ function escapeIdent(s) {
|
|
|
2633
2633
|
function attachCron(q, handlers) {
|
|
2634
2634
|
;
|
|
2635
2635
|
q.cron = function(pattern, handler) {
|
|
2636
|
-
const id = "__cron_" + pattern.replace(/[^a-zA-Z0-9]/g, "_") + "_" +
|
|
2636
|
+
const id = "__cron_" + pattern.replace(/[^a-zA-Z0-9]/g, "_") + "_" + crypto3.randomUUID().slice(0, 8);
|
|
2637
2637
|
q.process(id, async () => {
|
|
2638
2638
|
await handler();
|
|
2639
2639
|
});
|
|
@@ -2674,7 +2674,7 @@ function createMemoryQueue(opts) {
|
|
|
2674
2674
|
try {
|
|
2675
2675
|
insertJob({
|
|
2676
2676
|
...job,
|
|
2677
|
-
id:
|
|
2677
|
+
id: crypto3.randomUUID(),
|
|
2678
2678
|
runAt: cronNext(job.schedule),
|
|
2679
2679
|
createdAt: Date.now()
|
|
2680
2680
|
});
|
|
@@ -2699,7 +2699,7 @@ function createMemoryQueue(opts) {
|
|
|
2699
2699
|
});
|
|
2700
2700
|
const q = mw;
|
|
2701
2701
|
mw.add = function add(type, payload, opts2) {
|
|
2702
|
-
const id =
|
|
2702
|
+
const id = crypto3.randomUUID();
|
|
2703
2703
|
let runAt;
|
|
2704
2704
|
if (opts2?.schedule) {
|
|
2705
2705
|
try {
|
|
@@ -2815,7 +2815,7 @@ function createPgQueue(opts) {
|
|
|
2815
2815
|
await sql.unsafe(
|
|
2816
2816
|
`INSERT INTO ${escapeIdent(table)} (id, type, payload, run_at, schedule) VALUES ($1, $2, $3::jsonb, $4, $5)`,
|
|
2817
2817
|
[
|
|
2818
|
-
|
|
2818
|
+
crypto3.randomUUID(),
|
|
2819
2819
|
job.type,
|
|
2820
2820
|
JSON.stringify(job.payload),
|
|
2821
2821
|
new Date(nextRun).toISOString(),
|
|
@@ -2865,7 +2865,7 @@ function createPgQueue(opts) {
|
|
|
2865
2865
|
const q = mw;
|
|
2866
2866
|
mw.add = function add(type, payload, opts2) {
|
|
2867
2867
|
return (async () => {
|
|
2868
|
-
const id =
|
|
2868
|
+
const id = crypto3.randomUUID();
|
|
2869
2869
|
let runAt;
|
|
2870
2870
|
if (opts2?.schedule) {
|
|
2871
2871
|
try {
|
|
@@ -2996,7 +2996,7 @@ function createRedisQueue(opts) {
|
|
|
2996
2996
|
nextRun,
|
|
2997
2997
|
JSON.stringify({
|
|
2998
2998
|
...job,
|
|
2999
|
-
id:
|
|
2999
|
+
id: crypto3.randomUUID(),
|
|
3000
3000
|
runAt: nextRun,
|
|
3001
3001
|
createdAt: Date.now()
|
|
3002
3002
|
})
|
|
@@ -3014,14 +3014,14 @@ function createRedisQueue(opts) {
|
|
|
3014
3014
|
while (running && inflight < MAX_CONCURRENT) {
|
|
3015
3015
|
const result = await redis2.zpopmin(jobKey);
|
|
3016
3016
|
if (result.length < 2) break;
|
|
3017
|
-
const
|
|
3017
|
+
const raw = result[0], score = parseInt(result[1], 10);
|
|
3018
3018
|
if (score > now) {
|
|
3019
|
-
await redis2.zadd(jobKey, score,
|
|
3019
|
+
await redis2.zadd(jobKey, score, raw);
|
|
3020
3020
|
break;
|
|
3021
3021
|
}
|
|
3022
3022
|
let job;
|
|
3023
3023
|
try {
|
|
3024
|
-
job = JSON.parse(
|
|
3024
|
+
job = JSON.parse(raw);
|
|
3025
3025
|
} catch {
|
|
3026
3026
|
continue;
|
|
3027
3027
|
}
|
|
@@ -3039,7 +3039,7 @@ function createRedisQueue(opts) {
|
|
|
3039
3039
|
});
|
|
3040
3040
|
const q = mw;
|
|
3041
3041
|
mw.add = function add(type, payload, opts2) {
|
|
3042
|
-
const id =
|
|
3042
|
+
const id = crypto3.randomUUID();
|
|
3043
3043
|
let runAt;
|
|
3044
3044
|
if (opts2?.schedule) {
|
|
3045
3045
|
runAt = cronNext(opts2.schedule);
|
|
@@ -3071,8 +3071,8 @@ function createRedisQueue(opts) {
|
|
|
3071
3071
|
redis2.disconnect();
|
|
3072
3072
|
};
|
|
3073
3073
|
mw.jobs = async function jobs(limit) {
|
|
3074
|
-
const
|
|
3075
|
-
return
|
|
3074
|
+
const raw = await redis2.zrevrange(jobKey, 0, (limit ?? 50) - 1);
|
|
3075
|
+
return raw.map((r) => {
|
|
3076
3076
|
try {
|
|
3077
3077
|
return JSON.parse(r);
|
|
3078
3078
|
} catch {
|
|
@@ -3081,8 +3081,8 @@ function createRedisQueue(opts) {
|
|
|
3081
3081
|
}).filter(Boolean);
|
|
3082
3082
|
};
|
|
3083
3083
|
mw.failedJobs = async function failedJobs(limit) {
|
|
3084
|
-
const
|
|
3085
|
-
return
|
|
3084
|
+
const raw = await redis2.lrange(failedKey, 0, (limit ?? 50) - 1);
|
|
3085
|
+
return raw.map((r) => {
|
|
3086
3086
|
try {
|
|
3087
3087
|
return JSON.parse(r);
|
|
3088
3088
|
} catch {
|
|
@@ -3091,8 +3091,8 @@ function createRedisQueue(opts) {
|
|
|
3091
3091
|
}).filter(Boolean);
|
|
3092
3092
|
};
|
|
3093
3093
|
mw.retryFailed = async function retryFailed(jobId) {
|
|
3094
|
-
const
|
|
3095
|
-
for (const entry of
|
|
3094
|
+
const raw = await redis2.lrange(failedKey, 0, -1);
|
|
3095
|
+
for (const entry of raw) {
|
|
3096
3096
|
try {
|
|
3097
3097
|
const job = JSON.parse(entry);
|
|
3098
3098
|
if (job.id === jobId) {
|
|
@@ -3111,8 +3111,8 @@ function createRedisQueue(opts) {
|
|
|
3111
3111
|
};
|
|
3112
3112
|
mw.retryAllFailed = async function retryAllFailed(type) {
|
|
3113
3113
|
let count = 0;
|
|
3114
|
-
const
|
|
3115
|
-
for (const entry of
|
|
3114
|
+
const raw = await redis2.lrange(failedKey, 0, -1);
|
|
3115
|
+
for (const entry of raw) {
|
|
3116
3116
|
try {
|
|
3117
3117
|
const job = JSON.parse(entry);
|
|
3118
3118
|
if (type && job.type !== type) continue;
|
|
@@ -3190,540 +3190,6 @@ function health(options) {
|
|
|
3190
3190
|
r.head(path, handler);
|
|
3191
3191
|
return r;
|
|
3192
3192
|
}
|
|
3193
|
-
|
|
3194
|
-
// middleware/theme.ts
|
|
3195
|
-
function makeSetTheme(cookie, location) {
|
|
3196
|
-
return (value, loc) => {
|
|
3197
|
-
const finalLoc = loc ?? location;
|
|
3198
|
-
const c = `${cookie}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`;
|
|
3199
|
-
return new Response(null, { status: 302, headers: { Location: finalLoc, "Set-Cookie": c } });
|
|
3200
|
-
};
|
|
3201
|
-
}
|
|
3202
|
-
function theme(options) {
|
|
3203
|
-
const opts = { default: "system", cookie: "theme", ...options };
|
|
3204
|
-
const mw = async (req, ctx, next) => {
|
|
3205
|
-
let themeValue = opts.default;
|
|
3206
|
-
if (opts.cookie) {
|
|
3207
|
-
const fromCookie = getCookies(req)[opts.cookie];
|
|
3208
|
-
if (fromCookie) themeValue = fromCookie;
|
|
3209
|
-
}
|
|
3210
|
-
;
|
|
3211
|
-
ctx.theme = {
|
|
3212
|
-
value: themeValue,
|
|
3213
|
-
set: makeSetTheme(opts.cookie, req.headers.get("referer") || "/")
|
|
3214
|
-
};
|
|
3215
|
-
return next(req, ctx);
|
|
3216
|
-
};
|
|
3217
|
-
mw.__meta = { injects: ["theme"], depends: [] };
|
|
3218
|
-
class ThemeRouter extends Router {
|
|
3219
|
-
middleware() {
|
|
3220
|
-
return mw;
|
|
3221
|
-
}
|
|
3222
|
-
}
|
|
3223
|
-
const router = new ThemeRouter();
|
|
3224
|
-
router.get("/__theme/:value", (req) => {
|
|
3225
|
-
const url = new URL(req.url);
|
|
3226
|
-
const value = url.pathname.split("/__theme/")[1] ?? "";
|
|
3227
|
-
const cookie = `${opts.cookie}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`;
|
|
3228
|
-
const accept = req.headers.get("accept") ?? "";
|
|
3229
|
-
if (accept.includes("application/json")) {
|
|
3230
|
-
return Response.json({ ok: true, theme: value }, { headers: { "Set-Cookie": cookie } });
|
|
3231
|
-
}
|
|
3232
|
-
const referer = req.headers.get("referer") || "/";
|
|
3233
|
-
return new Response(null, { status: 302, headers: { Location: referer, "Set-Cookie": cookie } });
|
|
3234
|
-
});
|
|
3235
|
-
return router;
|
|
3236
|
-
}
|
|
3237
|
-
|
|
3238
|
-
// middleware/i18n.ts
|
|
3239
|
-
import { readFile, stat } from "node:fs/promises";
|
|
3240
|
-
import { join as join2, resolve as resolve3 } from "node:path";
|
|
3241
|
-
var DEFAULTS2 = {
|
|
3242
|
-
default: "en",
|
|
3243
|
-
cookie: "locale",
|
|
3244
|
-
fromAcceptLanguage: true
|
|
3245
|
-
};
|
|
3246
|
-
function translate(msgs, key, params, fallback) {
|
|
3247
|
-
const msg = key.split(".").reduce((o, k) => o?.[k], msgs);
|
|
3248
|
-
if (msg === void 0 || msg === null) return fallback ?? key;
|
|
3249
|
-
if (!params) return String(msg);
|
|
3250
|
-
let result = String(msg);
|
|
3251
|
-
for (const [k, v] of Object.entries(params)) {
|
|
3252
|
-
result = result.replace(`{${k}}`, v);
|
|
3253
|
-
}
|
|
3254
|
-
return result;
|
|
3255
|
-
}
|
|
3256
|
-
function i18n(options) {
|
|
3257
|
-
const opts = { ...DEFAULTS2, ...options };
|
|
3258
|
-
const dir = opts.dir ? resolve3(opts.dir) : void 0;
|
|
3259
|
-
const cache = /* @__PURE__ */ new Map();
|
|
3260
|
-
function validLocale(locale) {
|
|
3261
|
-
return /^[\w-]+$/.test(locale) && !locale.includes("..");
|
|
3262
|
-
}
|
|
3263
|
-
async function loadMessages(locale) {
|
|
3264
|
-
if (opts.messages?.[locale] && Object.keys(opts.messages[locale]).length > 0) {
|
|
3265
|
-
cache.set(locale, opts.messages[locale]);
|
|
3266
|
-
return opts.messages[locale];
|
|
3267
|
-
}
|
|
3268
|
-
if (!dir || !validLocale(locale)) return {};
|
|
3269
|
-
const cached = cache.get(locale);
|
|
3270
|
-
if (cached) return cached;
|
|
3271
|
-
const filePath = join2(dir, `${locale}.json`);
|
|
3272
|
-
try {
|
|
3273
|
-
await stat(filePath);
|
|
3274
|
-
const content = await readFile(filePath, "utf-8");
|
|
3275
|
-
const data = JSON.parse(content);
|
|
3276
|
-
cache.set(locale, data);
|
|
3277
|
-
return data;
|
|
3278
|
-
} catch {
|
|
3279
|
-
}
|
|
3280
|
-
const short = locale.split("-")[0];
|
|
3281
|
-
if (short !== locale) {
|
|
3282
|
-
const fallback = cache.get(short) || await loadMessages(short);
|
|
3283
|
-
if (fallback && Object.keys(fallback).length > 0) {
|
|
3284
|
-
cache.set(locale, fallback);
|
|
3285
|
-
return fallback;
|
|
3286
|
-
}
|
|
3287
|
-
}
|
|
3288
|
-
return {};
|
|
3289
|
-
}
|
|
3290
|
-
function detectLocale(req) {
|
|
3291
|
-
if (opts.cookie) {
|
|
3292
|
-
const fromCookie = getCookies(req)[opts.cookie];
|
|
3293
|
-
if (fromCookie && validLocale(fromCookie)) return fromCookie;
|
|
3294
|
-
}
|
|
3295
|
-
if (opts.fromAcceptLanguage) {
|
|
3296
|
-
const fromHeader = req.headers.get("Accept-Language")?.split(",")[0]?.trim();
|
|
3297
|
-
if (fromHeader && validLocale(fromHeader)) return fromHeader;
|
|
3298
|
-
}
|
|
3299
|
-
return opts.default;
|
|
3300
|
-
}
|
|
3301
|
-
const mw = async (req, ctx, next) => {
|
|
3302
|
-
const locale = detectLocale(req);
|
|
3303
|
-
const msgs = await loadMessages(locale);
|
|
3304
|
-
ctx.i18n = {
|
|
3305
|
-
locale,
|
|
3306
|
-
messages: msgs,
|
|
3307
|
-
t: (key, params, fallback) => translate(msgs, key, params, fallback),
|
|
3308
|
-
set: (value, loc) => {
|
|
3309
|
-
const cookie = `${opts.cookie}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`;
|
|
3310
|
-
const location = loc ?? (req.headers.get("referer") || "/");
|
|
3311
|
-
return new Response(null, {
|
|
3312
|
-
status: 302,
|
|
3313
|
-
headers: { Location: location, "Set-Cookie": cookie }
|
|
3314
|
-
});
|
|
3315
|
-
}
|
|
3316
|
-
};
|
|
3317
|
-
return next(req, ctx);
|
|
3318
|
-
};
|
|
3319
|
-
mw.__meta = { injects: ["i18n"], depends: [] };
|
|
3320
|
-
class I18nRouter extends Router {
|
|
3321
|
-
middleware() {
|
|
3322
|
-
return mw;
|
|
3323
|
-
}
|
|
3324
|
-
}
|
|
3325
|
-
const router = new I18nRouter();
|
|
3326
|
-
router.get("/__lang/:locale", async (req) => {
|
|
3327
|
-
const url = new URL(req.url);
|
|
3328
|
-
const value = url.pathname.split("/__lang/")[1] ?? "";
|
|
3329
|
-
const cookie = `${opts.cookie}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`;
|
|
3330
|
-
const messages = await loadMessages(value);
|
|
3331
|
-
const accept = req.headers.get("accept") ?? "";
|
|
3332
|
-
if (accept.includes("application/json")) {
|
|
3333
|
-
return Response.json(
|
|
3334
|
-
{
|
|
3335
|
-
ok: true,
|
|
3336
|
-
locale: value,
|
|
3337
|
-
messages: Object.keys(messages).length > 0 ? messages : void 0
|
|
3338
|
-
},
|
|
3339
|
-
{ headers: { "Set-Cookie": cookie } }
|
|
3340
|
-
);
|
|
3341
|
-
}
|
|
3342
|
-
const referer = req.headers.get("referer") || "/";
|
|
3343
|
-
return new Response(null, { status: 302, headers: { Location: referer, "Set-Cookie": cookie } });
|
|
3344
|
-
});
|
|
3345
|
-
return router;
|
|
3346
|
-
}
|
|
3347
|
-
|
|
3348
|
-
// middleware/flash.ts
|
|
3349
|
-
function makeSetFlash(name, location) {
|
|
3350
|
-
return (data, loc) => {
|
|
3351
|
-
const finalLoc = loc ?? location;
|
|
3352
|
-
const value = encodeURIComponent(JSON.stringify(data));
|
|
3353
|
-
return new Response(null, {
|
|
3354
|
-
status: 302,
|
|
3355
|
-
headers: {
|
|
3356
|
-
Location: finalLoc,
|
|
3357
|
-
"Set-Cookie": `${name}=${value}; Path=/; SameSite=Lax`
|
|
3358
|
-
}
|
|
3359
|
-
});
|
|
3360
|
-
};
|
|
3361
|
-
}
|
|
3362
|
-
function flash(options) {
|
|
3363
|
-
const name = options?.name ?? "flash";
|
|
3364
|
-
const mw = async (req, ctx, next) => {
|
|
3365
|
-
const raw2 = getCookies(req)[name] ?? null;
|
|
3366
|
-
const referer = req.headers.get("referer") || "/";
|
|
3367
|
-
let value = void 0;
|
|
3368
|
-
if (raw2) {
|
|
3369
|
-
try {
|
|
3370
|
-
value = JSON.parse(decodeURIComponent(raw2));
|
|
3371
|
-
} catch {
|
|
3372
|
-
value = raw2;
|
|
3373
|
-
}
|
|
3374
|
-
}
|
|
3375
|
-
ctx.flash = {
|
|
3376
|
-
value,
|
|
3377
|
-
set: makeSetFlash(name, referer)
|
|
3378
|
-
};
|
|
3379
|
-
const res = await next(req, ctx);
|
|
3380
|
-
if (raw2) {
|
|
3381
|
-
const headers = new Headers(res.headers);
|
|
3382
|
-
headers.append("Set-Cookie", `${name}=; Path=/; Max-Age=0`);
|
|
3383
|
-
return new Response(res.body, { status: res.status, statusText: res.statusText, headers });
|
|
3384
|
-
}
|
|
3385
|
-
return res;
|
|
3386
|
-
};
|
|
3387
|
-
mw.__meta = { injects: ["flash"], depends: [] };
|
|
3388
|
-
return mw;
|
|
3389
|
-
}
|
|
3390
|
-
|
|
3391
|
-
// middleware/csrf.ts
|
|
3392
|
-
function csrf(options) {
|
|
3393
|
-
const cookieName = options?.cookie ?? "_csrf";
|
|
3394
|
-
const headerName = options?.header ?? "x-csrf-token";
|
|
3395
|
-
const bodyKey = options?.key ?? "_csrf";
|
|
3396
|
-
const excluded = new Set(options?.excludeMethods ?? ["GET", "HEAD", "OPTIONS"]);
|
|
3397
|
-
const mw = async (req, ctx, next) => {
|
|
3398
|
-
const method = req.method.toUpperCase();
|
|
3399
|
-
if (excluded.has(method)) {
|
|
3400
|
-
let token = getCookies(req)[cookieName];
|
|
3401
|
-
if (!token) {
|
|
3402
|
-
token = crypto.randomUUID();
|
|
3403
|
-
ctx.csrf = { token };
|
|
3404
|
-
} else {
|
|
3405
|
-
;
|
|
3406
|
-
ctx.csrf = { token };
|
|
3407
|
-
}
|
|
3408
|
-
const res = await next(req, ctx);
|
|
3409
|
-
const tokenToSet = ctx.csrf?.token;
|
|
3410
|
-
if (tokenToSet && !getCookies(req)[cookieName]) {
|
|
3411
|
-
return setCookie(res, cookieName, tokenToSet, {
|
|
3412
|
-
httpOnly: true,
|
|
3413
|
-
sameSite: "strict",
|
|
3414
|
-
path: "/"
|
|
3415
|
-
});
|
|
3416
|
-
}
|
|
3417
|
-
return res;
|
|
3418
|
-
}
|
|
3419
|
-
const cookieToken = getCookies(req)[cookieName];
|
|
3420
|
-
let headerToken = req.headers.get(headerName) ?? "";
|
|
3421
|
-
if (!headerToken && (req.method === "POST" || req.method === "PUT" || req.method === "PATCH" || req.method === "DELETE")) {
|
|
3422
|
-
try {
|
|
3423
|
-
const body = await req.clone().json();
|
|
3424
|
-
headerToken = body[bodyKey] ?? "";
|
|
3425
|
-
} catch (e) {
|
|
3426
|
-
return new Response("Invalid request body", { status: 400 });
|
|
3427
|
-
}
|
|
3428
|
-
}
|
|
3429
|
-
if (!cookieToken || !headerToken || cookieToken !== headerToken) {
|
|
3430
|
-
return new Response("CSRF token mismatch", { status: 403 });
|
|
3431
|
-
}
|
|
3432
|
-
return next(req, ctx);
|
|
3433
|
-
};
|
|
3434
|
-
mw.__meta = { injects: ["csrf"], depends: [] };
|
|
3435
|
-
return mw;
|
|
3436
|
-
}
|
|
3437
|
-
|
|
3438
|
-
// ssr/html.ts
|
|
3439
|
-
var ESCAPE_MAP = {
|
|
3440
|
-
"&": "&",
|
|
3441
|
-
"<": "<",
|
|
3442
|
-
">": ">",
|
|
3443
|
-
'"': """,
|
|
3444
|
-
"'": "'"
|
|
3445
|
-
};
|
|
3446
|
-
function escapeHtml(s) {
|
|
3447
|
-
const str = String(s);
|
|
3448
|
-
if (!/[&<>"']/.test(str)) return str;
|
|
3449
|
-
return str.replace(/[&<>"']/g, (c) => ESCAPE_MAP[c] || c);
|
|
3450
|
-
}
|
|
3451
|
-
function raw(s) {
|
|
3452
|
-
return {
|
|
3453
|
-
__brand: "RawString",
|
|
3454
|
-
value: s,
|
|
3455
|
-
toString() {
|
|
3456
|
-
return this.value;
|
|
3457
|
-
}
|
|
3458
|
-
};
|
|
3459
|
-
}
|
|
3460
|
-
function isRaw(v) {
|
|
3461
|
-
return typeof v === "object" && v !== null && "__brand" in v && v.__brand === "RawString";
|
|
3462
|
-
}
|
|
3463
|
-
function html(strings, ...values) {
|
|
3464
|
-
let result = "";
|
|
3465
|
-
for (let i = 0; i < strings.length; i++) {
|
|
3466
|
-
result += strings[i];
|
|
3467
|
-
if (i < values.length) {
|
|
3468
|
-
result += stringify(values[i]);
|
|
3469
|
-
}
|
|
3470
|
-
}
|
|
3471
|
-
return raw(result);
|
|
3472
|
-
}
|
|
3473
|
-
function stringify(v) {
|
|
3474
|
-
if (v === null || v === void 0 || v === false) return "";
|
|
3475
|
-
if (isRaw(v)) return v.value;
|
|
3476
|
-
if (Array.isArray(v)) {
|
|
3477
|
-
let out = "";
|
|
3478
|
-
for (let i = 0; i < v.length; i++) {
|
|
3479
|
-
out += stringify(v[i]);
|
|
3480
|
-
}
|
|
3481
|
-
return out;
|
|
3482
|
-
}
|
|
3483
|
-
if (typeof v === "number") return String(v);
|
|
3484
|
-
return escapeHtml(v);
|
|
3485
|
-
}
|
|
3486
|
-
|
|
3487
|
-
// ssr/layout.ts
|
|
3488
|
-
import { resolve as resolve4, isAbsolute } from "node:path";
|
|
3489
|
-
function layout(path) {
|
|
3490
|
-
const absPath = isAbsolute(path) ? path : resolve4(process.cwd(), path);
|
|
3491
|
-
let modPromise = null;
|
|
3492
|
-
async function getRenderFn() {
|
|
3493
|
-
if (!modPromise) {
|
|
3494
|
-
modPromise = import(absPath).catch((err) => {
|
|
3495
|
-
modPromise = null;
|
|
3496
|
-
throw new Error(
|
|
3497
|
-
`[layout] Failed to load layout module "${path}": ${err instanceof Error ? err.message : String(err)}`
|
|
3498
|
-
);
|
|
3499
|
-
});
|
|
3500
|
-
}
|
|
3501
|
-
const mod = await modPromise;
|
|
3502
|
-
const renderFn = mod.default;
|
|
3503
|
-
if (typeof renderFn !== "function") {
|
|
3504
|
-
throw new Error(
|
|
3505
|
-
`[layout] Layout module "${path}" must export a default function, got ${typeof renderFn}`
|
|
3506
|
-
);
|
|
3507
|
-
}
|
|
3508
|
-
return renderFn;
|
|
3509
|
-
}
|
|
3510
|
-
const mw = async (req, ctx, next) => {
|
|
3511
|
-
const renderFn = await getRenderFn();
|
|
3512
|
-
const response = await next(req, ctx);
|
|
3513
|
-
const ct = response.headers.get("content-type") ?? "";
|
|
3514
|
-
if (!ct.includes("text/html")) return response;
|
|
3515
|
-
const body = await response.text();
|
|
3516
|
-
const wrapped = await renderFn(body, ctx);
|
|
3517
|
-
return new Response(wrapped, {
|
|
3518
|
-
status: response.status,
|
|
3519
|
-
headers: { "content-type": "text/html; charset=utf-8" }
|
|
3520
|
-
});
|
|
3521
|
-
};
|
|
3522
|
-
mw.__meta = { injects: [], depends: [] };
|
|
3523
|
-
return mw;
|
|
3524
|
-
}
|
|
3525
|
-
|
|
3526
|
-
// ssr/compile.ts
|
|
3527
|
-
import { resolve as resolve5, isAbsolute as isAbsolute2 } from "node:path";
|
|
3528
|
-
var moduleCache = /* @__PURE__ */ new Map();
|
|
3529
|
-
var loading = /* @__PURE__ */ new Map();
|
|
3530
|
-
async function loadModule(path) {
|
|
3531
|
-
const absPath = isAbsolute2(path) ? path : resolve5(process.cwd(), path);
|
|
3532
|
-
const cached = moduleCache.get(absPath);
|
|
3533
|
-
if (cached) return cached;
|
|
3534
|
-
const inFlight = loading.get(absPath);
|
|
3535
|
-
if (inFlight) return inFlight;
|
|
3536
|
-
const promise = import(absPath).then((mod) => {
|
|
3537
|
-
loading.delete(absPath);
|
|
3538
|
-
moduleCache.set(absPath, Promise.resolve(mod));
|
|
3539
|
-
return mod;
|
|
3540
|
-
}).catch((err) => {
|
|
3541
|
-
loading.delete(absPath);
|
|
3542
|
-
moduleCache.delete(absPath);
|
|
3543
|
-
throw new Error(
|
|
3544
|
-
`[compile] Failed to load module "${path}": ${err instanceof Error ? err.message : String(err)}`
|
|
3545
|
-
);
|
|
3546
|
-
});
|
|
3547
|
-
loading.set(absPath, promise);
|
|
3548
|
-
return promise;
|
|
3549
|
-
}
|
|
3550
|
-
function clearModuleCache(path) {
|
|
3551
|
-
if (path) {
|
|
3552
|
-
const absPath = isAbsolute2(path) ? path : resolve5(process.cwd(), path);
|
|
3553
|
-
moduleCache.delete(absPath);
|
|
3554
|
-
loading.delete(absPath);
|
|
3555
|
-
} else {
|
|
3556
|
-
moduleCache.clear();
|
|
3557
|
-
loading.clear();
|
|
3558
|
-
}
|
|
3559
|
-
}
|
|
3560
|
-
|
|
3561
|
-
// ssr/view.ts
|
|
3562
|
-
function isRawString(v) {
|
|
3563
|
-
return typeof v === "object" && v !== null && "__brand" in v && v.__brand === "RawString";
|
|
3564
|
-
}
|
|
3565
|
-
function isResponse(v) {
|
|
3566
|
-
return v instanceof Response;
|
|
3567
|
-
}
|
|
3568
|
-
function view(path, options) {
|
|
3569
|
-
return async (req, ctx) => {
|
|
3570
|
-
let mod;
|
|
3571
|
-
if (options?.module) {
|
|
3572
|
-
mod = options.module;
|
|
3573
|
-
} else {
|
|
3574
|
-
mod = await loadModule(path);
|
|
3575
|
-
}
|
|
3576
|
-
const renderFn = mod.default;
|
|
3577
|
-
if (typeof renderFn !== "function") {
|
|
3578
|
-
throw new Error(
|
|
3579
|
-
`[view] Module "${path}" must export a default function, got ${typeof renderFn}`
|
|
3580
|
-
);
|
|
3581
|
-
}
|
|
3582
|
-
const result = renderFn.length >= 1 ? await renderFn(ctx) : await renderFn();
|
|
3583
|
-
if (isResponse(result)) return result;
|
|
3584
|
-
const body = isRawString(result) ? result.value : String(result);
|
|
3585
|
-
return new Response(body, {
|
|
3586
|
-
status: 200,
|
|
3587
|
-
headers: { "content-type": "text/html; charset=utf-8" }
|
|
3588
|
-
});
|
|
3589
|
-
};
|
|
3590
|
-
}
|
|
3591
|
-
|
|
3592
|
-
// ssr/css.ts
|
|
3593
|
-
import { createHash } from "node:crypto";
|
|
3594
|
-
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
3595
|
-
import { join as join3, resolve as resolve6, sep as sep2 } from "node:path";
|
|
3596
|
-
import tailwindPlugin from "@tailwindcss/postcss";
|
|
3597
|
-
import postcssNesting from "postcss-nesting";
|
|
3598
|
-
import postcss from "postcss";
|
|
3599
|
-
var cssCache = /* @__PURE__ */ new Map();
|
|
3600
|
-
function findTailwindDir() {
|
|
3601
|
-
const hoisted = resolve6(process.cwd(), "node_modules", "tailwindcss");
|
|
3602
|
-
if (existsSync(hoisted)) return hoisted;
|
|
3603
|
-
let dir = process.cwd();
|
|
3604
|
-
const root = sep2 === "/" ? "/" : "";
|
|
3605
|
-
while (dir !== root) {
|
|
3606
|
-
const wfwDir = resolve6(dir, "node_modules", "weifuwu");
|
|
3607
|
-
if (existsSync(wfwDir)) {
|
|
3608
|
-
const nested = resolve6(wfwDir, "node_modules", "tailwindcss");
|
|
3609
|
-
if (existsSync(nested)) return nested;
|
|
3610
|
-
const parent = resolve6(dir, "..", "node_modules", "tailwindcss");
|
|
3611
|
-
if (existsSync(parent)) return parent;
|
|
3612
|
-
}
|
|
3613
|
-
dir = resolve6(dir, "..");
|
|
3614
|
-
}
|
|
3615
|
-
return null;
|
|
3616
|
-
}
|
|
3617
|
-
async function compileCSS(cssPath, sourceDir) {
|
|
3618
|
-
if (!existsSync(cssPath)) {
|
|
3619
|
-
return { css: "", hash: "empty", url: "" };
|
|
3620
|
-
}
|
|
3621
|
-
let raw2 = readFileSync2(cssPath, "utf-8");
|
|
3622
|
-
const tailwindDir = findTailwindDir();
|
|
3623
|
-
if (tailwindDir && raw2.includes('@import "tailwindcss"')) {
|
|
3624
|
-
const tailwindCss = readFileSync2(join3(tailwindDir, "index.css"), "utf-8");
|
|
3625
|
-
raw2 = raw2.replace('@import "tailwindcss"', tailwindCss);
|
|
3626
|
-
}
|
|
3627
|
-
const src = `@source "${sourceDir}";
|
|
3628
|
-
${raw2}`;
|
|
3629
|
-
const result = await postcss([tailwindPlugin(), postcssNesting()]).process(src, { from: cssPath });
|
|
3630
|
-
const hash = createHash("md5").update(result.css).digest("hex").slice(0, 8);
|
|
3631
|
-
const asset = { css: result.css, hash, url: `/__wfw/style/${hash}.css` };
|
|
3632
|
-
cssCache.set(cssPath, asset);
|
|
3633
|
-
return asset;
|
|
3634
|
-
}
|
|
3635
|
-
function cssContext(dir) {
|
|
3636
|
-
const appDir = resolve6(dir, "app");
|
|
3637
|
-
const cssPath = join3(appDir, "globals.css");
|
|
3638
|
-
let cached = null;
|
|
3639
|
-
return async (req, ctx, next) => {
|
|
3640
|
-
if (!cached) cached = compileCSS(cssPath, appDir);
|
|
3641
|
-
const asset = await cached;
|
|
3642
|
-
if (asset.css) ctx.css = asset;
|
|
3643
|
-
return next(req, ctx);
|
|
3644
|
-
};
|
|
3645
|
-
}
|
|
3646
|
-
function cssRouter(dir) {
|
|
3647
|
-
const router = new Router();
|
|
3648
|
-
router.get("/__wfw/style/:hash.css", async () => {
|
|
3649
|
-
const cssPath = join3(resolve6(dir, "app"), "globals.css");
|
|
3650
|
-
const asset = cssCache.get(cssPath);
|
|
3651
|
-
if (!asset) return new Response("", { status: 404 });
|
|
3652
|
-
return new Response(asset.css, {
|
|
3653
|
-
headers: { "content-type": "text/css; charset=utf-8" }
|
|
3654
|
-
});
|
|
3655
|
-
});
|
|
3656
|
-
return router;
|
|
3657
|
-
}
|
|
3658
|
-
function clearCSSCache() {
|
|
3659
|
-
cssCache.clear();
|
|
3660
|
-
}
|
|
3661
|
-
|
|
3662
|
-
// ssr/ui/assets.ts
|
|
3663
|
-
import { readFileSync as readFileSync3 } from "node:fs";
|
|
3664
|
-
import { resolve as resolve7, dirname } from "node:path";
|
|
3665
|
-
import { fileURLToPath } from "node:url";
|
|
3666
|
-
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
3667
|
-
var wfuwVersion = (() => {
|
|
3668
|
-
try {
|
|
3669
|
-
const js = readFileSync3(resolve7(__dirname, "weifuwu-ui.js"), "utf-8");
|
|
3670
|
-
const m = js.match(/WFU_VERSION = '([^']+)'/);
|
|
3671
|
-
return m ? m[1] : "0.0.0";
|
|
3672
|
-
} catch {
|
|
3673
|
-
return "0.0.0";
|
|
3674
|
-
}
|
|
3675
|
-
})();
|
|
3676
|
-
function wfuwAssets() {
|
|
3677
|
-
const router = new Router();
|
|
3678
|
-
const jsPath = resolve7(__dirname, "weifuwu-ui.js");
|
|
3679
|
-
const cssPath = resolve7(__dirname, "weifuwu-ui.css");
|
|
3680
|
-
let jsContent = null;
|
|
3681
|
-
let cssContent = null;
|
|
3682
|
-
router.get("/__wfw/js/weifuwu-ui.js", () => {
|
|
3683
|
-
if (!jsContent) {
|
|
3684
|
-
try {
|
|
3685
|
-
jsContent = readFileSync3(jsPath, "utf-8");
|
|
3686
|
-
} catch {
|
|
3687
|
-
return new Response("weifuwu-ui.js not found", { status: 404 });
|
|
3688
|
-
}
|
|
3689
|
-
}
|
|
3690
|
-
return new Response(jsContent, {
|
|
3691
|
-
headers: {
|
|
3692
|
-
"content-type": "application/javascript; charset=utf-8",
|
|
3693
|
-
"cache-control": "public, max-age=31536000, immutable"
|
|
3694
|
-
}
|
|
3695
|
-
});
|
|
3696
|
-
});
|
|
3697
|
-
router.get("/__wfw/css/weifuwu-ui.css", () => {
|
|
3698
|
-
if (!cssContent) {
|
|
3699
|
-
try {
|
|
3700
|
-
cssContent = readFileSync3(cssPath, "utf-8");
|
|
3701
|
-
} catch {
|
|
3702
|
-
return new Response("weifuwu-ui.css not found", { status: 404 });
|
|
3703
|
-
}
|
|
3704
|
-
}
|
|
3705
|
-
return new Response(cssContent, {
|
|
3706
|
-
headers: {
|
|
3707
|
-
"content-type": "text/css; charset=utf-8",
|
|
3708
|
-
"cache-control": "public, max-age=31536000, immutable"
|
|
3709
|
-
}
|
|
3710
|
-
});
|
|
3711
|
-
});
|
|
3712
|
-
return router;
|
|
3713
|
-
}
|
|
3714
|
-
|
|
3715
|
-
// ssr/assets.ts
|
|
3716
|
-
function assetRouter() {
|
|
3717
|
-
console.warn("[weifuwu] assetRouter() is deprecated. Use wfuwAssets() instead.");
|
|
3718
|
-
return wfuwAssets();
|
|
3719
|
-
}
|
|
3720
|
-
function assetScripts() {
|
|
3721
|
-
console.warn("[weifuwu] assetScripts() is deprecated. Use weifuwu-ui.js directly.");
|
|
3722
|
-
return raw(`
|
|
3723
|
-
<script src="/__wfw/js/weifuwu-ui.js"></script>
|
|
3724
|
-
<link rel="stylesheet" href="/__wfw/css/weifuwu-ui.css">
|
|
3725
|
-
`);
|
|
3726
|
-
}
|
|
3727
3193
|
export {
|
|
3728
3194
|
DEFAULT_MAX_BODY,
|
|
3729
3195
|
HttpError,
|
|
@@ -3733,10 +3199,6 @@ export {
|
|
|
3733
3199
|
TestRequest,
|
|
3734
3200
|
aiProvider,
|
|
3735
3201
|
aiStream,
|
|
3736
|
-
assetRouter,
|
|
3737
|
-
assetScripts,
|
|
3738
|
-
clearCSSCache,
|
|
3739
|
-
clearModuleCache,
|
|
3740
3202
|
compress,
|
|
3741
3203
|
cors,
|
|
3742
3204
|
createHub,
|
|
@@ -3744,16 +3206,12 @@ export {
|
|
|
3744
3206
|
createSSEStream,
|
|
3745
3207
|
createTestDb,
|
|
3746
3208
|
createTestServer,
|
|
3747
|
-
csrf,
|
|
3748
|
-
cssContext,
|
|
3749
|
-
cssRouter,
|
|
3750
3209
|
currentTrace,
|
|
3751
3210
|
currentTraceId,
|
|
3752
3211
|
deleteCookie,
|
|
3753
3212
|
embed,
|
|
3754
3213
|
embedMany,
|
|
3755
3214
|
env,
|
|
3756
|
-
flash,
|
|
3757
3215
|
formatSSE,
|
|
3758
3216
|
formatSSEData,
|
|
3759
3217
|
generateObject,
|
|
@@ -3763,20 +3221,15 @@ export {
|
|
|
3763
3221
|
graphql,
|
|
3764
3222
|
health,
|
|
3765
3223
|
helmet,
|
|
3766
|
-
html,
|
|
3767
|
-
i18n,
|
|
3768
3224
|
isBundled,
|
|
3769
3225
|
isDev,
|
|
3770
3226
|
isProd,
|
|
3771
|
-
layout,
|
|
3772
3227
|
loadEnv,
|
|
3773
|
-
loadModule,
|
|
3774
3228
|
logger,
|
|
3775
3229
|
openai,
|
|
3776
3230
|
postgres,
|
|
3777
3231
|
queue,
|
|
3778
3232
|
rateLimit,
|
|
3779
|
-
raw,
|
|
3780
3233
|
redis,
|
|
3781
3234
|
requestId,
|
|
3782
3235
|
runWithTrace,
|
|
@@ -3787,14 +3240,10 @@ export {
|
|
|
3787
3240
|
streamObject,
|
|
3788
3241
|
streamText,
|
|
3789
3242
|
testApp,
|
|
3790
|
-
theme,
|
|
3791
3243
|
tool,
|
|
3792
3244
|
trace,
|
|
3793
3245
|
traceElapsed,
|
|
3794
3246
|
upload,
|
|
3795
3247
|
validate,
|
|
3796
|
-
view,
|
|
3797
|
-
wfuwAssets,
|
|
3798
|
-
wfuwVersion,
|
|
3799
3248
|
withTestDb
|
|
3800
3249
|
};
|