@bounded-sh/core 0.0.50 → 0.0.52

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.
Files changed (24) hide show
  1. package/dist/{index-D6xrWKP7.js → index-BaSw33ec.js} +131 -19
  2. package/dist/index-BaSw33ec.js.map +1 -0
  3. package/dist/{index-CXyMnWgK.mjs → index-DpfaeNTQ.mjs} +130 -20
  4. package/dist/index-DpfaeNTQ.mjs.map +1 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +3 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +1 -1
  9. package/dist/{realtime-store-Djb_Dm-M.mjs → realtime-store-CBdRkQfZ.mjs} +2 -2
  10. package/dist/{realtime-store-Djb_Dm-M.mjs.map → realtime-store-CBdRkQfZ.mjs.map} +1 -1
  11. package/dist/{realtime-store-HkfnxBu3.js → realtime-store-PVPxsvPr.js} +2 -2
  12. package/dist/{realtime-store-HkfnxBu3.js.map → realtime-store-PVPxsvPr.js.map} +1 -1
  13. package/dist/{solana-message-CTUg0Tqd.js → solana-message-BVJWVB6C.js} +2 -2
  14. package/dist/{solana-message-CTUg0Tqd.js.map → solana-message-BVJWVB6C.js.map} +1 -1
  15. package/dist/{solana-message-l5OZo1j2.mjs → solana-message-kTeT43eQ.mjs} +2 -2
  16. package/dist/{solana-message-l5OZo1j2.mjs.map → solana-message-kTeT43eQ.mjs.map} +1 -1
  17. package/dist/{solana-operations-TSE5Tbgo.mjs → solana-operations-B8UyHL26.mjs} +2 -2
  18. package/dist/{solana-operations-TSE5Tbgo.mjs.map → solana-operations-B8UyHL26.mjs.map} +1 -1
  19. package/dist/{solana-operations-CSh2uF_0.js → solana-operations-bXuuPlov.js} +2 -2
  20. package/dist/{solana-operations-CSh2uF_0.js.map → solana-operations-bXuuPlov.js.map} +1 -1
  21. package/dist/utils/api.d.ts +12 -0
  22. package/package.json +2 -2
  23. package/dist/index-CXyMnWgK.mjs.map +0 -1
  24. package/dist/index-D6xrWKP7.js.map +0 -1
