lakesync 0.1.8 → 0.2.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.
Files changed (60) hide show
  1. package/dist/adapter-types-DwsQGQS4.d.ts +94 -0
  2. package/dist/adapter.d.ts +23 -49
  3. package/dist/adapter.js +9 -4
  4. package/dist/analyst.js +1 -1
  5. package/dist/{base-poller-Bj9kX9dv.d.ts → base-poller-Y7ORYgUv.d.ts} +2 -0
  6. package/dist/catalogue.js +2 -2
  7. package/dist/{chunk-LDFFCG2K.js → chunk-4SG66H5K.js} +44 -31
  8. package/dist/chunk-4SG66H5K.js.map +1 -0
  9. package/dist/{chunk-LPWXOYNS.js → chunk-C4KD6YKP.js} +59 -43
  10. package/dist/chunk-C4KD6YKP.js.map +1 -0
  11. package/dist/{chunk-JI4C4R5H.js → chunk-FIIHPQMQ.js} +196 -118
  12. package/dist/chunk-FIIHPQMQ.js.map +1 -0
  13. package/dist/{chunk-TMLG32QV.js → chunk-U2NV4DUX.js} +2 -2
  14. package/dist/{chunk-QNITY4F6.js → chunk-XVP5DJJ7.js} +16 -13
  15. package/dist/{chunk-QNITY4F6.js.map → chunk-XVP5DJJ7.js.map} +1 -1
  16. package/dist/{chunk-KVSWLIJR.js → chunk-YHYBLU6W.js} +2 -2
  17. package/dist/{chunk-PYRS74YP.js → chunk-ZNY4DSFU.js} +16 -13
  18. package/dist/{chunk-PYRS74YP.js.map → chunk-ZNY4DSFU.js.map} +1 -1
  19. package/dist/{chunk-SSICS5KI.js → chunk-ZU7RC7CT.js} +2 -2
  20. package/dist/client.d.ts +28 -10
  21. package/dist/client.js +150 -29
  22. package/dist/client.js.map +1 -1
  23. package/dist/compactor.d.ts +1 -1
  24. package/dist/compactor.js +3 -3
  25. package/dist/connector-jira.d.ts +13 -3
  26. package/dist/connector-jira.js +6 -2
  27. package/dist/connector-salesforce.d.ts +13 -3
  28. package/dist/connector-salesforce.js +6 -2
  29. package/dist/{coordinator-NXy6tA0h.d.ts → coordinator-eGmZMnJ_.d.ts} +99 -16
  30. package/dist/create-poller-Cc2MGfhh.d.ts +55 -0
  31. package/dist/factory-DFfR-030.d.ts +33 -0
  32. package/dist/gateway-server.d.ts +398 -95
  33. package/dist/gateway-server.js +743 -56
  34. package/dist/gateway-server.js.map +1 -1
  35. package/dist/gateway.d.ts +14 -8
  36. package/dist/gateway.js +6 -5
  37. package/dist/index.d.ts +45 -73
  38. package/dist/index.js +5 -3
  39. package/dist/parquet.js +2 -2
  40. package/dist/proto.js +2 -2
  41. package/dist/react.d.ts +3 -3
  42. package/dist/{registry-BcspAtZI.d.ts → registry-Dd8JuW8T.d.ts} +1 -1
  43. package/dist/{request-handler-pUvL7ozF.d.ts → request-handler-B1I5xDOx.d.ts} +71 -27
  44. package/dist/{src-ROW4XLO7.js → src-WU7IBVC4.js} +6 -4
  45. package/dist/{types-BrcD1oJg.d.ts → types-D2C9jTbL.d.ts} +33 -23
  46. package/package.json +1 -1
  47. package/dist/auth-CAVutXzx.d.ts +0 -30
  48. package/dist/chunk-JI4C4R5H.js.map +0 -1
  49. package/dist/chunk-LDFFCG2K.js.map +0 -1
  50. package/dist/chunk-LPWXOYNS.js.map +0 -1
  51. package/dist/db-types-CfLMUBfW.d.ts +0 -29
  52. package/dist/src-B6NLV3FP.js +0 -27
  53. package/dist/src-ROW4XLO7.js.map +0 -1
  54. package/dist/src-ZRHKG42A.js +0 -25
  55. package/dist/src-ZRHKG42A.js.map +0 -1
  56. package/dist/types-DSC_EiwR.d.ts +0 -45
  57. /package/dist/{chunk-TMLG32QV.js.map → chunk-U2NV4DUX.js.map} +0 -0
  58. /package/dist/{chunk-KVSWLIJR.js.map → chunk-YHYBLU6W.js.map} +0 -0
  59. /package/dist/{chunk-SSICS5KI.js.map → chunk-ZU7RC7CT.js.map} +0 -0
  60. /package/dist/{src-B6NLV3FP.js.map → src-WU7IBVC4.js.map} +0 -0
