@bounded-sh/core 0.0.48 → 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.
Files changed (23) hide show
  1. package/dist/client/config.d.ts +10 -5
  2. package/dist/client/token-subscription.d.ts +22 -0
  3. package/dist/{index-Bq2Zug5X.mjs → index-CXyMnWgK.mjs} +138 -52
  4. package/dist/index-CXyMnWgK.mjs.map +1 -0
  5. package/dist/{index-Kn7IIh5d.js → index-D6xrWKP7.js} +138 -52
  6. package/dist/index-D6xrWKP7.js.map +1 -0
  7. package/dist/index.js +1 -1
  8. package/dist/index.mjs +1 -1
  9. package/dist/{realtime-store-XKFKDJRA.mjs → realtime-store-Djb_Dm-M.mjs} +2 -2
  10. package/dist/{realtime-store-XKFKDJRA.mjs.map → realtime-store-Djb_Dm-M.mjs.map} +1 -1
  11. package/dist/{realtime-store-DssTn6Nr.js → realtime-store-HkfnxBu3.js} +2 -2
  12. package/dist/{realtime-store-DssTn6Nr.js.map → realtime-store-HkfnxBu3.js.map} +1 -1
  13. package/dist/{solana-message-DSkrhvy8.js → solana-message-CTUg0Tqd.js} +2 -2
  14. package/dist/{solana-message-DSkrhvy8.js.map → solana-message-CTUg0Tqd.js.map} +1 -1
  15. package/dist/{solana-message-Svo0_paO.mjs → solana-message-l5OZo1j2.mjs} +2 -2
  16. package/dist/{solana-message-Svo0_paO.mjs.map → solana-message-l5OZo1j2.mjs.map} +1 -1
  17. package/dist/{solana-operations-C_aie1aF.js → solana-operations-CSh2uF_0.js} +2 -2
  18. package/dist/{solana-operations-C_aie1aF.js.map → solana-operations-CSh2uF_0.js.map} +1 -1
  19. package/dist/{solana-operations-4MGSdS1U.mjs → solana-operations-TSE5Tbgo.mjs} +2 -2
  20. package/dist/{solana-operations-4MGSdS1U.mjs.map → solana-operations-TSE5Tbgo.mjs.map} +1 -1
  21. package/package.json +1 -1
  22. package/dist/index-Bq2Zug5X.mjs.map +0 -1
  23. package/dist/index-Kn7IIh5d.js.map +0 -1
@@ -24,12 +24,17 @@ export interface ClientConfig {
24
24
  * optional text OTP. App-origin OTP helpers are retired; use hosted
25
25
  * loginWithRedirect/loginWithPopup against this issuer. Defaults per network. */
26
26
  humanAuthApiUrl?: string;
27
+ /**
28
+ * Base URL of the Bounded developer API used by first-party platform
29
+ * surfaces for app provisioning and build control. Defaults per network.
30
+ */
31
+ devApiUrl?: string;
27
32
  /**
28
33
  * Selects a Bounded backend preset. When set, the endpoint defaults
29
- * (apiUrl/wsApiUrl/authApiUrl/functionsUrl) resolve to that network's Bounded
30
- * services instead of the legacy defaults — so a Bounded app needs only
31
- * `{ appId, network }`. Anything you pass explicitly still wins. Unset =
32
- * legacy behavior (unchanged for existing apps).
34
+ * (apiUrl/wsApiUrl/authApiUrl/humanAuthApiUrl/functionsUrl/devApiUrl) resolve
35
+ * to that network's Bounded services instead of the legacy defaults — so a
36
+ * Bounded app needs only `{ appId, network }`. Anything you pass explicitly
37
+ * still wins. Unset = legacy behavior (unchanged for existing apps).
33
38
  */
34
39
  network?: 'bounded' | 'bounded-staging' | 'bounded-production';
35
40
  chain: string;
@@ -121,7 +126,7 @@ export interface ClientConfig {
121
126
  };
122
127
  }
