@bounded-sh/core 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/live.d.ts +3 -0
- package/dist/client/realtime-store.d.ts +8 -0
- package/dist/index.js +304 -192
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +304 -192
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3800,24 +3800,19 @@ async function buildSetDocumentsTransaction(connection, idl, anchorProvider, pay
|
|
|
3800
3800
|
/* ------------------------------------------------------------------ */
|
|
3801
3801
|
/* ENV helpers */
|
|
3802
3802
|
/* ------------------------------------------------------------------ */
|
|
3803
|
-
// Canonical `BOUNDED_PRIVATE_KEY` (matches the CLI)
|
|
3804
|
-
//
|
|
3805
|
-
|
|
3806
|
-
const
|
|
3803
|
+
// Canonical `BOUNDED_PRIVATE_KEY` (matches the CLI). Only consulted when no
|
|
3804
|
+
// explicit keypair was provided (createWalletClient passes one).
|
|
3805
|
+
const ENV_KEYPAIR = "BOUNDED_PRIVATE_KEY";
|
|
3806
|
+
const LEGACY_ENV_KEYPAIR = "BOUNDED_SOLANA_KEYPAIR";
|
|
3807
3807
|
function loadKeypairFromEnv() {
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
const v = process.env[name];
|
|
3812
|
-
if (v) {
|
|
3813
|
-
secret = v;
|
|
3814
|
-
found = name;
|
|
3815
|
-
break;
|
|
3816
|
-
}
|
|
3808
|
+
if (process.env[LEGACY_ENV_KEYPAIR]) {
|
|
3809
|
+
throw new Error(`${LEGACY_ENV_KEYPAIR} is no longer supported. Set ${ENV_KEYPAIR} instead, ` +
|
|
3810
|
+
`or pass an explicit keypair via createWalletClient({ keypair }).`);
|
|
3817
3811
|
}
|
|
3812
|
+
const secret = process.env[ENV_KEYPAIR];
|
|
3818
3813
|
if (!secret) {
|
|
3819
3814
|
throw new Error(`No server keypair for this top-level call. The top-level get/set/subscribe/etc. use an ` +
|
|
3820
|
-
`AMBIENT session — set ${
|
|
3815
|
+
`AMBIENT session — set ${ENV_KEYPAIR} to a base-58 secret key (or JSON array) to provide one. ` +
|
|
3821
3816
|
`If you already created a wallet with createWalletClient({ keypair }), call ITS methods instead ` +
|
|
3822
3817
|
`(client.subscribe / client.set / client.get): that client is self-contained and deliberately does ` +
|
|
3823
3818
|
`not set the ambient session, so the top-level functions can't see it.`);
|
|
@@ -3829,7 +3824,7 @@ function loadKeypairFromEnv() {
|
|
|
3829
3824
|
return web3_js.Keypair.fromSecretKey(secretKey);
|
|
3830
3825
|
}
|
|
3831
3826
|
catch (err) {
|
|
3832
|
-
throw new Error(`Unable to parse ${
|
|
3827
|
+
throw new Error(`Unable to parse ${ENV_KEYPAIR}. Ensure it is valid base-58 or JSON.`);
|
|
3833
3828
|
}
|
|
3834
3829
|
}
|
|
3835
3830
|
/* ------------------------------------------------------------------ */
|
|
@@ -3896,7 +3891,7 @@ class ServerSessionManager {
|
|
|
3896
3891
|
this.session = session;
|
|
3897
3892
|
}
|
|
3898
3893
|
/* ---------------------------------------------- *
|
|
3899
|
-
* CLEAR (e.g. after logout
|
|
3894
|
+
* CLEAR (e.g. after explicit logout)
|
|
3900
3895
|
* ---------------------------------------------- */
|
|
3901
3896
|
clearSession() {
|
|
3902
3897
|
this.session = null;
|
|
@@ -3920,11 +3915,6 @@ class ServerSessionManager {
|
|
|
3920
3915
|
/* The default singleton instance (reads keypair from env) */
|
|
3921
3916
|
ServerSessionManager.instance = new ServerSessionManager();
|
|
3922
3917
|
|
|
3923
|
-
var serverSessionManager = /*#__PURE__*/Object.freeze({
|
|
3924
|
-
__proto__: null,
|
|
3925
|
-
ServerSessionManager: ServerSessionManager
|
|
3926
|
-
});
|
|
3927
|
-
|
|
3928
3918
|
/**
|
|
3929
3919
|
* Safe base64 helpers for bounded-core.
|
|
3930
3920
|
*
|
|
@@ -4149,13 +4139,6 @@ async function refreshAuthSessionOnce(appId, isServer) {
|
|
|
4149
4139
|
async function makeApiRequest(method, urlPath, data, _overrides) {
|
|
4150
4140
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
4151
4141
|
const config = await getConfig();
|
|
4152
|
-
let hasRetriedAfterServerSessionReset = false;
|
|
4153
|
-
const clearServerSession = async () => {
|
|
4154
|
-
if (!config.isServer)
|
|
4155
|
-
return;
|
|
4156
|
-
const { ServerSessionManager } = await Promise.resolve().then(function () { return serverSessionManager; });
|
|
4157
|
-
ServerSessionManager.instance.clearSession();
|
|
4158
|
-
};
|
|
4159
4142
|
async function executeRequest() {
|
|
4160
4143
|
var _a;
|
|
4161
4144
|
// When _getAuthHeaders is provided (wallet client), use it as the sole auth source.
|
|
@@ -4205,13 +4188,7 @@ async function makeApiRequest(method, urlPath, data, _overrides) {
|
|
|
4205
4188
|
}
|
|
4206
4189
|
return await executeRequest();
|
|
4207
4190
|
}
|
|
4208
|
-
catch (
|
|
4209
|
-
// Server-side fallback: clear global session and retry once
|
|
4210
|
-
if (config.isServer && !hasRetriedAfterServerSessionReset) {
|
|
4211
|
-
hasRetriedAfterServerSessionReset = true;
|
|
4212
|
-
await clearServerSession();
|
|
4213
|
-
return await executeRequest();
|
|
4214
|
-
}
|
|
4191
|
+
catch (_j) {
|
|
4215
4192
|
throw error;
|
|
4216
4193
|
}
|
|
4217
4194
|
}
|
|
@@ -4358,99 +4335,54 @@ function normalizeReadResult(responseData, pathIsDocument) {
|
|
|
4358
4335
|
}
|
|
4359
4336
|
return responseData;
|
|
4360
4337
|
}
|
|
4361
|
-
function hashForKey$
|
|
4338
|
+
function hashForKey$2(value) {
|
|
4362
4339
|
let h = 5381;
|
|
4363
4340
|
for (let i = 0; i < value.length; i++) {
|
|
4364
4341
|
h = ((h << 5) + h + value.charCodeAt(i)) & 0x7fffffff;
|
|
4365
4342
|
}
|
|
4366
4343
|
return h.toString(36);
|
|
4367
4344
|
}
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4345
|
+
function authValueFromHeaders(headers) {
|
|
4346
|
+
return (headers === null || headers === void 0 ? void 0 : headers.Authorization) || (headers === null || headers === void 0 ? void 0 : headers.authorization) || '';
|
|
4347
|
+
}
|
|
4348
|
+
function principalFromAuthValue(authValue) {
|
|
4349
|
+
return authValue ? `h${hashForKey$2(authValue)}` : 'anon';
|
|
4350
|
+
}
|
|
4374
4351
|
function principalFromIdToken$1(idToken) {
|
|
4375
|
-
|
|
4376
|
-
return 'anon';
|
|
4377
|
-
try {
|
|
4378
|
-
const parts = idToken.split('.');
|
|
4379
|
-
if (parts.length < 2)
|
|
4380
|
-
return `t${hashForKey$1(idToken)}`;
|
|
4381
|
-
const payload = JSON.parse(decodeBase64Url(parts[1]));
|
|
4382
|
-
const subject =
|
|
4383
|
-
// Prefer the universal identity (@user.id) — it is the stable principal a
|
|
4384
|
-
// request presents (equals the wallet for wallet logins; the account id for
|
|
4385
|
-
// email/social logins, which carry no walletAddress). Keying the read cache
|
|
4386
|
-
// on it keeps an email user's private snapshot scoped to that user.
|
|
4387
|
-
payload['custom:userId'] ||
|
|
4388
|
-
payload['custom:walletAddress'] ||
|
|
4389
|
-
payload.walletAddress ||
|
|
4390
|
-
payload.sub ||
|
|
4391
|
-
payload.address;
|
|
4392
|
-
if (subject)
|
|
4393
|
-
return `s${hashForKey$1(String(subject))}`;
|
|
4394
|
-
return `t${hashForKey$1(idToken)}`;
|
|
4395
|
-
}
|
|
4396
|
-
catch (_a) {
|
|
4397
|
-
return `t${hashForKey$1(idToken)}`;
|
|
4398
|
-
}
|
|
4352
|
+
return idToken ? `t${hashForKey$2(idToken)}` : 'anon';
|
|
4399
4353
|
}
|
|
4400
4354
|
/**
|
|
4401
4355
|
* SECURITY (H1): Read caches must be keyed by the caller's principal, not just
|
|
4402
4356
|
* by path/filter/shape. In a shared process / SSR worker / browser login-switch,
|
|
4403
4357
|
* keying by path alone lets User B receive User A's cached private read before
|
|
4404
4358
|
* any server read rule runs. This returns `appId:<principal>` for the identity a
|
|
4405
|
-
* given read will actually authenticate as
|
|
4406
|
-
*
|
|
4407
|
-
*
|
|
4408
|
-
*
|
|
4409
|
-
* - otherwise the ambient logged-in session's idToken subject (or `anon`)
|
|
4359
|
+
* given read will actually authenticate as. This intentionally treats JWTs as
|
|
4360
|
+
* opaque unverified bearer material — never decoded claims and never caller
|
|
4361
|
+
* identity hints such as `_walletAddress`. A forged token that merely claims
|
|
4362
|
+
* another user's `sub` must miss that user's cache and go to the server.
|
|
4410
4363
|
*/
|
|
4411
4364
|
async function getReadPrincipalKey(overrides) {
|
|
4412
|
-
var _a, _b;
|
|
4413
4365
|
const config = await getConfig();
|
|
4414
4366
|
const appId = config.appId || '';
|
|
4415
|
-
//
|
|
4416
|
-
|
|
4417
|
-
|
|
4367
|
+
// makeApiRequest applies overrides.headers AFTER its computed auth header, so
|
|
4368
|
+
// caller-supplied Authorization is the real request auth when present.
|
|
4369
|
+
const directAuth = authValueFromHeaders(overrides === null || overrides === void 0 ? void 0 : overrides.headers);
|
|
4370
|
+
if (directAuth) {
|
|
4371
|
+
return `${appId}:${principalFromAuthValue(directAuth)}`;
|
|
4418
4372
|
}
|
|
4419
|
-
// Per-request auth-header override (wallet client). Key by the
|
|
4420
|
-
// it produces
|
|
4373
|
+
// Per-request auth-header override (wallet client). Key by the exact opaque
|
|
4374
|
+
// header it produces, not decoded claims or the unverified _walletAddress hint.
|
|
4421
4375
|
if (overrides === null || overrides === void 0 ? void 0 : overrides._getAuthHeaders) {
|
|
4422
4376
|
try {
|
|
4423
4377
|
const headers = await overrides._getAuthHeaders();
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
// Decode the bearer token's subject when present for a stable key across
|
|
4428
|
-
// refreshes; otherwise hash whatever header material we were given.
|
|
4429
|
-
const bearer = authValue.startsWith('Bearer ')
|
|
4430
|
-
? authValue.slice('Bearer '.length)
|
|
4431
|
-
: authValue;
|
|
4432
|
-
const principal = bearer ? principalFromIdToken$1(bearer) : 'anon';
|
|
4433
|
-
return `${appId}:o${principal}`;
|
|
4434
|
-
}
|
|
4435
|
-
catch (_c) {
|
|
4378
|
+
return `${appId}:o${principalFromAuthValue(authValueFromHeaders(headers))}`;
|
|
4379
|
+
}
|
|
4380
|
+
catch (_a) {
|
|
4436
4381
|
// If we can't resolve the override identity, use a unique-ish key so we
|
|
4437
4382
|
// never collide with (and serve) another principal's cached entry.
|
|
4438
|
-
return `${appId}:o${hashForKey$
|
|
4383
|
+
return `${appId}:o${hashForKey$2(String(Date.now()) + Math.random())}`;
|
|
4439
4384
|
}
|
|
4440
4385
|
}
|
|
4441
|
-
// Direct per-request header override. makeApiRequest applies overrides.headers
|
|
4442
|
-
// AFTER its computed auth header (api.ts), so a caller-supplied Authorization
|
|
4443
|
-
// is the REAL request auth — key the cache by it so an entry is never served
|
|
4444
|
-
// to a different principal under the ambient key. (Hardening: no in-repo read
|
|
4445
|
-
// caller passes this today, but the cache must never trail the actual auth.)
|
|
4446
|
-
const directAuth = ((_a = overrides === null || overrides === void 0 ? void 0 : overrides.headers) === null || _a === void 0 ? void 0 : _a.Authorization) ||
|
|
4447
|
-
((_b = overrides === null || overrides === void 0 ? void 0 : overrides.headers) === null || _b === void 0 ? void 0 : _b.authorization);
|
|
4448
|
-
if (directAuth) {
|
|
4449
|
-
const bearer = directAuth.startsWith('Bearer ')
|
|
4450
|
-
? directAuth.slice('Bearer '.length)
|
|
4451
|
-
: directAuth;
|
|
4452
|
-
return `${appId}:h${bearer ? principalFromIdToken$1(bearer) : hashForKey$1(directAuth)}`;
|
|
4453
|
-
}
|
|
4454
4386
|
// Ambient session principal.
|
|
4455
4387
|
const idToken = await getIdToken(config.isServer);
|
|
4456
4388
|
return `${appId}:${principalFromIdToken$1(idToken)}`;
|
|
@@ -4750,9 +4682,9 @@ async function get(path, opts = {}) {
|
|
|
4750
4682
|
const shapeKey = opts.shape ? JSON.stringify(opts.shape) : '';
|
|
4751
4683
|
const includeSubPathsKey = opts.includeSubPaths ? ':subpaths' : '';
|
|
4752
4684
|
const limitKey = opts.limit !== undefined ? `:l${opts.limit}` : '';
|
|
4753
|
-
const cursorKey = opts.cursor ? `:c${hashForKey$
|
|
4754
|
-
const filterKey = opts.filter ? `:f${hashForKey$
|
|
4755
|
-
const sortKey = opts.sort ? `:s${hashForKey$
|
|
4685
|
+
const cursorKey = opts.cursor ? `:c${hashForKey$2(opts.cursor)}` : '';
|
|
4686
|
+
const filterKey = opts.filter ? `:f${hashForKey$2(JSON.stringify(opts.filter))}` : '';
|
|
4687
|
+
const sortKey = opts.sort ? `:s${hashForKey$2(JSON.stringify(opts.sort))}` : '';
|
|
4756
4688
|
const principalKey = await getReadPrincipalKey(opts._overrides);
|
|
4757
4689
|
const cacheKey = `${principalKey}|${normalizedPath}:${opts.prompt || ''}${filterKey}${sortKey}${includeSubPathsKey}:${shapeKey}${limitKey}${cursorKey}`;
|
|
4758
4690
|
const now = Date.now();
|
|
@@ -5502,7 +5434,7 @@ let reconnectInProgress = null;
|
|
|
5502
5434
|
function generateSubscriptionId() {
|
|
5503
5435
|
return `sub_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
5504
5436
|
}
|
|
5505
|
-
function hashForKey(value) {
|
|
5437
|
+
function hashForKey$1(value) {
|
|
5506
5438
|
let h = 5381;
|
|
5507
5439
|
for (let i = 0; i < value.length; i++) {
|
|
5508
5440
|
h = ((h << 5) + h + value.charCodeAt(i)) & 0x7fffffff;
|
|
@@ -5521,49 +5453,23 @@ function getCacheKey(path, prompt, shape, limit, cursor, filter, identity) {
|
|
|
5521
5453
|
const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
|
|
5522
5454
|
const shapeKey = shape && Object.keys(shape).length > 0 ? JSON.stringify(shape) : '';
|
|
5523
5455
|
const limitKey = limit !== undefined ? `:l${limit}` : '';
|
|
5524
|
-
const cursorKey = cursor ? `:c${hashForKey(cursor)}` : '';
|
|
5525
|
-
const filterKey = filter && Object.keys(filter).length > 0 ? `:f${hashForKey(JSON.stringify(filter))}` : '';
|
|
5456
|
+
const cursorKey = cursor ? `:c${hashForKey$1(cursor)}` : '';
|
|
5457
|
+
const filterKey = filter && Object.keys(filter).length > 0 ? `:f${hashForKey$1(JSON.stringify(filter))}` : '';
|
|
5526
5458
|
const identityKey = identity || 'anon';
|
|
5527
5459
|
return `${identityKey}|${normalizedPath}:${prompt || 'default'}:${shapeKey}${limitKey}${cursorKey}${filterKey}`;
|
|
5528
5460
|
}
|
|
5529
5461
|
/**
|
|
5530
|
-
* Derive
|
|
5531
|
-
*
|
|
5532
|
-
*
|
|
5462
|
+
* Derive an opaque identity string for the bearer material a subscription sends.
|
|
5463
|
+
* JWT payloads are deliberately not trusted here: cache isolation must happen
|
|
5464
|
+
* before the server has verified any claims.
|
|
5533
5465
|
*/
|
|
5534
5466
|
function principalFromIdToken(idToken) {
|
|
5535
|
-
|
|
5536
|
-
return 'anon';
|
|
5537
|
-
try {
|
|
5538
|
-
const parts = idToken.split('.');
|
|
5539
|
-
if (parts.length < 2)
|
|
5540
|
-
return `t${hashForKey(idToken)}`;
|
|
5541
|
-
const payload = JSON.parse(decodeBase64Url(parts[1]));
|
|
5542
|
-
const subject =
|
|
5543
|
-
// Universal identity (@user.id) first — the stable principal a request
|
|
5544
|
-
// presents (the account id for email/social logins, which carry no
|
|
5545
|
-
// walletAddress). Keeps the H1 response-cache scoping correct for them.
|
|
5546
|
-
payload['custom:userId'] ||
|
|
5547
|
-
payload['custom:walletAddress'] ||
|
|
5548
|
-
payload.walletAddress ||
|
|
5549
|
-
payload.sub ||
|
|
5550
|
-
payload.address;
|
|
5551
|
-
if (subject)
|
|
5552
|
-
return `s${hashForKey(String(subject))}`;
|
|
5553
|
-
return `t${hashForKey(idToken)}`;
|
|
5554
|
-
}
|
|
5555
|
-
catch (_a) {
|
|
5556
|
-
return `t${hashForKey(idToken)}`;
|
|
5557
|
-
}
|
|
5467
|
+
return idToken ? `t${hashForKey$1(idToken)}` : 'anon';
|
|
5558
5468
|
}
|
|
5559
5469
|
async function getSubscriptionIdentity(effectiveAppId, isServer, overrides) {
|
|
5560
|
-
// Per-subscription wallet override (server WalletClient.subscribe): key by
|
|
5561
|
-
// wallet's own token, mirroring getReadPrincipalKey
|
|
5562
|
-
//
|
|
5563
|
-
// cached snapshots are never crossed with another principal's.
|
|
5564
|
-
if (overrides === null || overrides === void 0 ? void 0 : overrides._walletAddress) {
|
|
5565
|
-
return `${effectiveAppId}:w${overrides._walletAddress}`;
|
|
5566
|
-
}
|
|
5470
|
+
// Per-subscription wallet override (server WalletClient.subscribe): key by
|
|
5471
|
+
// the wallet's own opaque token material, mirroring getReadPrincipalKey. Do
|
|
5472
|
+
// not trust decoded claims or the unverified _walletAddress hint.
|
|
5567
5473
|
if (overrides === null || overrides === void 0 ? void 0 : overrides._getAuthHeaders) {
|
|
5568
5474
|
try {
|
|
5569
5475
|
const bearer = bearerFromAuthHeaders(await overrides._getAuthHeaders());
|
|
@@ -5934,7 +5840,7 @@ async function getOrCreateConnection(appId, isServer, routePath, authTokenProvid
|
|
|
5934
5840
|
return connection;
|
|
5935
5841
|
}
|
|
5936
5842
|
function handleServerMessage(connection, message) {
|
|
5937
|
-
var _a
|
|
5843
|
+
var _a;
|
|
5938
5844
|
switch (message.type) {
|
|
5939
5845
|
case 'authenticated': {
|
|
5940
5846
|
connection.isAuthenticating = false;
|
|
@@ -6028,28 +5934,27 @@ function handleServerMessage(connection, message) {
|
|
|
6028
5934
|
err.subscriptionId = message.subscriptionId;
|
|
6029
5935
|
return err;
|
|
6030
5936
|
};
|
|
5937
|
+
const wsError = buildWsError();
|
|
6031
5938
|
// Handle CRUD request errors (requestId present)
|
|
6032
5939
|
if (message.requestId) {
|
|
6033
5940
|
const pendingReq = connection.pendingRequests.get(message.requestId);
|
|
6034
5941
|
if (pendingReq) {
|
|
6035
5942
|
connection.pendingRequests.delete(message.requestId);
|
|
6036
5943
|
clearTimeout(pendingReq.timer);
|
|
6037
|
-
pendingReq.reject(
|
|
5944
|
+
pendingReq.reject(wsError);
|
|
6038
5945
|
}
|
|
6039
5946
|
}
|
|
6040
5947
|
if (message.subscriptionId) {
|
|
6041
5948
|
// Reject pending subscription if this is a subscription error
|
|
6042
5949
|
const pending = connection.pendingSubscriptions.get(message.subscriptionId);
|
|
6043
5950
|
if (pending) {
|
|
6044
|
-
pending.reject(
|
|
5951
|
+
pending.reject(wsError);
|
|
6045
5952
|
connection.pendingSubscriptions.delete(message.subscriptionId);
|
|
6046
5953
|
}
|
|
6047
5954
|
// Notify error callbacks for this subscription
|
|
6048
5955
|
const subscription = connection.subscriptions.get(message.subscriptionId);
|
|
6049
5956
|
if (subscription) {
|
|
6050
|
-
|
|
6051
|
-
(_b = callback.onError) === null || _b === void 0 ? void 0 : _b.call(callback, buildWsError());
|
|
6052
|
-
}
|
|
5957
|
+
notifyErrorCallbacks(subscription, wsError);
|
|
6053
5958
|
}
|
|
6054
5959
|
}
|
|
6055
5960
|
break;
|
|
@@ -6102,6 +6007,25 @@ function notifyCallbacks(subscription, data) {
|
|
|
6102
6007
|
}
|
|
6103
6008
|
}
|
|
6104
6009
|
}
|
|
6010
|
+
function notifyErrorCallbacks(subscription, error) {
|
|
6011
|
+
let delivered = false;
|
|
6012
|
+
const callbacks = subscription.callbacks.slice();
|
|
6013
|
+
for (const callback of callbacks) {
|
|
6014
|
+
if (!callback.onError)
|
|
6015
|
+
continue;
|
|
6016
|
+
delivered = true;
|
|
6017
|
+
try {
|
|
6018
|
+
callback.onError(error);
|
|
6019
|
+
}
|
|
6020
|
+
catch (callbackError) {
|
|
6021
|
+
console.error('[WS v2] Error in subscription error callback:', callbackError);
|
|
6022
|
+
}
|
|
6023
|
+
}
|
|
6024
|
+
if (delivered) {
|
|
6025
|
+
error.__boundedDeliveredToOnError = true;
|
|
6026
|
+
}
|
|
6027
|
+
return delivered;
|
|
6028
|
+
}
|
|
6105
6029
|
// WebSocket readyState constants
|
|
6106
6030
|
const WS_READY_STATE_OPEN = 1;
|
|
6107
6031
|
const WS_READY_STATE_CLOSED = 3;
|
|
@@ -6176,10 +6100,8 @@ async function subscribeV2(path, subscriptionOptions, roomRoutePath) {
|
|
|
6176
6100
|
const authTokenProvider = (overrides === null || overrides === void 0 ? void 0 : overrides._getAuthHeaders)
|
|
6177
6101
|
? async () => bearerFromAuthHeaders(await overrides._getAuthHeaders()) || null
|
|
6178
6102
|
: undefined;
|
|
6179
|
-
const principalKey = (overrides === null || overrides === void 0 ? void 0 : overrides._walletAddress)
|
|
6180
|
-
? `w${overrides._walletAddress}`
|
|
6181
|
-
: (authTokenProvider ? `o${principalFromIdToken(await authTokenProvider())}` : undefined);
|
|
6182
6103
|
const identity = await getSubscriptionIdentity(effectiveAppId, config.isServer, overrides);
|
|
6104
|
+
const principalKey = authTokenProvider ? identity : undefined;
|
|
6183
6105
|
const cacheKey = getCacheKey(normalizedPath, subscriptionOptions.prompt, subscriptionOptions.shape, subscriptionOptions.limit, subscriptionOptions.cursor, subscriptionOptions.filter, identity);
|
|
6184
6106
|
// Deliver cached data immediately if available
|
|
6185
6107
|
const cachedEntry = responseCache.get(cacheKey);
|
|
@@ -6258,7 +6180,18 @@ async function subscribeV2(path, subscriptionOptions, roomRoutePath) {
|
|
|
6258
6180
|
await subscriptionPromise;
|
|
6259
6181
|
}
|
|
6260
6182
|
catch (error) {
|
|
6261
|
-
|
|
6183
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
6184
|
+
if (!err.__boundedDeliveredToOnError) {
|
|
6185
|
+
notifyErrorCallbacks(subscription, err);
|
|
6186
|
+
}
|
|
6187
|
+
if (!subscriptionOptions.onError) {
|
|
6188
|
+
connection.pendingSubscriptions.delete(subscriptionId);
|
|
6189
|
+
connection.subscriptions.delete(subscriptionId);
|
|
6190
|
+
throw err;
|
|
6191
|
+
}
|
|
6192
|
+
if (!err.__boundedDeliveredToOnError) {
|
|
6193
|
+
console.warn('[WS v2] Subscription confirmation failed, keeping for reconnect recovery:', error);
|
|
6194
|
+
}
|
|
6262
6195
|
}
|
|
6263
6196
|
}
|
|
6264
6197
|
// Return unsubscribe function
|
|
@@ -6421,6 +6354,13 @@ async function doReconnectWithNewAuth() {
|
|
|
6421
6354
|
catch (error) {
|
|
6422
6355
|
console.warn('[WS v2] Failed to clear HTTP read cache on auth change:', error);
|
|
6423
6356
|
}
|
|
6357
|
+
try {
|
|
6358
|
+
const { reconnectRealtimeStoreWithNewAuth } = await Promise.resolve().then(function () { return realtimeStore; });
|
|
6359
|
+
await reconnectRealtimeStoreWithNewAuth();
|
|
6360
|
+
}
|
|
6361
|
+
catch (error) {
|
|
6362
|
+
console.warn('[WS v2] Failed to reset legacy realtime store on auth change:', error);
|
|
6363
|
+
}
|
|
6424
6364
|
for (const [appId, connection] of connections) {
|
|
6425
6365
|
if (!connection.ws) {
|
|
6426
6366
|
continue;
|
|
@@ -6888,6 +6828,16 @@ async function idbSet(key, value) {
|
|
|
6888
6828
|
// RealtimeStore
|
|
6889
6829
|
// ---------------------------------------------------------------------------
|
|
6890
6830
|
let nextRequestId = 1;
|
|
6831
|
+
function hashForKey(value) {
|
|
6832
|
+
let h = 5381;
|
|
6833
|
+
for (let i = 0; i < value.length; i++) {
|
|
6834
|
+
h = ((h << 5) + h + value.charCodeAt(i)) & 0x7fffffff;
|
|
6835
|
+
}
|
|
6836
|
+
return h.toString(36);
|
|
6837
|
+
}
|
|
6838
|
+
function principalFromToken(token) {
|
|
6839
|
+
return token ? `t${hashForKey(token)}` : 'anon';
|
|
6840
|
+
}
|
|
6891
6841
|
class RealtimeStore {
|
|
6892
6842
|
constructor() {
|
|
6893
6843
|
this.ws = null;
|
|
@@ -6903,7 +6853,9 @@ class RealtimeStore {
|
|
|
6903
6853
|
this.idbDirtyKeys = new Set();
|
|
6904
6854
|
this.closed = false;
|
|
6905
6855
|
this.authToken = null;
|
|
6856
|
+
this.authPrincipalKey = 'anon';
|
|
6906
6857
|
this.authenticating = false;
|
|
6858
|
+
this.suppressNextReconnect = false;
|
|
6907
6859
|
this.isServer = false;
|
|
6908
6860
|
this.tokenRefreshTimer = null;
|
|
6909
6861
|
// -----------------------------------------------------------------------
|
|
@@ -6923,34 +6875,98 @@ class RealtimeStore {
|
|
|
6923
6875
|
this.startTokenRefresh();
|
|
6924
6876
|
}
|
|
6925
6877
|
async refreshToken() {
|
|
6878
|
+
let token = null;
|
|
6926
6879
|
try {
|
|
6927
6880
|
const { getIdToken } = await Promise.resolve().then(function () { return utils; });
|
|
6928
|
-
|
|
6929
|
-
if (token)
|
|
6930
|
-
this.authToken = token;
|
|
6881
|
+
token = await getIdToken(this.isServer);
|
|
6931
6882
|
}
|
|
6932
6883
|
catch ( /* no auth available */_a) { /* no auth available */ }
|
|
6884
|
+
this.authToken = token !== null && token !== void 0 ? token : null;
|
|
6885
|
+
this.authPrincipalKey = principalFromToken(this.authToken);
|
|
6933
6886
|
}
|
|
6934
6887
|
startTokenRefresh() {
|
|
6935
6888
|
if (this.tokenRefreshTimer)
|
|
6936
6889
|
return;
|
|
6937
6890
|
this.tokenRefreshTimer = setInterval(async () => {
|
|
6938
|
-
|
|
6939
|
-
const prevToken = this.authToken;
|
|
6891
|
+
const prevPrincipal = this.authPrincipalKey;
|
|
6940
6892
|
await this.refreshToken();
|
|
6941
|
-
if (this.
|
|
6942
|
-
|
|
6943
|
-
this.
|
|
6893
|
+
if (this.authPrincipalKey !== prevPrincipal) {
|
|
6894
|
+
await this.applyAuthPrincipalChange();
|
|
6895
|
+
if (this.subscriptions.size > 0) {
|
|
6896
|
+
await this.ensureConnected().catch(() => {
|
|
6897
|
+
this.setAllSubscriptionStatus('error');
|
|
6898
|
+
});
|
|
6899
|
+
}
|
|
6944
6900
|
}
|
|
6945
6901
|
}, 5 * 60 * 1000); // Check every 5 minutes
|
|
6946
6902
|
}
|
|
6903
|
+
async ensureInitialized() {
|
|
6904
|
+
if (this.appId)
|
|
6905
|
+
return;
|
|
6906
|
+
if (!this.initPromise)
|
|
6907
|
+
this.initPromise = this.init();
|
|
6908
|
+
await this.initPromise;
|
|
6909
|
+
}
|
|
6910
|
+
async ensureCurrentAuth() {
|
|
6911
|
+
await this.ensureInitialized();
|
|
6912
|
+
const prevPrincipal = this.authPrincipalKey;
|
|
6913
|
+
await this.refreshToken();
|
|
6914
|
+
if (this.authPrincipalKey !== prevPrincipal) {
|
|
6915
|
+
await this.applyAuthPrincipalChange();
|
|
6916
|
+
}
|
|
6917
|
+
}
|
|
6918
|
+
rekeySubscriptionsForPrincipal() {
|
|
6919
|
+
const subs = Array.from(this.subscriptions.values());
|
|
6920
|
+
this.subscriptions.clear();
|
|
6921
|
+
for (const sub of subs) {
|
|
6922
|
+
this.subscriptions.set(this.getSubKey(sub.path, sub.options), sub);
|
|
6923
|
+
}
|
|
6924
|
+
}
|
|
6925
|
+
async applyAuthPrincipalChange() {
|
|
6926
|
+
if (this.idbFlushTimer) {
|
|
6927
|
+
clearTimeout(this.idbFlushTimer);
|
|
6928
|
+
this.idbFlushTimer = null;
|
|
6929
|
+
}
|
|
6930
|
+
this.idbDirtyKeys.clear();
|
|
6931
|
+
this.rekeySubscriptionsForPrincipal();
|
|
6932
|
+
for (const sub of this.subscriptions.values()) {
|
|
6933
|
+
sub.docs.clear();
|
|
6934
|
+
sub.ref.current = sub.docs;
|
|
6935
|
+
sub.error = null;
|
|
6936
|
+
sub.isStale = false;
|
|
6937
|
+
let loaded = false;
|
|
6938
|
+
if (sub.tier !== 'ephemeral') {
|
|
6939
|
+
const cached = await idbGet(this.idbKey(sub.path));
|
|
6940
|
+
if (cached && Array.isArray(cached)) {
|
|
6941
|
+
for (const doc of cached) {
|
|
6942
|
+
if (doc && doc._id)
|
|
6943
|
+
sub.docs.set(doc._id, doc);
|
|
6944
|
+
}
|
|
6945
|
+
sub.ref.current = sub.docs;
|
|
6946
|
+
loaded = sub.docs.size > 0;
|
|
6947
|
+
}
|
|
6948
|
+
}
|
|
6949
|
+
sub.status = loaded ? 'cached' : 'loading';
|
|
6950
|
+
sub.isStale = loaded;
|
|
6951
|
+
if (loaded)
|
|
6952
|
+
this.notifySubscription(sub);
|
|
6953
|
+
else
|
|
6954
|
+
this.notifyState(sub);
|
|
6955
|
+
}
|
|
6956
|
+
if (this.ws) {
|
|
6957
|
+
const ws = this.ws;
|
|
6958
|
+
this.ws = null;
|
|
6959
|
+
this.connectPromise = null;
|
|
6960
|
+
this.suppressNextReconnect = true;
|
|
6961
|
+
try {
|
|
6962
|
+
ws.close(1000, 'Auth changed');
|
|
6963
|
+
}
|
|
6964
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
6965
|
+
}
|
|
6966
|
+
}
|
|
6947
6967
|
async ensureConnected() {
|
|
6948
6968
|
var _a;
|
|
6949
|
-
|
|
6950
|
-
if (!this.initPromise)
|
|
6951
|
-
this.initPromise = this.init();
|
|
6952
|
-
await this.initPromise;
|
|
6953
|
-
}
|
|
6969
|
+
await this.ensureCurrentAuth();
|
|
6954
6970
|
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN && !this.authenticating)
|
|
6955
6971
|
return;
|
|
6956
6972
|
if (this.connectPromise)
|
|
@@ -7034,11 +7050,20 @@ class RealtimeStore {
|
|
|
7034
7050
|
ws.addEventListener('close', () => {
|
|
7035
7051
|
if (authTimer)
|
|
7036
7052
|
clearTimeout(authTimer);
|
|
7053
|
+
if (this.ws !== ws) {
|
|
7054
|
+
if (this.suppressNextReconnect)
|
|
7055
|
+
this.suppressNextReconnect = false;
|
|
7056
|
+
return;
|
|
7057
|
+
}
|
|
7037
7058
|
this.authenticating = false;
|
|
7038
7059
|
this.ws = null;
|
|
7039
7060
|
this.connectPromise = null;
|
|
7040
7061
|
this.rejectAllPending('WebSocket closed');
|
|
7041
7062
|
this.setAllSubscriptionStatus('reconnecting');
|
|
7063
|
+
if (this.suppressNextReconnect) {
|
|
7064
|
+
this.suppressNextReconnect = false;
|
|
7065
|
+
return;
|
|
7066
|
+
}
|
|
7042
7067
|
this.scheduleReconnect();
|
|
7043
7068
|
});
|
|
7044
7069
|
});
|
|
@@ -7214,14 +7239,34 @@ class RealtimeStore {
|
|
|
7214
7239
|
}
|
|
7215
7240
|
}
|
|
7216
7241
|
handleError(msg) {
|
|
7217
|
-
var _a;
|
|
7242
|
+
var _a, _b, _c;
|
|
7243
|
+
const error = new Error((_a = msg.message) !== null && _a !== void 0 ? _a : (msg.code ? `${msg.code}: Server error` : 'Server error'));
|
|
7244
|
+
if (msg.code)
|
|
7245
|
+
error.code = msg.code;
|
|
7246
|
+
if (msg.subscriptionId || msg.id)
|
|
7247
|
+
error.subscriptionId = (_b = msg.subscriptionId) !== null && _b !== void 0 ? _b : msg.id;
|
|
7218
7248
|
const requestId = msg.requestId;
|
|
7219
7249
|
if (requestId) {
|
|
7220
7250
|
const pending = this.pendingRequests.get(requestId);
|
|
7221
7251
|
if (pending) {
|
|
7222
7252
|
this.pendingRequests.delete(requestId);
|
|
7223
7253
|
clearTimeout(pending.timeout);
|
|
7224
|
-
pending.reject(
|
|
7254
|
+
pending.reject(error);
|
|
7255
|
+
}
|
|
7256
|
+
}
|
|
7257
|
+
const subId = (_c = msg.subscriptionId) !== null && _c !== void 0 ? _c : msg.id;
|
|
7258
|
+
if (subId) {
|
|
7259
|
+
const sub = this.findSubscriptionById(subId);
|
|
7260
|
+
if (sub) {
|
|
7261
|
+
sub.status = 'error';
|
|
7262
|
+
sub.error = error;
|
|
7263
|
+
this.notifyState(sub);
|
|
7264
|
+
for (const callback of Array.from(sub.errorCallbacks)) {
|
|
7265
|
+
try {
|
|
7266
|
+
callback(error);
|
|
7267
|
+
}
|
|
7268
|
+
catch ( /* swallow */_d) { /* swallow */ }
|
|
7269
|
+
}
|
|
7225
7270
|
}
|
|
7226
7271
|
}
|
|
7227
7272
|
}
|
|
@@ -7230,6 +7275,7 @@ class RealtimeStore {
|
|
|
7230
7275
|
// -----------------------------------------------------------------------
|
|
7231
7276
|
async subscribe(path, opts = {}) {
|
|
7232
7277
|
var _a;
|
|
7278
|
+
await this.ensureCurrentAuth();
|
|
7233
7279
|
const tier = (_a = opts.tier) !== null && _a !== void 0 ? _a : 'durable';
|
|
7234
7280
|
const subKey = this.getSubKey(path, opts);
|
|
7235
7281
|
let sub = this.subscriptions.get(subKey);
|
|
@@ -7239,6 +7285,8 @@ class RealtimeStore {
|
|
|
7239
7285
|
sub.callbacks.add(opts.onData);
|
|
7240
7286
|
if (opts.onState)
|
|
7241
7287
|
sub.stateCallbacks.add(opts.onState);
|
|
7288
|
+
if (opts.onError)
|
|
7289
|
+
sub.errorCallbacks.add(opts.onError);
|
|
7242
7290
|
// Immediately deliver current state
|
|
7243
7291
|
if (opts.onData && sub.docs.size > 0) {
|
|
7244
7292
|
opts.onData(this.docsToArray(sub));
|
|
@@ -7246,7 +7294,7 @@ class RealtimeStore {
|
|
|
7246
7294
|
if (opts.onState) {
|
|
7247
7295
|
opts.onState(this.getState(sub));
|
|
7248
7296
|
}
|
|
7249
|
-
return this.createUnsubscribe(subKey, opts.onData, opts.onState);
|
|
7297
|
+
return this.createUnsubscribe(subKey, sub.id, opts.onData, opts.onState, opts.onError);
|
|
7250
7298
|
}
|
|
7251
7299
|
// New subscription
|
|
7252
7300
|
const subId = `sub_${nextRequestId++}`;
|
|
@@ -7261,6 +7309,7 @@ class RealtimeStore {
|
|
|
7261
7309
|
error: null,
|
|
7262
7310
|
callbacks: new Set(opts.onData ? [opts.onData] : []),
|
|
7263
7311
|
stateCallbacks: new Set(opts.onState ? [opts.onState] : []),
|
|
7312
|
+
errorCallbacks: new Set(opts.onError ? [opts.onError] : []),
|
|
7264
7313
|
ref: { current: new Map() },
|
|
7265
7314
|
};
|
|
7266
7315
|
this.subscriptions.set(subKey, sub);
|
|
@@ -7290,7 +7339,7 @@ class RealtimeStore {
|
|
|
7290
7339
|
sub.error = new Error('Connection failed');
|
|
7291
7340
|
this.notifyState(sub);
|
|
7292
7341
|
}
|
|
7293
|
-
return this.createUnsubscribe(subKey, opts.onData, opts.onState);
|
|
7342
|
+
return this.createUnsubscribe(subKey, sub.id, opts.onData, opts.onState, opts.onError);
|
|
7294
7343
|
}
|
|
7295
7344
|
getRef(path, opts = {}) {
|
|
7296
7345
|
var _a;
|
|
@@ -7364,6 +7413,7 @@ class RealtimeStore {
|
|
|
7364
7413
|
}
|
|
7365
7414
|
}
|
|
7366
7415
|
async get(path) {
|
|
7416
|
+
await this.ensureCurrentAuth();
|
|
7367
7417
|
const normalizedPath = path.startsWith('/') ? path.slice(1) : path;
|
|
7368
7418
|
// Check local subscriptions first
|
|
7369
7419
|
const collectionPath = this.getCollectionPath(normalizedPath);
|
|
@@ -7544,7 +7594,7 @@ class RealtimeStore {
|
|
|
7544
7594
|
return docPath;
|
|
7545
7595
|
}
|
|
7546
7596
|
getSubKey(path, opts) {
|
|
7547
|
-
const parts = [path];
|
|
7597
|
+
const parts = [this.appId, this.authPrincipalKey, path];
|
|
7548
7598
|
if (opts.filter)
|
|
7549
7599
|
parts.push(JSON.stringify(opts.filter));
|
|
7550
7600
|
if (opts.prompt)
|
|
@@ -7554,7 +7604,7 @@ class RealtimeStore {
|
|
|
7554
7604
|
return parts.join('::');
|
|
7555
7605
|
}
|
|
7556
7606
|
idbKey(path) {
|
|
7557
|
-
return `${this.appId}:${path}`;
|
|
7607
|
+
return `${this.appId}:${this.authPrincipalKey}:${path}`;
|
|
7558
7608
|
}
|
|
7559
7609
|
markIdbDirty(path) {
|
|
7560
7610
|
const sub = this.findSubscriptionByPath(path);
|
|
@@ -7579,18 +7629,23 @@ class RealtimeStore {
|
|
|
7579
7629
|
}
|
|
7580
7630
|
}
|
|
7581
7631
|
}
|
|
7582
|
-
createUnsubscribe(subKey, onData, onState) {
|
|
7632
|
+
createUnsubscribe(subKey, subId, onData, onState, onError) {
|
|
7583
7633
|
return async () => {
|
|
7584
|
-
|
|
7634
|
+
var _a;
|
|
7635
|
+
const sub = (_a = this.subscriptions.get(subKey)) !== null && _a !== void 0 ? _a : this.findSubscriptionById(subId);
|
|
7585
7636
|
if (!sub)
|
|
7586
7637
|
return;
|
|
7638
|
+
const currentSubKey = this.getSubKey(sub.path, sub.options);
|
|
7587
7639
|
if (onData)
|
|
7588
7640
|
sub.callbacks.delete(onData);
|
|
7589
7641
|
if (onState)
|
|
7590
7642
|
sub.stateCallbacks.delete(onState);
|
|
7643
|
+
if (onError)
|
|
7644
|
+
sub.errorCallbacks.delete(onError);
|
|
7591
7645
|
// If no more callbacks, unsubscribe entirely
|
|
7592
|
-
if (sub.callbacks.size === 0 && sub.stateCallbacks.size === 0) {
|
|
7646
|
+
if (sub.callbacks.size === 0 && sub.stateCallbacks.size === 0 && sub.errorCallbacks.size === 0) {
|
|
7593
7647
|
this.subscriptions.delete(subKey);
|
|
7648
|
+
this.subscriptions.delete(currentSubKey);
|
|
7594
7649
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
7595
7650
|
this.ws.send(JSON.stringify({
|
|
7596
7651
|
type: 'unsubscribe',
|
|
@@ -7662,6 +7717,22 @@ class RealtimeStore {
|
|
|
7662
7717
|
this.rejectAllPending('Store closed');
|
|
7663
7718
|
this.subscriptions.clear();
|
|
7664
7719
|
}
|
|
7720
|
+
async reconnectWithNewAuth() {
|
|
7721
|
+
if (this.closed)
|
|
7722
|
+
return;
|
|
7723
|
+
await this.ensureInitialized();
|
|
7724
|
+
await this.refreshToken();
|
|
7725
|
+
await this.applyAuthPrincipalChange();
|
|
7726
|
+
if (this.subscriptions.size > 0) {
|
|
7727
|
+
await this.ensureConnected().catch((error) => {
|
|
7728
|
+
this.setAllSubscriptionStatus('error');
|
|
7729
|
+
for (const sub of this.subscriptions.values()) {
|
|
7730
|
+
sub.error = error instanceof Error ? error : new Error(String(error));
|
|
7731
|
+
this.notifyState(sub);
|
|
7732
|
+
}
|
|
7733
|
+
});
|
|
7734
|
+
}
|
|
7735
|
+
}
|
|
7665
7736
|
}
|
|
7666
7737
|
// ---------------------------------------------------------------------------
|
|
7667
7738
|
// Singleton instance
|
|
@@ -7679,6 +7750,19 @@ function resetRealtimeStore() {
|
|
|
7679
7750
|
storeInstance = null;
|
|
7680
7751
|
}
|
|
7681
7752
|
}
|
|
7753
|
+
async function reconnectRealtimeStoreWithNewAuth() {
|
|
7754
|
+
if (storeInstance) {
|
|
7755
|
+
await storeInstance.reconnectWithNewAuth();
|
|
7756
|
+
}
|
|
7757
|
+
}
|
|
7758
|
+
|
|
7759
|
+
var realtimeStore = /*#__PURE__*/Object.freeze({
|
|
7760
|
+
__proto__: null,
|
|
7761
|
+
RealtimeStore: RealtimeStore,
|
|
7762
|
+
getRealtimeStore: getRealtimeStore,
|
|
7763
|
+
reconnectRealtimeStoreWithNewAuth: reconnectRealtimeStoreWithNewAuth,
|
|
7764
|
+
resetRealtimeStore: resetRealtimeStore
|
|
7765
|
+
});
|
|
7682
7766
|
|
|
7683
7767
|
// ---------------------------------------------------------------------------
|
|
7684
7768
|
// functions.ts -- Bounded Functions client (the imperative escape hatch).
|
|
@@ -7834,6 +7918,23 @@ function realtimeHttpBase(wsApiUrl) {
|
|
|
7834
7918
|
// Strip trailing slash from the resulting origin+path.
|
|
7835
7919
|
return url.toString().replace(/\/$/, '');
|
|
7836
7920
|
}
|
|
7921
|
+
function withoutAuthorization(headers) {
|
|
7922
|
+
if (!headers)
|
|
7923
|
+
return undefined;
|
|
7924
|
+
const clean = {};
|
|
7925
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
7926
|
+
if (key.toLowerCase() === 'authorization')
|
|
7927
|
+
continue;
|
|
7928
|
+
clean[key] = value;
|
|
7929
|
+
}
|
|
7930
|
+
return Object.keys(clean).length > 0 ? clean : undefined;
|
|
7931
|
+
}
|
|
7932
|
+
async function liveAuthHeader(configIsServer, overrides) {
|
|
7933
|
+
if (overrides === null || overrides === void 0 ? void 0 : overrides._getAuthHeaders) {
|
|
7934
|
+
return overrides._getAuthHeaders();
|
|
7935
|
+
}
|
|
7936
|
+
return createAuthHeader(configIsServer);
|
|
7937
|
+
}
|
|
7837
7938
|
/**
|
|
7838
7939
|
* Send a player intent to a running live room. Returns `{ ok: true }`.
|
|
7839
7940
|
*
|
|
@@ -7848,7 +7949,7 @@ function realtimeHttpBase(wsApiUrl) {
|
|
|
7848
7949
|
* transport error / timeout.
|
|
7849
7950
|
*/
|
|
7850
7951
|
async function intent(roomPath, intent, opts = {}) {
|
|
7851
|
-
var _a, _b, _c, _d, _e;
|
|
7952
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
7852
7953
|
if (!roomPath || typeof roomPath !== 'string') {
|
|
7853
7954
|
throw new LiveIntentError('A room path is required');
|
|
7854
7955
|
}
|
|
@@ -7869,37 +7970,48 @@ async function intent(roomPath, intent, opts = {}) {
|
|
|
7869
7970
|
// (e.g. the first join before subscribeView's WS connects) — HTTP also throws
|
|
7870
7971
|
// on a non-2xx, so errors are surfaced there too.
|
|
7871
7972
|
const normalizedRoomPath = roomPath.replace(/\/$/, '');
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7973
|
+
const hasAuthOverride = !!((_a = opts._overrides) === null || _a === void 0 ? void 0 : _a._getAuthHeaders);
|
|
7974
|
+
if (!hasAuthOverride) {
|
|
7975
|
+
if (opts.fireAndForget) {
|
|
7976
|
+
if (wsIntent(config.appId, normalizedRoomPath, intent))
|
|
7977
|
+
return { ok: true };
|
|
7978
|
+
}
|
|
7979
|
+
else {
|
|
7980
|
+
const ack = wsIntentReliable(config.appId, normalizedRoomPath, intent);
|
|
7981
|
+
if (ack)
|
|
7982
|
+
return await ack;
|
|
7983
|
+
}
|
|
7880
7984
|
}
|
|
7881
7985
|
const base = realtimeHttpBase(config.wsApiUrl);
|
|
7882
|
-
|
|
7883
|
-
const
|
|
7884
|
-
const
|
|
7986
|
+
const extraHeaders = withoutAuthorization(opts.headers);
|
|
7987
|
+
const overrideHeaders = withoutAuthorization((_b = opts._overrides) === null || _b === void 0 ? void 0 : _b.headers);
|
|
7988
|
+
const buildHeaders = async () => {
|
|
7989
|
+
const authHeader = await liveAuthHeader(config.isServer, opts._overrides);
|
|
7990
|
+
return Object.assign(Object.assign(Object.assign({ 'Content-Type': 'application/json', 'X-App-Id': config.appId, 'X-Public-App-Id': config.appId }, (overrideHeaders !== null && overrideHeaders !== void 0 ? overrideHeaders : {})), (extraHeaders !== null && extraHeaders !== void 0 ? extraHeaders : {})), (authHeader !== null && authHeader !== void 0 ? authHeader : {}));
|
|
7991
|
+
};
|
|
7885
7992
|
const controller = new AbortController();
|
|
7886
|
-
const timeoutMs = (
|
|
7993
|
+
const timeoutMs = (_c = opts.timeoutMs) !== null && _c !== void 0 ? _c : 60000;
|
|
7887
7994
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
7888
7995
|
let res;
|
|
7889
7996
|
try {
|
|
7890
|
-
|
|
7997
|
+
const send = async () => fetch(`${base}/live/intent`, {
|
|
7891
7998
|
method: 'POST',
|
|
7892
|
-
headers,
|
|
7999
|
+
headers: await buildHeaders(),
|
|
7893
8000
|
body: JSON.stringify({ path: normalizedRoomPath, intent }),
|
|
7894
8001
|
signal: controller.signal,
|
|
7895
8002
|
});
|
|
8003
|
+
res = await send();
|
|
8004
|
+
if (res.status === 401 && ((_d = opts._overrides) === null || _d === void 0 ? void 0 : _d._clearAuth)) {
|
|
8005
|
+
await opts._overrides._clearAuth();
|
|
8006
|
+
res = await send();
|
|
8007
|
+
}
|
|
7896
8008
|
}
|
|
7897
8009
|
catch (err) {
|
|
7898
8010
|
clearTimeout(timer);
|
|
7899
8011
|
if ((err === null || err === void 0 ? void 0 : err.name) === 'AbortError') {
|
|
7900
8012
|
throw new LiveIntentError(`Live intent to "${roomPath}" timed out after ${timeoutMs}ms`);
|
|
7901
8013
|
}
|
|
7902
|
-
throw new LiveIntentError(`Failed to reach the realtime worker: ${(
|
|
8014
|
+
throw new LiveIntentError(`Failed to reach the realtime worker: ${(_e = err === null || err === void 0 ? void 0 : err.message) !== null && _e !== void 0 ? _e : String(err)}`);
|
|
7903
8015
|
}
|
|
7904
8016
|
clearTimeout(timer);
|
|
7905
8017
|
let body = null;
|
|
@@ -7908,12 +8020,12 @@ async function intent(roomPath, intent, opts = {}) {
|
|
|
7908
8020
|
try {
|
|
7909
8021
|
body = JSON.parse(text);
|
|
7910
8022
|
}
|
|
7911
|
-
catch (
|
|
8023
|
+
catch (_h) {
|
|
7912
8024
|
body = { raw: text };
|
|
7913
8025
|
}
|
|
7914
8026
|
}
|
|
7915
8027
|
if (!res.ok) {
|
|
7916
|
-
const message = (
|
|
8028
|
+
const message = (_g = (_f = body === null || body === void 0 ? void 0 : body.error) !== null && _f !== void 0 ? _f : body === null || body === void 0 ? void 0 : body.message) !== null && _g !== void 0 ? _g : `Live intent failed with HTTP ${res.status}`;
|
|
7917
8029
|
throw new LiveIntentError(message, res.status, body);
|
|
7918
8030
|
}
|
|
7919
8031
|
return (body !== null && body !== void 0 ? body : { ok: true });
|