@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.
- package/dist/auth/hooks/useAuth.d.ts +2 -1
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/providers/offchain-auth-provider.d.ts +1 -0
- package/dist/auth/providers/phantom-wallet-provider.d.ts +10 -0
- package/dist/{index-CzVM_hix.esm.js → index-6iINLG88.esm.js} +1058 -47
- package/dist/index-6iINLG88.esm.js.map +1 -0
- package/dist/index-CWD6VMLX.esm.js +6 -0
- package/dist/index-CWD6VMLX.esm.js.map +1 -0
- package/dist/{index-C65AsRIA.esm.js → index-Dg3y_pRI.esm.js} +14 -364
- package/dist/index-Dg3y_pRI.esm.js.map +1 -0
- package/dist/{index-Eb2uyla3.js → index-GeHpO_Ud.js} +1060 -46
- package/dist/index-GeHpO_Ud.js.map +1 -0
- package/dist/{index-DOYZMq5N.esm.js → index-dEUHmDSl.esm.js} +13 -363
- package/dist/index-dEUHmDSl.esm.js.map +1 -0
- package/dist/{index-BZ_W-sR0.js → index-eFFOC8cE.js} +2 -2
- package/dist/index-eFFOC8cE.js.map +1 -0
- package/dist/{index-lt7AQ69U.js → index-m53xb2JA.js} +14 -364
- package/dist/index-m53xb2JA.js.map +1 -0
- package/dist/{index-CqpErQap.js → index-vi52dsn6.js} +13 -363
- package/dist/index-vi52dsn6.js.map +1 -0
- package/dist/{index.browser-BltIkY00.js → index.browser-B1Zbn8LC.js} +2 -2
- package/dist/{index.browser-BltIkY00.js.map → index.browser-B1Zbn8LC.js.map} +1 -1
- package/dist/{index.browser-C81ODkjH.js → index.browser-D6XPhu6r.js} +2 -2
- package/dist/{index.browser-C81ODkjH.js.map → index.browser-D6XPhu6r.js.map} +1 -1
- package/dist/{index.browser-D1uNEi4e.esm.js → index.browser-RwalL3F4.esm.js} +2 -2
- package/dist/{index.browser-D1uNEi4e.esm.js.map → index.browser-RwalL3F4.esm.js.map} +1 -1
- package/dist/{index.browser-zlnKOqcR.esm.js → index.browser-wZkBhwtI.esm.js} +2 -2
- package/dist/{index.browser-zlnKOqcR.esm.js.map → index.browser-wZkBhwtI.esm.js.map} +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/{index.native-DYddSSgT.js → index.native-Bjx72Wnj.js} +950 -28
- package/dist/index.native-Bjx72Wnj.js.map +1 -0
- package/dist/{index.native-Czk4F68O.esm.js → index.native-Dd783Qva.esm.js} +948 -29
- package/dist/index.native-Dd783Qva.esm.js.map +1 -0
- package/dist/index.native.esm.js +1 -1
- package/dist/index.native.js +4 -1
- package/dist/index.native.js.map +1 -1
- package/dist/{phantom-wallet-provider-DR5qAGcS.js → phantom-wallet-provider-DUVViY9g.js} +99 -16
- package/dist/phantom-wallet-provider-DUVViY9g.js.map +1 -0
- package/dist/{phantom-wallet-provider-0tX0Zs72.esm.js → phantom-wallet-provider-Dloixy8s.esm.js} +99 -16
- package/dist/phantom-wallet-provider-Dloixy8s.esm.js.map +1 -0
- package/dist/{privy-wallet-provider-ChMUFAEp.esm.js → privy-wallet-provider-BQbIRyXF.esm.js} +18 -9
- package/dist/privy-wallet-provider-BQbIRyXF.esm.js.map +1 -0
- package/dist/{privy-wallet-provider-BqTsdJPZ.js → privy-wallet-provider-CEdQMBT2.js} +18 -9
- package/dist/privy-wallet-provider-CEdQMBT2.js.map +1 -0
- package/dist/{solana-mobile-wallet-provider-Cq2H4eoW.esm.js → solana-mobile-wallet-provider-ARvDWjP7.esm.js} +4 -4
- package/dist/{solana-mobile-wallet-provider-Cq2H4eoW.esm.js.map → solana-mobile-wallet-provider-ARvDWjP7.esm.js.map} +1 -1
- package/dist/{solana-mobile-wallet-provider-DiXPSf8n.js → solana-mobile-wallet-provider-CgQWQ7Sk.js} +4 -4
- package/dist/{solana-mobile-wallet-provider-DiXPSf8n.js.map → solana-mobile-wallet-provider-CgQWQ7Sk.js.map} +1 -1
- package/package.json +1 -1
- package/dist/index-BZ_W-sR0.js.map +0 -1
- package/dist/index-C65AsRIA.esm.js.map +0 -1
- package/dist/index-CqpErQap.js.map +0 -1
- package/dist/index-CzVM_hix.esm.js.map +0 -1
- package/dist/index-D72o3sHT.esm.js +0 -6
- package/dist/index-D72o3sHT.esm.js.map +0 -1
- package/dist/index-DOYZMq5N.esm.js.map +0 -1
- package/dist/index-Eb2uyla3.js.map +0 -1
- package/dist/index-lt7AQ69U.js.map +0 -1
- package/dist/index.native-Czk4F68O.esm.js.map +0 -1
- package/dist/index.native-DYddSSgT.js.map +0 -1
- package/dist/phantom-wallet-provider-0tX0Zs72.esm.js.map +0 -1
- package/dist/phantom-wallet-provider-DR5qAGcS.js.map +0 -1
- package/dist/privy-wallet-provider-BqTsdJPZ.js.map +0 -1
- 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
|
-
//
|
|
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
|
-
|
|
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((
|
|
10634
|
-
const deficitSol = Number((
|
|
10635
|
-
throw new InsufficientBalanceError(String((
|
|
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
|
|
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() {
|
|
@@ -13051,13 +13920,46 @@ async function hotSwapToPrivyProvider(config) {
|
|
|
13051
13920
|
if (config.chain === "offchain") {
|
|
13052
13921
|
provider = new OffchainAuthProvider(privyProvider);
|
|
13053
13922
|
}
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
|
|
13060
|
-
|
|
13923
|
+
// STAGED swap: do NOT mutate the module-level auth globals here. The caller (the
|
|
13924
|
+
// Phantom modal's email button / the `method:'email'` bridge) calls `.login()` on
|
|
13925
|
+
// the returned provider separately. If the user cancels, committing up-front would
|
|
13926
|
+
// strand the globals on Privy and break reload/signing. Instead, return a provider
|
|
13927
|
+
// whose `login()` commits the globals to Privy ONLY on success and restores the
|
|
13928
|
+
// saved Phantom provider on cancel/failure.
|
|
13929
|
+
const commitPrivy = async () => {
|
|
13930
|
+
currentAuthProvider = provider;
|
|
13931
|
+
currentAuthMethod = 'privy';
|
|
13932
|
+
const coreConfig = await getConfig();
|
|
13933
|
+
coreConfig.authProvider = provider;
|
|
13934
|
+
coreConfig.authMethod = 'privy';
|
|
13935
|
+
setAuthProviderInstance(provider);
|
|
13936
|
+
};
|
|
13937
|
+
// Proxy intercepts only `login`; every other access delegates to `provider`. The
|
|
13938
|
+
// proxy is used solely for this one `.login()` call — on success the globals are
|
|
13939
|
+
// committed to the inner `provider` (not the proxy), so subsequent calls bypass it.
|
|
13940
|
+
return new Proxy(provider, {
|
|
13941
|
+
get(target, prop, receiver) {
|
|
13942
|
+
if (prop === 'login') {
|
|
13943
|
+
return async (...args) => {
|
|
13944
|
+
try {
|
|
13945
|
+
const user = await target.login(...args);
|
|
13946
|
+
if (user) {
|
|
13947
|
+
await commitPrivy();
|
|
13948
|
+
}
|
|
13949
|
+
else {
|
|
13950
|
+
await restoreSavedPhantomProvider();
|
|
13951
|
+
}
|
|
13952
|
+
return user;
|
|
13953
|
+
}
|
|
13954
|
+
catch (err) {
|
|
13955
|
+
await restoreSavedPhantomProvider();
|
|
13956
|
+
throw err;
|
|
13957
|
+
}
|
|
13958
|
+
};
|
|
13959
|
+
}
|
|
13960
|
+
return Reflect.get(target, prop, receiver);
|
|
13961
|
+
},
|
|
13962
|
+
});
|
|
13061
13963
|
}
|
|
13062
13964
|
async function hotSwapToMWAProvider(config) {
|
|
13063
13965
|
var _a, _b;
|
|
@@ -13088,7 +13990,7 @@ const SOLANA_DEVNET_RPC_URL = "https://idelle-8nxsep-fast-devnet.helius-rpc.com"
|
|
|
13088
13990
|
const SOLANA_MAINNET_RPC_URL = "https://celestia-cegncv-fast-mainnet.helius-rpc.com";
|
|
13089
13991
|
const SURFNET_RPC_URL$2 = "https://surfpool.fly.dev";
|
|
13090
13992
|
async function getAuthProvider(config) {
|
|
13091
|
-
var _a, _b, _c
|
|
13993
|
+
var _a, _b, _c;
|
|
13092
13994
|
if (currentAuthProvider) {
|
|
13093
13995
|
return currentAuthProvider;
|
|
13094
13996
|
}
|
|
@@ -13132,11 +14034,13 @@ async function getAuthProvider(config) {
|
|
|
13132
14034
|
// MWA hot-swap is always available (custom modal shows MWA button on Android)
|
|
13133
14035
|
currentAuthProvider.onSwitchToMWA =
|
|
13134
14036
|
() => hotSwapToMWAProvider(config);
|
|
13135
|
-
// Privy
|
|
13136
|
-
|
|
13137
|
-
|
|
13138
|
-
|
|
13139
|
-
|
|
14037
|
+
// Privy email bridge: ALWAYS wired, independent of enablePrivyFallback, so
|
|
14038
|
+
// `login({ method: 'email' })` works even on a custom Phantom app (which
|
|
14039
|
+
// sets enablePrivyFallback:false). enablePrivyFallback now only controls
|
|
14040
|
+
// whether the DEFAULT modal renders its own email button (see
|
|
14041
|
+
// phantom-wallet-provider.ts) — not whether the bridge exists.
|
|
14042
|
+
currentAuthProvider.onSwitchToPrivy =
|
|
14043
|
+
() => hotSwapToPrivyProvider(config);
|
|
13140
14044
|
break;
|
|
13141
14045
|
}
|
|
13142
14046
|
case "privy-expo":
|
|
@@ -13167,6 +14071,13 @@ async function getAuthProvider(config) {
|
|
|
13167
14071
|
console.log("[Offchain] Wrapping auth provider for Poofnet transaction tracking");
|
|
13168
14072
|
currentAuthProvider = new OffchainAuthProvider(currentAuthProvider);
|
|
13169
14073
|
}
|
|
14074
|
+
// Save the EFFECTIVE Phantom provider (post offchain-wrap) so the staged Privy
|
|
14075
|
+
// email swap can restore it on cancel/failure and method-specific login() calls
|
|
14076
|
+
// can re-anchor on Phantom. Captured after the wrap so restore doesn't bypass the
|
|
14077
|
+
// OffchainAuthProvider on Poofnet/preview.
|
|
14078
|
+
if (authMethod === 'phantom') {
|
|
14079
|
+
savedPhantomProvider = currentAuthProvider;
|
|
14080
|
+
}
|
|
13170
14081
|
return currentAuthProvider;
|
|
13171
14082
|
}
|
|
13172
14083
|
async function login$1(options) {
|
|
@@ -13182,6 +14093,14 @@ async function login$1(options) {
|
|
|
13182
14093
|
if (!currentAuthProvider) {
|
|
13183
14094
|
throw new Error("Auth provider not initialized. Please call init() first.");
|
|
13184
14095
|
}
|
|
14096
|
+
// Method-specific login() always operates on the Phantom provider (which then
|
|
14097
|
+
// bridges to Privy for 'email'). If a prior email swap committed Privy, re-anchor
|
|
14098
|
+
// on the saved Phantom provider before delegating so the `method` directive lands
|
|
14099
|
+
// on Phantom. (Cancelled swaps already restore Phantom via the staged proxy; this
|
|
14100
|
+
// covers the post-success case.) No-op for non-Phantom apps (no saved provider).
|
|
14101
|
+
if ((options === null || options === void 0 ? void 0 : options.method) && currentAuthMethod !== 'phantom' && savedPhantomProvider) {
|
|
14102
|
+
await restoreSavedPhantomProvider();
|
|
14103
|
+
}
|
|
13185
14104
|
// Forward per-call overrides to providers that support them (duck-typed to stay
|
|
13186
14105
|
// lazy-load safe — no direct import of PhantomWalletProvider here).
|
|
13187
14106
|
// Always call (even with null) so previous overrides are cleared when this
|
|
@@ -13341,7 +14260,7 @@ function useAuth() {
|
|
|
13341
14260
|
// Provide a fallback so server render doesn't break
|
|
13342
14261
|
if (isSSR) {
|
|
13343
14262
|
return {
|
|
13344
|
-
login: async () => undefined,
|
|
14263
|
+
login: async (_options) => undefined,
|
|
13345
14264
|
logout: async () => undefined,
|
|
13346
14265
|
loading: true,
|
|
13347
14266
|
user: null,
|
|
@@ -13361,10 +14280,10 @@ function useAuth() {
|
|
|
13361
14280
|
return () => {
|
|
13362
14281
|
};
|
|
13363
14282
|
}, []);
|
|
13364
|
-
const login$1 = async () => {
|
|
14283
|
+
const login$1 = async (options) => {
|
|
13365
14284
|
try {
|
|
13366
14285
|
setLoading(true);
|
|
13367
|
-
const user = await login();
|
|
14286
|
+
const user = await login(options);
|
|
13368
14287
|
setUser(user);
|
|
13369
14288
|
}
|
|
13370
14289
|
catch (error) {
|
|
@@ -15901,7 +16820,7 @@ async function loadDependencies() {
|
|
|
15901
16820
|
const [reactModule, reactDomModule, phantomModule] = await Promise.all([
|
|
15902
16821
|
import('react'),
|
|
15903
16822
|
import('react-dom/client'),
|
|
15904
|
-
Promise.resolve().then(function () { return require('./index-
|
|
16823
|
+
Promise.resolve().then(function () { return require('./index-vi52dsn6.js'); })
|
|
15905
16824
|
]);
|
|
15906
16825
|
// Extract default export from ESM module namespace
|
|
15907
16826
|
// Dynamic import() returns { default: Module, ...exports }, not the module directly
|
|
@@ -15921,7 +16840,15 @@ class PhantomWalletProvider {
|
|
|
15921
16840
|
this.containerElement = null;
|
|
15922
16841
|
this.root = null;
|
|
15923
16842
|
this.phantomMethods = null;
|
|
16843
|
+
// The FULL set registered with the Phantom SDK (so login({ method:'google'|'apple' })
|
|
16844
|
+
// can connect directly even when those buttons are hidden from the default modal).
|
|
15924
16845
|
this.resolvedProviders = ['injected'];
|
|
16846
|
+
// What the DEFAULT chooser modal renders as buttons (social hidden when the Privy
|
|
16847
|
+
// email fallback owns vanilla email). Subset of resolvedProviders.
|
|
16848
|
+
this.modalProviders = ['injected'];
|
|
16849
|
+
// True only while a `login({ method: 'wallet' })` modal is open — suppresses the
|
|
16850
|
+
// social + email buttons so wallet mode shows wallets only.
|
|
16851
|
+
this.modalWalletOnly = false;
|
|
15925
16852
|
this.pendingLogin = null;
|
|
15926
16853
|
this.loginInProgress = false;
|
|
15927
16854
|
this.autoLoginInProgress = false;
|
|
@@ -15978,10 +16905,15 @@ class PhantomWalletProvider {
|
|
|
15978
16905
|
else {
|
|
15979
16906
|
this.resolvedProviders = ['injected'];
|
|
15980
16907
|
}
|
|
15981
|
-
//
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
16908
|
+
// resolvedProviders stays the FULL set (registered with the Phantom SDK), so a
|
|
16909
|
+
// direct login({ method: 'google'|'apple' }) can connect even when the default
|
|
16910
|
+
// modal hides those buttons. modalProviders is what the DEFAULT chooser renders:
|
|
16911
|
+
// when the Privy email fallback owns vanilla email we hide social from the
|
|
16912
|
+
// default modal — but it remains registered above. (Apps with their own auth UI
|
|
16913
|
+
// use login({ method }) and bypass the default modal entirely.)
|
|
16914
|
+
this.modalProviders = this.config.enablePrivyFallback
|
|
16915
|
+
? this.resolvedProviders.filter(p => p !== 'google' && p !== 'apple')
|
|
16916
|
+
: [...this.resolvedProviders];
|
|
15985
16917
|
}
|
|
15986
16918
|
/**
|
|
15987
16919
|
* Patch the Phantom extension's provider to gracefully handle `phantom_getFeatures`
|
|
@@ -16083,6 +17015,52 @@ class PhantomWalletProvider {
|
|
|
16083
17015
|
connectError,
|
|
16084
17016
|
showCustomModal: () => setShowWalletModal(true),
|
|
16085
17017
|
hideCustomModal: () => setShowWalletModal(false),
|
|
17018
|
+
// Per-method login bridge: connect a specific provider directly
|
|
17019
|
+
// (no modal). The connection-success effect resolves the pending
|
|
17020
|
+
// login. Used for login({ method: 'google' | 'apple' }).
|
|
17021
|
+
connectWithProvider: async (provider) => {
|
|
17022
|
+
walletClickedRef.current = true;
|
|
17023
|
+
setShowWalletModal(false);
|
|
17024
|
+
try {
|
|
17025
|
+
await connect({ provider });
|
|
17026
|
+
}
|
|
17027
|
+
catch (err) {
|
|
17028
|
+
// connectError effect handles failure
|
|
17029
|
+
}
|
|
17030
|
+
},
|
|
17031
|
+
// Per-method login bridge: jump straight to the Privy email
|
|
17032
|
+
// screen (mirrors the modal's email button) and resolve/reject
|
|
17033
|
+
// the pending login directly. Used for login({ method: 'email' }).
|
|
17034
|
+
switchToEmail: async () => {
|
|
17035
|
+
that.loginInProgress = false;
|
|
17036
|
+
walletClickedRef.current = true;
|
|
17037
|
+
setShowWalletModal(false);
|
|
17038
|
+
if (!that.onSwitchToPrivy) {
|
|
17039
|
+
if (that.pendingLogin) {
|
|
17040
|
+
that.pendingLogin.reject(new Error('Email login is not available'));
|
|
17041
|
+
that.pendingLogin = null;
|
|
17042
|
+
}
|
|
17043
|
+
return;
|
|
17044
|
+
}
|
|
17045
|
+
try {
|
|
17046
|
+
const privyProvider = await that.onSwitchToPrivy();
|
|
17047
|
+
const user = await privyProvider.login();
|
|
17048
|
+
if (that.pendingLogin && user) {
|
|
17049
|
+
that.pendingLogin.resolve(user);
|
|
17050
|
+
that.pendingLogin = null;
|
|
17051
|
+
}
|
|
17052
|
+
else if (that.pendingLogin) {
|
|
17053
|
+
that.pendingLogin.reject(new Error('User cancelled login'));
|
|
17054
|
+
that.pendingLogin = null;
|
|
17055
|
+
}
|
|
17056
|
+
}
|
|
17057
|
+
catch (error) {
|
|
17058
|
+
if (that.pendingLogin) {
|
|
17059
|
+
that.pendingLogin.reject(error);
|
|
17060
|
+
that.pendingLogin = null;
|
|
17061
|
+
}
|
|
17062
|
+
}
|
|
17063
|
+
},
|
|
16086
17064
|
solana: solana && solanaHook.isAvailable ? {
|
|
16087
17065
|
// Wrap methods to preserve 'this' context - direct references lose binding
|
|
16088
17066
|
signMessage: (message) => solana.signMessage(message),
|
|
@@ -16441,8 +17419,9 @@ class PhantomWalletProvider {
|
|
|
16441
17419
|
boxSizing: 'border-box',
|
|
16442
17420
|
});
|
|
16443
17421
|
const walletButtons = [];
|
|
16444
|
-
// Google OAuth button —
|
|
16445
|
-
|
|
17422
|
+
// Google OAuth button — default chooser only (hidden in wallet-only mode),
|
|
17423
|
+
// gated on modalProviders (registered-but-hidden under the email fallback).
|
|
17424
|
+
if (!that.modalWalletOnly && that.modalProviders.includes('google')) {
|
|
16446
17425
|
walletButtons.push(React$1.createElement('button', {
|
|
16447
17426
|
key: 'google-oauth',
|
|
16448
17427
|
style: buttonStyle('google-oauth'),
|
|
@@ -16466,8 +17445,9 @@ class PhantomWalletProvider {
|
|
|
16466
17445
|
fill: '#EA4335',
|
|
16467
17446
|
})), 'Continue with Google'));
|
|
16468
17447
|
}
|
|
16469
|
-
// Apple OAuth button —
|
|
16470
|
-
|
|
17448
|
+
// Apple OAuth button — default chooser only (hidden in wallet-only mode),
|
|
17449
|
+
// gated on modalProviders (registered-but-hidden under the email fallback).
|
|
17450
|
+
if (!that.modalWalletOnly && that.modalProviders.includes('apple')) {
|
|
16471
17451
|
walletButtons.push(React$1.createElement('button', {
|
|
16472
17452
|
key: 'apple-oauth',
|
|
16473
17453
|
style: buttonStyle('apple-oauth'),
|
|
@@ -16548,8 +17528,10 @@ class PhantomWalletProvider {
|
|
|
16548
17528
|
stroke: textColor, strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', fill: 'none',
|
|
16549
17529
|
})), 'Connect Mobile Wallet'));
|
|
16550
17530
|
}
|
|
16551
|
-
// Email button — only
|
|
16552
|
-
|
|
17531
|
+
// Email button — default chooser only (hidden in wallet-only mode), and
|
|
17532
|
+
// only when the Privy email fallback owns the default modal's email button.
|
|
17533
|
+
// (login({ method:'email' }) bridges to Privy directly, independent of this.)
|
|
17534
|
+
if (!that.modalWalletOnly && that.config.enablePrivyFallback) {
|
|
16553
17535
|
walletButtons.push(React$1.createElement('button', {
|
|
16554
17536
|
key: 'email-login',
|
|
16555
17537
|
style: emailButtonStyle('email-login'),
|
|
@@ -16845,10 +17827,29 @@ class PhantomWalletProvider {
|
|
|
16845
17827
|
});
|
|
16846
17828
|
}
|
|
16847
17829
|
const loginPromise = new Promise((resolve, reject) => {
|
|
17830
|
+
var _a;
|
|
16848
17831
|
this.pendingLogin = { resolve, reject };
|
|
16849
17832
|
this.loginInProgress = true;
|
|
16850
|
-
//
|
|
16851
|
-
|
|
17833
|
+
// Drive the requested flow. pendingLogin + loginInProgress are already set
|
|
17834
|
+
// above, so the connection-success effect (google/apple/wallet) and the
|
|
17835
|
+
// email bridge resolve THIS promise. A method-specific call goes straight to
|
|
17836
|
+
// the matching connect/bridge; no method → the full chooser modal.
|
|
17837
|
+
const method = (_a = this.loginOverrides) === null || _a === void 0 ? void 0 : _a.method;
|
|
17838
|
+
if (method === 'google' || method === 'apple') {
|
|
17839
|
+
void this.phantomMethods.connectWithProvider(method);
|
|
17840
|
+
}
|
|
17841
|
+
else if (method === 'email') {
|
|
17842
|
+
void this.phantomMethods.switchToEmail();
|
|
17843
|
+
}
|
|
17844
|
+
else if (method === 'wallet') {
|
|
17845
|
+
// Wallet-only modal: wallets shown, social + email hidden.
|
|
17846
|
+
this.modalWalletOnly = true;
|
|
17847
|
+
this.phantomMethods.showCustomModal();
|
|
17848
|
+
}
|
|
17849
|
+
else {
|
|
17850
|
+
this.modalWalletOnly = false;
|
|
17851
|
+
this.phantomMethods.showCustomModal();
|
|
17852
|
+
}
|
|
16852
17853
|
// Safety timeout
|
|
16853
17854
|
setTimeout(() => {
|
|
16854
17855
|
if (this.pendingLogin) {
|
|
@@ -16860,6 +17861,7 @@ class PhantomWalletProvider {
|
|
|
16860
17861
|
});
|
|
16861
17862
|
return loginPromise.finally(() => {
|
|
16862
17863
|
this.loginOverrides = null;
|
|
17864
|
+
this.modalWalletOnly = false;
|
|
16863
17865
|
});
|
|
16864
17866
|
}
|
|
16865
17867
|
async restoreSession() {
|
|
@@ -20655,6 +21657,7 @@ const getDefaultPrivyConfig = () => ({
|
|
|
20655
21657
|
});
|
|
20656
21658
|
class PrivyWalletProvider {
|
|
20657
21659
|
constructor(appName, appLogoUrl, privyConfig, networkUrl = null) {
|
|
21660
|
+
var _a, _b;
|
|
20658
21661
|
this.containerElement = null;
|
|
20659
21662
|
this.chainId = 'solana:mainnet';
|
|
20660
21663
|
this.root = null;
|
|
@@ -20688,12 +21691,20 @@ class PrivyWalletProvider {
|
|
|
20688
21691
|
this.privyConfig.config.appearance.logo = appLogoUrl;
|
|
20689
21692
|
}
|
|
20690
21693
|
}
|
|
20691
|
-
// Ensure externalWallets
|
|
20692
|
-
//
|
|
20693
|
-
|
|
20694
|
-
|
|
20695
|
-
|
|
20696
|
-
|
|
21694
|
+
// Ensure externalWallets.solana.connectors is a VALID Privy connector registry.
|
|
21695
|
+
// Privy (@privy-io/react-auth) calls connectors.onMount() during mount, so a
|
|
21696
|
+
// MISSING key OR an array (the common `connectors: []` mistake, e.g. in
|
|
21697
|
+
// social-only configs) crashes with
|
|
21698
|
+
// "externalWallets.solana.connectors.onMount is not a function". Normalize both
|
|
21699
|
+
// cases to a real toSolanaWalletConnectors() registry. This does NOT force a
|
|
21700
|
+
// wallet picker — the modal's visible login methods are controlled by
|
|
21701
|
+
// loginMethods; connectors only need to be present for useWallets().ready.
|
|
21702
|
+
if (privySolana) {
|
|
21703
|
+
const ew = this.privyConfig.config.externalWallets;
|
|
21704
|
+
const connectors = (_a = ew === null || ew === void 0 ? void 0 : ew.solana) === null || _a === void 0 ? void 0 : _a.connectors;
|
|
21705
|
+
if (!ew || !connectors || Array.isArray(connectors)) {
|
|
21706
|
+
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() }) });
|
|
21707
|
+
}
|
|
20697
21708
|
}
|
|
20698
21709
|
// Add Solana RPC configuration for v3
|
|
20699
21710
|
let devnetUrl = SOLANA_DEVNET_RPC_URL;
|
|
@@ -22041,7 +23052,7 @@ async function registerMobileWalletAdapter(config) {
|
|
|
22041
23052
|
if (typeof window === 'undefined')
|
|
22042
23053
|
return;
|
|
22043
23054
|
try {
|
|
22044
|
-
const walletStandardMobile = await Promise.resolve().then(function () { return require('./index.browser-
|
|
23055
|
+
const walletStandardMobile = await Promise.resolve().then(function () { return require('./index.browser-B1Zbn8LC.js'); });
|
|
22045
23056
|
const registerMwa = walletStandardMobile.registerMwa || ((_a = walletStandardMobile.default) === null || _a === void 0 ? void 0 : _a.registerMwa);
|
|
22046
23057
|
if (!registerMwa) {
|
|
22047
23058
|
console.warn('[SolanaMobileWallet] registerMwa not found in @solana-mobile/wallet-standard-mobile');
|
|
@@ -22180,7 +23191,7 @@ class SolanaMobileWalletProvider {
|
|
|
22180
23191
|
async ensureWallet() {
|
|
22181
23192
|
if (this.wallet)
|
|
22182
23193
|
return this.wallet;
|
|
22183
|
-
const mod = await Promise.resolve().then(function () { return require('./index.browser-
|
|
23194
|
+
const mod = await Promise.resolve().then(function () { return require('./index.browser-B1Zbn8LC.js'); });
|
|
22184
23195
|
const chain = mapChainToWalletStandard(this.cluster);
|
|
22185
23196
|
this.wallet = new mod.LocalSolanaMobileWalletAdapterWallet({
|
|
22186
23197
|
appIdentity: this.appIdentity,
|
|
@@ -23380,6 +24391,7 @@ exports.PhantomWalletProvider = PhantomWalletProvider;
|
|
|
23380
24391
|
exports.PrivyExpoProvider = PrivyExpoProvider;
|
|
23381
24392
|
exports.PrivyWalletProvider = PrivyWalletProvider;
|
|
23382
24393
|
exports.ReactNativeSessionManager = ReactNativeSessionManager;
|
|
24394
|
+
exports.RealtimeStore = RealtimeStore;
|
|
23383
24395
|
exports.ServerSessionManager = ServerSessionManager;
|
|
23384
24396
|
exports.SolanaMobileWalletProvider = SolanaMobileWalletProvider;
|
|
23385
24397
|
exports.WebSessionManager = WebSessionManager;
|
|
@@ -23407,6 +24419,7 @@ exports.getFiles = getFiles;
|
|
|
23407
24419
|
exports.getIdToken = getIdToken;
|
|
23408
24420
|
exports.getMany = getMany;
|
|
23409
24421
|
exports.getPlatform = getPlatform;
|
|
24422
|
+
exports.getRealtimeStore = getRealtimeStore;
|
|
23410
24423
|
exports.hasActiveConnection = hasActiveConnection;
|
|
23411
24424
|
exports.init = init;
|
|
23412
24425
|
exports.isMobileWalletAvailable = isMobileWalletAvailable;
|
|
@@ -23417,6 +24430,7 @@ exports.onAuthStateChanged = onAuthStateChanged;
|
|
|
23417
24430
|
exports.reconnectWithNewAuth = reconnectWithNewAuth;
|
|
23418
24431
|
exports.refreshSession = refreshSession;
|
|
23419
24432
|
exports.registerMobileWalletAdapter = registerMobileWalletAdapter;
|
|
24433
|
+
exports.resetRealtimeStore = resetRealtimeStore;
|
|
23420
24434
|
exports.runExpression = runExpression;
|
|
23421
24435
|
exports.runExpressionMany = runExpressionMany;
|
|
23422
24436
|
exports.runQuery = runQuery;
|
|
@@ -23436,4 +24450,4 @@ exports.wsGet = wsGet;
|
|
|
23436
24450
|
exports.wsGetMany = wsGetMany;
|
|
23437
24451
|
exports.wsQuery = wsQuery;
|
|
23438
24452
|
exports.wsSet = wsSet;
|
|
23439
|
-
//# sourceMappingURL=index-
|
|
24453
|
+
//# sourceMappingURL=index-GeHpO_Ud.js.map
|