@pooflabs/web 0.0.90 → 0.0.92-rc1

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 (66) hide show
  1. package/dist/auth/hooks/useAuth.d.ts +2 -1
  2. package/dist/auth/index.d.ts +6 -0
  3. package/dist/auth/providers/offchain-auth-provider.d.ts +1 -0
  4. package/dist/auth/providers/phantom-wallet-provider.d.ts +10 -0
  5. package/dist/{index-C65AsRIA.esm.js → index-8ttHwD7w.esm.js} +14 -364
  6. package/dist/index-8ttHwD7w.esm.js.map +1 -0
  7. package/dist/index-B41m9KgX.esm.js +6 -0
  8. package/dist/index-B41m9KgX.esm.js.map +1 -0
  9. package/dist/{index-DOYZMq5N.esm.js → index-B8EmyNEY.esm.js} +13 -363
  10. package/dist/index-B8EmyNEY.esm.js.map +1 -0
  11. package/dist/{index-CzVM_hix.esm.js → index-DOAgc25g.esm.js} +1050 -42
  12. package/dist/index-DOAgc25g.esm.js.map +1 -0
  13. package/dist/{index-lt7AQ69U.js → index-DYlgQ5eM.js} +14 -364
  14. package/dist/index-DYlgQ5eM.js.map +1 -0
  15. package/dist/{index-BZ_W-sR0.js → index-Db6dsdHn.js} +2 -2
  16. package/dist/index-Db6dsdHn.js.map +1 -0
  17. package/dist/{index-Eb2uyla3.js → index-Qooqzt4n.js} +1052 -41
  18. package/dist/index-Qooqzt4n.js.map +1 -0
  19. package/dist/{index-CqpErQap.js → index-YWtjwOnI.js} +13 -363
  20. package/dist/index-YWtjwOnI.js.map +1 -0
  21. package/dist/{index.browser-D1uNEi4e.esm.js → index.browser-BAtXWf8k.esm.js} +2 -2
  22. package/dist/{index.browser-D1uNEi4e.esm.js.map → index.browser-BAtXWf8k.esm.js.map} +1 -1
  23. package/dist/{index.browser-C81ODkjH.js → index.browser-BtqDgoVf.js} +2 -2
  24. package/dist/{index.browser-C81ODkjH.js.map → index.browser-BtqDgoVf.js.map} +1 -1
  25. package/dist/{index.browser-zlnKOqcR.esm.js → index.browser-Cmz2nG0U.esm.js} +2 -2
  26. package/dist/{index.browser-zlnKOqcR.esm.js.map → index.browser-Cmz2nG0U.esm.js.map} +1 -1
  27. package/dist/{index.browser-BltIkY00.js → index.browser-DC1jUfJf.js} +2 -2
  28. package/dist/{index.browser-BltIkY00.js.map → index.browser-DC1jUfJf.js.map} +1 -1
  29. package/dist/index.esm.js +1 -1
  30. package/dist/index.js +4 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/{index.native-Czk4F68O.esm.js → index.native-BbmtowC0.esm.js} +955 -30
  33. package/dist/index.native-BbmtowC0.esm.js.map +1 -0
  34. package/dist/{index.native-DYddSSgT.js → index.native-DoznXdQe.js} +957 -29
  35. package/dist/index.native-DoznXdQe.js.map +1 -0
  36. package/dist/index.native.esm.js +1 -1
  37. package/dist/index.native.js +4 -1
  38. package/dist/index.native.js.map +1 -1
  39. package/dist/{phantom-wallet-provider-0tX0Zs72.esm.js → phantom-wallet-provider-BZwDaCQQ.esm.js} +99 -16
  40. package/dist/phantom-wallet-provider-BZwDaCQQ.esm.js.map +1 -0
  41. package/dist/{phantom-wallet-provider-DR5qAGcS.js → phantom-wallet-provider-CNrBhCG1.js} +99 -16
  42. package/dist/phantom-wallet-provider-CNrBhCG1.js.map +1 -0
  43. package/dist/{privy-wallet-provider-BqTsdJPZ.js → privy-wallet-provider-BjiWbKiy.js} +3 -3
  44. package/dist/privy-wallet-provider-BjiWbKiy.js.map +1 -0
  45. package/dist/{privy-wallet-provider-ChMUFAEp.esm.js → privy-wallet-provider-CFa78z1Z.esm.js} +3 -3
  46. package/dist/privy-wallet-provider-CFa78z1Z.esm.js.map +1 -0
  47. package/dist/{solana-mobile-wallet-provider-Cq2H4eoW.esm.js → solana-mobile-wallet-provider-P6brXW6S.esm.js} +4 -4
  48. package/dist/{solana-mobile-wallet-provider-Cq2H4eoW.esm.js.map → solana-mobile-wallet-provider-P6brXW6S.esm.js.map} +1 -1
  49. package/dist/{solana-mobile-wallet-provider-DiXPSf8n.js → solana-mobile-wallet-provider-yETC7TqO.js} +4 -4
  50. package/dist/{solana-mobile-wallet-provider-DiXPSf8n.js.map → solana-mobile-wallet-provider-yETC7TqO.js.map} +1 -1
  51. package/package.json +2 -2
  52. package/dist/index-BZ_W-sR0.js.map +0 -1
  53. package/dist/index-C65AsRIA.esm.js.map +0 -1
  54. package/dist/index-CqpErQap.js.map +0 -1
  55. package/dist/index-CzVM_hix.esm.js.map +0 -1
  56. package/dist/index-D72o3sHT.esm.js +0 -6
  57. package/dist/index-D72o3sHT.esm.js.map +0 -1
  58. package/dist/index-DOYZMq5N.esm.js.map +0 -1
  59. package/dist/index-Eb2uyla3.js.map +0 -1
  60. package/dist/index-lt7AQ69U.js.map +0 -1
  61. package/dist/index.native-Czk4F68O.esm.js.map +0 -1
  62. package/dist/index.native-DYddSSgT.js.map +0 -1
  63. package/dist/phantom-wallet-provider-0tX0Zs72.esm.js.map +0 -1
  64. package/dist/phantom-wallet-provider-DR5qAGcS.js.map +0 -1
  65. package/dist/privy-wallet-provider-BqTsdJPZ.js.map +0 -1
  66. package/dist/privy-wallet-provider-ChMUFAEp.esm.js.map +0 -1
@@ -10383,9 +10383,40 @@ async function get(path, opts = {}) {
10383
10383
  }
10384
10384
  // Create a new request promise and store it
