@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
|
@@ -40,6 +40,23 @@ export declare function reconnectWithNewAuthV2(): Promise<void>;
|
|
|
40
40
|
* a complete teardown and rebuild of connections.
|
|
41
41
|
*/
|
|
42
42
|
export declare function forceReconnectV2(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Returns true if there is an active v2 WebSocket connection open.
|
|
45
|
+
*/
|
|
46
|
+
export declare function hasActiveConnection(): boolean;
|
|
47
|
+
export declare function wsGet(path: string): Promise<any>;
|
|
48
|
+
export declare function wsSet(documents: Array<{
|
|
49
|
+
destinationPath: string;
|
|
50
|
+
document: any;
|
|
51
|
+
}>): Promise<any>;
|
|
52
|
+
export declare function wsQuery(path: string, opts?: {
|
|
53
|
+
filter?: any;
|
|
54
|
+
sort?: any;
|
|
55
|
+
limit?: number;
|
|
56
|
+
includeSubPaths?: boolean;
|
|
57
|
+
}): Promise<any>;
|
|
58
|
+
export declare function wsDelete(path: string): Promise<any>;
|
|
59
|
+
export declare function wsGetMany(paths: string[]): Promise<any>;
|
|
43
60
|
declare global {
|
|
44
61
|
interface Window {
|
|
45
62
|
CUSTOM_TAROBASE_APP_ID_HEADER?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { init } from './client/config';
|
|
|
2
2
|
export { getConfig, ClientConfig } from './client/config';
|
|
3
3
|
export { get, getMany, set, setMany, setFile, getFiles, runQuery, runQueryMany, runExpression, runExpressionMany, signMessage, signTransaction, signAndSubmitTransaction, count, aggregate, RequestOverrides, SetOptions, GetOptions, RunQueryOptions, CountOptions, AggregateOptions, AggregateOperation, AggregateResult, RunExpressionOptions, RunExpressionResult, InsufficientBalanceError, GetManyResult } from './client/operations';
|
|
4
4
|
export { subscribe, closeAllSubscriptions, clearCache, getCachedData, reconnectWithNewAuth } from './client/subscription';
|
|
5
|
+
export { hasActiveConnection, wsGet, wsSet, wsQuery, wsDelete, wsGetMany } from './client/subscription-v2';
|
|
5
6
|
export * from './types';
|
|
6
7
|
export { getIdToken } from './utils/utils';
|
|
7
8
|
export { WebSessionManager } from './utils/web-session-manager';
|
package/dist/index.js
CHANGED
|
@@ -396,6 +396,28 @@ class WebSessionManager {
|
|
|
396
396
|
static async storeSession(address, accessToken, idToken, refreshToken) {
|
|
397
397
|
if (typeof window === "undefined")
|
|
398
398
|
return;
|
|
399
|
+
// JWT-wallet binding: refuse to store a session whose idToken is bound
|
|
400
|
+
// to a different wallet than `address`. Prevents races that would otherwise
|
|
401
|
+
// leave localStorage with mismatched address/token state.
|
|
402
|
+
try {
|
|
403
|
+
const payloadB64 = idToken.split(".")[1];
|
|
404
|
+
if (payloadB64) {
|
|
405
|
+
const payload = JSON.parse(this.decodeBase64Url(payloadB64));
|
|
406
|
+
const tokenWallet = payload["custom:walletAddress"];
|
|
407
|
+
if (tokenWallet && tokenWallet !== address) {
|
|
408
|
+
throw new Error(`[WebSessionManager] Refusing to store session: address (${address}) does not match idToken custom:walletAddress (${tokenWallet})`);
|
|
409
|
+
}
|
|
410
|
+
if (!tokenWallet) {
|
|
411
|
+
console.warn("[WebSessionManager] storeSession: idToken has no custom:walletAddress claim — writing without validation");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
if (typeof (err === null || err === void 0 ? void 0 : err.message) === "string" && err.message.includes("Refusing to store session")) {
|
|
417
|
+
throw err;
|
|
418
|
+
}
|
|
419
|
+
console.warn("[WebSessionManager] storeSession: failed to decode idToken for validation:", err);
|
|
420
|
+
}
|
|
399
421
|
const config = await getConfig();
|
|
400
422
|
const currentAppId = config.appId;
|
|
401
423
|
localStorage.setItem(this.TAROBASE_SESSION_STORAGE_KEY, JSON.stringify({
|
|
@@ -4742,6 +4764,7 @@ async function getOrCreateConnection(appId, isServer) {
|
|
|
4742
4764
|
subscriptions: new Map(),
|
|
4743
4765
|
pendingSubscriptions: new Map(),
|
|
4744
4766
|
pendingUnsubscriptions: new Map(),
|
|
4767
|
+
pendingRequests: new Map(),
|
|
4745
4768
|
isConnecting: false,
|
|
4746
4769
|
isConnected: false,
|
|
4747
4770
|
appId,
|
|
@@ -4840,11 +4863,17 @@ async function getOrCreateConnection(appId, isServer) {
|
|
|
4840
4863
|
clearInterval(connection.tokenRefreshTimer);
|
|
4841
4864
|
connection.tokenRefreshTimer = null;
|
|
4842
4865
|
}
|
|
4866
|
+
// Reject all pending WS CRUD requests on disconnect (fail fast)
|
|
4867
|
+
for (const [id, pending] of connection.pendingRequests) {
|
|
4868
|
+
clearTimeout(pending.timer);
|
|
4869
|
+
pending.reject(new Error('WebSocket disconnected'));
|
|
4870
|
+
}
|
|
4871
|
+
connection.pendingRequests.clear();
|
|
4843
4872
|
});
|
|
4844
4873
|
return connection;
|
|
4845
4874
|
}
|
|
4846
4875
|
function handleServerMessage(connection, message) {
|
|
4847
|
-
var _a;
|
|
4876
|
+
var _a, _b;
|
|
4848
4877
|
switch (message.type) {
|
|
4849
4878
|
case 'subscribed': {
|
|
4850
4879
|
const subscription = connection.subscriptions.get(message.subscriptionId);
|
|
@@ -4889,8 +4918,45 @@ function handleServerMessage(connection, message) {
|
|
|
4889
4918
|
}
|
|
4890
4919
|
break;
|
|
4891
4920
|
}
|
|
4921
|
+
case 'response': {
|
|
4922
|
+
const pendingReq = connection.pendingRequests.get(message.requestId);
|
|
4923
|
+
if (pendingReq) {
|
|
4924
|
+
connection.pendingRequests.delete(message.requestId);
|
|
4925
|
+
clearTimeout(pendingReq.timer);
|
|
4926
|
+
if (message.status >= 400) {
|
|
4927
|
+
pendingReq.reject(new Error(`Request failed with status ${message.status}`));
|
|
4928
|
+
}
|
|
4929
|
+
else {
|
|
4930
|
+
pendingReq.resolve(message.data);
|
|
4931
|
+
}
|
|
4932
|
+
}
|
|
4933
|
+
break;
|
|
4934
|
+
}
|
|
4935
|
+
case 'setResponse': {
|
|
4936
|
+
const pendingSet = connection.pendingRequests.get(message.requestId);
|
|
4937
|
+
if (pendingSet) {
|
|
4938
|
+
connection.pendingRequests.delete(message.requestId);
|
|
4939
|
+
clearTimeout(pendingSet.timer);
|
|
4940
|
+
if (message.statusCode >= 400) {
|
|
4941
|
+
pendingSet.reject(new Error(((_a = message.body) === null || _a === void 0 ? void 0 : _a.message) || `Set failed with status ${message.statusCode}`));
|
|
4942
|
+
}
|
|
4943
|
+
else {
|
|
4944
|
+
pendingSet.resolve(message.body);
|
|
4945
|
+
}
|
|
4946
|
+
}
|
|
4947
|
+
break;
|
|
4948
|
+
}
|
|
4892
4949
|
case 'error': {
|
|
4893
4950
|
console.error('[WS v2] Server error:', message.code, message.message);
|
|
4951
|
+
// Handle CRUD request errors (requestId present)
|
|
4952
|
+
if (message.requestId) {
|
|
4953
|
+
const pendingReq = connection.pendingRequests.get(message.requestId);
|
|
4954
|
+
if (pendingReq) {
|
|
4955
|
+
connection.pendingRequests.delete(message.requestId);
|
|
4956
|
+
clearTimeout(pendingReq.timer);
|
|
4957
|
+
pendingReq.reject(new Error(`${message.code}: ${message.message}`));
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4894
4960
|
if (message.subscriptionId) {
|
|
4895
4961
|
// Reject pending subscription if this is a subscription error
|
|
4896
4962
|
const pending = connection.pendingSubscriptions.get(message.subscriptionId);
|
|
@@ -4902,7 +4968,7 @@ function handleServerMessage(connection, message) {
|
|
|
4902
4968
|
const subscription = connection.subscriptions.get(message.subscriptionId);
|
|
4903
4969
|
if (subscription) {
|
|
4904
4970
|
for (const callback of subscription.callbacks) {
|
|
4905
|
-
(
|
|
4971
|
+
(_b = callback.onError) === null || _b === void 0 ? void 0 : _b.call(callback, new Error(`${message.code}: ${message.message}`));
|
|
4906
4972
|
}
|
|
4907
4973
|
}
|
|
4908
4974
|
}
|
|
@@ -5191,6 +5257,97 @@ async function doReconnectWithNewAuth() {
|
|
|
5191
5257
|
}
|
|
5192
5258
|
}
|
|
5193
5259
|
}
|
|
5260
|
+
// ============ CRUD over WebSocket ============
|
|
5261
|
+
const WS_REQUEST_TIMEOUT_MS = 30000;
|
|
5262
|
+
function generateRequestId() {
|
|
5263
|
+
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
5264
|
+
}
|
|
5265
|
+
/**
|
|
5266
|
+
* Returns true if there is an active v2 WebSocket connection open.
|
|
5267
|
+
*/
|
|
5268
|
+
function hasActiveConnection() {
|
|
5269
|
+
for (const connection of connections.values()) {
|
|
5270
|
+
if (connection.ws && connection.isConnected) {
|
|
5271
|
+
return true;
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
return false;
|
|
5275
|
+
}
|
|
5276
|
+
async function sendRequest(msgBuilder) {
|
|
5277
|
+
const config = await getConfig();
|
|
5278
|
+
const appId = config.appId;
|
|
5279
|
+
const connection = await getOrCreateConnection(appId, config.isServer);
|
|
5280
|
+
// Wait for the connection to be open (getOrCreateConnection may return
|
|
5281
|
+
// while still connecting).
|
|
5282
|
+
if (!connection.isConnected && connection.ws) {
|
|
5283
|
+
await new Promise((resolve, reject) => {
|
|
5284
|
+
const timeout = setTimeout(() => {
|
|
5285
|
+
var _a;
|
|
5286
|
+
(_a = connection.ws) === null || _a === void 0 ? void 0 : _a.removeEventListener('open', onOpen);
|
|
5287
|
+
reject(new Error('WebSocket connection timeout'));
|
|
5288
|
+
}, 10000);
|
|
5289
|
+
const onOpen = () => { clearTimeout(timeout); resolve(); };
|
|
5290
|
+
if (connection.isConnected) {
|
|
5291
|
+
clearTimeout(timeout);
|
|
5292
|
+
resolve();
|
|
5293
|
+
return;
|
|
5294
|
+
}
|
|
5295
|
+
connection.ws.addEventListener('open', onOpen);
|
|
5296
|
+
});
|
|
5297
|
+
}
|
|
5298
|
+
if (!connection.ws || !connection.isConnected) {
|
|
5299
|
+
throw new Error('WebSocket connection not available');
|
|
5300
|
+
}
|
|
5301
|
+
const requestId = generateRequestId();
|
|
5302
|
+
const message = msgBuilder(requestId);
|
|
5303
|
+
return new Promise((resolve, reject) => {
|
|
5304
|
+
const timer = setTimeout(() => {
|
|
5305
|
+
connection.pendingRequests.delete(requestId);
|
|
5306
|
+
reject(new Error(`WebSocket request timed out after ${WS_REQUEST_TIMEOUT_MS}ms`));
|
|
5307
|
+
}, WS_REQUEST_TIMEOUT_MS);
|
|
5308
|
+
connection.pendingRequests.set(requestId, { resolve, reject, timer });
|
|
5309
|
+
try {
|
|
5310
|
+
connection.ws.send(JSON.stringify(message));
|
|
5311
|
+
}
|
|
5312
|
+
catch (error) {
|
|
5313
|
+
connection.pendingRequests.delete(requestId);
|
|
5314
|
+
clearTimeout(timer);
|
|
5315
|
+
reject(error);
|
|
5316
|
+
}
|
|
5317
|
+
});
|
|
5318
|
+
}
|
|
5319
|
+
async function wsGet(path) {
|
|
5320
|
+
return sendRequest((requestId) => ({
|
|
5321
|
+
type: 'get',
|
|
5322
|
+
requestId,
|
|
5323
|
+
path,
|
|
5324
|
+
}));
|
|
5325
|
+
}
|
|
5326
|
+
async function wsSet(documents) {
|
|
5327
|
+
return sendRequest((requestId) => ({
|
|
5328
|
+
type: 'set',
|
|
5329
|
+
requestId,
|
|
5330
|
+
documents,
|
|
5331
|
+
}));
|
|
5332
|
+
}
|
|
5333
|
+
async function wsQuery(path, opts) {
|
|
5334
|
+
return sendRequest((requestId) => (Object.assign(Object.assign(Object.assign(Object.assign({ type: 'query', requestId,
|
|
5335
|
+
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 } : {}))));
|
|
5336
|
+
}
|
|
5337
|
+
async function wsDelete(path) {
|
|
5338
|
+
return sendRequest((requestId) => ({
|
|
5339
|
+
type: 'delete',
|
|
5340
|
+
requestId,
|
|
5341
|
+
path,
|
|
5342
|
+
}));
|
|
5343
|
+
}
|
|
5344
|
+
async function wsGetMany(paths) {
|
|
5345
|
+
return sendRequest((requestId) => ({
|
|
5346
|
+
type: 'getMany',
|
|
5347
|
+
requestId,
|
|
5348
|
+
paths,
|
|
5349
|
+
}));
|
|
5350
|
+
}
|
|
5194
5351
|
|
|
5195
5352
|
/**
|
|
5196
5353
|
* WebSocket Subscription Module
|
|
@@ -5314,6 +5471,28 @@ class ReactNativeSessionManager {
|
|
|
5314
5471
|
/* STORE */
|
|
5315
5472
|
/* ------------------------------------------------------------------ */
|
|
5316
5473
|
static async storeSession(address, accessToken, idToken, refreshToken) {
|
|
5474
|
+
// JWT-wallet binding: refuse to store a session whose idToken is bound
|
|
5475
|
+
// to a different wallet than `address`. Prevents races that would otherwise
|
|
5476
|
+
// leave storage with mismatched address/token state.
|
|
5477
|
+
try {
|
|
5478
|
+
const payloadB64 = idToken.split(".")[1];
|
|
5479
|
+
if (payloadB64) {
|
|
5480
|
+
const payload = JSON.parse(this.decodeBase64Url(payloadB64));
|
|
5481
|
+
const tokenWallet = payload["custom:walletAddress"];
|
|
5482
|
+
if (tokenWallet && tokenWallet !== address) {
|
|
5483
|
+
throw new Error(`[ReactNativeSessionManager] Refusing to store session: address (${address}) does not match idToken custom:walletAddress (${tokenWallet})`);
|
|
5484
|
+
}
|
|
5485
|
+
if (!tokenWallet) {
|
|
5486
|
+
console.warn("[ReactNativeSessionManager] storeSession: idToken has no custom:walletAddress claim — writing without validation");
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5489
|
+
}
|
|
5490
|
+
catch (err) {
|
|
5491
|
+
if (typeof (err === null || err === void 0 ? void 0 : err.message) === "string" && err.message.includes("Refusing to store session")) {
|
|
5492
|
+
throw err;
|
|
5493
|
+
}
|
|
5494
|
+
console.warn("[ReactNativeSessionManager] storeSession: failed to decode idToken for validation:", err);
|
|
5495
|
+
}
|
|
5317
5496
|
const config = await getConfig();
|
|
5318
5497
|
const currentAppId = config.appId;
|
|
5319
5498
|
this.getStorage().setItem(this.TAROBASE_SESSION_STORAGE_KEY, JSON.stringify({
|
|
@@ -5443,6 +5622,7 @@ exports.getConfig = getConfig;
|
|
|
5443
5622
|
exports.getFiles = getFiles;
|
|
5444
5623
|
exports.getIdToken = getIdToken;
|
|
5445
5624
|
exports.getMany = getMany;
|
|
5625
|
+
exports.hasActiveConnection = hasActiveConnection;
|
|
5446
5626
|
exports.init = init;
|
|
5447
5627
|
exports.reconnectWithNewAuth = reconnectWithNewAuth;
|
|
5448
5628
|
exports.refreshSession = refreshSession;
|
|
@@ -5458,4 +5638,9 @@ exports.signMessage = signMessage;
|
|
|
5458
5638
|
exports.signSessionCreateMessage = signSessionCreateMessage;
|
|
5459
5639
|
exports.signTransaction = signTransaction;
|
|
5460
5640
|
exports.subscribe = subscribe;
|
|
5641
|
+
exports.wsDelete = wsDelete;
|
|
5642
|
+
exports.wsGet = wsGet;
|
|
5643
|
+
exports.wsGetMany = wsGetMany;
|
|
5644
|
+
exports.wsQuery = wsQuery;
|
|
5645
|
+
exports.wsSet = wsSet;
|
|
5461
5646
|
//# sourceMappingURL=index.js.map
|