@pooflabs/web 0.0.91 → 0.0.92-rc2

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-CzVM_hix.esm.js → index-6iINLG88.esm.js} +1058 -47
  6. package/dist/index-6iINLG88.esm.js.map +1 -0
  7. package/dist/index-CWD6VMLX.esm.js +6 -0
  8. package/dist/index-CWD6VMLX.esm.js.map +1 -0
  9. package/dist/{index-C65AsRIA.esm.js → index-Dg3y_pRI.esm.js} +14 -364
  10. package/dist/index-Dg3y_pRI.esm.js.map +1 -0
  11. package/dist/{index-Eb2uyla3.js → index-GeHpO_Ud.js} +1060 -46
  12. package/dist/index-GeHpO_Ud.js.map +1 -0
  13. package/dist/{index-DOYZMq5N.esm.js → index-dEUHmDSl.esm.js} +13 -363
  14. package/dist/index-dEUHmDSl.esm.js.map +1 -0
  15. package/dist/{index-BZ_W-sR0.js → index-eFFOC8cE.js} +2 -2
  16. package/dist/index-eFFOC8cE.js.map +1 -0
  17. package/dist/{index-lt7AQ69U.js → index-m53xb2JA.js} +14 -364
  18. package/dist/index-m53xb2JA.js.map +1 -0
  19. package/dist/{index-CqpErQap.js → index-vi52dsn6.js} +13 -363
  20. package/dist/index-vi52dsn6.js.map +1 -0
  21. package/dist/{index.browser-BltIkY00.js → index.browser-B1Zbn8LC.js} +2 -2
  22. package/dist/{index.browser-BltIkY00.js.map → index.browser-B1Zbn8LC.js.map} +1 -1
  23. package/dist/{index.browser-C81ODkjH.js → index.browser-D6XPhu6r.js} +2 -2
  24. package/dist/{index.browser-C81ODkjH.js.map → index.browser-D6XPhu6r.js.map} +1 -1
  25. package/dist/{index.browser-D1uNEi4e.esm.js → index.browser-RwalL3F4.esm.js} +2 -2
  26. package/dist/{index.browser-D1uNEi4e.esm.js.map → index.browser-RwalL3F4.esm.js.map} +1 -1
  27. package/dist/{index.browser-zlnKOqcR.esm.js → index.browser-wZkBhwtI.esm.js} +2 -2
  28. package/dist/{index.browser-zlnKOqcR.esm.js.map → index.browser-wZkBhwtI.esm.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-DYddSSgT.js → index.native-Bjx72Wnj.js} +950 -28
  33. package/dist/index.native-Bjx72Wnj.js.map +1 -0
  34. package/dist/{index.native-Czk4F68O.esm.js → index.native-Dd783Qva.esm.js} +948 -29
  35. package/dist/index.native-Dd783Qva.esm.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-DR5qAGcS.js → phantom-wallet-provider-DUVViY9g.js} +99 -16
  40. package/dist/phantom-wallet-provider-DUVViY9g.js.map +1 -0
  41. package/dist/{phantom-wallet-provider-0tX0Zs72.esm.js → phantom-wallet-provider-Dloixy8s.esm.js} +99 -16
  42. package/dist/phantom-wallet-provider-Dloixy8s.esm.js.map +1 -0
  43. package/dist/{privy-wallet-provider-ChMUFAEp.esm.js → privy-wallet-provider-BQbIRyXF.esm.js} +18 -9
  44. package/dist/privy-wallet-provider-BQbIRyXF.esm.js.map +1 -0
  45. package/dist/{privy-wallet-provider-BqTsdJPZ.js → privy-wallet-provider-CEdQMBT2.js} +18 -9
  46. package/dist/privy-wallet-provider-CEdQMBT2.js.map +1 -0
  47. package/dist/{solana-mobile-wallet-provider-Cq2H4eoW.esm.js → solana-mobile-wallet-provider-ARvDWjP7.esm.js} +4 -4
  48. package/dist/{solana-mobile-wallet-provider-Cq2H4eoW.esm.js.map → solana-mobile-wallet-provider-ARvDWjP7.esm.js.map} +1 -1
  49. package/dist/{solana-mobile-wallet-provider-DiXPSf8n.js → solana-mobile-wallet-provider-CgQWQ7Sk.js} +4 -4
  50. package/dist/{solana-mobile-wallet-provider-DiXPSf8n.js.map → solana-mobile-wallet-provider-CgQWQ7Sk.js.map} +1 -1
  51. package/package.json +1 -1
  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
@@ -10362,9 +10362,40 @@ async function get(path, opts = {}) {
10362
10362
  }
10363
10363
  // Create a new request promise and store it