123
128
  export declare let clientConfig: ClientConfig;
124
- type BoundedEndpoints = Pick<ClientConfig, 'wsApiUrl' | 'apiUrl' | 'authApiUrl' | 'humanAuthApiUrl' | 'functionsUrl'>;
129
+ type BoundedEndpoints = Pick<ClientConfig, 'wsApiUrl' | 'apiUrl' | 'authApiUrl' | 'humanAuthApiUrl' | 'functionsUrl' | 'devApiUrl'>;
125
130
  export declare const BOUNDED_NETWORKS: Record<string, BoundedEndpoints>;
126
131
  /**
127
132
  * The hosted `/.well-known/bounded-webhook-keys.json` URL for the configured
@@ -0,0 +1,22 @@
1
+ export interface TokenSubscriptionOptions {
2
+ limit?: number;
3
+ onData(data: unknown): void;
4
+ onError?(error: Error): void;
5
+ }
6
+ export interface BoundedTokenSubscribeClient {
7
+ subscribe(path: string, options: TokenSubscriptionOptions): Promise<() => Promise<void>>;
8
+ close(): void;
9
+ }
10
+ export interface BoundedTokenSubscribeConfig {
11
+ appId: string;
12
+ wsApiUrl: string;
13
+ token(): Promise<string>;
14
+ }
15
+ /**
16
+ * Narrow browser client for Bounded's authenticated WebSocket-v2 `subscribe`
17
+ * protocol. It exists for access-gated pages that already own a short-lived,
18
+ * read-only token and therefore must not pull wallet, write, or chain code into
19
+ * the browser bundle. Tokens are sent only in the first socket frame, never in
20
+ * the URL, storage, errors, or logs.
21
+ */
22
+ export declare function createBoundedTokenSubscribeClient(config: BoundedTokenSubscribeConfig): BoundedTokenSubscribeClient;
@@ -15,6 +15,7 @@ let clientConfig = {
15
15
  authApiUrl: 'https://wallet-auth.bounded.sh',
16
16
  humanAuthApiUrl: 'https://auth.bounded.sh',
17
17
  functionsUrl: 'https://functions.bounded.sh',
18
+ devApiUrl: 'https://dev-api.bounded.sh',
18
19
  appId: '',
19
20
  // No hidden modal fallback: browser clients start hosted Bounded Auth with
20
21
  // loginWithRedirect/loginWithPopup. For crypto/onchain wallet login use
@@ -39,6 +40,7 @@ const BOUNDED_NETWORKS = {
39
40
  authApiUrl: 'https://wallet-auth-staging.bounded.sh',
40
41
  humanAuthApiUrl: 'https://auth-staging.bounded.sh',
41
42
  functionsUrl: 'https://functions-staging.bounded.sh',
43
+ devApiUrl: 'https://dev-api-staging.bounded.sh',
42
44
  },
43
45
  'bounded-production': {
44
46
  // All on bounded.sh custom domains. authApiUrl = WALLET/SIWS issuer
@@ -49,6 +51,7 @@ const BOUNDED_NETWORKS = {
49
51
  authApiUrl: 'https://wallet-auth.bounded.sh',
50
52
  humanAuthApiUrl: 'https://auth.bounded.sh',
51
53
  functionsUrl: 'https://functions.bounded.sh',
54
+ devApiUrl: 'https://dev-api.bounded.sh',
52
55
  },
53
56
  };
54
57
  // `network: 'bounded'` is an alias for the current GA target.
@@ -3405,7 +3408,7 @@ class ServerSessionManager {
3405
3408
  const address = kp.publicKey.toBase58();
3406
3409
  /* fetch nonce from auth API */
3407
3410
  const nonce = await genAuthNonce();
3408
- const { genSolanaMessage } = await import('./solana-message-Svo0_paO.mjs');
3411
+ const { genSolanaMessage } = await import('./solana-message-l5OZo1j2.mjs');
3409
3412
  const message = await genSolanaMessage(address, nonce);
3410
3413
  /* sign the message */
3411
3414
  const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
@@ -5442,11 +5445,11 @@ async function setMany(many, options) {
5442
5445
  transactionResult = await handleEvmTransaction(curTx.evmTransaction, authProvider, options);
5443
5446
  }
5444
5447
  else if (curTx.serializedTransaction) {
5445
- const { handlePreBuiltTransaction } = await import('./solana-operations-4MGSdS1U.mjs');
5448
+ const { handlePreBuiltTransaction } = await import('./solana-operations-TSE5Tbgo.mjs');
5446
5449
  transactionResult = await handlePreBuiltTransaction(curTx, authProvider, options, expectedWritePaths);
5447
5450
  }
5448
5451
  else {
5449
- const { handleSolanaTransaction } = await import('./solana-operations-4MGSdS1U.mjs');
5452
+ const { handleSolanaTransaction } = await import('./solana-operations-TSE5Tbgo.mjs');
5450
5453
  transactionResult = await handleSolanaTransaction(curTx, authProvider, options, expectedWritePaths);
5451
5454
  }
5452
5455
  // Sync items after all transactions are confirmed
@@ -6104,7 +6107,10 @@ function roomKeyFromRoutePath(routePath) {
6104
6107
  function replaySubscriptions(connection) {
6105
6108
  for (const sub of connection.subscriptions.values()) {
6106
6109
  sub.lastData = undefined;
6107
- sendSubscribe(connection, sub);
6110
+ if (!connection.pendingSubscriptions.has(sub.subscriptionId)) {
6111
+ registerDetachedReplayConfirmation(connection, sub);
6112
+ }
6113
+ sendSubscribeWithConfirmation(connection, sub);
6108
6114
  }
6109
6115
  }
6110
6116
  /**
@@ -6144,6 +6150,9 @@ function closeConnectionNow(connection) {
6144
6150
  connection.isConnected = false;
6145
6151
  connection.isAuthenticating = false;
6146
6152
  const closeError = new Error('WebSocket connection closed');
6153
+ for (const subscriptionId of Array.from(connection.pendingSubscriptions.keys())) {
6154
+ rejectPendingSubscriptionConfirmation(connection, subscriptionId, closeError);
6155
+ }
6147
6156
  for (const subscription of connection.subscriptions.values()) {
6148
6157
  rejectSubscriptionRefresh(subscription, closeError);
6149
6158
  }
@@ -6789,6 +6798,75 @@ function armSubscriptionConfirmationTimeout(connection, subscription, pending) {
6789
6798
  rejectPendingSubscriptionConfirmation(connection, subscription.subscriptionId, new Error('Subscription timeout'));
6790
6799
  }, SUBSCRIPTION_CONFIRM_TIMEOUT_MS);
6791
6800
  }
6801
+ /**
6802
+ * Bind an existing confirmation promise to the exact socket session that is
6803
+ * about to carry its subscribe frame. Cold-start registrations create the
6804
+ * promise before the socket opens; replay uses this helper to move the timeout
6805
+ * from the connection wait onto the authenticated socket without allocating a
6806
+ * second promise or leaving the original timer behind.
6807
+ */
6808
+ function sendSubscribeWithConfirmation(connection, subscription) {
6809
+ const pending = connection.pendingSubscriptions.get(subscription.subscriptionId);
6810
+ const socket = connection.ws;
6811
+ if (pending && socket && socket.readyState === WS_READY_STATE_OPEN) {
6812
+ if (pending.timer) {
6813
+ clearTimeout(pending.timer);
6814
+ pending.timer = null;
6815
+ }
6816
+ pending.socket = socket;
6817
+ pending.socketGeneration = connection.socketGeneration;
6818
+ pending.authTokenValue = connection.authTokenValue;
6819
+ pending.authPrincipal = connection.authPrincipal;
6820
+ // Arm before send: browser/test transports may synchronously dispatch
6821
+ // the subscribed/data/error response from send().
6822
+ armSubscriptionConfirmationTimeout(connection, subscription, pending);
6823
+ }
6824
+ sendSubscribe(connection, subscription);
6825
+ }
6826
+ /**
6827
+ * A reconnect replay has no caller awaiting subscribeV2 anymore, but it still
6828
+ * needs the same bounded confirmation/retry guarantee. Attach the rejection
6829
+ * handler immediately so this recovery record can never become an unhandled
6830
+ * promise. Structured server errors are delivered by handleServerMessage;
6831
+ * terminal local timeouts are surfaced here exactly once to active callbacks.
6832
+ */
6833
+ function registerDetachedReplayConfirmation(connection, subscription) {
6834
+ const socket = connection.ws;
6835
+ if (!socket || connection.pendingSubscriptions.has(subscription.subscriptionId))
6836
+ return;
6837
+ let resolve;
6838
+ let reject;
6839
+ const promise = new Promise((resolvePromise, rejectPromise) => {
6840
+ resolve = resolvePromise;
6841
+ reject = rejectPromise;
6842
+ });
6843
+ const pending = {
6844
+ resolve,
6845
+ reject,
6846
+ timer: null,
6847
+ retrySent: false,
6848
+ socket,
6849
+ socketGeneration: connection.socketGeneration,
6850
+ authTokenValue: connection.authTokenValue,
6851
+ authPrincipal: connection.authPrincipal,
6852
+ };
6853
+ connection.pendingSubscriptions.set(subscription.subscriptionId, pending);
6854
+ void promise.catch((error) => {
6855
+ if (error.__boundedDeliveredToOnError)
6856
+ return;
6857
+ if (connection.subscriptions.get(subscription.subscriptionId) !== subscription)
6858
+ return;
6859
+ // Ordinary socket/auth-change close paths will replay again and did not
6860
+ // historically poison every established callback. Only a healthy socket
6861
+ // exhausting its bounded confirmation retry is a new terminal condition.
6862
+ if (error.message === 'Subscription timeout') {
6863
+ const delivered = notifyErrorCallbacks(subscription, error);
6864
+ if (!delivered) {
6865
+ console.warn('[WS v2] Replayed subscription confirmation timed out:', subscription.path);
6866
+ }
6867
+ }
6868
+ });
6869
+ }
6792
6870
  function resolveSubscriptionRefresh(subscription) {
6793
6871
  const refresh = subscription.refresh;
6794
6872
  if (!refresh)
@@ -7061,53 +7139,57 @@ async function subscribeV2(path, subscriptionOptions, roomRoutePath) {
7061
7139
  throw error;
7062
7140
  }
7063
7141
  releaseConnection();
7064
- // Send subscribe message if connected
7142
+ // Register the confirmation before any possible synchronous send/ACK. This
7143
+ // includes the first subscription on a cold socket: replaySubscriptions()
7144
+ // will bind this same pending record to the authenticated socket and send it.
7145
+ const subscriptionPromise = new Promise((resolve, reject) => {
7146
+ const socket = connection.ws;
7147
+ if (!socket) {
7148
+ reject(new Error('WebSocket disconnected'));
7149
+ return;
7150
+ }
7151
+ const pending = {
7152
+ resolve,
7153
+ reject,
7154
+ timer: null,
7155
+ retrySent: false,
7156
+ socket,
7157
+ socketGeneration: connection.socketGeneration,
7158
+ authTokenValue: connection.authTokenValue,
7159
+ authPrincipal: connection.authPrincipal,
7160
+ };
7161
+ connection.pendingSubscriptions.set(subscriptionId, pending);
7162
+ // Bound a cold connection/auth wait as well. Once the socket becomes
7163
+ // ready, sendSubscribeWithConfirmation() replaces this timer with a
7164
+ // fresh per-socket confirmation window.
7165
+ armSubscriptionConfirmationTimeout(connection, subscription, pending);
7166
+ });
7065
7167
  if (connection.isConnected && !connection.isAuthenticating) {
7066
- // Create a promise to wait for subscription confirmation
7067
- const subscriptionPromise = new Promise((resolve, reject) => {
7068
- const socket = connection.ws;
7069
- if (!socket) {
7070
- reject(new Error('WebSocket disconnected'));
7071
- return;
7072
- }
7073
- const pending = {
7074
- resolve,
7075
- reject,
7076
- timer: null,
7077
- retrySent: false,
7078
- socket,
7079
- socketGeneration: connection.socketGeneration,
7080
- authTokenValue: connection.authTokenValue,
7081
- authPrincipal: connection.authPrincipal,
7082
- };
7083
- connection.pendingSubscriptions.set(subscriptionId, pending);
7084
- armSubscriptionConfirmationTimeout(connection, subscription, pending);
7085
- });
7086
- sendSubscribe(connection, subscription);
7087
- try {
7088
- await subscriptionPromise;
7168
+ sendSubscribeWithConfirmation(connection, subscription);
7169
+ }
7170
+ try {
7171
+ await subscriptionPromise;
7172
+ }
7173
+ catch (error) {
7174
+ const err = error instanceof Error ? error : new Error(String(error));
7175
+ let deliveredLocally = false;
7176
+ if (!err.__boundedDeliveredToOnError) {
7177
+ // A local confirmation timeout belongs to the caller that
7178
+ // originated it. Joined callbacks may already have a viable
7179
+ // same-id refresh and must not be poisoned by that old attempt.
7180
+ deliveredLocally = notifyErrorCallback(subscriptionOptions, err);
7181
+ }
7182
+ if (!subscriptionOptions.onError) {
7183
+ rejectPendingSubscriptionConfirmation(connection, subscriptionId, err);
7184
+ // This registration may have acquired joined callbacks while
7185
+ // its confirmation was pending. Remove only the originating
7186
+ // callback; deleting the whole logical subscription would
7187
+ // orphan those remounted consumers and their refresh flight.
7188
+ await removeCallbackFromSubscription(connection, subscriptionId, subscriptionOptions);
7189
+ throw err;
7089
7190
  }
7090
- catch (error) {
7091
- const err = error instanceof Error ? error : new Error(String(error));
7092
- let deliveredLocally = false;
7093
- if (!err.__boundedDeliveredToOnError) {
7094
- // A local confirmation timeout belongs to the caller that
7095
- // originated it. Joined callbacks may already have a viable
7096
- // same-id refresh and must not be poisoned by that old attempt.
7097
- deliveredLocally = notifyErrorCallback(subscriptionOptions, err);
7098
- }
7099
- if (!subscriptionOptions.onError) {
7100
- connection.pendingSubscriptions.delete(subscriptionId);
7101
- // This registration may have acquired joined callbacks while
7102
- // its confirmation was pending. Remove only the originating
7103
- // callback; deleting the whole logical subscription would
7104
- // orphan those remounted consumers and their refresh flight.
7105
- await removeCallbackFromSubscription(connection, subscriptionId, subscriptionOptions);
7106
- throw err;
7107
- }
7108
- if (!err.__boundedDeliveredToOnError && !deliveredLocally) {
7109
- console.warn('[WS v2] Subscription confirmation failed, keeping for reconnect recovery:', error);
7110
- }
7191
+ if (!err.__boundedDeliveredToOnError && !deliveredLocally) {
7192
+ console.warn('[WS v2] Subscription confirmation failed, keeping for reconnect recovery:', error);
7111
7193
  }
7112
7194
  }
7113
7195
  // Return unsubscribe function
@@ -7130,6 +7212,7 @@ async function removeCallbackFromSubscription(connection, subscriptionId, callba
7130
7212
  }
7131
7213
  // No more callbacks, unsubscribe from server
7132
7214
  connection.subscriptions.delete(subscriptionId);
7215
+ rejectPendingSubscriptionConfirmation(connection, subscriptionId, new Error('Subscription unsubscribed'));
7133
7216
  if (connection.isConnected && !connection.isAuthenticating) {
7134
7217
  // Create a promise to wait for unsubscription confirmation
7135
7218
  const unsubscribePromise = new Promise((resolve, reject) => {
@@ -7157,6 +7240,9 @@ async function closeAllSubscriptionsV2() {
7157
7240
  for (const [appId, connection] of connections) {
7158
7241
  cancelIdleConnectionClose(connection);
7159
7242
  const closeError = new Error('Subscriptions closed');
7243
+ for (const subscriptionId of Array.from(connection.pendingSubscriptions.keys())) {
7244
+ rejectPendingSubscriptionConfirmation(connection, subscriptionId, closeError);
7245
+ }
7160
7246
  for (const subscription of connection.subscriptions.values()) {
7161
7247
  rejectSubscriptionRefresh(subscription, closeError);
7162
7248
  }
@@ -7278,7 +7364,7 @@ async function doReconnectWithNewAuth() {
7278
7364
  console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
7279
7365
  }
7280
7366
  try {
7281
- const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-XKFKDJRA.mjs');
7367
+ const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-Djb_Dm-M.mjs');
7282
7368
  await reconnectRealtimeStoreWithNewAuth();
7283
7369
  }
7284
7370
  catch (error) {
@@ -7623,7 +7709,7 @@ async function buildSetDocumentsTransaction(connection, idl, programProvider, pa
7623
7709
  }
7624
7710
  /** SIWS message creation is async already, so its onchain module can be lazy. */
7625
7711
  async function genSolanaMessage(address, nonce) {
7626
- const implementation = await import('./solana-message-Svo0_paO.mjs');
7712
+ const implementation = await import('./solana-message-l5OZo1j2.mjs');
7627
7713
  return implementation.genSolanaMessage(address, nonce);
7628
7714
  }
7629
7715
  const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
@@ -8156,4 +8242,4 @@ function defineLiveModule(mod) {
8156
8242
  }
8157
8243
 
8158
8244
  export { normalizeEvmAddress as $, reconnectWithNewAuth as A, BoundedDeclineError as B, deriveDecline as C, dispatchDeclineEvent as D, DECLINE_EVENT_NAME as E, hasActiveConnection as F, increment as G, serverTimestamp as H, InsufficientBalanceError as I, now as J, toSeconds as K, toMillis as L, getIdToken as M, deriveUserIdentityFromIdToken as N, getActiveSessionManager as O, createSessionWithSignature as P, refreshSession as Q, ReactNativeSessionManager as R, ServerSessionManager as S, revokeSession as T, signSessionCreateMessage as U, genAuthNonce as V, WebSessionManager as W, buildSetDocumentsTransaction as X, convertRemainingAccounts as Y, genSolanaMessage as Z, genSiweMessage as _, getWebhookKeysUrl as a, toLowerEvmAddress as a0, assertCanonicalBoundedRuntimeWriteCalldata as a1, assertEvmTransactionMatchesWrite as a2, BOUNDED_RUNTIME_SET_DOCUMENTS_SELECTOR as a3, BOUNDED_RUNTIME_SET_DOCUMENTS_ATTESTED_SELECTOR as a4, functions as a5, invoke as a6, FunctionInvokeError as a7, live as a8, intent as a9, status as aa, subscribeView as ab, LiveIntentError as ac, withEffects as ad, isEffectResult as ae, defineLiveModule as af, EFFECT_INTENT_ADDRESS as ag, bufferExports as b, get as c, getMany as d, docId as e, setMany as f, getConfig as g, setFile as h, init as i, getFiles as j, search as k, runQueryMany as l, runExpression as m, runExpressionMany as n, signMessage as o, signTransaction as p, queryAggregate as q, runQuery as r, set as s, signAndSubmitTransaction as t, count as u, aggregate as v, subscribe as w, closeAllSubscriptions as x, clearCache as y, getCachedData as z };
8159
- //# sourceMappingURL=index-Bq2Zug5X.mjs.map
8245
+ //# sourceMappingURL=index-CXyMnWgK.mjs.map