fauxbase 0.5.9 → 0.6.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.
package/dist/index.cjs CHANGED
@@ -1609,6 +1609,593 @@ var HttpDriver = class {
1609
1609
  }
1610
1610
  };
1611
1611
 
1612
+ // src/drivers/sync/sync-queue.ts
1613
+ var STORE_NAME = "queue";
1614
+ function generateId() {
1615
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1616
+ return crypto.randomUUID();
1617
+ }
1618
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1619
+ const r = Math.random() * 16 | 0;
1620
+ const v = c === "x" ? r : r & 3 | 8;
1621
+ return v.toString(16);
1622
+ });
1623
+ }
1624
+ var SyncQueue = class {
1625
+ db = null;
1626
+ cache = [];
1627
+ _ready;
1628
+ constructor(dbName = "fauxbase-sync-queue") {
1629
+ this._ready = this.open(dbName);
1630
+ }
1631
+ get ready() {
1632
+ return this._ready;
1633
+ }
1634
+ open(dbName) {
1635
+ return new Promise((resolve, reject) => {
1636
+ const req = indexedDB.open(dbName, 1);
1637
+ req.onupgradeneeded = () => {
1638
+ const db = req.result;
1639
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
1640
+ const store = db.createObjectStore(STORE_NAME, { keyPath: "id" });
1641
+ store.createIndex("resource_entity", ["resource", "entityId"]);
1642
+ store.createIndex("status", "status");
1643
+ }
1644
+ };
1645
+ req.onsuccess = async () => {
1646
+ this.db = req.result;
1647
+ await this.loadAll();
1648
+ resolve();
1649
+ };
1650
+ req.onerror = () => reject(req.error);
1651
+ });
1652
+ }
1653
+ async loadAll() {
1654
+ const db = this.db;
1655
+ const tx = db.transaction(STORE_NAME, "readonly");
1656
+ const store = tx.objectStore(STORE_NAME);
1657
+ return new Promise((resolve, reject) => {
1658
+ const req = store.getAll();
1659
+ req.onsuccess = () => {
1660
+ this.cache = req.result.sort((a, b) => a.timestamp - b.timestamp);
1661
+ resolve();
1662
+ };
1663
+ req.onerror = () => reject(req.error);
1664
+ });
1665
+ }
1666
+ persist(entry) {
1667
+ if (!this.db) return;
1668
+ const tx = this.db.transaction(STORE_NAME, "readwrite");
1669
+ tx.objectStore(STORE_NAME).put(entry);
1670
+ }
1671
+ removeFromDb(id) {
1672
+ if (!this.db) return;
1673
+ const tx = this.db.transaction(STORE_NAME, "readwrite");
1674
+ tx.objectStore(STORE_NAME).delete(id);
1675
+ }
1676
+ async enqueue(entry) {
1677
+ const existing = this.cache.find(
1678
+ (e) => e.resource === entry.resource && e.entityId === entry.entityId && (e.status === "pending" || e.status === "failed")
1679
+ );
1680
+ if (existing) {
1681
+ if (existing.action === "create" && entry.action === "update") {
1682
+ existing.data = { ...existing.data, ...entry.data };
1683
+ this.persist(existing);
1684
+ return;
1685
+ }
1686
+ if (existing.action === "create" && entry.action === "delete") {
1687
+ this.cache = this.cache.filter((e) => e.id !== existing.id);
1688
+ this.removeFromDb(existing.id);
1689
+ return;
1690
+ }
1691
+ if (existing.action === "update" && entry.action === "update") {
1692
+ existing.data = { ...existing.data, ...entry.data };
1693
+ this.persist(existing);
1694
+ return;
1695
+ }
1696
+ if (existing.action === "update" && entry.action === "delete") {
1697
+ existing.action = "delete";
1698
+ existing.data = null;
1699
+ this.persist(existing);
1700
+ return;
1701
+ }
1702
+ }
1703
+ const full = {
1704
+ ...entry,
1705
+ id: generateId(),
1706
+ status: "pending",
1707
+ retries: 0
1708
+ };
1709
+ this.cache.push(full);
1710
+ this.persist(full);
1711
+ }
1712
+ getPending() {
1713
+ return this.cache.filter((e) => e.status === "pending" || e.status === "failed").sort((a, b) => a.timestamp - b.timestamp);
1714
+ }
1715
+ getPendingCount() {
1716
+ return this.cache.filter((e) => e.status === "pending" || e.status === "failed").length;
1717
+ }
1718
+ async markSyncing(id) {
1719
+ const entry = this.cache.find((e) => e.id === id);
1720
+ if (entry) {
1721
+ entry.status = "syncing";
1722
+ this.persist(entry);
1723
+ }
1724
+ }
1725
+ async remove(id) {
1726
+ this.cache = this.cache.filter((e) => e.id !== id);
1727
+ this.removeFromDb(id);
1728
+ }
1729
+ async markFailed(id, error) {
1730
+ const entry = this.cache.find((e) => e.id === id);
1731
+ if (entry) {
1732
+ entry.status = "failed";
1733
+ entry.retries++;
1734
+ entry.error = error;
1735
+ this.persist(entry);
1736
+ }
1737
+ }
1738
+ getDeadLetters(maxRetries) {
1739
+ return this.cache.filter((e) => e.status === "failed" && e.retries >= maxRetries);
1740
+ }
1741
+ };
1742
+
1743
+ // src/drivers/sync/sync-engine.ts
1744
+ var SyncEngine = class {
1745
+ localDriver;
1746
+ httpDriver;
1747
+ queue;
1748
+ config;
1749
+ eventBus = null;
1750
+ online = true;
1751
+ syncing = false;
1752
+ pullTimer = null;
1753
+ pushTimer = null;
1754
+ lastSynced = null;
1755
+ registeredResources = /* @__PURE__ */ new Map();
1756
+ // resource -> endpoint
1757
+ _syncPromise = null;
1758
+ constructor(localDriver, httpDriver, queue, syncConfig) {
1759
+ this.localDriver = localDriver;
1760
+ this.httpDriver = httpDriver;
1761
+ this.queue = queue;
1762
+ this.config = {
1763
+ interval: syncConfig?.interval ?? 3e4,
1764
+ retryDelay: syncConfig?.retryDelay ?? 5e3,
1765
+ maxRetries: syncConfig?.maxRetries ?? 10,
1766
+ conflictStrategy: syncConfig?.conflictStrategy ?? "last-write-wins",
1767
+ resources: syncConfig?.resources ?? null,
1768
+ pingUrl: syncConfig?.pingUrl ?? "/ping"
1769
+ };
1770
+ }
1771
+ setEventBus(bus) {
1772
+ this.eventBus = bus;
1773
+ }
1774
+ registerResource(resource, endpoint) {
1775
+ this.registeredResources.set(resource, endpoint);
1776
+ }
1777
+ start() {
1778
+ this.setupConnectivityListeners();
1779
+ if (this.config.interval > 0) {
1780
+ this.pullTimer = setInterval(() => {
1781
+ this.sync().catch(() => {
1782
+ });
1783
+ }, this.config.interval);
1784
+ }
1785
+ this.sync().catch(() => {
1786
+ });
1787
+ }
1788
+ stop() {
1789
+ if (this.pullTimer) {
1790
+ clearInterval(this.pullTimer);
1791
+ this.pullTimer = null;
1792
+ }
1793
+ if (this.pushTimer) {
1794
+ clearTimeout(this.pushTimer);
1795
+ this.pushTimer = null;
1796
+ }
1797
+ }
1798
+ async sync() {
1799
+ if (this._syncPromise) return this._syncPromise;
1800
+ this._syncPromise = this._doSync();
1801
+ try {
1802
+ return await this._syncPromise;
1803
+ } finally {
1804
+ this._syncPromise = null;
1805
+ }
1806
+ }
1807
+ async _doSync() {
1808
+ if (this.syncing) return { pushed: 0, pulled: 0 };
1809
+ this.syncing = true;
1810
+ this.emitSyncState();
1811
+ this.emitEvent("sync:start");
1812
+ let pushed = 0;
1813
+ let pulled = 0;
1814
+ try {
1815
+ this.online = await this.checkOnline();
1816
+ if (!this.online) {
1817
+ this.emitEvent("sync:complete", { pushed: 0, pulled: 0 });
1818
+ return { pushed: 0, pulled: 0 };
1819
+ }
1820
+ pushed = await this.push();
1821
+ pulled = await this.pull();
1822
+ this.lastSynced = Date.now();
1823
+ this.emitEvent("sync:complete", { pushed, pulled });
1824
+ } catch (err) {
1825
+ this.emitEvent("sync:error", { error: err.message });
1826
+ } finally {
1827
+ this.syncing = false;
1828
+ this.emitSyncState();
1829
+ }
1830
+ return { pushed, pulled };
1831
+ }
1832
+ // --- Push: replay queued mutations to remote ---
1833
+ async push() {
1834
+ const pending = this.queue.getPending();
1835
+ let pushed = 0;
1836
+ for (const entry of pending) {
1837
+ if (entry.retries >= this.config.maxRetries) continue;
1838
+ if (this.config.resources && !this.config.resources.includes(entry.resource)) continue;
1839
+ await this.queue.markSyncing(entry.id);
1840
+ try {
1841
+ switch (entry.action) {
1842
+ case "create": {
1843
+ const result = await this.httpDriver.create(entry.resource, entry.data);
1844
+ const serverData = result.data;
1845
+ if (serverData && serverData.id) {
1846
+ this.localDriver.getStorageBackend().set(entry.resource, serverData.id, serverData);
1847
+ if (serverData.id !== entry.entityId) {
1848
+ this.localDriver.getStorageBackend().remove(entry.resource, entry.entityId);
1849
+ }
1850
+ }
1851
+ break;
1852
+ }
1853
+ case "update": {
1854
+ const result = await this.httpDriver.update(entry.resource, entry.entityId, entry.data);
1855
+ const serverData = result.data;
1856
+ if (serverData) {
1857
+ this.localDriver.getStorageBackend().set(entry.resource, entry.entityId, serverData);
1858
+ }
1859
+ break;
1860
+ }
1861
+ case "delete": {
1862
+ await this.httpDriver.delete(entry.resource, entry.entityId);
1863
+ break;
1864
+ }
1865
+ }
1866
+ await this.queue.remove(entry.id);
1867
+ pushed++;
1868
+ } catch (err) {
1869
+ if (err.status === 409 || err.code === "CONFLICT") {
1870
+ await this.handleConflict(entry);
1871
+ } else {
1872
+ await this.queue.markFailed(entry.id, err.message ?? "Sync failed");
1873
+ this.emitEvent("sync:error", {
1874
+ resource: entry.resource,
1875
+ action: entry.action,
1876
+ error: err.message
1877
+ });
1878
+ }
1879
+ }
1880
+ }
1881
+ return pushed;
1882
+ }
1883
+ // --- Pull: fetch updated data from remote ---
1884
+ async pull() {
1885
+ let totalPulled = 0;
1886
+ for (const [resource] of this.registeredResources) {
1887
+ if (this.config.resources && !this.config.resources.includes(resource)) continue;
1888
+ try {
1889
+ totalPulled += await this.pullResource(resource);
1890
+ } catch {
1891
+ }
1892
+ }
1893
+ return totalPulled;
1894
+ }
1895
+ async pullResource(resource) {
1896
+ const storage = this.localDriver.getStorageBackend();
1897
+ const lastPullKey = `_sync:lastPull:${resource}`;
1898
+ const lastPull = storage.getMeta(lastPullKey);
1899
+ const filter = {};
1900
+ if (lastPull) {
1901
+ filter.updatedAt__gt = lastPull;
1902
+ }
1903
+ let page = 1;
1904
+ let pulled = 0;
1905
+ let maxUpdatedAt = lastPull ?? "";
1906
+ while (true) {
1907
+ const result = await this.httpDriver.list(resource, {
1908
+ filter: Object.keys(filter).length > 0 ? filter : void 0,
1909
+ size: 200,
1910
+ page,
1911
+ sort: { field: "updatedAt", direction: "asc" }
1912
+ });
1913
+ for (const item of result.items) {
1914
+ const hasPending = this.queue.getPending().some(
1915
+ (e) => e.resource === resource && e.entityId === item.id
1916
+ );
1917
+ if (hasPending && this.config.conflictStrategy !== "server-wins") continue;
1918
+ storage.set(resource, item.id, item);
1919
+ pulled++;
1920
+ if (item.updatedAt && item.updatedAt > maxUpdatedAt) {
1921
+ maxUpdatedAt = item.updatedAt;
1922
+ }
1923
+ }
1924
+ if (result.items.length < 200 || page >= result.meta.totalPages) break;
1925
+ page++;
1926
+ }
1927
+ if (maxUpdatedAt) {
1928
+ storage.setMeta(lastPullKey, maxUpdatedAt);
1929
+ }
1930
+ if (pulled > 0 && this.eventBus) {
1931
+ this.eventBus.emit({
1932
+ action: "updated",
1933
+ resource,
1934
+ timestamp: Date.now(),
1935
+ source: "remote"
1936
+ });
1937
+ }
1938
+ return pulled;
1939
+ }
1940
+ // --- Conflict resolution ---
1941
+ async handleConflict(entry) {
1942
+ const strategy = this.config.conflictStrategy;
1943
+ if (strategy === "server-wins") {
1944
+ await this.queue.remove(entry.id);
1945
+ try {
1946
+ const result = await this.httpDriver.get(entry.resource, entry.entityId);
1947
+ this.localDriver.getStorageBackend().set(entry.resource, entry.entityId, result.data);
1948
+ } catch {
1949
+ }
1950
+ } else if (strategy === "client-wins" || strategy === "last-write-wins") {
1951
+ try {
1952
+ const serverResult = await this.httpDriver.get(entry.resource, entry.entityId);
1953
+ const serverData = serverResult.data;
1954
+ if (entry.action === "update" && entry.data) {
1955
+ const merged = { ...serverData, ...entry.data };
1956
+ await this.httpDriver.update(entry.resource, entry.entityId, merged);
1957
+ this.localDriver.getStorageBackend().set(entry.resource, entry.entityId, merged);
1958
+ }
1959
+ await this.queue.remove(entry.id);
1960
+ } catch {
1961
+ await this.queue.markFailed(entry.id, "Conflict resolution failed");
1962
+ }
1963
+ }
1964
+ this.emitEvent("sync:conflict", {
1965
+ resource: entry.resource,
1966
+ action: entry.action,
1967
+ entityId: entry.entityId
1968
+ });
1969
+ }
1970
+ // --- Connectivity ---
1971
+ setupConnectivityListeners() {
1972
+ if (typeof window === "undefined") return;
1973
+ window.addEventListener("online", () => this.handleOnline());
1974
+ window.addEventListener("offline", () => this.handleOffline());
1975
+ }
1976
+ async checkOnline() {
1977
+ if (typeof navigator !== "undefined" && !navigator.onLine) return false;
1978
+ try {
1979
+ const baseUrl = this.httpDriver.baseUrl;
1980
+ const response = await fetch(`${baseUrl}${this.config.pingUrl}`, {
1981
+ method: "HEAD",
1982
+ cache: "no-store",
1983
+ signal: AbortSignal.timeout(5e3)
1984
+ });
1985
+ return response.ok;
1986
+ } catch {
1987
+ return false;
1988
+ }
1989
+ }
1990
+ handleOnline() {
1991
+ if (this.online) return;
1992
+ this.online = true;
1993
+ this.emitEvent("online");
1994
+ this.emitSyncState();
1995
+ this.sync().catch(() => {
1996
+ });
1997
+ }
1998
+ handleOffline() {
1999
+ if (!this.online) return;
2000
+ this.online = false;
2001
+ this.emitEvent("offline");
2002
+ this.emitSyncState();
2003
+ }
2004
+ // --- State ---
2005
+ getSyncState() {
2006
+ return {
2007
+ isOnline: this.online,
2008
+ isSyncing: this.syncing,
2009
+ pendingCount: this.queue.getPendingCount(),
2010
+ lastSynced: this.lastSynced
2011
+ };
2012
+ }
2013
+ // --- Events ---
2014
+ emitEvent(type, data) {
2015
+ if (!this.eventBus) return;
2016
+ this.eventBus.emit({
2017
+ action: type,
2018
+ resource: "__sync",
2019
+ data: { type, ...data },
2020
+ timestamp: Date.now(),
2021
+ source: "local"
2022
+ });
2023
+ }
2024
+ emitSyncState() {
2025
+ this.emitEvent("state", this.getSyncState());
2026
+ }
2027
+ };
2028
+
2029
+ // src/drivers/sync/sync-driver.ts
2030
+ var SyncDriver = class {
2031
+ localDriver;
2032
+ httpDriver;
2033
+ queue;
2034
+ _syncEngine;
2035
+ _ready;
2036
+ _isReady = false;
2037
+ constructor(config) {
2038
+ this.localDriver = new LocalDriver({
2039
+ type: "local",
2040
+ persist: config.local.persist ?? "indexeddb",
2041
+ dbName: config.local.dbName
2042
+ });
2043
+ this.httpDriver = new HttpDriver({
2044
+ type: "http",
2045
+ baseUrl: config.remote.baseUrl,
2046
+ preset: config.remote.preset,
2047
+ timeout: config.remote.timeout,
2048
+ retry: config.remote.retry,
2049
+ headers: config.remote.headers
2050
+ });
2051
+ this.queue = new SyncQueue(
2052
+ config.local.dbName ? `${config.local.dbName}-sync-queue` : void 0
2053
+ );
2054
+ this._syncEngine = new SyncEngine(
2055
+ this.localDriver,
2056
+ this.httpDriver,
2057
+ this.queue,
2058
+ config.sync
2059
+ );
2060
+ this._ready = Promise.all([this.localDriver.ready, this.queue.ready]).then(() => {
2061
+ this._isReady = true;
2062
+ });
2063
+ }
2064
+ get ready() {
2065
+ return this._ready;
2066
+ }
2067
+ get isReady() {
2068
+ return this._isReady;
2069
+ }
2070
+ get syncEngine() {
2071
+ return this._syncEngine;
2072
+ }
2073
+ /** @internal — exposed for createClient auth wiring */
2074
+ get _httpDriver() {
2075
+ return this.httpDriver;
2076
+ }
2077
+ // --- Reads: delegate to local ---
2078
+ async list(resource, query) {
2079
+ return this.localDriver.list(resource, query);
2080
+ }
2081
+ async get(resource, id) {
2082
+ return this.localDriver.get(resource, id);
2083
+ }
2084
+ async count(resource, filter) {
2085
+ return this.localDriver.count(resource, filter);
2086
+ }
2087
+ // --- Writes: local first, then enqueue ---
2088
+ async create(resource, data) {
2089
+ const result = await this.localDriver.create(resource, data);
2090
+ await this.queue.enqueue({
2091
+ resource,
2092
+ action: "create",
2093
+ entityId: result.data.id,
2094
+ data: result.data,
2095
+ timestamp: Date.now()
2096
+ });
2097
+ return result;
2098
+ }
2099
+ async update(resource, id, data) {
2100
+ const result = await this.localDriver.update(resource, id, data);
2101
+ await this.queue.enqueue({
2102
+ resource,
2103
+ action: "update",
2104
+ entityId: id,
2105
+ data,
2106
+ timestamp: Date.now()
2107
+ });
2108
+ return result;
2109
+ }
2110
+ async delete(resource, id) {
2111
+ const result = await this.localDriver.delete(resource, id);
2112
+ await this.queue.enqueue({
2113
+ resource,
2114
+ action: "delete",
2115
+ entityId: id,
2116
+ data: null,
2117
+ timestamp: Date.now()
2118
+ });
2119
+ return result;
2120
+ }
2121
+ // --- Bulk: delegate to local + enqueue each ---
2122
+ async bulkCreate(resource, data) {
2123
+ const results = [];
2124
+ for (const item of data) {
2125
+ const { data: created } = await this.create(resource, item);
2126
+ results.push(created);
2127
+ }
2128
+ return { data: results };
2129
+ }
2130
+ async bulkUpdate(resource, updates) {
2131
+ const results = [];
2132
+ for (const { id, data } of updates) {
2133
+ const { data: updated } = await this.update(resource, id, data);
2134
+ results.push(updated);
2135
+ }
2136
+ return { data: results };
2137
+ }
2138
+ async bulkDelete(resource, ids) {
2139
+ for (const id of ids) {
2140
+ await this.delete(resource, id);
2141
+ }
2142
+ return { data: { count: ids.length } };
2143
+ }
2144
+ // --- Custom request: local handler offline, HTTP when online ---
2145
+ async request(resource, path, options) {
2146
+ try {
2147
+ return await this.httpDriver.request(resource, path, options);
2148
+ } catch {
2149
+ if (options?.local) {
2150
+ return options.local();
2151
+ }
2152
+ throw new Error(
2153
+ "service.request() failed and no local handler provided. Provide a `local` callback for offline support."
2154
+ );
2155
+ }
2156
+ }
2157
+ // --- Seed/meta: delegate to local ---
2158
+ seed(resource, data, entityClass) {
2159
+ this.localDriver.seed(resource, data, entityClass);
2160
+ }
2161
+ getSeedVersion() {
2162
+ return this.localDriver.getSeedVersion();
2163
+ }
2164
+ setSeedVersion(version) {
2165
+ this.localDriver.setSeedVersion(version);
2166
+ }
2167
+ clear(resource) {
2168
+ this.localDriver.clear(resource);
2169
+ }
2170
+ // --- Wiring methods (called by createClient) ---
2171
+ setAuthProvider(provider) {
2172
+ this.httpDriver.setAuthProvider(provider);
2173
+ this.localDriver.setAuthProvider(() => {
2174
+ const auth = provider();
2175
+ if (!auth) return null;
2176
+ try {
2177
+ const payload = JSON.parse(atob(auth.token));
2178
+ return { userId: payload.userId, userName: payload.email };
2179
+ } catch {
2180
+ return null;
2181
+ }
2182
+ });
2183
+ }
2184
+ setOnUnauthorized(handler) {
2185
+ this.httpDriver.setOnUnauthorized(handler);
2186
+ }
2187
+ registerEndpoint(resource, endpoint) {
2188
+ this.httpDriver.registerEndpoint(resource, endpoint);
2189
+ this._syncEngine.registerResource(resource, endpoint);
2190
+ }
2191
+ registerEntity(resource, entityClass) {
2192
+ this.localDriver.registerEntity(resource, entityClass);
2193
+ }
2194
+ getStorageBackend() {
2195
+ return this.localDriver.getStorageBackend();
2196
+ }
2197
+ };
2198
+
1612
2199
  // src/events/event-bus.ts