@@ -3410,7 +3410,7 @@ class ServerSessionManager {
3410
3410
  const address = kp.publicKey.toBase58();
3411
3411
  /* fetch nonce from auth API */
3412
3412
  const nonce = await genAuthNonce();
3413
- const { genSolanaMessage } = await Promise.resolve().then(function () { return require('./solana-message-CTUg0Tqd.js'); });
3413
+ const { genSolanaMessage } = await Promise.resolve().then(function () { return require('./solana-message-BVJWVB6C.js'); });
3414
3414
  const message = await genSolanaMessage(address, nonce);
3415
3415
  /* sign the message */
3416
3416
  const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
@@ -3886,30 +3886,101 @@ class BoundedDeclineError extends Error {
3886
3886
 
3887
3887
  const apiClient = axios.create();
3888
3888
  const TRANSIENT_GET_STATUSES = new Set([500, 502, 503, 504]);
3889
+ const RATE_LIMIT_RETRY_BASE_DELAY_MS = 100;
3890
+ /** The window event emitted when the API responds with HTTP 429. */
3891
+ const RATE_LIMIT_EVENT_NAME = 'bounded:ratelimit';
3892
+ /** Dispatch a rate-limit event without affecting request error handling. */
3893
+ function dispatchRateLimitEvent(detail) {
3894
+ try {
3895
+ if (typeof window === 'undefined')
3896
+ return;
3897
+ const Ctor = globalThis.CustomEvent;
3898
+ if (typeof Ctor !== 'function')
3899
+ return;
3900
+ window.dispatchEvent(new Ctor(RATE_LIMIT_EVENT_NAME, {
3901
+ detail: Object.assign(Object.assign({}, detail), { at: Date.now() }),
3902
+ }));
3903
+ }
3904
+ catch (_a) {
3905
+ // Fire-and-forget: event consumers must never delay or mask the request.
3906
+ }
3907
+ }
3908
+ function getRateLimitEventDetail(error, attempt, willRetry) {
3909
+ var _a, _b;
3910
+ const endpoint = typeof ((_a = error.config) === null || _a === void 0 ? void 0 : _a.url) === 'string' ? error.config.url : '';
3911
+ let path = endpoint;
3912
+ try {
3913
+ const parsed = new URL(endpoint);
3914
+ path = `${parsed.pathname}${parsed.search}`;
3915
+ }
3916
+ catch (_c) {
3917
+ // Keep a relative or otherwise non-standard endpoint as-is.
3918
+ }
3919
+ const responseBody = (_b = error.response) === null || _b === void 0 ? void 0 : _b.data;
3920
+ const body = responseBody && typeof responseBody === 'object' && !Array.isArray(responseBody)
3921
+ ? responseBody
3922
+ : responseBody == null
3923
+ ? {}
3924
+ : { message: String(responseBody) };
3925
+ return Object.assign(Object.assign({}, body), { endpoint,
3926
+ path, status: 429, attempt,
3927
+ willRetry });
3928
+ }
3929
+ function getRateLimitAttempt(error) {
3930
+ var _a, _b;
3931
+ const retryCount = (_b = (_a = error.config) === null || _a === void 0 ? void 0 : _a['axios-retry']) === null || _b === void 0 ? void 0 : _b.retryCount;
3932
+ return typeof retryCount === 'number' ? retryCount + 1 : 1;
3933
+ }
3889
3934
  axiosRetry(apiClient, {
3890
3935
  retries: 2,
3891
3936
  retryCondition: (error) => {
3892
- var _a, _b, _c, _d, _e, _f;
3937
+ var _a, _b, _c, _d, _e, _f, _g, _h;
3893
3938
  // A caller cancellation is final. In particular, never turn a route
3894
3939
  // unmount into a fresh GET after its AbortSignal has fired.
3895
- if (axios.isCancel(error) || error.code === 'ERR_CANCELED' || ((_b = (_a = error.config) === null || _a === void 0 ? void 0 : _a.signal) === null || _b === void 0 ? void 0 : _b.aborted)) {
3940
+ const canceled = axios.isCancel(error)
3941
+ || error.code === 'ERR_CANCELED'
3942
+ || ((_b = (_a = error.config) === null || _a === void 0 ? void 0 : _a.signal) === null || _b === void 0 ? void 0 : _b.aborted);
3943
+ if (canceled) {
3944
+ if (((_c = error.response) === null || _c === void 0 ? void 0 : _c.status) === 429) {
3945
+ dispatchRateLimitEvent(getRateLimitEventDetail(error, getRateLimitAttempt(error), false));
3946
+ }
3896
3947
  return false;
3897
3948
  }
3949
+ // A 429 means the server did not execute the request, so retrying is
3950
+ // safe for every method, including POST requests to the queries API.
3951
+ if (((_d = error.response) === null || _d === void 0 ? void 0 : _d.status) === 429)
3952
+ return true;
3898
3953
  // Only retry idempotent GETs. Network failures and the small set of
3899
3954
  // conventional transient gateway/runtime statuses get two bounded
3900
- // retries; auth, policy, invariant, and usage-cap responses stay final.
3955
+ // retries; auth, policy, invariant, and other terminal responses stay final.
3901
3956
  // Writes are never retried because the server may already have applied
3902
3957
  // them before the response was lost.
3903
- if (((_d = (_c = error.config) === null || _c === void 0 ? void 0 : _c.method) === null || _d === void 0 ? void 0 : _d.toLowerCase()) !== 'get')
3958
+ if (((_f = (_e = error.config) === null || _e === void 0 ? void 0 : _e.method) === null || _f === void 0 ? void 0 : _f.toLowerCase()) !== 'get')
3904
3959
  return false;
3905
3960
  return axiosRetry.isNetworkError(error)
3906
- || TRANSIENT_GET_STATUSES.has((_f = (_e = error.response) === null || _e === void 0 ? void 0 : _e.status) !== null && _f !== void 0 ? _f : 0);
3961
+ || TRANSIENT_GET_STATUSES.has((_h = (_g = error.response) === null || _g === void 0 ? void 0 : _g.status) !== null && _h !== void 0 ? _h : 0);
3962
+ },
3963
+ retryDelay: (retryCount, error) => {
3964
+ var _a;
3965
+ if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 429) {
3966
+ const exponentialCap = RATE_LIMIT_RETRY_BASE_DELAY_MS * (2 ** retryCount);
3967
+ return Math.floor(Math.random() * exponentialCap);
3968
+ }
3969
+ return axiosRetry.exponentialDelay(retryCount, error);
3907
3970
  },
3908
- retryDelay: axiosRetry.exponentialDelay,
3909
3971
  shouldResetTimeout: true,
3910
3972
  onRetry: (retryCount, error, requestConfig) => {
3973
+ var _a, _b, _c;
3974
+ if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 429) {
3975
+ dispatchRateLimitEvent(getRateLimitEventDetail(error, retryCount, !((_b = requestConfig.signal) === null || _b === void 0 ? void 0 : _b.aborted)));
3976
+ }
3977
+ console.warn(`[bounded-sdk] retry ${retryCount} for ${(_c = requestConfig.method) === null || _c === void 0 ? void 0 : _c.toUpperCase()} ${requestConfig.url} - ${error.code || error.message}`);
3978
+ },
3979
+ onMaxRetryTimesExceeded: (error, retryCount) => {
3911
3980
  var _a;
3912
- console.warn(`[bounded-sdk] retry ${retryCount} for ${(_a = requestConfig.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()} ${requestConfig.url} — ${error.code || error.message}`);
3981
+ if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 429) {
3982
+ dispatchRateLimitEvent(getRateLimitEventDetail(error, retryCount + 1, false));
3983
+ }
3913
3984
  },
3914
3985
  });