package/dist/client.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  decodeSyncResponse,
7
7
  encodeSyncPull,
8
8
  encodeSyncPush
9
- } from "./chunk-KVSWLIJR.js";
9
+ } from "./chunk-YHYBLU6W.js";
10
10
  import {
11
11
  Err,
12
12
  HLC,
@@ -22,7 +22,7 @@ import {
22
22
  quoteIdentifier,
23
23
  toError,
24
24
  unwrapOrThrow
25
- } from "./chunk-LDFFCG2K.js";
25
+ } from "./chunk-4SG66H5K.js";
26
26
  import "./chunk-DGUM43GV.js";
27
27
 
28
28
  // ../client/src/db/local-db.ts
@@ -654,7 +654,7 @@ var ActionProcessor = class {
654
654
  */
655
655
  async enqueue(params) {
656
656
  const hlc = this.hlc.now();
657
- const { generateActionId } = await import("./src-ROW4XLO7.js");
657
+ const { generateActionId } = await import("./src-WU7IBVC4.js");
658
658
  const actionId = await generateActionId({
659
659
  clientId: this.clientId,
660
660
  hlc,
@@ -1001,6 +1001,36 @@ var AutoSyncScheduler = class {
1001
1001
  }
1002
1002
  };
1003
1003
 
1004
+ // ../client/src/sync/strategy.ts
1005
+ var PullFirstStrategy = class {
1006
+ async execute(ctx) {
1007
+ if (ctx.syncMode !== "pushOnly") {
1008
+ if (ctx.isFirstSync) {
1009
+ await ctx.initialSync();
1010
+ }
1011
+ await ctx.pull();
1012
+ }
1013
+ if (ctx.syncMode !== "pullOnly") {
1014
+ await ctx.push();
1015
+ }
1016
+ await ctx.processActions();
1017
+ }
1018
+ };
1019
+ var PushFirstStrategy = class {
1020
+ async execute(ctx) {
1021
+ if (ctx.syncMode !== "pullOnly") {
1022
+ await ctx.push();
1023
+ }
1024
+ if (ctx.syncMode !== "pushOnly") {
1025
+ if (ctx.isFirstSync) {
1026
+ await ctx.initialSync();
1027
+ }
1028
+ await ctx.pull();
1029
+ }
1030
+ await ctx.processActions();
1031
+ }
1032
+ };
1033
+
1004
1034
  // ../client/src/sync/tracker.ts
1005
1035
  function rowWithoutId(row) {
1006
1036
  const result = {};
@@ -1204,6 +1234,10 @@ var SyncCoordinator = class {
1204
1234
  lastSyncedHlc = HLC.encode(0, 0);
1205
1235
  _lastSyncTime = null;
1206
1236
  syncing = false;
1237
+ _online = true;
1238
+ onlineHandler = null;
1239
+ offlineHandler = null;
1240
+ strategy;
1207
1241
  autoSyncScheduler;
1208
1242
  actionProcessor;
1209
1243
  listeners = {
@@ -1221,6 +1255,7 @@ var SyncCoordinator = class {
1221
1255
  this._clientId = config?.clientId ?? `client-${crypto.randomUUID()}`;
1222
1256
  this.maxRetries = config?.maxRetries ?? 10;
1223
1257
  this.syncMode = config?.syncMode ?? "full";
1258
+ this.strategy = config?.strategy ?? new PullFirstStrategy();
1224
1259
  this.tracker = new SyncTracker(db, this.queue, this.hlc, this._clientId);
1225
1260
  const autoSyncIntervalMs = config?.autoSyncIntervalMs ?? AUTO_SYNC_INTERVAL_MS;
1226
1261
  const realtimeHeartbeatMs = config?.realtimeHeartbeatMs ?? REALTIME_HEARTBEAT_MS;
@@ -1252,9 +1287,8 @@ var SyncCoordinator = class {
1252
1287
  }
1253
1288
  /** Remove an event listener */
1254
1289
  off(event, listener) {
1255
- const arr = this.listeners[event];
1256
- const idx = arr.indexOf(listener);
1257
- if (idx !== -1) arr.splice(idx, 1);
1290
+ const listeners = this.listeners;
1291
+ listeners[event] = this.listeners[event].filter((fn) => fn !== listener);
1258
1292
  }
1259
1293
  emit(event, ...args) {
1260
1294
  for (const fn of this.listeners[event]) {
@@ -1272,6 +1306,10 @@ var SyncCoordinator = class {
1272
1306
  lastSyncedHlc: this.lastSyncedHlc
1273
1307
  };
1274
1308
  }
1309
+ /** Whether the client believes it is online. */
1310
+ get isOnline() {
1311
+ return this._online;
1312
+ }
1275
1313
  /** Push pending deltas to the gateway via the transport */
1276
1314
  async pushToGateway() {
1277
1315
  const peekResult = await this.queue.peek(100);
@@ -1374,10 +1412,13 @@ var SyncCoordinator = class {
1374
1412
  /**
1375
1413
  * Start auto-sync: periodic interval + visibility change handler.
1376
1414
  * Synchronises (push + pull) on tab focus and every N seconds.
1415
+ * Registers online/offline listeners to skip sync when offline
1416
+ * and trigger an immediate sync on reconnect.
1377
1417
  */
1378
1418
  startAutoSync() {
1379
1419
  this.transport.connect?.();
1380
1420
  this.autoSyncScheduler.start();
1421
+ this.setupOnlineListeners();
1381
1422
  }
1382
1423
  /**
1383
1424
  * Perform initial sync via checkpoint download.
@@ -1399,22 +1440,25 @@ var SyncCoordinator = class {
1399
1440
  this.lastSyncedHlc = snapshotHlc;
1400
1441
  this._lastSyncTime = /* @__PURE__ */ new Date();
1401
1442
  }
1443
+ /** Build a {@link SyncContext} exposing sync operations for the current cycle. */
1444
+ createSyncContext() {
1445
+ return {
1446
+ isFirstSync: this.lastSyncedHlc === HLC.encode(0, 0),
1447
+ syncMode: this.syncMode,
1448
+ initialSync: () => this.initialSync(),
1449
+ pull: () => this.pullFromGateway(),
1450
+ push: () => this.pushToGateway(),
1451
+ processActions: () => this.processActionQueue()
1452
+ };
1453
+ }
1402
1454
  /** Perform a single sync cycle (push + pull + actions, depending on syncMode). */
1403
1455
  async syncOnce() {
1404
1456
  if (this.syncing) return;
1457
+ if (!this._online) return;
1405
1458
  this.syncing = true;
1406
1459
  this.emit("onSyncStart");
1407
1460
  try {
1408
- if (this.syncMode !== "pushOnly") {
1409
- if (this.lastSyncedHlc === HLC.encode(0, 0)) {
1410
- await this.initialSync();
1411
- }
1412
- await this.pullFromGateway();
1413
- }
1414
- if (this.syncMode !== "pullOnly") {
1415
- await this.pushToGateway();
1416
- }
1417
- await this.processActionQueue();
1461
+ await this.strategy.execute(this.createSyncContext());
1418
1462
  this.emit("onSyncComplete");
1419
1463
  } catch (err) {
1420
1464
  this.emit("onError", err instanceof Error ? err : new Error(String(err)));
@@ -1480,8 +1524,40 @@ var SyncCoordinator = class {
1480
1524
  /** Stop auto-sync and clean up listeners */
1481
1525
  stopAutoSync() {
1482
1526
  this.autoSyncScheduler.stop();
1527
+ this.teardownOnlineListeners();
1483
1528
  this.transport.disconnect?.();
1484
1529
  }
1530
+ /**
1531
+ * Register window online/offline event listeners.
1532
+ * Guards all browser API access with typeof checks for Node/SSR safety.
1533
+ */
1534
+ setupOnlineListeners() {
1535
+ if (typeof window === "undefined") return;
1536
+ if (typeof navigator !== "undefined" && typeof navigator.onLine === "boolean") {
1537
+ this._online = navigator.onLine;
1538
+ }
1539
+ this.onlineHandler = () => {
1540
+ this._online = true;
1541
+ void this.syncOnce();
1542
+ };
1543
+ this.offlineHandler = () => {
1544
+ this._online = false;
1545
+ };
1546
+ window.addEventListener("online", this.onlineHandler);
1547
+ window.addEventListener("offline", this.offlineHandler);
1548
+ }
1549
+ /** Remove online/offline listeners. */
1550
+ teardownOnlineListeners() {
1551
+ if (typeof window === "undefined") return;
1552
+ if (this.onlineHandler) {
1553
+ window.removeEventListener("online", this.onlineHandler);
1554
+ this.onlineHandler = null;
1555
+ }
1556
+ if (this.offlineHandler) {
1557
+ window.removeEventListener("offline", this.offlineHandler);
1558
+ this.offlineHandler = null;
1559
+ }
1560
+ }
1485
1561
  };
1486
1562
 
1487
1563
  // ../client/src/sync/transport-http.ts
@@ -1489,29 +1565,52 @@ var HttpTransport = class {
1489
1565
  baseUrl;
1490
1566
  gatewayId;
1491
1567
  token;
1568
+ getToken;
1492
1569
  _fetch;
1493
1570
  constructor(config) {
1494
1571
  this.baseUrl = config.baseUrl.replace(/\/+$/, "");
1495
1572
  this.gatewayId = config.gatewayId;
1496
1573
  this.token = config.token;
1574
+ this.getToken = config.getToken;
1497
1575
  this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);
1498
1576
  }
1577
+ /** Resolve the current bearer token, preferring getToken callback over static token. */
1578
+ async resolveToken() {
1579
+ if (this.getToken) {
1580
+ return this.getToken();
1581
+ }
1582
+ return this.token;
1583
+ }
1499
1584
  /**
1500
1585
  * Push local deltas to the remote gateway.
1501
1586
  *
1502
1587
  * Sends a POST request with the push payload as BigInt-safe JSON.
1588
+ * On 401, if `getToken` is configured, refreshes the token and retries once.
1503
1589
  */
1504
1590
  async push(msg) {
1505
1591
  const url = `${this.baseUrl}/sync/${this.gatewayId}/push`;
1592
+ const body = JSON.stringify(msg, bigintReplacer);
1506
1593
  try {
1507
- const response = await this._fetch(url, {
1594
+ let token = await this.resolveToken();
1595
+ let response = await this._fetch(url, {
1508
1596
  method: "POST",
1509
1597
  headers: {
1510
1598
  "Content-Type": "application/json",
1511
- Authorization: `Bearer ${this.token}`
1599
+ Authorization: `Bearer ${token}`
1512
1600
  },
1513
- body: JSON.stringify(msg, bigintReplacer)
1601
+ body
1514
1602
  });
1603
+ if (response.status === 401 && this.getToken) {
1604
+ token = await this.getToken();
1605
+ response = await this._fetch(url, {
1606
+ method: "POST",
1607
+ headers: {
1608
+ "Content-Type": "application/json",
1609
+ Authorization: `Bearer ${token}`
1610
+ },
1611
+ body
1612
+ });
1613
+ }
1515
1614
  if (!response.ok) {
1516
1615
  const text = await response.text().catch(() => "Unknown error");
1517
1616
  return Err(new LakeSyncError(`Push failed (${response.status}): ${text}`, "TRANSPORT_ERROR"));
@@ -1528,6 +1627,7 @@ var HttpTransport = class {
1528
1627
  * Pull remote deltas from the gateway.
1529
1628
  *
1530
1629
  * Sends a GET request with query parameters for the pull cursor.
1630
+ * On 401, if `getToken` is configured, refreshes the token and retries once.
1531
1631
  */
1532
1632
  async pull(msg) {
1533
1633
  const params = new URLSearchParams({
@@ -1540,12 +1640,22 @@ var HttpTransport = class {
1540
1640
  }
1541
1641
  const url = `${this.baseUrl}/sync/${this.gatewayId}/pull?${params}`;
1542
1642
  try {
1543
- const response = await this._fetch(url, {
1643
+ let token = await this.resolveToken();
1644
+ let response = await this._fetch(url, {
1544
1645
  method: "GET",
1545
1646
  headers: {
1546
- Authorization: `Bearer ${this.token}`
1647
+ Authorization: `Bearer ${token}`
1547
1648
  }
1548
1649
  });
1650
+ if (response.status === 401 && this.getToken) {
1651
+ token = await this.getToken();
1652
+ response = await this._fetch(url, {
1653
+ method: "GET",
1654
+ headers: {
1655
+ Authorization: `Bearer ${token}`
1656
+ }
1657
+ });
1658
+ }
1549
1659
  if (!response.ok) {
1550
1660
  const text = await response.text().catch(() => "Unknown error");
1551
1661
  return Err(new LakeSyncError(`Pull failed (${response.status}): ${text}`, "TRANSPORT_ERROR"));
@@ -1565,14 +1675,16 @@ var HttpTransport = class {
1565
1675
  */
1566
1676
  async executeAction(msg) {
1567
1677
  const url = `${this.baseUrl}/sync/${this.gatewayId}/action`;
1678
+ const body = JSON.stringify(msg, bigintReplacer);
1568
1679
  try {
1680
+ const token = await this.resolveToken();
1569
1681
  const response = await this._fetch(url, {
1570
1682
  method: "POST",
1571
1683
  headers: {
1572
1684
  "Content-Type": "application/json",
1573
- Authorization: `Bearer ${this.token}`
1685
+ Authorization: `Bearer ${token}`
1574
1686
  },
1575
- body: JSON.stringify(msg, bigintReplacer)
1687
+ body
1576
1688
  });
1577
1689
  if (!response.ok) {
1578
1690
  const text = await response.text().catch(() => "Unknown error");
@@ -1594,10 +1706,11 @@ var HttpTransport = class {
1594
1706
  async describeActions() {
1595
1707
  const url = `${this.baseUrl}/sync/${this.gatewayId}/actions`;
1596
1708
  try {
1709
+ const token = await this.resolveToken();
1597
1710
  const response = await this._fetch(url, {
1598
1711
  method: "GET",
1599
1712
  headers: {
1600
- Authorization: `Bearer ${this.token}`
1713
+ Authorization: `Bearer ${token}`
1601
1714
  }
1602
1715
  });
1603
1716
  if (!response.ok) {
@@ -1657,10 +1770,11 @@ var HttpTransport = class {
1657
1770
  async checkpoint() {
1658
1771
  const url = `${this.baseUrl}/sync/${this.gatewayId}/checkpoint`;
1659
1772
  try {
1773
+ const token = await this.resolveToken();
1660
1774
  const response = await this._fetch(url, {
1661
1775
  method: "GET",
1662
1776
  headers: {
1663
- Authorization: `Bearer ${this.token}`,
1777
+ Authorization: `Bearer ${token}`,
1664
1778
  Accept: "application/x-lakesync-checkpoint-stream"
1665
1779
  }
1666
1780
  });
@@ -2046,6 +2160,10 @@ var LocalTransport = class {
2046
2160
  }
2047
2161
  return Ok(this.gateway.describeActions());
2048
2162
  }
2163
+ /** List available connector types — not supported by local transport. */
2164
+ async listConnectorTypes() {
2165
+ return Ok([]);
2166
+ }
2049
2167
  };
2050
2168
 
2051
2169
  // ../client/src/sync/transport-ws.ts
@@ -2090,7 +2208,7 @@ var WebSocketTransport = class {
2090
2208
  connect() {
2091
2209
  if (this.ws) return;
2092
2210
  this.intentionalClose = false;
2093
- this.openWebSocket();
2211
+ void this.openWebSocket();
2094
2212
  }
2095
2213
  /** Close the WebSocket connection and stop reconnecting. */
2096
2214
  disconnect() {
@@ -2161,8 +2279,9 @@ var WebSocketTransport = class {
2161
2279
  // -----------------------------------------------------------------------
2162
2280
  // Internal
2163
2281
  // -----------------------------------------------------------------------
2164
- openWebSocket() {
2165
- const url = `${this.config.url}?token=${encodeURIComponent(this.config.token)}`;
2282
+ async openWebSocket() {
2283
+ const token = this.config.getToken ? await this.config.getToken() : this.config.token;
2284
+ const url = `${this.config.url}?token=${encodeURIComponent(token)}`;
2166
2285
  this.ws = new WebSocket(url);
2167
2286
  this.ws.binaryType = "arraybuffer";
2168
2287
  this.ws.onopen = () => {
@@ -2219,7 +2338,7 @@ var WebSocketTransport = class {
2219
2338
  this.reconnectAttempts++;
2220
2339
  this.reconnectTimer = setTimeout(() => {
2221
2340
  this.reconnectTimer = null;
2222
- this.openWebSocket();
2341
+ void this.openWebSocket();
2223
2342
  }, delay);
2224
2343
  }
2225
2344
  sendAndAwaitResponse(frame) {
@@ -2261,6 +2380,8 @@ export {
2261
2380
  MemoryActionQueue,
2262
2381
  MemoryOutbox,
2263
2382
  MemoryQueue,
2383
+ PullFirstStrategy,
2384
+ PushFirstStrategy,
2264
2385
  SchemaSynchroniser,
2265
2386
  SyncCoordinator,
2266
2387
  SyncTracker,