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.
- package/README.md +1 -1
- package/dist/client/components/style.css +1 -12219
- package/dist/client/layout/bundle.d.ts +42 -0
- package/dist/client/layout/weifuwu-layout.css +1 -1495
- package/dist/server/index.js +183 -68
- package/docs/client.md +69 -7
- package/docs/layout.md +243 -0
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -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((
|
|
274
|
-
res.once("close", () =>
|
|
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
|
-
(
|
|
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((
|
|
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
|
-
|
|
1494
|
+
resolve4();
|
|
1495
1495
|
}
|
|
1496
1496
|
};
|
|
1497
|
-
const timer = setTimeout(
|
|
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((
|
|
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((
|
|
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
|
|
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
|
-
|
|
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
|
|
9491
|
-
if (!
|
|
9490
|
+
const resolve4 = approvals.get(response.id);
|
|
9491
|
+
if (!resolve4) return false;
|
|
9492
9492
|
approvals.delete(response.id);
|
|
9493
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
9979
|
-
if (!
|
|
9978
|
+
const resolve4 = approvals.get(response.id);
|
|
9979
|
+
if (!resolve4) return false;
|
|
9980
9980
|
approvals.delete(response.id);
|
|
9981
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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((
|
|
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
|
|
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((
|
|
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: () =>
|
|
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((
|
|
14262
|
+
return new Promise((resolve4, reject) => {
|
|
14176
14263
|
const out = [];
|
|
14177
|
-
obs.subscribe({ next: (c) => out.push(c), error: reject, complete: () =>
|
|
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
|
|
14344
|
+
return resolve3(entryPath);
|
|
14229
14345
|
}
|
|
14230
14346
|
try {
|
|
14231
14347
|
return fileURLToPath(import.meta.resolve(entryPath));
|
|
14232
14348
|
} catch {
|
|
14233
|
-
return
|
|
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 =
|
|
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
|
-
|
|
14355
|
-
|
|
14356
|
-
|
|
14357
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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:
|
|
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,
|
|
15171
|
+
this.issueExtendedQuery(sql, encoded, resolve4, reject);
|
|
15057
15172
|
} else {
|
|
15058
15173
|
this.currentQuery = {
|
|
15059
15174
|
columns: [...stmtEntry.columns],
|
|
15060
15175
|
rows: [],
|
|
15061
|
-
resolve:
|
|
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,
|
|
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:
|
|
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((
|
|
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((
|
|
15445
|
+
return new Promise((resolve4, reject) => {
|
|
15331
15446
|
const waiter = {
|
|
15332
|
-
resolve:
|
|
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((
|
|
16897
|
+
this.connectPromise = new Promise((resolve4, reject) => {
|
|
16783
16898
|
this.openSocket();
|
|
16784
|
-
this.onceReady = () =>
|
|
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((
|
|
16893
|
-
this.offlineQueue.push({ name, args, resolve:
|
|
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((
|
|
17013
|
+
return new Promise((resolve4, reject) => {
|
|
16899
17014
|
const p = {
|
|
16900
|
-
resolve:
|
|
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((
|
|
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
|
-
|
|
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((
|
|
17485
|
+
return new Promise((resolve4, reject) => {
|
|
17371
17486
|
const waiter = () => {
|
|
17372
|
-
if (this.clients.length > 0 && this.clients[0].connected)
|
|
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((
|
|
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
|
-
|
|
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((
|
|
18522
|
-
if (!this.server) return
|
|
18523
|
-
this.server.close(() =>
|
|
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((
|
|
18610
|
-
const waiter = { keys, resolve:
|
|
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
|
-
|
|
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/
|
|
60
|
-
色阶/排版/动效/圆角/阴影/z-index
|
|
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/`——
|
|
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
|
-
- **命名规则**:三类词根 +
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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 命令流断言)
|