3915
3986
  const refreshInFlight = new Map();
@@ -5244,21 +5315,60 @@ async function getMany(paths, opts = {}) {
5244
5315
  }
5245
5316
  return results;
5246
5317
  }
5247
- async function runQuery(absolutePath, queryName, queryArgs, opts) {
5318
+ const MAX_RUN_QUERIES_PER_POST = 10;
5319
+ let pendingRunQueries = [];
5320
+ let runQueryFlushScheduled = false;
5321
+ function unwrapRunQueryRow(row, entry) {
5248
5322
  var _a;
5249
- const rows = await runQueryManyRows([{ absolutePath, queryName, queryArgs }], opts);
5250
- const row = rows[0];
5251
5323
  // The worker disambiguates a computed null from a failed/undeclared query
5252
- // with a per-row `error` string — surface it instead of returning undefined.
5324
+ // with a per-row `error` string, so surface it instead of returning undefined.
5253
5325
  if ((row === null || row === void 0 ? void 0 : row.error) != null) {
5254
5326
  const err = new Error(String(row.error));
5255
5327
  err.isQueryError = true;
5256
- err.queryName = queryName;
5257
- err.path = absolutePath;
5328
+ err.queryName = entry.queryName;
5329
+ err.path = entry.absolutePath;
5258
5330
  throw err;
5259
5331
  }
5260
5332
  return (_a = row === null || row === void 0 ? void 0 : row.result) !== null && _a !== void 0 ? _a : null;
5261
5333
  }
5334
+ function flushPendingRunQueries() {
5335
+ const pending = pendingRunQueries;
5336
+ pendingRunQueries = [];
5337
+ runQueryFlushScheduled = false;
5338
+ // Calls with any request overrides bypass this buffer, so every entry here
5339
+ // belongs to the same default-overrides identity bucket.
5340
+ for (let offset = 0; offset < pending.length; offset += MAX_RUN_QUERIES_PER_POST) {
5341
+ const chunk = pending.slice(offset, offset + MAX_RUN_QUERIES_PER_POST);
5342
+ void runQueryManyRows(chunk.map(({ entry }) => entry)).then((rows) => {
5343
+ chunk.forEach((caller, index) => {
5344
+ try {
5345
+ caller.resolve(unwrapRunQueryRow(rows[index], caller.entry));
5346
+ }
5347
+ catch (error) {
5348
+ caller.reject(error);
5349
+ }
5350
+ });
5351
+ }, (error) => {
5352
+ chunk.forEach(({ reject }) => reject(error));
5353
+ });
5354
+ }
5355
+ }
5356
+ async function runQuery(absolutePath, queryName, queryArgs, opts) {
5357
+ const entry = { absolutePath, queryName, queryArgs };
5358
+ // Auth, custom headers, timeout, and signal overrides must remain local to
5359
+ // one request, so any override-bearing call deliberately bypasses coalescing.
5360
+ if ((opts === null || opts === void 0 ? void 0 : opts._overrides) != null) {
5361
+ const rows = await runQueryManyRows([entry], opts);
5362
+ return unwrapRunQueryRow(rows[0], entry);
5363
+ }
5364
+ return new Promise((resolve, reject) => {
5365
+ pendingRunQueries.push({ entry, resolve, reject });
5366
+ if (!runQueryFlushScheduled) {
5367
+ runQueryFlushScheduled = true;
5368
+ queueMicrotask(flushPendingRunQueries);
5369
+ }
5370
+ });
5371
+ }
5262
5372
  async function runQueryMany(many, opts) {
5263
5373
  // Batch shape stays an array of result values (errored rows map to null) so
5264
5374
  // existing callers keep working; use runQuery for per-row error surfacing.
@@ -5447,11 +5557,11 @@ async function setMany(many, options) {
5447
5557
  transactionResult = await handleEvmTransaction(curTx.evmTransaction, authProvider, options);
5448
5558
  }
5449
5559
  else if (curTx.serializedTransaction) {
5450
- const { handlePreBuiltTransaction } = await Promise.resolve().then(function () { return require('./solana-operations-CSh2uF_0.js'); });
5560
+ const { handlePreBuiltTransaction } = await Promise.resolve().then(function () { return require('./solana-operations-bXuuPlov.js'); });
5451
5561
  transactionResult = await handlePreBuiltTransaction(curTx, authProvider, options, expectedWritePaths);
5452
5562
  }
5453
5563
  else {
5454
- const { handleSolanaTransaction } = await Promise.resolve().then(function () { return require('./solana-operations-CSh2uF_0.js'); });
5564
+ const { handleSolanaTransaction } = await Promise.resolve().then(function () { return require('./solana-operations-bXuuPlov.js'); });
5455
5565
  transactionResult = await handleSolanaTransaction(curTx, authProvider, options, expectedWritePaths);
5456
5566
  }
5457
5567
  // Sync items after all transactions are confirmed
@@ -7366,7 +7476,7 @@ async function doReconnectWithNewAuth() {
7366
7476
  console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
7367
7477
  }
7368
7478
  try {
7369
- const { reconnectRealtimeStoreWithNewAuth } = await Promise.resolve().then(function () { return require('./realtime-store-HkfnxBu3.js'); });
7479
+ const { reconnectRealtimeStoreWithNewAuth } = await Promise.resolve().then(function () { return require('./realtime-store-PVPxsvPr.js'); });
7370
7480
  await reconnectRealtimeStoreWithNewAuth();
7371
7481
  }
7372
7482
  catch (error) {
@@ -7711,7 +7821,7 @@ async function buildSetDocumentsTransaction(connection, idl, programProvider, pa
7711
7821
  }
7712
7822
  /** SIWS message creation is async already, so its onchain module can be lazy. */
7713
7823
  async function genSolanaMessage(address, nonce) {
7714
- const implementation = await Promise.resolve().then(function () { return require('./solana-message-CTUg0Tqd.js'); });
7824
+ const implementation = await Promise.resolve().then(function () { return require('./solana-message-BVJWVB6C.js'); });
7715
7825
  return implementation.genSolanaMessage(address, nonce);
7716
7826
  }
7717
7827
  const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
@@ -8251,6 +8361,7 @@ exports.EFFECT_INTENT_ADDRESS = EFFECT_INTENT_ADDRESS;
8251
8361
  exports.FunctionInvokeError = FunctionInvokeError;
8252
8362
  exports.InsufficientBalanceError = InsufficientBalanceError;
8253
8363
  exports.LiveIntentError = LiveIntentError;
8364
+ exports.RATE_LIMIT_EVENT_NAME = RATE_LIMIT_EVENT_NAME;
8254
8365
  exports.ReactNativeSessionManager = ReactNativeSessionManager;
8255
8366
  exports.ServerSessionManager = ServerSessionManager;
8256
8367
  exports.WebSessionManager = WebSessionManager;
@@ -8268,6 +8379,7 @@ exports.defineLiveModule = defineLiveModule;
8268
8379
  exports.deriveDecline = deriveDecline;
8269
8380
  exports.deriveUserIdentityFromIdToken = deriveUserIdentityFromIdToken;
8270
8381
  exports.dispatchDeclineEvent = dispatchDeclineEvent;
8382
+ exports.dispatchRateLimitEvent = dispatchRateLimitEvent;
8271
8383
  exports.docId = docId;
8272
8384
  exports.functions = functions;
8273
8385
  exports.genAuthNonce = genAuthNonce;
@@ -8314,4 +8426,4 @@ exports.toLowerEvmAddress = toLowerEvmAddress;
8314
8426
  exports.toMillis = toMillis;
8315
8427
  exports.toSeconds = toSeconds;
8316
8428
  exports.withEffects = withEffects;
8317
- //# sourceMappingURL=index-D6xrWKP7.js.map
8429
+ //# sourceMappingURL=index-BaSw33ec.js.map