weifuwu 0.92.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();
@@ -1486,15 +1486,15 @@ var Router = class _Router {
1486
1486
  } catch {
1487
1487
  }
1488
1488
  }
1489
- await new Promise((resolve3) => {
1489
+ await new Promise((resolve4) => {
1490
1490
  let remaining = clients.length;
1491
1491
  const done = () => {
1492
1492
  if (--remaining === 0) {
1493
1493
  clearTimeout(timer);
1494
- resolve3();
1494
+ resolve4();
1495
1495
  }
1496
1496
  };
1497
- const timer = setTimeout(resolve3, 500);
1497
+ const timer = setTimeout(resolve4, 500);
1498
1498
  for (const c of clients) c.once("close", done);
1499
1499
  });
1500
1500
  }
@@ -2012,7 +2012,7 @@ async function MemoryEmailServer(options = {}) {
2012
2012
  res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
2013
2013
  });
2014
2014
  });
2015
- 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));
2016
2016
  const port = server.address().port;
2017
2017
  return {
2018
2018
  port,
@@ -2020,7 +2020,7 @@ async function MemoryEmailServer(options = {}) {
2020
2020
  emails,
2021
2021
  requests,
2022
2022
  closeAllConnections: () => server.closeAllConnections(),
2023
- 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()))
2024
2024
  };
2025
2025
  }
2026
2026
 
