@pooflabs/core 0.0.39 → 0.0.40

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
@@ -42,7 +42,12 @@ async function createSessionWithSignature(address, message, signature) {
42
42
  return response.data;
43
43
  }
44
44
  async function createSessionWithPrivy(authToken, address, privyIdToken) {
45
- if (typeof window === 'undefined')
45
+ var _a;
46
+ // Block in true SSR/Node.js. Allow in browser (window exists) and React Native (no window,
47
+ // but not Node — detected via process.versions.node). Node 21+ defines navigator globally,
48
+ // so we can't rely on navigator alone.
49
+ const isNode = typeof process !== 'undefined' && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
50
+ if (typeof window === 'undefined' && isNode)
46
51
  return;
47
52
  const client = await getAxiosAuthClient();
48
53
  const config = await getConfig();
@@ -70,6 +75,19 @@ async function signSessionCreateMessage(_signMessageFunction) {
70
75
  }
71
76
 
72
77
  class WebSessionManager {
78
+ /**
79
+ * Decode a base64url-encoded string (used by JWT payloads).
80
+ * Normalises base64url → standard base64 before calling browser atob().
81
+ */
82
+ static decodeBase64Url(input) {
83
+ let b64 = input.replace(/-/g, '+').replace(/_/g, '/');
84
+ const pad = b64.length % 4;
85
+ if (pad === 2)
86
+ b64 += '==';
87
+ else if (pad === 3)
88
+ b64 += '=';
89
+ return atob(b64);
90
+ }
73
91
  /* ------------------------------------------------------------------ */
74
92
  /* STORE */
75
93
  /* ------------------------------------------------------------------ */
@@ -105,7 +123,7 @@ class WebSessionManager {
105
123
  /* ---------- check JWT expiration ---------- */
106
124
  try {
107
125
  const { accessToken } = sessionObj;
108
- const { exp } = JSON.parse(atob(accessToken.split(".")[1]));
126
+ const { exp } = JSON.parse(this.decodeBase64Url(accessToken.split(".")[1]));
109
127
  if (Date.now() > exp * 1000) {
110
128
  const { refreshToken } = sessionObj;
111
129
  if (!refreshToken)
@@ -3119,6 +3137,41 @@ var serverSessionManager = /*#__PURE__*/Object.freeze({
3119
3137
  ServerSessionManager: ServerSessionManager
3120
3138
  });
3121
3139
 
3140
+ /**
3141
+ * Safe base64 helpers for tarobase-core.
3142
+ *
3143
+ * Uses the global atob/btoa when available (browser, RN with polyfill),
3144
+ * falls back to the 'buffer' package (Node.js / SSR / RN without polyfill).
3145
+ *
3146
+ * These are used instead of bare `atob()`/`btoa()` so the core package
3147
+ * works in React Native without requiring the consumer to polyfill globals.
3148
+ */
3149
+ function safeAtob(input) {
3150
+ if (typeof globalThis.atob === 'function') {
3151
+ return globalThis.atob(input);
3152
+ }
3153
+ return bufferExports.Buffer.from(input, 'base64').toString('binary');
3154
+ }
3155
+ function safeBtoa(input) {
3156
+ if (typeof globalThis.btoa === 'function') {
3157
+ return globalThis.btoa(input);
3158
+ }
3159
+ return bufferExports.Buffer.from(input, 'binary').toString('base64');
3160
+ }
3161
+ /**
3162
+ * Decode a base64url-encoded string (used by JWT payloads).
3163
+ * Normalises base64url → standard base64 before decoding.
3164
+ */
3165
+ function decodeBase64Url(input) {
3166
+ let b64 = input.replace(/-/g, '+').replace(/_/g, '/');
3167
+ const pad = b64.length % 4;
3168
+ if (pad === 2)
3169
+ b64 += '==';
3170
+ else if (pad === 3)
3171
+ b64 += '=';
3172
+ return safeAtob(b64);
3173
+ }
3174
+
3122
3175
  async function createBearerToken(isServer) {
3123
3176
  if (isServer) {
3124
3177
  const sessionMgr = ServerSessionManager.instance;
@@ -3618,7 +3671,7 @@ async function get(path, opts = {}) {
3618
3671
  }
3619
3672
  else {
3620
3673
  const path = encodeURIComponent(normalizedPath);
3621
- const promptQueryParam = (opts === null || opts === void 0 ? void 0 : opts.prompt) ? `&prompt=${btoa(opts.prompt)}` : "";
3674
+ const promptQueryParam = (opts === null || opts === void 0 ? void 0 : opts.prompt) ? `&prompt=${safeBtoa(opts.prompt)}` : "";
3622
3675
  const apiPath = `items?path=${path}${promptQueryParam}${includeSubPathsParam}${shapeParam}${limitParam}${cursorParam}`;
3623
3676
  response = await makeApiRequest('GET', apiPath, null, opts._overrides);
3624
3677
  }
@@ -4073,7 +4126,7 @@ function getCacheKey(path, prompt, shape, limit, cursor) {
4073
4126
  }
4074
4127
  function isTokenExpired(token) {
4075
4128
  try {
4076
- const payload = JSON.parse(atob(token.split('.')[1]));
4129
+ const payload = JSON.parse(decodeBase64Url(token.split('.')[1]));
4077
4130
  const expirationTime = payload.exp * 1000;
4078
4131
  const currentTime = Date.now();
4079
4132
  return currentTime > (expirationTime - 60000); // 60 second buffer
@@ -4085,7 +4138,7 @@ function isTokenExpired(token) {
4085
4138
  }
4086
4139
  function getTokenExpirationTime(token) {
4087
4140
  try {
4088
- const payload = JSON.parse(atob(token.split('.')[1]));
4141
+ const payload = JSON.parse(decodeBase64Url(token.split('.')[1]));
4089
4142
  return payload.exp ? payload.exp * 1000 : null;
4090
4143
  }
4091
4144
  catch (_a) {
@@ -4413,7 +4466,7 @@ function sendSubscribe(connection, subscription) {
4413
4466
  type: 'subscribe',
4414
4467
  subscriptionId: subscription.subscriptionId,
4415
4468
  path: subscription.path,
4416
- prompt: subscription.prompt ? btoa(subscription.prompt) : undefined,
4469
+ prompt: subscription.prompt ? safeBtoa(subscription.prompt) : undefined,
4417
4470
  includeSubPaths: subscription.includeSubPaths,
4418
4471
  shape: subscription.shape && Object.keys(subscription.shape).length > 0
4419
4472
  ? subscription.shape
@@ -4739,5 +4792,167 @@ async function reconnectWithNewAuth() {
4739
4792
  return reconnectWithNewAuthV2();
4740
4793
  }
4741
4794
 
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 };
4795
+ let _config = null;
4796
+ class ReactNativeSessionManager {
4797
+ /**
4798
+ * Must be called once before any other method.
4799
+ *
4800
+ * ```ts
4801
+ * import { ReactNativeSessionManager } from '@pooflabs/core';
4802
+ * import { MMKV } from 'react-native-mmkv';
4803
+ * import { decode } from 'base-64';
4804
+ *
4805
+ * const mmkv = new MMKV();
4806
+ * ReactNativeSessionManager.configure({
4807
+ * storage: {
4808
+ * getItem: (k) => mmkv.getString(k) ?? null,
4809
+ * setItem: (k, v) => mmkv.set(k, v),
4810
+ * removeItem: (k) => mmkv.delete(k),
4811
+ * },
4812
+ * atob: decode,
4813
+ * });
4814
+ * ```
4815
+ */
4816
+ static configure(cfg) {
4817
+ _config = cfg;
4818
+ }
4819
+ static getStorage() {
4820
+ if (!_config) {
4821
+ throw new Error("ReactNativeSessionManager.configure() must be called before using session methods.");
4822
+ }
4823
+ return _config.storage;
4824
+ }
4825
+ static decodeBase64(input) {
4826
+ if (!_config) {
4827
+ throw new Error("ReactNativeSessionManager.configure() must be called before using session methods.");
4828
+ }
4829
+ return _config.atob(input);
4830
+ }
4831
+ /**
4832
+ * Decode a base64url-encoded string (used by JWT payloads).
4833
+ * Normalises base64url → standard base64 before decoding.
4834
+ */
4835
+ static decodeBase64Url(input) {
4836
+ let b64 = input.replace(/-/g, '+').replace(/_/g, '/');
4837
+ const pad = b64.length % 4;
4838
+ if (pad === 2)
4839
+ b64 += '==';
4840
+ else if (pad === 3)
4841
+ b64 += '=';
4842
+ return this.decodeBase64(b64);
4843
+ }
4844
+ /* ------------------------------------------------------------------ */
4845
+ /* STORE */
4846
+ /* ------------------------------------------------------------------ */
4847
+ static async storeSession(address, accessToken, idToken, refreshToken) {
4848
+ const config = await getConfig();
4849
+ const currentAppId = config.appId;
4850
+ this.getStorage().setItem(this.TAROBASE_SESSION_STORAGE_KEY, JSON.stringify({
4851
+ address,
4852
+ accessToken,
4853
+ idToken,
4854
+ refreshToken,
4855
+ appId: currentAppId,
4856
+ }));
4857
+ }
4858
+ /* ------------------------------------------------------------------ */
4859
+ /* GET */
4860
+ /* ------------------------------------------------------------------ */
4861
+ static async getSession() {
4862
+ const session = this.getStorage().getItem(this.TAROBASE_SESSION_STORAGE_KEY);
4863
+ if (!session)
4864
+ return null;
4865
+ const sessionObj = JSON.parse(session);
4866
+ /* ---------- validate app-id ---------- */
4867
+ const config = await getConfig();
4868
+ if (sessionObj.appId && sessionObj.appId !== config.appId) {
4869
+ this.clearSession();
4870
+ return null;
4871
+ }
4872
+ /* ---------- check JWT expiration ---------- */
4873
+ try {
4874
+ const { accessToken } = sessionObj;
4875
+ const { exp } = JSON.parse(this.decodeBase64Url(accessToken.split(".")[1]));
4876
+ if (Date.now() > exp * 1000) {
4877
+ const { refreshToken } = sessionObj;
4878
+ if (!refreshToken)
4879
+ return null;
4880
+ const refreshed = await refreshSession(refreshToken);
4881
+ if ((refreshed === null || refreshed === void 0 ? void 0 : refreshed.idToken) && (refreshed === null || refreshed === void 0 ? void 0 : refreshed.accessToken)) {
4882
+ await this.updateIdTokenAndAccessToken(refreshed.idToken, refreshed.accessToken);
4883
+ const newSession = this.getStorage().getItem(this.TAROBASE_SESSION_STORAGE_KEY);
4884
+ if (!newSession)
4885
+ return null;
4886
+ const newObj = JSON.parse(newSession);
4887
+ return { address: newObj.address, session: newObj };
4888
+ }
4889
+ // Refresh failed — clear stale session to prevent retry loops
4890
+ this.clearSession();
4891
+ return null;
4892
+ }
4893
+ }
4894
+ catch (err) {
4895
+ // Token decode or refresh failed — clear stale session to prevent retry loops
4896
+ this.clearSession();
4897
+ return null;
4898
+ }
4899
+ return { address: sessionObj.address, session: sessionObj };
4900
+ }
4901
+ /* ------------------------------------------------------------------ */
4902
+ /* CLEAR */
4903
+ /* ------------------------------------------------------------------ */
4904
+ static clearSession() {
4905
+ this.getStorage().removeItem(this.TAROBASE_SESSION_STORAGE_KEY);
4906
+ }
4907
+ /* ------------------------------------------------------------------ */
4908
+ /* IS-AUTH */
4909
+ /* ------------------------------------------------------------------ */
4910
+ static isAuthenticated() {
4911
+ return !!this.getStorage().getItem(this.TAROBASE_SESSION_STORAGE_KEY);
4912
+ }
4913
+ /* ------------------------------------------------------------------ */
4914
+ /* TOKEN HELPERS */
4915
+ /* ------------------------------------------------------------------ */
4916
+ static getIdToken() {
4917
+ try {
4918
+ const session = this.getStorage().getItem(this.TAROBASE_SESSION_STORAGE_KEY);
4919
+ return session ? JSON.parse(session).idToken : null;
4920
+ }
4921
+ catch (_a) {
4922
+ return null;
4923
+ }
4924
+ }
4925
+ static getRefreshToken() {
4926
+ try {
4927
+ const session = this.getStorage().getItem(this.TAROBASE_SESSION_STORAGE_KEY);
4928
+ return session ? JSON.parse(session).refreshToken : null;
4929
+ }
4930
+ catch (_a) {
4931
+ return null;
4932
+ }
4933
+ }
4934
+ /* ------------------------------------------------------------------ */
4935
+ /* UPDATE TOKENS */
4936
+ /* ------------------------------------------------------------------ */
4937
+ static async updateIdTokenAndAccessToken(idToken, accessToken) {
4938
+ var _a;
4939
+ const session = this.getStorage().getItem(this.TAROBASE_SESSION_STORAGE_KEY);
4940
+ if (!session)
4941
+ return;
4942
+ const sessionObj = JSON.parse(session);
4943
+ /* ---------- app-id guard ---------- */
4944
+ const config = await getConfig();
4945
+ if (sessionObj.appId && sessionObj.appId !== config.appId) {
4946
+ this.clearSession();
4947
+ return;
4948
+ }
4949
+ sessionObj.idToken = idToken;
4950
+ sessionObj.accessToken = accessToken;
4951
+ (_a = sessionObj.appId) !== null && _a !== void 0 ? _a : (sessionObj.appId = config.appId);
4952
+ this.getStorage().setItem(this.TAROBASE_SESSION_STORAGE_KEY, JSON.stringify(sessionObj));
4953
+ }
4954
+ }
4955
+ ReactNativeSessionManager.TAROBASE_SESSION_STORAGE_KEY = "tarobase_session_storage";
4956
+
4957
+ export { InsufficientBalanceError, ReactNativeSessionManager, 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 };
4743
4958
  //# sourceMappingURL=index.mjs.map