@pooflabs/core 0.0.38 → 0.0.39

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/index.mjs CHANGED
@@ -3027,31 +3027,39 @@ function loadKeypairFromEnv() {
3027
3027
  }
3028
3028
  }
3029
3029
  /* ------------------------------------------------------------------ */
3030
- /* REAL createSession() */
3031
- /* ------------------------------------------------------------------ */
3032
- async function createSession() {
3033
- const kp = loadKeypairFromEnv();
3034
- const address = kp.publicKey.toBase58();
3035
- /* fetch nonce from auth API */
3036
- const nonce = await genAuthNonce();
3037
- const message = await genSolanaMessage(address, nonce);
3038
- /* sign the message */
3039
- const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
3040
- const signature = bufferExports.Buffer.from(sigBytes).toString("base64");
3041
- /* call auth API */
3042
- const { accessToken, idToken, refreshToken, } = await createSessionWithSignature(address, message, signature);
3043
- return { address, accessToken, idToken, refreshToken };
3044
- }
3045
- /* ------------------------------------------------------------------ */
3046
- /* SINGLETON */
3030
+ /* SESSION MANAGER */
3047
3031
  /* ------------------------------------------------------------------ */
3048
3032
  class ServerSessionManager {
3049
- /* Private constructor prevents external `new` */
3050
- constructor() {
3051
- /* Private cache (lives for the life of the process) */
3033
+ constructor(keypair) {
3034
+ /* Private cache (lives for the life of the instance) */
3052
3035
  this.session = null;
3053
3036
  /* Coalesce concurrent getSession() calls into a single in-flight request */
3054
3037
  this.pendingSession = null;
3038
+ this.keypair = keypair !== null && keypair !== void 0 ? keypair : null;
3039
+ }
3040
+ /**
3041
+ * Create a session manager for a specific keypair.
3042
+ * Each instance has its own independent session cache.
3043
+ */
3044
+ static forKeypair(keypair) {
3045
+ return new ServerSessionManager(keypair);
3046
+ }
3047
+ /* ---------------------------------------------- *
3048
+ * Session creation (instance method)
3049
+ * ---------------------------------------------- */
3050
+ async createSession() {
3051
+ var _a;
3052
+ const kp = (_a = this.keypair) !== null && _a !== void 0 ? _a : loadKeypairFromEnv();
3053
+ const address = kp.publicKey.toBase58();
3054
+ /* fetch nonce from auth API */
3055
+ const nonce = await genAuthNonce();
3056
+ const message = await genSolanaMessage(address, nonce);
3057
+ /* sign the message */
3058
+ const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
3059
+ const signature = bufferExports.Buffer.from(sigBytes).toString("base64");
3060
+ /* call auth API */
3061
+ const { accessToken, idToken, refreshToken, } = await createSessionWithSignature(address, message, signature);
3062
+ return { address, accessToken, idToken, refreshToken };
3055
3063
  }
3056
3064
  /* ---------------------------------------------- *
3057
3065
  * GET (lazy-fetch)
@@ -3065,7 +3073,7 @@ class ServerSessionManager {
3065
3073
  if (this.pendingSession !== null) {
3066
3074
  return this.pendingSession;
3067
3075
  }
3068
- this.pendingSession = createSession()
3076
+ this.pendingSession = this.createSession()
3069
3077
  .then((session) => {
3070
3078
  this.session = session;
3071
3079
  return session;
@@ -3082,7 +3090,7 @@ class ServerSessionManager {
3082
3090
  this.session = session;
3083
3091
  }
3084
3092
  /* ---------------------------------------------- *
3085
- * CLEAR (e.g. after logout)
3093
+ * CLEAR (e.g. after logout or 401 retry)
3086
3094
  * ---------------------------------------------- */
3087
3095
  clearSession() {
3088
3096
  this.session = null;
@@ -3103,7 +3111,7 @@ class ServerSessionManager {
3103
3111
  return (_b = (_a = this.session) === null || _a === void 0 ? void 0 : _a.refreshToken) !== null && _b !== void 0 ? _b : null;
3104
3112
  }
3105
3113
  }
3106
- /* The single, shared instance */
3114
+ /* The default singleton instance (reads keypair from env) */
3107
3115
  ServerSessionManager.instance = new ServerSessionManager();
3108
3116
 
3109
3117
  var serverSessionManager = /*#__PURE__*/Object.freeze({
@@ -3218,7 +3226,11 @@ async function makeApiRequest(method, urlPath, data, _overrides) {
3218
3226
  ServerSessionManager.instance.clearSession();
3219
3227
  };
3220
3228
  async function executeRequest() {
3221
- const authHeader = await createAuthHeader(config.isServer);
3229
+ // When _getAuthHeaders is provided (wallet client), use it as the sole auth source.
3230
+ // Otherwise use the global createAuthHeader (default path).
3231
+ const authHeader = (_overrides === null || _overrides === void 0 ? void 0 : _overrides._getAuthHeaders)
3232
+ ? await _overrides._getAuthHeaders()
3233
+ : await createAuthHeader(config.isServer);
3222
3234
  const headers = Object.assign({ "Content-Type": "application/json", "X-Public-App-Id": config.appId, "X-App-Id": config.appId }, authHeader);
3223
3235
  if (typeof window !== "undefined" &&
3224
3236
  window.CUSTOM_TAROBASE_APP_ID_HEADER) {
@@ -3247,6 +3259,13 @@ async function makeApiRequest(method, urlPath, data, _overrides) {
3247
3259
  }
3248
3260
  catch (error) {
3249
3261
  if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 401) {
3262
+ // Wallet client path: use client-specific retry, skip global fallback entirely
3263
+ if (_overrides === null || _overrides === void 0 ? void 0 : _overrides._clearAuth) {
3264
+ await _overrides._clearAuth();
3265
+ return await executeRequest();
3266
+ // If this throws, let it propagate — do NOT clear global ServerSessionManager
3267
+ }
3268
+ // Default path (no wallet client): refresh global session
3250
3269
  try {
3251
3270
  const refreshed = await refreshAuthSessionOnce(config.appId, config.isServer);
3252
3271
  if (!refreshed) {
@@ -3255,8 +3274,7 @@ async function makeApiRequest(method, urlPath, data, _overrides) {
3255
3274
  return await executeRequest();
3256
3275
  }
3257
3276
  catch (_refreshError) {
3258
- // Server-side fallback: clear cached session and retry once to force
3259
- // createSession() with a fresh Cognito session.
3277
+ // Server-side fallback: clear global session and retry once
3260
3278
  if (config.isServer && !hasRetriedAfterServerSessionReset) {
3261
3279
  hasRetriedAfterServerSessionReset = true;
3262
3280
  await clearServerSession();
@@ -3374,6 +3392,21 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
3374
3392
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
3375
3393
  };
3376
3394
 
3395
+ /**
3396
+ * Thrown when a user's wallet doesn't have enough SOL to cover the transaction.
3397
+ * Apps can catch this to trigger an onramp/funding flow.
3398
+ */
3399
+ class InsufficientBalanceError extends Error {
3400
+ constructor(address, balanceLamports, estimatedCostLamports, deficitLamports, deficitSol) {
3401
+ super(`Insufficient balance: wallet ${address} needs ~${deficitSol.toFixed(6)} SOL more to complete this transaction.`);
3402
+ this.name = 'InsufficientBalanceError';
3403
+ this.address = address;
3404
+ this.balanceLamports = balanceLamports;
3405
+ this.estimatedCostLamports = estimatedCostLamports;
3406
+ this.deficitLamports = deficitLamports;
3407
+ this.deficitSol = deficitSol;
3408
+ }
3409
+ }
3377
3410
  // Cache for get responses
3378
3411
  const getCache = {};
3379
3412
  // Track in-flight requests to coalesce multiple identical requests
@@ -3534,6 +3567,7 @@ async function aggregate(path, operation, opts = {}) {
3534
3567
  return { value: parseAggregateValue(result) };
3535
3568
  }
3536
3569
  async function get(path, opts = {}) {
3570
+ var _a;
3537
3571
  try {
3538
3572
  let normalizedPath = path.startsWith("/") ? path.slice(1) : path;
3539
3573
  // Remove last '*' if it exists in the path
@@ -3543,12 +3577,13 @@ async function get(path, opts = {}) {
3543
3577
  if (!normalizedPath || normalizedPath.length === 0) {
3544
3578
  return new Error("Invalid path provided.");
3545
3579
  }
3546
- // Create cache key combining path, prompt, includeSubPaths, shape, limit, and cursor
3580
+ // Create cache key combining path, prompt, includeSubPaths, shape, limit, cursor, and wallet
3547
3581
  const shapeKey = opts.shape ? JSON.stringify(opts.shape) : '';
3548
3582
  const includeSubPathsKey = opts.includeSubPaths ? ':subpaths' : '';
3549
3583
  const limitKey = opts.limit !== undefined ? `:l${opts.limit}` : '';
3550
3584
  const cursorKey = opts.cursor ? `:c${hashForKey$1(opts.cursor)}` : '';
3551
- const cacheKey = `${normalizedPath}:${opts.prompt || ''}${includeSubPathsKey}:${shapeKey}${limitKey}${cursorKey}`;
3585
+ const walletSuffix = ((_a = opts._overrides) === null || _a === void 0 ? void 0 : _a._walletAddress) ? `:w${opts._overrides._walletAddress}` : '';
3586
+ const cacheKey = `${normalizedPath}:${opts.prompt || ''}${includeSubPathsKey}:${shapeKey}${limitKey}${cursorKey}${walletSuffix}`;
3552
3587
  const now = Date.now();
3553
3588
  // Check for valid cache entry if not bypassing cache
3554
3589
  if (!opts.bypassCache && getCache[cacheKey] && now < getCache[cacheKey].expiresAt) {
@@ -3687,10 +3722,11 @@ async function set(path, document, options) {
3687
3722
  return result;
3688
3723
  }
3689
3724
  async function setMany(many, options) {
3725
+ var _a, _b, _c, _d, _e, _f, _g;
3690
3726
  // Returns the data that was set, or undefined if the document was already set.
3691
3727
  try {
3692
3728
  const config = await getConfig();
3693
- const authProvider = config.authProvider;
3729
+ const authProvider = (_b = (_a = options === null || options === void 0 ? void 0 : options._overrides) === null || _a === void 0 ? void 0 : _a.authProvider) !== null && _b !== void 0 ? _b : config.authProvider;
3694
3730
  if (!authProvider) {
3695
3731
  throw new Error('Auth provider not initialized');
3696
3732
  }
@@ -3721,7 +3757,18 @@ async function setMany(many, options) {
3721
3757
  }
3722
3758
  documents.push({ destinationPath, document });
3723
3759
  }
3724
- const setResponse = await makeApiRequest('PUT', `items`, { documents }, options === null || options === void 0 ? void 0 : options._overrides);
3760
+ let setResponse;
3761
+ try {
3762
+ setResponse = await makeApiRequest('PUT', `items`, { documents }, options === null || options === void 0 ? void 0 : options._overrides);
3763
+ }
3764
+ catch (error) {
3765
+ if ((error === null || error === void 0 ? void 0 : error.statusCode) === 402 && (error === null || error === void 0 ? void 0 : error.error) === 'INSUFFICIENT_BALANCE') {
3766
+ const deficitLamports = Number((_c = error.deficitLamports) !== null && _c !== void 0 ? _c : 0);
3767
+ const deficitSol = Number((_d = error.deficitSol) !== null && _d !== void 0 ? _d : deficitLamports / 1000000000);
3768
+ throw new InsufficientBalanceError(String((_e = error.address) !== null && _e !== void 0 ? _e : ''), Number((_f = error.balanceLamports) !== null && _f !== void 0 ? _f : 0), Number((_g = error.estimatedCostLamports) !== null && _g !== void 0 ? _g : 0), deficitLamports, deficitSol);
3769
+ }
3770
+ throw error;
3771
+ }
3725
3772
  // Invalidate caches and pending requests for affected paths
3726
3773
  pathsToInvalidate.forEach(path => {
3727
3774
  clearCacheByPrefix(path);
@@ -3762,7 +3809,7 @@ async function setMany(many, options) {
3762
3809
  else if (setResponse.data &&
3763
3810
  typeof setResponse.data === 'object' &&
3764
3811
  setResponse.data.success === true) {
3765
- const _a = setResponse.data, { success: _success } = _a, rest = __rest(_a, ["success"]);
3812
+ const _h = setResponse.data, { success: _success } = _h, rest = __rest(_h, ["success"]);
3766
3813
  return Object.assign(Object.assign(Object.assign({}, documents.map(d => d.document)), rest), { transactionId: null });
3767
3814
  }
3768
3815
  else {
@@ -3829,7 +3876,7 @@ async function setMany(many, options) {
3829
3876
  return transactionResult;
3830
3877
  }
3831
3878
  async function handleOffchainTransaction(tx, authProvider, options) {
3832
- var _a, _b, _c, _d, _e;
3879
+ var _a, _b, _c, _d, _e, _f;
3833
3880
  const config = await getConfig();
3834
3881
  // 1. Sign the transaction message using mock signing for offchain transactions
3835
3882
  // Use signMessageMock if available (OffchainAuthProvider), otherwise fall back to signMessage
@@ -3848,32 +3895,18 @@ async function setMany(many, options) {
3848
3895
  signedTransaction: bufferExports.Buffer.from(JSON.stringify(signedTx)).toString('base64')
3849
3896
  };
3850
3897
  }
3851
- // 4. Submit to RPC endpoint
3852
- // Use appId from headers if provided, otherwise fallback to config.appId
3898
+ // 4. Submit to RPC endpoint via makeApiRequest (gets auth headers + 401 retry)
3853
3899
  const appId = ((_b = (_a = options === null || options === void 0 ? void 0 : options._overrides) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b['x-app-id']) || ((_d = (_c = options === null || options === void 0 ? void 0 : options._overrides) === null || _c === void 0 ? void 0 : _c.headers) === null || _d === void 0 ? void 0 : _d['X-App-Id']) || config.appId;
3854
- const rpcUrl = `${config.apiUrl}/app/${appId}/rpc`;
3855
- // Build headers, applying overrides if provided
3856
- const headers = {
3857
- 'Content-Type': 'application/json',
3858
- };
3859
- if ((_e = options === null || options === void 0 ? void 0 : options._overrides) === null || _e === void 0 ? void 0 : _e.headers) {
3860
- Object.assign(headers, options._overrides.headers);
3861
- }
3862
- const rpcResponse = await fetch(rpcUrl, {
3863
- method: 'POST',
3864
- headers,
3865
- body: JSON.stringify({
3866
- jsonrpc: '2.0',
3867
- id: 1,
3868
- method: 'sendTransaction',
3869
- params: [bufferExports.Buffer.from(JSON.stringify(signedTx)).toString('base64')]
3870
- })
3871
- });
3872
- const result = await rpcResponse.json();
3873
- if (result.error) {
3874
- throw new Error(result.error.message);
3900
+ const rpcResponse = await makeApiRequest('POST', `/app/${appId}/rpc`, {
3901
+ jsonrpc: '2.0',
3902
+ id: 1,
3903
+ method: 'sendTransaction',
3904
+ params: [bufferExports.Buffer.from(JSON.stringify(signedTx)).toString('base64')]
3905
+ }, options === null || options === void 0 ? void 0 : options._overrides);
3906
+ if ((_e = rpcResponse.data) === null || _e === void 0 ? void 0 : _e.error) {
3907
+ throw new Error(rpcResponse.data.error.message);
3875
3908
  }
3876
- return { signature: result.result };
3909
+ return { signature: (_f = rpcResponse.data) === null || _f === void 0 ? void 0 : _f.result };
3877
3910
  }
3878
3911
  }
3879
3912
  // Helper to clear cache entries by prefix
@@ -4706,5 +4739,5 @@ async function reconnectWithNewAuth() {
4706
4739
  return reconnectWithNewAuthV2();
4707
4740
  }
4708
4741
 
4709
- export { ServerSessionManager, WebSessionManager, aggregate, buildSetDocumentsTransaction, clearCache, closeAllSubscriptions, convertRemainingAccounts, count, createSessionWithPrivy, createSessionWithSignature, genAuthNonce, genSolanaMessage, get, getCachedData, getConfig, getFiles, getIdToken, init, reconnectWithNewAuth, refreshSession, runExpression, runExpressionMany, runQuery, runQueryMany, set, setFile, setMany, signAndSubmitTransaction, signMessage, signSessionCreateMessage, signTransaction, subscribe };
4742
+ export { InsufficientBalanceError, ServerSessionManager, WebSessionManager, aggregate, buildSetDocumentsTransaction, clearCache, closeAllSubscriptions, convertRemainingAccounts, count, createSessionWithPrivy, createSessionWithSignature, genAuthNonce, genSolanaMessage, get, getCachedData, getConfig, getFiles, getIdToken, init, reconnectWithNewAuth, refreshSession, runExpression, runExpressionMany, runQuery, runQueryMany, set, setFile, setMany, signAndSubmitTransaction, signMessage, signSessionCreateMessage, signTransaction, subscribe };
4710
4743
  //# sourceMappingURL=index.mjs.map