10385
10385
  const requestPromise = (async () => {
10386
+ var _a;
10386
10387
  try {
10387
- // Cache miss or bypass - proceed with API request
10388
+ // For realtime chains, prefer WebSocket reads (lower latency, already connected)
10389
+ const config = await getConfig();
10388
10390
  const pathIsDocument = normalizedPath.split("/").length % 2 === 0;
10391
+ if (((_a = config.chain) === null || _a === void 0 ? void 0 : _a.startsWith('realtime_')) && !config.isServer && !opts.prompt && !opts.shape && !opts.cursor) {
10392
+ try {
10393
+ const { wsGet, wsQuery, hasActiveConnection } = await Promise.resolve().then(function () { return subscriptionV2; });
10394
+ if (hasActiveConnection()) {
10395
+ if (pathIsDocument) {
10396
+ const wsResult = await wsGet(normalizedPath);
10397
+ const responseData = wsResult;
10398
+ if (!opts.bypassCache) {
10399
+ getCache[cacheKey] = { data: responseData, expiresAt: now + GET_CACHE_TTL };
10400
+ }
10401
+ return responseData;
10402
+ }
10403
+ else if (!opts.limit) {
10404
+ const wsResult = await wsQuery(normalizedPath, {
10405
+ filter: undefined,
10406
+ sort: undefined,
10407
+ includeSubPaths: opts.includeSubPaths,
10408
+ });
10409
+ const responseData = wsResult;
10410
+ if (!opts.bypassCache) {
10411
+ getCache[cacheKey] = { data: responseData, expiresAt: now + GET_CACHE_TTL };
10412
+ }
10413
+ return responseData;
10414
+ }
10415
+ }
10416
+ }
10417
+ catch ( /* fall through to HTTP */_b) { /* fall through to HTTP */ }
10418
+ }
10419
+ // Cache miss or bypass - proceed with HTTP API request
10389
10420
  let response;
10390
10421
  // Build common query params
10391
10422
  const includeSubPathsParam = opts.includeSubPaths ? '&includeSubPaths=true' : '';
@@ -10394,7 +10425,6 @@ async function get(path, opts = {}) {
10394
10425
  const cursorParam = opts.cursor ? `&cursor=${encodeURIComponent(opts.cursor)}` : '';
10395
10426
  if (pathIsDocument) {
10396
10427
  const itemId = encodeURIComponent(normalizedPath);
10397
- // For documents, query params go after the path
10398
10428
  const queryParams = [includeSubPathsParam, shapeParam].filter(p => p).join('');
10399
10429
  const apiPath = queryParams ? `items/${itemId}?${queryParams.substring(1)}` : `items/${itemId}`;
10400
10430
  response = await makeApiRequest('GET', apiPath, null, opts._overrides);
@@ -10589,7 +10619,7 @@ async function set(path, document, options) {
10589
10619
  return result;
10590
10620
  }
10591
10621
  async function setMany(many, options) {
10592
- var _a, _b, _c, _d, _e, _f, _g;
10622
+ var _a, _b, _c, _d, _e, _f, _g, _h;
10593
10623
  // Returns the data that was set, or undefined if the document was already set.
10594
10624
  try {
10595
10625
  const config = await getConfig();
@@ -10626,13 +10656,28 @@ async function setMany(many, options) {
10626
10656
  }
10627
10657
  let setResponse;
10628
10658
  try {
10629
- setResponse = await makeApiRequest('PUT', `items`, { documents }, options === null || options === void 0 ? void 0 : options._overrides);
10659
+ // For realtime chains, prefer WebSocket if a connection is active (lower latency)
10660
+ const useWs = ((_c = config.chain) === null || _c === void 0 ? void 0 : _c.startsWith('realtime_')) && !config.isServer;
10661
+ if (useWs) {
10662
+ try {
10663
+ const { wsSet, hasActiveConnection } = await Promise.resolve().then(function () { return subscriptionV2; });
10664
+ if (hasActiveConnection()) {
10665
+ const wsResult = await wsSet(documents);
10666
+ // Normalize to same shape as HTTP 200 response so downstream handling is identical
10667
+ setResponse = { data: wsResult, status: 200 };
10668
+ }
10669
+ }
10670
+ catch ( /* fall through to HTTP */_j) { /* fall through to HTTP */ }
10671
+ }
10672
+ if (!setResponse) {
10673
+ setResponse = await makeApiRequest('PUT', `items`, { documents }, options === null || options === void 0 ? void 0 : options._overrides);
10674
+ }
10630
10675
  }
10631
10676
  catch (error) {
10632
10677
  if ((error === null || error === void 0 ? void 0 : error.statusCode) === 402 && (error === null || error === void 0 ? void 0 : error.error) === 'INSUFFICIENT_BALANCE') {
10633
- const deficitLamports = Number((_c = error.deficitLamports) !== null && _c !== void 0 ? _c : 0);
10634
- const deficitSol = Number((_d = error.deficitSol) !== null && _d !== void 0 ? _d : deficitLamports / 1000000000);
10635
- 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);
10678
+ const deficitLamports = Number((_d = error.deficitLamports) !== null && _d !== void 0 ? _d : 0);
10679
+ const deficitSol = Number((_e = error.deficitSol) !== null && _e !== void 0 ? _e : deficitLamports / 1000000000);
10680
+ 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);
10636
10681
  }
10637
10682
  throw error;
10638
10683
  }
@@ -10676,7 +10721,7 @@ async function setMany(many, options) {
10676
10721
  else if (setResponse.data &&
10677
10722
  typeof setResponse.data === 'object' &&
10678
10723
  setResponse.data.success === true) {
10679
- const _h = setResponse.data, { success: _success } = _h, rest = __rest(_h, ["success"]);
10724
+ const _k = setResponse.data, { success: _success } = _k, rest = __rest(_k, ["success"]);
10680
10725
  return Object.assign(Object.assign(Object.assign({}, documents.map(d => d.document)), rest), { transactionId: null });
10681
10726
  }
10682
10727
  else {
@@ -11723,6 +11768,21 @@ async function wsGetMany(paths) {
11723
11768
  }));
11724
11769
  }
11725
11770
 
11771
+ var subscriptionV2 = /*#__PURE__*/Object.freeze({
11772
+ __proto__: null,
11773
+ clearCacheV2: clearCacheV2,
11774
+ closeAllSubscriptionsV2: closeAllSubscriptionsV2,
11775
+ getCachedDataV2: getCachedDataV2,
11776
+ hasActiveConnection: hasActiveConnection,
11777
+ reconnectWithNewAuthV2: reconnectWithNewAuthV2,
11778
+ subscribeV2: subscribeV2,
11779
+ wsDelete: wsDelete,
11780
+ wsGet: wsGet,
11781
+ wsGetMany: wsGetMany,
11782
+ wsQuery: wsQuery,
11783
+ wsSet: wsSet
11784
+ });
11785
+
11726
11786
  /**
11727
11787
  * WebSocket Subscription Module
11728
11788
  *
@@ -11976,6 +12036,786 @@ class ReactNativeSessionManager {
11976
12036
  }
11977
12037
  ReactNativeSessionManager.TAROBASE_SESSION_STORAGE_KEY = "tarobase_session_storage";
11978
12038
 
12039
+ // ---------------------------------------------------------------------------
12040
+ // realtime-store.ts — Client-side state manager for realtime apps.
12041
+ //
12042
+ // Manages: WS connection, in-memory state, IDB persistence, optimistic
12043
+ // writes, delta accumulation, loading states, ephemeral/durable tiers.
12044
+ // ---------------------------------------------------------------------------
12045
+ // ---------------------------------------------------------------------------
12046
+ // IDB helpers (lazy-loaded, non-blocking)
12047
+ // ---------------------------------------------------------------------------
12048
+ const IDB_NAME = 'tarobase-realtime';
12049
+ const IDB_STORE = 'subscriptions';
12050
+ const IDB_VERSION = 1;
12051
+ let idbPromise = null;
12052
+ function getIDB() {
12053
+ if (idbPromise)
12054
+ return idbPromise;
12055
+ if (typeof indexedDB === 'undefined') {
12056
+ return Promise.reject(new Error('IndexedDB not available'));
12057
+ }
12058
+ idbPromise = new Promise((resolve, reject) => {
12059
+ const req = indexedDB.open(IDB_NAME, IDB_VERSION);
12060
+ req.onupgradeneeded = () => {
12061
+ const db = req.result;
12062
+ if (!db.objectStoreNames.contains(IDB_STORE)) {
12063
+ db.createObjectStore(IDB_STORE);
12064
+ }
12065
+ };
12066
+ req.onsuccess = () => resolve(req.result);
12067
+ req.onerror = () => reject(req.error);
12068
+ });
12069
+ return idbPromise;
12070
+ }
12071
+ async function idbGet(key) {
12072
+ try {
12073
+ const db = await getIDB();
12074
+ return new Promise((resolve) => {
12075
+ const tx = db.transaction(IDB_STORE, 'readonly');
12076
+ const store = tx.objectStore(IDB_STORE);
12077
+ const req = store.get(key);
12078
+ req.onsuccess = () => { var _a; return resolve((_a = req.result) !== null && _a !== void 0 ? _a : null); };
12079
+ req.onerror = () => resolve(null);
12080
+ });
12081
+ }
12082
+ catch (_a) {
12083
+ return null;
12084
+ }
12085
+ }
12086
+ async function idbSet(key, value) {
12087
+ try {
12088
+ const db = await getIDB();
12089
+ return new Promise((resolve) => {
12090
+ const tx = db.transaction(IDB_STORE, 'readwrite');
12091
+ const store = tx.objectStore(IDB_STORE);
12092
+ store.put(value, key);
12093
+ tx.oncomplete = () => resolve();
12094
+ tx.onerror = () => resolve();
12095
+ });
12096
+ }
12097
+ catch (_a) {
12098
+ // Best-effort persistence
12099
+ }
12100
+ }
12101
+ // ---------------------------------------------------------------------------
12102
+ // RealtimeStore
12103
+ // ---------------------------------------------------------------------------
12104
+ let nextRequestId = 1;
12105
+ class RealtimeStore {
12106
+ constructor() {
12107
+ this.ws = null;
12108
+ this.wsUrl = '';
12109
+ this.appId = '';
12110
+ this.subscriptions = new Map();
12111
+ this.pendingRequests = new Map();
12112
+ this.connectPromise = null;
12113
+ this.reconnectTimer = null;
12114
+ this.reconnectDelay = 1000;
12115
+ this.maxReconnectDelay = 30000;
12116
+ this.idbFlushTimer = null;
12117
+ this.idbDirtyKeys = new Set();
12118
+ this.closed = false;
12119
+ this.authToken = null;
12120
+ }
12121
+ // -----------------------------------------------------------------------
12122
+ // Initialization
12123
+ // -----------------------------------------------------------------------
12124
+ async init() {
12125
+ var _a, _b;
12126
+ const config = await getConfig();
12127
+ this.appId = config.appId;
12128
+ this.wsUrl = config.wsApiUrl;
12129
+ if (config.authProvider) {
12130
+ try {
12131
+ const headers = await ((_b = (_a = config.authProvider).getAuthHeaders) === null || _b === void 0 ? void 0 : _b.call(_a));
12132
+ if (headers === null || headers === void 0 ? void 0 : headers.Authorization) {
12133
+ this.authToken = headers.Authorization.replace('Bearer ', '');
12134
+ }
12135
+ }
12136
+ catch ( /* no auth */_c) { /* no auth */ }
12137
+ }
12138
+ }
12139
+ // -----------------------------------------------------------------------
12140
+ // WebSocket connection
12141
+ // -----------------------------------------------------------------------
12142
+ async ensureConnected() {
12143
+ var _a;
12144
+ if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN)
12145
+ return;
12146
+ if (this.connectPromise)
12147
+ return this.connectPromise;
12148
+ this.connectPromise = this.connect();
12149
+ return this.connectPromise;
12150
+ }
12151
+ connect() {
12152
+ return new Promise((resolve, reject) => {
12153
+ if (this.closed) {
12154
+ reject(new Error('Store closed'));
12155
+ return;
12156
+ }
12157
+ const params = new URLSearchParams();
12158
+ params.set('apiKey', this.appId);
12159
+ // Auth token sent via subprotocol to avoid leaking in URL/logs
12160
+ const url = `${this.wsUrl}?${params.toString()}`;
12161
+ const protocols = this.authToken ? [`bearer-${this.authToken}`] : undefined;
12162
+ const ws = protocols ? new WebSocket(url, protocols) : new WebSocket(url);
12163
+ this.ws = ws;
12164
+ const onOpen = () => {
12165
+ ws.removeEventListener('error', onError);
12166
+ this.reconnectDelay = 1000;
12167
+ this.connectPromise = null;
12168
+ this.resubscribeAll();
12169
+ resolve();
12170
+ };
12171
+ const onError = (e) => {
12172
+ ws.removeEventListener('open', onOpen);
12173
+ this.connectPromise = null;
12174
+ reject(new Error('WebSocket connection failed'));
12175
+ };
12176
+ ws.addEventListener('open', onOpen, { once: true });
12177
+ ws.addEventListener('error', onError, { once: true });
12178
+ ws.addEventListener('message', (event) => {
12179
+ this.handleMessage(event.data);
12180
+ });
12181
+ ws.addEventListener('close', () => {
12182
+ this.ws = null;
12183
+ this.connectPromise = null;
12184
+ this.rejectAllPending('WebSocket closed');
12185
+ this.setAllSubscriptionStatus('reconnecting');
12186
+ this.scheduleReconnect();
12187
+ });
12188
+ });
12189
+ }
12190
+ scheduleReconnect() {
12191
+ if (this.closed)
12192
+ return;
12193
+ if (this.reconnectTimer)
12194
+ clearTimeout(this.reconnectTimer);
12195
+ this.reconnectTimer = setTimeout(() => {
12196
+ this.ensureConnected().catch(() => {
12197
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
12198
+ this.scheduleReconnect();
12199
+ });
12200
+ }, this.reconnectDelay);
12201
+ }
12202
+ resubscribeAll() {
12203
+ for (const sub of this.subscriptions.values()) {
12204
+ this.sendSubscribe(sub);
12205
+ }
12206
+ }
12207
+ // -----------------------------------------------------------------------
12208
+ // Message handling
12209
+ // -----------------------------------------------------------------------
12210
+ handleMessage(raw) {
12211
+ const text = typeof raw === 'string' ? raw : new TextDecoder().decode(raw);
12212
+ let msg;
12213
+ try {
12214
+ msg = JSON.parse(text);
12215
+ }
12216
+ catch (_a) {
12217
+ return;
12218
+ }
12219
+ switch (msg.type) {
12220
+ case 'snapshot':
12221
+ this.handleSnapshot(msg);
12222
+ break;
12223
+ case 'delta':
12224
+ this.handleDelta(msg);
12225
+ break;
12226
+ case 'result':
12227
+ this.handleResult(msg);
12228
+ break;
12229
+ case 'error':
12230
+ this.handleError(msg);
12231
+ break;
12232
+ case 'pong':
12233
+ break;
12234
+ // v1 compat: handle legacy message types during transition
12235
+ case 'subscribed':
12236
+ this.handleSnapshot(Object.assign(Object.assign({}, msg), { type: 'snapshot', docs: msg.data }));
12237
+ break;
12238
+ case 'data':
12239
+ // Legacy full-snapshot delta — treat as snapshot replacement
12240
+ this.handleLegacyData(msg);
12241
+ break;
12242
+ case 'response':
12243
+ this.handleResult(Object.assign(Object.assign({}, msg), { type: 'result', ok: msg.status === 200, doc: msg.data }));
12244
+ break;
12245
+ }
12246
+ }
12247
+ handleSnapshot(msg) {
12248
+ var _a, _b, _c;
12249
+ const subId = (_a = msg.id) !== null && _a !== void 0 ? _a : msg.subscriptionId;
12250
+ if (!subId)
12251
+ return;
12252
+ const sub = this.findSubscriptionById(subId);
12253
+ if (!sub)
12254
+ return;
12255
+ const docs = (_c = (_b = msg.docs) !== null && _b !== void 0 ? _b : msg.data) !== null && _c !== void 0 ? _c : [];
12256
+ const docsArray = Array.isArray(docs) ? docs : [docs];
12257
+ sub.docs.clear();
12258
+ for (const doc of docsArray) {
12259
+ if (doc && doc._id) {
12260
+ sub.docs.set(doc._id, doc);
12261
+ }
12262
+ }
12263
+ sub.ref.current = sub.docs;
12264
+ sub.status = 'live';
12265
+ sub.isStale = false;
12266
+ sub.error = null;
12267
+ this.notifySubscription(sub);
12268
+ this.markIdbDirty(sub.path);
12269
+ }
12270
+ handleDelta(msg) {
12271
+ var _a, _b;
12272
+ const subId = (_a = msg.id) !== null && _a !== void 0 ? _a : msg.subscriptionId;
12273
+ if (!subId)
12274
+ return;
12275
+ const sub = this.findSubscriptionById(subId);
12276
+ if (!sub)
12277
+ return;
12278
+ if (sub.tier === 'ephemeral') {
12279
+ // Ephemeral: just overwrite, no accumulation logic
12280
+ if (msg.change === 'removed' && msg.docId) {
12281
+ sub.docs.delete(msg.docId);
12282
+ }
12283
+ else if (msg.doc && msg.doc._id) {
12284
+ sub.docs.set(msg.doc._id, msg.doc);
12285
+ }
12286
+ sub.ref.current = sub.docs;
12287
+ if (sub.options.mode !== 'ref') {
12288
+ this.notifySubscription(sub);
12289
+ }
12290
+ return;
12291
+ }
12292
+ // Durable/checkpointed: full delta handling
12293
+ switch (msg.change) {
12294
+ case 'added':
12295
+ case 'modified':
12296
+ if (msg.doc && msg.doc._id) {
12297
+ sub.docs.set(msg.doc._id, msg.doc);
12298
+ }
12299
+ break;
12300
+ case 'removed':
12301
+ if (msg.docId) {
12302
+ sub.docs.delete(msg.docId);
12303
+ }
12304
+ else if ((_b = msg.doc) === null || _b === void 0 ? void 0 : _b._id) {
12305
+ sub.docs.delete(msg.doc._id);
12306
+ }
12307
+ break;
12308
+ }
12309
+ sub.ref.current = sub.docs;
12310
+ this.notifySubscription(sub);
12311
+ this.markIdbDirty(sub.path);
12312
+ }
12313
+ handleLegacyData(msg) {
12314
+ // Legacy v1 format: 'data' message with full snapshot or single doc
12315
+ const subId = msg.subscriptionId;
12316
+ if (!subId)
12317
+ return;
12318
+ const sub = this.findSubscriptionById(subId);
12319
+ if (!sub)
12320
+ return;
12321
+ if (Array.isArray(msg.data)) {
12322
+ // Full snapshot replacement
12323
+ sub.docs.clear();
12324
+ for (const doc of msg.data) {
12325
+ if (doc && doc._id)
12326
+ sub.docs.set(doc._id, doc);
12327
+ }
12328
+ }
12329
+ else if (msg.data && msg.data._id) {
12330
+ // Single doc update
12331
+ sub.docs.set(msg.data._id, msg.data);
12332
+ }
12333
+ else if (msg.data === null) ;
12334
+ sub.ref.current = sub.docs;
12335
+ sub.status = 'live';
12336
+ sub.isStale = false;
12337
+ this.notifySubscription(sub);
12338
+ this.markIdbDirty(sub.path);
12339
+ }
12340
+ handleResult(msg) {
12341
+ var _a, _b, _c, _d;
12342
+ const requestId = msg.requestId;
12343
+ if (!requestId)
12344
+ return;
12345
+ const pending = this.pendingRequests.get(requestId);
12346
+ if (!pending)
12347
+ return;
12348
+ this.pendingRequests.delete(requestId);
12349
+ clearTimeout(pending.timeout);
12350
+ const ok = (_a = msg.ok) !== null && _a !== void 0 ? _a : (msg.status === 200);
12351
+ if (ok) {
12352
+ pending.resolve((_c = (_b = msg.doc) !== null && _b !== void 0 ? _b : msg.data) !== null && _c !== void 0 ? _c : true);
12353
+ }
12354
+ else {
12355
+ pending.reject(new Error((_d = msg.error) !== null && _d !== void 0 ? _d : 'Operation failed'));
12356
+ }
12357
+ }
12358
+ handleError(msg) {
12359
+ var _a;
12360
+ const requestId = msg.requestId;
12361
+ if (requestId) {
12362
+ const pending = this.pendingRequests.get(requestId);
12363
+ if (pending) {
12364
+ this.pendingRequests.delete(requestId);
12365
+ clearTimeout(pending.timeout);
12366
+ pending.reject(new Error((_a = msg.message) !== null && _a !== void 0 ? _a : 'Server error'));
12367
+ }
12368
+ }
12369
+ }
12370
+ // -----------------------------------------------------------------------
12371
+ // Subscribe
12372
+ // -----------------------------------------------------------------------
12373
+ async subscribe(path, opts = {}) {
12374
+ var _a;
12375
+ const tier = (_a = opts.tier) !== null && _a !== void 0 ? _a : 'durable';
12376
+ const subKey = this.getSubKey(path, opts);
12377
+ let sub = this.subscriptions.get(subKey);
12378
+ if (sub) {
12379
+ // Existing subscription — add callback
12380
+ if (opts.onData)
12381
+ sub.callbacks.add(opts.onData);
12382
+ if (opts.onState)
12383
+ sub.stateCallbacks.add(opts.onState);
12384
+ // Immediately deliver current state
12385
+ if (opts.onData && sub.docs.size > 0) {
12386
+ opts.onData(this.docsToArray(sub));
12387
+ }
12388
+ if (opts.onState) {
12389
+ opts.onState(this.getState(sub));
12390
+ }
12391
+ return this.createUnsubscribe(subKey, opts.onData, opts.onState);
12392
+ }
12393
+ // New subscription
12394
+ const subId = `sub_${nextRequestId++}`;
12395
+ sub = {
12396
+ id: subId,
12397
+ path,
12398
+ tier,
12399
+ options: opts,
12400
+ docs: new Map(),
12401
+ status: 'idle',
12402
+ isStale: false,
12403
+ error: null,
12404
+ callbacks: new Set(opts.onData ? [opts.onData] : []),
12405
+ stateCallbacks: new Set(opts.onState ? [opts.onState] : []),
12406
+ ref: { current: new Map() },
12407
+ };
12408
+ this.subscriptions.set(subKey, sub);
12409
+ // Step 1: Load from IDB (durable/checkpointed only)
12410
+ if (tier !== 'ephemeral') {
12411
+ const cached = await idbGet(this.idbKey(path));
12412
+ if (cached && Array.isArray(cached)) {
12413
+ for (const doc of cached) {
12414
+ if (doc && doc._id)
12415
+ sub.docs.set(doc._id, doc);
12416
+ }
12417
+ sub.ref.current = sub.docs;
12418
+ sub.status = 'cached';
12419
+ sub.isStale = true;
12420
+ this.notifySubscription(sub);
12421
+ }
12422
+ }
12423
+ // Step 2: Connect and subscribe via WS
12424
+ sub.status = sub.docs.size > 0 ? 'cached' : 'loading';
12425
+ this.notifyState(sub);
12426
+ try {
12427
+ await this.ensureConnected();
12428
+ this.sendSubscribe(sub);
12429
+ }
12430
+ catch (_b) {
12431
+ sub.status = 'error';
12432
+ sub.error = new Error('Connection failed');
12433
+ this.notifyState(sub);
12434
+ }
12435
+ return this.createUnsubscribe(subKey, opts.onData, opts.onState);
12436
+ }
12437
+ getRef(path, opts = {}) {
12438
+ var _a;
12439
+ const subKey = this.getSubKey(path, opts);
12440
+ const sub = this.subscriptions.get(subKey);
12441
+ if (sub)
12442
+ return sub.ref;
12443
+ // Auto-subscribe in ref mode
12444
+ const ref = { current: new Map() };
12445
+ this.subscribe(path, Object.assign(Object.assign({}, opts), { mode: 'ref', tier: 'ephemeral' })).catch(() => { });
12446
+ const newSub = this.subscriptions.get(this.getSubKey(path, Object.assign(Object.assign({}, opts), { tier: 'ephemeral' })));
12447
+ return (_a = newSub === null || newSub === void 0 ? void 0 : newSub.ref) !== null && _a !== void 0 ? _a : ref;
12448
+ }
12449
+ // -----------------------------------------------------------------------
12450
+ // CRUD operations
12451
+ // -----------------------------------------------------------------------
12452
+ async set(path, doc) {
12453
+ var _a;
12454
+ await this.ensureConnected();
12455
+ // Resolve operations (Increment, Time.Now) client-side for optimistic update
12456
+ const resolvedDoc = this.resolveOperations(doc, path);
12457
+ // Optimistic update: apply to local state immediately
12458
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12459
+ const collectionPath = this.getCollectionPath(normalizedPath);
12460
+ const optimisticDoc = Object.assign(Object.assign({ _id: normalizedPath, pathId: normalizedPath }, resolvedDoc), { tarobase_updated_at: Date.now() });
12461
+ const sub = this.findSubscriptionByPath(collectionPath);
12462
+ let prevDoc = null;
12463
+ if (sub) {
12464
+ prevDoc = (_a = sub.docs.get(normalizedPath)) !== null && _a !== void 0 ? _a : null;
12465
+ sub.docs.set(normalizedPath, optimisticDoc);
12466
+ sub.ref.current = sub.docs;
12467
+ this.notifySubscription(sub);
12468
+ }
12469
+ // Send to server
12470
+ const requestId = `r_${nextRequestId++}`;
12471
+ try {
12472
+ const result = await this.sendRequest(requestId, {
12473
+ type: 'set',
12474
+ requestId,
12475
+ documents: [{ destinationPath: normalizedPath, document: doc }],
12476
+ });
12477
+ // Replace optimistic doc with server-confirmed version
12478
+ if (sub && result && typeof result === 'object') {
12479
+ const serverDoc = Array.isArray(result) ? result[0] : result;
12480
+ if (serverDoc && serverDoc._id) {
12481
+ sub.docs.set(serverDoc._id, serverDoc);
12482
+ sub.ref.current = sub.docs;
12483
+ this.notifySubscription(sub);
12484
+ this.markIdbDirty(collectionPath);
12485
+ }
12486
+ }
12487
+ return Array.isArray(result) ? result[0] : result;
12488
+ }
12489
+ catch (err) {
12490
+ // Revert optimistic update
12491
+ if (sub) {
12492
+ if (prevDoc) {
12493
+ sub.docs.set(normalizedPath, prevDoc);
12494
+ }
12495
+ else {
12496
+ sub.docs.delete(normalizedPath);
12497
+ }
12498
+ sub.ref.current = sub.docs;
12499
+ this.notifySubscription(sub);
12500
+ }
12501
+ throw err;
12502
+ }
12503
+ }
12504
+ async get(path) {
12505
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12506
+ // Check local subscriptions first
12507
+ const collectionPath = this.getCollectionPath(normalizedPath);
12508
+ const sub = this.findSubscriptionByPath(collectionPath);
12509
+ if (sub && sub.status === 'live') {
12510
+ const doc = sub.docs.get(normalizedPath);
12511
+ return doc !== null && doc !== void 0 ? doc : null;
12512
+ }
12513
+ // One-shot WS fetch
12514
+ await this.ensureConnected();
12515
+ const requestId = `r_${nextRequestId++}`;
12516
+ return this.sendRequest(requestId, {
12517
+ type: 'get',
12518
+ requestId,
12519
+ path: normalizedPath,
12520
+ });
12521
+ }
12522
+ async getMany(paths) {
12523
+ await this.ensureConnected();
12524
+ const normalizedPaths = paths.map(p => p.startsWith('/') ? p.slice(1) : p);
12525
+ const requestId = `r_${nextRequestId++}`;
12526
+ return this.sendRequest(requestId, {
12527
+ type: 'getMany',
12528
+ requestId,
12529
+ paths: normalizedPaths,
12530
+ });
12531
+ }
12532
+ async delete(path) {
12533
+ var _a;
12534
+ await this.ensureConnected();
12535
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12536
+ // Optimistic: remove from local state
12537
+ const collectionPath = this.getCollectionPath(normalizedPath);
12538
+ const sub = this.findSubscriptionByPath(collectionPath);
12539
+ let prevDoc = null;
12540
+ if (sub) {
12541
+ prevDoc = (_a = sub.docs.get(normalizedPath)) !== null && _a !== void 0 ? _a : null;
12542
+ sub.docs.delete(normalizedPath);
12543
+ sub.ref.current = sub.docs;
12544
+ this.notifySubscription(sub);
12545
+ }
12546
+ const requestId = `r_${nextRequestId++}`;
12547
+ try {
12548
+ await this.sendRequest(requestId, {
12549
+ type: 'delete',
12550
+ requestId,
12551
+ path: normalizedPath,
12552
+ });
12553
+ if (sub)
12554
+ this.markIdbDirty(collectionPath);
12555
+ }
12556
+ catch (err) {
12557
+ // Revert
12558
+ if (sub && prevDoc) {
12559
+ sub.docs.set(normalizedPath, prevDoc);
12560
+ sub.ref.current = sub.docs;
12561
+ this.notifySubscription(sub);
12562
+ }
12563
+ throw err;
12564
+ }
12565
+ }
12566
+ async query(path, opts) {
12567
+ await this.ensureConnected();
12568
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12569
+ const requestId = `r_${nextRequestId++}`;
12570
+ 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 } : {})));
12571
+ }
12572
+ async count(path) {
12573
+ var _a;
12574
+ await this.ensureConnected();
12575
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12576
+ const requestId = `r_${nextRequestId++}`;
12577
+ const result = await this.sendRequest(requestId, {
12578
+ type: 'count',
12579
+ requestId,
12580
+ path: normalizedPath,
12581
+ });
12582
+ return typeof result === 'number' ? result : ((_a = result === null || result === void 0 ? void 0 : result.value) !== null && _a !== void 0 ? _a : 0);
12583
+ }
12584
+ async aggregate(path, operation, opts) {
12585
+ await this.ensureConnected();
12586
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12587
+ const requestId = `r_${nextRequestId++}`;
12588
+ return this.sendRequest(requestId, Object.assign({ type: 'aggregate', requestId, path: normalizedPath, operation }, ((opts === null || opts === void 0 ? void 0 : opts.field) ? { field: opts.field } : {})));
12589
+ }
12590
+ // -----------------------------------------------------------------------
12591
+ // Helpers
12592
+ // -----------------------------------------------------------------------
12593
+ sendSubscribe(sub) {
12594
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
12595
+ return;
12596
+ const msg = {
12597
+ type: 'subscribe',
12598
+ subscriptionId: sub.id,
12599
+ path: sub.path,
12600
+ };
12601
+ if (sub.options.filter)
12602
+ msg.filter = sub.options.filter;
12603
+ if (sub.options.includeSubPaths)
12604
+ msg.includeSubPaths = true;
12605
+ if (sub.options.limit)
12606
+ msg.limit = sub.options.limit;
12607
+ if (sub.options.prompt)
12608
+ msg.prompt = sub.options.prompt;
12609
+ this.ws.send(JSON.stringify(msg));
12610
+ }
12611
+ sendRequest(requestId, msg) {
12612
+ return new Promise((resolve, reject) => {
12613
+ const timeout = setTimeout(() => {
12614
+ this.pendingRequests.delete(requestId);
12615
+ reject(new Error('Request timed out'));
12616
+ }, 30000);
12617
+ this.pendingRequests.set(requestId, { resolve, reject, timeout });
12618
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
12619
+ this.ws.send(JSON.stringify(msg));
12620
+ }
12621
+ else {
12622
+ this.pendingRequests.delete(requestId);
12623
+ clearTimeout(timeout);
12624
+ reject(new Error('WebSocket not connected'));
12625
+ }
12626
+ });
12627
+ }
12628
+ notifySubscription(sub) {
12629
+ const data = this.docsToArray(sub);
12630
+ const callbacks = Array.from(sub.callbacks);
12631
+ for (const cb of callbacks) {
12632
+ try {
12633
+ cb(data);
12634
+ }
12635
+ catch ( /* swallow callback errors */_a) { /* swallow callback errors */ }
12636
+ }
12637
+ this.notifyState(sub);
12638
+ }
12639
+ notifyState(sub) {
12640
+ const state = this.getState(sub);
12641
+ const callbacks = Array.from(sub.stateCallbacks);
12642
+ for (const cb of callbacks) {
12643
+ try {
12644
+ cb(state);
12645
+ }
12646
+ catch ( /* swallow */_a) { /* swallow */ }
12647
+ }
12648
+ }
12649
+ getState(sub) {
12650
+ return {
12651
+ data: this.docsToArray(sub),
12652
+ status: sub.status,
12653
+ isStale: sub.isStale,
12654
+ error: sub.error,
12655
+ };
12656
+ }
12657
+ docsToArray(sub) {
12658
+ return Array.from(sub.docs.values());
12659
+ }
12660
+ findSubscriptionById(id) {
12661
+ for (const sub of this.subscriptions.values()) {
12662
+ if (sub.id === id)
12663
+ return sub;
12664
+ }
12665
+ return undefined;
12666
+ }
12667
+ findSubscriptionByPath(collectionPath) {
12668
+ for (const sub of this.subscriptions.values()) {
12669
+ const subPath = sub.path.startsWith('/') ? sub.path.slice(1) : sub.path;
12670
+ if (subPath === collectionPath)
12671
+ return sub;
12672
+ if (collectionPath.startsWith(subPath + '/'))
12673
+ return sub;
12674
+ }
12675
+ return undefined;
12676
+ }
12677
+ getCollectionPath(docPath) {
12678
+ const segments = docPath.split('/');
12679
+ if (segments.length % 2 === 0) {
12680
+ return segments.slice(0, -1).join('/');
12681
+ }
12682
+ return docPath;
12683
+ }
12684
+ getSubKey(path, opts) {
12685
+ const parts = [path];
12686
+ if (opts.filter)
12687
+ parts.push(JSON.stringify(opts.filter));
12688
+ if (opts.prompt)
12689
+ parts.push(opts.prompt);
12690
+ if (opts.tier)
12691
+ parts.push(opts.tier);
12692
+ return parts.join('::');
12693
+ }
12694
+ idbKey(path) {
12695
+ return `${this.appId}:${path}`;
12696
+ }
12697
+ markIdbDirty(path) {
12698
+ const sub = this.findSubscriptionByPath(path);
12699
+ if (sub && sub.tier === 'ephemeral')
12700
+ return;
12701
+ this.idbDirtyKeys.add(path);
12702
+ if (!this.idbFlushTimer) {
12703
+ this.idbFlushTimer = setTimeout(() => {
12704
+ this.flushIdb();
12705
+ this.idbFlushTimer = null;
12706
+ }, 500);
12707
+ }
12708
+ }
12709
+ async flushIdb() {
12710
+ const keys = Array.from(this.idbDirtyKeys);
12711
+ this.idbDirtyKeys.clear();
12712
+ for (const path of keys) {
12713
+ const sub = this.findSubscriptionByPath(path);
12714
+ if (sub && sub.tier !== 'ephemeral') {
12715
+ const docs = this.docsToArray(sub);
12716
+ await idbSet(this.idbKey(path), docs);
12717
+ }
12718
+ }
12719
+ }
12720
+ createUnsubscribe(subKey, onData, onState) {
12721
+ return async () => {
12722
+ const sub = this.subscriptions.get(subKey);
12723
+ if (!sub)
12724
+ return;
12725
+ if (onData)
12726
+ sub.callbacks.delete(onData);
12727
+ if (onState)
12728
+ sub.stateCallbacks.delete(onState);
12729
+ // If no more callbacks, unsubscribe entirely
12730
+ if (sub.callbacks.size === 0 && sub.stateCallbacks.size === 0) {
12731
+ this.subscriptions.delete(subKey);
12732
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
12733
+ this.ws.send(JSON.stringify({
12734
+ type: 'unsubscribe',
12735
+ subscriptionId: sub.id,
12736
+ }));
12737
+ }
12738
+ }
12739
+ };
12740
+ }
12741
+ resolveOperations(doc, path) {
12742
+ var _a;
12743
+ if (!doc || typeof doc !== 'object')
12744
+ return doc;
12745
+ const resolved = {};
12746
+ for (const [key, value] of Object.entries(doc)) {
12747
+ if (value && typeof value === 'object' && !Array.isArray(value) && value.operation) {
12748
+ const op = value;
12749
+ if (op.operation === 'time' && op.value === 'now') {
12750
+ resolved[key] = Math.floor(Date.now() / 1000);
12751
+ }
12752
+ else if (op.operation === 'increment') {
12753
+ // For optimistic: get current value and add
12754
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12755
+ const collectionPath = this.getCollectionPath(normalizedPath);
12756
+ const sub = this.findSubscriptionByPath(collectionPath);
12757
+ const existing = sub === null || sub === void 0 ? void 0 : sub.docs.get(normalizedPath);
12758
+ const current = (_a = existing === null || existing === void 0 ? void 0 : existing[key]) !== null && _a !== void 0 ? _a : 0;
12759
+ resolved[key] = (typeof current === 'number' ? current : 0) + op.value;
12760
+ }
12761
+ else {
12762
+ resolved[key] = value;
12763
+ }
12764
+ }
12765
+ else {
12766
+ resolved[key] = value;
12767
+ }
12768
+ }
12769
+ return resolved;
12770
+ }
12771
+ rejectAllPending(reason) {
12772
+ for (const [requestId, pending] of this.pendingRequests) {
12773
+ clearTimeout(pending.timeout);
12774
+ pending.reject(new Error(reason));
12775
+ }
12776
+ this.pendingRequests.clear();
12777
+ }
12778
+ setAllSubscriptionStatus(status) {
12779
+ for (const sub of this.subscriptions.values()) {
12780
+ sub.status = status;
12781
+ this.notifyState(sub);
12782
+ }
12783
+ }
12784
+ // -----------------------------------------------------------------------
12785
+ // Lifecycle
12786
+ // -----------------------------------------------------------------------
12787
+ close() {
12788
+ this.closed = true;
12789
+ if (this.reconnectTimer)
12790
+ clearTimeout(this.reconnectTimer);
12791
+ if (this.idbFlushTimer)
12792
+ clearTimeout(this.idbFlushTimer);
12793
+ this.flushIdb();
12794
+ if (this.ws) {
12795
+ this.ws.close(1000, 'Store closed');
12796
+ this.ws = null;
12797
+ }
12798
+ this.rejectAllPending('Store closed');
12799
+ this.subscriptions.clear();
12800
+ }
12801
+ }
12802
+ // ---------------------------------------------------------------------------
12803
+ // Singleton instance
12804
+ // ---------------------------------------------------------------------------
12805
+ let storeInstance = null;
12806
+ function getRealtimeStore() {
12807
+ if (!storeInstance) {
12808
+ storeInstance = new RealtimeStore();
12809
+ }
12810
+ return storeInstance;
12811
+ }
12812
+ function resetRealtimeStore() {
12813
+ if (storeInstance) {
12814
+ storeInstance.close();
12815
+ storeInstance = null;
12816
+ }
12817
+ }
12818
+
11979
12819
  /**
11980
12820
  * Platform abstraction layer for React Native compatibility.
11981
12821
  *
@@ -12281,6 +13121,15 @@ class OffchainAuthProvider {
12281
13121
  }
12282
13122
  return null;
12283
13123
  }
13124
+ // Forward per-call login overrides (theme/title/subtitle and the `method`
13125
+ // directive) to the wrapped provider. Without this, the top-level login()
13126
+ // forwarder (which duck-types `setLoginOverrides` on the active provider)
13127
+ // skips the wrapper and the override — including `method` — is dropped on
13128
+ // offchain/Poofnet builds.
13129
+ setLoginOverrides(opts) {
13130
+ var _a, _b;
13131
+ (_b = (_a = this.wrappedProvider).setLoginOverrides) === null || _b === void 0 ? void 0 : _b.call(_a, opts);
13132
+ }
12284
13133
  async logout() {
12285
13134
  await this.wrappedProvider.logout();
12286
13135
  }
@@ -12972,6 +13821,26 @@ class OffchainAuthProvider {
12972
13821
  let currentAuthProvider = null;
12973
13822
  let currentAuthMethod = null;
12974
13823
  let initConfig = null;
13824
+ // The effective Phantom provider (offchain-wrapped when chain:"offchain"), captured
13825
+ // when a Phantom-primary app initializes. Used to restore Phantom after a staged
13826
+ // Privy email swap is cancelled/fails, and to route method-specific login() calls
13827
+ // back to Phantom. Null for non-Phantom apps.
13828
+ let savedPhantomProvider = null;
13829
+ /**
13830
+ * Restore the saved Phantom provider as the active auth provider. Used to undo a
13831
+ * staged Privy email swap on cancel/failure and to re-anchor method-specific
13832
+ * login() calls on Phantom. No-op when there is no saved Phantom provider.
13833
+ */
13834
+ async function restoreSavedPhantomProvider() {
13835
+ if (!savedPhantomProvider)
13836
+ return;
13837
+ currentAuthProvider = savedPhantomProvider;
13838
+ currentAuthMethod = 'phantom';
13839
+ const coreConfig = await getConfig();
13840
+ coreConfig.authProvider = savedPhantomProvider;
13841
+ coreConfig.authMethod = 'phantom';
13842
+ setAuthProviderInstance(savedPhantomProvider);
13843
+ }
12975
13844
  // --- localStorage helpers: track which auth method the user last logged in with ---
