weifuwu 0.23.3 → 0.24.0

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.js CHANGED
@@ -5,8 +5,8 @@ var __export = (target, all) => {
5
5
  };
6
6
 
7
7
  // trace.ts
8
+ import crypto2 from "node:crypto";
8
9
  import { AsyncLocalStorage } from "node:async_hooks";
9
- import { randomUUID } from "node:crypto";
10
10
  var als = new AsyncLocalStorage();
11
11
  function currentTraceId() {
12
12
  return als.getStore()?.traceId;
@@ -15,7 +15,7 @@ function currentTrace() {
15
15
  return als.getStore();
16
16
  }
17
17
  function runWithTrace(incomingTraceId, fn) {
18
- const traceId = incomingTraceId || randomUUID();
18
+ const traceId = incomingTraceId || crypto2.randomUUID();
19
19
  const startTime = Date.now();
20
20
  return als.run({ traceId, startTime }, fn);
21
21
  }
@@ -24,10 +24,46 @@ function traceElapsed() {
24
24
  if (!ctx) return 0;
25
25
  return Date.now() - ctx.startTime;
26
26
  }
27
+ function trace(options) {
28
+ const header = options?.header ?? "X-Request-ID";
29
+ const gen = options?.generator ?? (() => crypto2.randomUUID());
30
+ return async (req, ctx, next) => {
31
+ const existing = req.headers.get(header);
32
+ const requestId2 = existing ?? gen();
33
+ const tc = als.getStore();
34
+ ctx.trace = {
35
+ requestId: requestId2,
36
+ traceId: tc?.traceId ?? requestId2,
37
+ startTime: tc?.startTime ?? Date.now(),
38
+ elapsed: () => {
39
+ const t = als.getStore();
40
+ return t ? Date.now() - t.startTime : 0;
41
+ }
42
+ };
43
+ const res = await next(req, ctx);
44
+ if (res.headers.has(header)) return res;
45
+ const h = new Headers(res.headers);
46
+ h.set(header, requestId2);
47
+ return new Response(res.body, { status: res.status, statusText: res.statusText, headers: h });
48
+ };
49
+ }
27
50
 
28
51
  // env.ts
29
52
  import { readFileSync } from "node:fs";
30
53
  import { resolve } from "node:path";
54
+ var PUBLIC_PREFIX = "WEIFUWU_PUBLIC_";
55
+ function getPublicEnv() {
56
+ const result = {};
57
+ for (const [key, value] of Object.entries(process.env)) {
58
+ if (key.startsWith(PUBLIC_PREFIX) && value !== void 0) {
59
+ result[key.slice(PUBLIC_PREFIX.length)] = value;
60
+ }
61
+ }
62
+ return result;
63
+ }
64
+ function isBundled() {
65
+ return true ? true : false;
66
+ }
31
67
  function isDev() {
32
68
  return process.env.NODE_ENV === "development";
33
69
  }
@@ -62,6 +98,14 @@ function loadEnv(path2) {
62
98
  process.env[key] = value;
63
99
  }
64
100
  }
101
+ function env() {
102
+ const entries = getPublicEnv();
103
+ return async (req, ctx, next) => {
104
+ ;
105
+ ctx.env = entries;
106
+ return next(req, ctx);
107
+ };
108
+ }
65
109
 
66
110
  // serve.ts
67
111
  import http from "node:http";
@@ -444,6 +488,10 @@ var Router = class _Router {
444
488
  }
445
489
  } else if (typeof arg1 === "function") {
446
490
  this.globalMws.push(arg1);
491
+ } else if (typeof arg1 === "object" && arg1 !== null && "middleware" in arg1 && typeof arg1.middleware === "function" && arg1 instanceof _Router) {
492
+ const mod = arg1;
493
+ this.globalMws.push(mod.middleware());
494
+ this._mountRouter("/", mod);
447
495
  }
448
496
  return this;
449
497
  }
@@ -988,114 +1036,6 @@ function cors(options) {
988
1036
  };
989
1037
  }
990
1038
 
991
- // auth.ts
992
- function auth(options) {
993
- if (!options.token && !options.verify && !options.proxy && !options.session) {
994
- throw new Error("auth() requires at least one of: token, verify, proxy, or session");
995
- }
996
- return async (req, ctx, next) => {
997
- if (options.session) {
998
- const sessionUserId = ctx.session?.userId;
999
- if (sessionUserId !== void 0 && sessionUserId !== null) {
1000
- if (options.resolveUser) {
1001
- const userData = await options.resolveUser(sessionUserId);
1002
- if (userData) {
1003
- ctx.user = userData;
1004
- return next(req, ctx);
1005
- }
1006
- if (typeof ctx.session?.destroy === "function") {
1007
- ;
1008
- ctx.session.destroy();
1009
- }
1010
- console.warn(`[${currentTraceId()}] auth: session userId ${sessionUserId} resolved to null`);
1011
- } else {
1012
- ctx.user = { id: sessionUserId };
1013
- return next(req, ctx);
1014
- }
1015
- }
1016
- }
1017
- const headerName = options.header ?? "Authorization";
1018
- let from = "header";
1019
- let header = req.headers.get(headerName);
1020
- let token = "";
1021
- if (header) {
1022
- token = header.trim();
1023
- if (headerName.toLowerCase() === "authorization") {
1024
- const parts = header.split(" ");
1025
- if (parts[0]?.toLowerCase() === "bearer") {
1026
- token = parts.slice(1).join(" ").trim();
1027
- }
1028
- }
1029
- } else if (!options.header) {
1030
- const url = new URL(req.url);
1031
- const qsToken = url.searchParams.get("access_token");
1032
- if (qsToken) {
1033
- token = qsToken;
1034
- from = "query";
1035
- }
1036
- }
1037
- if (!token) {
1038
- return new Response("Unauthorized", {
1039
- status: 401,
1040
- headers: headerName.toLowerCase() === "authorization" ? { "WWW-Authenticate": "Bearer" } : void 0
1041
- });
1042
- }
1043
- if (options.proxy) {
1044
- let proxyUrl;
1045
- try {
1046
- proxyUrl = typeof options.proxy === "string" ? new URL(options.proxy) : options.proxy;
1047
- } catch {
1048
- return new Response("Invalid proxy URL", { status: 500 });
1049
- }
1050
- const proxyHeaders = {};
1051
- if (from === "header" && header) {
1052
- proxyHeaders[headerName] = header;
1053
- } else {
1054
- proxyUrl.searchParams.set("access_token", token);
1055
- }
1056
- for (const name of ["x-forwarded-for", "x-real-ip", "user-agent", "content-type"]) {
1057
- const v = req.headers.get(name);
1058
- if (v) proxyHeaders[name] = v;
1059
- }
1060
- const proxyRes = await fetch(proxyUrl.href, { headers: proxyHeaders });
1061
- if (proxyRes.status >= 400) {
1062
- return new Response(await proxyRes.text() || "Forbidden", { status: proxyRes.status });
1063
- }
1064
- let userData = void 0;
1065
- if (proxyRes.status === 200) {
1066
- const ct = proxyRes.headers.get("content-type");
1067
- if (ct?.includes("application/json")) {
1068
- try {
1069
- userData = await proxyRes.json();
1070
- } catch {
1071
- }
1072
- }
1073
- }
1074
- ctx.user = userData;
1075
- return next(req, ctx);
1076
- }
1077
- if (options.token) {
1078
- if (token !== options.token) {
1079
- console.warn(`[${currentTraceId()}] auth: invalid static token`);
1080
- return new Response("Forbidden", { status: 403 });
1081
- }
1082
- return next(req, ctx);
1083
- }
1084
- if (options.verify) {
1085
- const result = await options.verify(token, req);
1086
- if (!result) {
1087
- console.warn(`[${currentTraceId()}] auth: verify failed for token`);
1088
- return new Response("Forbidden", { status: 403 });
1089
- }
1090
- if (typeof result === "object" && result !== null) {
1091
- ctx.user = result;
1092
- }
1093
- return next(req, ctx);
1094
- }
1095
- return next(req, ctx);
1096
- };
1097
- }
1098
-
1099
1039
  // static.ts
