weifuwu 0.84.0 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3316,7 +3316,6 @@ var HtmlSafe = class {
3316
3316
 
3317
3317
  // src/ui-dom/vdom3/types.ts
3318
3318
  function flatten(c) {
3319
- if (c == null || typeof c === "boolean") return [];
3320
3319
  if (Array.isArray(c)) return c.flatMap((x) => flatten(x));
3321
3320
  return [c];
3322
3321
  }
@@ -3368,8 +3367,19 @@ function createEventStream(max = 2e4, opts) {
3368
3367
  }
3369
3368
  }
3370
3369
  };
3370
+ let currentSession = null;
3371
+ let sessionUid = 0;
3371
3372
  return {
3373
+ /** 新渲染会话开始(每次渲染入口调用——同一次渲染的事件共享 session id) */
3374
+ setSession() {
3375
+ currentSession = `s${++sessionUid}`;
3376
+ return currentSession;
3377
+ },
3378
+ currentSession() {
3379
+ return currentSession;
3380
+ },
3372
3381
  emit(evt) {
3382
+ if (currentSession != null && evt.session == null) evt.session = currentSession;
3373
3383
  emitOne(evt, false);
3374
3384
  if (watermark > 0 && !watermarkFired && len >= max * watermark) {
3375
3385
  watermarkFired = true;
@@ -3417,6 +3427,10 @@ function createEventStream(max = 2e4, opts) {
3417
3427
  for (let i = 0; i < len; i++) out[i] = at(i);
3418
3428
  return out;
3419
3429
  },
3430
+ /** 按渲染会话过滤(阶段 5——session 字段的消费 API——一次渲染的事件全量) */
3431
+ eventsBySession(sessionId) {
3432
+ return this.events().filter((e) => e.session === sessionId);
3433
+ },
3420
3434
  inverse(evt) {
3421
3435
  switch (evt.entity + ":" + evt.action) {
3422
3436
  case "node:insert":
@@ -3444,6 +3458,35 @@ function createEventStream(max = 2e4, opts) {
3444
3458
  };
3445
3459
  }
3446
3460
  var stream = createEventStream();
3461
+ if (typeof globalThis !== "undefined") {
3462
+ const w = globalThis;
3463
+ if (!w.__wf_tail) {
3464
+ w.__wf_tail = (n = 200, filter) => {
3465
+ const evs = stream.events().slice(-n);
3466
+ if (!filter) return evs;
3467
+ return evs.filter((e) => {
3468
+ if (filter.comp && e.payload?.name !== filter.comp) return false;
3469
+ if (filter.decision && e.payload?.reason !== filter.decision) return false;
3470
+ if (filter.causeId && e.payload?.causeId !== filter.causeId) return false;
3471
+ return true;
3472
+ });
3473
+ };
3474
+ w.__wf_builds = (comp) => {
3475
+ const evs = stream.events().filter((e) => e.entity === "comp" && e.action === "build");
3476
+ if (!comp) return evs.map((e) => ({ name: e.payload?.name, reason: e.payload?.reason, changedKeys: e.payload?.changedKeys }));
3477
+ return evs.filter((e) => e.payload?.name === comp).map((e) => ({ name: e.payload?.name, reason: e.payload?.reason, changedKeys: e.payload?.changedKeys }));
3478
+ };
3479
+ w.__wf_recent = (n = 50) => stream.events().slice(-n);
3480
+ w.__wf_comp = (compId) => {
3481
+ const evs = stream.events().filter((e) => {
3482
+ if (e.target === compId) return true;
3483
+ if (e.payload?.name && !compId) return true;
3484
+ return false;
3485
+ });
3486
+ return evs.map((e) => ({ entity: e.entity, action: e.action, target: e.target, payload: e.payload, session: e.session }));
3487
+ };
3488
+ }
3489
+ }
3447
3490
  var nodeUid = 0;
3448
3491
  function nextNodeId() {
3449
3492
  return `n${++nodeUid}`;
@@ -5367,6 +5410,21 @@ var unmountHooks = /* @__PURE__ */ new Map();
5367
5410
  function isVNode(v) {
5368
5411
  return v != null && typeof v === "object" && !Array.isArray(v) && "type" in v;
5369
5412
  }
5413
+ function propsSnap(props) {
5414
+ try {
5415
+ const picked = {};
5416
+ for (const [k, v] of Object.entries(props)) {
5417
+ if (v != null && typeof v === "object") {
5418
+ const s = JSON.stringify(v);
5419
+ if (s != null && s.length <= 1e4) picked[k] = s;
5420
+ }
5421
+ }
5422
+ return Object.keys(picked).length > 0 ? JSON.stringify(picked) : null;
5423
+ } catch {
5424
+ return null;
5425
+ }
5426
+ }
5427
+ var warnedContentChange = /* @__PURE__ */ new Set();
5370
5428
  function propsEqual(a, b) {
5371
5429
  const ka = Object.keys(a);
5372
5430
  const kb = Object.keys(b);
@@ -5387,17 +5445,33 @@ async function buildVNode(vnode, ctx, oldV, isRoot = false) {
5387
5445
  v2._render = reuse._render;
5388
5446
  v2._id = reuse._id;
5389
5447
  indexComponent(v2);
5390
- stream.emit(ev("comp", "build", v2._id, { name: compName(v2.type), reused: true, index: true }));
5391
5448
  if (!isRoot && propsEqual(reuse.props, v2.props)) {
5392
5449
  v2._child = reuse._child;
5450
+ if (reuse.el != null) v2.el = reuse.el;
5451
+ if (globalThis.__WF_V3_AUDIT !== "0" && reuse._propsSnap != null) {
5452
+ const cur = propsSnap(v2.props);
5453
+ if (cur != null && cur !== reuse._propsSnap) {
5454
+ const key = `${compName(v2.type)}:${cur.slice(0, 40)}`;
5455
+ if (!warnedContentChange.has(key)) {
5456
+ warnedContentChange.add(key);
5457
+ console.warn(
5458
+ `[vdom3/audit] \u7EC4\u4EF6 ${compName(v2.type)} \u7684 props \u5BF9\u8C61\u5185\u5BB9\u5DF2\u53D8\u4F46\u5F15\u7528\u672A\u53D8\uFF08\u539F\u5730\u4FEE\u6539\u5BF9\u8C61\uFF1F\uFF09\u2014\u2014\u526A\u679D\u5C06\u8DF3\u8FC7\u91CD\u6E32\u67D3\u2014\u2014\u8BF7\u65B0\u5EFA\u5BF9\u8C61\u4F20 props\uFF08props \u4E0D\u53EF\u53D8\u5951\u7EA6\uFF09`
5459
+ );
5460
+ }
5461
+ }
5462
+ }
5463
+ stream.emit(ev("comp", "build", v2._id, { name: compName(v2.type), reused: true, index: true, reason: "reuse-skip", propsKeys: Object.keys(v2.props) }));
5464
+ v2._propsSnap = propsSnap(v2.props);
5393
5465
  return v2;
5394
5466
  }
5395
5467
  const changedKeys = Object.keys({ ...reuse.props, ...v2.props }).filter((k) => reuse.props[k] !== v2.props[k]);
5396
5468
  stream.emit(ev("props", "update", v2._id, { name: compName(v2.type), keys: changedKeys }));
5469
+ v2._propsSnap = propsSnap(v2.props);
5470
+ stream.emit(ev("comp", "build", v2._id, { name: compName(v2.type), reused: true, index: true, reason: isRoot ? "root-render" : "props-changed", changedKeys }));
5397
5471
  } else {
5398
5472
  v2._id = nextNodeId();
5399
5473
  indexComponent(v2);
5400
- stream.emit(ev("comp", "build", v2._id, { name: compName(v2.type), reused: false, index: true }));
5474
+ stream.emit(ev("comp", "build", v2._id, { name: compName(v2.type), reused: false, index: true, reason: "mount" }));
5401
5475
  stream.emit(ev("comp", "mount", v2._id, { name: compName(v2.type) }));
5402
5476
  const compId = v2._id;
5403
5477
  const componentRender = () => {
@@ -5437,6 +5511,7 @@ async function buildVNode(vnode, ctx, oldV, isRoot = false) {
5437
5511
  throw e;
5438
5512
  }
5439
5513
  v2._render = renderFn;
5514
+ v2._propsSnap = propsSnap(v2.props);
5440
5515
  }
5441
5516
  stream.emit(ev("comp", "render", v2._id, { name: compName(v2.type) }));
5442
5517
  let output = null;
@@ -5535,7 +5610,11 @@ async function renderToEvents(vnode) {
5535
5610
  }
5536
5611
  events.push(ev("node", "insert", id, { parent: parentId, ref: null }));
5537
5612
  for (const c of childrenOf(v)) {
5538
- if (typeof c === "string" || typeof c === "number") {
5613
+ if (c == null || c === false || c === true) {
5614
+ const hid = nextId();
5615
+ events.push(ev("node", "create", hid, { kind: "hole" }));
5616
+ events.push(ev("node", "insert", hid, { parent: id, ref: null }));
5617
+ } else if (typeof c === "string" || typeof c === "number") {
5539
5618
  const tid = nextId();
5540
5619
  events.push(ev("text", "create", tid, { value: String(c) }));
5541
5620
  events.push(ev("node", "insert", tid, { parent: id, ref: null }));
@@ -5551,9 +5630,13 @@ function eventsToHtml(events) {
5551
5630
  const tags = /* @__PURE__ */ new Map();
5552
5631
  const attrs = /* @__PURE__ */ new Map();
5553
5632
  const texts = /* @__PURE__ */ new Map();
5633
+ const holes = /* @__PURE__ */ new Set();
5554
5634
  for (const e of events) {
5555
- if (e.entity === "node" && e.action === "create") tags.set(e.target, e.payload.tag);
5556
- else if (e.entity === "prop" && e.action === "update") {
5635
+ if (e.entity === "node" && e.action === "create") {
5636
+ const pl = e.payload;
5637
+ if (pl.kind === "hole") holes.add(e.target);
5638
+ else tags.set(e.target, pl.tag);
5639
+ } else if (e.entity === "prop" && e.action === "update") {
5557
5640
  const pl = e.payload;
5558
5641
  const m = attrs.get(e.target) ?? /* @__PURE__ */ new Map();
5559
5642
  if (pl.value == null || pl.value === false) m.delete(pl.key);
@@ -5572,6 +5655,7 @@ function eventsToHtml(events) {
5572
5655
  }
5573
5656
  const esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5574
5657
  const emit = (id) => {
5658
+ if (holes.has(id)) return "<!--wf-hole-->";
5575
5659
  const tag = tags.get(id);
5576
5660
  if (tag) {
5577
5661
  const a = attrs.get(id);