12976
13845
  const STORED_AUTH_METHOD_KEY$1 = 'tarobase_last_auth_method';
12977
13846
  function getStoredAuthMethod() {
@@ -13044,20 +13913,59 @@ async function hotSwapToPrivyProvider(config) {
13044
13913
  solana: {
13045
13914
  createOnLogin: 'users-without-wallets'
13046
13915
  }
13047
- }, appearance: Object.assign(Object.assign({}, (_f = (_e = config.privyConfig) === null || _e === void 0 ? void 0 : _e.config) === null || _f === void 0 ? void 0 : _f.appearance), { walletChainType: 'solana-only', showWalletLoginFirst: false, theme: 'dark', walletList: [] }) })
13916
+ }, appearance: Object.assign(Object.assign({}, (_f = (_e = config.privyConfig) === null || _e === void 0 ? void 0 : _e.config) === null || _f === void 0 ? void 0 : _f.appearance), { walletChainType: 'solana-only', showWalletLoginFirst: false, theme: 'dark', walletList: [] }),
13917
+ // Explicitly empty external wallets — social-only mode. Setting the
13918
+ // key (with an empty connectors array) keeps useWallets().ready able to
13919
+ // resolve while suppressing the wallet picker, AND prevents the
13920
+ // PrivyWalletProvider constructor from re-injecting the full Solana
13921
+ // connector set when externalWallets is absent (see privy-wallet-provider.ts).
13922
+ externalWallets: { solana: { connectors: [] } } })
13048
13923
  };
