@wcstack/devtools 1.32.0 → 2.0.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.esm.js CHANGED
@@ -11,7 +11,7 @@
11
11
  /** グローバル registry のプロパティ名 */
12
12
  const DEVTOOLS_HOOK_GLOBAL = "__WCSTACK_DEVTOOLS_HOOK__";
13
13
  /** プロトコル版。additive change では上げない(protocol §2) */
14
- const DEVTOOLS_PROTOCOL_VERSION = 1;
14
+ const DEVTOOLS_PROTOCOL_VERSION = 2;
15
15
 
16
16
  /**
17
17
  * protocol/registry.ts
@@ -210,36 +210,44 @@ function formatArgs(args) {
210
210
  * 台帳はすべて devtools 側に置く(protocol 原則 2)。disconnect で
211
211
  * sources / roster / wiring をクリアし、残留参照を持たない。
212
212
  */
213
- /** 予約 state 名 prefix(protocol §5)。この prefix の要素・イベントは常に除外 */
214
- const RESERVED_STATE_NAME_PREFIX = "wcs-devtools";
215
213
  const DEFAULT_TIMELINE_CAPACITY = 500;
216
214
  /** 台帳キーの区切り文字(state 名・パスに現れ得ない NUL)。
217
215
  * エスケープ表記でなく生成式にしているのは、ツーリングがソース中の
218
216
  * `エスケープ列` リテラルを生バイトへ壊す事故が実際に起きたため。 */
219
217
  const NUL = String.fromCharCode(0);
