@rickcedwhat/playwright-smart-table 6.16.0 → 6.17.1

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.
@@ -1,15 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NavigationBarrier = void 0;
4
- /**
5
- * A synchronization barrier that allows multiple parallel row processors
6
- * to coordinate their navigation actions.
7
- */
4
+ const mutex_1 = require("./mutex");
8
5
  class NavigationBarrier {
9
6
  constructor(total) {
10
7
  this.finishedCount = 0;
11
- // Map of colIndex -> Set of waiting resolvers
12
8
  this.buckets = new Map();
9
+ this.recoveryMutex = null;
13
10
  this.total = total;
14
11
  }
15
12
  /** True when more than one row shares this barrier (scrollToRow would evict peers). */
@@ -18,7 +15,7 @@ class NavigationBarrier {
18
15
  }
19
16
  /**
20
17
  * Synchronize all active rows at a specific column index.
21
- * The last row to arrive will trigger the `moveAction`.
18
+ * The last row to arrive (or the last markFinished call) triggers the `moveAction`.
22
19
  */
23
20
  async sync(colIndex, moveAction) {
24
21
  if (this.total <= 1) {
@@ -26,50 +23,82 @@ class NavigationBarrier {
26
23
  return await moveAction();
27
24
  return;
28
25
  }
29
- return new Promise((resolve) => {
26
+ return new Promise((resolve, reject) => {
30
27
  let bucket = this.buckets.get(colIndex);
31
28
  if (!bucket) {
32
29
  bucket = new Set();
33
30
  this.buckets.set(colIndex, bucket);
34
31
  }
35
- bucket.add(resolve);
32
+ bucket.add({ resolve, reject, moveAction });
36
33
  if (bucket.size + this.finishedCount >= this.total) {
37
- // Use a self-executing async function to handle the moveAction synchronously in this context
38
- (async () => {
39
- let result;
40
- try {
41
- if (moveAction)
42
- result = await moveAction();
43
- }
44
- finally {
45
- this.broadcast(colIndex, result);
46
- }
47
- })();
34
+ this.triggerBucket(colIndex);
48
35
  }
49
36
  });
50
37
  }
51
38
  /**
52
39
  * Mark a row as finished (no more cells to process).
53
- * This ensures the barrier doesn't wait for rows that have exited the loop.
40
+ * This ensures the barrier doesn't wait for rows that have exited the loop,
41
+ * and runs the pending move action so waiting peers are not starved.
54
42
  */
55
43
  markFinished() {
56
44
  this.finishedCount++;
57
- // Check all buckets - any bucket that was waiting for this row might now be ready
58
45
  for (const colIndex of Array.from(this.buckets.keys())) {
59
46
  const bucket = this.buckets.get(colIndex);
60
47
  if (bucket.size + this.finishedCount >= this.total) {
61
- this.broadcast(colIndex);
48
+ this.triggerBucket(colIndex);
62
49
  }
63
50
  }
64
51
  }
65
- broadcast(colIndex, result) {
52
+ /**
53
+ * Run a recovery action exclusively — serialized so concurrent rows don't race
54
+ * on scrollTop after a horizontal scroll evicts rows from the vertical viewport.
55
+ * The fn receives a fresh `targetReached` check; if the previous row's scroll
56
+ * already brought this row into view, fn can no-op immediately.
57
+ */
58
+ async runExclusiveRecovery(fn) {
59
+ if (!this.recoveryMutex)
60
+ this.recoveryMutex = new mutex_1.Mutex();
61
+ return this.recoveryMutex.run(fn);
62
+ }
63
+ /**
64
+ * Detach the bucket for `colIndex`, run the first available moveAction,
65
+ * then resolve or reject all captured waiters.
66
+ *
67
+ * Detaching before the await prevents a concurrent markFinished() from
68
+ * broadcasting the same bucket a second time while the action is in-flight.
69
+ */
70
+ triggerBucket(colIndex) {
66
71
  const bucket = this.buckets.get(colIndex);
67
- if (bucket) {
68
- for (const resolve of bucket) {
69
- resolve(result);
72
+ if (!bucket)
73
+ return;
74
+ const waiters = Array.from(bucket);
75
+ this.buckets.delete(colIndex);
76
+ (async () => {
77
+ var _a;
78
+ let result;
79
+ let err;
80
+ try {
81
+ // Find the first waiter that supplied a moveAction — the last arrival may be
82
+ // a no-op (fast-path) row with no action, but an earlier waiter's action must
83
+ // still run, otherwise the column scroll is silently skipped.
84
+ const action = (_a = waiters.find(w => w.moveAction)) === null || _a === void 0 ? void 0 : _a.moveAction;
85
+ if (action)
86
+ result = await action();
70
87
  }
71
- this.buckets.delete(colIndex);
72
- }
88
+ catch (e) {
89
+ err = e;
90
+ }
91
+ finally {
92
+ if (err !== undefined) {
93
+ for (const { reject } of waiters)
94
+ reject(err);
95
+ }
96
+ else {
97
+ for (const { resolve } of waiters)
98
+ resolve(result);
99
+ }
100
+ }
101
+ })();
73
102
  }
74
103
  }
75
104
  exports.NavigationBarrier = NavigationBarrier;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rickcedwhat/playwright-smart-table",
3
- "version": "6.16.0",
3
+ "version": "6.17.1",
4
4
  "description": "Smart, column-aware table interactions for Playwright",
5
5
  "author": "Cedrick Catalan",
6
6
  "license": "MIT",