@wcstack/state 1.21.1 → 1.21.3

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
@@ -3332,6 +3332,38 @@ let nextRecordId = 0;
3332
3332
  let nextGeneration = 0;
3333
3333
  const recordByBinding = new WeakMap();
3334
3334
  const sessionByRoot = new WeakMap();
3335
+ // node → その node に関心を持つ session(anchor として binding を覚えている、
3336
+ // または定義待ちタスクを抱えている)。BindingOwner は mutation で増減した
3337
+ // サブツリーを1回だけ走査し、ここに登録された session だけへ per-node 配送する。
3338
+ // 全 session ブロードキャストだと、リスト行の逐次 append などで
3339
+ // 「session 数 × 変異ノード数」の O(n²) ファンアウトになるため、その正本台帳。
3340
+ // 大多数の node は関心 session が1つなので単一値で持ち、2つ目から Set に昇格する。
3341
+ const interestedSessionsByNode = new WeakMap();
3342
+ function addInterestedSession(node, session) {
3343
+ const current = interestedSessionsByNode.get(node);
3344
+ if (typeof current === "undefined") {
3345
+ interestedSessionsByNode.set(node, session);
3346
+ return;
3347
+ }
3348
+ if (current === session)
3349
+ return;
3350
+ if (current instanceof Set) {
3351
+ current.add(session);
3352
+ return;
3353
+ }
3354
+ interestedSessionsByNode.set(node, new Set([current, session]));
3355
+ }
3356
+ function forEachInterestedSession(node, callback) {
3357
+ const current = interestedSessionsByNode.get(node);
3358
+ if (typeof current === "undefined")
3359
+ return;
3360
+ if (current instanceof Set) {
3361
+ for (const session of Array.from(current))
3362
+ callback(session);
3363
+ return;
3364
+ }
3365
+ callback(current);
3366
+ }
3335
3367
  function forEachInclusive(root, callback) {
3336
3368
  callback(root);
3337
3369
  for (const child of Array.from(root.childNodes)) {
@@ -3350,8 +3382,6 @@ function observableRootFor(node) {
3350
3382
  }
3351
3383
  class BindingOwner {
3352
3384
  root;
3353
- sessionRefs = new Set();
3354
- knownSessions = new WeakSet();
3355
3385
  observer;
3356
3386
  constructor(root) {
3357
3387
  this.root = root;
@@ -3361,12 +3391,6 @@ class BindingOwner {
3361
3391
  : null;
3362
3392
  this.observer?.observe(root, { childList: true, subtree: true });
3363
3393
  }
3364
- add(session) {
3365
- if (this.knownSessions.has(session))
3366
- return;
3367
- this.knownSessions.add(session);
3368
- this.sessionRefs.add(new WeakRef(session));
3369
- }
3370
3394
  handleMutations(mutations) {
3371
3395
  const removed = [];
3372
3396
  const added = [];
@@ -3374,14 +3398,29 @@ class BindingOwner {
3374
3398
  removed.push(...Array.from(mutation.removedNodes));
3375
3399
  added.push(...Array.from(mutation.addedNodes));
3376
3400
  }
3377
- for (const ref of Array.from(this.sessionRefs)) {
3378
- const session = ref.deref();
3379
- if (typeof session === "undefined") {
3380
- this.sessionRefs.delete(ref);
3381
- continue;
3382
- }
3383
- session.handleMutations(this.root, removed, added);
3401
+ // 走査は owner が1回だけ行い、関心 session が居る node だけを配送・contains
3402
+ // 検査へ進める。contains O(木の深さ) なので、関心の無い node で呼ばない。
3403
+ const reconnected = [];
3404
+ for (const subtree of removed) {
3405
+ forEachInclusive(subtree, (node) => {
3406
+ forEachInterestedSession(node, (session) => {
3407
+ if (this.root.contains(node))
3408
+ return;
3409
+ session.handleRemovedNode(node);
3410
+ });
3411
+ });
3412
+ }
3413
+ for (const subtree of added) {
3414
+ forEachInclusive(subtree, (node) => {
3415
+ forEachInterestedSession(node, (session) => {
3416
+ if (!this.root.contains(node))
3417
+ return;
3418
+ session.handleAddedNode(node, reconnected);
3419
+ });
3420
+ });
3384
3421
  }
3422
+ if (reconnected.length > 0)
3423
+ applyChangeFromBindings(reconnected);
3385
3424
  }
3386
3425
  }
3387
3426
  const ownerByRoot = new WeakMap();
@@ -3477,6 +3516,7 @@ class BindingSession {
3477
3516
  raiseError(`CustomElementRegistry is unavailable for <${tagName}>.`);
3478
3517
  }
3479
3518
  this.observe(node);
3519
+ addInterestedSession(node, this);
3480
3520
  const task = { node, active: true, cancel: null };
3481
3521
  let tasks = this.deferredByNode.get(node);
3482
3522
  if (typeof tasks === "undefined") {
@@ -3534,27 +3574,17 @@ class BindingSession {
3534
3574
  const root = observableRootFor(node);
3535
3575
  if (root === null)
3536
3576
  return;
3537
- getBindingOwner(root).add(this);
3577
+ // owner(root ごとの MutationObserver)の存在だけ保証する。session の配送先
3578
+ // 登録は node 単位(interestedSessionsByNode)で行い、owner は session を
3579
+ // 直接は保持しない。
3580
+ getBindingOwner(root);
3538
3581
  }
3539
3582
  handleMutations(root, removed, added) {
3540
3583
  for (const subtree of removed) {
3541
3584
  forEachInclusive(subtree, (node) => {
3542
3585
  if (root.contains(node))
3543
3586
  return;
3544
- const known = this.knownBindingsByNode.get(node);
3545
- if (typeof known !== "undefined") {
3546
- for (const binding of known.values())
3547
- this.disposeBinding(binding);
3548
- }
3549
- const tasks = this.deferredByNode.get(node);
3550
- if (typeof tasks !== "undefined") {
3551
- for (const task of Array.from(tasks)) {
3552
- task.active = false;
3553
- task.cancel?.();
3554
- tasks.delete(task);
3555
- this.deferred.delete(task);
3556
- }
3557
- }
3587
+ this.handleRemovedNode(node);
3558
3588
  });
3559
3589
  }
3560
3590
  const reconnected = [];
@@ -3562,36 +3592,58 @@ class BindingSession {
3562
3592
  forEachInclusive(subtree, (node) => {
3563
3593
  if (!root.contains(node))
3564
3594
  return;
3565
- const known = this.knownBindingsByNode.get(node);
3566
- if (typeof known === "undefined")
3567
- return;
3568
- for (const binding of known.values()) {
3569
- const record = recordByBinding.get(binding);
3570
- if (record?.phase === "active") {
3571
- this.settleConnectedSnapshot(record);
3572
- continue;
3573
- }
3574
- if (record?.phase !== "disposed")
3575
- continue;
3576
- const options = this.optionsByBinding.get(binding);
3577
- if (typeof options === "undefined")
3578
- continue;
3579
- try {
3580
- this.start(binding, options);
3581
- if (options.applyOnReconnect && this.shouldApplyState(binding))
3582
- reconnected.push(binding);
3583
- }
3584
- catch {
3585
- // Mutation delivery cannot surface initialization errors to a caller.
3586
- }
3587
- }
3595
+ this.handleAddedNode(node, reconnected);
3588
3596
  });
3589
3597
  }
3590
3598
  if (reconnected.length > 0)
3591
3599
  applyChangeFromBindings(reconnected);
3592
3600
  }
3601
+ handleRemovedNode(node) {
3602
+ const known = this.knownBindingsByNode.get(node);
3603
+ if (typeof known !== "undefined") {
3604
+ for (const binding of known.values())
3605
+ this.disposeBinding(binding);
3606
+ }
3607
+ const tasks = this.deferredByNode.get(node);
3608
+ if (typeof tasks !== "undefined") {
3609
+ for (const task of Array.from(tasks)) {
3610
+ task.active = false;
3611
+ task.cancel?.();
3612
+ tasks.delete(task);
3613
+ this.deferred.delete(task);
3614
+ }
3615
+ }
3616
+ }
3617
+ handleAddedNode(node, reconnected) {
3618
+ const known = this.knownBindingsByNode.get(node);
3619
+ if (typeof known === "undefined")
3620
+ return;
3621
+ for (const binding of known.values()) {
3622
+ const record = recordByBinding.get(binding);
3623
+ if (record?.phase === "active") {
3624
+ this.settleConnectedSnapshot(record);
3625
+ continue;
3626
+ }
3627
+ if (record?.phase !== "disposed")
3628
+ continue;
3629
+ const options = this.optionsByBinding.get(binding);
3630
+ if (typeof options === "undefined")
3631
+ continue;
3632
+ try {
3633
+ this.start(binding, options);
3634
+ if (options.applyOnReconnect && this.shouldApplyState(binding))
3635
+ reconnected.push(binding);
3636
+ }
3637
+ catch {
3638
+ // Mutation delivery cannot surface initialization errors to a caller.
3639
+ }
3640
+ }
3641
+ }
3593
3642
  remember(binding, options) {
3594
3643
  const anchor = binding.replaceNode;
3644
+ // detached fragment 上でも登録しておく(node 単位の台帳なので root 非依存)。
3645
+ // fragment 一括マウントで後から接続された行にも mutation 配送が届くようにする。
3646
+ addInterestedSession(anchor, this);
3595
3647
  let known = this.knownBindingsByNode.get(anchor);
3596
3648
  if (typeof known === "undefined") {
3597
3649
  known = new Map();
@@ -6058,7 +6110,7 @@ async function buildBindings(root) {
6058
6110
  }
6059
6111
  }
6060
6112
 
6061
- var version = "1.21.1";
6113
+ var version = "1.21.3";
6062
6114
  var pkg = {
6063
6115
  version: version};
6064
6116