@tiptap/y-tiptap 3.0.2 → 3.0.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/y-tiptap.cjs CHANGED
@@ -1416,24 +1416,115 @@ const matchNodeName = (yElement, pNode) =>
1416
1416
  */
1417
1417
  let viewsToUpdate = null;
1418
1418
 
1419
- const updateMetas = () => {
1420
- const ups = /** @type {Map<EditorView, Map<any, any>>} */ (viewsToUpdate);
1421
- viewsToUpdate = null;
1422
- ups.forEach((metas, view) => {
1423
- const tr = view.state.tr;
1424
- const syncState = ySyncPluginKey.getState(view.state);
1419
+ class MetaEntry {
1420
+ /**
1421
+ * @param {EditorView} view
1422
+ * @param {any} key
1423
+ * @param {any} value
1424
+ */
1425
+ constructor (view, key, value) {
1426
+ this.view = view;
1427
+ this.key = key;
1428
+ this.value = value;
1429
+ }
1430
+
1431
+ apply () {
1432
+ const syncState = ySyncPluginKey.getState(this.view.state);
1425
1433
  if (syncState && syncState.binding && !syncState.binding.isDestroyed) {
1426
- metas.forEach((val, key) => {
1427
- tr.setMeta(key, val);
1434
+ const tr = this.view.state.tr;
1435
+ tr.setMeta(this.key, this.value);
1436
+ this.view.dispatch(tr);
1437
+ }
1438
+ }
1439
+ }
1440
+
1441
+ class MetaEntriesQueue {
1442
+ /**
1443
+ * @param {Array<MetaEntry>} [entries=[]]
1444
+ */
1445
+ constructor (entries = []) {
1446
+ this.entries = entries;
1447
+ }
1448
+
1449
+ /**
1450
+ * @return {MetaEntry|undefined}
1451
+ */
1452
+ getFirst () {
1453
+ return this.entries[0]
1454
+ }
1455
+
1456
+ /**
1457
+ * @return {MetaEntry|undefined}
1458
+ */
1459
+ dequeueFirst () {
1460
+ return this.entries.shift()
1461
+ }
1462
+
1463
+ /**
1464
+ * @return {boolean}
1465
+ */
1466
+ isEmpty () {
1467
+ return this.entries.length === 0
1468
+ }
1469
+
1470
+ static fromViewsToUpdate () {
1471
+ const ups = /** @type {Map<EditorView, Map<any, any>>} */ (viewsToUpdate);
1472
+ viewsToUpdate = null;
1473
+ const entries = [];
1474
+ ups.forEach((metas, view) => {
1475
+ metas.forEach((value, key) => {
1476
+ entries.push(new MetaEntry(view, key, value));
1428
1477
  });
1429
- view.dispatch(tr);
1478
+ });
1479
+ return new MetaEntriesQueue(entries)
1480
+ }
1481
+ }
1482
+
1483
+ /**
1484
+ * Dispatch queued plugin metadata in order, retrying only the remaining
1485
+ * entries if a transaction becomes stale while the async queue is flushing.
1486
+ *
1487
+ * Cursor awareness updates are decoration-only refreshes. If a real document
1488
+ * transaction lands before one of these queued meta transactions is applied,
1489
+ * ProseMirror can reject it with a RangeError. In that case we reschedule the
1490
+ * remaining entries on the next tick. If the first remaining entry fails again
1491
+ * on retry, we drop that entry and continue with the rest of the queue instead
1492
+ * of retrying forever or crashing the editor.
1493
+ *
1494
+ * @param {MetaEntriesQueue} [metaEntries=MetaEntriesQueue.fromViewsToUpdate()]
1495
+ * @param {boolean} [isRetry=false]
1496
+ */
1497
+ const updateMetas = (metaEntries = MetaEntriesQueue.fromViewsToUpdate(), isRetry = false) => {
1498
+ let isFirst = true;
1499
+ while (!metaEntries.isEmpty()) {
1500
+ const metaEntry = metaEntries.getFirst();
1501
+ try {
1502
+ metaEntry.apply();
1503
+ } catch (err) {
1504
+ // ProseMirror throws a RangeError when this transaction was created from
1505
+ // an older state and another transaction changed the document before this
1506
+ // meta-only dispatch was applied ("Applying a mismatched transaction").
1507
+ if (err instanceof RangeError) {
1508
+ if (isRetry && isFirst) {
1509
+ // Drop the repeatedly stale entry so the queue can continue flushing.
1510
+ metaEntries.dequeueFirst();
1511
+ }
1512
+ if (!metaEntries.isEmpty()) {
1513
+ eventloop__namespace.timeout(0, () => updateMetas(metaEntries, true));
1514
+ }
1515
+ return
1516
+ }
1517
+ throw err
1430
1518
  }
1431
- });
1519
+ isFirst = false;
1520
+ metaEntries.dequeueFirst();
1521
+ }
1432
1522
  };
1433
1523
 
1434
1524
  const setMeta = (view, key, value) => {
1435
1525
  if (!viewsToUpdate) {
1436
1526
  viewsToUpdate = new Map();
1527
+ // Awareness listeners can fire in bursts, so batch them into one tick.
1437
1528
  eventloop__namespace.timeout(0, updateMetas);
1438
1529
  }
1439
1530
  map__namespace.setIfUndefined(viewsToUpdate, view, map__namespace.create).set(key, value);