@@ -5337,7 +5337,7 @@ function messager(options) {
5337
5337
  function sendTo(userId, event) {
5338
5338
  broadcast(`user:${userId}`, event);
5339
5339
  }
5340
- function join(room, ws2) {
5340
+ function join2(room, ws2) {
5341
5341
  let members = rooms.get(room);
5342
5342
  if (!members) {
5343
5343
  members = /* @__PURE__ */ new Set();
@@ -5395,7 +5395,7 @@ function messager(options) {
5395
5395
  return;
5396
5396
  }
5397
5397
  }
5398
- join(msg.room, ws2);
5398
+ join2(msg.room, ws2);
5399
5399
  ws2.send(JSON.stringify({ type: "subscribed", room: msg.room }));
5400
5400
  } else if (msg.type === "unsubscribe" && msg.room) {
5401
5401
  leave(msg.room, ws2);
@@ -5428,7 +5428,7 @@ function messager(options) {
5428
5428
  handler,
5429
5429
  broadcast,
5430
5430
  sendTo,
5431
- join,
5431
+ join: join2,
5432
5432
  leave,
5433
5433
  close,
5434
5434
  createConversation,
@@ -9487,23 +9487,23 @@ function createAiClient(opts) {
9487
9487
  }
9488
9488
  const approvals = /* @__PURE__ */ new Map();
9489
9489
  function approve(response) {
9490
- const resolve3 = approvals.get(response.id);
9491
- if (!resolve3) return false;
9490
+ const resolve4 = approvals.get(response.id);
9491
+ if (!resolve4) return false;
9492
9492
  approvals.delete(response.id);
9493
- resolve3(response);
9493
+ resolve4(response);
9494
9494
  return true;
9495
9495
  }
9496
9496
  async function waitApproval(req, emit, timeoutMs = DEFAULT_APPROVAL_TIMEOUT, signal) {
9497
9497
  const expiresAt = Date.now() + timeoutMs;
9498
9498
  emit("wf:approval_request", { ...req, expiresAt });
9499
- return new Promise((resolve3) => {
9499
+ return new Promise((resolve4) => {
9500
9500
  let settled = false;
9501
9501
  const finish = (resp) => {
9502
9502
  if (settled) return;
9503
9503
  settled = true;
9504
9504
  clearTimeout(timer);
9505
9505
  signal?.removeEventListener("abort", onAbort);
9506
- resolve3(resp);
9506
+ resolve4(resp);
9507
9507
  };
9508
9508
  const timer = setTimeout(() => {
9509
9509
  if (approvals.has(req.id)) approvals.delete(req.id);
@@ -9975,23 +9975,23 @@ function createApprovalHub(timeoutMs = DEFAULT_APPROVAL_TIMEOUT2) {
9975
9975
  const approvals = /* @__PURE__ */ new Map();
9976
9976
  return {
9977
9977
  approve(response) {
9978
- const resolve3 = approvals.get(response.id);
9979
- if (!resolve3) return false;
9978
+ const resolve4 = approvals.get(response.id);
9979
+ if (!resolve4) return false;
9980
9980
  approvals.delete(response.id);
9981
- resolve3(response);
9981
+ resolve4(response);
9982
9982
  return true;
9983
9983
  },
9984
9984
  async waitApproval(req, emit, timeout = timeoutMs, signal) {
9985
9985
  const expiresAt = Date.now() + timeout;
9986
9986
  emit("wf:approval_request", { ...req, expiresAt });
9987
- return new Promise((resolve3) => {
9987
+ return new Promise((resolve4) => {
9988
9988
  let settled = false;
9989
9989
  const finish = (resp) => {
9990
9990
  if (settled) return;
9991
9991
  settled = true;
9992
9992
  clearTimeout(timer);
9993
9993
  signal?.removeEventListener("abort", onAbort);
9994
- resolve3(resp);
9994
+ resolve4(resp);
9995
9995
  };
9996
9996
  const timer = setTimeout(() => {
9997
9997
  if (approvals.has(req.id)) approvals.delete(req.id);
@@ -10205,7 +10205,7 @@ async function MemoryAiServer(options = {}) {
10205
10205
  }
10206
10206
  });
10207
10207
  });
10208
- 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));
10209
10209
  const addr = server.address();
10210
10210
  const port = typeof addr === "object" && addr ? addr.port : 0;
10211
10211
  return {
@@ -10213,7 +10213,7 @@ async function MemoryAiServer(options = {}) {
10213
10213
  url: `http://127.0.0.1:${port}`,
10214
10214
  requests,
10215
10215
  closeAllConnections: () => server.closeAllConnections(),
10216
- close: () => new Promise((resolve3) => server.close(() => resolve3()))
10216
+ close: () => new Promise((resolve4) => server.close(() => resolve4()))
10217
10217
  };
10218
10218
  }
10219
10219
  async function readJson(req) {
@@ -10304,11 +10304,98 @@ async function handle(ai, method, url, req, res, preBody) {
10304
10304
 
10305
10305
  // src/server/ui/index.ts
10306
10306
  import { build } from "esbuild";
10307
- import { readFile, stat as stat2 } from "node:fs/promises";
10307
+ import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
10308
10308
  import { createHash as createHash2 } from "node:crypto";
10309
- import { resolve as resolve2 } from "node:path";
10309
+ import { resolve as resolve3, dirname, basename } from "node:path";
10310
10310
  import { fileURLToPath } from "node:url";
10311
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
+
10312
10399
  // src/server/ui/html-safe.ts
10313
10400
  var HtmlSafe = class {
10314
10401
  value;
@@ -12892,11 +12979,11 @@ function openPopup(env, opts) {
12892
12979
  const registry = applier.registry;
12893
12980
  if (!registry) return;
12894
12981
  const ctx = env.getSharedContext() ?? {};
12895
- const cmds = await new Promise((resolve3, reject) => {
12982
+ const cmds = await new Promise((resolve4, reject) => {
12896
12983
  const out = [];
12897
12984
  const req = () => env.requestRender?.();
12898
12985
  const obs = prev ? diffV2(prev, content, ctx, state2.segments, registry, req) : renderV2(content, ctx, registry, state2.segments, req);
12899
- 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) });
12900
12987
  });
12901
12988
  for (const value of cmds) {
12902
12989
  if (state2.disposed || v !== state2.version) return;
@@ -14172,9 +14259,9 @@ function concatWith(tail) {
14172
14259
 
14173
14260
  // src/client/vdom/core/v2/integrate.ts
14174
14261
  function collectCommands(obs) {
14175
- return new Promise((resolve3, reject) => {
14262
+ return new Promise((resolve4, reject) => {
14176
14263
  const out = [];
14177
- 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) });
14178
14265
  });
14179
14266
  }
14180
14267
  async function v2ToHtml(root, ctx, registry) {
@@ -14211,6 +14298,35 @@ function unsafe(s) {
14211
14298
  return new HtmlSafe(s);
14212
14299
  }
14213
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
+ }
14214
14330
  var postcssAvailable;
14215
14331
  async function checkPostcss() {
14216
14332
  if (postcssAvailable !== void 0) return postcssAvailable;
@@ -14225,12 +14341,12 @@ async function checkPostcss() {
14225
14341
  }
14226
14342
  function resolveEntry(entryPath) {
14227
14343
  if (entryPath.startsWith(".") || entryPath.startsWith("/")) {
14228
- return resolve2(entryPath);
14344
+ return resolve3(entryPath);
14229
14345
  }
14230
14346
  try {
14231
14347
  return fileURLToPath(import.meta.resolve(entryPath));
14232
14348
  } catch {
14233
- return resolve2(entryPath);
14349
+ return resolve3(entryPath);
14234
14350
  }
14235
14351
  }
14236
14352
  function ui(options = {}) {
@@ -14336,7 +14452,7 @@ function ui(options = {}) {
14336
14452
  });
14337
14453
  const inputs = {};
14338
14454
  for (const key of Object.keys(result.metafile?.inputs ?? {})) {
14339
- const abs = resolve2(key);
14455
+ const abs = resolve3(key);
14340
14456
  try {
14341
14457
  const st = await stat2(abs);
14342
14458
  inputs[abs] = { mtimeMs: st.mtimeMs, size: st.size };
@@ -14351,14 +14467,13 @@ function ui(options = {}) {
14351
14467
  async css(entryPath) {
14352
14468
  const absPath = resolveEntry(entryPath);
14353
14469
  const { code, etag } = await compile2("css", absPath, async () => {
14354
- let code2 = await readFile(absPath, "utf-8");
14355
- const inputs = {};
14356
- try {
14357
- const st = await stat2(absPath);
14358
- inputs[absPath] = { mtimeMs: st.mtimeMs, size: st.size };
14359
- } catch {
14360
- 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) };
14361
14474
  }
14475
+ let code2 = await readFile2(absPath, "utf-8");
14476
+ const inputs = await statAll([absPath, ...await importClosure(absPath)]);
14362
14477
  if (await checkPostcss()) {
14363
14478
  try {
14364
14479
  const postcss = await import("postcss");
@@ -14701,7 +14816,7 @@ var PgConnection = class _PgConnection {
14701
14816
  /** 池空闲回收用:最近一次查询完成时间戳(pool release 时更新) */
14702
14817
  lastUsed = 0;
14703
14818
  connect() {
14704
- return new Promise((resolve3, reject) => {
14819
+ return new Promise((resolve4, reject) => {
14705
14820
  this.status = "connecting";
14706
14821
  const sock = net.connect(this.opts.port, this.opts.host);
14707
14822
  this.socket = sock;
@@ -14733,7 +14848,7 @@ var PgConnection = class _PgConnection {
14733
14848
  this.onReady = () => {
14734
14849
  clearTimeout(timeout);
14735
14850
  this.status = "ready";
14736
- resolve3();
14851
+ resolve4();
14737
14852
  };
14738
14853
  this.onAuthFail = (err) => {
14739
14854
  clearTimeout(timeout);
@@ -15030,7 +15145,7 @@ var PgConnection = class _PgConnection {
15030
15145
  }
15031
15146
  /** 查询:无参数走简单协议(Q),有参数走扩展查询(Parse/Bind/Execute/Sync) */
15032
15147
  query(sql, params) {
15033
- return new Promise((resolve3, reject) => {
15148
+ return new Promise((resolve4, reject) => {
15034
15149
  if (this.status !== "ready" || !this.socket) {
15035
15150
  reject(new ConnectionError("postgres: not connected"));
15036
15151
  return;
@@ -15043,7 +15158,7 @@ var PgConnection = class _PgConnection {
15043
15158
  this.currentQuery = {
15044
15159
  columns: [],
15045
15160
  rows: [],
15046
- resolve: resolve3,
15161
+ resolve: resolve4,
15047
15162
  reject
15048
15163
  };
15049
15164
  if (process.env.PGDBG2) console.error("[cquery]", JSON.stringify(sql.slice(0, 120)));
@@ -15053,12 +15168,12 @@ var PgConnection = class _PgConnection {
15053
15168
  let stmtEntry = this.getPrepared(sig);
15054
15169
  const encoded = encodeParams(params);
15055
15170
  if (!stmtEntry) {
15056
- this.issueExtendedQuery(sql, encoded, resolve3, reject);
15171
+ this.issueExtendedQuery(sql, encoded, resolve4, reject);
15057
15172
  } else {
15058
15173
  this.currentQuery = {
15059
15174
  columns: [...stmtEntry.columns],
15060
15175
  rows: [],
15061
- resolve: resolve3,
15176
+ resolve: resolve4,
15062
15177
  reject,
15063
15178
  sql,
15064
15179
  params: encoded,
@@ -15077,12 +15192,12 @@ var PgConnection = class _PgConnection {
15077
15192
  * 扩展查询首次准备:Parse + Describe + Flush(无 Sync——错误时由调用方发 Sync 复位)。
15078
15193
  * retry 路径复用:新语句名强制服务器按当前 schema 重新解析(DDL 失效恢复)。
15079
15194
  */
15080
- issueExtendedQuery(sql, params, resolve3, reject, retried) {
15195
+ issueExtendedQuery(sql, params, resolve4, reject, retried) {
15081
15196
  const name = `wf_s${++this.stmtSeq}`;
15082
15197
  this.currentQuery = {
15083
15198
  columns: [],
15084
15199
  rows: [],
15085
- resolve: resolve3,
15200
+ resolve: resolve4,
15086
15201
  reject,
15087
15202
  sql,
15088
15203
  params,
@@ -15106,7 +15221,7 @@ var PgConnection = class _PgConnection {
15106
15221
  /** 等待连接空闲(事务/多语句流程用) */
15107
15222
  waitIdle() {
15108
15223
  if (!this.currentQuery) return Promise.resolve();
15109
- return new Promise((resolve3) => this.waiters.push(resolve3));
15224
+ return new Promise((resolve4) => this.waiters.push(resolve4));
15110
15225
  }
15111
15226
  /** 终止连接 */
15112
15227
  async close() {
@@ -15327,9 +15442,9 @@ var PgPool = class _PgPool {
15327
15442
  );
15328
15443
  }
15329
15444
  const timeoutMs = this.opts.acquireTimeoutMs ?? 3e4;
15330
- return new Promise((resolve3, reject) => {
15445
+ return new Promise((resolve4, reject) => {
15331
15446
  const waiter = {
15332
- resolve: resolve3,
15447
+ resolve: resolve4,
15333
15448
  reject
15334
15449
  };
15335
15450
  if (timeoutMs > 0) {
@@ -16779,9 +16894,9 @@ var RedisConnection = class {
16779
16894
  connect() {
16780
16895
  if (this.connectPromise) return this.connectPromise;
16781
16896
  this.closed = false;
16782
- this.connectPromise = new Promise((resolve3, reject) => {
16897
+ this.connectPromise = new Promise((resolve4, reject) => {
16783
16898
  this.openSocket();
16784
- this.onceReady = () => resolve3();
16899
+ this.onceReady = () => resolve4();
16785
16900
  this.onceFailed = (err) => reject(err);
16786
16901
  }).finally(() => {
16787
16902
  this.connectPromise = null;
@@ -16889,15 +17004,15 @@ var RedisConnection = class {
16889
17004
  if (this.offlineQueue.length >= this.opts.maxOfflineQueue) {
16890
17005
  return Promise.reject(new ConnectionError(`redis: offline queue full (${this.opts.maxOfflineQueue})`));
16891
17006
  }
16892
- return new Promise((resolve3, reject) => {
16893
- 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 });
16894
17009
  });
16895
17010
  }
16896
17011
  sendNow(name, args, asBuffer) {
16897
17012
  const start = performance.now();
16898
- return new Promise((resolve3, reject) => {
17013
+ return new Promise((resolve4, reject) => {
16899
17014
  const p = {
16900
- resolve: resolve3,
17015
+ resolve: resolve4,
16901
17016
  reject,
16902
17017
  asBuffer,
16903
17018
  blocking: BLOCKING_COMMANDS.has(name.toUpperCase())
@@ -16967,7 +17082,7 @@ var RedisConnection = class {
16967
17082
  if (this.status !== "ready" || !this.socket) {
16968
17083
  return Promise.reject(new ConnectionError("redis: not connected"));
16969
17084
  }
16970
- return new Promise((resolve3, reject) => {
17085
+ return new Promise((resolve4, reject) => {
16971
17086
  const results = [];
16972
17087
  const batchPending = [];
16973
17088
  let settled = false;
@@ -16976,7 +17091,7 @@ var RedisConnection = class {
16976
17091
  if (results.length === count) {
16977
17092
  settled = true;
16978
17093
  if (batchTimer) clearTimeout(batchTimer);
16979
- resolve3(results);
17094
+ resolve4(results);
16980
17095
  }
16981
17096
  };
16982
17097
  for (let i = 0; i < count; i++) {
@@ -17367,9 +17482,9 @@ var RedisPool = class _RedisPool {
17367
17482
  });
17368
17483
  this.replenish();
17369
17484
  }
17370
- return new Promise((resolve3, reject) => {
17485
+ return new Promise((resolve4, reject) => {
17371
17486
  const waiter = () => {
17372
- 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]);
17373
17488
  else reject(new ConnectionError("redis: pool exhausted after reconnect wait"));
17374
17489
  };
17375
17490
  this.replenishWaiters.push(waiter);
@@ -18500,13 +18615,13 @@ var MemoryRedisServer = class {
18500
18615
  }
18501
18616
  async start() {
18502
18617
  if (this.server) return;
18503
- await new Promise((resolve3) => {
18618
+ await new Promise((resolve4) => {
18504
18619
  this.server = net3.createServer((sock) => this.handleSocket(sock));
18505
18620
  this.server.listen(this.opts.port, "127.0.0.1", () => {
18506
18621
  const addr = this.server.address();
18507
18622
  this.port = addr.port;
18508
18623
  this.url = `redis://127.0.0.1:${addr.port}`;
18509
- resolve3();
18624
+ resolve4();
18510
18625
  });
18511
18626
  });
18512
18627
  }
@@ -18518,9 +18633,9 @@ var MemoryRedisServer = class {
18518
18633
  ctx.sock.destroy();
18519
18634
  }
18520
18635
  this.conns.clear();
18521
- await new Promise((resolve3) => {
18522
- if (!this.server) return resolve3();
18523
- this.server.close(() => resolve3());
18636
+ await new Promise((resolve4) => {
18637
+ if (!this.server) return resolve4();
18638
+ this.server.close(() => resolve4());
18524
18639
  this.server = null;
18525
18640
  });
18526
18641
  }
@@ -18606,14 +18721,14 @@ var MemoryRedisServer = class {
18606
18721
  const v = await this.engine.command("LPOP", k);
18607
18722
  if (v !== null && v !== void 0) return encodeReply([k, v]);
18608
18723
  }
18609
- return await new Promise((resolve3) => {
18610
- 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 };
18611
18726
  this.blpopWaiters.push(waiter);
18612
18727
  if (timeout > 0) {
18613
18728
  waiter.timer = setTimeout(() => {
18614
18729
  const i = this.blpopWaiters.indexOf(waiter);
18615
18730
  if (i >= 0) this.blpopWaiters.splice(i, 1);
18616
- resolve3(encodeReply(null));
18731
+ resolve4(encodeReply(null));
18617
18732
  }, timeout * 1e3);
18618
18733
  }
18619
18734
  });
package/docs/client.md CHANGED
@@ -56,21 +56,57 @@ app.router(Router())
56
56
  ## 3. 设计语言
57
57
 
58
58
  微流明(Whisper Luminance):中性色主导、品牌色点睛、动效短促有目的(120–300ms)、
59
- 1px 边界即结构。**Token 即规范**——`src/client/layout/style.css`(183 双层 token:
60
- 色阶/排版/动效/圆角/阴影/z-index)——组件只引用 token、零硬编码(audit 强制)。
59
+ 1px 边界即结构。**Token 即规范**——`src/client/layout/_tokens.css`(178 token:
60
+ 色阶/排版/动效/圆角/阴影/z-index;`_dark.css` 暗色覆写 · `_presets.css` 紧凑预设覆写)
61
+ ——组件只引用 token、零硬编码(audit 强制)。
62
+
63
+ **token 两面纪律**(layout-inventory L10/L11 哨兵):
64
+
65
+ - **断点单源**:`--wf-bp-sm/md/lg/xl` = 640/768/1024/1280——CSS 媒体查询语法不能
66
+ `var()`,故 token 作**审计白名单源**:合法字面量只有两形态 `V`(`min-width`)与
67
+ `V - 0.02`(`max-width`)——二者无缝对接(旧碎片:组件面 `639px`/`767px` vs
68
+ layout 面 `767.98px`——767.00–767.98 区间两侧规则同时失配)
69
+ - **死面 = 0(登记制)**:token 声明必须有消费证据(`var()` / TS 字面)——零消费即删,
70
+ 结构性不可消费的(bp-* 机制源 · gap-2xl 标尺完整性)逐条登记理由;反向哨兵:
71
+ 登记项一旦有消费者即需移出(防登记表腐化)
72
+ - **双标尺(定案)**:`--wf-gap-*` **派生**自 `--wf-space-*`(紧一档:gap-md = space 裸档
73
+ = 12px · gap-lg = space-md)。容器内元素间距 < 控件内边距是有意的密度设计,**不是**
74
+ 同名不同值的命名事故——关系入代码(L13 登记 + 值冻结),预设只覆写 space 标尺
75
+ (gap 自动跟随)
76
+ - **同值双名(L12)**:同主题文件内值全等且名为「限定词插入」形态(`shadow` ⊂
77
+ `surface-shadow`)= 历史双名 → 必须 `var()` 单源(语义名保留、值改引用);
78
+ 跨域巧合(motion-sm 4px ≡ overlay-blur 4px · 240px/200px 三胞胎)**判负不耦合**
79
+ (耦合即隐形爆炸半径)
80
+ - **px 字面量(L14)**:layout 类文件只允许登记白名单内的结构魔数(44px 命中区 ·
81
+ 999px 胶囊 · 1px 发丝走 `--wf-border-width` · 2px/4px bleed 对 · 32px 视口内缩 ·
82
+ 2px hover 微抬升)——新增字面量必须 token 化或登记
61
83
 
62
84
  品牌换色 = 改 seed 一个值;预设主题 = `data-theme` 一个属性(minimal/dark)。
63
85
 
64
86
  ## 4. 布局系统
65
87
 
66
- `src/client/layout/`——49 原语(`_*.css`)+ 92 工具 + 2 内部,全部 `wf-` 前缀:
88
+ `src/client/layout/`——50 原语(`_*.css`)+ 98 工具 + 2 内部,全部 `wf-` 前缀;
89
+ **装配单源**:四处管线(build 产物 / showcase dev / 场景 dev / `ctx.ui.css`)共用
90
+ `bundle.ts`——层序单源(D4 审计比对);**发布产物 minify**(esbuild——见文末载荷面):
67
91
 
68
92
  - **原语**:`wf-stack` / `wf-row` / `wf-grid` / `wf-center` / `wf-card-surface` / `wf-divider`
69
93
  - **工具**:`wf-padding` / `wf-margin` / `wf-text-*` / `wf-bg-*` / `wf-hidden`
70
- - **命名规则**:三类词根 + 三后缀(完整词根表见 `apps/showcase/src/demos/layout.tsx`
71
- 与 `src/test/contract/layout-inventory.test.ts` L1 计数断言——**测试即登记表**)
72
-
73
- 零值形态唯一(`none` 归一)· 对齐域禁方向词 · 双名歼灭——layout-inventory 8 断言锁定。
94
+ - **命名规则**:三类词根 + 三后缀(完整词根表 = **机器生成** `docs/layout.md`:类清单/断点表/
95
+ 钩子表/标尺派生/零消费公共面示例——与 `layout-inventory` 哨兵同源,改布局源码后
96
+ `node scripts/layout-reference.mjs` 再生成;L15 校验漂移即红)
97
+
98
+ 零值形态唯一(`none` 归一)· 对齐域禁方向词 · 双名歼灭 · 零值档位矩阵(取消面缺口登记)·
99
+ 冲突矩阵(同属性不同值对的同元素共用逐对登记胜者+胜因)· 层叠机制锁定(层序/`:where()`/
100
+ `!important` 白名单/`@property` 钩子注册)· 断点与 token 单源 · token 双名歼灭 ·
101
+ 间距标尺派生登记 · px 字面量白名单——layout-inventory **20 断言**锁定;
102
+ 层叠语义的浏览器计算值读数见场景层 `e2e-layout-semantics.test.ts`。
103
+
104
+ **载荷面(W6 minify)**:dist 发布产物 `weifuwu-layout.css` **28.1K**(gzip 5.7K · brotli 5.1K)·
105
+ `weifuwu/components/style.css` **220.9K**(gzip 29.4K · brotli 24.3K)——esbuild minify(
106
+ `build.mjs` 实装:`@layer`/`@property`/`@supports`/转义类名全保留,注释全剥离);
107
+ **无组件应用只引 `weifuwu/layout`**(28.1K / br 5.1K——独立面,零组件 CSS 成本)。
108
+ 按需子集(per-component 子路径 / purge)**判负**:动态类名漏删风险 + 构建期改造成本
109
+ + br 后 24.3K 非瓶颈——推翻条件:真实应用首屏 CSS 成为 LCP 阻塞的实测数据。
74
110
 
75
111
  ## 5. 组件编写规范(唯一入口)
76
112
 
@@ -116,6 +152,32 @@ props camelCase · **`className=`(组件 props)`class=`(DOM 元素)** ·
116
152
  **状态变体类必须有规则或 `:where(.wf-x) {}` 显式声明**(L3 已扩围组件源码——未定义立即红)·
117
153
  动效 `--wf-dur-*`/`--wf-ease-*` · transition 含 visibility(隐藏不可 Tab 聚焦)
118
154
 
155
+ **组件 px 四纪律(C1 哨兵强制——`contract/components-px.test.ts` 四桶登记制)**:
156
+
157
+ 1. **间距走标尺**:padding/margin/gap 的 4/8/12/16/24/32px →
158
+ `var(--wf-space-*)`/`var(--wf-gap-*)`(**任一档位值字面量 = 红**——含 var 混合声明
159
+ `padding: 8px var(--wf-x)` 同样适用——半 token 禁止;档位外值 = 结构桶登记制)
160
+ 2. **边框宽走发丝 token**:`border[N]: 1px solid/dashed …` → `var(--wf-border-width)`
161
+ (色可走 var/currentColor/transparent——hex 已锁 0;**非 1px 宽 = 强调边框**——无 token 面登记制)
162
+ 3. **结构值登记制**:非档位 px(结构尺寸/魔数——height/width/min-*/outline/box-shadow 偏移/
163
+ 44px 命中区/z-index:1 等)→ `scripts/components-px-whitelist.json` structural/zIndex 桶
164
+ **登记 + 理由**(新增未登记 = 红 · 处理完未移出 = 幽灵红)
165
+ 4. **豁免面**:派生表达式(calc/env/min/max/clamp——L13 同款)与 0px 零值形态——非作者手写终值
166
+
167
+ > 紧凑预设语义:**只有走 var() 的组件才跟随缩放**(_presets.css「间距缩一档」——手写=静止=活体缺口)。
168
+
169
+ **C2 扩展纪律(`contract/components-token.test.ts` 三桶——token 消费哨兵)**:
170
+
171
+ 5. **文本行高走档**:`line-height` 值 ∈ {1.5, 1.25}(恒等档)→ `var(--wf-line-height)`/
172
+ `var(--wf-line-height-tight)`(近值=红);代码排版 1.6(CodeBlock/DiffView——2 消费者
173
+ <3 升档门——第三消费者出现即升 --wf-line-height-code);豁免面 {1 重置/0/px 图标}
174
+ 6. **弹层宽走钩子**:`.wf-popup` 系面板 **禁止手写 max-width**——基类
175
+ `min(var(--wf-popup-max, 480px), calc(100vw - 32px))` 是唯一入口;组件默认宽用
176
+ **回退值**表达(`min(var(--wf-popup-max, 320px), …)`——回退=默认宽);非弹层面板宽
177
+ (命令面板/通知条/工作区)判负登记——`popup-max` 钩子调不动 = 钩子失效面
178
+ 7. **token 零消费双分类**:{保留类(色板档/暗色映射档/布局档/断点档——消费形态非
179
+ var() 引用)/真死候选}——新增零消费 token 需登记(消费矩阵哨兵——对齐 L11 组件版)
180
+
119
181
  ### 5.5 测试二层(覆盖哨兵 ≥2 层)
120
182
 
121
183
  1. **契约 harness**(`<Comp>.test.ts`——mount/render/createTable 命令流断言)