1613
2200
  var EventBus = class {
1614
2201
  listeners = /* @__PURE__ */ new Map();
@@ -1806,6 +2393,10 @@ function createClient(config) {
1806
2393
  if (driver instanceof HttpDriver) {
1807
2394
  driver.registerEndpoint(name, instance.endpoint);
1808
2395
  }
2396
+ if (driver instanceof SyncDriver) {
2397
+ driver.registerEntity(name, instance.entity);
2398
+ driver.registerEndpoint(name, instance.endpoint);
2399
+ }
1809
2400
  client[name] = instance;
1810
2401
  }
1811
2402
  if (config.auth) {
@@ -1868,6 +2459,45 @@ function createClient(config) {
1868
2459
  return false;
1869
2460
  }
1870
2461
  });
2462
+ } else if (defaultDriver instanceof SyncDriver) {
2463
+ authInstance._init(defaultDriver, resourceName);
2464
+ defaultDriver.registerEntity(resourceName, authInstance.entity);
2465
+ defaultDriver.registerEndpoint(resourceName, authInstance.endpoint);
2466
+ const hasLocalStorage = typeof localStorage !== "undefined";
2467
+ const LS_AUTH_KEY = "fauxbase:auth";
2468
+ let memoryAuthState = null;
2469
+ authInstance._initAuth(
2470
+ () => {
2471
+ if (hasLocalStorage) {
2472
+ const raw = localStorage.getItem(LS_AUTH_KEY);
2473
+ return raw ? JSON.parse(raw) : null;
2474
+ }
2475
+ return memoryAuthState;
2476
+ },
2477
+ (state) => {
2478
+ if (hasLocalStorage) {
2479
+ if (state) {
2480
+ localStorage.setItem(LS_AUTH_KEY, JSON.stringify(state));
2481
+ } else {
2482
+ localStorage.removeItem(LS_AUTH_KEY);
2483
+ }
2484
+ }
2485
+ memoryAuthState = state;
2486
+ }
2487
+ );
2488
+ authInstance._setHttpMode(defaultDriver._httpDriver);
2489
+ defaultDriver.setAuthProvider(() => {
2490
+ const token = authInstance.token;
2491
+ return token ? { token } : null;
2492
+ });
2493
+ defaultDriver.setOnUnauthorized(async () => {
2494
+ try {
2495
+ await authInstance.refresh();
2496
+ return true;
2497
+ } catch {
2498
+ return false;
2499
+ }
2500
+ });
1871
2501
  }