1100
1040
  import { open, realpath } from "node:fs/promises";
1101
1041
  import { extname, resolve as resolve2, normalize, sep } from "node:path";
@@ -1379,7 +1319,7 @@ function deleteCookie(res, name, options) {
1379
1319
 
1380
1320
  // upload.ts
1381
1321
  import { writeFile, mkdir } from "node:fs/promises";
1382
- import { randomUUID as randomUUID2 } from "node:crypto";
1322
+ import { randomUUID } from "node:crypto";
1383
1323
  import { join, extname as extname2 } from "node:path";
1384
1324
  var extensionMimeMap = {
1385
1325
  ".jpg": "image/jpeg",
@@ -1451,7 +1391,7 @@ function upload(options) {
1451
1391
  };
1452
1392
  if (saveDir) {
1453
1393
  const safeName = value.name.replace(/[/\\\0]/g, "_").replace(/\.\./g, "_");
1454
- const filePath = join(saveDir, `${randomUUID2()}-${safeName}`);
1394
+ const filePath = join(saveDir, `${randomUUID()}-${safeName}`);
1455
1395
  await writeFile(filePath, buf);
1456
1396
  uf.path = filePath;
1457
1397
  }
@@ -1548,10 +1488,11 @@ function rateLimit(options) {
1548
1488
  const res = await next(req, ctx);
1549
1489
  return addRateLimitHeaders(res, max, remaining, reset);
1550
1490
  };
1551
- mw.stop = () => {
1491
+ mw.close = () => {
1552
1492
  if (interval) clearInterval(interval);
1553
1493
  hits.clear();
1554
1494
  };
1495
+ mw.stop = mw.close;
1555
1496
  mw.stats = () => ({
1556
1497
  store: storeType,
1557
1498
  entries: storeType === "memory" ? hits.size : void 0,
@@ -1669,10 +1610,10 @@ var DEFAULTS = {
1669
1610
  };
1670
1611
 
1671
1612
  // request-id.ts
1672
- import crypto2 from "node:crypto";
1613
+ import crypto3 from "node:crypto";
1673
1614
  function requestId(options) {
1674
1615
  const header = options?.header ?? "X-Request-ID";
1675
- const gen = options?.generator ?? (() => crypto2.randomUUID());
1616
+ const gen = options?.generator ?? (() => crypto3.randomUUID());
1676
1617
  return async (req, ctx, next) => {
1677
1618
  const existing = req.headers.get(header);
1678
1619
  const id2 = existing ?? gen();
@@ -2422,7 +2363,7 @@ function aiProvider(options) {
2422
2363
  const client = createOpenAI({ baseURL, apiKey });
2423
2364
  let _model;
2424
2365
  let _embedModel;
2425
- return {
2366
+ const provider = {
2426
2367
  get dimension() {
2427
2368
  return dimension;
2428
2369
  },
@@ -2451,6 +2392,12 @@ function aiProvider(options) {
2451
2392
  return aiStreamText({ ...params, model: this.model() });
2452
2393
  }
2453
2394
  };
2395
+ const mw = async (req, ctx, next) => {
2396
+ ;
2397
+ ctx.ai = provider;
2398
+ return next(req, ctx);
2399
+ };
2400
+ return Object.assign(mw, provider);
2454
2401
  }
2455
2402
 
2456
2403
  // ai-sdk.ts
@@ -3123,7 +3070,7 @@ import jwt2 from "jsonwebtoken";
3123
3070
  import { z as z2 } from "zod";
3124
3071
 
3125
3072
  // user/oauth2.ts
3126
- import crypto3 from "node:crypto";
3073
+ import crypto4 from "node:crypto";
3127
3074
  import jwt from "jsonwebtoken";
3128
3075
  function createOAuth2Server(deps) {
3129
3076
  const { pg, users, jwtSecret, expiresIn } = deps;
@@ -3142,8 +3089,8 @@ function createOAuth2Server(deps) {
3142
3089
  };
3143
3090
  }
3144
3091
  async function registerClient(data) {
3145
- const clientId = crypto3.randomUUID();
3146
- const clientSecret = crypto3.randomBytes(32).toString("hex");
3092
+ const clientId = crypto4.randomUUID();
3093
+ const clientSecret = crypto4.randomBytes(32).toString("hex");
3147
3094
  const [row] = await pg.sql`
3148
3095
  INSERT INTO "_oauth2_clients" ("name", "client_id", "client_secret", "redirect_uris")
3149
3096
  VALUES (${data.name}, ${clientId}, ${clientSecret}, ${pg.sql.array(data.redirectUris)})
@@ -3291,7 +3238,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
3291
3238
  const loc2 = `${redirectUri}?error=access_denied${state ? `&state=${state}` : ""}`;
3292
3239
  return Response.redirect(loc2, 302);
3293
3240
  }
3294
- const code = crypto3.randomUUID();
3241
+ const code = crypto4.randomUUID();
3295
3242
  const expiresAt = new Date(Date.now() + 10 * 60 * 1e3);
3296
3243
  await pg.sql`
3297
3244
  INSERT INTO "_oauth2_codes" ("code", "client_id", "user_id", "redirect_uri", "code_challenge", "code_challenge_method", "scope", "expires_at")
@@ -3356,7 +3303,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
3356
3303
  if (stored.code_challenge_method === "plain") {
3357
3304
  expected = codeVerifier;
3358
3305
  } else {
3359
- expected = crypto3.createHash("sha256").update(codeVerifier).digest().toString("base64url");
3306
+ expected = crypto4.createHash("sha256").update(codeVerifier).digest().toString("base64url");
3360
3307
  }
3361
3308
  if (expected !== stored.code_challenge) {
3362
3309
  return Response.json({ error: "invalid_grant", error_description: "code_verifier mismatch" }, { status: 400 });
@@ -3373,7 +3320,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
3373
3320
  jwtSecret,
3374
3321
  { expiresIn }
3375
3322
  );
3376
- const refreshToken = crypto3.randomUUID();
3323
+ const refreshToken = crypto4.randomUUID();
3377
3324
  const refreshExpires = new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3);
3378
3325
  await pg.sql`
3379
3326
  INSERT INTO "_oauth2_tokens" ("token", "client_id", "user_id", "scope", "expires_at")
@@ -3411,7 +3358,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
3411
3358
  }
3412
3359
 
3413
3360
  // user/oauth-login.ts
3414
- import crypto4 from "node:crypto";
3361
+ import crypto5 from "node:crypto";
3415
3362
  var BUILTIN_PROVIDERS = {
3416
3363
  google: {
3417
3364
  authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
@@ -3523,7 +3470,7 @@ function registerOAuthLoginRoutes(router, deps, providers) {
3523
3470
  return Response.json({ error: `Unsupported provider: ${providerName}` }, { status: 400 });
3524
3471
  }
3525
3472
  const { config, meta } = resolved;
3526
- const state = crypto4.randomUUID();
3473
+ const state = crypto5.randomUUID();
3527
3474
  const redirectUri = new URL(req.url);
3528
3475
  redirectUri.pathname = redirectUri.pathname.replace(/\/[^/]+$/, "/") + providerName + "/callback";
3529
3476
  if (ctx.session) {
@@ -3654,14 +3601,40 @@ function verifyPassword(password, stored) {
3654
3601
  if (hash.length !== verify.length) return false;
3655
3602
  return timingSafeEqual(Buffer.from(hash), Buffer.from(verify));
3656
3603
  }
3604
+ function extractToken(req, headerName, cookieName) {
3605
+ const header = req.headers.get(headerName);
3606
+ if (header) {
3607
+ if (headerName.toLowerCase() === "authorization") {
3608
+ const parts = header.split(" ");
3609
+ if (parts[0]?.toLowerCase() === "bearer") {
3610
+ return parts.slice(1).join(" ").trim();
3611
+ }
3612
+ }
3613
+ return header.trim();
3614
+ }
3615
+ if (headerName.toLowerCase() === "authorization") {
3616
+ const url = new URL(req.url);
3617
+ const qsToken = url.searchParams.get("access_token");
3618
+ if (qsToken) return qsToken;
3619
+ }
3620
+ if (cookieName) {
3621
+ const cookies = req.headers.get("cookie")?.split(";").map((c) => c.trim()).filter(Boolean) || [];
3622
+ for (const c of cookies) {
3623
+ const eq2 = c.indexOf("=");
3624
+ if (eq2 > 0 && c.slice(0, eq2) === cookieName) return c.slice(eq2 + 1);
3625
+ }
3626
+ }
3627
+ return null;
3628
+ }
3657
3629
  function user(options) {
3630
+ const hasDb = !!options.pg;
3658
3631
  const table = options.table ?? "_users";
3659
3632
  const pg = options.pg;
3660
3633
  const secret = options.jwtSecret;
3661
3634
  const expiresIn = options.expiresIn ?? "24h";
3662
3635
  const oauth2Enabled = options.oauth2?.server ?? false;
3663
- const base = new PgModule(pg);
3664
- const users = pg.table(table, {
3636
+ const base = hasDb ? new PgModule(pg) : null;
3637
+ const users = hasDb ? pg.table(table, {
3665
3638
  id: serial("id").primaryKey(),
3666
3639
  email: text("email").unique().notNull(),
3667
3640
  password: text("password").notNull(),
@@ -3669,15 +3642,17 @@ function user(options) {
3669
3642
  role: text("role").default("user"),
3670
3643
  created_at: timestamptz("created_at").default(sql`NOW()`),
3671
3644
  updated_at: timestamptz("updated_at").default(sql`NOW()`)
3672
- });
3645
+ }) : null;
3646
+ const _pg = pg;
3647
+ const _users = users;
3673
3648
  let oauth2 = null;
3674
3649
  if (oauth2Enabled) {
3675
- oauth2 = createOAuth2Server({ pg, users, jwtSecret: secret, expiresIn });
3650
+ oauth2 = createOAuth2Server({ pg: _pg, users: _users, jwtSecret: secret, expiresIn });
3676
3651
  }
3677
3652
  async function migrate() {
3678
- await users.create();
3653
+ await _users.create();
3679
3654
  if (options.oauthLogin) {
3680
- await pg.sql.unsafe(`
3655
+ await _pg.sql.unsafe(`
3681
3656
  CREATE TABLE IF NOT EXISTS "_auth_providers" (
3682
3657
  id SERIAL PRIMARY KEY,
3683
3658
  user_id INTEGER NOT NULL REFERENCES ${escapeIdent2(table)}(id) ON DELETE CASCADE,
@@ -3690,13 +3665,13 @@ function user(options) {
3690
3665
  UNIQUE(provider, provider_id)
3691
3666
  )
3692
3667
  `);
3693
- await pg.sql.unsafe(`
3668
+ await _pg.sql.unsafe(`
3694
3669
  CREATE INDEX IF NOT EXISTS "_auth_providers_user_idx"
3695
3670
  ON "_auth_providers"(user_id)
3696
3671
  `);
3697
3672
  }
3698
3673
  if (!oauth2Enabled) return;
3699
- const clients3 = pg.table("_oauth2_clients", {
3674
+ const clients3 = _pg.table("_oauth2_clients", {
3700
3675
  id: serial("id").primaryKey(),
3701
3676
  name: text("name").notNull(),
3702
3677
  client_id: text("client_id").unique().notNull(),
@@ -3706,7 +3681,7 @@ function user(options) {
3706
3681
  created_at: timestamptz("created_at").default(sql`NOW()`)
3707
3682
  });
3708
3683
  await clients3.create();
3709
- const codes = pg.table("_oauth2_codes", {
3684
+ const codes = _pg.table("_oauth2_codes", {
3710
3685
  id: serial("id").primaryKey(),
3711
3686
  code: text("code").unique().notNull(),
3712
3687
  client_id: text("client_id").notNull(),
@@ -3719,7 +3694,7 @@ function user(options) {
3719
3694
  used: boolean_("used").default(false)
3720
3695
  });
3721
3696
  await codes.create();
3722
- const tokens = pg.table("_oauth2_tokens", {
3697
+ const tokens = _pg.table("_oauth2_tokens", {
3723
3698
  id: serial("id").primaryKey(),
3724
3699
  token: text("token").unique().notNull(),
3725
3700
  client_id: text("client_id").notNull(),
@@ -3742,15 +3717,15 @@ function user(options) {
3742
3717
  return user2;
3743
3718
  }
3744
3719
  async function findByEmail(email) {
3745
- const { data: rows } = await users.readMany({ email });
3720
+ const { data: rows } = await _users.readMany({ email });
3746
3721
  return rows[0];
3747
3722
  }
3748
3723
  async function findById(id2) {
3749
- return await users.read(id2);
3724
+ return await _users.read(id2);
3750
3725
  }
3751
3726
  async function createPlaceholderUser(email, name) {
3752
3727
  const randomPassword = randomBytes(32).toString("hex");
3753
- const row = await users.insert({ email, password: randomPassword, name });
3728
+ const row = await _users.insert({ email, password: randomPassword, name });
3754
3729
  return row;
3755
3730
  }
3756
3731
  async function register(data) {
@@ -3762,14 +3737,14 @@ function user(options) {
3762
3737
  throw err;
3763
3738
  }
3764
3739
  const hashed = hashPassword(password);
3765
- const row = await users.insert({ email, password: hashed, name });
3740
+ const row = await _users.insert({ email, password: hashed, name });
3766
3741
  const userData = row;
3767
3742
  const token = signToken(userData);
3768
3743
  return { user: stripPassword(userData), token };
3769
3744
  }
3770
3745
  async function login(data) {
3771
3746
  const { email, password } = LoginSchema.parse(data);
3772
- const { data: rows } = await users.readMany({ email });
3747
+ const { data: rows } = await _users.readMany({ email });
3773
3748
  const row = rows[0];
3774
3749
  if (!row) {
3775
3750
  const err = new Error("Invalid email or password");
@@ -3789,6 +3764,7 @@ function user(options) {
3789
3764
  try {
3790
3765
  const payload = jwt2.verify(token, secret);
3791
3766
  if (payload.token_type === "client_credentials") return null;
3767
+ if (!hasDb || !findById) return null;
3792
3768
  const row = await findById(payload.sub);
3793
3769
  if (!row) return null;
3794
3770
  return stripPassword(row);
@@ -3796,26 +3772,14 @@ function user(options) {
3796
3772
  return null;
3797
3773
  }
3798
3774
  }
3799
- function extractToken(req, cookieName) {
3800
- const header = req.headers.get("Authorization");
3801
- if (header?.startsWith("Bearer ")) return header.slice(7);
3802
- if (cookieName) {
3803
- const cookies = req.headers.get("cookie")?.split(";").map((c) => c.trim()).filter(Boolean) || [];
3804
- for (const c of cookies) {
3805
- const eq2 = c.indexOf("=");
3806
- if (eq2 > 0 && c.slice(0, eq2) === cookieName) return c.slice(eq2 + 1);
3807
- }
3808
- }
3809
- return null;
3810
- }
3811
- function middleware() {
3812
- return async (req, ctx, next) => {
3813
- const sessionUserId = ctx.session?.userId;
3814
- if (sessionUserId) {
3775
+ const headerName = options.header ?? "Authorization";
3776
+ async function resolveUser(req, ctx) {
3777
+ const sessionUserId = ctx.session?.userId;
3778
+ if (sessionUserId !== void 0 && sessionUserId !== null) {
3779
+ if (hasDb) {
3815
3780
  const row = await findById(sessionUserId);
3816
3781
  if (row) {
3817
- ctx.user = stripPassword(row);
3818
- return next(req, ctx);
3782
+ return stripPassword(row);
3819
3783
  }
3820
3784
  if (typeof ctx.session?.destroy === "function") {
3821
3785
  ;
@@ -3823,27 +3787,99 @@ function user(options) {
3823
3787
  } else {
3824
3788
  delete ctx.session?.userId;
3825
3789
  }
3826
- }
3827
- const token = extractToken(req);
3828
- if (token) {
3829
- const userData = await verify(token);
3790
+ } else if (options.resolveUser) {
3791
+ const userData = await options.resolveUser(sessionUserId);
3830
3792
  if (userData) {
3831
- ctx.user = userData;
3832
- return next(req, ctx);
3793
+ return userData;
3794
+ }
3795
+ if (typeof ctx.session?.destroy === "function") {
3796
+ ;
3797
+ ctx.session.destroy();
3798
+ }
3799
+ console.warn(`[${currentTraceId()}] user: session userId ${sessionUserId} resolved to null`);
3800
+ } else {
3801
+ return { id: sessionUserId };
3802
+ }
3803
+ }
3804
+ const token = extractToken(req, headerName);
3805
+ if (!token) return null;
3806
+ if (options.tokens?.length) {
3807
+ if (options.tokens.includes(token)) {
3808
+ return { id: token };
3809
+ }
3810
+ console.warn(`[${currentTraceId()}] user: invalid static token`);
3811
+ return null;
3812
+ }
3813
+ if (options.verify) {
3814
+ const result = await options.verify(token, req);
3815
+ if (!result) {
3816
+ console.warn(`[${currentTraceId()}] user: verify failed for token`);
3817
+ return null;
3818
+ }
3819
+ return result;
3820
+ }
3821
+ if (options.proxy) {
3822
+ let proxyUrl;
3823
+ try {
3824
+ proxyUrl = typeof options.proxy === "string" ? new URL(options.proxy) : options.proxy;
3825
+ } catch {
3826
+ return null;
3827
+ }
3828
+ const proxyHeaders = {};
3829
+ proxyHeaders[headerName] = req.headers.get(headerName) ?? `Bearer ${token}`;
3830
+ for (const name of ["x-forwarded-for", "x-real-ip", "user-agent", "content-type"]) {
3831
+ const v = req.headers.get(name);
3832
+ if (v) proxyHeaders[name] = v;
3833
+ }
3834
+ try {
3835
+ const proxyRes = await fetch(proxyUrl.href, { headers: proxyHeaders });
3836
+ if (proxyRes.status >= 400) {
3837
+ console.warn(`[${currentTraceId()}] user: proxy auth rejected (${proxyRes.status})`);
3838
+ return null;
3833
3839
  }
3840
+ const ct = proxyRes.headers.get("content-type");
3841
+ if (ct?.includes("application/json")) {
3842
+ try {
3843
+ return await proxyRes.json();
3844
+ } catch {
3845
+ }
3846
+ }
3847
+ return { id: token };
3848
+ } catch (err) {
3849
+ console.warn(`[${currentTraceId()}] user: proxy auth error: ${err}`);
3850
+ return null;
3851
+ }
3852
+ }
3853
+ if (secret && hasDb) {
3854
+ try {
3855
+ const payload = jwt2.verify(token, secret);
3856
+ if (payload.token_type === "client_credentials") return null;
3857
+ const row = await findById(payload.sub);
3858
+ if (row) return stripPassword(row);
3859
+ } catch {
3860
+ }
3861
+ return null;
3862
+ }
3863
+ return null;
3864
+ }
3865
+ function middleware() {
3866
+ return async (req, ctx, next) => {
3867
+ const userData = await resolveUser(req, ctx);
3868
+ if (userData) {
3869
+ ctx.user = userData;
3870
+ return next(req, ctx);
3834
3871
  }
3835
- return new Response("Unauthorized", { status: 401, headers: { "WWW-Authenticate": "Bearer" } });
3872
+ return new Response("Unauthorized", {
3873
+ status: 401,
3874
+ headers: headerName.toLowerCase() === "authorization" ? { "WWW-Authenticate": "Bearer" } : void 0
3875
+ });
3836
3876
  };
3837
3877
  }
3838
- function middlewareOptional(opts) {
3839
- const cookieName = opts?.cookie;
3878
+ function middlewareOptional(_opts) {
3840
3879
  return async (req, ctx, next) => {
3841
- const token = extractToken(req, cookieName);
3842
- if (token) {
3843
- const userData = await verify(token);
3844
- if (userData) {
3845
- ctx.user = userData;
3846
- }
3880
+ const userData = await resolveUser(req, ctx);
3881
+ if (userData) {
3882
+ ctx.user = userData;
3847
3883
  }
3848
3884
  return next(req, ctx);
3849
3885
  };
@@ -3860,9 +3896,9 @@ function user(options) {
3860
3896
  }
3861
3897
  return obj;
3862
3898
  }
3863
- function router() {
3864
- const r2 = new Router();
3865
- r2.post("/register", async (req) => {
3899
+ const r = new Router();
3900
+ if (hasDb) {
3901
+ r.post("/register", async (req) => {
3866
3902
  try {
3867
3903
  const body = await parseBody2(req);
3868
3904
  const result = await register(body);
@@ -3875,7 +3911,7 @@ function user(options) {
3875
3911
  return Response.json({ error: err.message }, { status });
3876
3912
  }
3877
3913
  });
3878
- r2.post("/login", async (req, ctx) => {
3914
+ r.post("/login", async (req, ctx) => {
3879
3915
  try {
3880
3916
  const body = await parseBody2(req);
3881
3917
  const result = await login(body);
@@ -3897,17 +3933,15 @@ function user(options) {
3897
3933
  return Response.json({ error: err.message }, { status });
3898
3934
  }
3899
3935
  });
3900
- if (oauth2) {
3901
- r2.get("/oauth/authorize", (req, ctx) => oauth2.authorizeHandler(req, ctx));
3902
- r2.post("/oauth/consent", (req) => oauth2.consentHandler(req));
3903
- r2.post("/oauth/token", (req) => oauth2.tokenHandler(req));
3904
- }
3905
- return r2;
3906
3936
  }
3907
- const r = router();
3908
- if (options.oauthLogin) {
3937
+ if (oauth2) {
3938
+ r.get("/oauth/authorize", (req, ctx) => oauth2.authorizeHandler(req, ctx));
3939
+ r.post("/oauth/consent", (req) => oauth2.consentHandler(req));
3940
+ r.post("/oauth/token", (req) => oauth2.tokenHandler(req));
3941
+ }
3942
+ if (hasDb && options.oauthLogin) {
3909
3943
  registerOAuthLoginRoutes(r, {
3910
- sql: pg.sql,
3944
+ sql: _pg.sql,
3911
3945
  jwtSecret: secret,
3912
3946
  expiresIn,
3913
3947
  usersTable: table,
@@ -3922,10 +3956,15 @@ function user(options) {
3922
3956
  const mod = r;
3923
3957
  mod.middleware = middleware;
3924
3958
  mod.middlewareOptional = middlewareOptional;
3925
- mod.migrate = migrate;
3926
- mod.register = register;
3927
- mod.login = login;
3928
- mod.verify = verify;
3959
+ mod.migrate = hasDb ? migrate : async () => {
3960
+ };
3961
+ mod.register = hasDb ? register : async () => {
3962
+ throw new Error("user(): pg required for register");
3963
+ };
3964
+ mod.login = hasDb ? login : async () => {
3965
+ throw new Error("user(): pg required for login");
3966
+ };
3967
+ mod.verify = hasDb ? verify : async () => null;
3929
3968
  mod.registerClient = oauth2 ? (data) => oauth2.registerClient(data) : async () => {
3930
3969
  throw new Error("OAuth2 server is not enabled");
3931
3970
  };
@@ -3935,7 +3974,8 @@ function user(options) {
3935
3974
  mod.revokeClient = oauth2 ? (clientId) => oauth2.revokeClient(clientId) : async () => {
3936
3975
  throw new Error("OAuth2 server is not enabled");
3937
3976
  };
3938
- mod.close = () => base.close();
3977
+ mod.close = hasDb ? () => base.close() : async () => {
3978
+ };
3939
3979
  return mod;
3940
3980
  }
3941
3981
 
@@ -3957,7 +3997,7 @@ function redis(opts) {
3957
3997
 
3958
3998
  // queue/index.ts
3959
3999
  import { Redis as IORedis2 } from "ioredis";
3960
- import crypto5 from "node:crypto";
4000
+ import crypto6 from "node:crypto";
3961
4001
 
3962
4002
  // cron-utils.ts
3963
4003
  function parseField(field, min, max) {
@@ -4043,7 +4083,7 @@ function escapeIdent3(s) {
4043
4083
  function attachCron(q, handlers) {
4044
4084
  ;
4045
4085
  q.cron = function(pattern, handler) {
4046
- const id2 = "__cron_" + pattern.replace(/[^a-zA-Z0-9]/g, "_") + "_" + crypto5.randomUUID().slice(0, 8);
4086
+ const id2 = "__cron_" + pattern.replace(/[^a-zA-Z0-9]/g, "_") + "_" + crypto6.randomUUID().slice(0, 8);
4047
4087
  q.process(id2, async () => {
4048
4088
  await handler();
4049
4089
  });
@@ -4082,7 +4122,7 @@ function createMemoryQueue(opts) {
4082
4122
  }
4083
4123
  if (job.schedule) {
4084
4124
  try {
4085
- insertJob({ ...job, id: crypto5.randomUUID(), runAt: cronNext(job.schedule), createdAt: Date.now() });
4125
+ insertJob({ ...job, id: crypto6.randomUUID(), runAt: cronNext(job.schedule), createdAt: Date.now() });
4086
4126
  } catch (e) {
4087
4127
  console.error("[queue] cron re-queue failed:", e.message);
4088
4128
  }
@@ -4104,7 +4144,7 @@ function createMemoryQueue(opts) {
4104
4144
  });
4105
4145
  const q = mw;
4106
4146
  mw.add = function add(type, payload, opts2) {
4107
- const id2 = crypto5.randomUUID();
4147
+ const id2 = crypto6.randomUUID();
4108
4148
  let runAt;
4109
4149
  if (opts2?.schedule) {
4110
4150
  try {
@@ -4206,7 +4246,7 @@ function createPgQueue(opts) {
4206
4246
  if (job.schedule) {
4207
4247
  try {
4208
4248
  const nextRun = cronNext(job.schedule);
4209
- await sql2.unsafe(`INSERT INTO ${escapeIdent3(table)} (id, type, payload, run_at, schedule) VALUES ($1, $2, $3::jsonb, $4, $5)`, [crypto5.randomUUID(), job.type, JSON.stringify(job.payload), new Date(nextRun).toISOString(), job.schedule]);
4249
+ await sql2.unsafe(`INSERT INTO ${escapeIdent3(table)} (id, type, payload, run_at, schedule) VALUES ($1, $2, $3::jsonb, $4, $5)`, [crypto6.randomUUID(), job.type, JSON.stringify(job.payload), new Date(nextRun).toISOString(), job.schedule]);
4210
4250
  } catch (e) {
4211
4251
  console.error("[queue] cron re-queue failed:", e.message);
4212
4252
  }
@@ -4240,7 +4280,7 @@ function createPgQueue(opts) {
4240
4280
  const q = mw;
4241
4281
  mw.add = function add(type, payload, opts2) {
4242
4282
  return (async () => {
4243
- const id2 = crypto5.randomUUID();
4283
+ const id2 = crypto6.randomUUID();
4244
4284
  let runAt;
4245
4285
  if (opts2?.schedule) {
4246
4286
  try {
@@ -4327,7 +4367,7 @@ function createRedisQueue(opts) {
4327
4367
  if (job.schedule) {
4328
4368
  try {
4329
4369
  const nextRun = cronNext(job.schedule);
4330
- await redis2.zadd(jobKey, nextRun, JSON.stringify({ ...job, id: crypto5.randomUUID(), runAt: nextRun, createdAt: Date.now() }));
4370
+ await redis2.zadd(jobKey, nextRun, JSON.stringify({ ...job, id: crypto6.randomUUID(), runAt: nextRun, createdAt: Date.now() }));
4331
4371
  } catch (e) {
4332
4372
  console.error("[queue] cron re-queue failed:", e.message);
4333
4373
  }
@@ -4366,7 +4406,7 @@ function createRedisQueue(opts) {
4366
4406
  });
4367
4407
  const q = mw;
4368
4408
  mw.add = function add(type, payload, opts2) {
4369
- const id2 = crypto5.randomUUID();
4409
+ const id2 = crypto6.randomUUID();
4370
4410
  let runAt;
4371
4411
  if (opts2?.schedule) {
4372
4412
  runAt = cronNext(opts2.schedule);
@@ -6291,7 +6331,7 @@ function createGateway(config, getPort) {
6291
6331
  }
6292
6332
 
6293
6333
  // deploy/manager.ts
6294
- import crypto6 from "node:crypto";
6334
+ import crypto7 from "node:crypto";
6295
6335
 
6296
6336
  // deploy/process.ts
6297
6337
  import { fork } from "node:child_process";
@@ -6344,27 +6384,27 @@ async function healthCheck(port, path2 = "/") {
6344
6384
  // deploy/manager.ts
6345
6385
  function createManager(config, apps, manager) {
6346
6386
  const router = new Router();
6347
- const auth2 = (req, ctx, next) => {
6387
+ const auth = (req, ctx, next) => {
6348
6388
  if (!config.deployToken) return next(req, ctx);
6349
6389
  const header = req.headers.get("authorization") ?? "";
6350
6390
  const token = header.replace("Bearer ", "");
6351
6391
  const tokenBuf = Buffer.from(token);
6352
6392
  const secretBuf = Buffer.from(config.deployToken);
6353
- if (tokenBuf.length !== secretBuf.length || !crypto6.timingSafeEqual(tokenBuf, secretBuf)) {
6393
+ if (tokenBuf.length !== secretBuf.length || !crypto7.timingSafeEqual(tokenBuf, secretBuf)) {
6354
6394
  return Response.json({ error: "Unauthorized" }, { status: 401 });
6355
6395
  }
6356
6396
  return next(req, ctx);
6357
6397
  };
6358
- router.get("/apps", auth2, () => {
6398
+ router.get("/apps", auth, () => {
6359
6399
  const list = Array.from(apps.values()).map((a) => a.status);
6360
6400
  return Response.json(list);
6361
6401
  });
6362
- router.get("/apps/:name", auth2, (req, ctx) => {
6402
+ router.get("/apps/:name", auth, (req, ctx) => {
6363
6403
  const app = apps.get(ctx.params.name);
6364
6404
  if (!app) return new Response("Not Found", { status: 404 });
6365
6405
  return Response.json(app.status);
6366
6406
  });
6367
- router.post("/apps/:name/deploy", auth2, async (req, ctx) => {
6407
+ router.post("/apps/:name/deploy", auth, async (req, ctx) => {
6368
6408
  const app = apps.get(ctx.params.name);
6369
6409
  if (!app) return new Response("Not Found", { status: 404 });
6370
6410
  try {
@@ -6375,7 +6415,7 @@ function createManager(config, apps, manager) {
6375
6415
  return Response.json({ error: msg }, { status: 500 });
6376
6416
  }
6377
6417
  });
6378
- router.post("/apps/:name/restart", auth2, async (req, ctx) => {
6418
+ router.post("/apps/:name/restart", auth, async (req, ctx) => {
6379
6419
  const app = apps.get(ctx.params.name);
6380
6420
  if (!app) return new Response("Not Found", { status: 404 });
6381
6421
  try {
@@ -6386,7 +6426,7 @@ function createManager(config, apps, manager) {
6386
6426
  return Response.json({ error: msg }, { status: 500 });
6387
6427
  }
6388
6428
  });
6389
- router.post("/apps/:name/stop", auth2, async (req, ctx) => {
6429
+ router.post("/apps/:name/stop", auth, async (req, ctx) => {
6390
6430
  const app = apps.get(ctx.params.name);
6391
6431
  if (!app) return new Response("Not Found", { status: 404 });
6392
6432
  if (app.process) {
@@ -6396,7 +6436,7 @@ function createManager(config, apps, manager) {
6396
6436
  app.status = { ...app.status, status: "stopped", pid: void 0 };
6397
6437
  return Response.json({ success: true });
6398
6438
  });
6399
- router.post("/apps/:name/start", auth2, async (req, ctx) => {
6439
+ router.post("/apps/:name/start", auth, async (req, ctx) => {
6400
6440
  const app = apps.get(ctx.params.name);
6401
6441
  if (!app) return new Response("Not Found", { status: 404 });
6402
6442
  try {
@@ -6407,7 +6447,7 @@ function createManager(config, apps, manager) {
6407
6447
  return Response.json({ error: msg }, { status: 500 });
6408
6448
  }
6409
6449
  });
6410
- router.get("/apps/:name/logs", auth2, (req, ctx) => {
6450
+ router.get("/apps/:name/logs", auth, (req, ctx) => {
6411
6451
  const app = apps.get(ctx.params.name);
6412
6452
  if (!app) return new Response("Not Found", { status: 404 });
6413
6453
  let index = app.logs.length;
@@ -6459,9 +6499,9 @@ function defineConfig(config) {
6459
6499
  async function deploy(config) {
6460
6500
  const apps = /* @__PURE__ */ new Map();
6461
6501
  let httpServer;
6462
- async function forkAndCheck(name, cwd, entry, port, env, onLog, healthEndpoint) {
6502
+ async function forkAndCheck(name, cwd, entry, port, env2, onLog, healthEndpoint) {
6463
6503
  try {
6464
- const mp = forkApp({ cwd, entry, port, env, onLog });
6504
+ const mp = forkApp({ cwd, entry, port, env: env2, onLog });
6465
6505
  onLog(`[deploy] forked ${name} (pid ${mp.child.pid}) on port ${mp.port}`);
6466
6506
  const healthy = await healthCheck(port, healthEndpoint ?? "/");
6467
6507
  if (healthy) onLog(`[deploy] health check passed`);
@@ -6778,13 +6818,24 @@ async function compileVendorBundle() {
6778
6818
  const keys = Object.keys(mod).filter((k) => !k.startsWith("_") && k !== "default");
6779
6819
  modules[request] = keys;
6780
6820
  }
6781
- const reactTsPath = resolve3(import.meta.dirname ?? __dirname, "react.ts");
6782
- const reactSrc = readFileSync2(reactTsPath, "utf-8");
6821
+ const baseDir = import.meta.dirname ?? __dirname;
6822
+ const reactAbsPath = isBundled() ? resolve3(baseDir, "react.js") : resolve3(baseDir, "react.ts");
6823
+ const reactSrc = readFileSync2(reactAbsPath, "utf-8");
6783
6824
  const wfwKeys = [];
6784
- for (const line of reactSrc.split("\n")) {
6785
- const m = line.match(/^export\s+\{[^}]+\}\s*from/);
6786
- if (m) {
6787
- const names = line.slice(line.indexOf("{") + 1, line.indexOf("}")).split(",").map((s) => s.trim()).filter(Boolean);
6825
+ if (reactAbsPath.endsWith(".ts")) {
6826
+ for (const line of reactSrc.split("\n")) {
6827
+ const m = line.match(/^export\s+\{[^}]+\}\s*from/);
6828
+ if (m) {
6829
+ const names = line.slice(line.indexOf("{") + 1, line.indexOf("}")).split(",").map((s) => s.trim()).filter(Boolean);
6830
+ for (const n of names) {
6831
+ if (!n.startsWith("type ") && !wfwKeys.includes(n)) wfwKeys.push(n);
6832
+ }
6833
+ }
6834
+ }
6835
+ } else {
6836
+ const exportMatch = reactSrc.match(/\bexport\s*\{([^}]+)\}\s*;/);
6837
+ if (exportMatch) {
6838
+ const names = exportMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
6788
6839
  for (const n of names) {
6789
6840
  if (!n.startsWith("type ") && !wfwKeys.includes(n)) wfwKeys.push(n);
6790
6841
  }
@@ -6797,7 +6848,7 @@ async function compileVendorBundle() {
6797
6848
  if (unique.length > 0) stmts.push(`export { ${unique.join(", ")} } from ${JSON.stringify(request)};`);
6798
6849
  }
6799
6850
  const uidWfw = wfwKeys.filter((k) => !used.has(k) && used.add(k));
6800
- if (uidWfw.length > 0) stmts.push(`export { ${uidWfw.join(", ")} } from ${JSON.stringify(reactTsPath)};`);
6851
+ if (uidWfw.length > 0) stmts.push(`export { ${uidWfw.join(", ")} } from ${JSON.stringify(reactAbsPath)};`);
6801
6852
  const result = await esbuild.build({
6802
6853
  stdin: { contents: stmts.join("\n"), resolveDir: process.cwd() },
6803
6854
  format: "esm",
@@ -6889,7 +6940,7 @@ async function compileHotComponent(path2) {
6889
6940
  // stream.ts
6890
6941
  import { TextDecoder as TextDecoder2, TextEncoder as TextEncoder2 } from "node:util";
6891
6942
  var _publicEnv = null;
6892
- function getPublicEnv() {
6943
+ function getPublicEnv2() {
6893
6944
  if (_publicEnv) return _publicEnv;
6894
6945
  _publicEnv = {};
6895
6946
  for (const key of Object.keys(process.env)) {
@@ -6940,7 +6991,7 @@ function buildHeadPayload(opts) {
6940
6991
  }
6941
6992
  ctxData.user = safeUser;
6942
6993
  }
6943
- const publicEnv = getPublicEnv();
6994
+ const publicEnv = getPublicEnv2();
6944
6995
  if (Object.keys(publicEnv).length > 0) {
6945
6996
  ctxData.env = publicEnv;
6946
6997
  }
@@ -7648,7 +7699,7 @@ function ssr(opts) {
7648
7699
  }
7649
7700
 
7650
7701
  // opencode/session.ts
7651
- import { randomUUID as randomUUID3 } from "node:crypto";
7702
+ import { randomUUID as randomUUID2 } from "node:crypto";
7652
7703
  import { join as join6 } from "node:path";
7653
7704
  import { mkdir as mkdir2 } from "node:fs/promises";
7654
7705
  var sessions = pgTable("_opencode_sessions", {
@@ -7674,7 +7725,7 @@ var messages = pgTable("_opencode_messages", {
7674
7725
  created_at: timestamptz("created_at")
7675
7726
  });
7676
7727
  async function createSession(sql2, opts, cwd, mountPath) {
7677
- const id2 = randomUUID3();
7728
+ const id2 = randomUUID2();
7678
7729
  const ws = computeSessionWorkspace(cwd, mountPath, id2);
7679
7730
  await mkdir2(ws, { recursive: true });
7680
7731
  const [row] = await sql2`
@@ -8846,30 +8897,37 @@ function makeSetTheme(cookie, location) {
8846
8897
  }
8847
8898
  function theme(options) {
8848
8899
  const opts = { default: "system", cookie: "theme", ...options };
8849
- return async (req, ctx, next) => {
8850
- const url = new URL(req.url);
8851
- const match = url.pathname.match(/^\/__theme\/([\w-]+)$/);
8852
- if (match && req.method === "GET") {
8853
- const value = match[1];
8854
- const cookie = `${opts.cookie}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`;
8855
- const accept = req.headers.get("accept") ?? "";
8856
- if (accept.includes("application/json")) {
8857
- return Response.json({ ok: true, theme: value }, { headers: { "Set-Cookie": cookie } });
8858
- }
8859
- const referer = req.headers.get("referer") || "/";
8860
- return new Response(null, { status: 302, headers: { Location: referer, "Set-Cookie": cookie } });
8861
- }
8900
+ const mw = async (req, ctx, next) => {
8862
8901
  let themeValue = opts.default;
8863
8902
  if (opts.cookie) {
8864
8903
  const fromCookie = getCookies(req)[opts.cookie];
8865
8904
  if (fromCookie) themeValue = fromCookie;
8866
8905
  }
8906
+ ;
8867
8907
  ctx.theme = {
8868
8908
  value: themeValue,
8869
8909
  set: makeSetTheme(opts.cookie, req.headers.get("referer") || "/")
8870
8910
  };
8871
8911
  return next(req, ctx);
8872
8912
  };
8913
+ class ThemeRouter extends Router {
8914
+ middleware() {
8915
+ return mw;
8916
+ }
8917
+ }
8918
+ const router = new ThemeRouter();
8919
+ router.get("/__theme/:value", (req) => {
8920
+ const url = new URL(req.url);
8921
+ const value = url.pathname.split("/__theme/")[1] ?? "";
8922
+ const cookie = `${opts.cookie}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`;
8923
+ const accept = req.headers.get("accept") ?? "";
8924
+ if (accept.includes("application/json")) {
8925
+ return Response.json({ ok: true, theme: value }, { headers: { "Set-Cookie": cookie } });
8926
+ }
8927
+ const referer = req.headers.get("referer") || "/";
8928
+ return new Response(null, { status: 302, headers: { Location: referer, "Set-Cookie": cookie } });
8929
+ });
8930
+ return router;
8873
8931
  }
8874
8932
 
8875
8933
  // i18n.ts
@@ -8935,23 +8993,7 @@ function i18n(options) {
8935
8993
  }
8936
8994
  return opts.default;
8937
8995
  }
8938
- return async (req, ctx, next) => {
8939
- const url = new URL(req.url);
8940
- const match = url.pathname.match(/^\/__lang\/([\w-]+)$/);
8941
- if (match && req.method === "GET") {
8942
- const value = match[1];
8943
- const cookie = `${opts.cookie}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`;
8944
- const messages2 = await loadMessages(value);
8945
- const accept = req.headers.get("accept") ?? "";
8946
- if (accept.includes("application/json")) {
8947
- return Response.json(
8948
- { ok: true, locale: value, messages: Object.keys(messages2).length > 0 ? messages2 : void 0 },
8949
- { headers: { "Set-Cookie": cookie } }
8950
- );
8951
- }
8952
- const referer = req.headers.get("referer") || "/";
8953
- return new Response(null, { status: 302, headers: { Location: referer, "Set-Cookie": cookie } });
8954
- }
8996
+ const mw = async (req, ctx, next) => {
8955
8997
  const locale = detectLocale(req);
8956
8998
  const msgs = await loadMessages(locale);
8957
8999
  ctx.i18n = {
@@ -8966,6 +9008,28 @@ function i18n(options) {
8966
9008
  };
8967
9009
  return next(req, ctx);
8968
9010
  };
9011
+ class I18nRouter extends Router {
9012
+ middleware() {
9013
+ return mw;
9014
+ }
9015
+ }
9016
+ const router = new I18nRouter();
9017
+ router.get("/__lang/:locale", async (req) => {
9018
+ const url = new URL(req.url);
9019
+ const value = url.pathname.split("/__lang/")[1] ?? "";
9020
+ const cookie = `${opts.cookie}=${encodeURIComponent(value)}; Path=/; SameSite=Lax`;
9021
+ const messages2 = await loadMessages(value);
9022
+ const accept = req.headers.get("accept") ?? "";
9023
+ if (accept.includes("application/json")) {
9024
+ return Response.json(
9025
+ { ok: true, locale: value, messages: Object.keys(messages2).length > 0 ? messages2 : void 0 },
9026
+ { headers: { "Set-Cookie": cookie } }
9027
+ );
9028
+ }
9029
+ const referer = req.headers.get("referer") || "/";
9030
+ return new Response(null, { status: 302, headers: { Location: referer, "Set-Cookie": cookie } });
9031
+ });
9032
+ return router;
8969
9033
  }
8970
9034
 
8971
9035
  // flash.ts
@@ -9195,13 +9259,13 @@ function csrf(options) {
9195
9259
  let token = getCookies(req)[cookieName];
9196
9260
  if (!token) {
9197
9261
  token = crypto.randomUUID();
9198
- ctx.csrfToken = token;
9262
+ ctx.csrf = { token };
9199
9263
  } else {
9200
9264
  ;
9201
- ctx.csrfToken = token;
9265
+ ctx.csrf = { token };
9202
9266
  }
9203
9267
  const res = await next(req, ctx);
9204
- const tokenToSet = ctx.csrfToken;
9268
+ const tokenToSet = ctx.csrf?.token;
9205
9269
  if (tokenToSet && !getCookies(req)[cookieName]) {
9206
9270
  return setCookie(res, cookieName, tokenToSet, {
9207
9271
  httpOnly: true,
@@ -9386,7 +9450,7 @@ function logdb(options) {
9386
9450
  }
9387
9451
 
9388
9452
  // iii/client.ts
9389
- import crypto7 from "node:crypto";
9453
+ import crypto8 from "node:crypto";
9390
9454
 
9391
9455
  // iii/stream.ts
9392
9456
  function notify(channels, stream, group, item, event, data) {
@@ -9915,7 +9979,7 @@ function iii(opts = {}) {
9915
9979
  registerBuiltin("stream::send", (p) => stream.send(p.stream_name, p.group_id, p.type, p.data, p.id));
9916
9980
  registerBuiltin("stream::update", (p) => stream.update(p.stream_name, p.group_id, p.item_id, p.ops));
9917
9981
  function addLocalWorker(worker) {
9918
- const workerId = crypto7.randomUUID();
9982
+ const workerId = crypto8.randomUUID();
9919
9983
  const reg = {
9920
9984
  id: workerId,
9921
9985
  name: worker.name,
@@ -9930,7 +9994,7 @@ function iii(opts = {}) {
9930
9994
  const triggerIds = [];
9931
9995
  for (const t of worker.getTriggers()) {
9932
9996
  if (t.input.function_id === fn.id) {
9933
- const tid = crypto7.randomUUID();
9997
+ const tid = crypto8.randomUUID();
9934
9998
  triggers.set(tid, {
9935
9999
  id: tid,
9936
10000
  type: t.input.type,
@@ -9959,7 +10023,7 @@ function iii(opts = {}) {
9959
10023
  if (!worker) return;
9960
10024
  const handler = async (payload) => {
9961
10025
  if (!worker.ws) throw new Error(`Worker "${worker.name}" disconnected`);
9962
- const invocationId = crypto7.randomUUID();
10026
+ const invocationId = crypto8.randomUUID();
9963
10027
  return new Promise((resolve14, reject) => {
9964
10028
  const timer = setTimeout(() => {
9965
10029
  pending.delete(invocationId);
@@ -9994,7 +10058,7 @@ function iii(opts = {}) {
9994
10058
  let engineRef = null;
9995
10059
  const wsHandler = createWsHandler({
9996
10060
  registerRemoteWorker(ws, name) {
9997
- const id2 = crypto7.randomUUID();
10061
+ const id2 = crypto8.randomUUID();
9998
10062
  workers.set(id2, { id: id2, name, ws, functions: [], triggers: [] });
9999
10063
  return id2;
10000
10064
  },
@@ -10005,7 +10069,7 @@ function iii(opts = {}) {
10005
10069
  addRemoteFunction(workerId, id2);
10006
10070
  },
10007
10071
  registerRemoteTrigger(workerId, input) {
10008
- const tid = crypto7.randomUUID();
10072
+ const tid = crypto8.randomUUID();
10009
10073
  const reg = { id: tid, ...input, workerId };
10010
10074
  triggers.set(tid, reg);
10011
10075
  const worker = workers.get(workerId);
@@ -10141,6 +10205,7 @@ function iii(opts = {}) {
10141
10205
  triggers.clear();
10142
10206
  await stream.close();
10143
10207
  };
10208
+ mod.close = mod.shutdown;
10144
10209
  return mod;
10145
10210
  }
10146
10211
 
@@ -10355,7 +10420,7 @@ function registerWorker(url) {
10355
10420
  }
10356
10421
 
10357
10422
  // session.ts
10358
- import crypto8 from "node:crypto";
10423
+ import crypto9 from "node:crypto";
10359
10424
  var kSaved = /* @__PURE__ */ Symbol("session.saved");
10360
10425
  var kDestroyed = /* @__PURE__ */ Symbol("session.destroyed");
10361
10426
  var kId = /* @__PURE__ */ Symbol("session.id");
@@ -10433,7 +10498,7 @@ var RedisStore = class {
10433
10498
  };
10434
10499
  var COOKIE_SEPARATOR = ".";
10435
10500
  function signSessionId(sid, secret) {
10436
- const hmac = crypto8.createHmac("sha256", secret).update(sid).digest("base64url").slice(0, 16);
10501
+ const hmac = crypto9.createHmac("sha256", secret).update(sid).digest("base64url").slice(0, 16);
10437
10502
  return sid + COOKIE_SEPARATOR + hmac;
10438
10503
  }
10439
10504
  function unsignSessionId(value, secret) {
@@ -10441,10 +10506,10 @@ function unsignSessionId(value, secret) {
10441
10506
  if (dot === -1) return null;
10442
10507
  const sid = value.slice(0, dot);
10443
10508
  const sig = value.slice(dot + 1);
10444
- const expected = crypto8.createHmac("sha256", secret).update(sid).digest("base64url").slice(0, 16);
10509
+ const expected = crypto9.createHmac("sha256", secret).update(sid).digest("base64url").slice(0, 16);
10445
10510
  if (sig.length !== expected.length) return null;
10446
10511
  try {
10447
- return crypto8.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)) ? sid : null;
10512
+ return crypto9.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)) ? sid : null;
10448
10513
  } catch {
10449
10514
  return null;
10450
10515
  }
@@ -10532,11 +10597,11 @@ function session(options) {
10532
10597
  }
10533
10598
  } else {
10534
10599
  loadedSid = null;
10535
- session2 = createSessionObject({}, crypto8.randomUUID(), store2, ttl, Date.now());
10600
+ session2 = createSessionObject({}, crypto9.randomUUID(), store2, ttl, Date.now());
10536
10601
  }
10537
10602
  } else {
10538
10603
  loadedSid = null;
10539
- session2 = createSessionObject({}, crypto8.randomUUID(), store2, ttl, Date.now());
10604
+ session2 = createSessionObject({}, crypto9.randomUUID(), store2, ttl, Date.now());
10540
10605
  }
10541
10606
  const snapshot = isSessionActive(session2) ? JSON.stringify(session2) : null;
10542
10607
  ctx.session = session2;
@@ -10549,7 +10614,7 @@ function session(options) {
10549
10614
  return deleteCookie(res, cookieName, cookieOpts);
10550
10615
  }
10551
10616
  if (needsRotation && loadedSid) {
10552
- const newId = crypto8.randomUUID();
10617
+ const newId = crypto9.randomUUID();
10553
10618
  const data = JSON.parse(JSON.stringify(currentSession));
10554
10619
  data[kCreatedAt] = Date.now();
10555
10620
  await store2.set(newId, data, ttl);
@@ -10590,7 +10655,7 @@ function session(options) {
10590
10655
  }
10591
10656
 
10592
10657
  // cache.ts
10593
- import crypto9 from "node:crypto";
10658
+ import crypto10 from "node:crypto";
10594
10659
  var BINARY_PREFIXES = [
10595
10660
  "image/",
10596
10661
  "audio/",
@@ -10610,7 +10675,7 @@ function isCacheableStatus(status, allowed) {
10610
10675
  return allowed.includes(status);
10611
10676
  }
10612
10677
  function defaultCacheKey(req) {
10613
- const hash = crypto9.createHash("sha256");
10678
+ const hash = crypto10.createHash("sha256");
10614
10679
  hash.update(req.method);
10615
10680
  hash.update(req.url);
10616
10681
  return hash.digest("hex");
@@ -10825,10 +10890,10 @@ function cache2(options) {
10825
10890
  }
10826
10891
 
10827
10892
  // webhook.ts
10828
- import crypto10 from "node:crypto";
10893
+ import crypto11 from "node:crypto";
10829
10894
  function timingSafeEqual2(a, b) {
10830
10895
  try {
10831
- return crypto10.timingSafeEqual(Buffer.from(a), Buffer.from(b));
10896
+ return crypto11.timingSafeEqual(Buffer.from(a), Buffer.from(b));
10832
10897
  } catch {
10833
10898
  return false;
10834
10899
  }
@@ -10846,7 +10911,7 @@ function createStripeVerifier(config) {
10846
10911
  const signature = parts["v1"];
10847
10912
  if (!timestamp || !signature) return { valid: false, provider: "stripe", event: "", id: void 0 };
10848
10913
  const signed = `${timestamp}.${body}`;
10849
- const expected = crypto10.createHmac("sha256", config.secret).update(signed).digest("hex");
10914
+ const expected = crypto11.createHmac("sha256", config.secret).update(signed).digest("hex");
10850
10915
  const valid = timingSafeEqual2(signature, expected);
10851
10916
  let event = "";
10852
10917
  let id2;
@@ -10863,7 +10928,7 @@ function createGitHubVerifier(config) {
10863
10928
  return (body, headers) => {
10864
10929
  const sig = headers["x-hub-signature-256"];
10865
10930
  if (!sig) return { valid: false, provider: "github", event: "", id: void 0 };
10866
- const expected = `sha256=${crypto10.createHmac("sha256", config.secret).update(body).digest("hex")}`;
10931
+ const expected = `sha256=${crypto11.createHmac("sha256", config.secret).update(body).digest("hex")}`;
10867
10932
  const valid = timingSafeEqual2(sig, expected);
10868
10933
  let event = headers["x-github-event"] ?? "";
10869
10934
  let id2;
@@ -10886,7 +10951,7 @@ function createSlackVerifier(config) {
10886
10951
  return { valid: false, provider: "slack", event: "", id: void 0 };
10887
10952
  }
10888
10953
  const sigBase = `v0:${timestamp}:${body}`;
10889
- const expected = `v0=${crypto10.createHmac("sha256", config.secret).update(sigBase).digest("hex")}`;
10954
+ const expected = `v0=${crypto11.createHmac("sha256", config.secret).update(sigBase).digest("hex")}`;
10890
10955
  const valid = timingSafeEqual2(signature, expected);
10891
10956
  let event = "";
10892
10957
  let id2;
@@ -11520,7 +11585,6 @@ export {
11520
11585
  aiProvider,
11521
11586
  aiStream,
11522
11587
  analytics,
11523
- auth,
11524
11588
  cache2 as cache,
11525
11589
  compress,
11526
11590
  cors,
@@ -11538,6 +11602,7 @@ export {
11538
11602
  deploy,
11539
11603
  embed,
11540
11604
  embedMany,
11605
+ env,
11541
11606
  flash,
11542
11607
  formatSSE,
11543
11608
  formatSSEData,
@@ -11545,11 +11610,13 @@ export {
11545
11610
  generateObject,
11546
11611
  generateText2 as generateText,
11547
11612
  getCookies,
11613
+ getPublicEnv,
11548
11614
  graphql,
11549
11615
  health,
11550
11616
  helmet,
11551
11617
  i18n,
11552
11618
  iii,
11619
+ isBundled,
11553
11620
  isDev,
11554
11621
  isProd,
11555
11622
  knowledgeBase,
@@ -11585,6 +11652,7 @@ export {
11585
11652
  testApp,
11586
11653
  theme,
11587
11654
  tool2 as tool,
11655
+ trace,
11588
11656
  traceElapsed,
11589
11657
  upload,
11590
11658
  user,