@powerhousedao/reactor-local 1.20.0 → 1.20.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## 1.20.2 (2025-02-19)
2
+
3
+ ### 🚀 Features
4
+
5
+ - **common:** improved drive story ([ec96a6b7](https://github.com/powerhouse-inc/powerhouse/commit/ec96a6b7))
6
+ - **common:** document drive generic layout ([e7518094](https://github.com/powerhouse-inc/powerhouse/commit/e7518094))
7
+ - **common:** initial commit ([f561b8c9](https://github.com/powerhouse-inc/powerhouse/commit/f561b8c9))
8
+
9
+ ### 🧱 Updated Dependencies
10
+
11
+ - Updated document-model-libs to 1.133.0
12
+ - Updated document-drive to 1.19.1
13
+ - Updated @powerhousedao/reactor-api to 1.21.2
14
+ - Updated @powerhousedao/scalars to 1.24.0
15
+
16
+ ### ❤️ Thank You
17
+
18
+ - acaldas @acaldas
19
+
1
20
  ## 1.1.0 (2024-10-29)
2
21
 
3
22
  ### 🚀 Features
@@ -1318,8 +1318,6 @@ var {
1318
1318
  var DefaultListenerManagerOptions = {
1319
1319
  sequentialUpdates: true
1320
1320
  };
1321
-
1322
- // ../document-drive/src/server/listener/manager.ts
1323
1321
  function debounce(func, delay = 250) {
1324
1322
  let timer;
1325
1323
  return (immediate, ...args) => {
@@ -1339,16 +1337,24 @@ function debounce(func, delay = 250) {
1339
1337
  }
1340
1338
  var ListenerManager = class _ListenerManager {
1341
1339
  static LISTENER_UPDATE_DELAY = 250;
1340
+ debugID = `[LM #${Math.floor(Math.random() * 999)}]`;
1342
1341
  driveServer;
1342
+ options;
1343
1343
  // driveId -> listenerId -> listenerState
1344
1344
  listenerStateByDriveId = /* @__PURE__ */ new Map();
1345
- options;
1346
1345
  constructor(drive, listenerState = /* @__PURE__ */ new Map(), options = DefaultListenerManagerOptions) {
1346
+ this.debugLog(`constructor(...)`);
1347
1347
  this.driveServer = drive;
1348
1348
  this.listenerStateByDriveId = listenerState;
1349
1349
  this.options = { ...DefaultListenerManagerOptions, ...options };
1350
1350
  }
1351
+ debugLog(...data) {
1352
+ {
1353
+ return;
1354
+ }
1355
+ }
1351
1356
  async initialize(handler) {
1357
+ this.debugLog("initialize(...)");
1352
1358
  if (typeof window !== "undefined") {
1353
1359
  window.addEventListener("online", () => {
1354
1360
  this.triggerUpdate(false, { type: "local" }, handler).catch((error) => {
@@ -1361,6 +1367,9 @@ var ListenerManager = class _ListenerManager {
1361
1367
  return this.listenerStateByDriveId.has(driveId);
1362
1368
  }
1363
1369
  async setListener(driveId, listener) {
1370
+ this.debugLog(
1371
+ `setListener(drive: ${driveId}, listener: ${listener.listenerId})`
1372
+ );
1364
1373
  if (driveId !== listener.driveId) {
1365
1374
  throw new Error("Drive ID mismatch");
1366
1375
  }
@@ -1382,6 +1391,7 @@ var ListenerManager = class _ListenerManager {
1382
1391
  this.triggerUpdate(true, { type: "local" });
1383
1392
  }
1384
1393
  async removeListener(driveId, listenerId) {
1394
+ this.debugLog("setListener()");
1385
1395
  const driveMap = this.listenerStateByDriveId.get(driveId);
1386
1396
  if (!driveMap) {
1387
1397
  return false;
@@ -1456,23 +1466,36 @@ var ListenerManager = class _ListenerManager {
1456
1466
  this._triggerUpdate.bind(this),
1457
1467
  _ListenerManager.LISTENER_UPDATE_DELAY
1458
1468
  );
1459
- async _triggerUpdate(source, onError) {
1469
+ async _triggerUpdate(source, onError, maxContinues = 500) {
1470
+ this.debugLog(
1471
+ `_triggerUpdate(source: ${source.type}, maxContinues: ${maxContinues})`,
1472
+ this.listenerStateByDriveId
1473
+ );
1474
+ if (maxContinues < 0) {
1475
+ throw new Error("Maximum retries exhausted.");
1476
+ }
1460
1477
  const listenerUpdates = [];
1461
1478
  for (const [driveId, drive] of this.listenerStateByDriveId) {
1462
- for (const [_, listenerState] of drive) {
1479
+ for (const [listenerId, listenerState] of drive) {
1463
1480
  const transmitter = listenerState.listener.transmitter;
1464
1481
  if (!transmitter?.transmit) {
1482
+ this.debugLog(`Transmitter not set on listener: ${listenerId}`);
1465
1483
  continue;
1466
1484
  }
1467
- const syncUnits = await this.getListenerSyncUnits(
1468
- driveId,
1469
- listenerState.listener.listenerId
1470
- );
1485
+ const syncUnits = await this.getListenerSyncUnits(driveId, listenerId);
1471
1486
  const strandUpdates = [];
1487
+ this.debugLog("syncUnits", syncUnits);
1472
1488
  const tasks = syncUnits.map((syncUnit) => async () => {
1473
1489
  const unitState = listenerState.syncUnits.get(syncUnit.syncId);
1474
1490
  if (unitState && unitState.listenerRev >= syncUnit.revision) {
1491
+ this.debugLog(
1492
+ `Abandoning push for sync unit ${syncUnit.syncId}: already up-to-date (${unitState.listenerRev} >= ${syncUnit.revision})`
1493
+ );
1475
1494
  return;
1495
+ } else {
1496
+ this.debugLog(
1497
+ `Listener out-of-date for sync unit ${syncUnit.syncId}: ${unitState?.listenerRev} < ${syncUnit.revision}`
1498
+ );
1476
1499
  }
1477
1500
  const opData = [];
1478
1501
  try {
@@ -1489,6 +1512,9 @@ var ListenerManager = class _ListenerManager {
1489
1512
  logger.error(e);
1490
1513
  }
1491
1514
  if (!opData.length) {
1515
+ this.debugLog(
1516
+ `Abandoning push for ${syncUnit.syncId}: no operations found`
1517
+ );
1492
1518
  return;
1493
1519
  }
1494
1520
  strandUpdates.push({
@@ -1500,13 +1526,20 @@ var ListenerManager = class _ListenerManager {
1500
1526
  });
1501
1527
  });
1502
1528
  if (this.options.sequentialUpdates) {
1529
+ this.debugLog(
1530
+ `Collecting ${tasks.length} syncUnit strandUpdates in sequence`
1531
+ );
1503
1532
  for (const task of tasks) {
1504
1533
  await task();
1505
1534
  }
1506
1535
  } else {
1536
+ this.debugLog(
1537
+ `Collecting ${tasks.length} syncUnit strandUpdates in parallel`
1538
+ );
1507
1539
  await Promise.all(tasks.map((task) => task()));
1508
1540
  }
1509
1541
  if (strandUpdates.length == 0) {
1542
+ this.debugLog(`No strandUpdates needed for listener ${listenerId}`);
1510
1543
  continue;
1511
1544
  }
1512
1545
  listenerState.pendingTimeout = new Date(
@@ -1514,13 +1547,21 @@ var ListenerManager = class _ListenerManager {
1514
1547
  ).toISOString();
1515
1548
  listenerState.listenerStatus = "PENDING";
1516
1549
  try {
1550
+ this.debugLog(
1551
+ `_triggerUpdate(source: ${source.type}) > transmitter.transmit`
1552
+ );
1517
1553
  const listenerRevisions = await transmitter.transmit(
1518
1554
  strandUpdates,
1519
1555
  source
1520
1556
  );
1557
+ this.debugLog(
1558
+ `_triggerUpdate(source: ${source.type}) > transmission succeeded`,
1559
+ listenerRevisions
1560
+ );
1521
1561
  listenerState.pendingTimeout = "0";
1522
1562
  listenerState.listenerStatus = "PENDING";
1523
1563
  const lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
1564
+ let continuationNeeded = false;
1524
1565
  for (const revision of listenerRevisions) {
1525
1566
  const syncUnit = syncUnits.find(
1526
1567
  (unit) => revision.documentId === unit.documentId && revision.scope === unit.scope && revision.branch === unit.branch
@@ -1530,6 +1571,28 @@ var ListenerManager = class _ListenerManager {
1530
1571
  lastUpdated,
1531
1572
  listenerRev: revision.revision
1532
1573
  });
1574
+ const su = strandUpdates.find(
1575
+ (su2) => su2.driveId === revision.driveId && su2.documentId === revision.documentId && su2.scope === revision.scope && su2.branch === revision.branch
1576
+ );
1577
+ if (su && su.operations.length > 0) {
1578
+ const suIndex = su.operations.at(
1579
+ su.operations.length - 1
1580
+ )?.index;
1581
+ if (suIndex !== revision.revision) {
1582
+ this.debugLog(
1583
+ `Revision still out-of-date for ${su.documentId}:${su.scope}:${su.branch} ${suIndex} <> ${revision.revision}`
1584
+ );
1585
+ continuationNeeded = true;
1586
+ } else {
1587
+ this.debugLog(
1588
+ `Revision match for ${su.documentId}:${su.scope}:${su.branch} ${suIndex}`
1589
+ );
1590
+ }
1591
+ } else {
1592
+ this.debugLog(
1593
+ `Cannot find strand update for (${revision.documentId}:${revision.scope}:${revision.branch} in drive ${revision.driveId})`
1594
+ );
1595
+ }
1533
1596
  } else {
1534
1597
  logger.warn(
1535
1598
  `Received revision for untracked unit for listener ${listenerState.listener.listenerId}`,
@@ -1540,23 +1603,29 @@ var ListenerManager = class _ListenerManager {
1540
1603
  for (const revision of listenerRevisions) {
1541
1604
  const error = revision.status === "ERROR";
1542
1605
  if (revision.error?.includes("Missing operations")) {
1543
- const updates = await this._triggerUpdate(source, onError);
1544
- listenerUpdates.push(...updates);
1545
- } else {
1546
- listenerUpdates.push({
1547
- listenerId: listenerState.listener.listenerId,
1548
- listenerRevisions
1549
- });
1550
- if (error) {
1551
- throw new OperationError(
1552
- revision.status,
1553
- void 0,
1554
- revision.error,
1555
- revision.error
1556
- );
1557
- }
1606
+ continuationNeeded = true;
1607
+ } else if (error) {
1608
+ throw new OperationError(
1609
+ revision.status,
1610
+ void 0,
1611
+ revision.error,
1612
+ revision.error
1613
+ );
1558
1614
  }
1559
1615
  }
1616
+ if (!continuationNeeded) {
1617
+ listenerUpdates.push({
1618
+ listenerId: listenerState.listener.listenerId,
1619
+ listenerRevisions
1620
+ });
1621
+ } else {
1622
+ const updates = await this._triggerUpdate(
1623
+ source,
1624
+ onError,
1625
+ maxContinues - 1
1626
+ );
1627
+ listenerUpdates.push(...updates);
1628
+ }
1560
1629
  listenerState.listenerStatus = "SUCCESS";
1561
1630
  } catch (e) {
1562
1631
  onError?.(e, driveId, listenerState);
@@ -1564,6 +1633,10 @@ var ListenerManager = class _ListenerManager {
1564
1633
  }
1565
1634
  }
1566
1635
  }
1636
+ this.debugLog(
1637
+ `Returning listener updates (maxContinues: ${maxContinues})`,
1638
+ listenerUpdates
1639
+ );
1567
1640
  return listenerUpdates;
1568
1641
  }
1569
1642
  _checkFilter(filter, syncUnit) {
@@ -1782,16 +1855,24 @@ var InternalTransmitter = class {
1782
1855
  return this.listener;
1783
1856
  }
1784
1857
  };
1785
-
1786
- // ../document-drive/src/server/listener/transmitter/pull-responder.ts
1787
1858
  var PullResponderTransmitter = class _PullResponderTransmitter {
1859
+ debugID = `[PRT #${Math.floor(Math.random() * 999)}]`;
1788
1860
  listener;
1789
1861
  manager;
1790
1862
  constructor(listener, manager) {
1791
1863
  this.listener = listener;
1792
1864
  this.manager = manager;
1865
+ this.debugLog(`constructor(listener: ${listener.listenerId})`);
1866
+ }
1867
+ debugLog(...data) {
1868
+ {
1869
+ return;
1870
+ }
1793
1871
  }
1794
1872
  getStrands(options) {
1873
+ this.debugLog(
1874
+ `getStrands(drive: ${this.listener.driveId}, listener: ${this.listener.listenerId})`
1875
+ );
1795
1876
  return this.manager.getStrands(
1796
1877
  this.listener.driveId,
1797
1878
  this.listener.listenerId,
@@ -1802,6 +1883,10 @@ var PullResponderTransmitter = class _PullResponderTransmitter {
1802
1883
  return Promise.resolve();
1803
1884
  }
1804
1885
  async processAcknowledge(driveId, listenerId, revisions) {
1886
+ this.debugLog(
1887
+ `processAcknowledge(drive: ${driveId}, listener: ${listenerId})`,
1888
+ revisions
1889
+ );
1805
1890
  const syncUnits = await this.manager.getListenerSyncUnitIds(
1806
1891
  driveId,
1807
1892
  listenerId
@@ -2046,13 +2131,21 @@ var PullResponderTransmitter = class _PullResponderTransmitter {
2046
2131
  return trigger.type === "PullResponder";
2047
2132
  }
2048
2133
  };
2134
+ var SYNC_OPS_BATCH_LIMIT = 10;
2049
2135
  var SwitchboardPushTransmitter = class {
2050
2136
  targetURL;
2137
+ debugID = `[SPT #${Math.floor(Math.random() * 999)}]`;
2051
2138
  constructor(targetURL) {
2052
2139
  this.targetURL = targetURL;
2053
2140
  }
2141
+ debugLog(...data) {
2142
+ {
2143
+ return false;
2144
+ }
2145
+ }
2054
2146
  async transmit(strands, source) {
2055
2147
  if (source.type === "trigger" && source.trigger.data?.url === this.targetURL) {
2148
+ this.debugLog(`Cutting trigger loop from ${this.targetURL}.`);
2056
2149
  return strands.map((strand) => ({
2057
2150
  driveId: strand.driveId,
2058
2151
  documentId: strand.documentId,
@@ -2062,6 +2155,29 @@ var SwitchboardPushTransmitter = class {
2062
2155
  revision: strand.operations.at(-1)?.index ?? -1
2063
2156
  }));
2064
2157
  }
2158
+ const culledStrands = [];
2159
+ let opsCounter = 0;
2160
+ for (let s = 0; opsCounter <= SYNC_OPS_BATCH_LIMIT && s < strands.length; s++) {
2161
+ const currentStrand = strands.at(s);
2162
+ if (!currentStrand) {
2163
+ break;
2164
+ }
2165
+ const newOps = Math.min(
2166
+ SYNC_OPS_BATCH_LIMIT - opsCounter,
2167
+ currentStrand.operations.length
2168
+ );
2169
+ culledStrands.push({
2170
+ ...currentStrand,
2171
+ operations: currentStrand.operations.slice(0, newOps)
2172
+ });
2173
+ opsCounter += newOps;
2174
+ }
2175
+ this.debugLog(
2176
+ ` Total update: [${strands.map((s) => s.operations.length).join(", ")}] operations`
2177
+ );
2178
+ this.debugLog(
2179
+ `Culled update: [${culledStrands.map((s) => s.operations.length).join(", ")}] operations`
2180
+ );
2065
2181
  try {
2066
2182
  const { pushUpdates } = await requestGraphql(
2067
2183
  this.targetURL,
@@ -2079,7 +2195,7 @@ var SwitchboardPushTransmitter = class {
2079
2195
  }
2080
2196
  `,
2081
2197
  {
2082
- strands: strands.map((strand) => ({
2198
+ strands: culledStrands.map((strand) => ({
2083
2199
  ...strand,
2084
2200
  operations: strand.operations.map((op) => ({
2085
2201
  ...op,
@@ -2525,7 +2641,15 @@ var BaseDocumentDriveServer = class {
2525
2641
  for (const zodListener of drive.state.local.listeners) {
2526
2642
  const transmitter = this.transmitterFactory.instance(
2527
2643
  zodListener.callInfo?.transmitterType ?? "",
2528
- zodListener,
2644
+ {
2645
+ driveId,
2646
+ listenerId: zodListener.listenerId,
2647
+ block: zodListener.block,
2648
+ filter: zodListener.filter,
2649
+ system: zodListener.system,
2650
+ label: zodListener.label || undefined,
2651
+ callInfo: zodListener.callInfo || undefined
2652
+ },
2529
2653
  this
2530
2654
  );
2531
2655
  await this.listenerManager.setListener(driveId, {
@@ -3536,7 +3660,15 @@ var BaseDocumentDriveServer = class {
3536
3660
  const zodListener = operation.input.listener;
3537
3661
  const transmitter = this.transmitterFactory.instance(
3538
3662
  zodListener.callInfo?.transmitterType ?? "",
3539
- zodListener,
3663
+ {
3664
+ driveId,
3665
+ listenerId: zodListener.listenerId,
3666
+ block: zodListener.block,
3667
+ filter: zodListener.filter,
3668
+ system: zodListener.system,
3669
+ label: zodListener.label || void 0,
3670
+ callInfo: zodListener.callInfo || void 0
3671
+ },
3540
3672
  this
3541
3673
  );
3542
3674
  const listener = {
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #! /usr/bin/env node
2
- import { startServer } from './chunk-JKUEBNSV.js';
2
+ import { startServer } from './chunk-PUCSQ2II.js';
3
3
  import './chunk-HCTWYIZG.js';
4
4
  import { Command } from 'commander';
5
5
 
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { DefaultStartServerOptions, startServer } from './chunk-JKUEBNSV.js';
1
+ export { DefaultStartServerOptions, startServer } from './chunk-PUCSQ2II.js';
2
2
  import './chunk-HCTWYIZG.js';
package/dist/server.js CHANGED
@@ -1,2 +1,2 @@
1
- export { DefaultStartServerOptions, startServer } from './chunk-JKUEBNSV.js';
1
+ export { DefaultStartServerOptions, startServer } from './chunk-PUCSQ2II.js';
2
2
  import './chunk-HCTWYIZG.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/reactor-local",
3
3
  "type": "module",
4
- "version": "1.20.0",
4
+ "version": "1.20.2",
5
5
  "main": "dist/server.js",
6
6
  "bin": {
7
7
  "reactor-local": "dist/cli.js"
@@ -27,10 +27,10 @@
27
27
  "nanoevents": "^9.0.0",
28
28
  "sanitize-filename": "^1.6.3",
29
29
  "uuid": "^11.0.2",
30
- "@powerhousedao/reactor-api": "1.21.0",
31
- "@powerhousedao/scalars": "1.23.0",
30
+ "@powerhousedao/reactor-api": "1.21.2",
31
+ "@powerhousedao/scalars": "1.24.0",
32
32
  "document-model": "2.20.0",
33
- "document-model-libs": "1.132.0"
33
+ "document-model-libs": "1.133.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@powerhousedao/analytics-engine-graphql": "^0.2.2",
@@ -41,7 +41,7 @@
41
41
  "@types/node": "^22.7.5",
42
42
  "tsup": "^8.3.5",
43
43
  "@powerhousedao/config": "1.17.0",
44
- "document-drive": "1.18.0"
44
+ "document-drive": "1.19.1"
45
45
  },
46
46
  "scripts": {
47
47
  "start": "vite-node src/index.ts",