@bounded-sh/core 0.0.100 → 0.0.101

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 (30) hide show
  1. package/dist/client/app-token.d.ts +77 -0
  2. package/dist/client/create-client.d.ts +69 -17
  3. package/dist/client/subscription-v2.d.ts +23 -0
  4. package/dist/{index-B8PpRj6x.js → index-BEGeDFQI.js} +739 -106
  5. package/dist/index-BEGeDFQI.js.map +1 -0
  6. package/dist/{index-DW5yMQwV.mjs → index-CMT0saxy.mjs} +736 -107
  7. package/dist/index-CMT0saxy.mjs.map +1 -0
  8. package/dist/index.d.ts +3 -1
  9. package/dist/index.js +5 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +1 -1
  12. package/dist/{realtime-store-CeDT7vJF.mjs → realtime-store-DqAAqfo9.mjs} +2 -2
  13. package/dist/{realtime-store-CeDT7vJF.mjs.map → realtime-store-DqAAqfo9.mjs.map} +1 -1
  14. package/dist/{realtime-store-D7mJ7rTj.js → realtime-store-DrtEj-bE.js} +2 -2
  15. package/dist/{realtime-store-D7mJ7rTj.js.map → realtime-store-DrtEj-bE.js.map} +1 -1
  16. package/dist/{solana-message-w2FxOkFA.js → solana-message-CJTnYiJu.js} +2 -2
  17. package/dist/{solana-message-w2FxOkFA.js.map → solana-message-CJTnYiJu.js.map} +1 -1
  18. package/dist/{solana-message-BGkYFU3C.mjs → solana-message-C_717vAx.mjs} +2 -2
  19. package/dist/{solana-message-BGkYFU3C.mjs.map → solana-message-C_717vAx.mjs.map} +1 -1
  20. package/dist/{solana-operations-DJQDYdrk.js → solana-operations-CU0YbiMu.js} +3 -3
  21. package/dist/{solana-operations-DJQDYdrk.js.map → solana-operations-CU0YbiMu.js.map} +1 -1
  22. package/dist/{solana-operations-CHgW7Pue.mjs → solana-operations-DGNA2nGZ.mjs} +4 -4
  23. package/dist/{solana-operations-CHgW7Pue.mjs.map → solana-operations-DGNA2nGZ.mjs.map} +1 -1
  24. package/dist/{solana-v1-operations-BPn-poee.mjs → solana-v1-operations-Bpd4TASG.mjs} +3 -3
  25. package/dist/{solana-v1-operations-BPn-poee.mjs.map → solana-v1-operations-Bpd4TASG.mjs.map} +1 -1
  26. package/dist/{solana-v1-operations-BF2rjG23.js → solana-v1-operations-yQII8r1s.js} +3 -3
  27. package/dist/{solana-v1-operations-BF2rjG23.js.map → solana-v1-operations-yQII8r1s.js.map} +1 -1
  28. package/package.json +2 -2
  29. package/dist/index-B8PpRj6x.js.map +0 -1
  30. package/dist/index-DW5yMQwV.mjs.map +0 -1
