weifuwu 0.91.0 → 0.93.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.
@@ -270,8 +270,8 @@ async function sendResponse(res, response, opts) {
270
270
  res.writeHead(response.status, response.statusText, headers);
271
271
  if (response.body) {
272
272
  const reader = response.body.getReader();
273
- const closed = new Promise((resolve3) => {
274
- res.once("close", () => resolve3("closed"));
273
+ const closed = new Promise((resolve4) => {
274
+ res.once("close", () => resolve4("closed"));
275
275
  });
276
276
  res.on("error", () => {
277
277
  });
@@ -291,7 +291,7 @@ async function sendResponse(res, response, opts) {
291
291
  if (done) break;
292
292
  if (!res.write(value)) {
293
293
  const drained = new Promise(
294
- (resolve3) => res.once("drain", () => resolve3("drain"))
294
+ (resolve4) => res.once("drain", () => resolve4("drain"))
295
295
  );
296
296
  if (await Promise.race([drained, closed]) === "closed") {
297
297
  await cancelStream();
@@ -457,6 +457,101 @@ function serve(router, options) {
457
457
  // src/server/core/router.ts
458
458
  import { WebSocketServer } from "ws";
459
459
 
460
+ // src/server/db/errors.ts
461
+ var DbError = class extends Error {
462
+ kind;
463
+ code;
464
+ cause;
465
+ constructor(kind, message, options) {
466
+ super(message);
467
+ this.name = "DbError";
468
+ this.kind = kind;
469
+ this.code = options?.code;
470
+ this.cause = options?.cause;
471
+ }
472
+ };
473
+ var ProtocolError = class extends DbError {
474
+ constructor(feature, message) {
475
+ super("protocol", message ?? `${feature} is not supported by weifuwu/db`, { code: "UNSUPPORTED" });
476
+ this.name = "ProtocolError";
477
+ }
478
+ };
479
+ var ConnectionError = class extends DbError {
480
+ attempts;
481
+ constructor(message, attempts = 1, cause) {
482
+ super("connection", message, { cause });
483
+ this.name = "ConnectionError";
484
+ this.attempts = attempts;
485
+ }
486
+ };
487
+ var TimeoutError = class extends DbError {
488
+ operation;
489
+ ms;
490
+ constructor(operation, ms) {
491
+ super("timeout", `${operation} timed out after ${ms}ms`);
492
+ this.name = "TimeoutError";
493
+ this.operation = operation;
494
+ this.ms = ms;
495
+ }
496
+ };
497
+ var ValidationError = class extends DbError {
498
+ constructor(message) {
499
+ super("validation", message);
500
+ this.name = "ValidationError";
501
+ }
502
+ };
503
+
504
+ // src/server/response.ts
505
+ function ok(data, init) {
506
+ return Response.json(data, { status: 200, ...init });
507
+ }
508
+ function created(data, init) {
509
+ return Response.json(data, { status: 201, ...init });
510
+ }
511
+ function noContent(init) {
512
+ return new Response(null, { status: 204, ...init });
513
+ }
514
+ function badRequest(message) {
515
+ return Response.json({ error: message ?? "Bad Request" }, { status: 400 });
516
+ }
517
+ function unauthorized(message) {
518
+ return Response.json({ error: message ?? "Unauthorized" }, { status: 401 });
519
+ }
520
+ function forbidden(message) {
521
+ return Response.json({ error: message ?? "Forbidden" }, { status: 403 });
522
+ }
523
+ function notFound(message) {
524
+ return Response.json({ error: message ?? "Not Found" }, { status: 404 });
525
+ }
526
+ function conflict(message) {
527
+ return Response.json({ error: message ?? "Conflict" }, { status: 409 });
528
+ }
529
+ function unprocessable(message) {
530
+ return Response.json({ error: message ?? "Unprocessable Entity" }, { status: 422 });
531
+ }
532
+ function tooManyRequests(message) {
533
+ return Response.json({ error: message ?? "Too Many Requests" }, { status: 429 });
534
+ }
535
+ function serverError(message) {
536
+ return Response.json({ error: message ?? "Internal Server Error" }, { status: 500 });
537
+ }
538
+ function redirect(url, status = 302) {
539
+ return new Response(null, { status, headers: { Location: url } });
540
+ }
541
+ function errorResponse(e, status) {
542
+ const msg = e instanceof Error ? e.message : String(e);
543
+ let code;
544
+ if (e instanceof HttpError) {
545
+ return Response.json({ error: msg }, { status: status ?? e.status });
546
+ }
547
+ if (e instanceof DbError) {
548
+ const st = status ?? (e.code === "23505" ? 409 : 400);
549
+ code = e.code === "23505" ? "conflict" : e.kind;
550
+ return Response.json({ error: msg, code }, { status: st });
551
+ }
552
+ return Response.json({ error: msg }, { status: status ?? 400 });
553
+ }
554
+
460
555
  // src/server/core/ws.ts
461
556
  function safeHook(hookName, fn, onError) {
462
557
  if (!fn) return () => {
@@ -995,12 +1090,12 @@ async function runChain(mws, finalHandler, req, ctx) {
995
1090
  }
996
1091
 
997
1092
  // src/shared/router/pipeline.ts
998
- function badRequest(reason) {
1093
+ function badRequest2(reason) {
999
1094
  return Response.json({ error: "Bad Request", reason }, { status: 400 });
1000
1095
  }
1001
1096
  async function dispatchRouter(root, pipeline, req, ctx) {
1002
1097
  const target = parseRequestTarget(req);
1003
- if (!target.ok) return badRequest(target.reason);
1098
+ if (!target.ok) return badRequest2(target.reason);
1004
1099
  const m = trieMatch(root, target.segments);
1005
1100
  const match = m ? { value: m.value, params: freshParams(m), wildcard: m.wildcard } : null;
1006
1101
  pipeline.enrichCtx?.(ctx, match, target.pathname, target.query);
@@ -1356,11 +1451,8 @@ var Router = class _Router {
1356
1451
  async handleError(e, req, ctx, path) {
1357
1452
  const err = e instanceof Error ? e : new Error(String(e));
1358
1453
  if (this.errorHandler) return this.errorHandler(err, req, ctx);
1359
- if (err instanceof HttpError) {
1360
- return new Response(JSON.stringify({ error: err.message }), {
1361
- status: err.status,
1362
- headers: { "Content-Type": "application/json" }
1363
- });
1454
+ if (err instanceof HttpError || err instanceof DbError || err instanceof ValidationError) {
1455
+ return errorResponse(err);
1364
1456
  }
1365
1457
  noteHandlerError(`${req.method} ${path ?? new URL(req.url).pathname}`, e);
1366
1458
  return Response.json({ error: "Internal Server Error" }, { status: 500 });
@@ -1394,15 +1486,15 @@ var Router = class _Router {
1394
1486
  } catch {
1395
1487
  }
1396
1488
  }
1397
- await new Promise((resolve3) => {
1489
+ await new Promise((resolve4) => {
1398
1490
  let remaining = clients.length;
1399
1491
  const done = () => {
1400
1492
  if (--remaining === 0) {
1401
1493
  clearTimeout(timer);
1402
- resolve3();
1494
+ resolve4();
1403
1495
  }
1404
1496
  };
1405
- const timer = setTimeout(resolve3, 500);
1497
+ const timer = setTimeout(resolve4, 500);
1406
1498
  for (const c of clients) c.once("close", done);
1407
1499
  });
1408
1500
  }
@@ -1920,7 +2012,7 @@ async function MemoryEmailServer(options = {}) {
1920
2012
  res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
1921
2013
  });
1922
2014
  });
1923
- await new Promise((resolve3) => server.listen(options.port ?? 0, "127.0.0.1", resolve3));
2015
+ await new Promise((resolve4) => server.listen(options.port ?? 0, "127.0.0.1", resolve4));
1924
2016
  const port = server.address().port;
1925
2017
  return {
1926
2018
  port,
@@ -1928,7 +2020,7 @@ async function MemoryEmailServer(options = {}) {
1928
2020
  emails,
1929
2021
  requests,
1930
2022
  closeAllConnections: () => server.closeAllConnections(),
1931
- close: () => new Promise((resolve3, reject) => server.close((e) => e ? reject(e) : resolve3()))
2023
+ close: () => new Promise((resolve4, reject) => server.close((e) => e ? reject(e) : resolve4()))
1932
2024
  };
1933
2025
  }
1934
2026
 
@@ -2521,44 +2613,6 @@ function hashRefreshToken(token) {
2521
2613
  return createHash("sha256").update(token).digest("hex");
2522
2614
  }
2523
2615
 
2524
- // src/server/response.ts
2525
- function ok(data, init) {
2526
- return Response.json(data, { status: 200, ...init });
2527
- }
2528
- function created(data, init) {
2529
- return Response.json(data, { status: 201, ...init });
2530
- }
2531
- function noContent(init) {
2532
- return new Response(null, { status: 204, ...init });
2533
- }
2534
- function badRequest2(message) {
2535
- return Response.json({ error: message ?? "Bad Request" }, { status: 400 });
2536
- }
2537
- function unauthorized(message) {
2538
- return Response.json({ error: message ?? "Unauthorized" }, { status: 401 });
2539
- }
2540
- function forbidden(message) {
2541
- return Response.json({ error: message ?? "Forbidden" }, { status: 403 });
2542
- }
2543
- function notFound(message) {
2544
- return Response.json({ error: message ?? "Not Found" }, { status: 404 });
2545
- }
2546
- function conflict(message) {
2547
- return Response.json({ error: message ?? "Conflict" }, { status: 409 });
2548
- }
2549
- function unprocessable(message) {
2550
- return Response.json({ error: message ?? "Unprocessable Entity" }, { status: 422 });
2551
- }
2552
- function tooManyRequests(message) {
2553
- return Response.json({ error: message ?? "Too Many Requests" }, { status: 429 });
2554
- }
2555
- function serverError(message) {
2556
- return Response.json({ error: message ?? "Internal Server Error" }, { status: 500 });
2557
- }
2558
- function redirect(url, status = 302) {
2559
- return new Response(null, { status, headers: { Location: url } });
2560
- }
2561
-
2562
2616
  // src/server/user/index.ts
2563
2617
  import { randomBytes as randomBytes3 } from "node:crypto";
2564
2618
  var USER_TABLES = {
@@ -3116,13 +3170,13 @@ function userSystem(options) {
3116
3170
  });
3117
3171
  if (!excluded.has("login")) app.post(`${p}/login`, async (req, ctx) => {
3118
3172
  const body = await req.json().catch(() => ({}));
3119
- if (!body.email || !body.password) return badRequest2("email and password are required");
3173
+ if (!body.email || !body.password) return badRequest("email and password are required");
3120
3174
  const result = await ctx.auth.login(body.email, body.password);
3121
3175
  return ok(result);
3122
3176
  });
3123
3177
  if (!excluded.has("logout")) app.post(`${p}/logout`, async (req, ctx) => {
3124
3178
  const body = await req.json().catch(() => ({}));
3125
- if (!body.refreshToken) return badRequest2("refreshToken is required");
3179
+ if (!body.refreshToken) return badRequest("refreshToken is required");
3126
3180
  await ctx.auth.logout(body.refreshToken);
3127
3181
  return noContent();
3128
3182
  });
@@ -3224,13 +3278,13 @@ function userSystem(options) {
3224
3278
  });
3225
3279
  app.post(`${p}/apps/:appSlug/auth/login`, async (req, ctx) => {
3226
3280
  const body = await req.json().catch(() => ({}));
3227
- if (!body.email || !body.password) return badRequest2("email and password are required");
3281
+ if (!body.email || !body.password) return badRequest("email and password are required");
3228
3282
  const result = await ctx.auth.loginApp(ctx.params.appSlug, body.email, body.password);
3229
3283
  return ok(result);
3230
3284
  });
3231
3285
  app.patch(`${p}/apps/:appSlug/auth/registration`, async (req, ctx) => {
3232
3286
  const body = await req.json().catch(() => ({}));
3233
- if (typeof body.open !== "boolean") return badRequest2("open is required (boolean)");
3287
+ if (typeof body.open !== "boolean") return badRequest("open is required (boolean)");
3234
3288
  const appId = await findAppIdBySlug(ctx.params.appSlug);
3235
3289
  if (!appId) throw new HttpError("Application not found", 404);
3236
3290
  await ctx.auth.setOpenRegistration(appId, body.open);
@@ -3239,7 +3293,7 @@ function userSystem(options) {
3239
3293
  app.post(`${p}/apps/:appSlug/auth/verify`, async (req, ctx) => {
3240
3294
  const appId = req.headers.get("x-wf-app-id");
3241
3295
  const appKey = req.headers.get("x-wf-app-key");
3242
- if (!appId || !appKey) return badRequest2("X-Wf-App-Id / X-Wf-App-Key required");
3296
+ if (!appId || !appKey) return badRequest("X-Wf-App-Id / X-Wf-App-Key required");
3243
3297
  const rows = await A.select("id", "slug", "name").where({ id: { eq: appId }, app_key: { eq: appKey } }).run();
3244
3298
  if (!rows.length) throw new HttpError("Invalid app credentials", 403);
3245
3299
  const a = rows[0];
@@ -3390,50 +3444,6 @@ function appAuth(options) {
3390
3444
  return mw;
3391
3445
  }
3392
3446
 
3393
- // src/server/db/errors.ts
3394
- var DbError = class extends Error {
3395
- kind;
3396
- code;
3397
- cause;
3398
- constructor(kind, message, options) {
3399
- super(message);
3400
- this.name = "DbError";
3401
- this.kind = kind;
3402
- this.code = options?.code;
3403
- this.cause = options?.cause;
3404
- }
3405
- };
3406
- var ProtocolError = class extends DbError {
3407
- constructor(feature, message) {
3408
- super("protocol", message ?? `${feature} is not supported by weifuwu/db`, { code: "UNSUPPORTED" });
3409
- this.name = "ProtocolError";
3410
- }
3411
- };
3412
- var ConnectionError = class extends DbError {
3413
- attempts;
3414
- constructor(message, attempts = 1, cause) {
3415
- super("connection", message, { cause });
3416
- this.name = "ConnectionError";
3417
- this.attempts = attempts;
3418
- }
3419
- };
3420
- var TimeoutError = class extends DbError {
3421
- operation;
3422
- ms;
3423
- constructor(operation, ms) {
3424
- super("timeout", `${operation} timed out after ${ms}ms`);
3425
- this.name = "TimeoutError";
3426
- this.operation = operation;
3427
- this.ms = ms;
3428
- }
3429
- };
3430
- var ValidationError = class extends DbError {
3431
- constructor(message) {
3432
- super("validation", message);
3433
- this.name = "ValidationError";
3434
- }
3435
- };
3436
-
3437
3447
  // src/server/db/body.ts
3438
3448
  function shapeOf(s) {
3439
3449
  const sh = s.__shape;
@@ -3510,11 +3520,6 @@ function listQuery(url, shapeDef, opts = {}) {
3510
3520
  const offset = Math.max(0, parseInt(search.get("offset") ?? "0", 10) || 0);
3511
3521
  return { filter: filter2, sort, limit, offset };
3512
3522
  }
3513
- function errorResponse(e, status) {
3514
- const msg = e instanceof Error ? e.message : String(e);
3515
- const st = status ?? (e instanceof DbError ? e.code === "23505" ? 409 : 400 : 400);
3516
- return Response.json({ error: msg }, { status: st });
3517
- }
3518
3523
 
3519
3524
  // src/server/db/schema.ts
3520
3525
  function innerOf(t) {
@@ -5332,7 +5337,7 @@ function messager(options) {
5332
5337
  function sendTo(userId, event) {
5333
5338
  broadcast(`user:${userId}`, event);
5334
5339
  }
5335
- function join(room, ws2) {
5340
+ function join2(room, ws2) {
5336
5341
  let members = rooms.get(room);
5337
5342
  if (!members) {
5338
5343
  members = /* @__PURE__ */ new Set();
@@ -5390,7 +5395,7 @@ function messager(options) {
5390
5395
  return;
5391
5396
  }
5392
5397
  }
5393
- join(msg.room, ws2);
5398
+ join2(msg.room, ws2);
5394
5399
  ws2.send(JSON.stringify({ type: "subscribed", room: msg.room }));
5395
5400
  } else if (msg.type === "unsubscribe" && msg.room) {
5396
5401
  leave(msg.room, ws2);
@@ -5423,7 +5428,7 @@ function messager(options) {
5423
5428
  handler,
5424
5429
  broadcast,
5425
5430
  sendTo,
5426
- join,
5431
+ join: join2,
5427
5432
  leave,
5428
5433
  close,
5429
5434
  createConversation,
@@ -5462,7 +5467,7 @@ function messager(options) {
5462
5467
  const conv = await createConversation(ctx.user.id, { type: "group", memberIds: body.memberIds });
5463
5468
  return created(conv);
5464
5469
  }
5465
- return badRequest2("conversation input invalid");
5470
+ return badRequest("conversation input invalid");
5466
5471
  });
5467
5472
  app.get(`${p}/conversations`, async (_req, ctx) => {
5468
5473
  if (!ctx.user) throw new HttpError("Unauthorized", 401);
@@ -5482,7 +5487,7 @@ function messager(options) {
5482
5487
  const convId = ctx.params.id;
5483
5488
  await requireMember(convId, ctx);
5484
5489
  const body = await req.json().catch(() => ({}));
5485
- if (!body.content?.trim()) return badRequest2("content is required");
5490
+ if (!body.content?.trim()) return badRequest("content is required");
5486
5491
  const message = await sendMessage(convId, {
5487
5492
  senderType: "user",
5488
5493
  senderId: ctx.user.id,
@@ -5503,15 +5508,15 @@ function messager(options) {
5503
5508
  if (!ctx.user) throw new HttpError("Unauthorized", 401);
5504
5509
  const messageId = ctx.params.id;
5505
5510
  const body = await req.json().catch(() => ({}));
5506
- if (!body.content?.trim()) return badRequest2("content is required");
5511
+ if (!body.content?.trim()) return badRequest("content is required");
5507
5512
  const rows = await orm.query.from(`${MESSAGES} m`).join(`${MEMBERS} mem`, { "mem.conversation_id": { col: "m.conversation_id" } }).where({ "m.id": { eq: messageId }, "mem.user_id": { eq: String(ctx.user.id) } }).select("m.conversation_id", "m.sender_type", "m.sender_id").run();
5508
- if (!rows.length) return badRequest2("message not found");
5513
+ if (!rows.length) return badRequest("message not found");
5509
5514
  const row = rows[0];
5510
5515
  if (row.sender_type === "user" && row.sender_id !== ctx.user.id) {
5511
5516
  throw new HttpError("Forbidden: not your message", 403);
5512
5517
  }
5513
5518
  const edited = await editMessage(messageId, body.content);
5514
- if (!edited) return badRequest2("message not found");
5519
+ if (!edited) return badRequest("message not found");
5515
5520
  broadcast(`conv:${edited.conversation_id}`, { type: "message_edited", message: edited });
5516
5521
  return ok(edited);
5517
5522
  });
@@ -5519,7 +5524,7 @@ function messager(options) {
5519
5524
  if (!ctx.user) throw new HttpError("Unauthorized", 401);
5520
5525
  const messageId = ctx.params.id;
5521
5526
  const rows = await orm.query.from(`${MESSAGES} m`).join(`${MEMBERS} mem`, { "mem.conversation_id": { col: "m.conversation_id" } }).where({ "m.id": { eq: messageId }, "mem.user_id": { eq: String(ctx.user.id) } }).select("m.conversation_id", "m.sender_type", "m.sender_id").run();
5522
- if (!rows.length) return badRequest2("message not found");
5527
+ if (!rows.length) return badRequest("message not found");
5523
5528
  const row = rows[0];
5524
5529
  if (row.sender_type === "user" && row.sender_id !== ctx.user.id) {
5525
5530
  throw new HttpError("Forbidden: not your message", 403);
@@ -8230,6 +8235,7 @@ function createScheduler(orm, execute, opts = {}) {
8230
8235
  start: () => {
8231
8236
  if (timer) return;
8232
8237
  timer = setInterval(() => void tick(), intervalMs);
8238
+ timer.unref?.();
8233
8239
  void tick();
8234
8240
  },
8235
8241
  stop: () => {
@@ -9481,23 +9487,23 @@ function createAiClient(opts) {
9481
9487
  }
9482
9488
  const approvals = /* @__PURE__ */ new Map();
9483
9489
  function approve(response) {
9484
- const resolve3 = approvals.get(response.id);
9485
- if (!resolve3) return false;
9490
+ const resolve4 = approvals.get(response.id);
9491
+ if (!resolve4) return false;
9486
9492
  approvals.delete(response.id);
9487
- resolve3(response);
9493
+ resolve4(response);
9488
9494
  return true;
9489
9495
  }
9490
9496
  async function waitApproval(req, emit, timeoutMs = DEFAULT_APPROVAL_TIMEOUT, signal) {
9491
9497
  const expiresAt = Date.now() + timeoutMs;
9492
9498
  emit("wf:approval_request", { ...req, expiresAt });
9493
- return new Promise((resolve3) => {
9499
+ return new Promise((resolve4) => {
9494
9500
  let settled = false;
9495
9501
  const finish = (resp) => {
9496
9502
  if (settled) return;
9497
9503
  settled = true;
9498
9504
  clearTimeout(timer);
9499
9505
  signal?.removeEventListener("abort", onAbort);
9500
- resolve3(resp);
9506
+ resolve4(resp);
9501
9507
  };
9502
9508
  const timer = setTimeout(() => {
9503
9509
  if (approvals.has(req.id)) approvals.delete(req.id);
@@ -9969,23 +9975,23 @@ function createApprovalHub(timeoutMs = DEFAULT_APPROVAL_TIMEOUT2) {
9969
9975
  const approvals = /* @__PURE__ */ new Map();
9970
9976
  return {
9971
9977
  approve(response) {
9972
- const resolve3 = approvals.get(response.id);
9973
- if (!resolve3) return false;
9978
+ const resolve4 = approvals.get(response.id);
9979
+ if (!resolve4) return false;
9974
9980
  approvals.delete(response.id);
9975
- resolve3(response);
9981
+ resolve4(response);
9976
9982
  return true;
9977
9983
  },
9978
9984
  async waitApproval(req, emit, timeout = timeoutMs, signal) {
9979
9985
  const expiresAt = Date.now() + timeout;
9980
9986
  emit("wf:approval_request", { ...req, expiresAt });
9981
- return new Promise((resolve3) => {
9987
+ return new Promise((resolve4) => {
9982
9988
  let settled = false;
9983
9989
  const finish = (resp) => {
9984
9990
  if (settled) return;
9985
9991
  settled = true;
9986
9992
  clearTimeout(timer);
9987
9993
  signal?.removeEventListener("abort", onAbort);
9988
- resolve3(resp);
9994
+ resolve4(resp);
9989
9995
  };
9990
9996
  const timer = setTimeout(() => {
9991
9997
  if (approvals.has(req.id)) approvals.delete(req.id);
@@ -10199,7 +10205,7 @@ async function MemoryAiServer(options = {}) {
10199
10205
  }
10200
10206
  });
10201
10207
  });
10202
- await new Promise((resolve3) => server.listen(options.port ?? 0, "127.0.0.1", resolve3));
10208
+ await new Promise((resolve4) => server.listen(options.port ?? 0, "127.0.0.1", resolve4));
10203
10209
  const addr = server.address();
10204
10210
  const port = typeof addr === "object" && addr ? addr.port : 0;
10205
10211
  return {
@@ -10207,7 +10213,7 @@ async function MemoryAiServer(options = {}) {
10207
10213
  url: `http://127.0.0.1:${port}`,
10208
10214
  requests,
10209
10215
  closeAllConnections: () => server.closeAllConnections(),
10210
- close: () => new Promise((resolve3) => server.close(() => resolve3()))
10216
+ close: () => new Promise((resolve4) => server.close(() => resolve4()))
10211
10217
  };
10212
10218
  }
10213
10219
  async function readJson(req) {
@@ -10298,11 +10304,98 @@ async function handle(ai, method, url, req, res, preBody) {
10298
10304
 
10299
10305
  // src/server/ui/index.ts
10300
10306
  import { build } from "esbuild";
10301
- import { readFile, stat as stat2 } from "node:fs/promises";
10307
+ import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
10302
10308
  import { createHash as createHash2 } from "node:crypto";
10303
- import { resolve as resolve2 } from "node:path";
10309
+ import { resolve as resolve3, dirname, basename } from "node:path";
10304
10310
  import { fileURLToPath } from "node:url";
10305
10311
 
10312
+ // src/client/layout/bundle.ts
10313
+ import { readFile, readdir } from "node:fs/promises";
10314
+ import { join, resolve as resolve2 } from "node:path";
10315
+ var LAYER_ORDER = ["tokens", "base", "layout", "components", "utilities"];
10316
+ var LAYER_OF = {
10317
+ _tokens: "tokens",
10318
+ _dark: "tokens",
10319
+ _presets: "tokens",
10320
+ _props: "tokens",
10321
+ _base: "base",
10322
+ _stack: "layout",
10323
+ _row: "layout",
10324
+ _split: "layout",
10325
+ _center: "layout",
10326
+ _justify: "layout",
10327
+ _fill: "layout",
10328
+ _grid: "layout",
10329
+ _cluster: "layout",
10330
+ _cover: "layout",
10331
+ _position: "layout",
10332
+ _sticky: "layout",
10333
+ _overflow: "layout",
10334
+ "_safe-area": "layout",
10335
+ _layer: "layout",
10336
+ "_align-self": "layout",
10337
+ _nowrap: "layout",
10338
+ _shrink: "layout",
10339
+ _container: "layout",
10340
+ "_app-shell": "layout",
10341
+ _surface: "utilities",
10342
+ _spacing: "utilities",
10343
+ _border: "utilities",
10344
+ _text: "utilities",
10345
+ _hidden: "utilities",
10346
+ _block: "utilities",
10347
+ _flex: "utilities",
10348
+ // display 工具族(基类 :where() 零优先级 + @media 变体正常优先级——变体恒胜基类,零 !important)
10349
+ _popup: "layout"
10350
+ // 框架内部浮层基类(popup-manager 消费——非用户词汇)
10351
+ };
10352
+ var TOKENS_LAYER = "tokens";
10353
+ var stripImports = (css) => css.replace(/@import\s+['"][^'"]+['"]\s*;?\s*\n?/g, "");
10354
+ async function layoutEntryFiles(layoutDir) {
10355
+ const entry = await readFile(join(layoutDir, "weifuwu-layout.css"), "utf-8");
10356
+ const files = [];
10357
+ for (const line of entry.split("\n")) {
10358
+ const m = line.match(/@import\s+['"]([^'"]+)['"]/);
10359
+ if (m) files.push(m[1].replace(/^\.\//, ""));
10360
+ }
10361
+ if (!files.length) throw new Error(`layout \u5165\u53E3\u65E0 @import\uFF08${layoutDir}/weifuwu-layout.css\uFF09\u2014\u2014\u88C5\u914D\u5355\u6E90\u5931\u6548`);
10362
+ return files;
10363
+ }
10364
+ async function isLayoutSourceDir(dir) {
10365
+ try {
10366
+ const entries = await readdir(dir);
10367
+ return entries.includes("weifuwu-layout.css") && entries.some((f2) => /^_[a-z-]+\.css$/.test(f2));
10368
+ } catch {
10369
+ return false;
10370
+ }
10371
+ }
10372
+ async function bundleLayout(layoutDir) {
10373
+ const dir = resolve2(layoutDir);
10374
+ const files = await layoutEntryFiles(dir);
10375
+ const inputs = [join(dir, "weifuwu-layout.css")];
10376
+ const chunks = [];
10377
+ for (const f2 of files) {
10378
+ const abs = join(dir, f2);
10379
+ inputs.push(abs);
10380
+ const content = stripImports(await readFile(abs, "utf-8")).trim();
10381
+ const name = f2.replace(/\.css$/, "");
10382
+ const layer = LAYER_OF[name];
10383
+ if (!layer) {
10384
+ throw new Error(`layout \u6587\u4EF6\u672A\u767B\u8BB0 @layer \u6620\u5C04: ${f2}\uFF08\u5728 src/client/layout/bundle.ts LAYER_OF \u4E2D\u767B\u8BB0\uFF09`);
10385
+ }
10386
+ chunks.push(layer === TOKENS_LAYER ? content : `@layer ${layer} {
10387
+ ${content}
10388
+ }`);
10389
+ }
10390
+ const css = `/* weifuwu/layout \u2014 CSS \u5E03\u5C40\u539F\u8BED + \u8BBE\u8BA1 Token\uFF08\u88C5\u914D\u5355\u6E90\uFF1Asrc/client/layout/bundle.ts\uFF09 */
10391
+
10392
+ @layer ${LAYER_ORDER.join(", ")};
10393
+
10394
+ ${chunks.join("\n\n")}
10395
+ `;
10396
+ return { css, inputs };
10397
+ }
10398
+
10306
10399
  // src/server/ui/html-safe.ts
10307
10400
  var HtmlSafe = class {
10308
10401
  value;
@@ -12886,11 +12979,11 @@ function openPopup(env, opts) {
12886
12979
  const registry = applier.registry;
12887
12980
  if (!registry) return;
12888
12981
  const ctx = env.getSharedContext() ?? {};
12889
- const cmds = await new Promise((resolve3, reject) => {
12982
+ const cmds = await new Promise((resolve4, reject) => {
12890
12983
  const out = [];
12891
12984
  const req = () => env.requestRender?.();
12892
12985
  const obs = prev ? diffV2(prev, content, ctx, state2.segments, registry, req) : renderV2(content, ctx, registry, state2.segments, req);
12893
- obs.subscribe({ next: (c) => out.push(c), error: reject, complete: () => resolve3(out) });
12986
+ obs.subscribe({ next: (c) => out.push(c), error: reject, complete: () => resolve4(out) });
12894
12987
  });
12895
12988
  for (const value of cmds) {
12896
12989
  if (state2.disposed || v !== state2.version) return;
@@ -14166,9 +14259,9 @@ function concatWith(tail) {
14166
14259
 
14167
14260
  // src/client/vdom/core/v2/integrate.ts
14168
14261
  function collectCommands(obs) {
14169
- return new Promise((resolve3, reject) => {
14262
+ return new Promise((resolve4, reject) => {
14170
14263
  const out = [];
14171
- obs.subscribe({ next: (c) => out.push(c), error: reject, complete: () => resolve3(out) });
14264
+ obs.subscribe({ next: (c) => out.push(c), error: reject, complete: () => resolve4(out) });
14172
14265
  });
14173
14266
  }
14174
14267
  async function v2ToHtml(root, ctx, registry) {
@@ -14205,6 +14298,35 @@ function unsafe(s) {
14205
14298
  return new HtmlSafe(s);
14206
14299
  }
14207
14300
  var MAX_CACHE_ENTRIES = 32;
14301
+ async function statAll(paths) {
14302
+ const out = {};
14303
+ for (const p of paths) {
14304
+ try {
14305
+ const st = await stat2(p);
14306
+ out[p] = { mtimeMs: st.mtimeMs, size: st.size };
14307
+ } catch {
14308
+ out[p] = { mtimeMs: -1, size: -1 };
14309
+ }
14310
+ }
14311
+ return out;
14312
+ }
14313
+ async function importClosure(absPath, seen = /* @__PURE__ */ new Set()) {
14314
+ if (seen.has(absPath)) return [];
14315
+ seen.add(absPath);
14316
+ let css;
14317
+ try {
14318
+ css = await readFile2(absPath, "utf-8");
14319
+ } catch {
14320
+ return [];
14321
+ }
14322
+ const dir = dirname(absPath);
14323
+ const out = [];
14324
+ for (const m of css.matchAll(/@import\s+['"](\.[^'"]+\.css)['"]/g)) {
14325
+ const dep = resolve3(dir, m[1]);
14326
+ out.push(dep, ...await importClosure(dep, seen));
14327
+ }
14328
+ return out;
14329
+ }
14208
14330
  var postcssAvailable;
14209
14331
  async function checkPostcss() {
14210
14332
  if (postcssAvailable !== void 0) return postcssAvailable;
@@ -14219,12 +14341,12 @@ async function checkPostcss() {
14219
14341
  }
14220
14342
  function resolveEntry(entryPath) {
14221
14343
  if (entryPath.startsWith(".") || entryPath.startsWith("/")) {
14222
- return resolve2(entryPath);
14344
+ return resolve3(entryPath);
14223
14345
  }
14224
14346
  try {
14225
14347
  return fileURLToPath(import.meta.resolve(entryPath));
14226
14348
  } catch {
14227
- return resolve2(entryPath);
14349
+ return resolve3(entryPath);
14228
14350
  }
14229
14351
  }
14230
14352
  function ui(options = {}) {
@@ -14330,7 +14452,7 @@ function ui(options = {}) {
14330
14452
  });
14331
14453
  const inputs = {};
14332
14454
  for (const key of Object.keys(result.metafile?.inputs ?? {})) {
14333
- const abs = resolve2(key);
14455
+ const abs = resolve3(key);
14334
14456
  try {
14335
14457
  const st = await stat2(abs);
14336
14458
  inputs[abs] = { mtimeMs: st.mtimeMs, size: st.size };
@@ -14345,14 +14467,13 @@ function ui(options = {}) {
14345
14467
  async css(entryPath) {
14346
14468
  const absPath = resolveEntry(entryPath);
14347
14469
  const { code, etag } = await compile2("css", absPath, async () => {
14348
- let code2 = await readFile(absPath, "utf-8");
14349
- const inputs = {};
14350
- try {
14351
- const st = await stat2(absPath);
14352
- inputs[absPath] = { mtimeMs: st.mtimeMs, size: st.size };
14353
- } catch {
14354
- inputs[absPath] = { mtimeMs: -1, size: -1 };
14470
+ const dir = dirname(absPath);
14471
+ if (basename(absPath) === "weifuwu-layout.css" && await isLayoutSourceDir(dir)) {
14472
+ const bundle = await bundleLayout(dir);
14473
+ return { code: bundle.css, inputs: await statAll(bundle.inputs) };
14355
14474
  }
14475
+ let code2 = await readFile2(absPath, "utf-8");
14476
+ const inputs = await statAll([absPath, ...await importClosure(absPath)]);
14356
14477
  if (await checkPostcss()) {
14357
14478
  try {
14358
14479
  const postcss = await import("postcss");
@@ -14695,7 +14816,7 @@ var PgConnection = class _PgConnection {
14695
14816
  /** 池空闲回收用:最近一次查询完成时间戳(pool release 时更新) */
14696
14817
  lastUsed = 0;
14697
14818
  connect() {
14698
- return new Promise((resolve3, reject) => {
14819
+ return new Promise((resolve4, reject) => {
14699
14820
  this.status = "connecting";
14700
14821
  const sock = net.connect(this.opts.port, this.opts.host);
14701
14822
  this.socket = sock;
@@ -14727,7 +14848,7 @@ var PgConnection = class _PgConnection {
14727
14848
  this.onReady = () => {
14728
14849
  clearTimeout(timeout);
14729
14850
  this.status = "ready";
14730
- resolve3();
14851
+ resolve4();
14731
14852
  };
14732
14853
  this.onAuthFail = (err) => {
14733
14854
  clearTimeout(timeout);
@@ -15024,7 +15145,7 @@ var PgConnection = class _PgConnection {
15024
15145
  }
15025
15146
  /** 查询:无参数走简单协议(Q),有参数走扩展查询(Parse/Bind/Execute/Sync) */
15026
15147
  query(sql, params) {
15027
- return new Promise((resolve3, reject) => {
15148
+ return new Promise((resolve4, reject) => {
15028
15149
  if (this.status !== "ready" || !this.socket) {
15029
15150
  reject(new ConnectionError("postgres: not connected"));
15030
15151
  return;
@@ -15037,7 +15158,7 @@ var PgConnection = class _PgConnection {
15037
15158
  this.currentQuery = {
15038
15159
  columns: [],
15039
15160
  rows: [],
15040
- resolve: resolve3,
15161
+ resolve: resolve4,
15041
15162
  reject
15042
15163
  };
15043
15164
  if (process.env.PGDBG2) console.error("[cquery]", JSON.stringify(sql.slice(0, 120)));
@@ -15047,12 +15168,12 @@ var PgConnection = class _PgConnection {
15047
15168
  let stmtEntry = this.getPrepared(sig);
15048
15169
  const encoded = encodeParams(params);
15049
15170
  if (!stmtEntry) {
15050
- this.issueExtendedQuery(sql, encoded, resolve3, reject);
15171
+ this.issueExtendedQuery(sql, encoded, resolve4, reject);
15051
15172
  } else {
15052
15173
  this.currentQuery = {
15053
15174
  columns: [...stmtEntry.columns],
15054
15175
  rows: [],
15055
- resolve: resolve3,
15176
+ resolve: resolve4,
15056
15177
  reject,
15057
15178
  sql,
15058
15179
  params: encoded,
@@ -15071,12 +15192,12 @@ var PgConnection = class _PgConnection {
15071
15192
  * 扩展查询首次准备:Parse + Describe + Flush(无 Sync——错误时由调用方发 Sync 复位)。
15072
15193
  * retry 路径复用:新语句名强制服务器按当前 schema 重新解析(DDL 失效恢复)。
15073
15194
  */
15074
- issueExtendedQuery(sql, params, resolve3, reject, retried) {
15195
+ issueExtendedQuery(sql, params, resolve4, reject, retried) {
15075
15196
  const name = `wf_s${++this.stmtSeq}`;
15076
15197
  this.currentQuery = {
15077
15198
  columns: [],
15078
15199
  rows: [],
15079
- resolve: resolve3,
15200
+ resolve: resolve4,
15080
15201
  reject,
15081
15202
  sql,
15082
15203
  params,
@@ -15100,7 +15221,7 @@ var PgConnection = class _PgConnection {
15100
15221
  /** 等待连接空闲(事务/多语句流程用) */
15101
15222
  waitIdle() {
15102
15223
  if (!this.currentQuery) return Promise.resolve();
15103
- return new Promise((resolve3) => this.waiters.push(resolve3));
15224
+ return new Promise((resolve4) => this.waiters.push(resolve4));
15104
15225
  }
15105
15226
  /** 终止连接 */
15106
15227
  async close() {
@@ -15321,9 +15442,9 @@ var PgPool = class _PgPool {
15321
15442
  );
15322
15443
  }
15323
15444
  const timeoutMs = this.opts.acquireTimeoutMs ?? 3e4;
15324
- return new Promise((resolve3, reject) => {
15445
+ return new Promise((resolve4, reject) => {
15325
15446
  const waiter = {
15326
- resolve: resolve3,
15447
+ resolve: resolve4,
15327
15448
  reject
15328
15449
  };
15329
15450
  if (timeoutMs > 0) {
@@ -16773,9 +16894,9 @@ var RedisConnection = class {
16773
16894
  connect() {
16774
16895
  if (this.connectPromise) return this.connectPromise;
16775
16896
  this.closed = false;
16776
- this.connectPromise = new Promise((resolve3, reject) => {
16897
+ this.connectPromise = new Promise((resolve4, reject) => {
16777
16898
  this.openSocket();
16778
- this.onceReady = () => resolve3();
16899
+ this.onceReady = () => resolve4();
16779
16900
  this.onceFailed = (err) => reject(err);
16780
16901
  }).finally(() => {
16781
16902
  this.connectPromise = null;
@@ -16883,15 +17004,15 @@ var RedisConnection = class {
16883
17004
  if (this.offlineQueue.length >= this.opts.maxOfflineQueue) {
16884
17005
  return Promise.reject(new ConnectionError(`redis: offline queue full (${this.opts.maxOfflineQueue})`));
16885
17006
  }
16886
- return new Promise((resolve3, reject) => {
16887
- this.offlineQueue.push({ name, args, resolve: resolve3, reject, asBuffer: opts?.asBuffer });
17007
+ return new Promise((resolve4, reject) => {
17008
+ this.offlineQueue.push({ name, args, resolve: resolve4, reject, asBuffer: opts?.asBuffer });
16888
17009
  });
16889
17010
  }
16890
17011
  sendNow(name, args, asBuffer) {
16891
17012
  const start = performance.now();
16892
- return new Promise((resolve3, reject) => {
17013
+ return new Promise((resolve4, reject) => {
16893
17014
  const p = {
16894
- resolve: resolve3,
17015
+ resolve: resolve4,
16895
17016
  reject,
16896
17017
  asBuffer,
16897
17018
  blocking: BLOCKING_COMMANDS.has(name.toUpperCase())
@@ -16961,7 +17082,7 @@ var RedisConnection = class {
16961
17082
  if (this.status !== "ready" || !this.socket) {
16962
17083
  return Promise.reject(new ConnectionError("redis: not connected"));
16963
17084
  }
16964
- return new Promise((resolve3, reject) => {
17085
+ return new Promise((resolve4, reject) => {
16965
17086
  const results = [];
16966
17087
  const batchPending = [];
16967
17088
  let settled = false;
@@ -16970,7 +17091,7 @@ var RedisConnection = class {
16970
17091
  if (results.length === count) {
16971
17092
  settled = true;
16972
17093
  if (batchTimer) clearTimeout(batchTimer);
16973
- resolve3(results);
17094
+ resolve4(results);
16974
17095
  }
16975
17096
  };
16976
17097
  for (let i = 0; i < count; i++) {
@@ -17361,9 +17482,9 @@ var RedisPool = class _RedisPool {
17361
17482
  });
17362
17483
  this.replenish();
17363
17484
  }
17364
- return new Promise((resolve3, reject) => {
17485
+ return new Promise((resolve4, reject) => {
17365
17486
  const waiter = () => {
17366
- if (this.clients.length > 0 && this.clients[0].connected) resolve3(this.clients[0]);
17487
+ if (this.clients.length > 0 && this.clients[0].connected) resolve4(this.clients[0]);
17367
17488
  else reject(new ConnectionError("redis: pool exhausted after reconnect wait"));
17368
17489
  };
17369
17490
  this.replenishWaiters.push(waiter);
@@ -18494,13 +18615,13 @@ var MemoryRedisServer = class {
18494
18615
  }
18495
18616
  async start() {
18496
18617
  if (this.server) return;
18497
- await new Promise((resolve3) => {
18618
+ await new Promise((resolve4) => {
18498
18619
  this.server = net3.createServer((sock) => this.handleSocket(sock));
18499
18620
  this.server.listen(this.opts.port, "127.0.0.1", () => {
18500
18621
  const addr = this.server.address();
18501
18622
  this.port = addr.port;
18502
18623
  this.url = `redis://127.0.0.1:${addr.port}`;
18503
- resolve3();
18624
+ resolve4();
18504
18625
  });
18505
18626
  });
18506
18627
  }
@@ -18512,9 +18633,9 @@ var MemoryRedisServer = class {
18512
18633
  ctx.sock.destroy();
18513
18634
  }
18514
18635
  this.conns.clear();
18515
- await new Promise((resolve3) => {
18516
- if (!this.server) return resolve3();
18517
- this.server.close(() => resolve3());
18636
+ await new Promise((resolve4) => {
18637
+ if (!this.server) return resolve4();
18638
+ this.server.close(() => resolve4());
18518
18639
  this.server = null;
18519
18640
  });
18520
18641
  }
@@ -18600,14 +18721,14 @@ var MemoryRedisServer = class {
18600
18721
  const v = await this.engine.command("LPOP", k);
18601
18722
  if (v !== null && v !== void 0) return encodeReply([k, v]);
18602
18723
  }
18603
- return await new Promise((resolve3) => {
18604
- const waiter = { keys, resolve: resolve3, timer: null, owner: ctx.id };
18724
+ return await new Promise((resolve4) => {
18725
+ const waiter = { keys, resolve: resolve4, timer: null, owner: ctx.id };
18605
18726
  this.blpopWaiters.push(waiter);
18606
18727
  if (timeout > 0) {
18607
18728
  waiter.timer = setTimeout(() => {
18608
18729
  const i = this.blpopWaiters.indexOf(waiter);
18609
18730
  if (i >= 0) this.blpopWaiters.splice(i, 1);
18610
- resolve3(encodeReply(null));
18731
+ resolve4(encodeReply(null));
18611
18732
  }, timeout * 1e3);
18612
18733
  }
18613
18734
  });
@@ -18729,7 +18850,7 @@ export {
18729
18850
  WEIFUWU_WORKFLOW_SCHEMA,
18730
18851
  and,
18731
18852
  appAuth,
18732
- badRequest2 as badRequest,
18853
+ badRequest,
18733
18854
  between,
18734
18855
  bodyOf,
18735
18856
  buildQuery,