@pooflabs/core 0.0.48 → 0.0.91

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.mjs CHANGED
@@ -3989,9 +3989,40 @@ async function get(path, opts = {}) {
3989
3989
  }
3990
3990
  // Create a new request promise and store it
3991
3991
  const requestPromise = (async () => {
3992
+ var _a;
3992
3993
  try {
3993
- // Cache miss or bypass - proceed with API request
3994
+ // For realtime chains, prefer WebSocket reads (lower latency, already connected)
3995
+ const config = await getConfig();
3994
3996
  const pathIsDocument = normalizedPath.split("/").length % 2 === 0;
3997
+ if (((_a = config.chain) === null || _a === void 0 ? void 0 : _a.startsWith('realtime_')) && !config.isServer && !opts.prompt && !opts.shape && !opts.cursor) {
3998
+ try {
3999
+ const { wsGet, wsQuery, hasActiveConnection } = await Promise.resolve().then(function () { return subscriptionV2; });
4000
+ if (hasActiveConnection()) {
4001
+ if (pathIsDocument) {
4002
+ const wsResult = await wsGet(normalizedPath);
4003
+ const responseData = wsResult;
4004
+ if (!opts.bypassCache) {
4005
+ getCache[cacheKey] = { data: responseData, expiresAt: now + GET_CACHE_TTL };
4006
+ }
4007
+ return responseData;
4008
+ }
4009
+ else if (!opts.limit) {
4010
+ const wsResult = await wsQuery(normalizedPath, {
4011
+ filter: undefined,
4012
+ sort: undefined,
4013
+ includeSubPaths: opts.includeSubPaths,
4014
+ });
4015
+ const responseData = wsResult;
4016
+ if (!opts.bypassCache) {
4017
+ getCache[cacheKey] = { data: responseData, expiresAt: now + GET_CACHE_TTL };
4018
+ }
4019
+ return responseData;
4020
+ }
4021
+ }
4022
+ }
4023
+ catch ( /* fall through to HTTP */_b) { /* fall through to HTTP */ }
4024
+ }
4025
+ // Cache miss or bypass - proceed with HTTP API request
3995
4026
  let response;
3996
4027
  // Build common query params
3997
4028
  const includeSubPathsParam = opts.includeSubPaths ? '&includeSubPaths=true' : '';
@@ -4000,7 +4031,6 @@ async function get(path, opts = {}) {
4000
4031
  const cursorParam = opts.cursor ? `&cursor=${encodeURIComponent(opts.cursor)}` : '';
4001
4032
  if (pathIsDocument) {
4002
4033
  const itemId = encodeURIComponent(normalizedPath);
4003
- // For documents, query params go after the path
4004
4034
  const queryParams = [includeSubPathsParam, shapeParam].filter(p => p).join('');
4005
4035
  const apiPath = queryParams ? `items/${itemId}?${queryParams.substring(1)}` : `items/${itemId}`;
4006
4036
  response = await makeApiRequest('GET', apiPath, null, opts._overrides);
@@ -4195,7 +4225,7 @@ async function set(path, document, options) {
4195
4225
  return result;
4196
4226
  }
4197
4227
  async function setMany(many, options) {
4198
- var _a, _b, _c, _d, _e, _f, _g;
4228
+ var _a, _b, _c, _d, _e, _f, _g, _h;
4199
4229
  // Returns the data that was set, or undefined if the document was already set.
4200
4230
  try {
4201
4231
  const config = await getConfig();
@@ -4232,13 +4262,28 @@ async function setMany(many, options) {
4232
4262
  }
4233
4263
  let setResponse;
4234
4264
  try {
4235
- setResponse = await makeApiRequest('PUT', `items`, { documents }, options === null || options === void 0 ? void 0 : options._overrides);
4265
+ // For realtime chains, prefer WebSocket if a connection is active (lower latency)
4266
+ const useWs = ((_c = config.chain) === null || _c === void 0 ? void 0 : _c.startsWith('realtime_')) && !config.isServer;
4267
+ if (useWs) {
4268
+ try {
4269
+ const { wsSet, hasActiveConnection } = await Promise.resolve().then(function () { return subscriptionV2; });
4270
+ if (hasActiveConnection()) {
4271
+ const wsResult = await wsSet(documents);
4272
+ // Normalize to same shape as HTTP 200 response so downstream handling is identical
4273
+ setResponse = { data: wsResult, status: 200 };
4274
+ }
4275
+ }
4276
+ catch ( /* fall through to HTTP */_j) { /* fall through to HTTP */ }
4277
+ }
4278
+ if (!setResponse) {
4279
+ setResponse = await makeApiRequest('PUT', `items`, { documents }, options === null || options === void 0 ? void 0 : options._overrides);
4280
+ }
4236
4281
  }
4237
4282
  catch (error) {
4238
4283
  if ((error === null || error === void 0 ? void 0 : error.statusCode) === 402 && (error === null || error === void 0 ? void 0 : error.error) === 'INSUFFICIENT_BALANCE') {
4239
- const deficitLamports = Number((_c = error.deficitLamports) !== null && _c !== void 0 ? _c : 0);
4240
- const deficitSol = Number((_d = error.deficitSol) !== null && _d !== void 0 ? _d : deficitLamports / 1000000000);
4241
- throw new InsufficientBalanceError(String((_e = error.address) !== null && _e !== void 0 ? _e : ''), Number((_f = error.balanceLamports) !== null && _f !== void 0 ? _f : 0), Number((_g = error.estimatedCostLamports) !== null && _g !== void 0 ? _g : 0), deficitLamports, deficitSol);
4284
+ const deficitLamports = Number((_d = error.deficitLamports) !== null && _d !== void 0 ? _d : 0);
4285
+ const deficitSol = Number((_e = error.deficitSol) !== null && _e !== void 0 ? _e : deficitLamports / 1000000000);
4286
+ throw new InsufficientBalanceError(String((_f = error.address) !== null && _f !== void 0 ? _f : ''), Number((_g = error.balanceLamports) !== null && _g !== void 0 ? _g : 0), Number((_h = error.estimatedCostLamports) !== null && _h !== void 0 ? _h : 0), deficitLamports, deficitSol);
4242
4287
  }
4243
4288
  throw error;
4244
4289
  }
@@ -4282,7 +4327,7 @@ async function setMany(many, options) {
4282
4327
  else if (setResponse.data &&
4283
4328
  typeof setResponse.data === 'object' &&
4284
4329
  setResponse.data.success === true) {
4285
- const _h = setResponse.data, { success: _success } = _h, rest = __rest(_h, ["success"]);
4330
+ const _k = setResponse.data, { success: _success } = _k, rest = __rest(_k, ["success"]);
4286
4331
  return Object.assign(Object.assign(Object.assign({}, documents.map(d => d.document)), rest), { transactionId: null });
4287
4332
  }
4288
4333
  else {
@@ -4744,6 +4789,7 @@ async function getOrCreateConnection(appId, isServer) {
4744
4789
  subscriptions: new Map(),
4745
4790
  pendingSubscriptions: new Map(),
4746
4791
  pendingUnsubscriptions: new Map(),
4792
+ pendingRequests: new Map(),
4747
4793
  isConnecting: false,
4748
4794
  isConnected: false,
4749
4795
  appId,
@@ -4842,11 +4888,17 @@ async function getOrCreateConnection(appId, isServer) {
4842
4888
  clearInterval(connection.tokenRefreshTimer);
4843
4889
  connection.tokenRefreshTimer = null;
4844
4890
  }
4891
+ // Reject all pending WS CRUD requests on disconnect (fail fast)
4892
+ for (const [id, pending] of connection.pendingRequests) {
4893
+ clearTimeout(pending.timer);
4894
+ pending.reject(new Error('WebSocket disconnected'));
4895
+ }
4896
+ connection.pendingRequests.clear();
4845
4897
  });
4846
4898
  return connection;
4847
4899
  }
4848
4900
  function handleServerMessage(connection, message) {
4849
- var _a;
4901
+ var _a, _b;
4850
4902
  switch (message.type) {
4851
4903
  case 'subscribed': {
4852
4904
  const subscription = connection.subscriptions.get(message.subscriptionId);
@@ -4891,8 +4943,45 @@ function handleServerMessage(connection, message) {
4891
4943
  }
4892
4944
  break;
4893
4945
  }
4946
+ case 'response': {
4947
+ const pendingReq = connection.pendingRequests.get(message.requestId);
4948
+ if (pendingReq) {
4949
+ connection.pendingRequests.delete(message.requestId);
4950
+ clearTimeout(pendingReq.timer);
4951
+ if (message.status >= 400) {
4952
+ pendingReq.reject(new Error(`Request failed with status ${message.status}`));
4953
+ }
4954
+ else {
4955
+ pendingReq.resolve(message.data);
4956
+ }
4957
+ }
4958
+ break;
4959
+ }
4960
+ case 'setResponse': {
4961
+ const pendingSet = connection.pendingRequests.get(message.requestId);
4962
+ if (pendingSet) {
4963
+ connection.pendingRequests.delete(message.requestId);
4964
+ clearTimeout(pendingSet.timer);
4965
+ if (message.statusCode >= 400) {
4966
+ pendingSet.reject(new Error(((_a = message.body) === null || _a === void 0 ? void 0 : _a.message) || `Set failed with status ${message.statusCode}`));
4967
+ }
4968
+ else {
4969
+ pendingSet.resolve(message.body);
4970
+ }
4971
+ }
4972
+ break;
4973
+ }
4894
4974
  case 'error': {
4895
4975
  console.error('[WS v2] Server error:', message.code, message.message);
4976
+ // Handle CRUD request errors (requestId present)
4977
+ if (message.requestId) {
4978
+ const pendingReq = connection.pendingRequests.get(message.requestId);
4979
+ if (pendingReq) {
4980
+ connection.pendingRequests.delete(message.requestId);
4981
+ clearTimeout(pendingReq.timer);
4982
+ pendingReq.reject(new Error(`${message.code}: ${message.message}`));
4983
+ }
4984
+ }
4896
4985
  if (message.subscriptionId) {
4897
4986
  // Reject pending subscription if this is a subscription error
4898
4987
  const pending = connection.pendingSubscriptions.get(message.subscriptionId);
@@ -4904,7 +4993,7 @@ function handleServerMessage(connection, message) {
4904
4993
  const subscription = connection.subscriptions.get(message.subscriptionId);
4905
4994
  if (subscription) {
4906
4995
  for (const callback of subscription.callbacks) {
4907
- (_a = callback.onError) === null || _a === void 0 ? void 0 : _a.call(callback, new Error(`${message.code}: ${message.message}`));
4996
+ (_b = callback.onError) === null || _b === void 0 ? void 0 : _b.call(callback, new Error(`${message.code}: ${message.message}`));
4908
4997
  }
4909
4998
  }
4910
4999
  }
@@ -5193,6 +5282,112 @@ async function doReconnectWithNewAuth() {
5193
5282
  }
5194
5283
  }
5195
5284
  }
5285
+ // ============ CRUD over WebSocket ============
5286
+ const WS_REQUEST_TIMEOUT_MS = 30000;
5287
+ function generateRequestId() {
5288
+ return `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
5289
+ }
5290
+ /**
5291
+ * Returns true if there is an active v2 WebSocket connection open.
5292
+ */
5293
+ function hasActiveConnection() {
5294
+ for (const connection of connections.values()) {
5295
+ if (connection.ws && connection.isConnected) {
5296
+ return true;
5297
+ }
5298
+ }
5299
+ return false;
5300
+ }
5301
+ async function sendRequest(msgBuilder) {
5302
+ const config = await getConfig();
5303
+ const appId = config.appId;
5304
+ const connection = await getOrCreateConnection(appId, config.isServer);
5305
+ // Wait for the connection to be open (getOrCreateConnection may return
5306
+ // while still connecting).
5307
+ if (!connection.isConnected && connection.ws) {
5308
+ await new Promise((resolve, reject) => {
5309
+ const timeout = setTimeout(() => {
5310
+ var _a;
5311
+ (_a = connection.ws) === null || _a === void 0 ? void 0 : _a.removeEventListener('open', onOpen);
5312
+ reject(new Error('WebSocket connection timeout'));
5313
+ }, 10000);
5314
+ const onOpen = () => { clearTimeout(timeout); resolve(); };
5315
+ if (connection.isConnected) {
5316
+ clearTimeout(timeout);
5317
+ resolve();
5318
+ return;
5319
+ }
5320
+ connection.ws.addEventListener('open', onOpen);
5321
+ });
5322
+ }
5323
+ if (!connection.ws || !connection.isConnected) {
5324
+ throw new Error('WebSocket connection not available');
5325
+ }
5326
+ const requestId = generateRequestId();
5327
+ const message = msgBuilder(requestId);
5328
+ return new Promise((resolve, reject) => {
5329
+ const timer = setTimeout(() => {
5330
+ connection.pendingRequests.delete(requestId);
5331
+ reject(new Error(`WebSocket request timed out after ${WS_REQUEST_TIMEOUT_MS}ms`));
5332
+ }, WS_REQUEST_TIMEOUT_MS);
5333
+ connection.pendingRequests.set(requestId, { resolve, reject, timer });
5334
+ try {
5335
+ connection.ws.send(JSON.stringify(message));
5336
+ }
5337
+ catch (error) {
5338
+ connection.pendingRequests.delete(requestId);
5339
+ clearTimeout(timer);
5340
+ reject(error);
5341
+ }
5342
+ });
5343
+ }
5344
+ async function wsGet(path) {
5345
+ return sendRequest((requestId) => ({
5346
+ type: 'get',
5347
+ requestId,
5348
+ path,
5349
+ }));
5350
+ }
5351
+ async function wsSet(documents) {
5352
+ return sendRequest((requestId) => ({
5353
+ type: 'set',
5354
+ requestId,
5355
+ documents,
5356
+ }));
5357
+ }
5358
+ async function wsQuery(path, opts) {
5359
+ return sendRequest((requestId) => (Object.assign(Object.assign(Object.assign(Object.assign({ type: 'query', requestId,
5360
+ path }, ((opts === null || opts === void 0 ? void 0 : opts.filter) ? { filter: opts.filter } : {})), ((opts === null || opts === void 0 ? void 0 : opts.sort) ? { sort: opts.sort } : {})), ((opts === null || opts === void 0 ? void 0 : opts.limit) !== undefined ? { limit: opts.limit } : {})), ((opts === null || opts === void 0 ? void 0 : opts.includeSubPaths) ? { includeSubPaths: opts.includeSubPaths } : {}))));
5361
+ }
5362
+ async function wsDelete(path) {
5363
+ return sendRequest((requestId) => ({
5364
+ type: 'delete',
5365
+ requestId,
5366
+ path,
5367
+ }));
5368
+ }
5369
+ async function wsGetMany(paths) {
5370
+ return sendRequest((requestId) => ({
5371
+ type: 'getMany',
5372
+ requestId,
5373
+ paths,
5374
+ }));
5375
+ }
5376
+
5377
+ var subscriptionV2 = /*#__PURE__*/Object.freeze({
5378
+ __proto__: null,
5379
+ clearCacheV2: clearCacheV2,
5380
+ closeAllSubscriptionsV2: closeAllSubscriptionsV2,
5381
+ getCachedDataV2: getCachedDataV2,
5382
+ hasActiveConnection: hasActiveConnection,
5383
+ reconnectWithNewAuthV2: reconnectWithNewAuthV2,
5384
+ subscribeV2: subscribeV2,
5385
+ wsDelete: wsDelete,
5386
+ wsGet: wsGet,
5387
+ wsGetMany: wsGetMany,
5388
+ wsQuery: wsQuery,
5389
+ wsSet: wsSet
5390
+ });
5196
5391
 
5197
5392
  /**
5198
5393
  * WebSocket Subscription Module
@@ -5447,5 +5642,785 @@ class ReactNativeSessionManager {
5447
5642
  }
5448
5643
  ReactNativeSessionManager.TAROBASE_SESSION_STORAGE_KEY = "tarobase_session_storage";
5449
5644
 
5450
- export { InsufficientBalanceError, ReactNativeSessionManager, ServerSessionManager, WebSessionManager, aggregate, buildSetDocumentsTransaction, clearCache, closeAllSubscriptions, convertRemainingAccounts, count, createSessionWithPrivy, createSessionWithSignature, genAuthNonce, genSolanaMessage, get, getCachedData, getConfig, getFiles, getIdToken, getMany, init, reconnectWithNewAuth, refreshSession, runExpression, runExpressionMany, runQuery, runQueryMany, set, setFile, setMany, signAndSubmitTransaction, signMessage, signSessionCreateMessage, signTransaction, subscribe };
5645
+ // ---------------------------------------------------------------------------
5646
+ // realtime-store.ts — Client-side state manager for realtime apps.
5647
+ //
5648
+ // Manages: WS connection, in-memory state, IDB persistence, optimistic
5649
+ // writes, delta accumulation, loading states, ephemeral/durable tiers.
5650
+ // ---------------------------------------------------------------------------
5651
+ // ---------------------------------------------------------------------------
5652
+ // IDB helpers (lazy-loaded, non-blocking)
5653
+ // ---------------------------------------------------------------------------
5654
+ const IDB_NAME = 'tarobase-realtime';
5655
+ const IDB_STORE = 'subscriptions';
5656
+ const IDB_VERSION = 1;
5657
+ let idbPromise = null;
5658
+ function getIDB() {
5659
+ if (idbPromise)
5660
+ return idbPromise;
5661
+ if (typeof indexedDB === 'undefined') {
5662
+ return Promise.reject(new Error('IndexedDB not available'));
5663
+ }
5664
+ idbPromise = new Promise((resolve, reject) => {
5665
+ const req = indexedDB.open(IDB_NAME, IDB_VERSION);
5666
+ req.onupgradeneeded = () => {
5667
+ const db = req.result;
5668
+ if (!db.objectStoreNames.contains(IDB_STORE)) {
5669
+ db.createObjectStore(IDB_STORE);
5670
+ }
5671
+ };
5672
+ req.onsuccess = () => resolve(req.result);
5673
+ req.onerror = () => reject(req.error);
5674
+ });
5675
+ return idbPromise;
5676
+ }
5677
+ async function idbGet(key) {
5678
+ try {
5679
+ const db = await getIDB();
5680
+ return new Promise((resolve) => {
5681
+ const tx = db.transaction(IDB_STORE, 'readonly');
5682
+ const store = tx.objectStore(IDB_STORE);
5683
+ const req = store.get(key);
5684
+ req.onsuccess = () => { var _a; return resolve((_a = req.result) !== null && _a !== void 0 ? _a : null); };
5685
+ req.onerror = () => resolve(null);
5686
+ });
5687
+ }
5688
+ catch (_a) {
5689
+ return null;
5690
+ }
5691
+ }
5692
+ async function idbSet(key, value) {
5693
+ try {
5694
+ const db = await getIDB();
5695
+ return new Promise((resolve) => {
5696
+ const tx = db.transaction(IDB_STORE, 'readwrite');
5697
+ const store = tx.objectStore(IDB_STORE);
5698
+ store.put(value, key);
5699
+ tx.oncomplete = () => resolve();
5700
+ tx.onerror = () => resolve();
5701
+ });
5702
+ }
5703
+ catch (_a) {
5704
+ // Best-effort persistence
5705
+ }
5706
+ }
5707
+ // ---------------------------------------------------------------------------
5708
+ // RealtimeStore
5709
+ // ---------------------------------------------------------------------------
5710
+ let nextRequestId = 1;
5711
+ class RealtimeStore {
5712
+ constructor() {
5713
+ this.ws = null;
5714
+ this.wsUrl = '';
5715
+ this.appId = '';
5716
+ this.subscriptions = new Map();
5717
+ this.pendingRequests = new Map();
5718
+ this.connectPromise = null;
5719
+ this.reconnectTimer = null;
5720
+ this.reconnectDelay = 1000;
5721
+ this.maxReconnectDelay = 30000;
5722
+ this.idbFlushTimer = null;
5723
+ this.idbDirtyKeys = new Set();
5724
+ this.closed = false;
5725
+ this.authToken = null;
5726
+ }
5727
+ // -----------------------------------------------------------------------
5728
+ // Initialization
5729
+ // -----------------------------------------------------------------------
5730
+ async init() {
5731
+ var _a, _b;
5732
+ const config = await getConfig();
5733
+ this.appId = config.appId;
5734
+ this.wsUrl = config.wsApiUrl;
5735
+ if (config.authProvider) {
5736
+ try {
5737
+ const headers = await ((_b = (_a = config.authProvider).getAuthHeaders) === null || _b === void 0 ? void 0 : _b.call(_a));
5738
+ if (headers === null || headers === void 0 ? void 0 : headers.Authorization) {
5739
+ this.authToken = headers.Authorization.replace('Bearer ', '');
5740
+ }
5741
+ }
5742
+ catch ( /* no auth */_c) { /* no auth */ }
5743
+ }
5744
+ }
5745
+ // -----------------------------------------------------------------------
5746
+ // WebSocket connection
5747
+ // -----------------------------------------------------------------------
5748
+ async ensureConnected() {
5749
+ var _a;
5750
+ if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN)
5751
+ return;
5752
+ if (this.connectPromise)
5753
+ return this.connectPromise;
5754
+ this.connectPromise = this.connect();
5755
+ return this.connectPromise;
5756
+ }
5757
+ connect() {
5758
+ return new Promise((resolve, reject) => {
5759
+ if (this.closed) {
5760
+ reject(new Error('Store closed'));
5761
+ return;
5762
+ }
5763
+ const params = new URLSearchParams();
5764
+ params.set('apiKey', this.appId);
5765
+ // Auth token sent via subprotocol to avoid leaking in URL/logs
5766
+ const url = `${this.wsUrl}?${params.toString()}`;
5767
+ const protocols = this.authToken ? [`bearer-${this.authToken}`] : undefined;
5768
+ const ws = protocols ? new WebSocket(url, protocols) : new WebSocket(url);
5769
+ this.ws = ws;
5770
+ const onOpen = () => {
5771
+ ws.removeEventListener('error', onError);
5772
+ this.reconnectDelay = 1000;
5773
+ this.connectPromise = null;
5774
+ this.resubscribeAll();
5775
+ resolve();
5776
+ };
5777
+ const onError = (e) => {
5778
+ ws.removeEventListener('open', onOpen);
5779
+ this.connectPromise = null;
5780
+ reject(new Error('WebSocket connection failed'));
5781
+ };
5782
+ ws.addEventListener('open', onOpen, { once: true });
5783
+ ws.addEventListener('error', onError, { once: true });
5784
+ ws.addEventListener('message', (event) => {
5785
+ this.handleMessage(event.data);
5786
+ });
5787
+ ws.addEventListener('close', () => {
5788
+ this.ws = null;
5789
+ this.connectPromise = null;
5790
+ this.rejectAllPending('WebSocket closed');
5791
+ this.setAllSubscriptionStatus('reconnecting');
5792
+ this.scheduleReconnect();
5793
+ });
5794
+ });
5795
+ }
5796
+ scheduleReconnect() {
5797
+ if (this.closed)
5798
+ return;
5799
+ if (this.reconnectTimer)
5800
+ clearTimeout(this.reconnectTimer);
5801
+ this.reconnectTimer = setTimeout(() => {
5802
+ this.ensureConnected().catch(() => {
5803
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
5804
+ this.scheduleReconnect();
5805
+ });
5806
+ }, this.reconnectDelay);
5807
+ }
5808
+ resubscribeAll() {
5809
+ for (const sub of this.subscriptions.values()) {
5810
+ this.sendSubscribe(sub);
5811
+ }
5812
+ }
5813
+ // -----------------------------------------------------------------------
5814
+ // Message handling
5815
+ // -----------------------------------------------------------------------
5816
+ handleMessage(raw) {
5817
+ const text = typeof raw === 'string' ? raw : new TextDecoder().decode(raw);
5818
+ let msg;
5819
+ try {
5820
+ msg = JSON.parse(text);
5821
+ }
5822
+ catch (_a) {
5823
+ return;
5824
+ }
5825
+ switch (msg.type) {
5826
+ case 'snapshot':
5827
+ this.handleSnapshot(msg);
5828
+ break;
5829
+ case 'delta':
5830
+ this.handleDelta(msg);
5831
+ break;
5832
+ case 'result':
5833
+ this.handleResult(msg);
5834
+ break;
5835
+ case 'error':
5836
+ this.handleError(msg);
5837
+ break;
5838
+ case 'pong':
5839
+ break;
5840
+ // v1 compat: handle legacy message types during transition
5841
+ case 'subscribed':
5842
+ this.handleSnapshot(Object.assign(Object.assign({}, msg), { type: 'snapshot', docs: msg.data }));
5843
+ break;
5844
+ case 'data':
5845
+ // Legacy full-snapshot delta — treat as snapshot replacement
5846
+ this.handleLegacyData(msg);
5847
+ break;
5848
+ case 'response':
5849
+ this.handleResult(Object.assign(Object.assign({}, msg), { type: 'result', ok: msg.status === 200, doc: msg.data }));
5850
+ break;
5851
+ }
5852
+ }
5853
+ handleSnapshot(msg) {
5854
+ var _a, _b, _c;
5855
+ const subId = (_a = msg.id) !== null && _a !== void 0 ? _a : msg.subscriptionId;
5856
+ if (!subId)
5857
+ return;
5858
+ const sub = this.findSubscriptionById(subId);
5859
+ if (!sub)
5860
+ return;
5861
+ const docs = (_c = (_b = msg.docs) !== null && _b !== void 0 ? _b : msg.data) !== null && _c !== void 0 ? _c : [];
5862
+ const docsArray = Array.isArray(docs) ? docs : [docs];
5863
+ sub.docs.clear();
5864
+ for (const doc of docsArray) {
5865
+ if (doc && doc._id) {
5866
+ sub.docs.set(doc._id, doc);
5867
+ }
5868
+ }
5869
+ sub.ref.current = sub.docs;
5870
+ sub.status = 'live';
5871
+ sub.isStale = false;
5872
+ sub.error = null;
5873
+ this.notifySubscription(sub);
5874
+ this.markIdbDirty(sub.path);
5875
+ }
5876
+ handleDelta(msg) {
5877
+ var _a, _b;
5878
+ const subId = (_a = msg.id) !== null && _a !== void 0 ? _a : msg.subscriptionId;
5879
+ if (!subId)
5880
+ return;
5881
+ const sub = this.findSubscriptionById(subId);
5882
+ if (!sub)
5883
+ return;
5884
+ if (sub.tier === 'ephemeral') {
5885
+ // Ephemeral: just overwrite, no accumulation logic
5886
+ if (msg.change === 'removed' && msg.docId) {
5887
+ sub.docs.delete(msg.docId);
5888
+ }
5889
+ else if (msg.doc && msg.doc._id) {
5890
+ sub.docs.set(msg.doc._id, msg.doc);
5891
+ }
5892
+ sub.ref.current = sub.docs;
5893
+ if (sub.options.mode !== 'ref') {
5894
+ this.notifySubscription(sub);
5895
+ }
5896
+ return;
5897
+ }
5898
+ // Durable/checkpointed: full delta handling
5899
+ switch (msg.change) {
5900
+ case 'added':
5901
+ case 'modified':
5902
+ if (msg.doc && msg.doc._id) {
5903
+ sub.docs.set(msg.doc._id, msg.doc);
5904
+ }
5905
+ break;
5906
+ case 'removed':
5907
+ if (msg.docId) {
5908
+ sub.docs.delete(msg.docId);
5909
+ }
5910
+ else if ((_b = msg.doc) === null || _b === void 0 ? void 0 : _b._id) {
5911
+ sub.docs.delete(msg.doc._id);
5912
+ }
5913
+ break;
5914
+ }
5915
+ sub.ref.current = sub.docs;
5916
+ this.notifySubscription(sub);
5917
+ this.markIdbDirty(sub.path);
5918
+ }
5919
+ handleLegacyData(msg) {
5920
+ // Legacy v1 format: 'data' message with full snapshot or single doc
5921
+ const subId = msg.subscriptionId;
5922
+ if (!subId)
5923
+ return;
5924
+ const sub = this.findSubscriptionById(subId);
5925
+ if (!sub)
5926
+ return;
5927
+ if (Array.isArray(msg.data)) {
5928
+ // Full snapshot replacement
5929
+ sub.docs.clear();
5930
+ for (const doc of msg.data) {
5931
+ if (doc && doc._id)
5932
+ sub.docs.set(doc._id, doc);
5933
+ }
5934
+ }
5935
+ else if (msg.data && msg.data._id) {
5936
+ // Single doc update
5937
+ sub.docs.set(msg.data._id, msg.data);
5938
+ }
5939
+ else if (msg.data === null) ;
5940
+ sub.ref.current = sub.docs;
5941
+ sub.status = 'live';
5942
+ sub.isStale = false;
5943
+ this.notifySubscription(sub);
5944
+ this.markIdbDirty(sub.path);
5945
+ }
5946
+ handleResult(msg) {
5947
+ var _a, _b, _c, _d;
5948
+ const requestId = msg.requestId;
5949
+ if (!requestId)
5950
+ return;
5951
+ const pending = this.pendingRequests.get(requestId);
5952
+ if (!pending)
5953
+ return;
5954
+ this.pendingRequests.delete(requestId);
5955
+ clearTimeout(pending.timeout);
5956
+ const ok = (_a = msg.ok) !== null && _a !== void 0 ? _a : (msg.status === 200);
5957
+ if (ok) {
5958
+ pending.resolve((_c = (_b = msg.doc) !== null && _b !== void 0 ? _b : msg.data) !== null && _c !== void 0 ? _c : true);
5959
+ }
5960
+ else {
5961
+ pending.reject(new Error((_d = msg.error) !== null && _d !== void 0 ? _d : 'Operation failed'));
5962
+ }
5963
+ }
5964
+ handleError(msg) {
5965
+ var _a;
5966
+ const requestId = msg.requestId;
5967
+ if (requestId) {
5968
+ const pending = this.pendingRequests.get(requestId);
5969
+ if (pending) {
5970
+ this.pendingRequests.delete(requestId);
5971
+ clearTimeout(pending.timeout);
5972
+ pending.reject(new Error((_a = msg.message) !== null && _a !== void 0 ? _a : 'Server error'));
5973
+ }
5974
+ }
5975
+ }
5976
+ // -----------------------------------------------------------------------
5977
+ // Subscribe
5978
+ // -----------------------------------------------------------------------
5979
+ async subscribe(path, opts = {}) {
5980
+ var _a;
5981
+ const tier = (_a = opts.tier) !== null && _a !== void 0 ? _a : 'durable';
5982
+ const subKey = this.getSubKey(path, opts);
5983
+ let sub = this.subscriptions.get(subKey);
5984
+ if (sub) {
5985
+ // Existing subscription — add callback
5986
+ if (opts.onData)
5987
+ sub.callbacks.add(opts.onData);
5988
+ if (opts.onState)
5989
+ sub.stateCallbacks.add(opts.onState);
5990
+ // Immediately deliver current state
5991
+ if (opts.onData && sub.docs.size > 0) {
5992
+ opts.onData(this.docsToArray(sub));
5993
+ }
5994
+ if (opts.onState) {
5995
+ opts.onState(this.getState(sub));
5996
+ }
5997
+ return this.createUnsubscribe(subKey, opts.onData, opts.onState);
5998
+ }
5999
+ // New subscription
6000
+ const subId = `sub_${nextRequestId++}`;
6001
+ sub = {
6002
+ id: subId,
6003
+ path,
6004
+ tier,
6005
+ options: opts,
6006
+ docs: new Map(),
6007
+ status: 'idle',
6008
+ isStale: false,
6009
+ error: null,
6010
+ callbacks: new Set(opts.onData ? [opts.onData] : []),
6011
+ stateCallbacks: new Set(opts.onState ? [opts.onState] : []),
6012
+ ref: { current: new Map() },
6013
+ };
6014
+ this.subscriptions.set(subKey, sub);
6015
+ // Step 1: Load from IDB (durable/checkpointed only)
6016
+ if (tier !== 'ephemeral') {
6017
+ const cached = await idbGet(this.idbKey(path));
6018
+ if (cached && Array.isArray(cached)) {
6019
+ for (const doc of cached) {
6020
+ if (doc && doc._id)
6021
+ sub.docs.set(doc._id, doc);
6022
+ }
6023
+ sub.ref.current = sub.docs;
6024
+ sub.status = 'cached';
6025
+ sub.isStale = true;
6026
+ this.notifySubscription(sub);
6027
+ }
6028
+ }
6029
+ // Step 2: Connect and subscribe via WS
6030
+ sub.status = sub.docs.size > 0 ? 'cached' : 'loading';
6031
+ this.notifyState(sub);
6032
+ try {
6033
+ await this.ensureConnected();
6034
+ this.sendSubscribe(sub);
6035
+ }
6036
+ catch (_b) {
6037
+ sub.status = 'error';
6038
+ sub.error = new Error('Connection failed');
6039
+ this.notifyState(sub);
6040
+ }
6041
+ return this.createUnsubscribe(subKey, opts.onData, opts.onState);
6042
+ }
6043
+ getRef(path, opts = {}) {
6044
+ var _a;
6045
+ const subKey = this.getSubKey(path, opts);
6046
+ const sub = this.subscriptions.get(subKey);
6047
+ if (sub)
6048
+ return sub.ref;
6049
+ // Auto-subscribe in ref mode
6050
+ const ref = { current: new Map() };
6051
+ this.subscribe(path, Object.assign(Object.assign({}, opts), { mode: 'ref', tier: 'ephemeral' })).catch(() => { });
6052
+ const newSub = this.subscriptions.get(this.getSubKey(path, Object.assign(Object.assign({}, opts), { tier: 'ephemeral' })));
6053
+ return (_a = newSub === null || newSub === void 0 ? void 0 : newSub.ref) !== null && _a !== void 0 ? _a : ref;
6054
+ }
6055
+ // -----------------------------------------------------------------------
6056
+ // CRUD operations
6057
+ // -----------------------------------------------------------------------
6058
+ async set(path, doc) {
6059
+ var _a;
6060
+ await this.ensureConnected();
6061
+ // Resolve operations (Increment, Time.Now) client-side for optimistic update
6062
+ const resolvedDoc = this.resolveOperations(doc, path);
6063
+ // Optimistic update: apply to local state immediately
6064
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
6065
+ const collectionPath = this.getCollectionPath(normalizedPath);
6066
+ const optimisticDoc = Object.assign(Object.assign({ _id: normalizedPath, pathId: normalizedPath }, resolvedDoc), { tarobase_updated_at: Date.now() });
6067
+ const sub = this.findSubscriptionByPath(collectionPath);
6068
+ let prevDoc = null;
6069
+ if (sub) {
6070
+ prevDoc = (_a = sub.docs.get(normalizedPath)) !== null && _a !== void 0 ? _a : null;
6071
+ sub.docs.set(normalizedPath, optimisticDoc);
6072
+ sub.ref.current = sub.docs;
6073
+ this.notifySubscription(sub);
6074
+ }
6075
+ // Send to server
6076
+ const requestId = `r_${nextRequestId++}`;
6077
+ try {
6078
+ const result = await this.sendRequest(requestId, {
6079
+ type: 'set',
6080
+ requestId,
6081
+ documents: [{ destinationPath: normalizedPath, document: doc }],
6082
+ });
6083
+ // Replace optimistic doc with server-confirmed version
6084
+ if (sub && result && typeof result === 'object') {
6085
+ const serverDoc = Array.isArray(result) ? result[0] : result;
6086
+ if (serverDoc && serverDoc._id) {
6087
+ sub.docs.set(serverDoc._id, serverDoc);
6088
+ sub.ref.current = sub.docs;
6089
+ this.notifySubscription(sub);
6090
+ this.markIdbDirty(collectionPath);
6091
+ }
6092
+ }
6093
+ return Array.isArray(result) ? result[0] : result;
6094
+ }
6095
+ catch (err) {
6096
+ // Revert optimistic update
6097
+ if (sub) {
6098
+ if (prevDoc) {
6099
+ sub.docs.set(normalizedPath, prevDoc);
6100
+ }
6101
+ else {
6102
+ sub.docs.delete(normalizedPath);
6103
+ }
6104
+ sub.ref.current = sub.docs;
6105
+ this.notifySubscription(sub);
6106
+ }
6107
+ throw err;
6108
+ }
6109
+ }
6110
+ async get(path) {
6111
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
6112
+ // Check local subscriptions first
6113
+ const collectionPath = this.getCollectionPath(normalizedPath);
6114
+ const sub = this.findSubscriptionByPath(collectionPath);
6115
+ if (sub && sub.status === 'live') {
6116
+ const doc = sub.docs.get(normalizedPath);
6117
+ return doc !== null && doc !== void 0 ? doc : null;
6118
+ }
6119
+ // One-shot WS fetch
6120
+ await this.ensureConnected();
6121
+ const requestId = `r_${nextRequestId++}`;
6122
+ return this.sendRequest(requestId, {
6123
+ type: 'get',
6124
+ requestId,
6125
+ path: normalizedPath,
6126
+ });
6127
+ }
6128
+ async getMany(paths) {
6129
+ await this.ensureConnected();
6130
+ const normalizedPaths = paths.map(p => p.startsWith('/') ? p.slice(1) : p);
6131
+ const requestId = `r_${nextRequestId++}`;
6132
+ return this.sendRequest(requestId, {
6133
+ type: 'getMany',
6134
+ requestId,
6135
+ paths: normalizedPaths,
6136
+ });
6137
+ }
6138
+ async delete(path) {
6139
+ var _a;
6140
+ await this.ensureConnected();
6141
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
6142
+ // Optimistic: remove from local state
6143
+ const collectionPath = this.getCollectionPath(normalizedPath);
6144
+ const sub = this.findSubscriptionByPath(collectionPath);
6145
+ let prevDoc = null;
6146
+ if (sub) {
6147
+ prevDoc = (_a = sub.docs.get(normalizedPath)) !== null && _a !== void 0 ? _a : null;
6148
+ sub.docs.delete(normalizedPath);
6149
+ sub.ref.current = sub.docs;
6150
+ this.notifySubscription(sub);
6151
+ }
6152
+ const requestId = `r_${nextRequestId++}`;
6153
+ try {
6154
+ await this.sendRequest(requestId, {
6155
+ type: 'delete',
6156
+ requestId,
6157
+ path: normalizedPath,
6158
+ });
6159
+ if (sub)
6160
+ this.markIdbDirty(collectionPath);
6161
+ }
6162
+ catch (err) {
6163
+ // Revert
6164
+ if (sub && prevDoc) {
6165
+ sub.docs.set(normalizedPath, prevDoc);
6166
+ sub.ref.current = sub.docs;
6167
+ this.notifySubscription(sub);
6168
+ }
6169
+ throw err;
6170
+ }
6171
+ }
6172
+ async query(path, opts) {
6173
+ await this.ensureConnected();
6174
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
6175
+ const requestId = `r_${nextRequestId++}`;
6176
+ return this.sendRequest(requestId, Object.assign(Object.assign(Object.assign(Object.assign({ type: 'query', requestId, path: normalizedPath }, ((opts === null || opts === void 0 ? void 0 : opts.filter) ? { filter: opts.filter } : {})), ((opts === null || opts === void 0 ? void 0 : opts.sort) ? { sort: opts.sort } : {})), ((opts === null || opts === void 0 ? void 0 : opts.limit) !== undefined ? { limit: opts.limit } : {})), ((opts === null || opts === void 0 ? void 0 : opts.includeSubPaths) ? { includeSubPaths: true } : {})));
6177
+ }
6178
+ async count(path) {
6179
+ var _a;
6180
+ await this.ensureConnected();
6181
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
6182
+ const requestId = `r_${nextRequestId++}`;
6183
+ const result = await this.sendRequest(requestId, {
6184
+ type: 'count',
6185
+ requestId,
6186
+ path: normalizedPath,
6187
+ });
6188
+ return typeof result === 'number' ? result : ((_a = result === null || result === void 0 ? void 0 : result.value) !== null && _a !== void 0 ? _a : 0);
6189
+ }
6190
+ async aggregate(path, operation, opts) {
6191
+ await this.ensureConnected();
6192
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
6193
+ const requestId = `r_${nextRequestId++}`;
6194
+ return this.sendRequest(requestId, Object.assign({ type: 'aggregate', requestId, path: normalizedPath, operation }, ((opts === null || opts === void 0 ? void 0 : opts.field) ? { field: opts.field } : {})));
6195
+ }
6196
+ // -----------------------------------------------------------------------
6197
+ // Helpers
6198
+ // -----------------------------------------------------------------------
6199
+ sendSubscribe(sub) {
6200
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
6201
+ return;
6202
+ const msg = {
6203
+ type: 'subscribe',
6204
+ subscriptionId: sub.id,
6205
+ path: sub.path,
6206
+ };
6207
+ if (sub.options.filter)
6208
+ msg.filter = sub.options.filter;
6209
+ if (sub.options.includeSubPaths)
6210
+ msg.includeSubPaths = true;
6211
+ if (sub.options.limit)
6212
+ msg.limit = sub.options.limit;
6213
+ if (sub.options.prompt)
6214
+ msg.prompt = sub.options.prompt;
6215
+ this.ws.send(JSON.stringify(msg));
6216
+ }
6217
+ sendRequest(requestId, msg) {
6218
+ return new Promise((resolve, reject) => {
6219
+ const timeout = setTimeout(() => {
6220
+ this.pendingRequests.delete(requestId);
6221
+ reject(new Error('Request timed out'));
6222
+ }, 30000);
6223
+ this.pendingRequests.set(requestId, { resolve, reject, timeout });
6224
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
6225
+ this.ws.send(JSON.stringify(msg));
6226
+ }
6227
+ else {
6228
+ this.pendingRequests.delete(requestId);
6229
+ clearTimeout(timeout);
6230
+ reject(new Error('WebSocket not connected'));
6231
+ }
6232
+ });
6233
+ }
6234
+ notifySubscription(sub) {
6235
+ const data = this.docsToArray(sub);
6236
+ const callbacks = Array.from(sub.callbacks);
6237
+ for (const cb of callbacks) {
6238
+ try {
6239
+ cb(data);
6240
+ }
6241
+ catch ( /* swallow callback errors */_a) { /* swallow callback errors */ }
6242
+ }
6243
+ this.notifyState(sub);
6244
+ }
6245
+ notifyState(sub) {
6246
+ const state = this.getState(sub);
6247
+ const callbacks = Array.from(sub.stateCallbacks);
6248
+ for (const cb of callbacks) {
6249
+ try {
6250
+ cb(state);
6251
+ }
6252
+ catch ( /* swallow */_a) { /* swallow */ }
6253
+ }
6254
+ }
6255
+ getState(sub) {
6256
+ return {
6257
+ data: this.docsToArray(sub),
6258
+ status: sub.status,
6259
+ isStale: sub.isStale,
6260
+ error: sub.error,
6261
+ };
6262
+ }
6263
+ docsToArray(sub) {
6264
+ return Array.from(sub.docs.values());
6265
+ }
6266
+ findSubscriptionById(id) {
6267
+ for (const sub of this.subscriptions.values()) {
6268
+ if (sub.id === id)
6269
+ return sub;
6270
+ }
6271
+ return undefined;
6272
+ }
6273
+ findSubscriptionByPath(collectionPath) {
6274
+ for (const sub of this.subscriptions.values()) {
6275
+ const subPath = sub.path.startsWith('/') ? sub.path.slice(1) : sub.path;
6276
+ if (subPath === collectionPath)
6277
+ return sub;
6278
+ if (collectionPath.startsWith(subPath + '/'))
6279
+ return sub;
6280
+ }
6281
+ return undefined;
6282
+ }
6283
+ getCollectionPath(docPath) {
6284
+ const segments = docPath.split('/');
6285
+ if (segments.length % 2 === 0) {
6286
+ return segments.slice(0, -1).join('/');
6287
+ }
6288
+ return docPath;
6289
+ }
6290
+ getSubKey(path, opts) {
6291
+ const parts = [path];
6292
+ if (opts.filter)
6293
+ parts.push(JSON.stringify(opts.filter));
6294
+ if (opts.prompt)
6295
+ parts.push(opts.prompt);
6296
+ if (opts.tier)
6297
+ parts.push(opts.tier);
6298
+ return parts.join('::');
6299
+ }
6300
+ idbKey(path) {
6301
+ return `${this.appId}:${path}`;
6302
+ }
6303
+ markIdbDirty(path) {
6304
+ const sub = this.findSubscriptionByPath(path);
6305
+ if (sub && sub.tier === 'ephemeral')
6306
+ return;
6307
+ this.idbDirtyKeys.add(path);
6308
+ if (!this.idbFlushTimer) {
6309
+ this.idbFlushTimer = setTimeout(() => {
6310
+ this.flushIdb();
6311
+ this.idbFlushTimer = null;
6312
+ }, 500);
6313
+ }
6314
+ }
6315
+ async flushIdb() {
6316
+ const keys = Array.from(this.idbDirtyKeys);
6317
+ this.idbDirtyKeys.clear();
6318
+ for (const path of keys) {
6319
+ const sub = this.findSubscriptionByPath(path);
6320
+ if (sub && sub.tier !== 'ephemeral') {
6321
+ const docs = this.docsToArray(sub);
6322
+ await idbSet(this.idbKey(path), docs);
6323
+ }
6324
+ }
6325
+ }
6326
+ createUnsubscribe(subKey, onData, onState) {
6327
+ return async () => {
6328
+ const sub = this.subscriptions.get(subKey);
6329
+ if (!sub)
6330
+ return;
6331
+ if (onData)
6332
+ sub.callbacks.delete(onData);
6333
+ if (onState)
6334
+ sub.stateCallbacks.delete(onState);
6335
+ // If no more callbacks, unsubscribe entirely
6336
+ if (sub.callbacks.size === 0 && sub.stateCallbacks.size === 0) {
6337
+ this.subscriptions.delete(subKey);
6338
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
6339
+ this.ws.send(JSON.stringify({
6340
+ type: 'unsubscribe',
6341
+ subscriptionId: sub.id,
6342
+ }));
6343
+ }
6344
+ }
6345
+ };
6346
+ }
6347
+ resolveOperations(doc, path) {
6348
+ var _a;
6349
+ if (!doc || typeof doc !== 'object')
6350
+ return doc;
6351
+ const resolved = {};
6352
+ for (const [key, value] of Object.entries(doc)) {
6353
+ if (value && typeof value === 'object' && !Array.isArray(value) && value.operation) {
6354
+ const op = value;
6355
+ if (op.operation === 'time' && op.value === 'now') {
6356
+ resolved[key] = Math.floor(Date.now() / 1000);
6357
+ }
6358
+ else if (op.operation === 'increment') {
6359
+ // For optimistic: get current value and add
6360
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
6361
+ const collectionPath = this.getCollectionPath(normalizedPath);
6362
+ const sub = this.findSubscriptionByPath(collectionPath);
6363
+ const existing = sub === null || sub === void 0 ? void 0 : sub.docs.get(normalizedPath);
6364
+ const current = (_a = existing === null || existing === void 0 ? void 0 : existing[key]) !== null && _a !== void 0 ? _a : 0;
6365
+ resolved[key] = (typeof current === 'number' ? current : 0) + op.value;
6366
+ }
6367
+ else {
6368
+ resolved[key] = value;
6369
+ }
6370
+ }
6371
+ else {
6372
+ resolved[key] = value;
6373
+ }
6374
+ }
6375
+ return resolved;
6376
+ }
6377
+ rejectAllPending(reason) {
6378
+ for (const [requestId, pending] of this.pendingRequests) {
6379
+ clearTimeout(pending.timeout);
6380
+ pending.reject(new Error(reason));
6381
+ }
6382
+ this.pendingRequests.clear();
6383
+ }
6384
+ setAllSubscriptionStatus(status) {
6385
+ for (const sub of this.subscriptions.values()) {
6386
+ sub.status = status;
6387
+ this.notifyState(sub);
6388
+ }
6389
+ }
6390
+ // -----------------------------------------------------------------------
6391
+ // Lifecycle
6392
+ // -----------------------------------------------------------------------
6393
+ close() {
6394
+ this.closed = true;
6395
+ if (this.reconnectTimer)
6396
+ clearTimeout(this.reconnectTimer);
6397
+ if (this.idbFlushTimer)
6398
+ clearTimeout(this.idbFlushTimer);
6399
+ this.flushIdb();
6400
+ if (this.ws) {
6401
+ this.ws.close(1000, 'Store closed');
6402
+ this.ws = null;
6403
+ }
6404
+ this.rejectAllPending('Store closed');
6405
+ this.subscriptions.clear();
6406
+ }
6407
+ }
6408
+ // ---------------------------------------------------------------------------
6409
+ // Singleton instance
6410
+ // ---------------------------------------------------------------------------
6411
+ let storeInstance = null;
6412
+ function getRealtimeStore() {
6413
+ if (!storeInstance) {
6414
+ storeInstance = new RealtimeStore();
6415
+ }
6416
+ return storeInstance;
6417
+ }
6418
+ function resetRealtimeStore() {
6419
+ if (storeInstance) {
6420
+ storeInstance.close();
6421
+ storeInstance = null;
6422
+ }
6423
+ }
6424
+
6425
+ export { InsufficientBalanceError, ReactNativeSessionManager, RealtimeStore, ServerSessionManager, WebSessionManager, aggregate, buildSetDocumentsTransaction, clearCache, closeAllSubscriptions, convertRemainingAccounts, count, createSessionWithPrivy, createSessionWithSignature, genAuthNonce, genSolanaMessage, get, getCachedData, getConfig, getFiles, getIdToken, getMany, getRealtimeStore, hasActiveConnection, init, reconnectWithNewAuth, refreshSession, resetRealtimeStore, runExpression, runExpressionMany, runQuery, runQueryMany, set, setFile, setMany, signAndSubmitTransaction, signMessage, signSessionCreateMessage, signTransaction, subscribe, wsDelete, wsGet, wsGetMany, wsQuery, wsSet };
5451
6426
  //# sourceMappingURL=index.mjs.map