@@ -4188,7 +4188,7 @@ class ServerSessionManager {
4188
4188
  const sessionConfig = (_a = this.sessionConfig) !== null && _a !== void 0 ? _a : undefined;
4189
4189
  /* fetch nonce from auth API (bound to the snapshotted app+issuer) */
4190
4190
  const nonce = await genAuthNonce(sessionConfig);
4191
- const { genSolanaMessage } = await import('./solana-message-BGkYFU3C.mjs');
4191
+ const { genSolanaMessage } = await import('./solana-message-C_717vAx.mjs');
4192
4192
  const message = await genSolanaMessage(address, nonce, sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.appId);
4193
4193
  /* sign the message */
4194
4194
  const sigBytes = nacl.sign.detached(new TextEncoder().encode(message), kp.secretKey);
@@ -5280,6 +5280,37 @@ const MIN_BROWSER_RECONNECT_INTERVAL_MS = 5000;
5280
5280
  // cancel teardown and reuse it; genuinely idle sockets still close promptly.
5281
5281
  const IDLE_CONNECTION_CLOSE_GRACE_MS = 5000;
5282
5282
  const SUBSCRIPTION_CONFIRM_TIMEOUT_MS = 10000;
5283
+ /**
5284
+ * Live subscriptions the SDK puts on ONE socket before opening another.
5285
+ *
5286
+ * The realtime worker refuses the 51st distinct subscribe on a connection with
5287
+ * `{ code: 'subscription_limit', status: 429 }` - see
5288
+ * `packages/cdk/cloudflare/realtime-worker/src/subscriptions.ts`
5289
+ * (`attachment.subscriptions.length >= 50`). The cap is structural there: every
5290
+ * subscription spec has to fit the 16KB Durable Object hibernation attachment,
5291
+ * and each write fans out across the whole set of one socket.
5292
+ *
5293
+ * 45 leaves deliberate headroom under that 50: two concurrent subscribeV2 calls
5294
+ * can both see room and overshoot by a frame or two, and another tab may already
5295
+ * hold subscriptions on the same DO. A refusal that happens anyway is treated as
5296
+ * backpressure, not a failure (see rehomeRefusedSubscription).
5297
+ */
5298
+ const SHARD_CAP = 45;
5299
+ /**
5300
+ * Sockets one routing target (app, room, or wallet identity) may hold. Past this
5301
+ * a subscribe fails loudly with `subscription_limit` instead of opening
5302
+ * connections without end - 8 * 45 = 360 concurrent live subscriptions per
5303
+ * target, far beyond any legitimate UI.
5304
+ */
5305
+ const MAX_SHARDS_PER_KEY = 8;
5306
+ /** Re-homes one logical subscription may attempt before its refusal surfaces. */
5307
+ const MAX_REHOME_ATTEMPTS = MAX_SHARDS_PER_KEY;
5308
+ const SUBSCRIPTION_LIMIT_CODE = 'subscription_limit';
5309
+ // Mirror of the server's `clampSubscriptionLimit` (realtime-worker
5310
+ // src/subscriptions.ts): an omitted or non-numeric limit becomes the default, a
5311
+ // value above the ceiling is clamped down, and a zero/negative limit is rejected.
5312
+ const SUBSCRIPTION_DEFAULT_LIMIT = 100;
5313
+ const SUBSCRIPTION_MAX_LIMIT = 1000;
5283
5314
  const WS_CONFIG = {
5284
5315
  // Keep retrying indefinitely so long outages recover without page refresh.
5285
5316
  maxRetries: Infinity,
@@ -5300,6 +5331,26 @@ let reconnectInProgress = null;
5300
5331
  function generateSubscriptionId() {
5301
5332
  return `sub_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
5302
5333
  }
5334
+ /**
5335
+ * Apply the server's own subscription-limit clamp so the client identifies a
5336
+ * subscription by the value the server will actually key it on.
5337
+ *
5338
+ * The worker de-dups a connection's subscriptions on the CLAMPED limit and
5339
+ * REPLACES a colliding entry (sending `unsubscribed` for the displaced id), so
5340
+ * `limit: undefined` and `limit: 100` are one server subscription. Matching on
5341
+ * the raw option made the SDK register two, and the second silently retired the
5342
+ * first stream. `null` mirrors the server's rejection of a zero/negative limit.
5343
+ * Authority: `clampSubscriptionLimit` in realtime-worker src/subscriptions.ts.
5344
+ */
5345
+ function normalizeSubscriptionLimit(raw) {
5346
+ if (raw === undefined || raw === null)
5347
+ return SUBSCRIPTION_DEFAULT_LIMIT;
5348
+ if (typeof raw !== 'number' || !Number.isFinite(raw))
5349
+ return SUBSCRIPTION_DEFAULT_LIMIT;
5350
+ if (raw <= 0)
5351
+ return null;
5352
+ return raw > SUBSCRIPTION_MAX_LIMIT ? SUBSCRIPTION_MAX_LIMIT : Math.floor(raw);
5353
+ }
5303
5354
  function hashForKey$1(value) {
5304
5355
  let h = 5381;
5305
5356
  for (let i = 0; i < value.length; i++) {
@@ -5319,7 +5370,11 @@ function hashForKey$1(value) {
5319
5370
  function getCacheKey(path, prompt, shape, limit, cursor, filter, identity) {
5320
5371
  const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
5321
5372
  const shapeKey = shape && Object.keys(shape).length > 0 ? JSON.stringify(shape) : '';
5322
- const limitKey = limit !== undefined ? `:l${limit}` : '';
5373
+ // Normalized, for the same reason the de-dup scan is: two options that clamp
5374
+ // to one server subscription must share one cache entry, or a joined callback
5375
+ // cannot read the snapshot its own subscription just wrote.
5376
+ const normalizedLimit = normalizeSubscriptionLimit(limit);
5377
+ const limitKey = `:l${normalizedLimit === null ? 'rejected' : normalizedLimit}`;
5323
5378
  const cursorKey = cursor ? `:c${hashForKey$1(cursor)}` : '';
5324
5379
  const filterKey = filter && Object.keys(filter).length > 0 ? `:f${hashForKey$1(JSON.stringify(filter))}` : '';
5325
5380
  return `${identity}|${normalizedPath}:${prompt || 'default'}:${shapeKey}${limitKey}${cursorKey}${filterKey}`;
@@ -5648,18 +5703,76 @@ function replaySubscriptions(connection) {
5648
5703
  * connections key by their routing target; wallet-scoped connections pin the
5649
5704
  * exact bearer so two distinct tokens never share a flight or a lease, even
5650
5705
  * when their compact `#id#<principalKey>` hashes collide.
5706
+ *
5707
+ * `shardIndex` selects which socket of that target's pool this is. Shard 0 adds
5708
+ * nothing, so every key an unsharded client produces is unchanged.
5651
5709
  */
5652
- function connectionLeaseKey(appId, routePath, principalKey, principalToken, anonymous) {
5710
+ function connectionLeaseKey(appId, routePath, principalKey, principalToken, anonymous, shardIndex = 0) {
5653
5711
  const roomKey = roomKeyFromRoutePath(routePath);
5654
5712
  // A read-only scoped instance is anonymous and must never share a socket with
5655
5713
  // the authenticated global path even when it targets the same appId.
5656
5714
  const appKey = anonymous ? `${appId}#ro` : appId;
5657
- const base = roomKey ? `${appKey}#room#${roomKey}` : appKey;
5715
+ const base = routingBaseKey(roomKey ? `${appKey}#room#${roomKey}` : appKey, shardIndex);
5658
5716
  if (!principalKey)
5659
5717
  return base;
5660
5718
  const bucket = `${base}#id#${principalKey}`;
5661
5719
  return principalToken ? `${bucket}#tok#${principalToken}` : bucket;
5662
5720
  }
5721
+ /**
5722
+ * Apply a shard suffix to the ROUTING part of a connection key, ahead of any
5723
+ * `#id#`/`#tok#` identity bucket. Keeping the suffix here means the room/app
5724
+ * prefix a caller already knows (`<appId>#room#<collection>/<roomId>`) is still
5725
+ * the shard-0 key, so existing single-socket lookups keep working verbatim.
5726
+ */
5727
+ function routingBaseKey(base, shardIndex) {
5728
+ return shardIndex > 0 ? `${base}#s${shardIndex}` : base;
5729
+ }
5730
+ /** The live connection holding a given lease key, across identity collisions. */
5731
+ function connectionForLeaseKey(leaseKey) {
5732
+ for (const connection of connections.values()) {
5733
+ if (connection.acquisitionKey === leaseKey)
5734
+ return connection;
5735
+ }
5736
+ return undefined;
5737
+ }
5738
+ /**
5739
+ * The connection that currently owns a subscription id.
5740
+ *
5741
+ * A subscriber's unsubscribe closure captures the connection it registered on,
5742
+ * but a `subscription_limit` re-home can move the subscription to another shard
5743
+ * afterwards. Re-resolving by id means an unsubscribe still reaches the socket
5744
+ * that actually holds it instead of silently no-oping and leaking the
5745
+ * subscription. Ids are minted locally and globally unique, so an id identifies
5746
+ * exactly one subscription across every connection.
5747
+ */
5748
+ function connectionHoldingSubscription(subscriptionId) {
5749
+ for (const connection of connections.values()) {
5750
+ if (connection.subscriptions.has(subscriptionId))
5751
+ return connection;
5752
+ }
5753
+ return undefined;
5754
+ }
5755
+ /**
5756
+ * Walk a routing target's shard pool, shard 0 first, and return the first
5757
+ * connection the caller accepts. Callers that used to read a single
5758
+ * `connections.get(<base>)` need this because shard 0 closes when it drains
5759
+ * while a later shard is still carrying subscriptions - a bare lookup would
5760
+ * then report "no socket" for an app that has several.
5761
+ */
5762
+ function findShardConnection(baseKey, accept) {
5763
+ for (let shardIndex = 0; shardIndex < MAX_SHARDS_PER_KEY; shardIndex++) {
5764
+ const connection = connections.get(routingBaseKey(baseKey, shardIndex));
5765
+ if (connection && accept(connection))
5766
+ return connection;
5767
+ }
5768
+ return undefined;
5769
+ }
5770
+ /** An OPEN socket that is not mid-auth: safe to send an authorized frame on. */
5771
+ function isWarmConnection(connection) {
5772
+ return !!connection.ws
5773
+ && connection.ws.readyState === WS_READY_STATE_OPEN
5774
+ && !connection.isAuthenticating;
5775
+ }
5663
5776
  function cancelIdleConnectionClose(connection) {
5664
5777
  if (!connection.idleCloseTimer)
5665
5778
  return;
@@ -5723,9 +5836,9 @@ function scheduleConnectionCloseIfIdle(connection) {
5723
5836
  * reuse. The lease is released once the caller has registered its
5724
5837
  * subscription/callback.
5725
5838
  */
5726
- async function acquireConnection(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, source) {
5839
+ async function acquireConnection(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, source, shardIndex = 0) {
5727
5840
  var _a;
5728
- const leaseKey = connectionLeaseKey(appId, routePath, principalKey, principalToken, source === null || source === void 0 ? void 0 : source.anonymous);
5841
+ const leaseKey = connectionLeaseKey(appId, routePath, principalKey, principalToken, source === null || source === void 0 ? void 0 : source.anonymous, shardIndex);
5729
5842
  connectionAcquisitions.set(leaseKey, ((_a = connectionAcquisitions.get(leaseKey)) !== null && _a !== void 0 ? _a : 0) + 1);
5730
5843
  let released = false;
5731
5844
  let releaseTimer = null;
@@ -5747,7 +5860,7 @@ async function acquireConnection(appId, isServer, routePath, authTokenProvider,
5747
5860
  scheduleConnectionCloseIfIdle(connection);
5748
5861
  };
5749
5862
  try {
5750
- const connection = await getOrCreateConnection(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, source);
5863
+ const connection = await getOrCreateConnection(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, source, shardIndex);
5751
5864
  const releaseConnection = () => release(connection);
5752
5865
  // Registration is synchronous after the caller's await. This fail-safe
5753
5866
  // releases on the next task even if an option getter/callback throws
@@ -5760,15 +5873,94 @@ async function acquireConnection(appId, isServer, routePath, authTokenProvider,
5760
5873
  throw error;
5761
5874
  }
5762
5875
  }
5763
- async function getOrCreateConnection(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, source) {
5876
+ function shardTargetFromConnection(connection) {
5877
+ var _a;
5878
+ return {
5879
+ appId: connection.appId,
5880
+ isServer: connection.isServer,
5881
+ routePath: connection.routePath,
5882
+ authTokenProvider: connection.authTokenProvider,
5883
+ principalKey: connection.principalKey,
5884
+ principalToken: (_a = connection.connectionIdentityToken) !== null && _a !== void 0 ? _a : undefined,
5885
+ source: connection.source,
5886
+ };
5887
+ }
5888
+ function shardLeaseKeyFor(target, shardIndex) {
5889
+ var _a;
5890
+ return connectionLeaseKey(target.appId, target.routePath, target.principalKey, target.principalToken, (_a = target.source) === null || _a === void 0 ? void 0 : _a.anonymous, shardIndex);
5891
+ }
5892
+ function subscriptionLimitExhaustedError(target) {
5893
+ const error = new Error(`${SUBSCRIPTION_LIMIT_CODE}: this client already holds the maximum ${MAX_SHARDS_PER_KEY * SHARD_CAP} live subscriptions for ${target.appId}`);
5894
+ error.code = SUBSCRIPTION_LIMIT_CODE;
5895
+ error.status = 429;
5896
+ return error;
5897
+ }
5898
+ /**
5899
+ * Choose the shard that should carry a subscription.
5900
+ *
5901
+ * The de-dup scan spans EVERY shard of the target before any placement decision:
5902
+ * a second subscriber with identical specs must ref-count onto the existing
5903
+ * subscription wherever it lives, never open a duplicate on another socket.
5904
+ * Failing that, the lowest-index shard with room wins, so a client that drops
5905
+ * back under one socket's worth of subscriptions collapses onto shard 0 again.
5906
+ */
5907
+ function planSubscriptionShard(target, matches) {
5908
+ let firstShardWithRoom = -1;
5909
+ for (let shardIndex = 0; shardIndex < MAX_SHARDS_PER_KEY; shardIndex++) {
5910
+ const connection = connectionForLeaseKey(shardLeaseKeyFor(target, shardIndex));
5911
+ if (!connection) {
5912
+ if (firstShardWithRoom < 0)
5913
+ firstShardWithRoom = shardIndex;
5914
+ continue;
5915
+ }
5916
+ for (const subscription of connection.subscriptions.values()) {
5917
+ if (matches(subscription))
5918
+ return { shardIndex, existing: subscription };
5919
+ }
5920
+ if (firstShardWithRoom < 0 && connection.subscriptions.size < connection.capacityCeiling) {
5921
+ firstShardWithRoom = shardIndex;
5922
+ }
5923
+ }
5924
+ return { shardIndex: firstShardWithRoom < 0 ? MAX_SHARDS_PER_KEY : firstShardWithRoom };
5925
+ }
5926
+ /**
5927
+ * Acquire the connection that will carry a subscription, opening another shard
5928
+ * when the current ones are at SHARD_CAP.
5929
+ *
5930
+ * Placement is re-decided after every acquisition because the auth preflight is
5931
+ * an await: concurrently mounting subscribers can fill the shard this call
5932
+ * planned for, and the subscription it planned to join can be gone. Both cases
5933
+ * re-plan rather than overshoot, bounded by the shard ceiling.
5934
+ */
5935
+ async function acquireSubscriptionShard(target, matches) {
5936
+ for (let attempt = 0; attempt <= MAX_SHARDS_PER_KEY; attempt++) {
5937
+ const placement = planSubscriptionShard(target, matches);
5938
+ if (placement.shardIndex >= MAX_SHARDS_PER_KEY)
5939
+ throw subscriptionLimitExhaustedError(target);
5940
+ const acquisition = await acquireConnection(target.appId, target.isServer, target.routePath, target.authTokenProvider, target.principalKey, target.principalToken, target.source, placement.shardIndex);
5941
+ const { connection } = acquisition;
5942
+ const existing = placement.existing;
5943
+ if (existing) {
5944
+ if (connection.subscriptions.get(existing.subscriptionId) === existing) {
5945
+ return { acquisition, existing };
5946
+ }
5947
+ }
5948
+ else if (connection.subscriptions.size < connection.capacityCeiling) {
5949
+ return { acquisition };
5950
+ }
5951
+ acquisition.release();
5952
+ }
5953
+ throw subscriptionLimitExhaustedError(target);
5954
+ }
5955
+ async function getOrCreateConnection(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, source, shardIndex = 0) {
5764
5956
  // Singleflight: concurrent first-use of the same routing target shares one
5765
5957
  // in-flight connection rather than each creating an orphan socket. Keyed by
5766
5958
  // the identity-bucket lease key so distinct wallet tokens never coalesce.
5767
- const leaseKey = connectionLeaseKey(appId, routePath, principalKey, principalToken, source === null || source === void 0 ? void 0 : source.anonymous);
5959
+ const leaseKey = connectionLeaseKey(appId, routePath, principalKey, principalToken, source === null || source === void 0 ? void 0 : source.anonymous, shardIndex);
5768
5960
  const activeFlight = connectionFlights.get(leaseKey);
5769
5961
  if (activeFlight)
5770
5962
  return activeFlight;
5771
- const flight = getOrCreateConnectionOnce(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, leaseKey, source);
5963
+ const flight = getOrCreateConnectionOnce(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, leaseKey, source, shardIndex);
5772
5964
  connectionFlights.set(leaseKey, flight);
5773
5965
  try {
5774
5966
  return await flight;
@@ -5779,7 +5971,7 @@ async function getOrCreateConnection(appId, isServer, routePath, authTokenProvid
5779
5971
  }
5780
5972
  }
5781
5973
  }
5782
- async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, leaseKey, source) {
5974
+ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenProvider, principalKey, principalToken, leaseKey, source, shardIndex = 0) {
5783
5975
  var _a, _b;
5784
5976
  attachBrowserReconnectHooksOnce();
5785
5977
  const anonymous = (source === null || source === void 0 ? void 0 : source.anonymous) === true;
@@ -5793,8 +5985,11 @@ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenPr
5793
5985
  // never shares a connection (or token) with any top-level server caller. A
5794
5986
  // read-only scoped (anonymous) instance is likewise isolated by an `#ro`
5795
5987
  // marker so it never shares the authenticated global socket on the same appId.
5988
+ // Shard N>0 of the same routing target is a SEPARATE socket to the SAME
5989
+ // Durable Object: the server's cap is per websocket attachment, so an extra
5990
+ // connection buys an extra budget without changing where anything routes.
5796
5991
  const appKey = anonymous ? `${appId}#ro` : appId;
5797
- const base = roomKey ? `${appKey}#room#${roomKey}` : appKey;
5992
+ const base = routingBaseKey(roomKey ? `${appKey}#room#${roomKey}` : appKey, shardIndex);
5798
5993
  const identityBucketKey = principalKey
5799
5994
  ? `${base}#id#${principalKey}`
5800
5995
  : base;
@@ -5869,6 +6064,9 @@ async function getOrCreateConnectionOnce(appId, isServer, routePath, authTokenPr
5869
6064
  appId,
5870
6065
  key: connKey,
5871
6066
  acquisitionKey: leaseKey,
6067
+ shardIndex,
6068
+ principalKey,
6069
+ capacityCeiling: SHARD_CAP,
5872
6070
  routePath: roomKey ? routePath : undefined,
5873
6071
  authTokenProvider,
5874
6072
  connectionIdentityToken: principalToken !== null && principalToken !== void 0 ? principalToken : null,
@@ -6169,6 +6367,16 @@ function handleServerMessage(connection, message) {
6169
6367
  if (connection.isAuthenticating && connection.pendingAuthToken && !connection.authTokenProvider && !connection.anonymous) {
6170
6368
  void recoverFromServerAuthRejection(connection);
6171
6369
  }
6370
+ // `subscription_limit` is backpressure on THIS socket, not a verdict
6371
+ // on the read: the DO refuses a subscribe once its hibernation
6372
+ // attachment is full. Move the subscription to another shard instead
6373
+ // of failing the caller. The client shards below the server's cap, so
6374
+ // this is the residual case - another tab on the same DO, a race, or
6375
+ // a server enforcing a lower cap.
6376
+ if (message.code === SUBSCRIPTION_LIMIT_CODE && message.subscriptionId
6377
+ && rehomeRefusedSubscription(connection, message.subscriptionId, message)) {
6378
+ break;
6379
+ }
6172
6380
  // Build an Error that ALSO carries the stable `code` as a structured
6173
6381
  // field (not just string-interpolated into the message) so callers can
6174
6382
  // branch on it — e.g. `catch (e) { if (e.code === 'invariant_violation') }`
@@ -6177,6 +6385,11 @@ function handleServerMessage(connection, message) {
6177
6385
  const buildWsError = () => {
6178
6386
  const err = new Error(`${message.code}: ${message.message}`);
6179
6387
  err.code = message.code;
6388
+ // The server's HTTP-equivalent status, so a caller can tell 429
6389
+ // backpressure from a 413 oversize frame from a 403 policy
6390
+ // denial without pattern-matching the code string.
6391
+ if (typeof message.status === 'number')
6392
+ err.status = message.status;
6180
6393
  if (message.requestId)
6181
6394
  err.requestId = message.requestId;
6182
6395
  if (message.subscriptionId)
@@ -6352,6 +6565,23 @@ function rejectPendingSubscriptionConfirmation(connection, subscriptionId, error
6352
6565
  pending.reject(error);
6353
6566
  return true;
6354
6567
  }
6568
+ /**
6569
+ * Take a confirmation record off a connection WITHOUT settling it, so it can be
6570
+ * re-armed on another socket. A `subscription_limit` refusal has not failed the
6571
+ * caller's subscribe - the subscription is still being placed - so rejecting the
6572
+ * promise here would turn backpressure into an error the app has to handle.
6573
+ */
6574
+ function detachPendingSubscriptionConfirmation(connection, subscriptionId) {
6575
+ const pending = connection.pendingSubscriptions.get(subscriptionId);
6576
+ if (!pending)
6577
+ return null;
6578
+ connection.pendingSubscriptions.delete(subscriptionId);
6579
+ if (pending.timer) {
6580
+ clearTimeout(pending.timer);
6581
+ pending.timer = null;
6582
+ }
6583
+ return pending;
6584
+ }
6355
6585
  function armSubscriptionConfirmationTimeout(connection, subscription, pending) {
6356
6586
  pending.timer = setTimeout(() => {
6357
6587
  if (connection.pendingSubscriptions.get(subscription.subscriptionId) !== pending)
@@ -6445,6 +6675,96 @@ function registerDetachedReplayConfirmation(connection, subscription) {
6445
6675
  }
6446
6676
  });
6447
6677
  }
6678
+ function subscriptionLimitError(message) {
6679
+ const error = new Error(`${message.code}: ${message.message}`);
6680
+ error.code = message.code;
6681
+ error.status = typeof message.status === 'number' ? message.status : 429;
6682
+ if (message.subscriptionId)
6683
+ error.subscriptionId = message.subscriptionId;
6684
+ return error;
6685
+ }
6686
+ /**
6687
+ * Handle a server `subscription_limit` refusal as backpressure: unregister the
6688
+ * subscription from the socket that refused it and place it on another shard.
6689
+ *
6690
+ * Unregistering is what makes the refusal non-permanent. A refused subscription
6691
+ * that stayed in `connection.subscriptions` was re-sent by replaySubscriptions()
6692
+ * on every reconnect, to be refused again forever.
6693
+ *
6694
+ * Returns true when this refusal has been taken over and must not also be
6695
+ * reported to the subscription's callbacks.
6696
+ */
6697
+ function rehomeRefusedSubscription(connection, subscriptionId, message) {
6698
+ const subscription = connection.subscriptions.get(subscriptionId);
6699
+ if (!subscription)
6700
+ return false;
6701
+ connection.subscriptions.delete(subscriptionId);
6702
+ // Believe the server over SHARD_CAP: whatever this socket still holds is the
6703
+ // most it demonstrably accepts. The planner stops offering it until it drains.
6704
+ connection.capacityCeiling = Math.min(connection.capacityCeiling, connection.subscriptions.size);
6705
+ const pending = detachPendingSubscriptionConfirmation(connection, subscriptionId);
6706
+ subscription.rehomeAttempts += 1;
6707
+ if (subscription.rehomeAttempts > MAX_REHOME_ATTEMPTS) {
6708
+ // Every shard refused it. This is the one case where the cap reaches the
6709
+ // app, and it arrives as a real error with `code` and `status`.
6710
+ const error = subscriptionLimitError(message);
6711
+ notifyErrorCallbacks(subscription, error);
6712
+ rejectSubscriptionRefresh(subscription, error);
6713
+ pending === null || pending === void 0 ? void 0 : pending.reject(error);
6714
+ scheduleConnectionCloseIfIdle(connection);
6715
+ return true;
6716
+ }
6717
+ void moveSubscriptionToAnotherShard(connection, subscription, pending, message);
6718
+ scheduleConnectionCloseIfIdle(connection);
6719
+ return true;
6720
+ }
6721
+ async function moveSubscriptionToAnotherShard(refusedConnection, subscription, pending, message) {
6722
+ const target = shardTargetFromConnection(refusedConnection);
6723
+ let acquisition;
6724
+ try {
6725
+ // Never join an existing subscription here: this id is the thing being
6726
+ // placed, and its de-dup identity was already settled when it was created.
6727
+ ({ acquisition } = await acquireSubscriptionShard(target, () => false));
6728
+ }
6729
+ catch (error) {
6730
+ const failure = error instanceof Error ? error : subscriptionLimitError(message);
6731
+ notifyErrorCallbacks(subscription, failure);
6732
+ rejectSubscriptionRefresh(subscription, failure);
6733
+ pending === null || pending === void 0 ? void 0 : pending.reject(failure);
6734
+ return;
6735
+ }
6736
+ const connection = acquisition.connection;
6737
+ try {
6738
+ if (subscription.callbacks.length === 0) {
6739
+ // Every consumer unsubscribed while the re-home was in flight.
6740
+ pending === null || pending === void 0 ? void 0 : pending.reject(new Error('Subscription unsubscribed'));
6741
+ return;
6742
+ }
6743
+ // The new socket owes this subscription a fresh snapshot, exactly as a
6744
+ // reconnect replay does, so callbacks are not left holding stale data.
6745
+ subscription.lastData = undefined;
6746
+ connection.subscriptions.set(subscription.subscriptionId, subscription);
6747
+ if (pending) {
6748
+ pending.retrySent = false;
6749
+ connection.pendingSubscriptions.set(subscription.subscriptionId, pending);
6750
+ }
6751
+ else {
6752
+ registerDetachedReplayConfirmation(connection, subscription);
6753
+ }
6754
+ if (isWarmConnection(connection)) {
6755
+ sendSubscribeWithConfirmation(connection, subscription);
6756
+ }
6757
+ else if (pending) {
6758
+ // Cold or authenticating: replaySubscriptions() sends it and re-arms
6759
+ // the per-socket window once the socket authenticates. Bound the wait
6760
+ // until then, the same way an original cold registration does.
6761
+ armSubscriptionConfirmationTimeout(connection, subscription, pending);
6762
+ }
6763
+ }
6764
+ finally {
6765
+ acquisition.release();
6766
+ }
6767
+ }
6448
6768
  function resolveSubscriptionRefresh(subscription) {
6449
6769
  const refresh = subscription.refresh;
6450
6770
  if (!refresh)
@@ -6540,7 +6860,7 @@ function sendUnsubscribe(connection, subscriptionId) {
6540
6860
  * the subscription onto, since a WS connection routes to a single DO.
6541
6861
  */
6542
6862
  async function subscribeV2(path, subscriptionOptions, roomRoutePath) {
6543
- var _a, _b, _c, _d, _e, _f;
6863
+ var _a, _b, _c, _d, _e;
6544
6864
  const overrides = subscriptionOptions._overrides;
6545
6865
  // A scoped createClient() instance passes its own resolved config; the
6546
6866
  // default path reads the module-global init() config.
@@ -6579,16 +6899,52 @@ async function subscribeV2(path, subscriptionOptions, roomRoutePath) {
6579
6899
  const cacheKey = responseCacheEnabled
6580
6900
  ? getCacheKey(normalizedPath, subscriptionOptions.prompt, subscriptionOptions.shape, subscriptionOptions.limit, subscriptionOptions.cursor, subscriptionOptions.filter, identity)
6581
6901
  : null;
6902
+ // Stringify THIS call's specs once; existing subscriptions carry their
6903
+ // precomputed specKey, so the shared-subscription scan is a string compare per
6904
+ // entry (previously each new subscribe re-stringified every existing
6905
+ // subscription's shape/filter/sort - O(N²) with many live hooks). Resolved
6906
+ // before acquisition because shard placement needs the de-dup predicate.
6907
+ const specKey = JSON.stringify([
6908
+ (_b = subscriptionOptions.shape) !== null && _b !== void 0 ? _b : null,
6909
+ (_c = subscriptionOptions.filter) !== null && _c !== void 0 ? _c : null,
6910
+ (_d = subscriptionOptions.sort) !== null && _d !== void 0 ? _d : null,
6911
+ ]);
6912
+ const normalizedLimit = normalizeSubscriptionLimit(subscriptionOptions.limit);
6913
+ const includeSubPaths = (_e = subscriptionOptions.includeSubPaths) !== null && _e !== void 0 ? _e : false;
6914
+ // H1: the scan also requires the same principal identity (compact hash AND
6915
+ // exact token) - a different logged-in user must not piggy-back on (and
6916
+ // inherit lastData from) another user's existing subscription, even with
6917
+ // identical path/prompt/shape/filter/sort.
6918
+ const matchesThisSubscription = (sub) => (sub.identity === identity
6919
+ && sub.identityToken === identityToken
6920
+ && sub.path === normalizedPath
6921
+ && sub.prompt === subscriptionOptions.prompt
6922
+ && sub.specKey === specKey
6923
+ && sub.includeSubPaths === includeSubPaths
6924
+ && sub.normalizedLimit === normalizedLimit
6925
+ && sub.cursor === subscriptionOptions.cursor);
6582
6926
  // Acquire a connection for this routing target (room-scoped when a room route
6583
- // is supplied by the live helper, else the app-level connection). The lease is
6584
- // held across auth preflight and released once this call registers below, so a
6585
- // concurrent old-route teardown cannot close a socket we are about to reuse.
6927
+ // is supplied by the live helper, else the app-level connection), on the shard
6928
+ // that either already holds this exact subscription or still has room under
6929
+ // the server's per-socket cap. The lease is held across auth preflight and
6930
+ // released once this call registers below, so a concurrent old-route teardown
6931
+ // cannot close a socket we are about to reuse.
6586
6932
  let connection;
6587
6933
  let releaseConnection;
6934
+ let existingSubscription;
6588
6935
  try {
6589
- const acquisition = await acquireConnection(effectiveAppId, config.isServer, roomRoutePath, authTokenProvider, principalKey, principalToken, source);
6936
+ const { acquisition, existing } = await acquireSubscriptionShard({
6937
+ appId: effectiveAppId,
6938
+ isServer: config.isServer,
6939
+ routePath: roomRoutePath,
6940
+ authTokenProvider,
6941
+ principalKey,
6942
+ principalToken,
6943
+ source,
6944
+ }, matchesThisSubscription);
6590
6945
  connection = acquisition.connection;
6591
6946
  releaseConnection = acquisition.release;
6947
+ existingSubscription = existing;
6592
6948
  }
6593
6949
  catch (error) {
6594
6950
  const err = error instanceof Error ? error : new Error(String(error));
@@ -6599,34 +6955,6 @@ async function subscribeV2(path, subscriptionOptions, roomRoutePath) {
6599
6955
  throw err;
6600
6956
  }
6601
6957
  const cachedEntry = cacheKey ? getResponseCacheEntry(cacheKey, identityToken) : undefined;
6602
- // Check if we already have a subscription for this path+prompt+shape+limit+cursor+filter+sort
6603
- let existingSubscription;
6604
- let specKey = '';
6605
- try {
6606
- // Stringify THIS call's specs once; existing subscriptions carry their
6607
- // precomputed specKey, so the scan is a string compare per entry
6608
- // (previously each new subscribe re-stringified every existing
6609
- // subscription's shape/filter/sort — O(N²) with many live hooks).
6610
- specKey = JSON.stringify([
6611
- (_b = subscriptionOptions.shape) !== null && _b !== void 0 ? _b : null,
6612
- (_c = subscriptionOptions.filter) !== null && _c !== void 0 ? _c : null,
6613
- (_d = subscriptionOptions.sort) !== null && _d !== void 0 ? _d : null,
6614
- ]);
6615
- for (const sub of connection.subscriptions.values()) {
6616
- // H1: also require the same principal identity (compact hash AND exact
6617
- // token) — a different logged-in user must not piggy-back on (and
6618
- // inherit lastData from) another user's existing subscription, even
6619
- // with identical path/prompt/shape/filter/sort.
6620
- if (sub.identity === identity && sub.identityToken === identityToken && sub.path === normalizedPath && sub.prompt === subscriptionOptions.prompt && sub.specKey === specKey && sub.includeSubPaths === ((_e = subscriptionOptions.includeSubPaths) !== null && _e !== void 0 ? _e : false) && sub.limit === subscriptionOptions.limit && sub.cursor === subscriptionOptions.cursor) {
6621
- existingSubscription = sub;
6622
- break;
6623
- }
6624
- }
6625
- }
6626
- catch (error) {
6627
- releaseConnection();
6628
- throw error;
6629
- }
6630
6958
  if (existingSubscription) {
6631
6959
  let replayData = undefined;
6632
6960
  let replayOnData;
@@ -6709,8 +7037,9 @@ async function subscribeV2(path, subscriptionOptions, roomRoutePath) {
6709
7037
  prompt: subscriptionOptions.prompt,
6710
7038
  shape: subscriptionOptions.shape,
6711
7039
  limit: subscriptionOptions.limit,
7040
+ normalizedLimit,
6712
7041
  cursor: subscriptionOptions.cursor,
6713
- includeSubPaths: (_f = subscriptionOptions.includeSubPaths) !== null && _f !== void 0 ? _f : false,
7042
+ includeSubPaths,
6714
7043
  callbacks: [subscriptionOptions],
6715
7044
  lastData: undefined,
6716
7045
  dataRevision: 0,
@@ -6719,6 +7048,7 @@ async function subscribeV2(path, subscriptionOptions, roomRoutePath) {
6719
7048
  identity,
6720
7049
  identityToken,
6721
7050
  specKey,
7051
+ rehomeAttempts: 0,
6722
7052
  };
6723
7053
  connection.subscriptions.set(subscriptionId, subscription);
6724
7054
  }
@@ -6792,15 +7122,21 @@ async function subscribeV2(path, subscriptionOptions, roomRoutePath) {
6792
7122
  await removeCallbackFromSubscription(connection, subscriptionId, subscriptionOptions);
6793
7123
  };
6794
7124
  }
6795
- async function removeCallbackFromSubscription(connection, subscriptionId, callback) {
6796
- var _a;
7125
+ async function removeCallbackFromSubscription(registeredOn, subscriptionId, callback) {
7126
+ var _a, _b;
7127
+ // A `subscription_limit` re-home can have moved this subscription to another
7128
+ // shard since the caller captured its connection, so resolve the current
7129
+ // owner rather than no-oping (which would leak the subscription forever).
7130
+ const connection = registeredOn.subscriptions.has(subscriptionId)
7131
+ ? registeredOn
7132
+ : (_a = connectionHoldingSubscription(subscriptionId)) !== null && _a !== void 0 ? _a : registeredOn;
6797
7133
  const subscription = connection.subscriptions.get(subscriptionId);
6798
7134
  if (!subscription) {
6799
7135
  return;
6800
7136
  }
6801
7137
  // Remove this callback
6802
7138
  subscription.callbacks = subscription.callbacks.filter(cb => cb !== callback);
6803
- (_a = subscription.refresh) === null || _a === void 0 ? void 0 : _a.callbacks.delete(callback);
7139
+ (_b = subscription.refresh) === null || _b === void 0 ? void 0 : _b.callbacks.delete(callback);
6804
7140
  // If there are still callbacks, don't unsubscribe
6805
7141
  if (subscription.callbacks.length > 0) {
6806
7142
  return;
@@ -6966,7 +7302,7 @@ async function doReconnectWithNewAuth() {
6966
7302
  console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
6967
7303
  }
6968
7304
  try {
6969
- const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-CeDT7vJF.mjs');
7305
+ const { reconnectRealtimeStoreWithNewAuth } = await import('./realtime-store-DqAAqfo9.mjs');
6970
7306
  await reconnectRealtimeStoreWithNewAuth();
6971
7307
  }
6972
7308
  catch (error) {
@@ -7041,8 +7377,10 @@ const WS_RUNQUERY_MAX_FRAME_BYTES = 60000;
7041
7377
  */
7042
7378
  async function runNamedQueriesOverSocket(appId, queries, isServer) {
7043
7379
  var _a;
7044
- const connection = connections.get(appId);
7045
- if (!connection || !connection.ws || connection.ws.readyState !== WS_READY_STATE_OPEN || connection.isAuthenticating) {
7380
+ // Any warm shard of this app's pool will do: a query is not bound to the
7381
+ // socket a subscription happens to live on.
7382
+ const connection = findShardConnection(appId, isWarmConnection);
7383
+ if (!connection) {
7046
7384
  return null; // no warm socket -> HTTP (never open one just for queries)
7047
7385
  }
7048
7386
  if (!((_a = connection.capabilities) === null || _a === void 0 ? void 0 : _a.runQuery)) {
@@ -7092,9 +7430,8 @@ async function runNamedQueriesOverSocket(appId, queries, isServer) {
7092
7430
  */
7093
7431
  async function wsIntent(appId, roomRoutePath, intent, isServer) {
7094
7432
  const roomKey = roomKeyFromRoutePath(roomRoutePath);
7095
- const connKey = roomKey ? `${appId}#room#${roomKey}` : appId;
7096
- const connection = connections.get(connKey);
7097
- if (!connection || !connection.ws || connection.ws.readyState !== WS_READY_STATE_OPEN || connection.isAuthenticating) {
7433
+ const connection = findShardConnection(roomKey ? `${appId}#room#${roomKey}` : appId, isWarmConnection);
7434
+ if (!connection) {
7098
7435
  return false;
7099
7436
  }
7100
7437
  // Identity-aware send gate. The server attributes intents to the
@@ -7124,6 +7461,9 @@ async function wsIntent(appId, roomRoutePath, intent, isServer) {
7124
7461
  return false;
7125
7462
  }
7126
7463
  try {
7464
+ // The identity gate above awaits, so the socket is re-read here; a
7465
+ // teardown in that window throws and is reported as not-sent, exactly as
7466
+ // wsIntentReliable does.
7127
7467
  connection.ws.send(JSON.stringify({ type: 'intent', intent }));
7128
7468
  return true;
7129
7469
  }
@@ -7161,9 +7501,8 @@ async function maybeReauthRoomConnection(connection, isServer) {
7161
7501
  */
7162
7502
  async function wsIntentReliable(appId, roomRoutePath, intent, isServer) {
7163
7503
  const roomKey = roomKeyFromRoutePath(roomRoutePath);
7164
- const connKey = roomKey ? `${appId}#room#${roomKey}` : appId;
7165
- const connection = connections.get(connKey);
7166
- if (!connection || !connection.ws || connection.ws.readyState !== WS_READY_STATE_OPEN || connection.isAuthenticating) {
7504
+ const connection = findShardConnection(roomKey ? `${appId}#room#${roomKey}` : appId, isWarmConnection);
7505
+ if (!connection) {
7167
7506
  return undefined;
7168
7507
  }
7169
7508
  if (!(await canSendAuthenticatedLiveIntent(connection, isServer))) {
@@ -8417,7 +8756,7 @@ async function setMany(many, options) {
8417
8756
  'Nothing was signed or submitted.');
8418
8757
  }
8419
8758
  const solanaChain = workerChain;
8420
- const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await import('./solana-operations-CHgW7Pue.mjs');
8759
+ const { handlePreBuiltTransaction, buildExpectedWriteIntent } = await import('./solana-operations-DGNA2nGZ.mjs');
8421
8760
  // OA-0001: derive the expected intent from OUR OWN request (the one
8422
8761
  // ground truth a tampered builder response cannot change) so the
8423
8762
  // validator can reject any transaction whose operation semantics
@@ -8781,7 +9120,7 @@ async function getTransactionJson(rpc, signature, commitment = 'confirmed') {
8781
9120
  }
8782
9121
 
8783
9122
  const DEFAULT_TIMEOUT_MS = 60000;
8784
- const DEFAULT_MAX_ATTEMPTS = 20;
9123
+ const DEFAULT_MAX_ATTEMPTS$1 = 20;
8785
9124
  const DEFAULT_POLL_INTERVAL_MS = 3000;
8786
9125
  /** Mirror read after a late confirmation. No settle delay: the tx already landed. */
8787
9126
  const RECONCILE_MIRROR_SETTLE_MS = 0;
@@ -8988,7 +9327,7 @@ async function waitForSetResult(receipt, options) {
8988
9327
  return reconcileSetResult(receipt, options);
8989
9328
  }
8990
9329
  const timeoutMs = (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT_MS;
8991
- const maxAttempts = (_b = options === null || options === void 0 ? void 0 : options.maxAttempts) !== null && _b !== void 0 ? _b : DEFAULT_MAX_ATTEMPTS;
9330
+ const maxAttempts = (_b = options === null || options === void 0 ? void 0 : options.maxAttempts) !== null && _b !== void 0 ? _b : DEFAULT_MAX_ATTEMPTS$1;
8992
9331
  const pollIntervalMs = (_c = options === null || options === void 0 ? void 0 : options.pollIntervalMs) !== null && _c !== void 0 ? _c : DEFAULT_POLL_INTERVAL_MS;
8993
9332
  const startedAt = Date.now();
8994
9333
  let current = receipt;
@@ -9106,72 +9445,95 @@ async function reconnectWithNewAuth() {
9106
9445
  return reconnectWithNewAuthV2();
9107
9446
  }
9108
9447
 
9109
- /**
9110
- * Scoped multi-app client factory.
9111
- *
9112
- * `createClient()` returns an isolated, READ-ONLY client bound to one Bounded
9113
- * app, independent of the module-global `init()` client. It exists so a single
9114
- * frontend can hold its normal authenticated client for its own app AND a
9115
- * read-only client for a SECOND Bounded app (e.g. a shared steward/runtime app),
9116
- * with live subscriptions on both and zero interference between them.
9117
- *
9118
- * Each instance carries its OWN fully-resolved config (endpoints + appId) and
9119
- * routes through the SAME shared transport/subscription implementation as the
9120
- * global path - it never forks that code. Isolation comes from threading the
9121
- * instance config (and a read-only marker) through the existing per-request /
9122
- * per-subscription override channel, plus the websocket layer already keying
9123
- * connections by appId.
9124
- *
9125
- * ## v1 scope: read-only, unauthenticated
9126
- *
9127
- * A scoped instance is UNAUTHENTICATED. It sends no auth, never reads or
9128
- * refreshes the global session, and connects to its app's realtime endpoint
9129
- * anonymously. It is therefore only useful against collections a Bounded app
9130
- * exposes with a public read rule (`read: true`) - private/per-user reads will
9131
- * be denied by the target app's policy, exactly as an anonymous caller would be.
9132
- *
9133
- * Writes and any auth-requiring surface are NOT supported on a scoped instance
9134
- * and throw a clear error. Per-instance authentication is a planned follow-up.
9135
- */
9136
- /** Error thrown when an unsupported (write / auth-requiring) call is made on a
9137
- * read-only scoped client. */
9448
+ var __rest$1 = (undefined && undefined.__rest) || function (s, e) {
9449
+ var t = {};
9450
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
9451
+ t[p] = s[p];
9452
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
9453
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
9454
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9455
+ t[p[i]] = s[p[i]];
9456
+ }
9457
+ return t;
9458
+ };
9459
+ /** Error thrown when an unsupported (write) call is made on a scoped client.
9460
+ * Reads may be authenticated per instance; writes still are not supported. */
9138
9461
  class ScopedClientReadOnlyError extends Error {
9139
9462
  constructor(operation) {
9140
- super(`createClient() instances are read-only in v1: "${operation}" is not supported. ` +
9141
- `Use the global init() client for authenticated writes, or read only public ` +
9142
- `(read: true) collections on this scoped instance.`);
9463
+ super(`createClient() instances are read-only: "${operation}" is not supported. ` +
9464
+ `Use the global init() client for authenticated writes, or read public ` +
9465
+ `(read: true) collections - and, with getAuthHeaders, this credential's own ` +
9466
+ `readable collections - on this scoped instance.`);
9143
9467
  this.name = 'ScopedClientReadOnlyError';
9144
9468
  }
9145
9469
  }
9146
9470
  /**
9147
- * Create a read-only client scoped to a single Bounded app.
9471
+ * Create a read-only client scoped to a single Bounded app, anonymous by default
9472
+ * and authenticated when given `getAuthHeaders`.
9148
9473
  *
9149
9474
  * @example
9150
9475
  * ```ts
9151
9476
  * // Global client stays the venue's own authenticated app:
9152
9477
  * await init({ appId: VENUE_APP, network: 'bounded-production', authMethod: 'email' });
9153
9478
  *
9154
- * // A second, read-only client for the steward runtime app:
9479
+ * // A second, anonymous client for the steward runtime app's public collections:
9155
9480
  * const steward = createClient({ appId: STEWARD_APP, network: 'bounded-production' });
9156
9481
  * const state = await steward.get('runtime/current');
9157
9482
  * const stop = await steward.subscribe('runtime/current', (d) => render(d));
9483
+ *
9484
+ * // The same second app, read AS THE SIGNED-IN HUMAN: the venue session is
9485
+ * // exchanged for a token pinned to the child app (see createAppTokenAuthHeaders).
9486
+ * const mine = createClient({
9487
+ * appId: ROOT_APP,
9488
+ * network: 'bounded-production',
9489
+ * getAuthHeaders: createAppTokenAuthHeaders({
9490
+ * authApiUrl: HUMAN_AUTH_URL,
9491
+ * appId: ROOT_APP,
9492
+ * parentToken: () => getIdToken(),
9493
+ * }),
9494
+ * });
9495
+ * const stopChat = await mine.subscribe(`conversations/${id}/m`, (d) => render(d));
9158
9496
  * ```
9159
9497
  */
9160
9498
  function createClient(config) {
9161
9499
  if (!config || !config.appId) {
9162
9500
  throw new Error('createClient requires an appId.');
9163
9501
  }
9502
+ // The credential source is an instance concern, not a ClientConfig field: keep
9503
+ // it out of the resolved config so it can never leak through getConfig() or the
9504
+ // `_config` override channel.
9505
+ const { getAuthHeaders } = config, clientFields = __rest$1(config, ["getAuthHeaders"]);
9164
9506
  // Resolve from the PRISTINE defaults (never the mutated global) so a scoped
9165
9507
  // instance is fully independent of any prior init(). Same precedence as init:
9166
9508
  // defaults < network preset < explicit fields.
9167
- const resolved = resolveClientConfig(DEFAULT_CLIENT_CONFIG, config);
9509
+ const resolved = resolveClientConfig(DEFAULT_CLIENT_CONFIG, clientFields);
9510
+ // Normalize the provider once. Downstream (api.ts, operations.ts,
9511
+ // subscription-v2.ts) consumes the same `_getAuthHeaders` shape a server wallet
9512
+ // client uses, so a null/undefined return becomes "no headers" here rather than
9513
+ // becoming a special case in three transports.
9514
+ const authHeaders = getAuthHeaders
9515
+ ? async () => { var _a; return (_a = (await getAuthHeaders())) !== null && _a !== void 0 ? _a : {}; }
9516
+ : undefined;
9517
+ /** True when the provider currently yields an Authorization header. */
9518
+ const hasCredential = async () => {
9519
+ if (!authHeaders)
9520
+ return false;
9521
+ const headers = await authHeaders();
9522
+ return Object.keys(headers).some((name) => name.toLowerCase() === 'authorization');
9523
+ };
9168
9524
  // The override channel that carries this instance's identity through the
9169
9525
  // shared transport and subscription code. `_config` retargets endpoints +
9170
- // appId; `_readOnly` omits auth and blocks ambient-session/global-cache reads.
9171
- const instanceOverrides = () => ({ _config: resolved, _readOnly: true });
9526
+ // appId. Then either `_getAuthHeaders` (this instance's credential is the SOLE
9527
+ // auth source - never the ambient session, and never a managed-credential 401
9528
+ // recovery that could sign another principal out) or `_readOnly` (omit auth and
9529
+ // block ambient-session/global-cache reads).
9530
+ const instanceOverrides = () => authHeaders
9531
+ ? { _config: resolved, _getAuthHeaders: authHeaders }
9532
+ : { _config: resolved, _readOnly: true };
9172
9533
  // Merge caller-supplied opts with the instance overrides. The instance
9173
- // `_config`/`_readOnly` are applied LAST so app code can never override the
9174
- // routing/identity of a scoped call (but may still pass e.g. an AbortSignal).
9534
+ // `_config` plus its `_getAuthHeaders`/`_readOnly` are applied LAST so app code
9535
+ // can never override the routing/identity of a scoped call (but may still pass
9536
+ // e.g. an AbortSignal).
9175
9537
  const withOverrides = (opts) => {
9176
9538
  var _a;
9177
9539
  const base = (opts !== null && opts !== void 0 ? opts : {});
@@ -9179,6 +9541,7 @@ function createClient(config) {
9179
9541
  };
9180
9542
  return {
9181
9543
  appId: resolved.appId,
9544
+ authenticated: !!authHeaders,
9182
9545
  getConfig: () => (Object.assign({}, resolved)),
9183
9546
  get: (path, opts) => get(path, withOverrides(opts)),
9184
9547
  getMany: (paths, opts) => getMany(paths, withOverrides(opts)),
@@ -9188,11 +9551,19 @@ function createClient(config) {
9188
9551
  aggregate: (path, operation, opts) => aggregate(path, operation, withOverrides(opts)),
9189
9552
  runQuery: (absolutePath, queryName, queryArgs, opts) => runQuery(absolutePath, queryName, queryArgs, withOverrides(opts)),
9190
9553
  runQueryMany: (many, opts) => runQueryMany(many, withOverrides(opts)),
9191
- subscribe: (path, options) => {
9192
- var _a;
9554
+ subscribe: async (path, options) => {
9555
+ var _a, _b;
9193
9556
  const opts = typeof options === 'function' ? { onData: options } : Object.assign({}, options);
9194
- // Force this instance's scoped, read-only routing onto the subscription.
9195
- opts._overrides = Object.assign(Object.assign({}, ((_a = opts._overrides) !== null && _a !== void 0 ? _a : {})), { _config: resolved, _readOnly: true });
9557
+ // Force this instance's scoped routing onto the subscription. An
9558
+ // authenticated instance threads its credential provider, which gives the
9559
+ // subscription its own connection keyed by that exact credential; with no
9560
+ // credential right now it subscribes anonymously (the `#ro` socket) instead
9561
+ // of failing, so a signed-out page still gets public data. This is resolved
9562
+ // per subscribe, so the socket a subscription lands on always matches the
9563
+ // credential it was authorized with - including after a shard re-home,
9564
+ // which rebuilds the same routing + auth arguments from the connection.
9565
+ opts._overrides = (await hasCredential())
9566
+ ? Object.assign(Object.assign({}, ((_a = opts._overrides) !== null && _a !== void 0 ? _a : {})), { _config: resolved, _getAuthHeaders: authHeaders }) : Object.assign(Object.assign({}, ((_b = opts._overrides) !== null && _b !== void 0 ? _b : {})), { _config: resolved, _readOnly: true });
9196
9567
  return subscribe(path, opts);
9197
9568
  },
9198
9569
  set: () => { throw new ScopedClientReadOnlyError('set'); },
@@ -9200,6 +9571,264 @@ function createClient(config) {
9200
9571
  };
9201
9572
  }
9202
9573
 
9574
+ /**
9575
+ * Second-app tokens: exchange a signed-in app's id token for one pinned to a
9576
+ * DIFFERENT Bounded app the platform records as its child.
9577
+ *
9578
+ * WHY. A browser signed in to app A holds an id token pinned to A
9579
+ * (`custom:appId`), and the realtime data plane refuses it for app B
9580
+ * (`app_mismatch`). When B is a child app of A - an OpenApps agent root under its
9581
+ * venue - the Bounded Auth issuer will mint a short-lived, B-pinned token for the
9582
+ * SAME human at `POST {authApiUrl}/app-token/exchange`. The issuer, not this
9583
+ * helper, decides whether the relationship holds; it reads it from the platform's
9584
+ * own records and refuses anything else. `custom:userId` of the minted token is
9585
+ * the presenting token's own, so B sees the same principal A does.
9586
+ *
9587
+ * WHAT THIS ADDS over a bare POST, all of it in memory and per process:
9588
+ * - a cache keyed by (exact parent credential, child appId), so one exchange
9589
+ * serves every read and socket for that pair;
9590
+ * - refresh shortly BEFORE expiry, driven by use rather than a timer, so no
9591
+ * interval keeps a process alive and no request ever carries a token that
9592
+ * expires in flight;
9593
+ * - single-flight, so N components mounting at once make ONE exchange;
9594
+ * - BOUNDED retry with backoff and a recorded give-up. A 429 stops the schedule
9595
+ * immediately rather than feeding it, and so does any other definitive
9596
+ * refusal. There is no unbounded retry loop anywhere in this module;
9597
+ * - invalidation on sign-out and identity change: the cache key IS the exact
9598
+ * parent credential (hashed), so a new session cannot read the old session's
9599
+ * entry, and `clearAppTokenCache()` drops entries outright.
9600
+ *
9601
+ * The parent token is treated as OPAQUE credential material for keying: it is
9602
+ * hashed, never decoded for identity, and never logged.
9603
+ */
9604
+ /** Thrown when an exchange stops trying. `giveUp` carries the recorded reason. */
9605
+ class AppTokenExchangeError extends Error {
9606
+ constructor(giveUp) {
9607
+ super(`app token exchange for ${giveUp.appId} gave up after ${giveUp.attempts} attempt(s): ${giveUp.reason}`);
9608
+ this.name = 'AppTokenExchangeError';
9609
+ this.giveUp = giveUp;
9610
+ }
9611
+ }
9612
+ /** Re-exchange once the remaining life drops under this. */
9613
+ const REFRESH_SKEW_MS = 60000;
9614
+ /** Floor for a response that reports no usable expiry of its own. */
9615
+ const FALLBACK_LIFETIME_MS = 5 * 60000;
9616
+ const DEFAULT_MAX_ATTEMPTS = 3;
9617
+ const MAX_ATTEMPTS_CEILING = 5;
9618
+ const FIRST_RETRY_DELAY_MS = 300;
9619
+ const MAX_RETRY_DELAY_MS = 2000;
9620
+ const cache = new Map();
9621
+ const inFlight = new Map();
9622
+ /** Opaque, stable key material for a credential we must never decode or log. */
9623
+ function fingerprint(value) {
9624
+ let hash = 5381;
9625
+ for (let index = 0; index < value.length; index++) {
9626
+ hash = ((hash << 5) + hash + value.charCodeAt(index)) >>> 0;
9627
+ }
9628
+ return hash.toString(16);
9629
+ }
9630
+ function cacheKey(appId, parentToken) {
9631
+ return `${appId}:${fingerprint(parentToken)}`;
9632
+ }
9633
+ function usable(entry, nowMs) {
9634
+ return !!entry && entry.expiresAtMs - REFRESH_SKEW_MS > nowMs;
9635
+ }
9636
+ /** Cache entries above which a lookup also drops the expired ones. */
9637
+ const PRUNE_THRESHOLD = 32;
9638
+ /**
9639
+ * Drop expired entries so a long-lived process (SSR, React Native) that sees a
9640
+ * new parent credential every rotation does not grow this map forever. Keyed by
9641
+ * the exact credential, entries are otherwise only replaced by a same-key refresh.
9642
+ */
9643
+ function pruneExpired(nowMs) {
9644
+ if (cache.size <= PRUNE_THRESHOLD)
9645
+ return;
9646
+ for (const [key, entry] of Array.from(cache.entries())) {
9647
+ if (entry.expiresAtMs <= nowMs)
9648
+ cache.delete(key);
9649
+ }
9650
+ }
9651
+ function exchangeUrl(authApiUrl) {
9652
+ return `${authApiUrl.replace(/\/+$/, '')}/app-token/exchange`;
9653
+ }
9654
+ /**
9655
+ * When the minted token stops being usable: the issuer's own `expiresAt`, else
9656
+ * the token's `exp`, else a short floor for a token that carries neither.
9657
+ *
9658
+ * Returns null when both stated expiries are already in the PAST. Caching a dead
9659
+ * credential under an invented lifetime would make every request fail for that
9660
+ * long, so an expired mint is a give-up instead.
9661
+ */
9662
+ function expiryFromResponse(body, token, nowMs) {
9663
+ const reported = Number(body.expiresAt);
9664
+ if (Number.isFinite(reported))
9665
+ return reported > nowMs ? reported : null;
9666
+ try {
9667
+ const payload = JSON.parse(decodeBase64Url(token.split('.')[1]));
9668
+ const exp = Number(payload.exp);
9669
+ if (Number.isFinite(exp))
9670
+ return exp * 1000 > nowMs ? exp * 1000 : null;
9671
+ }
9672
+ catch (_a) {
9673
+ /* an opaque token is fine; fall through to the floor */
9674
+ }
9675
+ return nowMs + FALLBACK_LIFETIME_MS;
9676
+ }
9677
+ /** 5xx and transport faults are worth one more try; nothing else is. */
9678
+ function retriable(status) {
9679
+ return status === null || (status >= 500 && status !== 501);
9680
+ }
9681
+ function delay(ms) {
9682
+ return new Promise((resolve) => setTimeout(resolve, ms));
9683
+ }
9684
+ function recordGiveUp(options, giveUp) {
9685
+ var _a;
9686
+ const report = (_a = options.onGiveUp) !== null && _a !== void 0 ? _a : ((info) => console.warn('[bounded] app token exchange gave up', info));
9687
+ try {
9688
+ report(giveUp);
9689
+ }
9690
+ catch (_b) {
9691
+ /* a reporting fault must not replace the exchange failure */
9692
+ }
9693
+ return new AppTokenExchangeError(giveUp);
9694
+ }
9695
+ async function performExchange(options) {
9696
+ var _a, _b;
9697
+ const attemptCeiling = Math.max(1, Math.min(MAX_ATTEMPTS_CEILING, (_a = options.maxAttempts) !== null && _a !== void 0 ? _a : DEFAULT_MAX_ATTEMPTS));
9698
+ const doFetch = (_b = options.fetchImpl) !== null && _b !== void 0 ? _b : fetch;
9699
+ let retryDelay = FIRST_RETRY_DELAY_MS;
9700
+ let lastStatus = null;
9701
+ let lastReason = 'unknown';
9702
+ for (let attempt = 1; attempt <= attemptCeiling; attempt++) {
9703
+ let status = null;
9704
+ let reason = 'transport_failed';
9705
+ try {
9706
+ const response = await doFetch(exchangeUrl(options.authApiUrl), {
9707
+ method: 'POST',
9708
+ headers: { 'Content-Type': 'application/json' },
9709
+ body: JSON.stringify({ idToken: options.parentToken, appId: options.appId }),
9710
+ });
9711
+ status = response.status;
9712
+ const body = await response.json().catch(() => ({}));
9713
+ if (response.ok) {
9714
+ const token = typeof body.idToken === 'string' ? body.idToken : '';
9715
+ if (!token) {
9716
+ throw recordGiveUp(options, { appId: options.appId, status, reason: 'no_token_in_response', attempts: attempt });
9717
+ }
9718
+ const nowMs = Date.now();
9719
+ const expiresAtMs = expiryFromResponse(body, token, nowMs);
9720
+ if (expiresAtMs === null) {
9721
+ throw recordGiveUp(options, { appId: options.appId, status, reason: 'expired_token_in_response', attempts: attempt });
9722
+ }
9723
+ return { token, expiresAtMs, appId: options.appId };
9724
+ }
9725
+ reason = typeof body.error === 'string' && body.error ? body.error : `http_${status}`;
9726
+ }
9727
+ catch (error) {
9728
+ // A give-up raised above is already final.
9729
+ if (error instanceof AppTokenExchangeError)
9730
+ throw error;
9731
+ status = null;
9732
+ reason = 'transport_failed';
9733
+ }
9734
+ lastStatus = status;
9735
+ lastReason = reason;
9736
+ // A 429 is the issuer telling us to stop. It stops the schedule; it never
9737
+ // feeds it. Every other definitive refusal stops too.
9738
+ if (!retriable(status) || attempt === attemptCeiling) {
9739
+ throw recordGiveUp(options, { appId: options.appId, status, reason, attempts: attempt });
9740
+ }
9741
+ await delay(retryDelay);
9742
+ retryDelay = Math.min(MAX_RETRY_DELAY_MS, retryDelay * 2);
9743
+ }
9744
+ // Unreachable: the loop always returns or throws.
9745
+ throw recordGiveUp(options, { appId: options.appId, status: lastStatus, reason: lastReason, attempts: attemptCeiling });
9746
+ }
9747
+ /**
9748
+ * Get a child-app id token for the human the parent token belongs to, from cache
9749
+ * when one is still comfortably fresh and otherwise by exchanging once.
9750
+ *
9751
+ * Concurrent callers for the same (parent credential, appId) share one exchange.
9752
+ * On give-up this rejects with {@link AppTokenExchangeError} and caches nothing.
9753
+ */
9754
+ async function exchangeAppToken(options) {
9755
+ if (!(options === null || options === void 0 ? void 0 : options.authApiUrl))
9756
+ throw new Error('exchangeAppToken requires an authApiUrl.');
9757
+ if (!(options === null || options === void 0 ? void 0 : options.appId))
9758
+ throw new Error('exchangeAppToken requires an appId.');
9759
+ if (!(options === null || options === void 0 ? void 0 : options.parentToken))
9760
+ throw new Error('exchangeAppToken requires a parentToken.');
9761
+ const key = cacheKey(options.appId, options.parentToken);
9762
+ pruneExpired(Date.now());
9763
+ const cached = cache.get(key);
9764
+ if (usable(cached, Date.now()))
9765
+ return cached;
9766
+ // Past the refresh skew the entry is no longer served; drop it so a failed
9767
+ // refresh can never fall back to a token that is about to expire mid-request.
9768
+ if (cached)
9769
+ cache.delete(key);
9770
+ const active = inFlight.get(key);
9771
+ if (active)
9772
+ return active;
9773
+ const flight = performExchange(options).then((token) => {
9774
+ cache.set(key, token);
9775
+ return token;
9776
+ }).finally(() => {
9777
+ if (inFlight.get(key) === flight)
9778
+ inFlight.delete(key);
9779
+ });
9780
+ inFlight.set(key, flight);
9781
+ return flight;
9782
+ }
9783
+ /**
9784
+ * Drop cached child-app tokens. Call this on sign-out; an identity change needs
9785
+ * no call, because the cache key is the exact parent credential.
9786
+ *
9787
+ * With no argument every entry goes. `appId` narrows to one child app, and
9788
+ * `parentToken` narrows to one session's entries.
9789
+ */
9790
+ function clearAppTokenCache(scope) {
9791
+ if (!scope || (!scope.appId && !scope.parentToken)) {
9792
+ cache.clear();
9793
+ inFlight.clear();
9794
+ return;
9795
+ }
9796
+ if (scope.appId && scope.parentToken) {
9797
+ const key = cacheKey(scope.appId, scope.parentToken);
9798
+ cache.delete(key);
9799
+ inFlight.delete(key);
9800
+ return;
9801
+ }
9802
+ const prefix = scope.appId ? `${scope.appId}:` : null;
9803
+ const suffix = scope.parentToken ? `:${fingerprint(scope.parentToken)}` : null;
9804
+ for (const key of Array.from(cache.keys())) {
9805
+ if ((prefix && key.startsWith(prefix)) || (suffix && key.endsWith(suffix)))
9806
+ cache.delete(key);
9807
+ }
9808
+ for (const key of Array.from(inFlight.keys())) {
9809
+ if ((prefix && key.startsWith(prefix)) || (suffix && key.endsWith(suffix)))
9810
+ inFlight.delete(key);
9811
+ }
9812
+ }
9813
+ /**
9814
+ * Build the `getAuthHeaders` a scoped `createClient({ appId, getAuthHeaders })`
9815
+ * instance authenticates with, backed by {@link exchangeAppToken}.
9816
+ *
9817
+ * Signed out (`parentToken()` yields nothing) returns NO headers, which leaves
9818
+ * the scoped instance anonymous - the public read lane - instead of failing. A
9819
+ * give-up on the exchange itself rejects, so a broken exchange surfaces as an
9820
+ * error rather than as silently unauthenticated reads.
9821
+ */
9822
+ function createAppTokenAuthHeaders(options) {
9823
+ return async () => {
9824
+ const parentToken = await options.parentToken();
9825
+ if (!parentToken)
9826
+ return {};
9827
+ const minted = await exchangeAppToken(Object.assign(Object.assign({}, options), { parentToken }));
9828
+ return { Authorization: `Bearer ${minted.token}` };
9829
+ };
9830
+ }
9831
+
9203
9832
  /**
9204
9833
  * Live named-query subscriptions (subscribeQuery) - HTTP client poller (v1).
9205
9834
  *
@@ -9902,7 +10531,7 @@ function toMillis(seconds) {
9902
10531
 
9903
10532
  /** SIWS message creation is async already, so its onchain module can be lazy. */
9904
10533
  async function genSolanaMessage(address, nonce) {
9905
- const implementation = await import('./solana-message-BGkYFU3C.mjs');
10534
+ const implementation = await import('./solana-message-C_717vAx.mjs');
9906
10535
  return implementation.genSolanaMessage(address, nonce);
9907
10536
  }
9908
10537
 
@@ -10451,5 +11080,5 @@ function defineLiveModule(mod) {
10451
11080
  return mod;
10452
11081
  }
10453
11082
 
10454
- export { getIdToken as $, subscribe as A, closeAllSubscriptions as B, clearCache as C, getCachedData as D, reconnectWithNewAuth as E, createClient as F, ScopedClientReadOnlyError as G, subscribeQuery as H, InsufficientBalanceError as I, closeAllQuerySubscriptions as J, canonicalizeArgs as K, canonicalizeResult as L, DEFAULT_QUERY_POLL_INTERVAL_MS as M, MIN_QUERY_POLL_INTERVAL_MS as N, BoundedDeclineError as O, deriveDecline as P, dispatchDeclineEvent as Q, DECLINE_EVENT_NAME as R, SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION as S, dispatchRateLimitEvent as T, RATE_LIMIT_EVENT_NAME as U, hasActiveConnection as V, increment as W, serverTimestamp as X, now as Y, toSeconds as Z, toMillis as _, getTransactionJson as a, deriveUserIdentityFromIdToken as a0, getSessionGeneration as a1, WebSessionManager as a2, ReactNativeSessionManager as a3, getActiveSessionManager as a4, onSessionCleared as a5, SessionEnvironmentError as a6, UNSUPPORTED_ENVIRONMENT_CODE as a7, AUTH_CHANGED_CODE as a8, AUTH_EXPIRED_CODE as a9, withEffects as aA, isEffectResult as aB, defineLiveModule as aC, EFFECT_INTENT_ADDRESS as aD, isAuthExpiredError as aa, getFreshAuthToken as ab, ServerSessionManager as ac, createSessionWithSignature as ad, refreshSession as ae, revokeSession as af, signSessionCreateMessage as ag, genAuthNonce as ah, genSolanaMessage as ai, createSolanaSignInRequest as aj, createSolanaSignInMessageText as ak, genSolanaSignInRequest as al, genSolanaSignInMessage as am, browserSignInDomain as an, SOLANA_SIGN_IN_STATEMENT as ao, genSiweMessage as ap, normalizeEvmAddress as aq, toLowerEvmAddress as ar, functions as as, invoke as at, FunctionInvokeError as au, live as av, intent as aw, status as ax, subscribeView as ay, LiveIntentError as az, bufferExports as b, getWebhookKeysUrl as c, getConfiguredAppId as d, get as e, getMany as f, getConfig as g, docId as h, init as i, setMany as j, setFile as k, getFiles as l, search as m, runQueryMany as n, runExpression as o, runExpressionMany as p, queryAggregate as q, runQuery as r, set as s, signMessage as t, signTransaction as u, signAndSubmitTransaction as v, count as w, aggregate as x, reconcileSetResult as y, waitForSetResult as z };
10455
- //# sourceMappingURL=index-DW5yMQwV.mjs.map
11083
+ export { serverTimestamp as $, subscribe as A, closeAllSubscriptions as B, clearCache as C, getCachedData as D, reconnectWithNewAuth as E, createClient as F, ScopedClientReadOnlyError as G, exchangeAppToken as H, InsufficientBalanceError as I, clearAppTokenCache as J, createAppTokenAuthHeaders as K, AppTokenExchangeError as L, subscribeQuery as M, closeAllQuerySubscriptions as N, canonicalizeArgs as O, canonicalizeResult as P, DEFAULT_QUERY_POLL_INTERVAL_MS as Q, MIN_QUERY_POLL_INTERVAL_MS as R, SOLANA_MAX_SUPPORTED_TRANSACTION_VERSION as S, BoundedDeclineError as T, deriveDecline as U, dispatchDeclineEvent as V, DECLINE_EVENT_NAME as W, dispatchRateLimitEvent as X, RATE_LIMIT_EVENT_NAME as Y, hasActiveConnection as Z, increment as _, getTransactionJson as a, now as a0, toSeconds as a1, toMillis as a2, getIdToken as a3, deriveUserIdentityFromIdToken as a4, getSessionGeneration as a5, WebSessionManager as a6, ReactNativeSessionManager as a7, getActiveSessionManager as a8, onSessionCleared as a9, intent as aA, status as aB, subscribeView as aC, LiveIntentError as aD, withEffects as aE, isEffectResult as aF, defineLiveModule as aG, EFFECT_INTENT_ADDRESS as aH, SessionEnvironmentError as aa, UNSUPPORTED_ENVIRONMENT_CODE as ab, AUTH_CHANGED_CODE as ac, AUTH_EXPIRED_CODE as ad, isAuthExpiredError as ae, getFreshAuthToken as af, ServerSessionManager as ag, createSessionWithSignature as ah, refreshSession as ai, revokeSession as aj, signSessionCreateMessage as ak, genAuthNonce as al, genSolanaMessage as am, createSolanaSignInRequest as an, createSolanaSignInMessageText as ao, genSolanaSignInRequest as ap, genSolanaSignInMessage as aq, browserSignInDomain as ar, SOLANA_SIGN_IN_STATEMENT as as, genSiweMessage as at, normalizeEvmAddress as au, toLowerEvmAddress as av, functions as aw, invoke as ax, FunctionInvokeError as ay, live as az, bufferExports as b, getWebhookKeysUrl as c, getConfiguredAppId as d, get as e, getMany as f, getConfig as g, docId as h, init as i, setMany as j, setFile as k, getFiles as l, search as m, runQueryMany as n, runExpression as o, runExpressionMany as p, queryAggregate as q, runQuery as r, set as s, signMessage as t, signTransaction as u, signAndSubmitTransaction as v, count as w, aggregate as x, reconcileSetResult as y, waitForSetResult as z };
11084
+ //# sourceMappingURL=index-CMT0saxy.mjs.map