@wcstack/state 2.0.0 → 2.1.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.d.ts CHANGED
@@ -91,7 +91,6 @@ interface IStateProxy extends IState {
91
91
  type Mutability = "readonly" | "writable";
92
92
 
93
93
  interface IStateElement {
94
- readonly name: string;
95
94
  /**
96
95
  * state のロードが完了しているか。`initializePromise` の同期版で、
97
96
  * DCC のアクセサが「今すぐ読み書きしてよいか」を判断するのに使う。
@@ -867,6 +866,13 @@ type DevtoolsEvent = {
867
866
  readonly tokenName: string;
868
867
  readonly args: readonly unknown[];
869
868
  readonly subscriberCount: number;
869
+ /**
870
+ * 発火元ツリーの state 要素(protocol v2 追補 2026-09-05・additive)。
871
+ * registry(getOrCreate*Token)経由で作られた token だけが持つ — 直接生成された
872
+ * token や旧ランタイムの payload には無い(optional)。無い emit の実測は
873
+ * ツリー別に分けられないため、消費側は全ツリーの照会へ合算で残す。
874
+ */
875
+ readonly stateElement?: IStateElement;
870
876
  } | {
871
877
  readonly type: "state:watch-error";
872
878
  /** throw 元。cur の評価(getter)とハンドラ本体では原因も直し方も違う */
@@ -883,6 +889,13 @@ type DevtoolsEvent = {
883
889
  readonly type: "state:watch-fired";
884
890
  /** `$watch` の宣言キー(ワイルドカードを含む生のパス) */
885
891
  readonly path: string;
892
+ /**
893
+ * 発火元ツリーの state 要素(protocol v2 追補 2026-09-05・additive)。
894
+ * 複数ツリーが同名の watch パスを宣言するページで実測台帳をツリー別に
895
+ * 分けるための識別。旧ランタイムの payload には無い(optional)— 無い発火は
896
+ * 消費側が全ツリーの照会へ合算で残す。値を載せない契約(§4.3.1)は不変。
897
+ */
898
+ readonly stateElement?: IStateElement;
886
899
  } | {
887
900
  readonly type: "state:path-unresolved";
888
901
  /** 書き手が書いた面。診断 code が binding / watch で変わる */
@@ -988,13 +1001,19 @@ declare function analyzeContract(manifest: IContractManifest): readonly Contract
988
1001
  declare class State extends HTMLElementBase implements IStateElement {
989
1002
  static hasConnectedCallbackPromise: boolean;
990
1003
  static getBindingsReady(rootNode: Node): Promise<void>;
1004
+ /**
1005
+ * `mount` の動的変更は未サポート(再マウントは非目標 — 設計書 §4-7)。
1006
+ * 初期化済み要素での変更は無言で捨てず warn で知らせる。初期化前の属性設定
1007
+ * (パース時・接続前の setAttribute)は正規の使い方なので黙る。
1008
+ * `name` は connectedCallback 冒頭で fail-fast 済みなので観測しない。
1009
+ */
1010
+ static get observedAttributes(): string[];
991
1011
  private __state;
992
1012
  private _hasUpdatedCallback;
993
1013
  /** enable-ssr のスナップショットから初期化された(D14: ボリュームはデータを採用する) */
994
1014
  private _hydratedFromSsr;
995
1015
  private _crossRowListPaths;
996
1016
  private _indexDependentGetterPaths;
997
- private _name;
998
1017
  private _initialized;
999
1018
  private _initializePromise;
1000
1019
  private _resolveInitialize;
@@ -1036,7 +1055,7 @@ declare class State extends HTMLElementBase implements IStateElement {
1036
1055
  constructor();
1037
1056
  private get _state();
1038
1057
  private set _state(value);
1039
- get name(): string;
1058
+ attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void;
1040
1059
  private _loadFromSsrElement;
1041
1060
  /** state / src / json / inner <script> / API set のソース解決(_initialize とボリュームで共用)。 */
1042
1061
  private _loadStateFromSource;
package/dist/index.esm.js CHANGED
@@ -3655,8 +3655,15 @@ class Token {
3655
3655
  // EventToken は共有 pub/sub プリミティブ Token の薄い特化(element→state 方向)。
3656
3656
  // instanceof による型判別を成立させるため独立クラスとして維持する。
3657
3657
  class EventToken extends Token {
3658
- constructor(name) {
3658
+ /**
3659
+ * 属する state 要素(devtools のツリー識別 — protocol v2 追補)。
3660
+ * registry(getOrCreateEventToken)経由の生成でのみ渡る optional 参照。
3661
+ * 寿命は registry 側の WeakMap が管理する(CommandToken と同じ位置づけ)。
3662
+ */
3663
+ _stateElement;
3664
+ constructor(name, stateElement) {
3659
3665
  super(name);
3666
+ this._stateElement = stateElement;
3660
3667
  }
3661
3668
  emit(...args) {
3662
3669
  if (devtoolsSink !== null) {
@@ -3666,6 +3673,7 @@ class EventToken extends Token {
3666
3673
  tokenName: this.name,
3667
3674
  args,
3668
3675
  subscriberCount: this.size,
3676
+ stateElement: this._stateElement,
3669
3677
  });
3670
3678
  }
3671
3679
  return super.emit(...args);
@@ -3681,7 +3689,8 @@ function getOrCreateEventToken(stateElement, name) {
3681
3689
  }
3682
3690
  let token = registry.get(name);
3683
3691
  if (typeof token === "undefined") {
3684
- token = new EventToken(name);
3692
+ // stateElement を渡すのは devtools のツリー識別(protocol v2 追補)のため
3693
+ token = new EventToken(name, stateElement);
3685
3694
  registry.set(name, token);
3686
3695
  }
3687
3696
  return token;
@@ -3806,8 +3815,16 @@ function detachEventTokenHandler(binding) {
3806
3815
  // instanceof による型判別を成立させるため独立クラスとして維持する。
3807
3816
  //
3808
3817
  class CommandToken extends Token {
3809
- constructor(name) {
3818
+ /**
3819
+ * 属する state 要素(devtools のツリー識別 — protocol v2 追補)。
3820
+ * registry(getOrCreateCommandToken)経由の生成でのみ渡る optional 参照で、
3821
+ * token の寿命は registry 側の WeakMap が state 要素に紐づけている(ここが
3822
+ * 寿命を延ばす新しい経路にはならない)。emit の payload にだけ載せる。
3823
+ */
3824
+ _stateElement;
3825
+ constructor(name, stateElement) {
3810
3826
  super(name);
3827
+ this._stateElement = stateElement;
3811
3828
  }
3812
3829
  emit(...args) {
3813
3830
  if (devtoolsSink !== null) {
@@ -3819,6 +3836,7 @@ class CommandToken extends Token {
3819
3836
  tokenName: this.name,
3820
3837
  args,
3821
3838
  subscriberCount: this.size,
3839
+ stateElement: this._stateElement,
3822
3840
  });
3823
3841
  }
3824
3842
  return super.emit(...args);
@@ -9164,7 +9182,7 @@ async function buildBindings(root) {
9164
9182
  }
9165
9183
  }
9166
9184
 
9167
- var version = "2.0.0";
9185
+ var version = "2.1.0";
9168
9186
  var pkg = {
9169
9187
  version: version};
9170
9188
 
@@ -11186,7 +11204,8 @@ function getOrCreateCommandToken(stateElement, name) {
11186
11204
  }
11187
11205
  let token = registry.get(name);
11188
11206
  if (typeof token === "undefined") {
11189
- token = new CommandToken(name);
11207
+ // stateElement を渡すのは devtools のツリー識別(protocol v2 追補)のため
11208
+ token = new CommandToken(name, stateElement);
11190
11209
  registry.set(name, token);
11191
11210
  }
11192
11211
  return token;
@@ -12774,8 +12793,10 @@ function fireOne(hit) {
12774
12793
  }
12775
12794
  // 正常発火の観測(設計書 §11 の予約イベント・配線カバレッジの実測面)。
12776
12795
  // 値は載せない。イベント生成は sink 接続時のみ(ゼロコスト契約)。
12796
+ // stateElement は発火元ツリーの識別(protocol v2 追補)— これが無いと
12797
+ // 複数ツリーが同名 watch パスを宣言するページで実測が合算される。
12777
12798
  if (devtoolsSink !== null) {
12778
- devtoolsSink({ type: "state:watch-fired", path: entry.path });
12799
+ devtoolsSink({ type: "state:watch-fired", path: entry.path, stateElement });
12779
12800
  }
12780
12801
  entry.handler.call(state, cur, prev, ...indexes);
12781
12802
  });
@@ -15317,6 +15338,14 @@ function updatedCallback(target, refs, receiver, handler) {
15317
15338
  }
15318
15339
  const pathInfo = ref.absolutePathInfo.pathInfo;
15319
15340
  const pathName = pathInfo.path;
15341
+ // D20/D21: マーカーパス(`#m<id>` セグメント = マウント私有キーの内部アドレス)は
15342
+ // マウントインスタンスの私有語彙。ルートの $updatedCallback へ素通しすると、
15343
+ // 作者に解釈不能で再初期化のたびに変わる内部 id が漏れるため配送しない
15344
+ // (`#` はパス文法で書けない文字 — ツリーパスとは構造的に衝突しない)。
15345
+ // 私有キーの可視化は devtools の overlays() 経由(プロトコル v2)。
15346
+ if (pathName.indexOf("#") !== -1) {
15347
+ continue;
15348
+ }
15320
15349
  paths.add(pathName);
15321
15350
  if (pathInfo.wildcardCount > 0) {
15322
15351
  const indexes = getScopedIndexes(ref.listIndex, pathInfo.wildcardCount);
@@ -15349,6 +15378,10 @@ function updatedCallback(target, refs, receiver, handler) {
15349
15378
  if (path !== volume.mountPath && !path.startsWith(prefix)) {
15350
15379
  continue;
15351
15380
  }
15381
+ // マーカーパス(マウント私有キー)はボリューム相対配送にも漏らさない(上と同じ D20/D21)
15382
+ if (path.indexOf("#") !== -1) {
15383
+ continue;
15384
+ }
15352
15385
  const relative = path === volume.mountPath ? "" : path.slice(prefix.length);
15353
15386
  if (relative === "") {
15354
15387
  continue; // マウントポイント自身(接ぎ木そのもの)は相対で表せない
@@ -16386,6 +16419,15 @@ class State extends HTMLElementBase {
16386
16419
  static getBindingsReady(rootNode) {
16387
16420
  return getBindingsReady(rootNode);
16388
16421
  }
16422
+ /**
16423
+ * `mount` の動的変更は未サポート(再マウントは非目標 — 設計書 §4-7)。
16424
+ * 初期化済み要素での変更は無言で捨てず warn で知らせる。初期化前の属性設定
16425
+ * (パース時・接続前の setAttribute)は正規の使い方なので黙る。
16426
+ * `name` は connectedCallback 冒頭で fail-fast 済みなので観測しない。
16427
+ */
16428
+ static get observedAttributes() {
16429
+ return ["mount"];
16430
+ }
16389
16431
  __state;
16390
16432
  _hasUpdatedCallback = false;
16391
16433
  /** enable-ssr のスナップショットから初期化された(D14: ボリュームはデータを採用する) */
@@ -16396,7 +16438,6 @@ class State extends HTMLElementBase {
16396
16438
  // $1 等のインデックスを読んだ getter パス(実行時検出)。位置のみ変わった行の
16397
16439
  // 静的子展開はこの集合の subtree に限定される。追加のみ・クリアしない(安全側)。
16398
16440
  _indexDependentGetterPaths = new Set();
16399
- _name = 'default';
16400
16441
  _initialized = false;
16401
16442
  _initializePromise;
16402
16443
  _resolveInitialize = null;
@@ -16530,8 +16571,14 @@ class State extends HTMLElementBase {
16530
16571
  }
16531
16572
  this._resolveLoading?.();
16532
16573
  }
16533
- get name() {
16534
- return this._name;
16574
+ attributeChangedCallback(_name, oldValue, newValue) {
16575
+ // observedAttributes は "mount" のみ。同値 set(oldValue === newValue)は変更ではない
16576
+ if (!this._initialized || oldValue === newValue) {
16577
+ return;
16578
+ }
16579
+ console.warn(`[@wcstack/state] Changing the "mount" attribute after initialization is not supported and is ignored ` +
16580
+ `(was ${oldValue === null ? "absent" : `"${oldValue}"`}, now ${newValue === null ? "absent" : `"${newValue}"`}). ` +
16581
+ `Remove this <${config.tagNames.state}> element and create a new one with the desired mount path instead.`);
16535
16582
  }
16536
16583
  _loadFromSsrElement() {
16537
16584
  if (!this.hasAttribute('enable-ssr'))
@@ -16571,7 +16618,9 @@ class State extends HTMLElementBase {
16571
16618
  else {
16572
16619
  const script = this.querySelector('script[type="module"]');
16573
16620
  if (script) {
16574
- return await loadFromInnerScript(script, `${this._name}`);
16621
+ // sourceURL ラベル。v2 はルートに 1 ツリーなので名前次元は無く、要素の
16622
+ // タグ名(DCC 経路が host のタグ名を渡すのと同じ流儀)で特定十分
16623
+ return await loadFromInnerScript(script, config.tagNames.state);
16575
16624
  }
16576
16625
  else {
16577
16626
  const timerId = setTimeout(() => {