13049
13924
  const privyProvider = new PrivyWalletProvider((_g = config.name) !== null && _g !== void 0 ? _g : null, (_h = config.logoUrl) !== null && _h !== void 0 ? _h : null, fallbackPrivyConfig, rpcUrl);
13050
13925
  let provider = privyProvider;
13051
13926
  if (config.chain === "offchain") {
13052
13927
  provider = new OffchainAuthProvider(privyProvider);
13053
13928
  }
13054
- currentAuthProvider = provider;
13055
- currentAuthMethod = 'privy';
13056
- const coreConfig = await getConfig();
13057
- coreConfig.authProvider = provider;
13058
- coreConfig.authMethod = 'privy';
13059
- setAuthProviderInstance(provider);
13060
- return privyProvider;
13929
+ // STAGED swap: do NOT mutate the module-level auth globals here. The caller (the
13930
+ // Phantom modal's email button / the `method:'email'` bridge) calls `.login()` on
13931
+ // the returned provider separately. If the user cancels, committing up-front would
13932
+ // strand the globals on Privy and break reload/signing. Instead, return a provider
13933
+ // whose `login()` commits the globals to Privy ONLY on success and restores the
13934
+ // saved Phantom provider on cancel/failure.
13935
+ const commitPrivy = async () => {
13936
+ currentAuthProvider = provider;
13937
+ currentAuthMethod = 'privy';
13938
+ const coreConfig = await getConfig();
13939
+ coreConfig.authProvider = provider;
13940
+ coreConfig.authMethod = 'privy';
13941
+ setAuthProviderInstance(provider);
13942
+ };
13943
+ // Proxy intercepts only `login`; every other access delegates to `provider`. The
13944
+ // proxy is used solely for this one `.login()` call — on success the globals are
13945
+ // committed to the inner `provider` (not the proxy), so subsequent calls bypass it.
13946
+ return new Proxy(provider, {
13947
+ get(target, prop, receiver) {
13948
+ if (prop === 'login') {
13949
+ return async (...args) => {
13950
+ try {
13951
+ const user = await target.login(...args);
13952
+ if (user) {
13953
+ await commitPrivy();
13954
+ }
13955
+ else {
13956
+ await restoreSavedPhantomProvider();
13957
+ }
13958
+ return user;
13959
+ }
13960
+ catch (err) {
13961
+ await restoreSavedPhantomProvider();
13962
+ throw err;
13963
+ }
13964
+ };
13965
+ }
13966
+ return Reflect.get(target, prop, receiver);
13967
+ },
13968
+ });
13061
13969
  }