10364
10364
  const requestPromise = (async () => {
10365
+ var _a;
10365
10366
  try {
10366
- // Cache miss or bypass - proceed with API request
10367
+ // For realtime chains, prefer WebSocket reads (lower latency, already connected)
10368
+ const config = await getConfig();
10367
10369
  const pathIsDocument = normalizedPath.split("/").length % 2 === 0;
10370
+ if (((_a = config.chain) === null || _a === void 0 ? void 0 : _a.startsWith('realtime_')) && !config.isServer && !opts.prompt && !opts.shape && !opts.cursor) {
10371
+ try {
10372
+ const { wsGet, wsQuery, hasActiveConnection } = await Promise.resolve().then(function () { return subscriptionV2; });
10373
+ if (hasActiveConnection()) {
10374
+ if (pathIsDocument) {
10375
+ const wsResult = await wsGet(normalizedPath);
10376
+ const responseData = wsResult;
10377
+ if (!opts.bypassCache) {
10378
+ getCache[cacheKey] = { data: responseData, expiresAt: now + GET_CACHE_TTL };
10379
+ }
10380
+ return responseData;
10381
+ }
10382
+ else if (!opts.limit) {
10383
+ const wsResult = await wsQuery(normalizedPath, {
10384
+ filter: undefined,
10385
+ sort: undefined,
10386
+ includeSubPaths: opts.includeSubPaths,
10387
+ });
10388
+ const responseData = wsResult;
10389
+ if (!opts.bypassCache) {
10390
+ getCache[cacheKey] = { data: responseData, expiresAt: now + GET_CACHE_TTL };
10391
+ }
10392
+ return responseData;
10393
+ }
10394
+ }
10395
+ }
10396
+ catch ( /* fall through to HTTP */_b) { /* fall through to HTTP */ }
10397
+ }
10398
+ // Cache miss or bypass - proceed with HTTP API request
10368
10399
  let response;
10369
10400
  // Build common query params
10370
10401
  const includeSubPathsParam = opts.includeSubPaths ? '&includeSubPaths=true' : '';
@@ -10373,7 +10404,6 @@ async function get(path, opts = {}) {
10373
10404
  const cursorParam = opts.cursor ? `&cursor=${encodeURIComponent(opts.cursor)}` : '';
10374
10405
  if (pathIsDocument) {
10375
10406
  const itemId = encodeURIComponent(normalizedPath);
10376
- // For documents, query params go after the path
10377
10407
  const queryParams = [includeSubPathsParam, shapeParam].filter(p => p).join('');
10378
10408
  const apiPath = queryParams ? `items/${itemId}?${queryParams.substring(1)}` : `items/${itemId}`;
10379
10409
  response = await makeApiRequest('GET', apiPath, null, opts._overrides);
@@ -10568,7 +10598,7 @@ async function set(path, document, options) {
10568
10598
  return result;
10569
10599
  }
10570
10600
  async function setMany(many, options) {
10571
- var _a, _b, _c, _d, _e, _f, _g;
10601
+ var _a, _b, _c, _d, _e, _f, _g, _h;
10572
10602
  // Returns the data that was set, or undefined if the document was already set.
10573
10603
  try {
10574
10604
  const config = await getConfig();
@@ -10605,13 +10635,28 @@ async function setMany(many, options) {
10605
10635
  }
10606
10636
  let setResponse;
10607
10637
  try {
10608
- setResponse = await makeApiRequest('PUT', `items`, { documents }, options === null || options === void 0 ? void 0 : options._overrides);
10638
+ // For realtime chains, prefer WebSocket if a connection is active (lower latency)
10639
+ const useWs = ((_c = config.chain) === null || _c === void 0 ? void 0 : _c.startsWith('realtime_')) && !config.isServer;
10640
+ if (useWs) {
10641
+ try {
10642
+ const { wsSet, hasActiveConnection } = await Promise.resolve().then(function () { return subscriptionV2; });
10643
+ if (hasActiveConnection()) {
10644
+ const wsResult = await wsSet(documents);
10645
+ // Normalize to same shape as HTTP 200 response so downstream handling is identical
10646
+ setResponse = { data: wsResult, status: 200 };
10647
+ }
10648
+ }
10649
+ catch ( /* fall through to HTTP */_j) { /* fall through to HTTP */ }
10650
+ }
10651
+ if (!setResponse) {
10652
+ setResponse = await makeApiRequest('PUT', `items`, { documents }, options === null || options === void 0 ? void 0 : options._overrides);
10653
+ }
10609
10654
  }
10610
10655
  catch (error) {
10611
10656
  if ((error === null || error === void 0 ? void 0 : error.statusCode) === 402 && (error === null || error === void 0 ? void 0 : error.error) === 'INSUFFICIENT_BALANCE') {
10612
- const deficitLamports = Number((_c = error.deficitLamports) !== null && _c !== void 0 ? _c : 0);
10613
- const deficitSol = Number((_d = error.deficitSol) !== null && _d !== void 0 ? _d : deficitLamports / 1000000000);
10614
- 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);
10657
+ const deficitLamports = Number((_d = error.deficitLamports) !== null && _d !== void 0 ? _d : 0);
10658
+ const deficitSol = Number((_e = error.deficitSol) !== null && _e !== void 0 ? _e : deficitLamports / 1000000000);
10659
+ 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);
10615
10660
  }
10616
10661
  throw error;
10617
10662
  }
@@ -10655,7 +10700,7 @@ async function setMany(many, options) {
10655
10700
  else if (setResponse.data &&
10656
10701
  typeof setResponse.data === 'object' &&
10657
10702
  setResponse.data.success === true) {
10658
- const _h = setResponse.data, { success: _success } = _h, rest = __rest(_h, ["success"]);
10703
+ const _k = setResponse.data, { success: _success } = _k, rest = __rest(_k, ["success"]);
10659
10704
  return Object.assign(Object.assign(Object.assign({}, documents.map(d => d.document)), rest), { transactionId: null });
10660
10705
  }
10661
10706
  else {
@@ -11702,6 +11747,21 @@ async function wsGetMany(paths) {
11702
11747
  }));
11703
11748
  }
11704
11749
 
11750
+ var subscriptionV2 = /*#__PURE__*/Object.freeze({
11751
+ __proto__: null,
11752
+ clearCacheV2: clearCacheV2,
11753
+ closeAllSubscriptionsV2: closeAllSubscriptionsV2,
11754
+ getCachedDataV2: getCachedDataV2,
11755
+ hasActiveConnection: hasActiveConnection,
11756
+ reconnectWithNewAuthV2: reconnectWithNewAuthV2,
11757
+ subscribeV2: subscribeV2,
11758
+ wsDelete: wsDelete,
11759
+ wsGet: wsGet,
11760
+ wsGetMany: wsGetMany,
11761
+ wsQuery: wsQuery,
11762
+ wsSet: wsSet
11763
+ });
11764
+
11705
11765
  /**
11706
11766
  * WebSocket Subscription Module
11707
11767
  *
@@ -11955,6 +12015,786 @@ class ReactNativeSessionManager {
11955
12015
  }
11956
12016
  ReactNativeSessionManager.TAROBASE_SESSION_STORAGE_KEY = "tarobase_session_storage";
11957
12017
 
12018
+ // ---------------------------------------------------------------------------
12019
+ // realtime-store.ts — Client-side state manager for realtime apps.
12020
+ //
12021
+ // Manages: WS connection, in-memory state, IDB persistence, optimistic
12022
+ // writes, delta accumulation, loading states, ephemeral/durable tiers.
12023
+ // ---------------------------------------------------------------------------
12024
+ // ---------------------------------------------------------------------------
12025
+ // IDB helpers (lazy-loaded, non-blocking)
12026
+ // ---------------------------------------------------------------------------
12027
+ const IDB_NAME = 'tarobase-realtime';
12028
+ const IDB_STORE = 'subscriptions';
12029
+ const IDB_VERSION = 1;
12030
+ let idbPromise = null;
12031
+ function getIDB() {
12032
+ if (idbPromise)
12033
+ return idbPromise;
12034
+ if (typeof indexedDB === 'undefined') {
12035
+ return Promise.reject(new Error('IndexedDB not available'));
12036
+ }
12037
+ idbPromise = new Promise((resolve, reject) => {
12038
+ const req = indexedDB.open(IDB_NAME, IDB_VERSION);
12039
+ req.onupgradeneeded = () => {
12040
+ const db = req.result;
12041
+ if (!db.objectStoreNames.contains(IDB_STORE)) {
12042
+ db.createObjectStore(IDB_STORE);
12043
+ }
12044
+ };
12045
+ req.onsuccess = () => resolve(req.result);
12046
+ req.onerror = () => reject(req.error);
12047
+ });
12048
+ return idbPromise;
12049
+ }
12050
+ async function idbGet(key) {
12051
+ try {
12052
+ const db = await getIDB();
12053
+ return new Promise((resolve) => {
12054
+ const tx = db.transaction(IDB_STORE, 'readonly');
12055
+ const store = tx.objectStore(IDB_STORE);
12056
+ const req = store.get(key);
12057
+ req.onsuccess = () => { var _a; return resolve((_a = req.result) !== null && _a !== void 0 ? _a : null); };
12058
+ req.onerror = () => resolve(null);
12059
+ });
12060
+ }
12061
+ catch (_a) {
12062
+ return null;
12063
+ }
12064
+ }
12065
+ async function idbSet(key, value) {
12066
+ try {
12067
+ const db = await getIDB();
12068
+ return new Promise((resolve) => {
12069
+ const tx = db.transaction(IDB_STORE, 'readwrite');
12070
+ const store = tx.objectStore(IDB_STORE);
12071
+ store.put(value, key);
12072
+ tx.oncomplete = () => resolve();
12073
+ tx.onerror = () => resolve();
12074
+ });
12075
+ }
12076
+ catch (_a) {
12077
+ // Best-effort persistence
12078
+ }
12079
+ }
12080
+ // ---------------------------------------------------------------------------
12081
+ // RealtimeStore
12082
+ // ---------------------------------------------------------------------------
12083
+ let nextRequestId = 1;
12084
+ class RealtimeStore {
12085
+ constructor() {
12086
+ this.ws = null;
12087
+ this.wsUrl = '';
12088
+ this.appId = '';
12089
+ this.subscriptions = new Map();
12090
+ this.pendingRequests = new Map();
12091
+ this.connectPromise = null;
12092
+ this.reconnectTimer = null;
12093
+ this.reconnectDelay = 1000;
12094
+ this.maxReconnectDelay = 30000;
12095
+ this.idbFlushTimer = null;
12096
+ this.idbDirtyKeys = new Set();
12097
+ this.closed = false;
12098
+ this.authToken = null;
12099
+ }
12100
+ // -----------------------------------------------------------------------
12101
+ // Initialization
12102
+ // -----------------------------------------------------------------------
12103
+ async init() {
12104
+ var _a, _b;
12105
+ const config = await getConfig();
12106
+ this.appId = config.appId;
12107
+ this.wsUrl = config.wsApiUrl;
12108
+ if (config.authProvider) {
12109
+ try {
12110
+ const headers = await ((_b = (_a = config.authProvider).getAuthHeaders) === null || _b === void 0 ? void 0 : _b.call(_a));
12111
+ if (headers === null || headers === void 0 ? void 0 : headers.Authorization) {
12112
+ this.authToken = headers.Authorization.replace('Bearer ', '');
12113
+ }
12114
+ }
12115
+ catch ( /* no auth */_c) { /* no auth */ }
12116
+ }
12117
+ }
12118
+ // -----------------------------------------------------------------------
12119
+ // WebSocket connection
12120
+ // -----------------------------------------------------------------------
12121
+ async ensureConnected() {
12122
+ var _a;
12123
+ if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN)
12124
+ return;
12125
+ if (this.connectPromise)
12126
+ return this.connectPromise;
12127
+ this.connectPromise = this.connect();
12128
+ return this.connectPromise;
12129
+ }
12130
+ connect() {
12131
+ return new Promise((resolve, reject) => {
12132
+ if (this.closed) {
12133
+ reject(new Error('Store closed'));
12134
+ return;
12135
+ }
12136
+ const params = new URLSearchParams();
12137
+ params.set('apiKey', this.appId);
12138
+ // Auth token sent via subprotocol to avoid leaking in URL/logs
12139
+ const url = `${this.wsUrl}?${params.toString()}`;
12140
+ const protocols = this.authToken ? [`bearer-${this.authToken}`] : undefined;
12141
+ const ws = protocols ? new WebSocket(url, protocols) : new WebSocket(url);
12142
+ this.ws = ws;
12143
+ const onOpen = () => {
12144
+ ws.removeEventListener('error', onError);
12145
+ this.reconnectDelay = 1000;
12146
+ this.connectPromise = null;
12147
+ this.resubscribeAll();
12148
+ resolve();
12149
+ };
12150
+ const onError = (e) => {
12151
+ ws.removeEventListener('open', onOpen);
12152
+ this.connectPromise = null;
12153
+ reject(new Error('WebSocket connection failed'));
12154
+ };
12155
+ ws.addEventListener('open', onOpen, { once: true });
12156
+ ws.addEventListener('error', onError, { once: true });
12157
+ ws.addEventListener('message', (event) => {
12158
+ this.handleMessage(event.data);
12159
+ });
12160
+ ws.addEventListener('close', () => {
12161
+ this.ws = null;
12162
+ this.connectPromise = null;
12163
+ this.rejectAllPending('WebSocket closed');
12164
+ this.setAllSubscriptionStatus('reconnecting');
12165
+ this.scheduleReconnect();
12166
+ });
12167
+ });
12168
+ }
12169
+ scheduleReconnect() {
12170
+ if (this.closed)
12171
+ return;
12172
+ if (this.reconnectTimer)
12173
+ clearTimeout(this.reconnectTimer);
12174
+ this.reconnectTimer = setTimeout(() => {
12175
+ this.ensureConnected().catch(() => {
12176
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
12177
+ this.scheduleReconnect();
12178
+ });
12179
+ }, this.reconnectDelay);
12180
+ }
12181
+ resubscribeAll() {
12182
+ for (const sub of this.subscriptions.values()) {
12183
+ this.sendSubscribe(sub);
12184
+ }
12185
+ }
12186
+ // -----------------------------------------------------------------------
12187
+ // Message handling
12188
+ // -----------------------------------------------------------------------
12189
+ handleMessage(raw) {
12190
+ const text = typeof raw === 'string' ? raw : new TextDecoder().decode(raw);
12191
+ let msg;
12192
+ try {
12193
+ msg = JSON.parse(text);
12194
+ }
12195
+ catch (_a) {
12196
+ return;
12197
+ }
12198
+ switch (msg.type) {
12199
+ case 'snapshot':
12200
+ this.handleSnapshot(msg);
12201
+ break;
12202
+ case 'delta':
12203
+ this.handleDelta(msg);
12204
+ break;
12205
+ case 'result':
12206
+ this.handleResult(msg);
12207
+ break;
12208
+ case 'error':
12209
+ this.handleError(msg);
12210
+ break;
12211
+ case 'pong':
12212
+ break;
12213
+ // v1 compat: handle legacy message types during transition
12214
+ case 'subscribed':
12215
+ this.handleSnapshot(Object.assign(Object.assign({}, msg), { type: 'snapshot', docs: msg.data }));
12216
+ break;
12217
+ case 'data':
12218
+ // Legacy full-snapshot delta — treat as snapshot replacement
12219
+ this.handleLegacyData(msg);
12220
+ break;
12221
+ case 'response':
12222
+ this.handleResult(Object.assign(Object.assign({}, msg), { type: 'result', ok: msg.status === 200, doc: msg.data }));
12223
+ break;
12224
+ }
12225
+ }
12226
+ handleSnapshot(msg) {
12227
+ var _a, _b, _c;
12228
+ const subId = (_a = msg.id) !== null && _a !== void 0 ? _a : msg.subscriptionId;
12229
+ if (!subId)
12230
+ return;
12231
+ const sub = this.findSubscriptionById(subId);
12232
+ if (!sub)
12233
+ return;
12234
+ const docs = (_c = (_b = msg.docs) !== null && _b !== void 0 ? _b : msg.data) !== null && _c !== void 0 ? _c : [];
12235
+ const docsArray = Array.isArray(docs) ? docs : [docs];
12236
+ sub.docs.clear();
12237
+ for (const doc of docsArray) {
12238
+ if (doc && doc._id) {
12239
+ sub.docs.set(doc._id, doc);
12240
+ }
12241
+ }
12242
+ sub.ref.current = sub.docs;
12243
+ sub.status = 'live';
12244
+ sub.isStale = false;
12245
+ sub.error = null;
12246
+ this.notifySubscription(sub);
12247
+ this.markIdbDirty(sub.path);
12248
+ }
12249
+ handleDelta(msg) {
12250
+ var _a, _b;
12251
+ const subId = (_a = msg.id) !== null && _a !== void 0 ? _a : msg.subscriptionId;
12252
+ if (!subId)
12253
+ return;
12254
+ const sub = this.findSubscriptionById(subId);
12255
+ if (!sub)
12256
+ return;
12257
+ if (sub.tier === 'ephemeral') {
12258
+ // Ephemeral: just overwrite, no accumulation logic
12259
+ if (msg.change === 'removed' && msg.docId) {
12260
+ sub.docs.delete(msg.docId);
12261
+ }
12262
+ else if (msg.doc && msg.doc._id) {
12263
+ sub.docs.set(msg.doc._id, msg.doc);
12264
+ }
12265
+ sub.ref.current = sub.docs;
12266
+ if (sub.options.mode !== 'ref') {
12267
+ this.notifySubscription(sub);
12268
+ }
12269
+ return;
12270
+ }
12271
+ // Durable/checkpointed: full delta handling
12272
+ switch (msg.change) {
12273
+ case 'added':
12274
+ case 'modified':
12275
+ if (msg.doc && msg.doc._id) {
12276
+ sub.docs.set(msg.doc._id, msg.doc);
12277
+ }
12278
+ break;
12279
+ case 'removed':
12280
+ if (msg.docId) {
12281
+ sub.docs.delete(msg.docId);
12282
+ }
12283
+ else if ((_b = msg.doc) === null || _b === void 0 ? void 0 : _b._id) {
12284
+ sub.docs.delete(msg.doc._id);
12285
+ }
12286
+ break;
12287
+ }
12288
+ sub.ref.current = sub.docs;
12289
+ this.notifySubscription(sub);
12290
+ this.markIdbDirty(sub.path);
12291
+ }
12292
+ handleLegacyData(msg) {
12293
+ // Legacy v1 format: 'data' message with full snapshot or single doc
12294
+ const subId = msg.subscriptionId;
12295
+ if (!subId)
12296
+ return;
12297
+ const sub = this.findSubscriptionById(subId);
12298
+ if (!sub)
12299
+ return;
12300
+ if (Array.isArray(msg.data)) {
12301
+ // Full snapshot replacement
12302
+ sub.docs.clear();
12303
+ for (const doc of msg.data) {
12304
+ if (doc && doc._id)
12305
+ sub.docs.set(doc._id, doc);
12306
+ }
12307
+ }
12308
+ else if (msg.data && msg.data._id) {
12309
+ // Single doc update
12310
+ sub.docs.set(msg.data._id, msg.data);
12311
+ }
12312
+ else if (msg.data === null) ;
12313
+ sub.ref.current = sub.docs;
12314
+ sub.status = 'live';
12315
+ sub.isStale = false;
12316
+ this.notifySubscription(sub);
12317
+ this.markIdbDirty(sub.path);
12318
+ }
12319
+ handleResult(msg) {
12320
+ var _a, _b, _c, _d;
12321
+ const requestId = msg.requestId;
12322
+ if (!requestId)
12323
+ return;
12324
+ const pending = this.pendingRequests.get(requestId);
12325
+ if (!pending)
12326
+ return;
12327
+ this.pendingRequests.delete(requestId);
12328
+ clearTimeout(pending.timeout);
12329
+ const ok = (_a = msg.ok) !== null && _a !== void 0 ? _a : (msg.status === 200);
12330
+ if (ok) {
12331
+ pending.resolve((_c = (_b = msg.doc) !== null && _b !== void 0 ? _b : msg.data) !== null && _c !== void 0 ? _c : true);
12332
+ }
12333
+ else {
12334
+ pending.reject(new Error((_d = msg.error) !== null && _d !== void 0 ? _d : 'Operation failed'));
12335
+ }
12336
+ }
12337
+ handleError(msg) {
12338
+ var _a;
12339
+ const requestId = msg.requestId;
12340
+ if (requestId) {
12341
+ const pending = this.pendingRequests.get(requestId);
12342
+ if (pending) {
12343
+ this.pendingRequests.delete(requestId);
12344
+ clearTimeout(pending.timeout);
12345
+ pending.reject(new Error((_a = msg.message) !== null && _a !== void 0 ? _a : 'Server error'));
12346
+ }
12347
+ }
12348
+ }
12349
+ // -----------------------------------------------------------------------
12350
+ // Subscribe
12351
+ // -----------------------------------------------------------------------
12352
+ async subscribe(path, opts = {}) {
12353
+ var _a;
12354
+ const tier = (_a = opts.tier) !== null && _a !== void 0 ? _a : 'durable';
12355
+ const subKey = this.getSubKey(path, opts);
12356
+ let sub = this.subscriptions.get(subKey);
12357
+ if (sub) {
12358
+ // Existing subscription — add callback
12359
+ if (opts.onData)
12360
+ sub.callbacks.add(opts.onData);
12361
+ if (opts.onState)
12362
+ sub.stateCallbacks.add(opts.onState);
12363
+ // Immediately deliver current state
12364
+ if (opts.onData && sub.docs.size > 0) {
12365
+ opts.onData(this.docsToArray(sub));
12366
+ }
12367
+ if (opts.onState) {
12368
+ opts.onState(this.getState(sub));
12369
+ }
12370
+ return this.createUnsubscribe(subKey, opts.onData, opts.onState);
12371
+ }
12372
+ // New subscription
12373
+ const subId = `sub_${nextRequestId++}`;
12374
+ sub = {
12375
+ id: subId,
12376
+ path,
12377
+ tier,
12378
+ options: opts,
12379
+ docs: new Map(),
12380
+ status: 'idle',
12381
+ isStale: false,
12382
+ error: null,
12383
+ callbacks: new Set(opts.onData ? [opts.onData] : []),
12384
+ stateCallbacks: new Set(opts.onState ? [opts.onState] : []),
12385
+ ref: { current: new Map() },
12386
+ };
12387
+ this.subscriptions.set(subKey, sub);
12388
+ // Step 1: Load from IDB (durable/checkpointed only)
12389
+ if (tier !== 'ephemeral') {
12390
+ const cached = await idbGet(this.idbKey(path));
12391
+ if (cached && Array.isArray(cached)) {
12392
+ for (const doc of cached) {
12393
+ if (doc && doc._id)
12394
+ sub.docs.set(doc._id, doc);
12395
+ }
12396
+ sub.ref.current = sub.docs;
12397
+ sub.status = 'cached';
12398
+ sub.isStale = true;
12399
+ this.notifySubscription(sub);
12400
+ }
12401
+ }
12402
+ // Step 2: Connect and subscribe via WS
12403
+ sub.status = sub.docs.size > 0 ? 'cached' : 'loading';
12404
+ this.notifyState(sub);
12405
+ try {
12406
+ await this.ensureConnected();
12407
+ this.sendSubscribe(sub);
12408
+ }
12409
+ catch (_b) {
12410
+ sub.status = 'error';
12411
+ sub.error = new Error('Connection failed');
12412
+ this.notifyState(sub);
12413
+ }
12414
+ return this.createUnsubscribe(subKey, opts.onData, opts.onState);
12415
+ }
12416
+ getRef(path, opts = {}) {
12417
+ var _a;
12418
+ const subKey = this.getSubKey(path, opts);
12419
+ const sub = this.subscriptions.get(subKey);
12420
+ if (sub)
12421
+ return sub.ref;
12422
+ // Auto-subscribe in ref mode
12423
+ const ref = { current: new Map() };
12424
+ this.subscribe(path, Object.assign(Object.assign({}, opts), { mode: 'ref', tier: 'ephemeral' })).catch(() => { });
12425
+ const newSub = this.subscriptions.get(this.getSubKey(path, Object.assign(Object.assign({}, opts), { tier: 'ephemeral' })));
12426
+ return (_a = newSub === null || newSub === void 0 ? void 0 : newSub.ref) !== null && _a !== void 0 ? _a : ref;
12427
+ }
12428
+ // -----------------------------------------------------------------------
12429
+ // CRUD operations
12430
+ // -----------------------------------------------------------------------
12431
+ async set(path, doc) {
12432
+ var _a;
12433
+ await this.ensureConnected();
12434
+ // Resolve operations (Increment, Time.Now) client-side for optimistic update
12435
+ const resolvedDoc = this.resolveOperations(doc, path);
12436
+ // Optimistic update: apply to local state immediately
12437
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12438
+ const collectionPath = this.getCollectionPath(normalizedPath);
12439
+ const optimisticDoc = Object.assign(Object.assign({ _id: normalizedPath, pathId: normalizedPath }, resolvedDoc), { tarobase_updated_at: Date.now() });
12440
+ const sub = this.findSubscriptionByPath(collectionPath);
12441
+ let prevDoc = null;
12442
+ if (sub) {
12443
+ prevDoc = (_a = sub.docs.get(normalizedPath)) !== null && _a !== void 0 ? _a : null;
12444
+ sub.docs.set(normalizedPath, optimisticDoc);
12445
+ sub.ref.current = sub.docs;
12446
+ this.notifySubscription(sub);
12447
+ }
12448
+ // Send to server
12449
+ const requestId = `r_${nextRequestId++}`;
12450
+ try {
12451
+ const result = await this.sendRequest(requestId, {
12452
+ type: 'set',
12453
+ requestId,
12454
+ documents: [{ destinationPath: normalizedPath, document: doc }],
12455
+ });
12456
+ // Replace optimistic doc with server-confirmed version
12457
+ if (sub && result && typeof result === 'object') {
12458
+ const serverDoc = Array.isArray(result) ? result[0] : result;
12459
+ if (serverDoc && serverDoc._id) {
12460
+ sub.docs.set(serverDoc._id, serverDoc);
12461
+ sub.ref.current = sub.docs;
12462
+ this.notifySubscription(sub);
12463
+ this.markIdbDirty(collectionPath);
12464
+ }
12465
+ }
12466
+ return Array.isArray(result) ? result[0] : result;
12467
+ }
12468
+ catch (err) {
12469
+ // Revert optimistic update
12470
+ if (sub) {
12471
+ if (prevDoc) {
12472
+ sub.docs.set(normalizedPath, prevDoc);
12473
+ }
12474
+ else {
12475
+ sub.docs.delete(normalizedPath);
12476
+ }
12477
+ sub.ref.current = sub.docs;
12478
+ this.notifySubscription(sub);
12479
+ }
12480
+ throw err;
12481
+ }
12482
+ }
12483
+ async get(path) {
12484
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12485
+ // Check local subscriptions first
12486
+ const collectionPath = this.getCollectionPath(normalizedPath);
12487
+ const sub = this.findSubscriptionByPath(collectionPath);
12488
+ if (sub && sub.status === 'live') {
12489
+ const doc = sub.docs.get(normalizedPath);
12490
+ return doc !== null && doc !== void 0 ? doc : null;
12491
+ }
12492
+ // One-shot WS fetch
12493
+ await this.ensureConnected();
12494
+ const requestId = `r_${nextRequestId++}`;
12495
+ return this.sendRequest(requestId, {
12496
+ type: 'get',
12497
+ requestId,
12498
+ path: normalizedPath,
12499
+ });
12500
+ }
12501
+ async getMany(paths) {
12502
+ await this.ensureConnected();
12503
+ const normalizedPaths = paths.map(p => p.startsWith('/') ? p.slice(1) : p);
12504
+ const requestId = `r_${nextRequestId++}`;
12505
+ return this.sendRequest(requestId, {
12506
+ type: 'getMany',
12507
+ requestId,
12508
+ paths: normalizedPaths,
12509
+ });
12510
+ }
12511
+ async delete(path) {
12512
+ var _a;
12513
+ await this.ensureConnected();
12514
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12515
+ // Optimistic: remove from local state
12516
+ const collectionPath = this.getCollectionPath(normalizedPath);
12517
+ const sub = this.findSubscriptionByPath(collectionPath);
12518
+ let prevDoc = null;
12519
+ if (sub) {
12520
+ prevDoc = (_a = sub.docs.get(normalizedPath)) !== null && _a !== void 0 ? _a : null;
12521
+ sub.docs.delete(normalizedPath);
12522
+ sub.ref.current = sub.docs;
12523
+ this.notifySubscription(sub);
12524
+ }
12525
+ const requestId = `r_${nextRequestId++}`;
12526
+ try {
12527
+ await this.sendRequest(requestId, {
12528
+ type: 'delete',
12529
+ requestId,
12530
+ path: normalizedPath,
12531
+ });
12532
+ if (sub)
12533
+ this.markIdbDirty(collectionPath);
12534
+ }
12535
+ catch (err) {
12536
+ // Revert
12537
+ if (sub && prevDoc) {
12538
+ sub.docs.set(normalizedPath, prevDoc);
12539
+ sub.ref.current = sub.docs;
12540
+ this.notifySubscription(sub);
12541
+ }
12542
+ throw err;
12543
+ }
12544
+ }
12545
+ async query(path, opts) {
12546
+ await this.ensureConnected();
12547
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12548
+ const requestId = `r_${nextRequestId++}`;
12549
+ 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 } : {})));
12550
+ }
12551
+ async count(path) {
12552
+ var _a;
12553
+ await this.ensureConnected();
12554
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12555
+ const requestId = `r_${nextRequestId++}`;
12556
+ const result = await this.sendRequest(requestId, {
12557
+ type: 'count',
12558
+ requestId,
12559
+ path: normalizedPath,
12560
+ });
12561
+ return typeof result === 'number' ? result : ((_a = result === null || result === void 0 ? void 0 : result.value) !== null && _a !== void 0 ? _a : 0);
12562
+ }
12563
+ async aggregate(path, operation, opts) {
12564
+ await this.ensureConnected();
12565
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12566
+ const requestId = `r_${nextRequestId++}`;
12567
+ return this.sendRequest(requestId, Object.assign({ type: 'aggregate', requestId, path: normalizedPath, operation }, ((opts === null || opts === void 0 ? void 0 : opts.field) ? { field: opts.field } : {})));
12568
+ }
12569
+ // -----------------------------------------------------------------------
12570
+ // Helpers
12571
+ // -----------------------------------------------------------------------
12572
+ sendSubscribe(sub) {
12573
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
12574
+ return;
12575
+ const msg = {
12576
+ type: 'subscribe',
12577
+ subscriptionId: sub.id,
12578
+ path: sub.path,
12579
+ };
12580
+ if (sub.options.filter)
12581
+ msg.filter = sub.options.filter;
12582
+ if (sub.options.includeSubPaths)
12583
+ msg.includeSubPaths = true;
12584
+ if (sub.options.limit)
12585
+ msg.limit = sub.options.limit;
12586
+ if (sub.options.prompt)
12587
+ msg.prompt = sub.options.prompt;
12588
+ this.ws.send(JSON.stringify(msg));
12589
+ }
12590
+ sendRequest(requestId, msg) {
12591
+ return new Promise((resolve, reject) => {
12592
+ const timeout = setTimeout(() => {
12593
+ this.pendingRequests.delete(requestId);
12594
+ reject(new Error('Request timed out'));
12595
+ }, 30000);
12596
+ this.pendingRequests.set(requestId, { resolve, reject, timeout });
12597
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
12598
+ this.ws.send(JSON.stringify(msg));
12599
+ }
12600
+ else {
12601
+ this.pendingRequests.delete(requestId);
12602
+ clearTimeout(timeout);
12603
+ reject(new Error('WebSocket not connected'));
12604
+ }
12605
+ });
12606
+ }
12607
+ notifySubscription(sub) {
12608
+ const data = this.docsToArray(sub);
12609
+ const callbacks = Array.from(sub.callbacks);
12610
+ for (const cb of callbacks) {
12611
+ try {
12612
+ cb(data);
12613
+ }
12614
+ catch ( /* swallow callback errors */_a) { /* swallow callback errors */ }
12615
+ }
12616
+ this.notifyState(sub);
12617
+ }
12618
+ notifyState(sub) {
12619
+ const state = this.getState(sub);
12620
+ const callbacks = Array.from(sub.stateCallbacks);
12621
+ for (const cb of callbacks) {
12622
+ try {
12623
+ cb(state);
12624
+ }
12625
+ catch ( /* swallow */_a) { /* swallow */ }
12626
+ }
12627
+ }
12628
+ getState(sub) {
12629
+ return {
12630
+ data: this.docsToArray(sub),
12631
+ status: sub.status,
12632
+ isStale: sub.isStale,
12633
+ error: sub.error,
12634
+ };
12635
+ }
12636
+ docsToArray(sub) {
12637
+ return Array.from(sub.docs.values());
12638
+ }
12639
+ findSubscriptionById(id) {
12640
+ for (const sub of this.subscriptions.values()) {
12641
+ if (sub.id === id)
12642
+ return sub;
12643
+ }
12644
+ return undefined;
12645
+ }
12646
+ findSubscriptionByPath(collectionPath) {
12647
+ for (const sub of this.subscriptions.values()) {
12648
+ const subPath = sub.path.startsWith('/') ? sub.path.slice(1) : sub.path;
12649
+ if (subPath === collectionPath)
12650
+ return sub;
12651
+ if (collectionPath.startsWith(subPath + '/'))
12652
+ return sub;
12653
+ }
12654
+ return undefined;
12655
+ }
12656
+ getCollectionPath(docPath) {
12657
+ const segments = docPath.split('/');
12658
+ if (segments.length % 2 === 0) {
12659
+ return segments.slice(0, -1).join('/');
12660
+ }
12661
+ return docPath;
12662
+ }
12663
+ getSubKey(path, opts) {
12664
+ const parts = [path];
12665
+ if (opts.filter)
12666
+ parts.push(JSON.stringify(opts.filter));
12667
+ if (opts.prompt)
12668
+ parts.push(opts.prompt);
12669
+ if (opts.tier)
12670
+ parts.push(opts.tier);
12671
+ return parts.join('::');
12672
+ }
12673
+ idbKey(path) {
12674
+ return `${this.appId}:${path}`;
12675
+ }
12676
+ markIdbDirty(path) {
12677
+ const sub = this.findSubscriptionByPath(path);
12678
+ if (sub && sub.tier === 'ephemeral')
12679
+ return;
12680
+ this.idbDirtyKeys.add(path);
12681
+ if (!this.idbFlushTimer) {
12682
+ this.idbFlushTimer = setTimeout(() => {
12683
+ this.flushIdb();
12684
+ this.idbFlushTimer = null;
12685
+ }, 500);
12686
+ }
12687
+ }
12688
+ async flushIdb() {
12689
+ const keys = Array.from(this.idbDirtyKeys);
12690
+ this.idbDirtyKeys.clear();
12691
+ for (const path of keys) {
12692
+ const sub = this.findSubscriptionByPath(path);
12693
+ if (sub && sub.tier !== 'ephemeral') {
12694
+ const docs = this.docsToArray(sub);
12695
+ await idbSet(this.idbKey(path), docs);
12696
+ }
12697
+ }
12698
+ }
12699
+ createUnsubscribe(subKey, onData, onState) {
12700
+ return async () => {
12701
+ const sub = this.subscriptions.get(subKey);
12702
+ if (!sub)
12703
+ return;
12704
+ if (onData)
12705
+ sub.callbacks.delete(onData);
12706
+ if (onState)
12707
+ sub.stateCallbacks.delete(onState);
12708
+ // If no more callbacks, unsubscribe entirely
12709
+ if (sub.callbacks.size === 0 && sub.stateCallbacks.size === 0) {
12710
+ this.subscriptions.delete(subKey);
12711
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
12712
+ this.ws.send(JSON.stringify({
12713
+ type: 'unsubscribe',
12714
+ subscriptionId: sub.id,
12715
+ }));
12716
+ }
12717
+ }
12718
+ };
12719
+ }
12720
+ resolveOperations(doc, path) {
12721
+ var _a;
12722
+ if (!doc || typeof doc !== 'object')
12723
+ return doc;
12724
+ const resolved = {};
12725
+ for (const [key, value] of Object.entries(doc)) {
12726
+ if (value && typeof value === 'object' && !Array.isArray(value) && value.operation) {
12727
+ const op = value;
12728
+ if (op.operation === 'time' && op.value === 'now') {
12729
+ resolved[key] = Math.floor(Date.now() / 1000);
12730
+ }
12731
+ else if (op.operation === 'increment') {
12732
+ // For optimistic: get current value and add
12733
+ const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
12734
+ const collectionPath = this.getCollectionPath(normalizedPath);
12735
+ const sub = this.findSubscriptionByPath(collectionPath);
12736
+ const existing = sub === null || sub === void 0 ? void 0 : sub.docs.get(normalizedPath);
12737
+ const current = (_a = existing === null || existing === void 0 ? void 0 : existing[key]) !== null && _a !== void 0 ? _a : 0;
12738
+ resolved[key] = (typeof current === 'number' ? current : 0) + op.value;
12739
+ }
12740
+ else {
12741
+ resolved[key] = value;
12742
+ }
12743
+ }
12744
+ else {
12745
+ resolved[key] = value;
12746
+ }
12747
+ }
12748
+ return resolved;
12749
+ }
12750
+ rejectAllPending(reason) {
12751
+ for (const [requestId, pending] of this.pendingRequests) {
12752
+ clearTimeout(pending.timeout);
12753
+ pending.reject(new Error(reason));
12754
+ }
12755
+ this.pendingRequests.clear();
12756
+ }
12757
+ setAllSubscriptionStatus(status) {
12758
+ for (const sub of this.subscriptions.values()) {
12759
+ sub.status = status;
12760
+ this.notifyState(sub);
12761
+ }
12762
+ }
12763
+ // -----------------------------------------------------------------------
12764
+ // Lifecycle
12765
+ // -----------------------------------------------------------------------
12766
+ close() {
12767
+ this.closed = true;
12768
+ if (this.reconnectTimer)
12769
+ clearTimeout(this.reconnectTimer);
12770
+ if (this.idbFlushTimer)
12771
+ clearTimeout(this.idbFlushTimer);
12772
+ this.flushIdb();
12773
+ if (this.ws) {
12774
+ this.ws.close(1000, 'Store closed');
12775
+ this.ws = null;
12776
+ }
12777
+ this.rejectAllPending('Store closed');
12778
+ this.subscriptions.clear();
12779
+ }
12780
+ }
12781
+ // ---------------------------------------------------------------------------
12782
+ // Singleton instance
12783
+ // ---------------------------------------------------------------------------
12784
+ let storeInstance = null;
12785
+ function getRealtimeStore() {
12786
+ if (!storeInstance) {
12787
+ storeInstance = new RealtimeStore();
12788
+ }
12789
+ return storeInstance;
12790
+ }
12791
+ function resetRealtimeStore() {
12792
+ if (storeInstance) {
12793
+ storeInstance.close();
12794
+ storeInstance = null;
12795
+ }
12796
+ }
12797
+
11958
12798
  /**
11959
12799
  * Platform abstraction layer for React Native compatibility.
11960
12800
  *
@@ -12260,6 +13100,15 @@ class OffchainAuthProvider {
12260
13100
  }
12261
13101
  return null;
12262
13102
  }
13103
+ // Forward per-call login overrides (theme/title/subtitle and the `method`
13104
+ // directive) to the wrapped provider. Without this, the top-level login()
13105
+ // forwarder (which duck-types `setLoginOverrides` on the active provider)
13106
+ // skips the wrapper and the override — including `method` — is dropped on
13107
+ // offchain/Poofnet builds.
13108
+ setLoginOverrides(opts) {
13109
+ var _a, _b;
13110
+ (_b = (_a = this.wrappedProvider).setLoginOverrides) === null || _b === void 0 ? void 0 : _b.call(_a, opts);
13111
+ }
12263
13112
  async logout() {
12264
13113
  await this.wrappedProvider.logout();
12265
13114
  }
@@ -12951,6 +13800,26 @@ class OffchainAuthProvider {
12951
13800
  let currentAuthProvider = null;
12952
13801
  let currentAuthMethod = null;
12953
13802
  let initConfig = null;
13803
+ // The effective Phantom provider (offchain-wrapped when chain:"offchain"), captured
13804
+ // when a Phantom-primary app initializes. Used to restore Phantom after a staged
13805
+ // Privy email swap is cancelled/fails, and to route method-specific login() calls
13806
+ // back to Phantom. Null for non-Phantom apps.
13807
+ let savedPhantomProvider = null;
13808
+ /**
13809
+ * Restore the saved Phantom provider as the active auth provider. Used to undo a
13810
+ * staged Privy email swap on cancel/failure and to re-anchor method-specific
13811
+ * login() calls on Phantom. No-op when there is no saved Phantom provider.
13812
+ */
13813
+ async function restoreSavedPhantomProvider() {
13814
+ if (!savedPhantomProvider)
13815
+ return;
13816
+ currentAuthProvider = savedPhantomProvider;
13817
+ currentAuthMethod = 'phantom';
13818
+ const coreConfig = await getConfig();
13819
+ coreConfig.authProvider = savedPhantomProvider;
13820
+ coreConfig.authMethod = 'phantom';
13821
+ setAuthProviderInstance(savedPhantomProvider);
13822
+ }
12954
13823
  // --- localStorage helpers: track which auth method the user last logged in with ---
