@palbase/web 7.3.8 → 7.4.0
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/{analytics-facade-BER0EyYT.d.ts → analytics-facade-But6gu2r.d.ts} +135 -16
- package/dist/{analytics-facade-CQXCQSoF.d.cts → analytics-facade-f8IVujJW.d.cts} +135 -16
- package/dist/{chunk-6DGCUHSO.js → chunk-4WMJTUDN.js} +28 -19
- package/dist/chunk-4WMJTUDN.js.map +1 -0
- package/dist/{chunk-CFDU23TB.js → chunk-G56VGUYZ.js} +3 -3
- package/dist/chunk-G56VGUYZ.js.map +1 -0
- package/dist/{chunk-HT5SNA5H.js → chunk-VHUCJBBQ.js} +2 -2
- package/dist/chunk-VHUCJBBQ.js.map +1 -0
- package/dist/{chunk-A42CPPQ7.js → chunk-WXBOPPHU.js} +502 -38
- package/dist/chunk-WXBOPPHU.js.map +1 -0
- package/dist/gen/cli.cjs +2 -2
- package/dist/gen/cli.cjs.map +1 -1
- package/dist/gen/cli.js +2 -2
- package/dist/gen/cli.js.map +1 -1
- package/dist/index.cjs +514 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/internal.cjs +527 -54
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +24 -4
- package/dist/internal.d.ts +24 -4
- package/dist/internal.js +3 -3
- package/dist/next/client.cjs +521 -51
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +3 -3
- package/dist/next/index.cjs +528 -55
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +2 -2
- package/dist/next/index.d.ts +2 -2
- package/dist/next/index.js +4 -4
- package/dist/next/index.js.map +1 -1
- package/dist/next/proxy.cjs +1 -1
- package/dist/next/proxy.cjs.map +1 -1
- package/dist/next/proxy.js +3 -3
- package/dist/{pb-WAQvwCfM.d.cts → pb-9MMDNQ9p.d.cts} +6 -1
- package/dist/{pb-BRlmBIAm.d.ts → pb-DRmdWQzG.d.ts} +6 -1
- package/dist/react/index.cjs +22 -19
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-6DGCUHSO.js.map +0 -1
- package/dist/chunk-A42CPPQ7.js.map +0 -1
- package/dist/chunk-CFDU23TB.js.map +0 -1
- package/dist/chunk-HT5SNA5H.js.map +0 -1
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
isBackendError,
|
|
11
11
|
solvePowChallenge,
|
|
12
12
|
unwrap
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-4WMJTUDN.js";
|
|
14
14
|
import {
|
|
15
15
|
environmentRefFromPublishableApiKey
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-G56VGUYZ.js";
|
|
17
17
|
import {
|
|
18
18
|
__export
|
|
19
19
|
} from "./chunk-PZ5AY32C.js";
|
|
@@ -1323,24 +1323,29 @@ var PalbeAuth = class {
|
|
|
1323
1323
|
* Self-service account erasure ("right to be forgotten"). Calls palauth
|
|
1324
1324
|
* `DELETE /auth/user` (session-authed + fresh re-auth): password users pass
|
|
1325
1325
|
* `{ password }`; passwordless/OAuth users rely on a freshly stepped-up
|
|
1326
|
-
* session.
|
|
1327
|
-
* already revoked server-side — so we tear down
|
|
1328
|
-
* `signOut()`'s clear, minus the pointless `/logout`)
|
|
1329
|
-
* `signedOut{reason:'accountDeleted'}`.
|
|
1326
|
+
* session. The handler answers **204 with no body** — the erasure has already
|
|
1327
|
+
* finished and every session is already revoked server-side — so we tear down
|
|
1328
|
+
* local state (mirroring `signOut()`'s clear, minus the pointless `/logout`)
|
|
1329
|
+
* and emit `signedOut{reason:'accountDeleted'}`.
|
|
1330
|
+
*
|
|
1331
|
+
* Returns nothing, because there is nothing to return: the handler used to
|
|
1332
|
+
* answer 202 with an `{erasure_id}` naming an asynchronous run, and that id
|
|
1333
|
+
* was removed along with the run (`gdpr_handlers.go:221-229`). Reading it
|
|
1334
|
+
* here threw a `TypeError` AFTER the account was already gone — a success
|
|
1335
|
+
* reported as a failure.
|
|
1330
1336
|
*
|
|
1331
1337
|
* On 401 `reauth_required` / 503 `not_configured` (or `erasure_unavailable`)
|
|
1332
1338
|
* the request throws BEFORE the teardown line, so the local session is left
|
|
1333
1339
|
* fully intact — the deletion did not happen and the user is still signed in.
|
|
1334
1340
|
*/
|
|
1335
1341
|
async deleteAccount(params = {}) {
|
|
1336
|
-
|
|
1342
|
+
await palbeRequest(this.rt, "DELETE", "/auth/user", {
|
|
1337
1343
|
body: params.password ? { password: params.password } : {}
|
|
1338
1344
|
});
|
|
1339
1345
|
this.nextSignOutReason = "accountDeleted";
|
|
1340
1346
|
this.rt.tokenManager.clearSession();
|
|
1341
1347
|
this.rt.storage.clear();
|
|
1342
1348
|
this.cachedUser = null;
|
|
1343
|
-
return { erasureId: res.erasure_id };
|
|
1344
1349
|
}
|
|
1345
1350
|
/**
|
|
1346
1351
|
* @internal — invoked by the transport choke point (`request.ts`), not app code.
|
|
@@ -1832,9 +1837,15 @@ var PalbeCalls = class {
|
|
|
1832
1837
|
* @param media - Which media tracks to publish (default: audio + video)
|
|
1833
1838
|
* @returns A `Call` in `connecting` state; transitions to `active` once the media room connects.
|
|
1834
1839
|
*
|
|
1835
|
-
*
|
|
1836
|
-
*
|
|
1837
|
-
*
|
|
1840
|
+
* Every failure below arrives as kind `'server'` — this SDK's error kinds are
|
|
1841
|
+
* `notConfigured | validation | unauthorized | rateLimited | server | network
|
|
1842
|
+
* | decode`, and only a 401, a 429 or a 400 carrying a field-error array gets
|
|
1843
|
+
* its own kind. So branch on `err.code`, never on the kind.
|
|
1844
|
+
*
|
|
1845
|
+
* @throws BackendError('server', { code: 'calling_unavailable' }) no SFU configured on this stack (503)
|
|
1846
|
+
* @throws BackendError('server', { code: 'not_group_member' }) the caller is not a member of the group (403)
|
|
1847
|
+
* @throws BackendError('server', { code: 'call_room_full' }) the call is at its participant limit (409)
|
|
1848
|
+
* @throws BackendError('server', { code: 'call_provider_error' }) the media provider failed upstream (502)
|
|
1838
1849
|
*/
|
|
1839
1850
|
async start(groupId, { media = ["audio", "video"] } = {}) {
|
|
1840
1851
|
if (!groupId) {
|
|
@@ -1855,7 +1866,11 @@ var PalbeCalls = class {
|
|
|
1855
1866
|
* Accept an incoming call in a messaging group.
|
|
1856
1867
|
*
|
|
1857
1868
|
* @param groupId - Messaging group display-id
|
|
1858
|
-
* @param callId - Call identifier
|
|
1869
|
+
* @param callId - Call identifier, carried by the `call_invite` the server
|
|
1870
|
+
* fans onto the group's conversation topic
|
|
1871
|
+
* (`pb.realtime.channel('messaging:conv:<rfcGroupId>').on('call_invite', …)`).
|
|
1872
|
+
* This SDK ships no web push: on the web the invite arrives over realtime or
|
|
1873
|
+
* over whatever signalling the app already runs.
|
|
1859
1874
|
* @param media - Which media tracks to publish (default: audio + video)
|
|
1860
1875
|
*/
|
|
1861
1876
|
async accept(groupId, callId, { media = ["audio", "video"] } = {}) {
|
|
@@ -3958,14 +3973,11 @@ var Chat = class {
|
|
|
3958
3973
|
get title() {
|
|
3959
3974
|
if (this.titleOverride) return this.titleOverride;
|
|
3960
3975
|
if (this._group?.name) return this._group.name;
|
|
3961
|
-
if (this.
|
|
3962
|
-
const
|
|
3963
|
-
|
|
3964
|
-
const peer = this.memberCache.find((m) => m.userId === mode.peerUserId);
|
|
3965
|
-
return peer?.displayName ?? "Chat";
|
|
3966
|
-
}
|
|
3967
|
-
return "Group";
|
|
3976
|
+
if (this.kind === "direct") {
|
|
3977
|
+
const peer = this.memberCache.find((m) => !m.isSelf);
|
|
3978
|
+
return peer?.displayName ?? "Chat";
|
|
3968
3979
|
}
|
|
3980
|
+
if (this._draft) return "Group";
|
|
3969
3981
|
const tail = this.id.startsWith("grp_") ? this.id.slice(4) : this.id;
|
|
3970
3982
|
const short = tail.slice(-4);
|
|
3971
3983
|
return short ? `Group ${short}` : "Group";
|
|
@@ -4939,6 +4951,11 @@ var MessageHub = class {
|
|
|
4939
4951
|
emitConv(displayId, e) {
|
|
4940
4952
|
for (const fn of this.convListeners.get(displayId) ?? []) fn(e);
|
|
4941
4953
|
}
|
|
4954
|
+
/** How many live conv listeners a group still has — the census the LAST
|
|
4955
|
+
* subscriber reads before tearing the conv topic (and its presence) down. */
|
|
4956
|
+
convListenerCount(displayId) {
|
|
4957
|
+
return this.convListeners.get(displayId)?.size ?? 0;
|
|
4958
|
+
}
|
|
4942
4959
|
add(map, key, fn) {
|
|
4943
4960
|
let set = map.get(key);
|
|
4944
4961
|
if (!set) {
|
|
@@ -4976,6 +4993,9 @@ var MessageDeliverySource = class {
|
|
|
4976
4993
|
started = false;
|
|
4977
4994
|
wakeUnsub = null;
|
|
4978
4995
|
// Per-observed-group conv subscription + heartbeat (presence/typing/read).
|
|
4996
|
+
// `teardown` is the ONE departure path: it stops the heartbeat, broadcasts the
|
|
4997
|
+
// offline frame and drops the subscriptions, so no caller can tear an
|
|
4998
|
+
// observation down without saying goodbye (see observeConversation).
|
|
4979
4999
|
observed = /* @__PURE__ */ new Map();
|
|
4980
5000
|
/** Subscribe the device wake topic + run an initial drain. Idempotent. */
|
|
4981
5001
|
async start() {
|
|
@@ -5000,10 +5020,7 @@ var MessageDeliverySource = class {
|
|
|
5000
5020
|
stop() {
|
|
5001
5021
|
this.wakeUnsub?.();
|
|
5002
5022
|
this.wakeUnsub = null;
|
|
5003
|
-
for (const [, o] of this.observed)
|
|
5004
|
-
o.unsub();
|
|
5005
|
-
if (o.heartbeat) clearInterval(o.heartbeat);
|
|
5006
|
-
}
|
|
5023
|
+
for (const [, o] of this.observed) o.teardown();
|
|
5007
5024
|
this.observed.clear();
|
|
5008
5025
|
this.started = false;
|
|
5009
5026
|
}
|
|
@@ -5234,14 +5251,26 @@ var MessageDeliverySource = class {
|
|
|
5234
5251
|
emitPresence(true);
|
|
5235
5252
|
const heartbeat = setInterval(() => emitPresence(true), 25e3);
|
|
5236
5253
|
this.observed.set(group.displayId, {
|
|
5237
|
-
|
|
5254
|
+
teardown: () => {
|
|
5255
|
+
clearInterval(heartbeat);
|
|
5256
|
+
try {
|
|
5257
|
+
emitPresence(false);
|
|
5258
|
+
} catch {
|
|
5259
|
+
}
|
|
5238
5260
|
for (const s of subs) s.cancel();
|
|
5239
|
-
}
|
|
5240
|
-
heartbeat
|
|
5261
|
+
}
|
|
5241
5262
|
});
|
|
5242
5263
|
} catch {
|
|
5243
5264
|
}
|
|
5244
5265
|
}
|
|
5266
|
+
/** Stop observing a group's conv topic: announce OFFLINE, stop the heartbeat
|
|
5267
|
+
* and drop the subscriptions. Idempotent — an unobserved group is a no-op. */
|
|
5268
|
+
unobserveConversation(group) {
|
|
5269
|
+
const o = this.observed.get(group.displayId);
|
|
5270
|
+
if (!o) return;
|
|
5271
|
+
this.observed.delete(group.displayId);
|
|
5272
|
+
o.teardown();
|
|
5273
|
+
}
|
|
5245
5274
|
/** Announce typing on a group's conv topic (the app calls per keystroke). */
|
|
5246
5275
|
setTyping(group, isTyping) {
|
|
5247
5276
|
this.observeConversation(group);
|
|
@@ -7469,7 +7498,11 @@ var MessagingCoordinator = class {
|
|
|
7469
7498
|
subscribeLive(group, chat) {
|
|
7470
7499
|
let offMsg = null;
|
|
7471
7500
|
let offConv = null;
|
|
7501
|
+
let resolved = null;
|
|
7502
|
+
let cancelled = false;
|
|
7472
7503
|
void this.resolve().then((r) => {
|
|
7504
|
+
if (cancelled) return;
|
|
7505
|
+
resolved = r;
|
|
7473
7506
|
offMsg = r.hub.onMessage(group.displayId, (m) => {
|
|
7474
7507
|
void chat.ingestLive(m);
|
|
7475
7508
|
});
|
|
@@ -7477,8 +7510,12 @@ var MessagingCoordinator = class {
|
|
|
7477
7510
|
offConv = r.hub.onConv(group.displayId, (e) => chat.applyConv(e.event, e.payload));
|
|
7478
7511
|
});
|
|
7479
7512
|
return () => {
|
|
7513
|
+
cancelled = true;
|
|
7480
7514
|
offMsg?.();
|
|
7481
7515
|
offConv?.();
|
|
7516
|
+
if (resolved && resolved.hub.convListenerCount(group.displayId) === 0) {
|
|
7517
|
+
resolved.source.unobserveConversation(group);
|
|
7518
|
+
}
|
|
7482
7519
|
};
|
|
7483
7520
|
}
|
|
7484
7521
|
async userIdForDevice(group, deviceId) {
|
|
@@ -8336,8 +8373,33 @@ function unwrapBroadcast(frame) {
|
|
|
8336
8373
|
function stripRealtimePrefix(topic) {
|
|
8337
8374
|
return topic.startsWith(REALTIME_PREFIX) ? topic.slice(REALTIME_PREFIX.length) : topic;
|
|
8338
8375
|
}
|
|
8376
|
+
function encodeStateSet(joinRef, ref, topic, key, value, life) {
|
|
8377
|
+
return JSON.stringify([
|
|
8378
|
+
joinRef,
|
|
8379
|
+
String(ref),
|
|
8380
|
+
REALTIME_PREFIX + topic,
|
|
8381
|
+
"state_set",
|
|
8382
|
+
{ key, value, life }
|
|
8383
|
+
]);
|
|
8384
|
+
}
|
|
8385
|
+
function encodeStateDel(joinRef, ref, topic, key) {
|
|
8386
|
+
return JSON.stringify([joinRef, String(ref), REALTIME_PREFIX + topic, "state_del", { key }]);
|
|
8387
|
+
}
|
|
8388
|
+
function encodeStateResync(joinRef, ref, topic) {
|
|
8389
|
+
return JSON.stringify([joinRef, String(ref), REALTIME_PREFIX + topic, "state_resync", {}]);
|
|
8390
|
+
}
|
|
8391
|
+
function encodeAccessToken(joinRef, ref, topic, token) {
|
|
8392
|
+
return JSON.stringify([
|
|
8393
|
+
joinRef,
|
|
8394
|
+
String(ref),
|
|
8395
|
+
REALTIME_PREFIX + topic,
|
|
8396
|
+
"access_token",
|
|
8397
|
+
{ access_token: token }
|
|
8398
|
+
]);
|
|
8399
|
+
}
|
|
8339
8400
|
|
|
8340
8401
|
// src/realtime/connection.ts
|
|
8402
|
+
var RETRYABLE_REFUSALS = /* @__PURE__ */ new Set(["unavailable", "rate_limited", "too_many_channels"]);
|
|
8341
8403
|
var WS_OPEN = 1;
|
|
8342
8404
|
var DEFAULT_HEARTBEAT_MS = 25e3;
|
|
8343
8405
|
var DEFAULT_MAX_BACKOFF_SECONDS = 30;
|
|
@@ -8570,6 +8632,31 @@ var RealtimeSocket = class {
|
|
|
8570
8632
|
this.handleChannelClose(frame);
|
|
8571
8633
|
return;
|
|
8572
8634
|
}
|
|
8635
|
+
if (frame.event === "state_snapshot") {
|
|
8636
|
+
this.handlers?.onStateSnapshot(stripRealtimePrefix(frame.topic), frame.payload);
|
|
8637
|
+
return;
|
|
8638
|
+
}
|
|
8639
|
+
if (frame.event === "state_diff") {
|
|
8640
|
+
this.handlers?.onStateDiff(stripRealtimePrefix(frame.topic), frame.payload);
|
|
8641
|
+
return;
|
|
8642
|
+
}
|
|
8643
|
+
if (frame.event === "phx_reply") {
|
|
8644
|
+
const body = frame.payload;
|
|
8645
|
+
const topic = stripRealtimePrefix(frame.topic);
|
|
8646
|
+
if (body && body.status === "ok" && body.response && typeof body.response === "object") {
|
|
8647
|
+
this.handlers?.onJoinInfo(topic, body.response);
|
|
8648
|
+
return;
|
|
8649
|
+
}
|
|
8650
|
+
if (body && body.status === "error" && String(frame.ref) === this.joinRefs.get(topic)) {
|
|
8651
|
+
const reason = typeof body.response?.["reason"] === "string" ? body.response["reason"] : "unauthorized";
|
|
8652
|
+
if (!RETRYABLE_REFUSALS.has(reason)) {
|
|
8653
|
+
this.joinedTopics.delete(topic);
|
|
8654
|
+
this.joinRefs.delete(topic);
|
|
8655
|
+
}
|
|
8656
|
+
this.handlers?.onChannelRefused(topic, reason);
|
|
8657
|
+
}
|
|
8658
|
+
return;
|
|
8659
|
+
}
|
|
8573
8660
|
const inbound = unwrapBroadcast(frame);
|
|
8574
8661
|
if (inbound) {
|
|
8575
8662
|
this.channelRejoinAttempts.delete(inbound.topic);
|
|
@@ -8610,6 +8697,40 @@ var RealtimeSocket = class {
|
|
|
8610
8697
|
}, delayMs);
|
|
8611
8698
|
this.channelRejoinTimers.set(topic, timer);
|
|
8612
8699
|
}
|
|
8700
|
+
// MARK: state
|
|
8701
|
+
/** Write one entry on a joined channel's state. Dropped when the topic has no
|
|
8702
|
+
* live join: the server refuses state on a channel this socket is not on, and
|
|
8703
|
+
* the reconnect's join brings a fresh snapshot anyway. */
|
|
8704
|
+
sendStateSet(topic, key, value, life) {
|
|
8705
|
+
const joinRef = this.joinRefs.get(topic);
|
|
8706
|
+
if (joinRef === void 0 || !this.isOpen()) return;
|
|
8707
|
+
this.send(encodeStateSet(joinRef, this.nextRef(), topic, key, value, life));
|
|
8708
|
+
}
|
|
8709
|
+
/** Remove one entry. The server refuses a key this connection does not own. */
|
|
8710
|
+
sendStateDel(topic, key) {
|
|
8711
|
+
const joinRef = this.joinRefs.get(topic);
|
|
8712
|
+
if (joinRef === void 0 || !this.isOpen()) return;
|
|
8713
|
+
this.send(encodeStateDel(joinRef, this.nextRef(), topic, key));
|
|
8714
|
+
}
|
|
8715
|
+
/** Ask for a fresh snapshot — the answer to a detected gap. */
|
|
8716
|
+
sendStateResync(topic) {
|
|
8717
|
+
const joinRef = this.joinRefs.get(topic);
|
|
8718
|
+
if (joinRef === void 0 || !this.isOpen()) return;
|
|
8719
|
+
this.send(encodeStateResync(joinRef, this.nextRef(), topic));
|
|
8720
|
+
}
|
|
8721
|
+
/**
|
|
8722
|
+
* Present a fresher credential on every joined channel WITHOUT rejoining.
|
|
8723
|
+
*
|
|
8724
|
+
* The alternative — dropping and rejoining — reaps this connection's
|
|
8725
|
+
* ephemeral entries (its presence) and forces a full resync on every channel.
|
|
8726
|
+
* A token rotation is not a reconnection and must not look like one.
|
|
8727
|
+
*/
|
|
8728
|
+
sendAccessToken(token) {
|
|
8729
|
+
if (!this.isOpen()) return;
|
|
8730
|
+
for (const [topic, joinRef] of this.joinRefs) {
|
|
8731
|
+
this.send(encodeAccessToken(joinRef, this.nextRef(), topic, token));
|
|
8732
|
+
}
|
|
8733
|
+
}
|
|
8613
8734
|
// MARK: helpers
|
|
8614
8735
|
/** Drop a single topic's token-expiry rejoin state (cancel its pending timer). */
|
|
8615
8736
|
clearChannelRejoin(topic) {
|
|
@@ -8635,6 +8756,60 @@ var RealtimeSocket = class {
|
|
|
8635
8756
|
}
|
|
8636
8757
|
};
|
|
8637
8758
|
|
|
8759
|
+
// src/realtime/state.ts
|
|
8760
|
+
var PRESENCE_PREFIX = "$p:";
|
|
8761
|
+
var ChannelState = class {
|
|
8762
|
+
/** The sequence this local copy is current as of. */
|
|
8763
|
+
seq = 0;
|
|
8764
|
+
/** key → value. Lifetimes are the server's business; a reader only needs the
|
|
8765
|
+
* values, and presence is derived from the key shape. */
|
|
8766
|
+
entries = /* @__PURE__ */ new Map();
|
|
8767
|
+
/** Replace everything. A snapshot is the WHOLE picture, never a merge —
|
|
8768
|
+
* merging one would keep entries the server has since removed. */
|
|
8769
|
+
applySnapshot(body) {
|
|
8770
|
+
this.entries.clear();
|
|
8771
|
+
for (const [k, e] of Object.entries(body.entries ?? {})) {
|
|
8772
|
+
this.entries.set(k, e.v);
|
|
8773
|
+
}
|
|
8774
|
+
this.seq = body.seq;
|
|
8775
|
+
}
|
|
8776
|
+
/**
|
|
8777
|
+
* Apply a diff, or report that it cannot be applied.
|
|
8778
|
+
*
|
|
8779
|
+
* `gap` when this copy is behind the diff's starting point: frames were
|
|
8780
|
+
* missed, and applying anyway would produce a state that never existed on the
|
|
8781
|
+
* server. The state is left untouched so the caller can resync from something
|
|
8782
|
+
* consistent.
|
|
8783
|
+
*
|
|
8784
|
+
* A diff at or below the current sequence is already applied — a resend after
|
|
8785
|
+
* a retry — and is silently accepted so duplicates are harmless.
|
|
8786
|
+
*/
|
|
8787
|
+
applyDiff(body) {
|
|
8788
|
+
if (this.seq < body.from_seq) return "gap";
|
|
8789
|
+
if (body.seq <= this.seq) return "applied";
|
|
8790
|
+
for (const [k, e] of Object.entries(body.set ?? {})) this.entries.set(k, e.v);
|
|
8791
|
+
for (const k of body.del ?? []) this.entries.delete(k);
|
|
8792
|
+
this.seq = body.seq;
|
|
8793
|
+
return "applied";
|
|
8794
|
+
}
|
|
8795
|
+
/**
|
|
8796
|
+
* Everyone else's presence.
|
|
8797
|
+
*
|
|
8798
|
+
* `selfConn` is this connection's id, as the join reply reported it. Its own
|
|
8799
|
+
* entry is excluded because an app rendering "who else is here" would
|
|
8800
|
+
* otherwise always show one extra face — itself.
|
|
8801
|
+
*/
|
|
8802
|
+
presence(selfConn) {
|
|
8803
|
+
const own = selfConn === void 0 ? void 0 : PRESENCE_PREFIX + selfConn;
|
|
8804
|
+
const out = [];
|
|
8805
|
+
for (const [k, v] of this.entries) {
|
|
8806
|
+
if (!k.startsWith(PRESENCE_PREFIX) || k === own) continue;
|
|
8807
|
+
out.push(v);
|
|
8808
|
+
}
|
|
8809
|
+
return out;
|
|
8810
|
+
}
|
|
8811
|
+
};
|
|
8812
|
+
|
|
8638
8813
|
// src/realtime/facade.ts
|
|
8639
8814
|
var StatusStore = class {
|
|
8640
8815
|
currentState = "idle";
|
|
@@ -8670,6 +8845,7 @@ var StatusStore = class {
|
|
|
8670
8845
|
for (const listener of this.listeners) listener(snapshot);
|
|
8671
8846
|
}
|
|
8672
8847
|
};
|
|
8848
|
+
var RETRYABLE_REASONS = /* @__PURE__ */ new Set(["unavailable", "rate_limited", "too_many_channels"]);
|
|
8673
8849
|
var RealtimeChannel = class {
|
|
8674
8850
|
/** The app-defined channel name (bare sub-topic, no "realtime:" prefix). */
|
|
8675
8851
|
name;
|
|
@@ -8687,6 +8863,23 @@ var RealtimeChannel = class {
|
|
|
8687
8863
|
on(event, handler) {
|
|
8688
8864
|
return this.owner.subscribe(this.name, event, handler);
|
|
8689
8865
|
}
|
|
8866
|
+
/**
|
|
8867
|
+
* Called when the server REFUSES this channel.
|
|
8868
|
+
*
|
|
8869
|
+
* A join is an authorization decision, and a decision the app never hears is
|
|
8870
|
+
* the same as no decision at all: silence looks exactly like a slow network.
|
|
8871
|
+
* This is where a refusal arrives, carrying the server's own distinction —
|
|
8872
|
+
* `retryable: false` means "you may not" and asking again will not help;
|
|
8873
|
+
* `retryable: true` means the project could not be asked, and the socket's
|
|
8874
|
+
* next reconnect retries on its own.
|
|
8875
|
+
*
|
|
8876
|
+
* pb.realtime.channel('room:42').onError((err) => {
|
|
8877
|
+
* if (!err.retryable) showJoinDenied()
|
|
8878
|
+
* })
|
|
8879
|
+
*/
|
|
8880
|
+
onError(handler) {
|
|
8881
|
+
return this.owner.subscribeError(this.name, handler);
|
|
8882
|
+
}
|
|
8690
8883
|
/**
|
|
8691
8884
|
* Broadcast `payload` to the channel's other subscribers (web addition —
|
|
8692
8885
|
* iOS is receive-only). Joins the channel if it isn't already; queued
|
|
@@ -8695,6 +8888,57 @@ var RealtimeChannel = class {
|
|
|
8695
8888
|
send(event, payload = {}) {
|
|
8696
8889
|
this.owner.send(this.name, event, payload);
|
|
8697
8890
|
}
|
|
8891
|
+
/**
|
|
8892
|
+
* Watch one key of the channel's shared state.
|
|
8893
|
+
*
|
|
8894
|
+
* The handler is called with the CURRENT value straight away when there is
|
|
8895
|
+
* one, and again on every change. That is the difference from `on()`: a
|
|
8896
|
+
* subscriber to an event learns nothing until the next one, while state is
|
|
8897
|
+
* already there when you ask.
|
|
8898
|
+
*/
|
|
8899
|
+
onState(key, handler) {
|
|
8900
|
+
return this.owner.subscribeState(this.name, key, handler);
|
|
8901
|
+
}
|
|
8902
|
+
/** Read one key without subscribing. */
|
|
8903
|
+
stateValue(key) {
|
|
8904
|
+
return this.owner.stateOf(this.name).entries.get(key);
|
|
8905
|
+
}
|
|
8906
|
+
/** Write one key. `durable` outlives this connection; `ephemeral` is reaped
|
|
8907
|
+
* when it closes, which is what presence is built on. */
|
|
8908
|
+
setState(key, value, opts = {}) {
|
|
8909
|
+
this.owner.writeState(this.name, key, value, opts.life ?? "durable");
|
|
8910
|
+
}
|
|
8911
|
+
/** Remove a key this connection wrote. */
|
|
8912
|
+
clearState(key) {
|
|
8913
|
+
this.owner.clearState(this.name, key);
|
|
8914
|
+
}
|
|
8915
|
+
/**
|
|
8916
|
+
* Announce this client on the channel. `meta` is whatever other participants
|
|
8917
|
+
* should see — a name, an avatar, a cursor.
|
|
8918
|
+
*
|
|
8919
|
+
* It is ephemeral state keyed by this connection, so it disappears when the
|
|
8920
|
+
* socket does. Nothing has to be un-announced on a crash.
|
|
8921
|
+
*/
|
|
8922
|
+
presenceEnter(meta) {
|
|
8923
|
+
this.owner.presenceWrite(this.name, meta);
|
|
8924
|
+
}
|
|
8925
|
+
/** Replace the announced value — a cursor moving is this, at whatever rate
|
|
8926
|
+
* the app likes: the server conflates writes it cannot deliver fast enough. */
|
|
8927
|
+
presenceUpdate(meta) {
|
|
8928
|
+
this.owner.presenceWrite(this.name, meta);
|
|
8929
|
+
}
|
|
8930
|
+
/** Leave early. Closing the socket does this on its own. */
|
|
8931
|
+
presenceLeave() {
|
|
8932
|
+
this.owner.presenceClear(this.name);
|
|
8933
|
+
}
|
|
8934
|
+
/** Everyone else currently announced, this client excluded. */
|
|
8935
|
+
presenceOthers() {
|
|
8936
|
+
return this.owner.presenceOthers(this.name);
|
|
8937
|
+
}
|
|
8938
|
+
/** Watch the announced set. Called on every change. */
|
|
8939
|
+
onPresence(handler) {
|
|
8940
|
+
return this.owner.subscribePresence(this.name, handler);
|
|
8941
|
+
}
|
|
8698
8942
|
};
|
|
8699
8943
|
var PalbeRealtime = class {
|
|
8700
8944
|
rt;
|
|
@@ -8704,9 +8948,21 @@ var PalbeRealtime = class {
|
|
|
8704
8948
|
// Per-(topic, event) handlers — an event may have multiple handlers
|
|
8705
8949
|
// (multiple .on calls); entry identity is the unsubscribe token.
|
|
8706
8950
|
handlers = /* @__PURE__ */ new Set();
|
|
8951
|
+
// Per-topic refusal handlers. Kept apart from `handlers` because they are not
|
|
8952
|
+
// refcounted: registering one must not join a channel, and cancelling the
|
|
8953
|
+
// last one must not leave a channel that still has event handlers on it.
|
|
8954
|
+
errorHandlers = /* @__PURE__ */ new Set();
|
|
8707
8955
|
// Joins are refcounted per topic so the socket only leaves a topic when
|
|
8708
8956
|
// its last handler cancels (iOS RealtimeClient parity).
|
|
8709
8957
|
topicRefcount = /* @__PURE__ */ new Map();
|
|
8958
|
+
// Per-topic shared state, and this connection's identity on each topic (from
|
|
8959
|
+
// the join ack). Presence is keyed by that identity, so a write before the
|
|
8960
|
+
// ack has nowhere to go and is queued until it arrives.
|
|
8961
|
+
states = /* @__PURE__ */ new Map();
|
|
8962
|
+
connIds = /* @__PURE__ */ new Map();
|
|
8963
|
+
pendingPresence = /* @__PURE__ */ new Map();
|
|
8964
|
+
stateWatchers = /* @__PURE__ */ new Set();
|
|
8965
|
+
presenceWatchers = /* @__PURE__ */ new Set();
|
|
8710
8966
|
constructor(rt) {
|
|
8711
8967
|
this.rt = rt;
|
|
8712
8968
|
}
|
|
@@ -8749,6 +9005,20 @@ var PalbeRealtime = class {
|
|
|
8749
9005
|
get status() {
|
|
8750
9006
|
return this.statusStore;
|
|
8751
9007
|
}
|
|
9008
|
+
/**
|
|
9009
|
+
* Hand the socket a fresher credential for the SAME user, without rejoining.
|
|
9010
|
+
*
|
|
9011
|
+
* Called when the app's token rotates. The alternative — letting the channels
|
|
9012
|
+
* die at expiry and rejoining — reaps this connection's ephemeral entries
|
|
9013
|
+
* (its presence, on every channel) and forces a full state resync. A token
|
|
9014
|
+
* getting older is not a disconnection and must not cost one.
|
|
9015
|
+
*
|
|
9016
|
+
* A no-op before a socket exists: the first connect resolves a fresh token
|
|
9017
|
+
* on its own.
|
|
9018
|
+
*/
|
|
9019
|
+
refreshToken(token) {
|
|
9020
|
+
this.socket?.sendAccessToken(token);
|
|
9021
|
+
}
|
|
8752
9022
|
/** @internal */
|
|
8753
9023
|
subscribe(topic, event, handler) {
|
|
8754
9024
|
const socket = this.ensureSocket();
|
|
@@ -8763,7 +9033,9 @@ var PalbeRealtime = class {
|
|
|
8763
9033
|
if (cancelled) return;
|
|
8764
9034
|
cancelled = true;
|
|
8765
9035
|
this.handlers.delete(entry);
|
|
8766
|
-
const
|
|
9036
|
+
const held = this.topicRefcount.get(topic);
|
|
9037
|
+
if (held === void 0) return;
|
|
9038
|
+
const next = held - 1;
|
|
8767
9039
|
if (next <= 0) {
|
|
8768
9040
|
this.topicRefcount.delete(topic);
|
|
8769
9041
|
socket.leaveTopic(topic);
|
|
@@ -8773,6 +9045,25 @@ var PalbeRealtime = class {
|
|
|
8773
9045
|
}
|
|
8774
9046
|
};
|
|
8775
9047
|
}
|
|
9048
|
+
/**
|
|
9049
|
+
* Register a refusal handler for one topic. Does NOT join: watching for a
|
|
9050
|
+
* refusal is not a subscription, and an app that registers one before its
|
|
9051
|
+
* first `.on()` should not be joining anything yet.
|
|
9052
|
+
*
|
|
9053
|
+
* @internal
|
|
9054
|
+
*/
|
|
9055
|
+
subscribeError(topic, handler) {
|
|
9056
|
+
const entry = { topic, handler };
|
|
9057
|
+
this.errorHandlers.add(entry);
|
|
9058
|
+
let cancelled = false;
|
|
9059
|
+
return {
|
|
9060
|
+
cancel: () => {
|
|
9061
|
+
if (cancelled) return;
|
|
9062
|
+
cancelled = true;
|
|
9063
|
+
this.errorHandlers.delete(entry);
|
|
9064
|
+
}
|
|
9065
|
+
};
|
|
9066
|
+
}
|
|
8776
9067
|
/** @internal */
|
|
8777
9068
|
send(topic, event, payload) {
|
|
8778
9069
|
const socket = this.ensureSocket();
|
|
@@ -8794,7 +9085,11 @@ var PalbeRealtime = class {
|
|
|
8794
9085
|
onReconnect: () => {
|
|
8795
9086
|
},
|
|
8796
9087
|
onStateChange: (state) => this.statusStore.setState(state),
|
|
8797
|
-
onChannelError: (topic) => this.handleChannelError(topic)
|
|
9088
|
+
onChannelError: (topic) => this.handleChannelError(topic),
|
|
9089
|
+
onChannelRefused: (topic, reason) => this.handleChannelRefused(topic, reason),
|
|
9090
|
+
onStateSnapshot: (topic, body) => this.applySnapshot(topic, body),
|
|
9091
|
+
onStateDiff: (topic, body) => this.applyDiff(topic, body),
|
|
9092
|
+
onJoinInfo: (topic, info) => this.applyJoinInfo(topic, info)
|
|
8798
9093
|
});
|
|
8799
9094
|
}
|
|
8800
9095
|
return this.socket;
|
|
@@ -8808,19 +9103,185 @@ var PalbeRealtime = class {
|
|
|
8808
9103
|
}
|
|
8809
9104
|
}
|
|
8810
9105
|
/**
|
|
8811
|
-
|
|
8812
|
-
*
|
|
8813
|
-
*
|
|
8814
|
-
*
|
|
9106
|
+
/**
|
|
9107
|
+
* The server refused a channel. Tell whoever asked, and — when the answer is
|
|
9108
|
+
* final — stop pretending the app is subscribed to it.
|
|
9109
|
+
*
|
|
9110
|
+
* `unavailable` leaves everything in place: the project could not be reached
|
|
9111
|
+
* to decide, the socket rejoins on its next reconnect, and dropping the app's
|
|
9112
|
+
* handlers would make a momentary outage look like a permanent denial.
|
|
9113
|
+
*/
|
|
9114
|
+
handleChannelRefused(topic, reason) {
|
|
9115
|
+
const retryable = RETRYABLE_REASONS.has(reason);
|
|
9116
|
+
for (const entry of this.errorHandlers) {
|
|
9117
|
+
if (entry.topic === topic) entry.handler({ topic, reason, retryable });
|
|
9118
|
+
}
|
|
9119
|
+
if (retryable) return;
|
|
9120
|
+
this.forgetTopic(topic);
|
|
9121
|
+
}
|
|
9122
|
+
/**
|
|
9123
|
+
* A channel died for good — a token-expiry rejoin that exhausted its attempts.
|
|
9124
|
+
* The app must stop expecting anything on it, and the status observable flips
|
|
9125
|
+
* to `'error'` (the React `useChannel` hook reports that).
|
|
9126
|
+
*
|
|
9127
|
+
* It clears the SAME registries a final refusal clears, and used to clear only
|
|
9128
|
+
* the event handlers. A state or presence watcher left behind here waits
|
|
9129
|
+
* forever for a snapshot that cannot arrive, and fires twice if the app ever
|
|
9130
|
+
* re-subscribes — the identical failure its sibling three functions up
|
|
9131
|
+
* describes, on a channel that was given up on rather than refused.
|
|
8815
9132
|
*/
|
|
8816
9133
|
handleChannelError(topic) {
|
|
9134
|
+
this.forgetTopic(topic);
|
|
9135
|
+
this.statusStore.setState("error");
|
|
9136
|
+
}
|
|
9137
|
+
/** Drop every registration this topic holds. One place, because "which
|
|
9138
|
+
* registries" is a question two callers must never answer differently. */
|
|
9139
|
+
forgetTopic(topic) {
|
|
8817
9140
|
for (const entry of this.handlers) {
|
|
8818
9141
|
if (entry.topic === topic) this.handlers.delete(entry);
|
|
8819
9142
|
}
|
|
9143
|
+
for (const entry of this.stateWatchers) {
|
|
9144
|
+
if (entry.topic === topic) this.stateWatchers.delete(entry);
|
|
9145
|
+
}
|
|
9146
|
+
for (const entry of this.presenceWatchers) {
|
|
9147
|
+
if (entry.topic === topic) this.presenceWatchers.delete(entry);
|
|
9148
|
+
}
|
|
9149
|
+
this.pendingPresence.delete(topic);
|
|
9150
|
+
this.states.delete(topic);
|
|
9151
|
+
this.connIds.delete(topic);
|
|
8820
9152
|
this.topicRefcount.delete(topic);
|
|
8821
|
-
|
|
9153
|
+
}
|
|
9154
|
+
// ── state ──────────────────────────────────────────────────────────────────
|
|
9155
|
+
/** @internal */
|
|
9156
|
+
stateOf(topic) {
|
|
9157
|
+
let st = this.states.get(topic);
|
|
9158
|
+
if (!st) {
|
|
9159
|
+
st = new ChannelState();
|
|
9160
|
+
this.states.set(topic, st);
|
|
9161
|
+
}
|
|
9162
|
+
return st;
|
|
9163
|
+
}
|
|
9164
|
+
/** @internal */
|
|
9165
|
+
subscribeState(topic, key, handler) {
|
|
9166
|
+
const socket = this.ensureSocket();
|
|
9167
|
+
const entry = { topic, key, handler };
|
|
9168
|
+
this.stateWatchers.add(entry);
|
|
9169
|
+
this.retainTopic(topic, socket);
|
|
9170
|
+
const known = this.stateOf(topic).entries.get(key);
|
|
9171
|
+
if (known !== void 0) handler(known);
|
|
9172
|
+
let cancelled = false;
|
|
9173
|
+
return {
|
|
9174
|
+
cancel: () => {
|
|
9175
|
+
if (cancelled) return;
|
|
9176
|
+
cancelled = true;
|
|
9177
|
+
this.stateWatchers.delete(entry);
|
|
9178
|
+
this.releaseTopic(topic, socket);
|
|
9179
|
+
}
|
|
9180
|
+
};
|
|
9181
|
+
}
|
|
9182
|
+
/** @internal */
|
|
9183
|
+
subscribePresence(topic, handler) {
|
|
9184
|
+
const socket = this.ensureSocket();
|
|
9185
|
+
const entry = { topic, handler };
|
|
9186
|
+
this.presenceWatchers.add(entry);
|
|
9187
|
+
this.retainTopic(topic, socket);
|
|
9188
|
+
handler(this.presenceOthers(topic));
|
|
9189
|
+
let cancelled = false;
|
|
9190
|
+
return {
|
|
9191
|
+
cancel: () => {
|
|
9192
|
+
if (cancelled) return;
|
|
9193
|
+
cancelled = true;
|
|
9194
|
+
this.presenceWatchers.delete(entry);
|
|
9195
|
+
this.releaseTopic(topic, socket);
|
|
9196
|
+
}
|
|
9197
|
+
};
|
|
9198
|
+
}
|
|
9199
|
+
/** @internal */
|
|
9200
|
+
writeState(topic, key, value, life) {
|
|
9201
|
+
const socket = this.ensureSocket();
|
|
9202
|
+
socket.joinTopic(topic);
|
|
9203
|
+
socket.sendStateSet(topic, key, value, life);
|
|
9204
|
+
}
|
|
9205
|
+
/** @internal */
|
|
9206
|
+
clearState(topic, key) {
|
|
9207
|
+
this.ensureSocket().sendStateDel(topic, key);
|
|
9208
|
+
}
|
|
9209
|
+
/** @internal */
|
|
9210
|
+
presenceWrite(topic, meta) {
|
|
9211
|
+
const socket = this.ensureSocket();
|
|
9212
|
+
socket.joinTopic(topic);
|
|
9213
|
+
const conn = this.connIds.get(topic);
|
|
9214
|
+
if (conn === void 0) {
|
|
9215
|
+
this.pendingPresence.set(topic, meta);
|
|
9216
|
+
return;
|
|
9217
|
+
}
|
|
9218
|
+
socket.sendStateSet(topic, PRESENCE_KEY(conn), meta, "ephemeral");
|
|
9219
|
+
}
|
|
9220
|
+
/** @internal */
|
|
9221
|
+
presenceClear(topic) {
|
|
9222
|
+
this.pendingPresence.delete(topic);
|
|
9223
|
+
const conn = this.connIds.get(topic);
|
|
9224
|
+
if (conn === void 0) return;
|
|
9225
|
+
this.ensureSocket().sendStateDel(topic, PRESENCE_KEY(conn));
|
|
9226
|
+
}
|
|
9227
|
+
/** @internal */
|
|
9228
|
+
presenceOthers(topic) {
|
|
9229
|
+
return this.stateOf(topic).presence(this.connIds.get(topic));
|
|
9230
|
+
}
|
|
9231
|
+
retainTopic(topic, socket) {
|
|
9232
|
+
const count = (this.topicRefcount.get(topic) ?? 0) + 1;
|
|
9233
|
+
this.topicRefcount.set(topic, count);
|
|
9234
|
+
if (count === 1) socket.joinTopic(topic);
|
|
9235
|
+
}
|
|
9236
|
+
releaseTopic(topic, socket) {
|
|
9237
|
+
const next = (this.topicRefcount.get(topic) ?? 1) - 1;
|
|
9238
|
+
if (next <= 0) {
|
|
9239
|
+
this.topicRefcount.delete(topic);
|
|
9240
|
+
socket.leaveTopic(topic);
|
|
9241
|
+
return;
|
|
9242
|
+
}
|
|
9243
|
+
this.topicRefcount.set(topic, next);
|
|
9244
|
+
}
|
|
9245
|
+
applySnapshot(topic, body) {
|
|
9246
|
+
this.stateOf(topic).applySnapshot(body);
|
|
9247
|
+
this.notifyState(topic);
|
|
9248
|
+
}
|
|
9249
|
+
applyDiff(topic, body) {
|
|
9250
|
+
if (this.stateOf(topic).applyDiff(body) === "gap") {
|
|
9251
|
+
this.socket?.sendStateResync(topic);
|
|
9252
|
+
return;
|
|
9253
|
+
}
|
|
9254
|
+
this.notifyState(topic);
|
|
9255
|
+
}
|
|
9256
|
+
applyJoinInfo(topic, info) {
|
|
9257
|
+
if (typeof info.conn !== "string" || info.conn === "") return;
|
|
9258
|
+
this.connIds.set(topic, info.conn);
|
|
9259
|
+
const seq = info.state_seq;
|
|
9260
|
+
if (typeof seq === "number" && seq < this.stateOf(topic).seq) {
|
|
9261
|
+
this.stateOf(topic).applySnapshot({ seq, entries: {} });
|
|
9262
|
+
}
|
|
9263
|
+
const held = this.pendingPresence.get(topic);
|
|
9264
|
+
if (held !== void 0) {
|
|
9265
|
+
this.pendingPresence.delete(topic);
|
|
9266
|
+
this.socket?.sendStateSet(topic, PRESENCE_KEY(info.conn), held, "ephemeral");
|
|
9267
|
+
}
|
|
9268
|
+
this.notifyState(topic);
|
|
9269
|
+
}
|
|
9270
|
+
notifyState(topic) {
|
|
9271
|
+
this.statusStore.recordEvent(/* @__PURE__ */ new Date());
|
|
9272
|
+
const st = this.stateOf(topic);
|
|
9273
|
+
for (const w of this.stateWatchers) {
|
|
9274
|
+
if (w.topic === topic) w.handler(st.entries.get(w.key));
|
|
9275
|
+
}
|
|
9276
|
+
if (this.presenceWatchers.size > 0) {
|
|
9277
|
+
const others = this.presenceOthers(topic);
|
|
9278
|
+
for (const w of this.presenceWatchers) {
|
|
9279
|
+
if (w.topic === topic) w.handler(others);
|
|
9280
|
+
}
|
|
9281
|
+
}
|
|
8822
9282
|
}
|
|
8823
9283
|
};
|
|
9284
|
+
var PRESENCE_KEY = (conn) => `$p:${conn}`;
|
|
8824
9285
|
|
|
8825
9286
|
// src/storage.ts
|
|
8826
9287
|
function memorySessionStorage() {
|
|
@@ -8881,7 +9342,7 @@ function defaultSessionStorage(key) {
|
|
|
8881
9342
|
}
|
|
8882
9343
|
|
|
8883
9344
|
// src/version.ts
|
|
8884
|
-
var VERSION = "7.
|
|
9345
|
+
var VERSION = "7.4.0";
|
|
8885
9346
|
|
|
8886
9347
|
// src/runtime.ts
|
|
8887
9348
|
function buildRuntime(config) {
|
|
@@ -9059,6 +9520,7 @@ async function callEndpoint(resolveRt, name, input, options) {
|
|
|
9059
9520
|
}
|
|
9060
9521
|
|
|
9061
9522
|
// src/upload.ts
|
|
9523
|
+
var UPLOAD_PATH = "/v1/storage/upload";
|
|
9062
9524
|
function abortError() {
|
|
9063
9525
|
return new BackendError("network", { code: "aborted", message: "Upload aborted" });
|
|
9064
9526
|
}
|
|
@@ -9112,7 +9574,9 @@ async function buildHeaders(rt, extra) {
|
|
|
9112
9574
|
}
|
|
9113
9575
|
function buildForm(options) {
|
|
9114
9576
|
const form = new FormData();
|
|
9115
|
-
|
|
9577
|
+
const { body, ...loose } = options.fields ?? {};
|
|
9578
|
+
if (body !== void 0) form.append("body", body);
|
|
9579
|
+
else if (Object.keys(loose).length > 0) form.append("body", JSON.stringify(loose));
|
|
9116
9580
|
const file = options.contentType && options.file.type !== options.contentType ? new Blob([options.file], { type: options.contentType }) : options.file;
|
|
9117
9581
|
const filename = options.filename ?? (typeof File !== "undefined" && options.file instanceof File ? options.file.name : "file");
|
|
9118
9582
|
form.append("file", file, filename);
|
|
@@ -9186,8 +9650,8 @@ async function uploadViaFetch(url, form, headers, options) {
|
|
|
9186
9650
|
async function uploadEndpoint(resolveRt, name, options) {
|
|
9187
9651
|
const rt = resolveRt();
|
|
9188
9652
|
checkConstraints(options);
|
|
9189
|
-
const
|
|
9190
|
-
const url = `${rt.config.url}${
|
|
9653
|
+
const route = name.startsWith("/") ? name : `/${name}`;
|
|
9654
|
+
const url = `${rt.config.url}${UPLOAD_PATH}${route}`;
|
|
9191
9655
|
const headers = await buildHeaders(rt, options.headers);
|
|
9192
9656
|
const form = buildForm(options);
|
|
9193
9657
|
const useXHR = options.onProgress !== void 0 && typeof XMLHttpRequest !== "undefined";
|
|
@@ -9301,4 +9765,4 @@ export {
|
|
|
9301
9765
|
pb,
|
|
9302
9766
|
createBoundClient
|
|
9303
9767
|
};
|
|
9304
|
-
//# sourceMappingURL=chunk-
|
|
9768
|
+
//# sourceMappingURL=chunk-WXBOPPHU.js.map
|