13062
13970
  async function hotSwapToMWAProvider(config) {
13063
13971
  var _a, _b;
@@ -13088,7 +13996,7 @@ const SOLANA_DEVNET_RPC_URL = "https://idelle-8nxsep-fast-devnet.helius-rpc.com"
13088
13996
  const SOLANA_MAINNET_RPC_URL = "https://celestia-cegncv-fast-mainnet.helius-rpc.com";
13089
13997
  const SURFNET_RPC_URL$2 = "https://surfpool.fly.dev";
13090
13998
  async function getAuthProvider(config) {
13091
- var _a, _b, _c, _d;
13999
+ var _a, _b, _c;
13092
14000
  if (currentAuthProvider) {
13093
14001
  return currentAuthProvider;
13094
14002
  }
@@ -13132,11 +14040,13 @@ async function getAuthProvider(config) {
13132
14040
  // MWA hot-swap is always available (custom modal shows MWA button on Android)
13133
14041
  currentAuthProvider.onSwitchToMWA =
13134
14042
  () => hotSwapToMWAProvider(config);
13135
- // Privy hot-swap only when enablePrivyFallback is on
13136
- if ((_d = config.phantomConfig) === null || _d === void 0 ? void 0 : _d.enablePrivyFallback) {
13137
- currentAuthProvider.onSwitchToPrivy =
13138
- () => hotSwapToPrivyProvider(config);
13139
- }
14043
+ // Privy email bridge: ALWAYS wired, independent of enablePrivyFallback, so
14044
+ // `login({ method: 'email' })` works even on a custom Phantom app (which
14045
+ // sets enablePrivyFallback:false). enablePrivyFallback now only controls
14046
+ // whether the DEFAULT modal renders its own email button (see
14047
+ // phantom-wallet-provider.ts) — not whether the bridge exists.
14048
+ currentAuthProvider.onSwitchToPrivy =
14049
+ () => hotSwapToPrivyProvider(config);
13140
14050
  break;
13141
14051
  }
13142
14052
  case "privy-expo":
@@ -13167,6 +14077,13 @@ async function getAuthProvider(config) {
13167
14077
  console.log("[Offchain] Wrapping auth provider for Poofnet transaction tracking");
13168
14078
  currentAuthProvider = new OffchainAuthProvider(currentAuthProvider);
13169
14079
  }
14080
+ // Save the EFFECTIVE Phantom provider (post offchain-wrap) so the staged Privy
14081
+ // email swap can restore it on cancel/failure and method-specific login() calls
14082
+ // can re-anchor on Phantom. Captured after the wrap so restore doesn't bypass the
14083
+ // OffchainAuthProvider on Poofnet/preview.
14084
+ if (authMethod === 'phantom') {
14085
+ savedPhantomProvider = currentAuthProvider;
14086
+ }
13170
14087
  return currentAuthProvider;
13171
14088
  }
13172
14089
  async function login$1(options) {
@@ -13182,6 +14099,14 @@ async function login$1(options) {
13182
14099
  if (!currentAuthProvider) {
13183
14100
  throw new Error("Auth provider not initialized. Please call init() first.");
13184
14101
  }
14102
+ // Method-specific login() always operates on the Phantom provider (which then
14103
+ // bridges to Privy for 'email'). If a prior email swap committed Privy, re-anchor
14104
+ // on the saved Phantom provider before delegating so the `method` directive lands
14105
+ // on Phantom. (Cancelled swaps already restore Phantom via the staged proxy; this
14106
+ // covers the post-success case.) No-op for non-Phantom apps (no saved provider).
14107
+ if ((options === null || options === void 0 ? void 0 : options.method) && currentAuthMethod !== 'phantom' && savedPhantomProvider) {
14108
+ await restoreSavedPhantomProvider();
14109
+ }
13185
14110
  // Forward per-call overrides to providers that support them (duck-typed to stay
13186
14111
  // lazy-load safe — no direct import of PhantomWalletProvider here).
13187
14112
  // Always call (even with null) so previous overrides are cleared when this
@@ -13341,7 +14266,7 @@ function useAuth() {
13341
14266
  // Provide a fallback so server render doesn't break
13342
14267
  if (isSSR) {
13343
14268
  return {
13344
- login: async () => undefined,
14269
+ login: async (_options) => undefined,
13345
14270
  logout: async () => undefined,
13346
14271
  loading: true,
13347
14272
  user: null,
@@ -13361,10 +14286,10 @@ function useAuth() {
13361
14286
  return () => {
13362
14287
  };
13363
14288
  }, []);
13364
- const login$1 = async () => {
14289
+ const login$1 = async (options) => {
13365
14290
  try {
13366
14291
  setLoading(true);
13367
- const user = await login();
14292
+ const user = await login(options);
13368
14293
  setUser(user);
13369
14294
  }
13370
14295
  catch (error) {
@@ -15901,7 +16826,7 @@ async function loadDependencies() {
15901
16826
  const [reactModule, reactDomModule, phantomModule] = await Promise.all([
15902
16827
  import('react'),
15903
16828
  import('react-dom/client'),
15904
- Promise.resolve().then(function () { return require('./index-CqpErQap.js'); })
16829
+ Promise.resolve().then(function () { return require('./index-YWtjwOnI.js'); })
15905
16830
  ]);
15906
16831
  // Extract default export from ESM module namespace
15907
16832
  // Dynamic import() returns { default: Module, ...exports }, not the module directly
@@ -15921,7 +16846,15 @@ class PhantomWalletProvider {
15921
16846
  this.containerElement = null;
15922
16847
  this.root = null;
15923
16848
  this.phantomMethods = null;
16849
+ // The FULL set registered with the Phantom SDK (so login({ method:'google'|'apple' })
16850
+ // can connect directly even when those buttons are hidden from the default modal).
15924
16851
  this.resolvedProviders = ['injected'];
16852
+ // What the DEFAULT chooser modal renders as buttons (social hidden when the Privy
16853
+ // email fallback owns vanilla email). Subset of resolvedProviders.
16854
+ this.modalProviders = ['injected'];
16855
+ // True only while a `login({ method: 'wallet' })` modal is open — suppresses the
16856
+ // social + email buttons so wallet mode shows wallets only.
16857
+ this.modalWalletOnly = false;
15925
16858
  this.pendingLogin = null;
15926
16859
  this.loginInProgress = false;
15927
16860
  this.autoLoginInProgress = false;
@@ -15978,10 +16911,15 @@ class PhantomWalletProvider {
15978
16911
  else {
15979
16912
  this.resolvedProviders = ['injected'];
15980
16913
  }
15981
- // When Privy handles social logins, strip them from Phantom
15982
- if (this.config.enablePrivyFallback) {
15983
- this.resolvedProviders = this.resolvedProviders.filter(p => p !== 'google' && p !== 'apple');
15984
- }
16914
+ // resolvedProviders stays the FULL set (registered with the Phantom SDK), so a
16915
+ // direct login({ method: 'google'|'apple' }) can connect even when the default
16916
+ // modal hides those buttons. modalProviders is what the DEFAULT chooser renders:
16917
+ // when the Privy email fallback owns vanilla email we hide social from the
16918
+ // default modal — but it remains registered above. (Apps with their own auth UI
16919
+ // use login({ method }) and bypass the default modal entirely.)
16920
+ this.modalProviders = this.config.enablePrivyFallback
16921
+ ? this.resolvedProviders.filter(p => p !== 'google' && p !== 'apple')
16922
+ : [...this.resolvedProviders];
15985
16923
  }
15986
16924
  /**
15987
16925
  * Patch the Phantom extension's provider to gracefully handle `phantom_getFeatures`
@@ -16083,6 +17021,52 @@ class PhantomWalletProvider {
16083
17021
  connectError,
16084
17022
  showCustomModal: () => setShowWalletModal(true),
16085
17023
  hideCustomModal: () => setShowWalletModal(false),
17024
+ // Per-method login bridge: connect a specific provider directly
17025
+ // (no modal). The connection-success effect resolves the pending
17026
+ // login. Used for login({ method: 'google' | 'apple' }).
17027
+ connectWithProvider: async (provider) => {
17028
+ walletClickedRef.current = true;
17029
+ setShowWalletModal(false);
17030
+ try {
17031
+ await connect({ provider });
17032
+ }
17033
+ catch (err) {
17034
+ // connectError effect handles failure
17035
+ }
17036
+ },
17037
+ // Per-method login bridge: jump straight to the Privy email
17038
+ // screen (mirrors the modal's email button) and resolve/reject
17039
+ // the pending login directly. Used for login({ method: 'email' }).
17040
+ switchToEmail: async () => {
17041
+ that.loginInProgress = false;
17042
+ walletClickedRef.current = true;
17043
+ setShowWalletModal(false);
17044
+ if (!that.onSwitchToPrivy) {
17045
+ if (that.pendingLogin) {
17046
+ that.pendingLogin.reject(new Error('Email login is not available'));
17047
+ that.pendingLogin = null;
17048
+ }
17049
+ return;
17050
+ }
17051
+ try {
17052
+ const privyProvider = await that.onSwitchToPrivy();
17053
+ const user = await privyProvider.login();
17054
+ if (that.pendingLogin && user) {
17055
+ that.pendingLogin.resolve(user);
17056
+ that.pendingLogin = null;
17057
+ }
17058
+ else if (that.pendingLogin) {
17059
+ that.pendingLogin.reject(new Error('User cancelled login'));
17060
+ that.pendingLogin = null;
17061
+ }
17062
+ }
17063
+ catch (error) {
17064
+ if (that.pendingLogin) {
17065
+ that.pendingLogin.reject(error);
17066
+ that.pendingLogin = null;
17067
+ }
17068
+ }
17069
+ },
16086
17070
  solana: solana && solanaHook.isAvailable ? {
16087
17071
  // Wrap methods to preserve 'this' context - direct references lose binding
16088
17072
  signMessage: (message) => solana.signMessage(message),
@@ -16441,8 +17425,9 @@ class PhantomWalletProvider {
16441
17425
  boxSizing: 'border-box',
16442
17426
  });
16443
17427
  const walletButtons = [];
16444
- // Google OAuth button — shown when 'google' is in resolved providers
16445
- if (sdkProviders.includes('google')) {
17428
+ // Google OAuth button — default chooser only (hidden in wallet-only mode),
17429
+ // gated on modalProviders (registered-but-hidden under the email fallback).
17430
+ if (!that.modalWalletOnly && that.modalProviders.includes('google')) {
16446
17431
  walletButtons.push(React$1.createElement('button', {
16447
17432
  key: 'google-oauth',
16448
17433
  style: buttonStyle('google-oauth'),
@@ -16466,8 +17451,9 @@ class PhantomWalletProvider {
16466
17451
  fill: '#EA4335',
16467
17452
  })), 'Continue with Google'));
16468
17453
  }
16469
- // Apple OAuth button — shown when 'apple' is in resolved providers
16470
- if (sdkProviders.includes('apple')) {
17454
+ // Apple OAuth button — default chooser only (hidden in wallet-only mode),
17455
+ // gated on modalProviders (registered-but-hidden under the email fallback).
17456
+ if (!that.modalWalletOnly && that.modalProviders.includes('apple')) {
16471
17457
  walletButtons.push(React$1.createElement('button', {
16472
17458
  key: 'apple-oauth',
16473
17459
  style: buttonStyle('apple-oauth'),
@@ -16548,8 +17534,10 @@ class PhantomWalletProvider {
16548
17534
  stroke: textColor, strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', fill: 'none',
16549
17535
  })), 'Connect Mobile Wallet'));
16550
17536
  }
16551
- // Email button — only when Privy fallback is enabled
16552
- if (that.config.enablePrivyFallback) {
17537
+ // Email button — default chooser only (hidden in wallet-only mode), and
17538
+ // only when the Privy email fallback owns the default modal's email button.
17539
+ // (login({ method:'email' }) bridges to Privy directly, independent of this.)
17540
+ if (!that.modalWalletOnly && that.config.enablePrivyFallback) {
16553
17541
  walletButtons.push(React$1.createElement('button', {
16554
17542
  key: 'email-login',
16555
17543
  style: emailButtonStyle('email-login'),
@@ -16845,10 +17833,29 @@ class PhantomWalletProvider {
16845
17833
  });
16846
17834
  }
16847
17835
  const loginPromise = new Promise((resolve, reject) => {
17836
+ var _a;
16848
17837
  this.pendingLogin = { resolve, reject };
16849
17838
  this.loginInProgress = true;
16850
- // Always use our custom wallet modal
16851
- this.phantomMethods.showCustomModal();
17839
+ // Drive the requested flow. pendingLogin + loginInProgress are already set
17840
+ // above, so the connection-success effect (google/apple/wallet) and the
17841
+ // email bridge resolve THIS promise. A method-specific call goes straight to
17842
+ // the matching connect/bridge; no method → the full chooser modal.
17843
+ const method = (_a = this.loginOverrides) === null || _a === void 0 ? void 0 : _a.method;
17844
+ if (method === 'google' || method === 'apple') {
17845
+ void this.phantomMethods.connectWithProvider(method);
17846
+ }
17847
+ else if (method === 'email') {
17848
+ void this.phantomMethods.switchToEmail();
17849
+ }
17850
+ else if (method === 'wallet') {
17851
+ // Wallet-only modal: wallets shown, social + email hidden.
17852
+ this.modalWalletOnly = true;
17853
+ this.phantomMethods.showCustomModal();
17854
+ }
17855
+ else {
17856
+ this.modalWalletOnly = false;
17857
+ this.phantomMethods.showCustomModal();
17858
+ }
16852
17859
  // Safety timeout
16853
17860
  setTimeout(() => {
16854
17861
  if (this.pendingLogin) {
@@ -16860,6 +17867,7 @@ class PhantomWalletProvider {
16860
17867
  });
16861
17868
  return loginPromise.finally(() => {
16862
17869
  this.loginOverrides = null;
17870
+ this.modalWalletOnly = false;
16863
17871
  });
16864
17872
  }
16865
17873
  async restoreSession() {
@@ -22041,7 +23049,7 @@ async function registerMobileWalletAdapter(config) {
22041
23049
  if (typeof window === 'undefined')
22042
23050
  return;
22043
23051
  try {
22044
- const walletStandardMobile = await Promise.resolve().then(function () { return require('./index.browser-BltIkY00.js'); });
23052
+ const walletStandardMobile = await Promise.resolve().then(function () { return require('./index.browser-DC1jUfJf.js'); });
22045
23053
  const registerMwa = walletStandardMobile.registerMwa || ((_a = walletStandardMobile.default) === null || _a === void 0 ? void 0 : _a.registerMwa);
22046
23054
  if (!registerMwa) {
22047
23055
  console.warn('[SolanaMobileWallet] registerMwa not found in @solana-mobile/wallet-standard-mobile');
@@ -22180,7 +23188,7 @@ class SolanaMobileWalletProvider {
22180
23188
  async ensureWallet() {
22181
23189
  if (this.wallet)
22182
23190
  return this.wallet;
22183
- const mod = await Promise.resolve().then(function () { return require('./index.browser-BltIkY00.js'); });
23191
+ const mod = await Promise.resolve().then(function () { return require('./index.browser-DC1jUfJf.js'); });
22184
23192
  const chain = mapChainToWalletStandard(this.cluster);
22185
23193
  this.wallet = new mod.LocalSolanaMobileWalletAdapterWallet({
22186
23194
  appIdentity: this.appIdentity,
@@ -23380,6 +24388,7 @@ exports.PhantomWalletProvider = PhantomWalletProvider;
23380
24388
  exports.PrivyExpoProvider = PrivyExpoProvider;
23381
24389
  exports.PrivyWalletProvider = PrivyWalletProvider;
23382
24390
  exports.ReactNativeSessionManager = ReactNativeSessionManager;
24391
+ exports.RealtimeStore = RealtimeStore;
23383
24392
  exports.ServerSessionManager = ServerSessionManager;
23384
24393
  exports.SolanaMobileWalletProvider = SolanaMobileWalletProvider;
23385
24394
  exports.WebSessionManager = WebSessionManager;
@@ -23407,6 +24416,7 @@ exports.getFiles = getFiles;
23407
24416
  exports.getIdToken = getIdToken;
23408
24417
  exports.getMany = getMany;
23409
24418
  exports.getPlatform = getPlatform;
24419
+ exports.getRealtimeStore = getRealtimeStore;
23410
24420
  exports.hasActiveConnection = hasActiveConnection;
23411
24421
  exports.init = init;
23412
24422
  exports.isMobileWalletAvailable = isMobileWalletAvailable;
@@ -23417,6 +24427,7 @@ exports.onAuthStateChanged = onAuthStateChanged;
23417
24427
  exports.reconnectWithNewAuth = reconnectWithNewAuth;
23418
24428
  exports.refreshSession = refreshSession;
23419
24429
  exports.registerMobileWalletAdapter = registerMobileWalletAdapter;
24430
+ exports.resetRealtimeStore = resetRealtimeStore;
23420
24431
  exports.runExpression = runExpression;
23421
24432
  exports.runExpressionMany = runExpressionMany;
23422
24433
  exports.runQuery = runQuery;
@@ -23436,4 +24447,4 @@ exports.wsGet = wsGet;
23436
24447
  exports.wsGetMany = wsGetMany;
23437
24448
  exports.wsQuery = wsQuery;
23438
24449
  exports.wsSet = wsSet;
23439
- //# sourceMappingURL=index-Eb2uyla3.js.map
24450
+ //# sourceMappingURL=index-Qooqzt4n.js.map