@pooflabs/core 0.0.47 → 0.0.49
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/client/subscription-v2.d.ts +17 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +187 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +182 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -376,6 +376,28 @@ class WebSessionManager {
|
|
|
376
376
|
static async storeSession(address, accessToken, idToken, refreshToken) {
|
|
377
377
|
if (typeof window === "undefined")
|
|
378
378
|
return;
|
|
379
|
+
// JWT-wallet binding: refuse to store a session whose idToken is bound
|
|
380
|
+
// to a different wallet than `address`. Prevents races that would otherwise
|
|
381
|
+
// leave localStorage with mismatched address/token state.
|
|
382
|
+
try {
|
|
383
|
+
const payloadB64 = idToken.split(".")[1];
|
|
384
|
+
if (payloadB64) {
|
|
385
|
+
const payload = JSON.parse(this.decodeBase64Url(payloadB64));
|
|
386
|
+
const tokenWallet = payload["custom:walletAddress"];
|
|
387
|
+
if (tokenWallet && tokenWallet !== address) {
|
|
388
|
+
throw new Error(`[WebSessionManager] Refusing to store session: address (${address}) does not match idToken custom:walletAddress (${tokenWallet})`);
|
|
389
|
+
}
|
|
390
|
+
if (!tokenWallet) {
|
|
391
|
+
console.warn("[WebSessionManager] storeSession: idToken has no custom:walletAddress claim — writing without validation");
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
if (typeof (err === null || err === void 0 ? void 0 : err.message) === "string" && err.message.includes("Refusing to store session")) {
|
|
397
|
+
throw err;
|
|
398
|
+
}
|
|
399
|
+
console.warn("[WebSessionManager] storeSession: failed to decode idToken for validation:", err);
|
|
400
|
+
}
|
|
379
401
|
const config = await getConfig();
|
|
380
402
|
const currentAppId = config.appId;
|
|
381
403
|
localStorage.setItem(this.TAROBASE_SESSION_STORAGE_KEY, JSON.stringify({
|
|
@@ -4722,6 +4744,7 @@ async function getOrCreateConnection(appId, isServer) {
|
|
|
4722
4744
|
subscriptions: new Map(),
|
|
4723
4745
|
pendingSubscriptions: new Map(),
|
|
4724
4746
|
pendingUnsubscriptions: new Map(),
|
|
4747
|
+
pendingRequests: new Map(),
|
|
4725
4748
|
isConnecting: false,
|
|
4726
4749
|
isConnected: false,
|
|
4727
4750
|
appId,
|
|
@@ -4820,11 +4843,17 @@ async function getOrCreateConnection(appId, isServer) {
|
|
|
4820
4843
|
clearInterval(connection.tokenRefreshTimer);
|
|
4821
4844
|
connection.tokenRefreshTimer = null;
|
|
4822
4845
|
}
|
|
4846
|
+
// Reject all pending WS CRUD requests on disconnect (fail fast)
|
|
4847
|
+
for (const [id, pending] of connection.pendingRequests) {
|
|
4848
|
+
clearTimeout(pending.timer);
|
|
4849
|
+
pending.reject(new Error('WebSocket disconnected'));
|
|
4850
|
+
}
|
|
4851
|
+
connection.pendingRequests.clear();
|
|
4823
4852
|
});
|
|
4824
4853
|
return connection;
|
|
4825
4854
|
}
|
|
4826
4855
|
function handleServerMessage(connection, message) {
|
|
4827
|
-
var _a;
|
|
4856
|
+
var _a, _b;
|
|
4828
4857
|
switch (message.type) {
|
|
4829
4858
|
case 'subscribed': {
|
|
4830
4859
|
const subscription = connection.subscriptions.get(message.subscriptionId);
|
|
@@ -4869,8 +4898,45 @@ function handleServerMessage(connection, message) {
|
|
|
4869
4898
|
}
|
|
4870
4899
|
break;
|
|
4871
4900
|
}
|
|
4901
|
+
case 'response': {
|
|
4902
|
+
const pendingReq = connection.pendingRequests.get(message.requestId);
|
|
4903
|
+
if (pendingReq) {
|
|
4904
|
+
connection.pendingRequests.delete(message.requestId);
|
|
4905
|
+
clearTimeout(pendingReq.timer);
|
|
4906
|
+
if (message.status >= 400) {
|
|
4907
|
+
pendingReq.reject(new Error(`Request failed with status ${message.status}`));
|
|
4908
|
+
}
|
|
4909
|
+
else {
|
|
4910
|
+
pendingReq.resolve(message.data);
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4913
|
+
break;
|
|
4914
|
+
}
|
|
4915
|
+
case 'setResponse': {
|
|
4916
|
+
const pendingSet = connection.pendingRequests.get(message.requestId);
|
|
4917
|
+
if (pendingSet) {
|
|
4918
|
+
connection.pendingRequests.delete(message.requestId);
|
|
4919
|
+
clearTimeout(pendingSet.timer);
|
|
4920
|
+
if (message.statusCode >= 400) {
|
|
4921
|
+
pendingSet.reject(new Error(((_a = message.body) === null || _a === void 0 ? void 0 : _a.message) || `Set failed with status ${message.statusCode}`));
|
|
4922
|
+
}
|
|
4923
|
+
else {
|
|
4924
|
+
pendingSet.resolve(message.body);
|
|
4925
|
+
}
|
|
4926
|
+
}
|
|
4927
|
+
break;
|
|
4928
|
+
}
|
|
4872
4929
|
case 'error': {
|
|
4873
4930
|
console.error('[WS v2] Server error:', message.code, message.message);
|
|
4931
|
+
// Handle CRUD request errors (requestId present)
|
|
4932
|
+
if (message.requestId) {
|
|
4933
|
+
const pendingReq = connection.pendingRequests.get(message.requestId);
|
|
4934
|
+
if (pendingReq) {
|
|
4935
|
+
connection.pendingRequests.delete(message.requestId);
|
|
4936
|
+
clearTimeout(pendingReq.timer);
|
|
4937
|
+
pendingReq.reject(new Error(`${message.code}: ${message.message}`));
|
|
4938
|
+
}
|
|
4939
|
+
}
|
|
4874
4940
|
if (message.subscriptionId) {
|
|
4875
4941
|
// Reject pending subscription if this is a subscription error
|
|
4876
4942
|
const pending = connection.pendingSubscriptions.get(message.subscriptionId);
|
|
@@ -4882,7 +4948,7 @@ function handleServerMessage(connection, message) {
|
|
|
4882
4948
|
const subscription = connection.subscriptions.get(message.subscriptionId);
|
|
4883
4949
|
if (subscription) {
|
|
4884
4950
|
for (const callback of subscription.callbacks) {
|
|
4885
|
-
(
|
|
4951
|
+
(_b = callback.onError) === null || _b === void 0 ? void 0 : _b.call(callback, new Error(`${message.code}: ${message.message}`));
|
|
4886
4952
|
}
|
|
4887
4953
|
}
|
|
4888
4954
|
}
|
|
@@ -5171,6 +5237,97 @@ async function doReconnectWithNewAuth() {
|
|
|
5171
5237
|
}
|
|
5172
5238
|
}
|
|
5173
5239
|
}
|
|
5240
|
+
// ============ CRUD over WebSocket ============
|
|
5241
|
+
const WS_REQUEST_TIMEOUT_MS = 30000;
|
|
5242
|
+
function generateRequestId() {
|
|
5243
|
+
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
5244
|
+
}
|
|
5245
|
+
/**
|
|
5246
|
+
* Returns true if there is an active v2 WebSocket connection open.
|
|
5247
|
+
*/
|
|
5248
|
+
function hasActiveConnection() {
|
|
5249
|
+
for (const connection of connections.values()) {
|
|
5250
|
+
if (connection.ws && connection.isConnected) {
|
|
5251
|
+
return true;
|
|
5252
|
+
}
|
|
5253
|
+
}
|
|
5254
|
+
return false;
|
|
5255
|
+
}
|
|
5256
|
+
async function sendRequest(msgBuilder) {
|
|
5257
|
+
const config = await getConfig();
|
|
5258
|
+
const appId = config.appId;
|
|
5259
|
+
const connection = await getOrCreateConnection(appId, config.isServer);
|
|
5260
|
+
// Wait for the connection to be open (getOrCreateConnection may return
|
|
5261
|
+
// while still connecting).
|
|
5262
|
+
if (!connection.isConnected && connection.ws) {
|
|
5263
|
+
await new Promise((resolve, reject) => {
|
|
5264
|
+
const timeout = setTimeout(() => {
|
|
5265
|
+
var _a;
|
|
5266
|
+
(_a = connection.ws) === null || _a === void 0 ? void 0 : _a.removeEventListener('open', onOpen);
|
|
5267
|
+
reject(new Error('WebSocket connection timeout'));
|
|
5268
|
+
}, 10000);
|
|
5269
|
+
const onOpen = () => { clearTimeout(timeout); resolve(); };
|
|
5270
|
+
if (connection.isConnected) {
|
|
5271
|
+
clearTimeout(timeout);
|
|
5272
|
+
resolve();
|
|
5273
|
+
return;
|
|
5274
|
+
}
|
|
5275
|
+
connection.ws.addEventListener('open', onOpen);
|
|
5276
|
+
});
|
|
5277
|
+
}
|
|
5278
|
+
if (!connection.ws || !connection.isConnected) {
|
|
5279
|
+
throw new Error('WebSocket connection not available');
|
|
5280
|
+
}
|
|
5281
|
+
const requestId = generateRequestId();
|
|
5282
|
+
const message = msgBuilder(requestId);
|
|
5283
|
+
return new Promise((resolve, reject) => {
|
|
5284
|
+
const timer = setTimeout(() => {
|
|
5285
|
+
connection.pendingRequests.delete(requestId);
|
|
5286
|
+
reject(new Error(`WebSocket request timed out after ${WS_REQUEST_TIMEOUT_MS}ms`));
|
|
5287
|
+
}, WS_REQUEST_TIMEOUT_MS);
|
|
5288
|
+
connection.pendingRequests.set(requestId, { resolve, reject, timer });
|
|
5289
|
+
try {
|
|
5290
|
+
connection.ws.send(JSON.stringify(message));
|
|
5291
|
+
}
|
|
5292
|
+
catch (error) {
|
|
5293
|
+
connection.pendingRequests.delete(requestId);
|
|
5294
|
+
clearTimeout(timer);
|
|
5295
|
+
reject(error);
|
|
5296
|
+
}
|
|
5297
|
+
});
|
|
5298
|
+
}
|
|
5299
|
+
async function wsGet(path) {
|
|
5300
|
+
return sendRequest((requestId) => ({
|
|
5301
|
+
type: 'get',
|
|
5302
|
+
requestId,
|
|
5303
|
+
path,
|
|
5304
|
+
}));
|
|
5305
|
+
}
|
|
5306
|
+
async function wsSet(documents) {
|
|
5307
|
+
return sendRequest((requestId) => ({
|
|
5308
|
+
type: 'set',
|
|
5309
|
+
requestId,
|
|
5310
|
+
documents,
|
|
5311
|
+
}));
|
|
5312
|
+
}
|
|
5313
|
+
async function wsQuery(path, opts) {
|
|
5314
|
+
return sendRequest((requestId) => (Object.assign(Object.assign(Object.assign(Object.assign({ type: 'query', requestId,
|
|
5315
|
+
path }, ((opts === null || opts === void 0 ? void 0 : opts.filter) ? { filter: opts.filter } : {})), ((opts === null || opts === void 0 ? void 0 : opts.sort) ? { sort: opts.sort } : {})), ((opts === null || opts === void 0 ? void 0 : opts.limit) !== undefined ? { limit: opts.limit } : {})), ((opts === null || opts === void 0 ? void 0 : opts.includeSubPaths) ? { includeSubPaths: opts.includeSubPaths } : {}))));
|
|
5316
|
+
}
|
|
5317
|
+
async function wsDelete(path) {
|
|
5318
|
+
return sendRequest((requestId) => ({
|
|
5319
|
+
type: 'delete',
|
|
5320
|
+
requestId,
|
|
5321
|
+
path,
|
|
5322
|
+
}));
|
|
5323
|
+
}
|
|
5324
|
+
async function wsGetMany(paths) {
|
|
5325
|
+
return sendRequest((requestId) => ({
|
|
5326
|
+
type: 'getMany',
|
|
5327
|
+
requestId,
|
|
5328
|
+
paths,
|
|
5329
|
+
}));
|
|
5330
|
+
}
|
|
5174
5331
|
|
|
5175
5332
|
/**
|
|
5176
5333
|
* WebSocket Subscription Module
|
|
@@ -5294,6 +5451,28 @@ class ReactNativeSessionManager {
|
|
|
5294
5451
|
/* STORE */
|
|
5295
5452
|
/* ------------------------------------------------------------------ */
|
|
5296
5453
|
static async storeSession(address, accessToken, idToken, refreshToken) {
|
|
5454
|
+
// JWT-wallet binding: refuse to store a session whose idToken is bound
|
|
5455
|
+
// to a different wallet than `address`. Prevents races that would otherwise
|
|
5456
|
+
// leave storage with mismatched address/token state.
|
|
5457
|
+
try {
|
|
5458
|
+
const payloadB64 = idToken.split(".")[1];
|
|
5459
|
+
if (payloadB64) {
|
|
5460
|
+
const payload = JSON.parse(this.decodeBase64Url(payloadB64));
|
|
5461
|
+
const tokenWallet = payload["custom:walletAddress"];
|
|
5462
|
+
if (tokenWallet && tokenWallet !== address) {
|
|
5463
|
+
throw new Error(`[ReactNativeSessionManager] Refusing to store session: address (${address}) does not match idToken custom:walletAddress (${tokenWallet})`);
|
|
5464
|
+
}
|
|
5465
|
+
if (!tokenWallet) {
|
|
5466
|
+
console.warn("[ReactNativeSessionManager] storeSession: idToken has no custom:walletAddress claim — writing without validation");
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
catch (err) {
|
|
5471
|
+
if (typeof (err === null || err === void 0 ? void 0 : err.message) === "string" && err.message.includes("Refusing to store session")) {
|
|
5472
|
+
throw err;
|
|
5473
|
+
}
|
|
5474
|
+
console.warn("[ReactNativeSessionManager] storeSession: failed to decode idToken for validation:", err);
|
|
5475
|
+
}
|
|
5297
5476
|
const config = await getConfig();
|
|
5298
5477
|
const currentAppId = config.appId;
|
|
5299
5478
|
this.getStorage().setItem(this.TAROBASE_SESSION_STORAGE_KEY, JSON.stringify({
|
|
@@ -5403,5 +5582,5 @@ class ReactNativeSessionManager {
|
|
|
5403
5582
|
}
|
|
5404
5583
|
ReactNativeSessionManager.TAROBASE_SESSION_STORAGE_KEY = "tarobase_session_storage";
|
|
5405
5584
|
|
|
5406
|
-
export { InsufficientBalanceError, ReactNativeSessionManager, ServerSessionManager, WebSessionManager, aggregate, buildSetDocumentsTransaction, clearCache, closeAllSubscriptions, convertRemainingAccounts, count, createSessionWithPrivy, createSessionWithSignature, genAuthNonce, genSolanaMessage, get, getCachedData, getConfig, getFiles, getIdToken, getMany, init, reconnectWithNewAuth, refreshSession, runExpression, runExpressionMany, runQuery, runQueryMany, set, setFile, setMany, signAndSubmitTransaction, signMessage, signSessionCreateMessage, signTransaction, subscribe };
|
|
5585
|
+
export { InsufficientBalanceError, ReactNativeSessionManager, ServerSessionManager, WebSessionManager, aggregate, buildSetDocumentsTransaction, clearCache, closeAllSubscriptions, convertRemainingAccounts, count, createSessionWithPrivy, createSessionWithSignature, genAuthNonce, genSolanaMessage, get, getCachedData, getConfig, getFiles, getIdToken, getMany, hasActiveConnection, init, reconnectWithNewAuth, refreshSession, runExpression, runExpressionMany, runQuery, runQueryMany, set, setFile, setMany, signAndSubmitTransaction, signMessage, signSessionCreateMessage, signTransaction, subscribe, wsDelete, wsGet, wsGetMany, wsQuery, wsSet };
|
|
5407
5586
|
//# sourceMappingURL=index.mjs.map
|