220
- function pathKeyOf(stateName, path) {
221
- return stateName + NUL + path;
218
+ function pathKeyOf(path) {
219
+ return path;
222
220
  }
223
- function tokenKeyOf(stateName, kind, name) {
224
- return stateName + NUL + kind + NUL + name;
221
+ function tokenKeyOf(kind, name) {
222
+ return kind + NUL + name;
225
223
  }
226
- /** 宣言(stateName+path+propName)単位の attach キー。NUL 区切りは pathKeyOf と同じ理由 */
227
- function attachKeyOf(stateName, path, propName) {
228
- return pathKeyOf(stateName, path) + NUL + propName;
224
+ /** 宣言(path+propName)単位の attach キー。NUL 区切りは tokenKeyOf と同じ理由 */
225
+ function attachKeyOf(path, propName) {
226
+ return path + NUL + propName;
227
+ }
228
+ /** roster 表示用のルート由来ラベル(v2: name 次元が無いのでルートで識別する) */
229
+ function rootLabelOf(rootNode) {
230
+ if (typeof ShadowRoot !== "undefined" && rootNode instanceof ShadowRoot) {
231
+ return `<${rootNode.host.tagName.toLowerCase()}>`;
232
+ }
233
+ if (rootNode.nodeType === 9 /* DOCUMENT_NODE */) {
234
+ return "document";
235
+ }
236
+ return rootNode.nodeName.toLowerCase();
229
237
  }
230
238
  /** token カバレッジ 1 行の判定(§4: 空撃ちのみ / 一部空撃ち / 正常 / 未発火) */
231
- function tokenCoverageOf(stateName, kind, name, stat) {
239
+ function tokenCoverageOf(kind, name, stat) {
232
240
  if (stat === undefined) {
233
- return { stateName, kind, name, status: "never", count: 0, note: null };
241
+ return { kind, name, status: "never", count: 0, note: null };
234
242
  }
235
243
  if (stat.zeroSubscriberCount === stat.count) {
236
244
  return {
237
- stateName, kind, name, status: "emitted-unheard", count: stat.count,
245
+ kind, name, status: "emitted-unheard", count: stat.count,
238
246
  note: `all ${stat.count} emit(s) had 0 subscribers`,
239
247
  };
240
248
  }
241
249
  return {
242
- stateName, kind, name, status: "emitted", count: stat.count,
250
+ kind, name, status: "emitted", count: stat.count,
243
251
  note: stat.zeroSubscriberCount > 0
244
252
  ? `${stat.zeroSubscriberCount}/${stat.count} emit(s) had 0 subscribers`
245
253
  : null,
@@ -247,7 +255,6 @@ function tokenCoverageOf(stateName, kind, name, stat) {
247
255
  }
248
256
  class DevtoolsCore {
249
257
  _timelineCapacity;
250
- _hiddenStateNames;
251
258
  _removeListener = null;
252
259
  _sources = new Map();
253
260
  _roster = new Map();
@@ -259,9 +266,12 @@ class DevtoolsCore {
259
266
  _changeListeners = new Set();
260
267
  /** 観測開始時刻(connect 時の performance.now。未接続は null)。 */
261
268
  _observingSince = null;
262
- /** watch 発火回数(stateName + NUL + path → count)。カバレッジの実測面。 */
269
+ /** watch 発火回数(path → count)。カバレッジの実測面。
270
+ * 既知の限界: `state:watch-fired` / `state:token-emit` の payload はツリー識別を
271
+ * 持たないため、複数ツリーが同名の watch パス / token 名を宣言するページでは
272
+ * 実測が合算される(ツリー別に分けるにはプロトコル追補が要る — slice 29 記録)。 */
263
273
  _watchFiredCounts = new Map();
264
- /** token emit 実測(stateName + NUL + kind + NUL + name → 回数と空撃ち回数)。 */
274
+ /** token emit 実測(kind + NUL + name → 回数と空撃ち回数)。上の限界注記と同じ。 */
265
275
  _tokenEmitCounts = new Map();
266
276
  /** 観測開始以降に一度でも attach を観測した宣言キー(attachKeyOf)。
267
277
  * live 配線台帳は WeakRef pruning / binding-removed で縮むため、binding
@@ -270,7 +280,6 @@ class DevtoolsCore {
270
280
  _everAttachedKeys = new Set();
271
281
  constructor(options) {
272
282
  this._timelineCapacity = options?.timelineCapacity ?? DEFAULT_TIMELINE_CAPACITY;
273
- this._hiddenStateNames = new Set(options?.hiddenStateNames ?? []);
274
283
  }
275
284
  get connected() {
276
285
  return this._removeListener !== null;
@@ -281,13 +290,6 @@ class DevtoolsCore {
281
290
  set paused(value) {
282
291
  this._paused = value;
283
292
  }
284
- /** 表示から除外する state 名か(予約 prefix + hiddenStateNames、protocol §5) */
285
- isHiddenStateName(name) {
286
- if (name === null) {
287
- return false;
288
- }
289
- return name.startsWith(RESERVED_STATE_NAME_PREFIX) || this._hiddenStateNames.has(name);
290
- }
291
293
  connect() {
292
294
  if (this._removeListener !== null) {
293
295
  return;
@@ -367,13 +369,22 @@ class DevtoolsCore {
367
369
  this._timeline = [];
368
370
  this._notify("timeline");
369
371
  }
370
- /** 指定パスに束縛された配線(生存している binding のみ) */
371
- getWiringForPath(stateName, path) {
372
- const set = this._wiringByPathKey.get(pathKeyOf(stateName, path));
372
+ /**
373
+ * 指定パスに束縛された配線(生存している binding のみ)。
374
+ * stateElement を渡すとそのツリーの配線だけに絞る(v2 は名前次元が無いため、
375
+ * 複数ツリーの同名パスは要素同一性で区別する)。stateElement を持たない
376
+ * 旧 payload 由来の entry は絞り込みでも残す(欠測を欠落にしない)。
377
+ */
378
+ getWiringForPath(path, stateElement) {
379
+ const set = this._wiringByPathKey.get(pathKeyOf(path));
373
380
  if (set === undefined) {
374
381
  return [];
375
382
  }
376
- return this._collectAlive(set);
383
+ const alive = this._collectAlive(set);
384
+ if (stateElement === undefined) {
385
+ return alive;
386
+ }
387
+ return alive.filter((entry) => entry.stateElementRef === null || entry.stateElementRef.deref() === stateElement);
377
388
  }
378
389
  /** 全配線のスナップショット(生存している binding のみ) */
379
390
  getAllWiring() {
@@ -421,10 +432,7 @@ class DevtoolsCore {
421
432
  }
422
433
  for (const root of roots) {
423
434
  for (const declared of source.getDeclaredBindings(root)) {
424
- if (this.isHiddenStateName(declared.stateName)) {
425
- continue;
426
- }
427
- const key = [declared.stateName, declared.statePathName, declared.propName,
435
+ const key = [declared.statePathName, declared.propName,
428
436
  declared.bindingType, declared.origin, declared.raw].join(NUL);
429
437
  if (seen.has(key)) {
430
438
  continue;
@@ -447,9 +455,9 @@ class DevtoolsCore {
447
455
  const summary = entry.summary;
448
456
  // --- watch: 宣言(watchPaths)× 実測(watch-fired 台帳) ---
449
457
  for (const path of summary.watchPaths ?? []) {
450
- const count = this._watchFiredCounts.get(pathKeyOf(entry.name, path)) ?? 0;
458
+ const count = this._watchFiredCounts.get(pathKeyOf(path)) ?? 0;
451
459
  if (count > 0) {
452
- out.push({ stateName: entry.name, kind: "watch", name: path, status: "fired", count, note: null });
460
+ out.push({ kind: "watch", name: path, status: "fired", count, note: null });
453
461
  continue;
454
462
  }
455
463
  // ワイルドカード行 watch に**リスト書き込み**が届く前提 = 各 `.*` 階層の
@@ -473,23 +481,23 @@ class DevtoolsCore {
473
481
  }
474
482
  if (missingList !== null) {
475
483
  out.push({
476
- stateName: entry.name, kind: "watch", name: path, status: "prerequisite-missing", count: 0,
484
+ kind: "watch", name: path, status: "prerequisite-missing", count: 0,
477
485
  note: keyedKnown
478
486
  ? `list "${missingList}" has no for binding and no $listKeys declaration — list writes never reach its rows (only explicit-index writes can fire this watch)`
479
487
  : `no for binding observed for list "${missingList}" (a $listKeys declaration would still let list writes fire it)`,
480
488
  });
481
489
  continue;
482
490
  }
483
- out.push({ stateName: entry.name, kind: "watch", name: path, status: "never", count: 0, note: null });
491
+ out.push({ kind: "watch", name: path, status: "never", count: 0, note: null });
484
492
  }
485
493
  // --- token: 宣言(commandTokenNames / eventTokenNames)× 実測(emit 台帳)。
486
494
  // subscriberCount 0 = 空撃ち(§4): 全 emit が空撃ちなら emitted-unheard として
487
495
  // 警告する(emit 側だけ配線されて受け手が一つも居ない構成ミスの検出)。 ---
488
496
  for (const name of summary.commandTokenNames) {
489
- out.push(tokenCoverageOf(entry.name, "command", name, this._tokenEmitCounts.get(tokenKeyOf(entry.name, "command", name))));
497
+ out.push(tokenCoverageOf("command", name, this._tokenEmitCounts.get(tokenKeyOf("command", name))));
490
498
  }
491
499
  for (const name of summary.eventTokenNames) {
492
- out.push(tokenCoverageOf(entry.name, "eventToken", name, this._tokenEmitCounts.get(tokenKeyOf(entry.name, "event", name))));
500
+ out.push(tokenCoverageOf("eventToken", name, this._tokenEmitCounts.get(tokenKeyOf("event", name))));
493
501
  }
494
502
  }
495
503
  // --- binding: canonical declared(宣言)× live 配線台帳(実測)。canonical が
@@ -504,7 +512,7 @@ class DevtoolsCore {
504
512
  continue;
505
513
  if (decl.propName.startsWith("eventToken."))
506
514
  continue;
507
- const key = attachKeyOf(decl.stateName, decl.statePathName, decl.propName);
515
+ const key = attachKeyOf(decl.statePathName, decl.propName);
508
516
  if (seen.has(key))
509
517
  continue;
510
518
  seen.add(key);
@@ -512,7 +520,7 @@ class DevtoolsCore {
512
520
  // 破棄や GC(WeakRef pruning)で縮むため、瞬間値だと逆戻りする。
513
521
  const attached = this._everAttachedKeys.has(key);
514
522
  out.push({
515
- stateName: decl.stateName, kind: "binding",
523
+ kind: "binding",
516
524
  name: `${decl.propName} ← ${decl.statePathName}`,
517
525
  status: attached ? "attached" : "never-attached",
518
526
  count: attached ? 1 : 0,
@@ -528,21 +536,21 @@ class DevtoolsCore {
528
536
  if (source === undefined || typeof source.keys !== "function") {
529
537
  return [];
530
538
  }
531
- return source.keys(entry.name, entry.rootNode);
539
+ return source.keys(entry.rootNode);
532
540
  }
533
541
  readValue(entry, path, indexes) {
534
542
  const source = this._sources.get(entry.sourceId);
535
543
  if (source === undefined) {
536
544
  return undefined;
537
545
  }
538
- return source.read(entry.name, entry.rootNode, path, indexes);
546
+ return source.read(entry.rootNode, path, indexes);
539
547
  }
540
548
  writeValue(entry, path, value, indexes) {
541
549
  const source = this._sources.get(entry.sourceId);
542
550
  if (source === undefined) {
543
551
  return;
544
552
  }
545
- source.write(entry.name, entry.rootNode, path, value, indexes);
553
+ source.write(entry.rootNode, path, value, indexes);
546
554
  }
547
555
  // --- internal ---
548
556
  _notify(kind) {
@@ -552,13 +560,14 @@ class DevtoolsCore {
552
560
  }
553
561
  _refreshRosterOf(source) {
554
562
  const entries = [];
563
+ const labelCounts = new Map();
555
564
  for (const summary of source.getStateElements()) {
556
- if (this.isHiddenStateName(summary.name)) {
557
- continue;
558
- }
565
+ const base = rootLabelOf(summary.rootNode);
566
+ const seen = (labelCounts.get(base) ?? 0) + 1;
567
+ labelCounts.set(base, seen);
559
568
  entries.push({
560
569
  sourceId: source.id,
561
- name: summary.name,
570
+ label: seen === 1 ? base : `${base}#${seen}`,
562
571
  rootNode: summary.rootNode,
563
572
  summary,
564
573
  });
@@ -600,9 +609,6 @@ class DevtoolsCore {
600
609
  _ingest(sourceId, event) {
601
610
  switch (event.type) {
602
611
  case "state:element-registered": {
603
- if (this.isHiddenStateName(event.name)) {
604
- return;
605
- }
606
612
  const source = this._sources.get(sourceId);
607
613
  if (source !== undefined) {
608
614
  this._refreshRosterOf(source);
@@ -611,17 +617,13 @@ class DevtoolsCore {
611
617
  this._appendTimeline({
612
618
  sourceId,
613
619
  kind: "element-registered",
614
- stateName: event.name,
615
- label: event.name,
620
+ label: rootLabelOf(event.rootNode),
616
621
  detail: "",
617
622
  subscriberCount: null,
618
623
  });
619
624
  return;
620
625
  }
621
626
  case "state:element-unregistered": {
622
- if (this.isHiddenStateName(event.name)) {
623
- return;
624
- }
625
627
  const source = this._sources.get(sourceId);
626
628
  if (source !== undefined) {
627
629
  this._refreshRosterOf(source);
@@ -630,25 +632,19 @@ class DevtoolsCore {
630
632
  this._appendTimeline({
631
633
  sourceId,
632
634
  kind: "element-unregistered",
633
- stateName: event.name,
634
- label: event.name,
635
+ label: rootLabelOf(event.rootNode),
635
636
  detail: "",
636
637
  subscriberCount: null,
637
638
  });
638
639
  return;
639
640
  }
640
641
  case "state:write": {
641
- const stateName = event.absoluteAddress.absolutePathInfo.stateName;
642
- if (this.isHiddenStateName(stateName)) {
643
- return;
644
- }
645
642
  const detail = event.hasOldValue
646
643
  ? `${formatValue(event.value)} (was ${formatValue(event.oldValue)})`
647
644
  : formatValue(event.value);
648
645
  this._appendTimeline({
649
646
  sourceId,
650
647
  kind: "write",
651
- stateName,
652
648
  label: this._labelOf(event.absoluteAddress),
653
649
  detail,
654
650
  subscriberCount: null,
@@ -659,9 +655,6 @@ class DevtoolsCore {
659
655
  const labels = [];
660
656
  let total = 0;
661
657
  for (const address of event.addresses) {
662
- if (this.isHiddenStateName(address.absolutePathInfo.stateName)) {
663
- continue;
664
- }
665
658
  total++;
666
659
  if (labels.length < 3) {
667
660
  labels.push(this._labelOf(address));
@@ -674,7 +667,6 @@ class DevtoolsCore {
674
667
  this._appendTimeline({
675
668
  sourceId,
676
669
  kind: "batch",
677
- stateName: null,
678
670
  label: `${total} address${total === 1 ? "" : "es"}`,
679
671
  detail: `${labels.join(", ")}${rest}`,
680
672
  subscriberCount: null,
@@ -682,29 +674,28 @@ class DevtoolsCore {
682
674
  return;
683
675
  }
684
676
  case "state:binding-added": {
685
- const stateName = event.absoluteAddress.absolutePathInfo.stateName;
686
- if (this.isHiddenStateName(stateName)) {
687
- return;
688
- }
689
677
  const path = event.absoluteAddress.absolutePathInfo.pathInfo.path;
690
- const key = pathKeyOf(stateName, path);
678
+ const key = pathKeyOf(path);
691
679
  let set = this._wiringByPathKey.get(key);
692
680
  if (set === undefined) {
693
681
  set = new Set();
694
682
  this._wiringByPathKey.set(key, set);
695
683
  }
684
+ const stateElement = event.absoluteAddress.absolutePathInfo.stateElement;
696
685
  const entry = {
697
686
  sourceId,
698
- stateName,
699
687
  path,
700
688
  propName: event.binding.propName,
701
689
  bindingType: event.binding.bindingType,
702
690
  bindingRef: new WeakRef(event.binding),
691
+ stateElementRef: typeof stateElement === "object" && stateElement !== null
692
+ ? new WeakRef(stateElement)
693
+ : null,
703
694
  };
704
695
  set.add(entry);
705
696
  this._wiringEntryByBinding.set(event.binding, entry);
706
697
  // カバレッジの実測面(B2): attach の観測を ever 台帳へ積む(remove では消さない)
707
- this._everAttachedKeys.add(attachKeyOf(stateName, path, event.binding.propName));
698
+ this._everAttachedKeys.add(attachKeyOf(path, event.binding.propName));
708
699
  this._notify("wiring");
709
700
  this._notify("coverage");
710
701
  return;
@@ -715,7 +706,7 @@ class DevtoolsCore {
715
706
  return;
716
707
  }
717
708
  this._wiringEntryByBinding.delete(event.binding);
718
- const key = pathKeyOf(entry.stateName, entry.path);
709
+ const key = pathKeyOf(entry.path);
719
710
  const set = this._wiringByPathKey.get(key);
720
711
  if (set !== undefined) {
721
712
  set.delete(entry);
@@ -727,41 +718,33 @@ class DevtoolsCore {
727
718
  return;
728
719
  }
729
720
  case "state:binding-cleared": {
730
- const stateName = event.absoluteAddress.absolutePathInfo.stateName;
731
721
  const path = event.absoluteAddress.absolutePathInfo.pathInfo.path;
732
- const key = pathKeyOf(stateName, path);
722
+ const key = pathKeyOf(path);
733
723
  if (this._wiringByPathKey.delete(key)) {
734
724
  this._notify("wiring");
735
725
  }
736
726
  return;
737
727
  }
738
728
  case "state:token-emit": {
739
- if (this.isHiddenStateName(event.stateName)) {
740
- return;
741
- }
742
729
  // カバレッジの実測面: emit 回数と空撃ち回数を台帳に積む(§4)。
743
- // stateName が null の emit はどの宣言とも突合できないため積まない。
744
- if (event.stateName !== null) {
745
- const emitKey = tokenKeyOf(event.stateName, event.kind, event.tokenName);
746
- const stat = this._tokenEmitCounts.get(emitKey);
747
- if (stat === undefined) {
748
- this._tokenEmitCounts.set(emitKey, {
749
- count: 1,
750
- zeroSubscriberCount: event.subscriberCount === 0 ? 1 : 0,
751
- });
752
- }
753
- else {
754
- stat.count += 1;
755
- if (event.subscriberCount === 0) {
756
- stat.zeroSubscriberCount += 1;
757
- }
730
+ const emitKey = tokenKeyOf(event.kind, event.tokenName);
731
+ const stat = this._tokenEmitCounts.get(emitKey);
732
+ if (stat === undefined) {
733
+ this._tokenEmitCounts.set(emitKey, {
734
+ count: 1,
735
+ zeroSubscriberCount: event.subscriberCount === 0 ? 1 : 0,
736
+ });
737
+ }
738
+ else {
739
+ stat.count += 1;
740
+ if (event.subscriberCount === 0) {
741
+ stat.zeroSubscriberCount += 1;
758
742
  }
759
- this._notify("coverage");
760
743
  }
744
+ this._notify("coverage");
761
745
  this._appendTimeline({
762
746
  sourceId,
763
747
  kind: event.kind,
764
- stateName: event.stateName,
765
748
  label: event.tokenName,
766
749
  detail: formatArgs(event.args),
767
750
  subscriberCount: event.subscriberCount,
@@ -769,26 +752,19 @@ class DevtoolsCore {
769
752
  return;
770
753
  }
771
754
  case "state:watch-fired": {
772
- if (this.isHiddenStateName(event.stateName)) {
773
- return;
774
- }
775
755
  // timeline 行にはしない(発火は高頻度になりうる活動でありカバレッジが受け皿)。
776
- const firedKey = pathKeyOf(event.stateName, event.path);
756
+ const firedKey = pathKeyOf(event.path);
777
757
  this._watchFiredCounts.set(firedKey, (this._watchFiredCounts.get(firedKey) ?? 0) + 1);
778
758
  this._notify("coverage");
779
759
  return;
780
760
  }
781
761
  case "state:watch-error": {
782
- if (this.isHiddenStateName(event.stateName)) {
783
- return;
784
- }
785
762
  // ランタイム側は例外を握って drain を守るため、ここに出ないと失敗が
786
763
  // どこにも現れない(console を見ていない限り)。phase を detail の先頭に
787
764
  // 置くのは、getter の評価失敗とハンドラの失敗で直し方が違うため。
788
765
  this._appendTimeline({
789
766
  sourceId,
790
767
  kind: "watch-error",
791
- stateName: event.stateName,
792
768
  label: event.path,
793
769
  detail: `${event.phase}: ${formatError(event.error)}`,
794
770
  subscriberCount: null,
@@ -799,9 +775,7 @@ class DevtoolsCore {
799
775
  this._appendTimeline({
800
776
  sourceId,
801
777
  kind: "watch-chain-limit",
802
- stateName: null,
803
- // 打ち切りはバッチ単位で state 名を持たない(複数 state のアドレスが
804
- // 載りうる)ため、hidden 判定はここでは行わない。
778
+ // 打ち切りはバッチ単位(複数ツリーのアドレスが載りうる)— 行の主語は深さ上限
805
779
  label: `depth > ${event.maxDepth}`,
806
780
  detail: event.paths.join(", "),
807
781
  subscriberCount: null,
@@ -809,15 +783,11 @@ class DevtoolsCore {
809
783
  return;
810
784
  }
811
785
  case "state:path-unresolved": {
812
- if (this.isHiddenStateName(event.stateName)) {
813
- return;
814
- }
815
786
  // ランタイムは warn で続行する(既存ページを止めないため)。lint を
816
787
  // 走らせていない相手には、ここが「配線が死んでいる」唯一の可視面になる。
817
788
  this._appendTimeline({
818
789
  sourceId,
819
790
  kind: "path-unresolved",
820
- stateName: event.stateName,
821
791
  label: event.path,
822
792
  detail: `${event.source}: "${event.missingSegment}" is not declared`,
823
793
  subscriberCount: null,
@@ -825,15 +795,11 @@ class DevtoolsCore {
825
795
  return;
826
796
  }
827
797
  case "state:binding-apply-error": {
828
- if (this.isHiddenStateName(event.stateName)) {
829
- return;
830
- }
831
798
  // 隔離された適用失敗。bindingType を detail 先頭に置くのは、構造
832
799
  // (for / if)の失敗と値の失敗で影響範囲が違うため。
833
800
  this._appendTimeline({
834
801
  sourceId,
835
802
  kind: "binding-apply-error",
836
- stateName: event.stateName,
837
803
  label: event.path,
838
804
  detail: `${event.bindingType}: ${formatError(event.error)}`,
839
805
  subscriberCount: null,
@@ -841,12 +807,10 @@ class DevtoolsCore {
841
807
  return;
842
808
  }
843
809
  case "propagation:suppressed": {
844
- // two-way エコーの辺単位抑止。state 名を持たない(node+member が主語)
845
- // ため hidden 判定はここでは行わない。
810
+ // two-way エコーの辺単位抑止(主語は node + member — パスではない)
846
811
  this._appendTimeline({
847
812
  sourceId,
848
813
  kind: "propagation-suppressed",
849
- stateName: null,
850
814
  label: event.member,
851
815
  detail: `${event.reason} (tx ${event.transactionId}, edge ${event.edgeId})`,
852
816
  subscriberCount: null,
@@ -854,14 +818,9 @@ class DevtoolsCore {
854
818
  return;
855
819
  }
856
820
  case "propagation:coalesced": {
857
- const stateName = event.absoluteAddress.absolutePathInfo.stateName;
858
- if (this.isHiddenStateName(stateName)) {
859
- return;
860
- }
861
821
  this._appendTimeline({
862
822
  sourceId,
863
823
  kind: "propagation-coalesced",
864
- stateName,
865
824
  label: this._labelOf(event.absoluteAddress),
866
825
  detail: `tx ${event.droppedTransactionId} dropped (winner tx ${event.winnerTransactionId})`,
867
826
  subscriberCount: null,
@@ -869,14 +828,9 @@ class DevtoolsCore {
869
828
  return;
870
829
  }
871
830
  case "propagation:hop-limit": {
872
- const stateName = event.absoluteAddress.absolutePathInfo.stateName;
873
- if (this.isHiddenStateName(stateName)) {
874
- return;
875
- }
876
831
  this._appendTimeline({
877
832
  sourceId,
878
833
  kind: "propagation-hop-limit",
879
- stateName,
880
834
  label: this._labelOf(event.absoluteAddress),
881
835
  detail: `hop ${event.hop} (tx ${event.transactionId})`,
882
836
  subscriberCount: null,
@@ -894,7 +848,6 @@ class DevtoolsCore {
894
848
  this._appendTimeline({
895
849
  sourceId,
896
850
  kind: "contract-drift",
897
- stateName: null,
898
851
  label: event.tag,
899
852
  detail: `${event.reason}${memberPart}${eventPart}`,
900
853
  subscriberCount: null,
@@ -922,7 +875,7 @@ class DevtoolsCore {
922
875
  * 「宣言レベルの配線ビュー」を組む。ライブ台帳と違い binding 実体・
923
876
  * 接続状態は分からない(UI では "declared" バッジで区別する)。
924
877
  *
925
- * パースは表示目的の簡易版(`prop[#mod]: path[@state][|filters]` を
878
+ * パースは表示目的の簡易版(`prop[#mod]: path[|filters]` を
926
879
  * `;` 区切りで分解するだけ)。正確なセマンティクスの正本は
927
880
  * @wcstack/state の bindTextParser であり、ここでは追随しない。
928
881
  */
@@ -943,18 +896,13 @@ function parseEntry(element, raw, origin) {
943
896
  return null;
944
897
  }
945
898
  const [pathPart, ...filterParts] = rhs.split("|").map((part) => part.trim());
946
- let path = pathPart;
947
- let stateName = "default";
948
- const at = pathPart.lastIndexOf("@");
949
- if (at > 0) {
950
- path = pathPart.slice(0, at).trim();
951
- stateName = pathPart.slice(at + 1).trim();
952
- }
899
+ // v2: `@name` セレクタは撤去済み(ランタイムは parse error にする)。
900
+ // 簡易パーサはパスをそのまま流す — 壊れた宣言の可視化はランタイム側の役目。
901
+ const path = pathPart;
953
902
  return {
954
903
  element,
955
904
  propName,
956
905
  path,
957
- stateName,
958
906
  filters: filterParts.filter((part) => part.length > 0),
959
907
  origin,
960
908
  raw: trimmed,
@@ -1178,13 +1126,8 @@ class WcsDevtools extends HTMLElement {
1178
1126
  this._buildShadow();
1179
1127
  }
1180
1128
  const capacity = Number(this.getAttribute("buffer") ?? "");
1181
- const hidden = (this.getAttribute("hidden-states") ?? "")
1182
- .split(",")
1183
- .map((name) => name.trim())
1184
- .filter((name) => name.length > 0);
1185
1129
  this._core = new DevtoolsCore({
1186
1130
  timelineCapacity: Number.isFinite(capacity) && capacity > 0 ? capacity : undefined,
1187
- hiddenStateNames: hidden,
1188
1131
  });
1189
1132
  this._removeCoreListener = this._core.onChange((kind) => {
1190
1133
  if (kind === "roster" || kind === "sources") {
@@ -1461,7 +1404,7 @@ class WcsDevtools extends HTMLElement {
1461
1404
  }
1462
1405
  }
1463
1406
  _rosterKey(entry) {
1464
- return `${entry.sourceId}:${entry.name}`;
1407
+ return `${entry.sourceId}:${entry.label}`;
1465
1408
  }
1466
1409
  _selectedRoster() {
1467
1410
  const core = this._core;
@@ -1481,7 +1424,7 @@ class WcsDevtools extends HTMLElement {
1481
1424
  select.replaceChildren(...roster.map((entry) => {
1482
1425
  const option = document.createElement("option");
1483
1426
  option.value = this._rosterKey(entry);
1484
- option.textContent = `${entry.name} (${entry.sourceId.slice(0, 12)})`;
1427
+ option.textContent = `${entry.label} (${entry.sourceId.slice(0, 12)})`;
1485
1428
  option.selected = selected !== null && this._rosterKey(entry) === this._rosterKey(selected);
1486
1429
  return option;
1487
1430
  }));
@@ -1632,8 +1575,9 @@ class WcsDevtools extends HTMLElement {
1632
1575
  }
1633
1576
  else if (this._selectedPath !== null) {
1634
1577
  const selected = this._selectedRoster();
1578
+ // 選択中のツリーでスコープ(複数ツリーの同名パスを混ぜない)
1635
1579
  entries =
1636
- selected !== null ? core.getWiringForPath(selected.name, this._selectedPath) : [];
1580
+ selected !== null ? core.getWiringForPath(this._selectedPath, selected.summary.element) : [];
1637
1581
  contextLabel = this._selectedPath;
1638
1582
  }
1639
1583
  else {
@@ -1726,7 +1670,7 @@ class WcsDevtools extends HTMLElement {
1726
1670
  }
1727
1671
  const label = document.createElement("span");
1728
1672
  label.className = "path";
1729
- label.textContent = ` ${entry.name}@${entry.stateName} `;
1673
+ label.textContent = ` ${entry.name} `;
1730
1674
  row.append(kind, document.createTextNode(" "), label, status);
1731
1675
  if (entry.note !== null) {
1732
1676
  // note はホバー頼み(title)にせず常時表示する — 前提未成立の理由は
@@ -1752,7 +1696,7 @@ class WcsDevtools extends HTMLElement {
1752
1696
  const path = document.createElement("span");
1753
1697
  path.className = "path";
1754
1698
  const filters = entry.outFilters.map((f) => ` | ${f.filterName}(${f.args.join(",")})`).join("");
1755
- path.textContent = `${entry.statePathName}@${entry.stateName}${filters}`;
1699
+ path.textContent = `${entry.statePathName}${filters}`;
1756
1700
  row.append(type, document.createTextNode(" "), prop, document.createTextNode(" ← "), path);
1757
1701
  if (entry.node !== null) {
1758
1702
  const target = entry.node;
@@ -1781,7 +1725,7 @@ class WcsDevtools extends HTMLElement {
1781
1725
  const arrow = document.createTextNode(" ← ");
1782
1726
  const path = document.createElement("span");
1783
1727
  path.className = "path";
1784
- path.textContent = `${entry.path}@${entry.stateName}`;
1728
+ path.textContent = entry.path;
1785
1729
  const type = document.createElement("span");
1786
1730
  type.className = "badge-tag";
1787
1731
  type.textContent = entry.bindingType;
@@ -1805,7 +1749,7 @@ class WcsDevtools extends HTMLElement {
1805
1749
  prop.textContent = entry.propName;
1806
1750
  const path = document.createElement("span");
1807
1751
  path.className = "path";
1808
- path.textContent = `${entry.path}@${entry.stateName}`;
1752
+ path.textContent = entry.path;
1809
1753
  row.append(type, document.createTextNode(" "), prop, document.createTextNode(" ← "), path);
1810
1754
  row.addEventListener("click", () => {
1811
1755
  this._highlightNodes([entry.element]);
@@ -1870,8 +1814,7 @@ class WcsDevtools extends HTMLElement {
1870
1814
  }
1871
1815
  const label = document.createElement("span");
1872
1816
  label.className = "label";
1873
- const stateName = entry.stateName !== null ? `@${entry.stateName}` : "";
1874
- label.textContent = ` ${entry.label}${stateName} `;
1817
+ label.textContent = ` ${entry.label} `;
1875
1818
  const detail = document.createElement("span");
1876
1819
  detail.className = "detail";
1877
1820
  detail.textContent = entry.detail;
@@ -1888,7 +1831,7 @@ class WcsDevtools extends HTMLElement {
1888
1831
  _highlightPath(entry, path) {
1889
1832
  const core = this._core;
1890
1833
  const nodes = [];
1891
- for (const wiring of core.getWiringForPath(entry.name, path)) {
1834
+ for (const wiring of core.getWiringForPath(path, entry.summary.element)) {
1892
1835
  const binding = wiring.bindingRef.deref();
1893
1836
  if (binding !== undefined) {
1894
1837
  nodes.push(binding.node, binding.replaceNode);
@@ -1954,5 +1897,5 @@ function bootstrapDevtools() {
1954
1897
  }
1955
1898
  }
1956
1899
 
1957
- export { DEVTOOLS_HOOK_GLOBAL, DEVTOOLS_PROTOCOL_VERSION, DevtoolsCore, RESERVED_STATE_NAME_PREFIX, WcsDevtools, bootstrapDevtools, formatArgs, formatValue, getOrCreateHookRegistry, scanDeclaredBindings };
1900
+ export { DEVTOOLS_HOOK_GLOBAL, DEVTOOLS_PROTOCOL_VERSION, DevtoolsCore, WcsDevtools, bootstrapDevtools, formatArgs, formatValue, getOrCreateHookRegistry, scanDeclaredBindings };
1958
1901
  //# sourceMappingURL=index.esm.js.map