1872
2502
  client.auth = authInstance;
1873
2503
  for (const driver of overrideDrivers.values()) {
@@ -1924,8 +2554,14 @@ function createClient(config) {
1924
2554
  eventSource.reconnect();
1925
2555
  });
1926
2556
  }
2557
+ if (defaultDriver instanceof SyncDriver) {
2558
+ defaultDriver.syncEngine.setEventBus(eventBus);
2559
+ }
1927
2560
  client.disconnect = () => {
1928
2561
  eventSource?.disconnect();
2562
+ if (defaultDriver instanceof SyncDriver) {
2563
+ defaultDriver.syncEngine.stop();
2564
+ }
1929
2565
  eventBus.destroy();
1930
2566
  };
1931
2567
  }
@@ -1943,6 +2579,13 @@ function createClient(config) {
1943
2579
  }
1944
2580
  });
1945
2581
  }
2582
+ } else if (defaultDriver instanceof SyncDriver) {
2583
+ readyPromise = defaultDriver.ready.then(() => {
2584
+ if (config.seeds) {
2585
+ applySeedsIfNeeded(defaultDriver, config.seeds);
2586
+ }
2587
+ defaultDriver.syncEngine.start();
2588
+ });
1946
2589
  } else {
1947
2590
  readyPromise = Promise.resolve();
1948
2591
  }
@@ -1960,6 +2603,8 @@ function createDriver(config) {
1960
2603
  return new LocalDriver(config);
1961
2604
  case "http":
1962
2605
  return new HttpDriver(config);
2606
+ case "sync":
2607
+ return new SyncDriver(config);
1963
2608
  default:
1964
2609
  throw new Error(`Unknown driver type: ${config.type}`);
1965
2610
  }
@@ -1986,6 +2631,7 @@ exports.LocalDriver = LocalDriver;
1986
2631
  exports.NetworkError = NetworkError;
1987
2632
  exports.NotFoundError = NotFoundError;
1988
2633
  exports.Service = Service;
2634
+ exports.SyncDriver = SyncDriver;
1989
2635
  exports.TimeoutError = TimeoutError;
1990
2636
  exports.ValidationError = ValidationError;
1991
2637
  exports.afterCreate = afterCreate;