12955
13824
  const STORED_AUTH_METHOD_KEY$1 = 'tarobase_last_auth_method';
12956
13825
  function getStoredAuthMethod() {
@@ -13030,13 +13899,46 @@ async function hotSwapToPrivyProvider(config) {
13030
13899
  if (config.chain === "offchain") {
13031
13900
  provider = new OffchainAuthProvider(privyProvider);
13032
13901
  }
13033
- currentAuthProvider = provider;
13034
- currentAuthMethod = 'privy';
13035
- const coreConfig = await getConfig();
13036
- coreConfig.authProvider = provider;
13037
- coreConfig.authMethod = 'privy';
13038
- setAuthProviderInstance(provider);
13039
- return privyProvider;
13902
+ // STAGED swap: do NOT mutate the module-level auth globals here. The caller (the
13903
+ // Phantom modal's email button / the `method:'email'` bridge) calls `.login()` on
13904
+ // the returned provider separately. If the user cancels, committing up-front would
13905
+ // strand the globals on Privy and break reload/signing. Instead, return a provider
13906
+ // whose `login()` commits the globals to Privy ONLY on success and restores the
13907
+ // saved Phantom provider on cancel/failure.
13908
+ const commitPrivy = async () => {
13909
+ currentAuthProvider = provider;
13910
+ currentAuthMethod = 'privy';
13911
+ const coreConfig = await getConfig();
13912
+ coreConfig.authProvider = provider;
13913
+ coreConfig.authMethod = 'privy';
13914
+ setAuthProviderInstance(provider);
13915
+ };
13916
+ // Proxy intercepts only `login`; every other access delegates to `provider`. The
13917
+ // proxy is used solely for this one `.login()` call — on success the globals are
13918
+ // committed to the inner `provider` (not the proxy), so subsequent calls bypass it.
13919
+ return new Proxy(provider, {
13920
+ get(target, prop, receiver) {
13921
+ if (prop === 'login') {
13922
+ return async (...args) => {
13923
+ try {
13924
+ const user = await target.login(...args);
13925
+ if (user) {
13926
+ await commitPrivy();
13927
+ }
13928
+ else {
13929
+ await restoreSavedPhantomProvider();
13930
+ }
13931
+ return user;
13932
+ }
13933
+ catch (err) {
13934
+ await restoreSavedPhantomProvider();
13935
+ throw err;
13936
+ }
13937
+ };
13938
+ }
13939
+ return Reflect.get(target, prop, receiver);
13940
+ },
13941
+ });
13040
13942
  }
13041
13943
  async function hotSwapToMWAProvider(config) {
13042
13944
  var _a, _b;
@@ -13067,7 +13969,7 @@ const SOLANA_DEVNET_RPC_URL = "https://idelle-8nxsep-fast-devnet.helius-rpc.com"
13067
13969
  const SOLANA_MAINNET_RPC_URL = "https://celestia-cegncv-fast-mainnet.helius-rpc.com";
13068
13970
  const SURFNET_RPC_URL$2 = "https://surfpool.fly.dev";
13069
13971
  async function getAuthProvider(config) {
13070
- var _a, _b, _c, _d;
13972
+ var _a, _b, _c;
13071
13973
  if (currentAuthProvider) {
13072
13974
  return currentAuthProvider;
13073
13975
  }
@@ -13111,11 +14013,13 @@ async function getAuthProvider(config) {
13111
14013
  // MWA hot-swap is always available (custom modal shows MWA button on Android)
13112
14014
  currentAuthProvider.onSwitchToMWA =
13113
14015
  () => hotSwapToMWAProvider(config);
13114
- // Privy hot-swap only when enablePrivyFallback is on
13115
- if ((_d = config.phantomConfig) === null || _d === void 0 ? void 0 : _d.enablePrivyFallback) {
13116
- currentAuthProvider.onSwitchToPrivy =
13117
- () => hotSwapToPrivyProvider(config);
13118
- }
14016
+ // Privy email bridge: ALWAYS wired, independent of enablePrivyFallback, so
14017
+ // `login({ method: 'email' })` works even on a custom Phantom app (which
14018
+ // sets enablePrivyFallback:false). enablePrivyFallback now only controls
14019
+ // whether the DEFAULT modal renders its own email button (see
14020
+ // phantom-wallet-provider.ts) — not whether the bridge exists.
14021
+ currentAuthProvider.onSwitchToPrivy =
14022
+ () => hotSwapToPrivyProvider(config);
13119
14023
  break;
13120
14024
  }
13121
14025
  case "privy-expo":
@@ -13146,6 +14050,13 @@ async function getAuthProvider(config) {
13146
14050
  console.log("[Offchain] Wrapping auth provider for Poofnet transaction tracking");
13147
14051
  currentAuthProvider = new OffchainAuthProvider(currentAuthProvider);
13148
14052
  }
14053
+ // Save the EFFECTIVE Phantom provider (post offchain-wrap) so the staged Privy
14054
+ // email swap can restore it on cancel/failure and method-specific login() calls
14055
+ // can re-anchor on Phantom. Captured after the wrap so restore doesn't bypass the
14056
+ // OffchainAuthProvider on Poofnet/preview.
14057
+ if (authMethod === 'phantom') {
14058
+ savedPhantomProvider = currentAuthProvider;
14059
+ }
13149
14060
  return currentAuthProvider;
13150
14061
  }
13151
14062
  async function login$1(options) {
@@ -13161,6 +14072,14 @@ async function login$1(options) {
13161
14072
  if (!currentAuthProvider) {
13162
14073
  throw new Error("Auth provider not initialized. Please call init() first.");
13163
14074
  }
14075
+ // Method-specific login() always operates on the Phantom provider (which then
14076
+ // bridges to Privy for 'email'). If a prior email swap committed Privy, re-anchor
14077
+ // on the saved Phantom provider before delegating so the `method` directive lands
14078
+ // on Phantom. (Cancelled swaps already restore Phantom via the staged proxy; this
14079
+ // covers the post-success case.) No-op for non-Phantom apps (no saved provider).
14080
+ if ((options === null || options === void 0 ? void 0 : options.method) && currentAuthMethod !== 'phantom' && savedPhantomProvider) {
14081
+ await restoreSavedPhantomProvider();
14082
+ }
13164
14083
  // Forward per-call overrides to providers that support them (duck-typed to stay
13165
14084
  // lazy-load safe — no direct import of PhantomWalletProvider here).
13166
14085
  // Always call (even with null) so previous overrides are cleared when this
@@ -13320,7 +14239,7 @@ function useAuth() {
13320
14239
  // Provide a fallback so server render doesn't break
13321
14240
  if (isSSR) {
13322
14241
  return {
13323
- login: async () => undefined,
14242
+ login: async (_options) => undefined,
13324
14243
  logout: async () => undefined,
13325
14244
  loading: true,
13326
14245
  user: null,
@@ -13340,10 +14259,10 @@ function useAuth() {
13340
14259
  return () => {
13341
14260
  };
13342
14261
  }, []);
13343
- const login$1 = async () => {
14262
+ const login$1 = async (options) => {
13344
14263
  try {
13345
14264
  setLoading(true);
13346
- const user = await login();
14265
+ const user = await login(options);
13347
14266
  setUser(user);
13348
14267
  }
13349
14268
  catch (error) {
@@ -15880,7 +16799,7 @@ async function loadDependencies() {
15880
16799
  const [reactModule, reactDomModule, phantomModule] = await Promise.all([
15881
16800
  import('react'),
15882
16801
  import('react-dom/client'),
15883
- import('./index-DOYZMq5N.esm.js')
16802
+ import('./index-dEUHmDSl.esm.js')
15884
16803
  ]);
15885
16804
  // Extract default export from ESM module namespace
15886
16805
  // Dynamic import() returns { default: Module, ...exports }, not the module directly
@@ -15900,7 +16819,15 @@ class PhantomWalletProvider {
15900
16819
  this.containerElement = null;
15901
16820
  this.root = null;
15902
16821
  this.phantomMethods = null;
16822
+ // The FULL set registered with the Phantom SDK (so login({ method:'google'|'apple' })
16823
+ // can connect directly even when those buttons are hidden from the default modal).
15903
16824
  this.resolvedProviders = ['injected'];
16825
+ // What the DEFAULT chooser modal renders as buttons (social hidden when the Privy
16826
+ // email fallback owns vanilla email). Subset of resolvedProviders.
16827
+ this.modalProviders = ['injected'];
16828
+ // True only while a `login({ method: 'wallet' })` modal is open — suppresses the
16829
+ // social + email buttons so wallet mode shows wallets only.
16830
+ this.modalWalletOnly = false;
15904
16831
  this.pendingLogin = null;
15905
16832
  this.loginInProgress = false;
15906
16833
  this.autoLoginInProgress = false;
@@ -15957,10 +16884,15 @@ class PhantomWalletProvider {
15957
16884
  else {
15958
16885
  this.resolvedProviders = ['injected'];
15959
16886
  }
15960
- // When Privy handles social logins, strip them from Phantom
15961
- if (this.config.enablePrivyFallback) {
15962
- this.resolvedProviders = this.resolvedProviders.filter(p => p !== 'google' && p !== 'apple');
15963
- }
16887
+ // resolvedProviders stays the FULL set (registered with the Phantom SDK), so a
16888
+ // direct login({ method: 'google'|'apple' }) can connect even when the default
16889
+ // modal hides those buttons. modalProviders is what the DEFAULT chooser renders:
16890
+ // when the Privy email fallback owns vanilla email we hide social from the
16891
+ // default modal — but it remains registered above. (Apps with their own auth UI
16892
+ // use login({ method }) and bypass the default modal entirely.)
16893
+ this.modalProviders = this.config.enablePrivyFallback
16894
+ ? this.resolvedProviders.filter(p => p !== 'google' && p !== 'apple')
16895
+ : [...this.resolvedProviders];
15964
16896
  }
15965
16897
  /**
15966
16898
  * Patch the Phantom extension's provider to gracefully handle `phantom_getFeatures`
@@ -16062,6 +16994,52 @@ class PhantomWalletProvider {
16062
16994
  connectError,
16063
16995
  showCustomModal: () => setShowWalletModal(true),
16064
16996
  hideCustomModal: () => setShowWalletModal(false),
16997
+ // Per-method login bridge: connect a specific provider directly
16998
+ // (no modal). The connection-success effect resolves the pending
16999
+ // login. Used for login({ method: 'google' | 'apple' }).
17000
+ connectWithProvider: async (provider) => {
17001
+ walletClickedRef.current = true;
17002
+ setShowWalletModal(false);
17003
+ try {
17004
+ await connect({ provider });
17005
+ }
17006
+ catch (err) {
17007
+ // connectError effect handles failure
17008
+ }
17009
+ },
17010
+ // Per-method login bridge: jump straight to the Privy email
17011
+ // screen (mirrors the modal's email button) and resolve/reject
17012
+ // the pending login directly. Used for login({ method: 'email' }).
17013
+ switchToEmail: async () => {
17014
+ that.loginInProgress = false;
17015
+ walletClickedRef.current = true;
17016
+ setShowWalletModal(false);
17017
+ if (!that.onSwitchToPrivy) {
17018
+ if (that.pendingLogin) {
17019
+ that.pendingLogin.reject(new Error('Email login is not available'));
17020
+ that.pendingLogin = null;
17021
+ }
17022
+ return;
17023
+ }
17024
+ try {
17025
+ const privyProvider = await that.onSwitchToPrivy();
17026
+ const user = await privyProvider.login();
17027
+ if (that.pendingLogin && user) {
17028
+ that.pendingLogin.resolve(user);
17029
+ that.pendingLogin = null;
17030
+ }
17031
+ else if (that.pendingLogin) {
17032
+ that.pendingLogin.reject(new Error('User cancelled login'));
17033
+ that.pendingLogin = null;
17034
+ }
17035
+ }
17036
+ catch (error) {
17037
+ if (that.pendingLogin) {
17038
+ that.pendingLogin.reject(error);
17039
+ that.pendingLogin = null;
17040
+ }
17041
+ }
17042
+ },
16065
17043
  solana: solana && solanaHook.isAvailable ? {
16066
17044
  // Wrap methods to preserve 'this' context - direct references lose binding
16067
17045
  signMessage: (message) => solana.signMessage(message),
@@ -16420,8 +17398,9 @@ class PhantomWalletProvider {
16420
17398
  boxSizing: 'border-box',
16421
17399
  });
16422
17400
  const walletButtons = [];
16423
- // Google OAuth button — shown when 'google' is in resolved providers
16424
- if (sdkProviders.includes('google')) {
17401
+ // Google OAuth button — default chooser only (hidden in wallet-only mode),
17402
+ // gated on modalProviders (registered-but-hidden under the email fallback).
17403
+ if (!that.modalWalletOnly && that.modalProviders.includes('google')) {
16425
17404
  walletButtons.push(React$1.createElement('button', {
16426
17405
  key: 'google-oauth',
16427
17406
  style: buttonStyle('google-oauth'),
@@ -16445,8 +17424,9 @@ class PhantomWalletProvider {
16445
17424
  fill: '#EA4335',
16446
17425
  })), 'Continue with Google'));
16447
17426
  }
16448
- // Apple OAuth button — shown when 'apple' is in resolved providers
16449
- if (sdkProviders.includes('apple')) {
17427
+ // Apple OAuth button — default chooser only (hidden in wallet-only mode),
17428
+ // gated on modalProviders (registered-but-hidden under the email fallback).
17429
+ if (!that.modalWalletOnly && that.modalProviders.includes('apple')) {
16450
17430
  walletButtons.push(React$1.createElement('button', {
16451
17431
  key: 'apple-oauth',
16452
17432
  style: buttonStyle('apple-oauth'),
@@ -16527,8 +17507,10 @@ class PhantomWalletProvider {
16527
17507
  stroke: textColor, strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', fill: 'none',
16528
17508
  })), 'Connect Mobile Wallet'));
16529
17509
  }
16530
- // Email button — only when Privy fallback is enabled
16531
- if (that.config.enablePrivyFallback) {
17510
+ // Email button — default chooser only (hidden in wallet-only mode), and
17511
+ // only when the Privy email fallback owns the default modal's email button.
17512
+ // (login({ method:'email' }) bridges to Privy directly, independent of this.)
17513
+ if (!that.modalWalletOnly && that.config.enablePrivyFallback) {
16532
17514
  walletButtons.push(React$1.createElement('button', {
16533
17515
  key: 'email-login',
16534
17516
  style: emailButtonStyle('email-login'),
@@ -16824,10 +17806,29 @@ class PhantomWalletProvider {
16824
17806
  });
16825
17807
  }
16826
17808
  const loginPromise = new Promise((resolve, reject) => {
17809
+ var _a;
16827
17810
  this.pendingLogin = { resolve, reject };
16828
17811
  this.loginInProgress = true;
16829
- // Always use our custom wallet modal
16830
- this.phantomMethods.showCustomModal();
17812
+ // Drive the requested flow. pendingLogin + loginInProgress are already set
17813
+ // above, so the connection-success effect (google/apple/wallet) and the
17814
+ // email bridge resolve THIS promise. A method-specific call goes straight to
17815
+ // the matching connect/bridge; no method → the full chooser modal.
17816
+ const method = (_a = this.loginOverrides) === null || _a === void 0 ? void 0 : _a.method;
17817
+ if (method === 'google' || method === 'apple') {
17818
+ void this.phantomMethods.connectWithProvider(method);
17819
+ }
17820
+ else if (method === 'email') {
17821
+ void this.phantomMethods.switchToEmail();
17822
+ }
17823
+ else if (method === 'wallet') {
17824
+ // Wallet-only modal: wallets shown, social + email hidden.
17825
+ this.modalWalletOnly = true;
17826
+ this.phantomMethods.showCustomModal();
17827
+ }
17828
+ else {
17829
+ this.modalWalletOnly = false;
17830
+ this.phantomMethods.showCustomModal();
17831
+ }
16831
17832
  // Safety timeout
16832
17833
  setTimeout(() => {
16833
17834
  if (this.pendingLogin) {
@@ -16839,6 +17840,7 @@ class PhantomWalletProvider {
16839
17840
  });
16840
17841
  return loginPromise.finally(() => {
16841
17842
  this.loginOverrides = null;
17843
+ this.modalWalletOnly = false;
16842
17844
  });
16843
17845
  }
16844
17846
  async restoreSession() {
@@ -20634,6 +21636,7 @@ const getDefaultPrivyConfig = () => ({
20634
21636
  });
20635
21637
  class PrivyWalletProvider {
20636
21638
  constructor(appName, appLogoUrl, privyConfig, networkUrl = null) {
21639
+ var _a, _b;
20637
21640
  this.containerElement = null;
20638
21641
  this.chainId = 'solana:mainnet';
20639
21642
  this.root = null;
@@ -20667,12 +21670,20 @@ class PrivyWalletProvider {
20667
21670
  this.privyConfig.config.appearance.logo = appLogoUrl;
20668
21671
  }
20669
21672
  }
20670
- // Ensure externalWallets connectors are always configured required for
20671
- // useWallets().ready to resolve even when no browser extension is present
20672
- if (!this.privyConfig.config.externalWallets && privySolana) {
20673
- this.privyConfig.config.externalWallets = {
20674
- solana: { connectors: privySolana.toSolanaWalletConnectors() }
20675
- };
21673
+ // Ensure externalWallets.solana.connectors is a VALID Privy connector registry.
21674
+ // Privy (@privy-io/react-auth) calls connectors.onMount() during mount, so a
21675
+ // MISSING key OR an array (the common `connectors: []` mistake, e.g. in
21676
+ // social-only configs) crashes with
21677
+ // "externalWallets.solana.connectors.onMount is not a function". Normalize both
21678
+ // cases to a real toSolanaWalletConnectors() registry. This does NOT force a
21679
+ // wallet picker — the modal's visible login methods are controlled by
21680
+ // loginMethods; connectors only need to be present for useWallets().ready.
21681
+ if (privySolana) {
21682
+ const ew = this.privyConfig.config.externalWallets;
21683
+ const connectors = (_a = ew === null || ew === void 0 ? void 0 : ew.solana) === null || _a === void 0 ? void 0 : _a.connectors;
21684
+ if (!ew || !connectors || Array.isArray(connectors)) {
21685
+ this.privyConfig.config.externalWallets = Object.assign(Object.assign({}, (ew !== null && ew !== void 0 ? ew : {})), { solana: Object.assign(Object.assign({}, ((_b = ew === null || ew === void 0 ? void 0 : ew.solana) !== null && _b !== void 0 ? _b : {})), { connectors: privySolana.toSolanaWalletConnectors() }) });
21686
+ }
20676
21687
  }
20677
21688
  // Add Solana RPC configuration for v3
20678
21689
  let devnetUrl = SOLANA_DEVNET_RPC_URL;
@@ -22020,7 +23031,7 @@ async function registerMobileWalletAdapter(config) {
22020
23031
  if (typeof window === 'undefined')
22021
23032
  return;
22022
23033
  try {
22023
- const walletStandardMobile = await import('./index.browser-D1uNEi4e.esm.js');
23034
+ const walletStandardMobile = await import('./index.browser-RwalL3F4.esm.js');
22024
23035
  const registerMwa = walletStandardMobile.registerMwa || ((_a = walletStandardMobile.default) === null || _a === void 0 ? void 0 : _a.registerMwa);
22025
23036
  if (!registerMwa) {
22026
23037
  console.warn('[SolanaMobileWallet] registerMwa not found in @solana-mobile/wallet-standard-mobile');
@@ -22159,7 +23170,7 @@ class SolanaMobileWalletProvider {
22159
23170
  async ensureWallet() {
22160
23171
  if (this.wallet)
22161
23172
  return this.wallet;
22162
- const mod = await import('./index.browser-D1uNEi4e.esm.js');
23173
+ const mod = await import('./index.browser-RwalL3F4.esm.js');
22163
23174
  const chain = mapChainToWalletStandard(this.cluster);
22164
23175
  this.wallet = new mod.LocalSolanaMobileWalletAdapterWallet({
22165
23176
  appIdentity: this.appIdentity,
@@ -23351,5 +24362,5 @@ class PrivyExpoProvider {
23351
24362
  }
23352
24363
  }
23353
24364
 
23354
- export { getCachedData as $, subscribe as A, useAuth as B, deserializeTransaction as C, getIdToken as D, setPlatform as E, getPlatform as F, PrivyWalletProvider as G, DEFAULT_TEST_ADDRESS as H, isMobileWalletAvailable as I, registerMobileWalletAdapter as J, PrivyExpoProvider as K, InsufficientBalanceError as L, MockAuthProvider as M, ServerSessionManager as N, OffchainAuthProvider as O, PhantomWalletProvider as P, buildSetDocumentsTransaction as Q, ReactNativeSessionManager as R, SolanaMobileWalletProvider as S, clearCache as T, closeAllSubscriptions as U, convertRemainingAccounts as V, WebSessionManager as W, createSessionWithPrivy as X, createSessionWithSignature as Y, genAuthNonce as Z, genSolanaMessage as _, base58 as a, getMany as a0, hasActiveConnection as a1, reconnectWithNewAuth as a2, refreshSession as a3, signSessionCreateMessage as a4, wsDelete as a5, wsGet as a6, wsGetMany as a7, wsQuery as a8, wsSet as a9, bufferExports as b, getCurrentUser as c, onAuthLoadingChanged as d, getAuthLoading as e, logout as f, getDefaultExportFromCjs$1 as g, getConfig as h, init as i, getAuthProvider as j, get as k, login as l, setMany as m, setFile as n, onAuthStateChanged as o, getFiles as p, runQueryMany as q, runQuery as r, set as s, runExpression as t, runExpressionMany as u, signMessage as v, signTransaction as w, signAndSubmitTransaction as x, count as y, aggregate as z };
23355
- //# sourceMappingURL=index-CzVM_hix.esm.js.map
24365
+ export { genSolanaMessage as $, subscribe as A, useAuth as B, deserializeTransaction as C, getIdToken as D, setPlatform as E, getPlatform as F, PrivyWalletProvider as G, DEFAULT_TEST_ADDRESS as H, isMobileWalletAvailable as I, registerMobileWalletAdapter as J, PrivyExpoProvider as K, InsufficientBalanceError as L, MockAuthProvider as M, RealtimeStore as N, OffchainAuthProvider as O, PhantomWalletProvider as P, ServerSessionManager as Q, ReactNativeSessionManager as R, SolanaMobileWalletProvider as S, buildSetDocumentsTransaction as T, clearCache as U, closeAllSubscriptions as V, WebSessionManager as W, convertRemainingAccounts as X, createSessionWithPrivy as Y, createSessionWithSignature as Z, genAuthNonce as _, base58 as a, getCachedData as a0, getMany as a1, getRealtimeStore as a2, hasActiveConnection as a3, reconnectWithNewAuth as a4, refreshSession as a5, resetRealtimeStore as a6, signSessionCreateMessage as a7, wsDelete as a8, wsGet as a9, wsGetMany as aa, wsQuery as ab, wsSet as ac, bufferExports as b, getCurrentUser as c, onAuthLoadingChanged as d, getAuthLoading as e, logout as f, getDefaultExportFromCjs$1 as g, getConfig as h, init as i, getAuthProvider as j, get as k, login as l, setMany as m, setFile as n, onAuthStateChanged as o, getFiles as p, runQueryMany as q, runQuery as r, set as s, runExpression as t, runExpressionMany as u, signMessage as v, signTransaction as w, signAndSubmitTransaction as x, count as y, aggregate as z };
24366
+ //# sourceMappingURL=index-6iINLG88.esm.js.map