weifuwu 0.27.4 → 0.27.6

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
@@ -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((resolve4) => {
296
+ return new Promise((resolve8) => {
297
297
  const timer = setTimeout(() => {
298
298
  server.closeAllConnections();
299
- resolve4();
299
+ resolve8();
300
300
  }, timeoutMs);
301
301
  server.on("close", () => {
302
302
  clearTimeout(timer);
303
- resolve4();
303
+ resolve8();
304
304
  });
305
305
  });
306
306
  }
@@ -345,7 +345,7 @@ function createHub(opts) {
345
345
  }
346
346
  });
347
347
  }
348
- function join3(key, ws) {
348
+ function join4(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: join3, leave, broadcast, close };
410
+ return { join: join4, leave, broadcast, close };
411
411
  }
412
412
 
413
413
  // core/router.ts
@@ -1176,27 +1176,27 @@ var MIME_TYPES = {
1176
1176
  };
1177
1177
 
1178
1178
  // middleware/validate.ts
1179
- function parseFormBody(text2) {
1180
- const params = new URLSearchParams(text2);
1179
+ function parseFormBody(text) {
1180
+ const params = new URLSearchParams(text);
1181
1181
  const result = {};
1182
1182
  for (const [key, value] of params) {
1183
1183
  result[key] = value;
1184
1184
  }
1185
1185
  return result;
1186
1186
  }
1187
- function parseBody(text2, ct) {
1187
+ function parseBody(text, ct) {
1188
1188
  if (ct.includes("application/x-www-form-urlencoded")) {
1189
- return parseFormBody(text2);
1189
+ return parseFormBody(text);
1190
1190
  }
1191
1191
  const isExplicitJson = ct.includes("application/json") || ct.includes("+json") || ct.includes("text/") || ct.includes("*/json");
1192
1192
  const isNotSpecialMultipart = !ct.includes("multipart/form-data") && !ct.includes("application/x-www-form-urlencoded");
1193
1193
  if (isExplicitJson || isNotSpecialMultipart) {
1194
1194
  try {
1195
- return JSON.parse(text2);
1195
+ return JSON.parse(text);
1196
1196
  } catch {
1197
1197
  }
1198
1198
  }
1199
- return text2;
1199
+ return text;
1200
1200
  }
1201
1201
  function validate(schemas) {
1202
1202
  const mw = async (req, ctx, next) => {
@@ -1693,8 +1693,8 @@ function createSSEStream(iterable, opts) {
1693
1693
  async start(controller) {
1694
1694
  try {
1695
1695
  for await (const event of iterable) {
1696
- const text2 = event.type ? formatSSE(event.type, event) : formatSSEData(event);
1697
- controller.enqueue(encoder.encode(text2));
1696
+ const text = event.type ? formatSSE(event.type, event) : formatSSEData(event);
1697
+ controller.enqueue(encoder.encode(text));
1698
1698
  }
1699
1699
  } catch (e) {
1700
1700
  if (e instanceof Error && e.name !== "AbortError") {
@@ -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((resolve4, reject) => {
1963
+ return new Promise((resolve8, 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
- resolve4(conn);
1972
+ resolve8(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 resolve4 = this.resolveQueue.shift();
2004
- resolve4(str);
2003
+ const resolve8 = this.resolveQueue.shift();
2004
+ resolve8(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((resolve4, reject) => {
2034
+ return new Promise((resolve8, reject) => {
2035
2035
  const timer = setTimeout(() => {
2036
- const idx = this.resolveQueue.indexOf(resolve4);
2036
+ const idx = this.resolveQueue.indexOf(resolve8);
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
- resolve4(msg);
2042
+ resolve8(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((resolve4, reject) => {
2056
+ return new Promise((resolve8, 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(() => resolve4(), ms);
2061
+ const timer = setTimeout(() => resolve8(), ms);
2062
2062
  const origPush = this.resolveQueue.push.bind(this.resolveQueue);
2063
2063
  this.resolveQueue.push = (_fn) => {
2064
2064
  clearTimeout(timer);
@@ -2092,17 +2092,17 @@ async function createTestDb(options) {
2092
2092
  await adminSql.unsafe('CREATE SCHEMA IF NOT EXISTS "' + schema.replace(/"/g, '""') + '"');
2093
2093
  const schemaUrl = new URL(dbUrl);
2094
2094
  schemaUrl.searchParams.set("search_path", schema);
2095
- const sql2 = postgres2(schemaUrl.toString());
2095
+ const sql = postgres2(schemaUrl.toString());
2096
2096
  await adminSql.end();
2097
2097
  return {
2098
- sql: sql2,
2098
+ sql,
2099
2099
  url: schemaUrl.toString(),
2100
2100
  schema,
2101
2101
  destroy: async () => {
2102
2102
  const destroySql = postgres2(dbUrl);
2103
2103
  await destroySql.unsafe('DROP SCHEMA IF EXISTS "' + schema.replace(/"/g, '""') + '" CASCADE');
2104
2104
  await destroySql.end();
2105
- await sql2.end();
2105
+ await sql.end();
2106
2106
  }
2107
2107
  };
2108
2108
  }
@@ -2121,15 +2121,15 @@ async function withTestDb(optionsOrFn, fn) {
2121
2121
  const resolvedUrl = dbUrl || process.env.TEST_DATABASE_URL || process.env.DATABASE_URL;
2122
2122
  if (!resolvedUrl) throw new Error("withTestDb: DATABASE_URL or TEST_DATABASE_URL required");
2123
2123
  const { default: postgres2 } = await import("postgres");
2124
- const sql2 = postgres2(resolvedUrl);
2124
+ const sql = postgres2(resolvedUrl);
2125
2125
  try {
2126
- await sql2.begin(async (txSql) => {
2126
+ await sql.begin(async (txSql) => {
2127
2127
  await callback(txSql);
2128
2128
  throw void 0;
2129
2129
  });
2130
2130
  } catch {
2131
2131
  } finally {
2132
- await sql2.end();
2132
+ await sql.end();
2133
2133
  }
2134
2134
  }
2135
2135
 
@@ -2405,8 +2405,8 @@ function aiProvider(options) {
2405
2405
  if (!_embedModel) _embedModel = client.embedding(m);
2406
2406
  return _embedModel;
2407
2407
  },
2408
- async embed(text2) {
2409
- const result = await aiEmbed({ model: this.embeddingModel(), value: text2 });
2408
+ async embed(text) {
2409
+ const result = await aiEmbed({ model: this.embeddingModel(), value: text });
2410
2410
  return result.embedding;
2411
2411
  },
2412
2412
  async embedMany(texts) {
@@ -2444,440 +2444,6 @@ import { openai, createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
2444
2444
 
2445
2445
  // postgres/client.ts
2446
2446
  import postgresFactory from "postgres";
2447
-
2448
- // postgres/schema/sql.ts
2449
- var SQL = class {
2450
- /** Template string parts (interleaved with values). */
2451
- strings;
2452
- /** Bound parameter values. */
2453
- values;
2454
- constructor(strings, values) {
2455
- this.strings = strings;
2456
- this.values = values;
2457
- }
2458
- /** Serialize to a raw SQL string (interpolating values inline for DDL use). */
2459
- toSQL() {
2460
- let result = "";
2461
- for (let i = 0; i < this.strings.length; i++) {
2462
- result += this.strings[i];
2463
- if (i < this.values.length) {
2464
- result += String(this.values[i]);
2465
- }
2466
- }
2467
- return result;
2468
- }
2469
- };
2470
-
2471
- // postgres/schema/columns.ts
2472
- function toDDL(col) {
2473
- const parts = [`"${col.name}"`, col.sqlType];
2474
- if (col.isPrimaryKey) parts.push("PRIMARY KEY");
2475
- if (!col.isPrimaryKey && !col.isNullable) parts.push("NOT NULL");
2476
- if (col.isUnique) parts.push("UNIQUE");
2477
- if (col.defaultExpr) parts.push(`DEFAULT ${col.defaultExpr}`);
2478
- if (col.ref) {
2479
- parts.push(`REFERENCES "${col.ref.table}"("${col.ref.column}")`);
2480
- if (col.ref.onDelete) parts.push(`ON DELETE ${col.ref.onDelete.toUpperCase()}`);
2481
- }
2482
- return parts.join(" ");
2483
- }
2484
-
2485
- // postgres/schema/where.ts
2486
- function combine(conditions, joiner) {
2487
- if (conditions.length === 0) return new SQL([""], []);
2488
- const strings = ["("];
2489
- const values = [];
2490
- for (let i = 0; i < conditions.length; i++) {
2491
- if (i > 0) strings[strings.length - 1] += ` ${joiner} `;
2492
- const s = conditions[i];
2493
- for (let j = 0; j < s.strings.length; j++) {
2494
- strings[strings.length - 1] += s.strings[j];
2495
- if (j < s.values.length) {
2496
- strings.push("");
2497
- values.push(s.values[j]);
2498
- }
2499
- }
2500
- }
2501
- strings[strings.length - 1] += ")";
2502
- return new SQL(strings, values);
2503
- }
2504
- function and(...conditions) {
2505
- return combine(conditions, "AND");
2506
- }
2507
-
2508
- // postgres/schema/table.ts
2509
- var Table = class {
2510
- /** Database table name. */
2511
- tableName;
2512
- /** All column builders (order-preserving). */
2513
- columns;
2514
- /** Column builders keyed by property name. */
2515
- builders;
2516
- colEntries;
2517
- constructor(tableName, builders) {
2518
- this.tableName = tableName;
2519
- this.builders = builders;
2520
- this.columns = Object.values(builders);
2521
- this.colEntries = Object.entries(builders).map(([prop, col]) => ({
2522
- prop,
2523
- db: col.name,
2524
- auto: col.isAutoGenerate,
2525
- column: col
2526
- }));
2527
- }
2528
- /** Check if the table has a column with the given DB name. */
2529
- hasColumn(dbName) {
2530
- return this.colEntries.some((e) => e.db === dbName);
2531
- }
2532
- /**
2533
- * Bind this table schema to a SQL connection, returning a `BoundTable`
2534
- * that can run queries without passing `sql` to every call.
2535
- */
2536
- bind(sql2) {
2537
- return new BoundTable(sql2, this.tableName, this.builders);
2538
- }
2539
- /** Returns the primary key column name (DB name), or 'id' as fallback. */
2540
- get pkColumn() {
2541
- const entry = this.colEntries.find((e) => e.column.isPrimaryKey);
2542
- return entry ? entry.db : "id";
2543
- }
2544
- /** Adds `deleted_at IS NULL` condition if the table has soft delete and not explicitly excluded. */
2545
- _softDeleteFilter(where, opts) {
2546
- if (!this.hasColumn("deleted_at")) return null;
2547
- if (opts?.withDeleted) return null;
2548
- if (where && typeof where === "object" && !Array.isArray(where) && !(where instanceof SQL)) {
2549
- if ("deleted_at" in where) return null;
2550
- }
2551
- return '"deleted_at" IS NULL';
2552
- }
2553
- async create(sql2, opts) {
2554
- const colDDL = this.columns.map(toDDL);
2555
- let ddl = `CREATE TABLE IF NOT EXISTS "${this.tableName}" (
2556
- ${colDDL.join(",\n ")}
2557
- )`;
2558
- if (opts?.partitionBy) {
2559
- ddl += ` PARTITION BY ${opts.partitionBy.type} ("${opts.partitionBy.column}")`;
2560
- }
2561
- await sql2.unsafe(ddl);
2562
- }
2563
- async drop(sql2, opts) {
2564
- const cascade = opts?.cascade ? " CASCADE" : "";
2565
- await sql2.unsafe(`DROP TABLE IF EXISTS "${this.tableName}"${cascade}`);
2566
- }
2567
- async createIndex(sql2, columns, opts) {
2568
- const cols = Array.isArray(columns) ? columns : [columns];
2569
- const name = `"${this.tableName}_${cols.join("_")}${opts?.unique ? "_uidx" : "_idx"}"`;
2570
- const unique = opts?.unique ? "UNIQUE" : "";
2571
- const using = opts?.type ? `USING ${opts.type.toUpperCase()}` : "";
2572
- const colList = cols.map((c) => opts?.desc ? `"${c}" DESC` : `"${c}"`).join(", ");
2573
- const operator = opts?.operator ? ` ${opts.operator}` : "";
2574
- const ddl = `CREATE ${unique} INDEX IF NOT EXISTS ${name} ON "${this.tableName}" ${using} (${colList}${operator})`.replace(
2575
- /\s+/g,
2576
- " "
2577
- );
2578
- await sql2.unsafe(ddl);
2579
- }
2580
- async createUniqueIndex(sql2, columns) {
2581
- await this.createIndex(sql2, columns, { unique: true });
2582
- }
2583
- // --- Private helpers ---
2584
- _buildConditions(where, startIndex) {
2585
- const conditions = [];
2586
- const values = [];
2587
- let w = where;
2588
- if (Array.isArray(w)) {
2589
- w = w.length > 0 ? and(...w) : void 0;
2590
- }
2591
- if (w instanceof SQL) {
2592
- let fragment = "";
2593
- for (let i = 0; i < w.strings.length; i++) {
2594
- fragment += w.strings[i];
2595
- if (i < w.values.length) {
2596
- fragment += `$${startIndex + values.length + 1}`;
2597
- values.push(w.values[i]);
2598
- }
2599
- }
2600
- conditions.push(fragment);
2601
- } else {
2602
- for (const [prop, value] of Object.entries(w || {})) {
2603
- if (value === void 0) continue;
2604
- const entry = this.colEntries.find((e) => e.prop === prop);
2605
- const db = entry ? entry.db : prop;
2606
- conditions.push(`"${db}" = $${startIndex + values.length + 1}`);
2607
- values.push(value);
2608
- }
2609
- }
2610
- return { conditions, values };
2611
- }
2612
- _buildSET(data) {
2613
- const sets = [];
2614
- const values = [];
2615
- const d = data;
2616
- for (const { prop, db } of this.colEntries) {
2617
- if (prop in d && d[prop] !== void 0) {
2618
- const val = d[prop];
2619
- if (val instanceof SQL) {
2620
- sets.push(`"${db}" = ${val.toSQL()}`);
2621
- } else {
2622
- sets.push(`"${db}" = $${sets.length + 1}`);
2623
- values.push(val);
2624
- }
2625
- }
2626
- }
2627
- if (this.hasColumn("updated_at") && !d.updated_at) {
2628
- sets.push('"updated_at" = NOW()');
2629
- }
2630
- return { sets, values };
2631
- }
2632
- // --- CRUD ---
2633
- async insert(sql2, data) {
2634
- const filtered = {};
2635
- for (const { prop, db, auto } of this.colEntries) {
2636
- if (auto) continue;
2637
- const val = data[prop];
2638
- if (val !== void 0) {
2639
- filtered[db] = val;
2640
- }
2641
- }
2642
- const [row] = await sql2`
2643
- INSERT INTO ${sql2(this.tableName)} ${sql2(filtered)} RETURNING *
2644
- `;
2645
- return row;
2646
- }
2647
- async insertMany(sql2, data) {
2648
- const filtered = [];
2649
- for (const item of data) {
2650
- const row = {};
2651
- for (const { prop, db, auto } of this.colEntries) {
2652
- if (auto) continue;
2653
- const val = item[prop];
2654
- if (val !== void 0) {
2655
- row[db] = val;
2656
- }
2657
- }
2658
- filtered.push(row);
2659
- }
2660
- const rows = await sql2`
2661
- INSERT INTO ${sql2(this.tableName)} ${sql2(filtered)} RETURNING *
2662
- `;
2663
- return rows;
2664
- }
2665
- async read(sql2, id, opts) {
2666
- const pk = this.pkColumn;
2667
- const columns = opts?.select?.length ? opts.select.map((c) => `"${c}"`).join(", ") : "*";
2668
- const softDel = this._softDeleteFilter(null, opts);
2669
- const extraAnd = softDel ? ` AND ${softDel}` : "";
2670
- const [row] = await sql2.unsafe(
2671
- `SELECT ${columns} FROM "${this.tableName}" WHERE "${pk}" = $1${extraAnd} LIMIT 1`,
2672
- [id]
2673
- );
2674
- return row ?? void 0;
2675
- }
2676
- async readMany(sql2, where, opts) {
2677
- const { conditions, values } = this._buildConditions(where, 0);
2678
- const softDel = this._softDeleteFilter(where, opts);
2679
- if (softDel) conditions.push(softDel);
2680
- const whereClause = conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "";
2681
- const [countRow] = await sql2.unsafe(
2682
- `SELECT COUNT(*) AS _total FROM "${this.tableName}"${whereClause}`,
2683
- values
2684
- );
2685
- const count = Number(countRow._total);
2686
- if (conditions.length === 0 && !opts?.orderBy && !opts?.limit && !opts?.offset && !opts?.select) {
2687
- const rows2 = await sql2`SELECT * FROM ${sql2(this.tableName)}`;
2688
- return { count, data: rows2 };
2689
- }
2690
- const columns = opts?.select?.length ? opts.select.map((c) => `"${c}"`).join(", ") : "*";
2691
- let query = `SELECT ${columns} FROM "${this.tableName}"${whereClause}`;
2692
- if (opts?.orderBy) {
2693
- const orders = Object.entries(opts.orderBy).map(([prop, dir]) => {
2694
- const entry = this.colEntries.find((e) => e.prop === prop);
2695
- return `"${entry?.db || prop}" ${dir.toUpperCase()}`;
2696
- }).join(", ");
2697
- query += ` ORDER BY ${orders}`;
2698
- }
2699
- if (opts?.limit) query += ` LIMIT ${opts.limit}`;
2700
- if (opts?.offset) query += ` OFFSET ${opts.offset}`;
2701
- const rows = await sql2.unsafe(query, values);
2702
- return { count, data: rows };
2703
- }
2704
- async update(sql2, id, data) {
2705
- const { sets, values: setValues } = this._buildSET(data);
2706
- if (sets.length === 0) return void 0;
2707
- const pk = this.pkColumn;
2708
- const [row] = await sql2.unsafe(
2709
- `UPDATE "${this.tableName}" SET ${sets.join(", ")} WHERE "${pk}" = $${setValues.length + 1} RETURNING *`,
2710
- [...setValues, id]
2711
- );
2712
- return row ?? void 0;
2713
- }
2714
- async updateMany(sql2, where, data) {
2715
- const { sets, values: setValues } = this._buildSET(data);
2716
- if (sets.length === 0) return 0;
2717
- const { conditions: wConditions, values: wValues } = this._buildConditions(
2718
- where,
2719
- setValues.length
2720
- );
2721
- if (wConditions.length === 0) return 0;
2722
- const rows = await sql2.unsafe(
2723
- `UPDATE "${this.tableName}" SET ${sets.join(", ")} WHERE ${wConditions.join(" AND ")} RETURNING 1`,
2724
- [...setValues, ...wValues]
2725
- );
2726
- return rows.length;
2727
- }
2728
- async delete(sql2, id) {
2729
- const pk = this.pkColumn;
2730
- if (this.hasColumn("deleted_at")) {
2731
- const [row2] = await sql2.unsafe(
2732
- `UPDATE "${this.tableName}" SET "deleted_at" = NOW() WHERE "${pk}" = $1 RETURNING *`,
2733
- [id]
2734
- );
2735
- return row2 ?? void 0;
2736
- }
2737
- const [row] = await sql2.unsafe(
2738
- `DELETE FROM "${this.tableName}" WHERE "${pk}" = $1 RETURNING *`,
2739
- [id]
2740
- );
2741
- return row ?? void 0;
2742
- }
2743
- async hardDelete(sql2, id) {
2744
- const pk = this.pkColumn;
2745
- const [row] = await sql2.unsafe(
2746
- `DELETE FROM "${this.tableName}" WHERE "${pk}" = $1 RETURNING *`,
2747
- [id]
2748
- );
2749
- return row ?? void 0;
2750
- }
2751
- async deleteMany(sql2, where) {
2752
- const { conditions, values } = this._buildConditions(where, 0);
2753
- if (conditions.length === 0) return 0;
2754
- if (this.hasColumn("deleted_at")) {
2755
- const rows2 = await sql2.unsafe(
2756
- `UPDATE "${this.tableName}" SET "deleted_at" = NOW() WHERE ${conditions.join(" AND ")} RETURNING 1`,
2757
- values
2758
- );
2759
- return rows2.length;
2760
- }
2761
- const rows = await sql2.unsafe(
2762
- `DELETE FROM "${this.tableName}" WHERE ${conditions.join(" AND ")} RETURNING 1`,
2763
- values
2764
- );
2765
- return rows.length;
2766
- }
2767
- async hardDeleteMany(sql2, where) {
2768
- const { conditions, values } = this._buildConditions(where, 0);
2769
- if (conditions.length === 0) return 0;
2770
- const rows = await sql2.unsafe(
2771
- `DELETE FROM "${this.tableName}" WHERE ${conditions.join(" AND ")} RETURNING 1`,
2772
- values
2773
- );
2774
- return rows.length;
2775
- }
2776
- async upsert(sql2, data, conflict) {
2777
- const filtered = {};
2778
- for (const { prop, db, auto } of this.colEntries) {
2779
- if (auto) continue;
2780
- const val = data[prop];
2781
- if (val !== void 0) {
2782
- filtered[db] = val;
2783
- }
2784
- }
2785
- const keys = Object.keys(filtered);
2786
- if (keys.length === 0) throw new Error("upsert: no data to insert");
2787
- const conflictCols = Array.isArray(conflict) ? conflict : [conflict];
2788
- const dbCols = keys.map((c) => `"${c}"`);
2789
- const placeholders = keys.map((_, i) => `$${i + 1}`);
2790
- const updateSet = keys.filter((k) => !conflictCols.includes(k)).map((k) => `"${k}" = EXCLUDED."${k}"`).join(", ");
2791
- if (!updateSet) {
2792
- const [row2] = await sql2.unsafe(
2793
- `INSERT INTO "${this.tableName}" (${dbCols.join(", ")}) VALUES (${placeholders.join(", ")}) ON CONFLICT (${conflictCols.map((c) => `"${c}"`).join(", ")}) DO NOTHING RETURNING *`,
2794
- Object.values(filtered)
2795
- );
2796
- return row2 ?? void 0;
2797
- }
2798
- const [row] = await sql2.unsafe(
2799
- `INSERT INTO "${this.tableName}" (${dbCols.join(", ")}) VALUES (${placeholders.join(", ")}) ON CONFLICT (${conflictCols.map((c) => `"${c}"`).join(", ")}) DO UPDATE SET ${updateSet} RETURNING *`,
2800
- Object.values(filtered)
2801
- );
2802
- return row;
2803
- }
2804
- async count(sql2, where) {
2805
- const { conditions, values } = this._buildConditions(where, 0);
2806
- const softDel = this._softDeleteFilter(where);
2807
- if (softDel) conditions.push(softDel);
2808
- const whereClause = conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "";
2809
- const [row] = await sql2.unsafe(
2810
- `SELECT COUNT(*) AS _total FROM "${this.tableName}"${whereClause}`,
2811
- values
2812
- );
2813
- return Number(row._total);
2814
- }
2815
- };
2816
- var BoundTable = class _BoundTable {
2817
- inner;
2818
- sql;
2819
- /** The underlying table name. */
2820
- get tableName() {
2821
- return this.inner.tableName;
2822
- }
2823
- constructor(sql2, tableName, builders) {
2824
- this.inner = new Table(tableName, builders);
2825
- this.sql = sql2;
2826
- }
2827
- async create(opts) {
2828
- await this.inner.create(this.sql, opts);
2829
- }
2830
- async drop(opts) {
2831
- await this.inner.drop(this.sql, opts);
2832
- }
2833
- async createIndex(columns, opts) {
2834
- await this.inner.createIndex(this.sql, columns, opts);
2835
- }
2836
- async createUniqueIndex(columns) {
2837
- await this.inner.createUniqueIndex(this.sql, columns);
2838
- }
2839
- async insert(data) {
2840
- return await this.inner.insert(this.sql, data);
2841
- }
2842
- async insertMany(data) {
2843
- return await this.inner.insertMany(this.sql, data);
2844
- }
2845
- async read(id, opts) {
2846
- return await this.inner.read(this.sql, id, opts);
2847
- }
2848
- async readMany(where, opts) {
2849
- return await this.inner.readMany(this.sql, where, opts);
2850
- }
2851
- async update(id, data) {
2852
- return await this.inner.update(this.sql, id, data);
2853
- }
2854
- async updateMany(where, data) {
2855
- return await this.inner.updateMany(this.sql, where, data);
2856
- }
2857
- async delete(id) {
2858
- return await this.inner.delete(this.sql, id);
2859
- }
2860
- async hardDelete(id) {
2861
- return await this.inner.hardDelete(this.sql, id);
2862
- }
2863
- async deleteMany(where) {
2864
- return await this.inner.deleteMany(this.sql, where);
2865
- }
2866
- async hardDeleteMany(where) {
2867
- return await this.inner.hardDeleteMany(this.sql, where);
2868
- }
2869
- async upsert(data, conflict) {
2870
- return await this.inner.upsert(this.sql, data, conflict);
2871
- }
2872
- async count(where) {
2873
- return await this.inner.count(this.sql, where);
2874
- }
2875
- withSql(sql2) {
2876
- return new _BoundTable(sql2, this.inner.tableName, this.inner.builders);
2877
- }
2878
- };
2879
-
2880
- // postgres/client.ts
2881
2447
  var MIGRATIONS_TABLE = "_weifuwu_migrations";
2882
2448
  var RETRYABLE_CODES = /* @__PURE__ */ new Set(["40P01", "40001"]);
2883
2449
  function isRetryable(err) {
@@ -2897,7 +2463,7 @@ function postgres(opts) {
2897
2463
  const sep2 = connStr.includes("?") ? "&" : "?";
2898
2464
  connStr = `${connStr}${sep2}options=-c%20statement_timeout%3D${stmtTimeout}`;
2899
2465
  }
2900
- const sql2 = postgresFactory(connStr, {
2466
+ const sql = postgresFactory(connStr, {
2901
2467
  max: options.max,
2902
2468
  ssl: options.ssl,
2903
2469
  idle_timeout: options.idle_timeout,
@@ -2907,7 +2473,7 @@ function postgres(opts) {
2907
2473
  options.signal.addEventListener(
2908
2474
  "abort",
2909
2475
  () => {
2910
- sql2.end();
2476
+ sql.end();
2911
2477
  },
2912
2478
  { once: true }
2913
2479
  );
@@ -2917,19 +2483,13 @@ function postgres(opts) {
2917
2483
  const _waiting = 0;
2918
2484
  const poolMax = options.max ?? 10;
2919
2485
  const mw = ((req, ctx, next) => {
2920
- ctx.sql = sql2;
2486
+ ctx.sql = sql;
2921
2487
  return next(req, ctx);
2922
2488
  });
2923
2489
  mw.__meta = { injects: ["sql"], depends: [] };
2924
- mw.sql = sql2;
2925
- mw.table = ((tableOrSchema, builders) => {
2926
- if (typeof tableOrSchema === "string") {
2927
- return new BoundTable(sql2, tableOrSchema, builders);
2928
- }
2929
- return new BoundTable(sql2, tableOrSchema.tableName, tableOrSchema.builders);
2930
- });
2490
+ mw.sql = sql;
2931
2491
  mw.migrate = async () => {
2932
- await sql2.unsafe(`
2492
+ await sql.unsafe(`
2933
2493
  CREATE TABLE IF NOT EXISTS "${MIGRATIONS_TABLE}" (
2934
2494
  name TEXT PRIMARY KEY,
2935
2495
  applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
@@ -2937,13 +2497,13 @@ function postgres(opts) {
2937
2497
  `);
2938
2498
  };
2939
2499
  mw.markMigrated = async (moduleName) => {
2940
- await sql2.unsafe(
2500
+ await sql.unsafe(
2941
2501
  `INSERT INTO "${MIGRATIONS_TABLE}" (name) VALUES ($1) ON CONFLICT DO NOTHING`,
2942
2502
  [moduleName]
2943
2503
  );
2944
2504
  };
2945
2505
  mw.isMigrated = async (moduleName) => {
2946
- const [row] = await sql2.unsafe(`SELECT 1 FROM "${MIGRATIONS_TABLE}" WHERE name = $1`, [
2506
+ const [row] = await sql.unsafe(`SELECT 1 FROM "${MIGRATIONS_TABLE}" WHERE name = $1`, [
2947
2507
  moduleName
2948
2508
  ]);
2949
2509
  return !!row;
@@ -2952,7 +2512,7 @@ function postgres(opts) {
2952
2512
  const maxRetries = retryOpts?.maxRetries ?? 3;
2953
2513
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
2954
2514
  try {
2955
- const result = await sql2.begin(fn);
2515
+ const result = await sql.begin(fn);
2956
2516
  return result;
2957
2517
  } catch (err) {
2958
2518
  if (attempt < maxRetries && isRetryable(err)) {
@@ -2971,7 +2531,7 @@ function postgres(opts) {
2971
2531
  waiting: _waiting,
2972
2532
  max: poolMax
2973
2533
  });
2974
- mw.close = () => sql2.end({ timeout: closeTimeout });
2534
+ mw.close = () => sql.end({ timeout: closeTimeout });
2975
2535
  return mw;
2976
2536
  }
2977
2537
 
@@ -3214,7 +2774,7 @@ function createMemoryQueue(opts) {
3214
2774
  return q;
3215
2775
  }
3216
2776
  function createPgQueue(opts) {
3217
- const sql2 = opts.pg.sql;
2777
+ const sql = opts.pg.sql;
3218
2778
  const pollInterval = opts?.pollInterval ?? 200;
3219
2779
  const table = (opts?.prefix ?? "queue") + "_jobs";
3220
2780
  const handlers = /* @__PURE__ */ new Map();
@@ -3224,10 +2784,10 @@ function createPgQueue(opts) {
3224
2784
  const MAX_FAILED = 1e3;
3225
2785
  async function ensureTable() {
3226
2786
  if (ready) return;
3227
- await sql2.unsafe(
2787
+ await sql.unsafe(
3228
2788
  `CREATE TABLE IF NOT EXISTS ${escapeIdent(table)} (id UUID PRIMARY KEY, type TEXT NOT NULL, payload JSONB NOT NULL DEFAULT '{}', run_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), schedule TEXT, status TEXT NOT NULL DEFAULT 'pending', error TEXT, failed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW())`
3229
2789
  );
3230
- await sql2.unsafe(
2790
+ await sql.unsafe(
3231
2791
  `CREATE INDEX IF NOT EXISTS ${escapeIdent(table + "_run_at_idx")} ON ${escapeIdent(table)} (run_at, status)`
3232
2792
  );
3233
2793
  ready = true;
@@ -3237,12 +2797,12 @@ function createPgQueue(opts) {
3237
2797
  try {
3238
2798
  await handler(job);
3239
2799
  _processed++;
3240
- await sql2.unsafe(`DELETE FROM ${escapeIdent(table)} WHERE id = $1`, [job.id]);
2800
+ await sql.unsafe(`DELETE FROM ${escapeIdent(table)} WHERE id = $1`, [job.id]);
3241
2801
  } catch (e) {
3242
2802
  _failed++;
3243
2803
  const msg = e.message;
3244
2804
  console.error("[queue] handler error:", msg);
3245
- await sql2.unsafe(
2805
+ await sql.unsafe(
3246
2806
  `UPDATE ${escapeIdent(table)} SET status = 'failed', error = $2, failed_at = NOW() WHERE id = $1`,
3247
2807
  [job.id, msg]
3248
2808
  );
@@ -3252,7 +2812,7 @@ function createPgQueue(opts) {
3252
2812
  if (job.schedule) {
3253
2813
  try {
3254
2814
  const nextRun = cronNext(job.schedule);
3255
- await sql2.unsafe(
2815
+ await sql.unsafe(
3256
2816
  `INSERT INTO ${escapeIdent(table)} (id, type, payload, run_at, schedule) VALUES ($1, $2, $3::jsonb, $4, $5)`,
3257
2817
  [
3258
2818
  crypto4.randomUUID(),
@@ -3271,7 +2831,7 @@ function createPgQueue(opts) {
3271
2831
  if (!running) return;
3272
2832
  try {
3273
2833
  while (running && inflight < MAX_CONCURRENT) {
3274
- const rows = await sql2.unsafe(
2834
+ const rows = await sql.unsafe(
3275
2835
  `UPDATE ${escapeIdent(table)} SET status = 'running' WHERE id = (SELECT id FROM ${escapeIdent(table)} WHERE run_at <= NOW() AND status = 'pending' ORDER BY run_at LIMIT 1 FOR UPDATE SKIP LOCKED) RETURNING *`
3276
2836
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
3277
2837
  );
@@ -3319,7 +2879,7 @@ function createPgQueue(opts) {
3319
2879
  } else {
3320
2880
  runAt = /* @__PURE__ */ new Date();
3321
2881
  }
3322
- await sql2.unsafe(
2882
+ await sql.unsafe(
3323
2883
  `INSERT INTO ${escapeIdent(table)} (id, type, payload, run_at, schedule) VALUES ($1, $2, $3::jsonb, $4, $5)`,
3324
2884
  [id, type, JSON.stringify(payload), runAt.toISOString(), opts2?.schedule || null]
3325
2885
  );
@@ -3345,7 +2905,7 @@ function createPgQueue(opts) {
3345
2905
  while (inflight > 0) await new Promise((r) => setTimeout(r, 50));
3346
2906
  };
3347
2907
  mw.jobs = async function jobs(limit) {
3348
- const rows = await sql2.unsafe(
2908
+ const rows = await sql.unsafe(
3349
2909
  `SELECT * FROM ${escapeIdent(table)} WHERE status = 'pending' ORDER BY run_at LIMIT $1`,
3350
2910
  [limit ?? 50]
3351
2911
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -3360,7 +2920,7 @@ function createPgQueue(opts) {
3360
2920
  }));
3361
2921
  };
3362
2922
  mw.failedJobs = async function failedJobs(limit) {
3363
- const rows = await sql2.unsafe(
2923
+ const rows = await sql.unsafe(
3364
2924
  `SELECT * FROM ${escapeIdent(table)} WHERE status = 'failed' ORDER BY failed_at DESC LIMIT $1`,
3365
2925
  [limit ?? 50]
3366
2926
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -3377,7 +2937,7 @@ function createPgQueue(opts) {
3377
2937
  }));
3378
2938
  };
3379
2939
  mw.retryFailed = async function retryFailed(jobId) {
3380
- const result = await sql2.unsafe(
2940
+ const result = await sql.unsafe(
3381
2941
  `UPDATE ${escapeIdent(table)} SET status = 'pending', error = NULL, failed_at = NULL, run_at = NOW() WHERE id = $1 AND status = 'failed' RETURNING id`,
3382
2942
  [jobId]
3383
2943
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -3385,7 +2945,7 @@ function createPgQueue(opts) {
3385
2945
  return result.length > 0;
3386
2946
  };
3387
2947
  mw.retryAllFailed = async function retryAllFailed(type) {
3388
- const result = await sql2.unsafe(
2948
+ const result = await sql.unsafe(
3389
2949
  type ? `UPDATE ${escapeIdent(table)} SET status = 'pending', error = NULL, failed_at = NULL, run_at = NOW() WHERE status = 'failed' AND type = $1 RETURNING id` : `UPDATE ${escapeIdent(table)} SET status = 'pending', error = NULL, failed_at = NULL, run_at = NOW() WHERE status = 'failed' RETURNING id`,
3390
2950
  type ? [type] : []
3391
2951
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -3454,14 +3014,14 @@ function createRedisQueue(opts) {
3454
3014
  while (running && inflight < MAX_CONCURRENT) {
3455
3015
  const result = await redis2.zpopmin(jobKey);
3456
3016
  if (result.length < 2) break;
3457
- const raw = result[0], score = parseInt(result[1], 10);
3017
+ const raw2 = result[0], score = parseInt(result[1], 10);
3458
3018
  if (score > now) {
3459
- await redis2.zadd(jobKey, score, raw);
3019
+ await redis2.zadd(jobKey, score, raw2);
3460
3020
  break;
3461
3021
  }
3462
3022
  let job;
3463
3023
  try {
3464
- job = JSON.parse(raw);
3024
+ job = JSON.parse(raw2);
3465
3025
  } catch {
3466
3026
  continue;
3467
3027
  }
@@ -3511,8 +3071,8 @@ function createRedisQueue(opts) {
3511
3071
  redis2.disconnect();
3512
3072
  };
3513
3073
  mw.jobs = async function jobs(limit) {
3514
- const raw = await redis2.zrevrange(jobKey, 0, (limit ?? 50) - 1);
3515
- return raw.map((r) => {
3074
+ const raw2 = await redis2.zrevrange(jobKey, 0, (limit ?? 50) - 1);
3075
+ return raw2.map((r) => {
3516
3076
  try {
3517
3077
  return JSON.parse(r);
3518
3078
  } catch {
@@ -3521,8 +3081,8 @@ function createRedisQueue(opts) {
3521
3081
  }).filter(Boolean);
3522
3082
  };
3523
3083
  mw.failedJobs = async function failedJobs(limit) {
3524
- const raw = await redis2.lrange(failedKey, 0, (limit ?? 50) - 1);
3525
- return raw.map((r) => {
3084
+ const raw2 = await redis2.lrange(failedKey, 0, (limit ?? 50) - 1);
3085
+ return raw2.map((r) => {
3526
3086
  try {
3527
3087
  return JSON.parse(r);
3528
3088
  } catch {
@@ -3531,8 +3091,8 @@ function createRedisQueue(opts) {
3531
3091
  }).filter(Boolean);
3532
3092
  };
3533
3093
  mw.retryFailed = async function retryFailed(jobId) {
3534
- const raw = await redis2.lrange(failedKey, 0, -1);
3535
- for (const entry of raw) {
3094
+ const raw2 = await redis2.lrange(failedKey, 0, -1);
3095
+ for (const entry of raw2) {
3536
3096
  try {
3537
3097
  const job = JSON.parse(entry);
3538
3098
  if (job.id === jobId) {
@@ -3551,8 +3111,8 @@ function createRedisQueue(opts) {
3551
3111
  };
3552
3112
  mw.retryAllFailed = async function retryAllFailed(type) {
3553
3113
  let count = 0;
3554
- const raw = await redis2.lrange(failedKey, 0, -1);
3555
- for (const entry of raw) {
3114
+ const raw2 = await redis2.lrange(failedKey, 0, -1);
3115
+ for (const entry of raw2) {
3556
3116
  try {
3557
3117
  const job = JSON.parse(entry);
3558
3118
  if (type && job.type !== type) continue;
@@ -3802,14 +3362,14 @@ function makeSetFlash(name, location) {
3802
3362
  function flash(options) {
3803
3363
  const name = options?.name ?? "flash";
3804
3364
  const mw = async (req, ctx, next) => {
3805
- const raw = getCookies(req)[name] ?? null;
3365
+ const raw2 = getCookies(req)[name] ?? null;
3806
3366
  const referer = req.headers.get("referer") || "/";
3807
3367
  let value = void 0;
3808
- if (raw) {
3368
+ if (raw2) {
3809
3369
  try {
3810
- value = JSON.parse(decodeURIComponent(raw));
3370
+ value = JSON.parse(decodeURIComponent(raw2));
3811
3371
  } catch {
3812
- value = raw;
3372
+ value = raw2;
3813
3373
  }
3814
3374
  }
3815
3375
  ctx.flash = {
@@ -3817,7 +3377,7 @@ function flash(options) {
3817
3377
  set: makeSetFlash(name, referer)
3818
3378
  };
3819
3379
  const res = await next(req, ctx);
3820
- if (raw) {
3380
+ if (raw2) {
3821
3381
  const headers = new Headers(res.headers);
3822
3382
  headers.append("Set-Cookie", `${name}=; Path=/; Max-Age=0`);
3823
3383
  return new Response(res.body, { status: res.status, statusText: res.statusText, headers });
@@ -3874,6 +3434,255 @@ function csrf(options) {
3874
3434
  mw.__meta = { injects: ["csrf"], depends: [] };
3875
3435
  return mw;
3876
3436
  }
3437
+
3438
+ // ssr/html.ts
3439
+ var ESCAPE_MAP = {
3440
+ "&": "&amp;",
3441
+ "<": "&lt;",
3442
+ ">": "&gt;",
3443
+ '"': "&quot;",
3444
+ "'": "&#39;"
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 } from "node:path";
3596
+ import tailwindPlugin from "@tailwindcss/postcss";
3597
+ import postcss from "postcss";
3598
+ var cssCache = /* @__PURE__ */ new Map();
3599
+ async function compileCSS(cssPath, sourceDir) {
3600
+ if (!existsSync(cssPath)) {
3601
+ return { css: "", hash: "empty", url: "" };
3602
+ }
3603
+ const raw2 = readFileSync2(cssPath, "utf-8");
3604
+ const src = `@source "${sourceDir}";
3605
+ ${raw2}`;
3606
+ const result = await postcss([tailwindPlugin()]).process(src, { from: cssPath });
3607
+ const hash = createHash("md5").update(result.css).digest("hex").slice(0, 8);
3608
+ const asset = { css: result.css, hash, url: `/__wfw/style/${hash}.css` };
3609
+ cssCache.set(cssPath, asset);
3610
+ return asset;
3611
+ }
3612
+ function cssContext(dir) {
3613
+ const appDir = resolve6(dir, "app");
3614
+ const cssPath = join3(appDir, "globals.css");
3615
+ let cached = null;
3616
+ return async (req, ctx, next) => {
3617
+ if (!cached) cached = compileCSS(cssPath, appDir);
3618
+ const asset = await cached;
3619
+ if (asset.css) ctx.css = asset;
3620
+ return next(req, ctx);
3621
+ };
3622
+ }
3623
+ function cssRouter(dir) {
3624
+ const router = new Router();
3625
+ router.get("/__wfw/style/:hash.css", async () => {
3626
+ const cssPath = join3(resolve6(dir, "app"), "globals.css");
3627
+ const asset = cssCache.get(cssPath);
3628
+ if (!asset) return new Response("", { status: 404 });
3629
+ return new Response(asset.css, {
3630
+ headers: { "content-type": "text/css; charset=utf-8" }
3631
+ });
3632
+ });
3633
+ return router;
3634
+ }
3635
+ function clearCSSCache() {
3636
+ cssCache.clear();
3637
+ }
3638
+
3639
+ // ssr/assets.ts
3640
+ import { readFileSync as readFileSync3 } from "node:fs";
3641
+ import { resolve as resolve7 } from "node:path";
3642
+ function resolvePackage(name, file) {
3643
+ return resolve7(process.cwd(), "node_modules", name, file);
3644
+ }
3645
+ var HTMX_PATH = resolvePackage("htmx.org", "dist/htmx.min.js");
3646
+ var ALPINE_PATH = resolvePackage("alpinejs", "dist/cdn.min.js");
3647
+ var htmxContent = null;
3648
+ var alpineContent = null;
3649
+ function loadAsset(path) {
3650
+ try {
3651
+ return readFileSync3(path, "utf-8");
3652
+ } catch {
3653
+ return null;
3654
+ }
3655
+ }
3656
+ function assetRouter() {
3657
+ const router = new Router();
3658
+ router.get("/__wfw/js/htmx.min.js", () => {
3659
+ if (!htmxContent) htmxContent = loadAsset(HTMX_PATH);
3660
+ if (!htmxContent) return new Response("HTMX not found", { status: 404 });
3661
+ return new Response(htmxContent, {
3662
+ headers: {
3663
+ "content-type": "application/javascript; charset=utf-8",
3664
+ "cache-control": "public, max-age=31536000, immutable"
3665
+ }
3666
+ });
3667
+ });
3668
+ router.get("/__wfw/js/alpine.min.js", () => {
3669
+ if (!alpineContent) alpineContent = loadAsset(ALPINE_PATH);
3670
+ if (!alpineContent) return new Response("Alpine not found", { status: 404 });
3671
+ return new Response(alpineContent, {
3672
+ headers: {
3673
+ "content-type": "application/javascript; charset=utf-8",
3674
+ "cache-control": "public, max-age=31536000, immutable"
3675
+ }
3676
+ });
3677
+ });
3678
+ return router;
3679
+ }
3680
+ function assetScripts() {
3681
+ return raw(`
3682
+ <script src="/__wfw/js/htmx.min.js"></script>
3683
+ <script defer src="/__wfw/js/alpine.min.js"></script>
3684
+ `);
3685
+ }
3877
3686
  export {
3878
3687
  DEFAULT_MAX_BODY,
3879
3688
  HttpError,
@@ -3883,6 +3692,10 @@ export {
3883
3692
  TestRequest,
3884
3693
  aiProvider,
3885
3694
  aiStream,
3695
+ assetRouter,
3696
+ assetScripts,
3697
+ clearCSSCache,
3698
+ clearModuleCache,
3886
3699
  compress,
3887
3700
  cors,
3888
3701
  createHub,
@@ -3891,6 +3704,8 @@ export {
3891
3704
  createTestDb,
3892
3705
  createTestServer,
3893
3706
  csrf,
3707
+ cssContext,
3708
+ cssRouter,
3894
3709
  currentTrace,
3895
3710
  currentTraceId,
3896
3711
  deleteCookie,
@@ -3907,16 +3722,20 @@ export {
3907
3722
  graphql,
3908
3723
  health,
3909
3724
  helmet,
3725
+ html,
3910
3726
  i18n,
3911
3727
  isBundled,
3912
3728
  isDev,
3913
3729
  isProd,
3730
+ layout,
3914
3731
  loadEnv,
3732
+ loadModule,
3915
3733
  logger,
3916
3734
  openai,
3917
3735
  postgres,
3918
3736
  queue,
3919
3737
  rateLimit,
3738
+ raw,
3920
3739
  redis,
3921
3740
  requestId,
3922
3741
  runWithTrace,
@@ -3933,5 +3752,6 @@ export {
3933
3752
  traceElapsed,
3934
3753
  upload,
3935
3754
  validate,
3755
+ view,
3936
3756
  withTestDb
3937
3757
  };