@palbase/web 1.6.2 → 1.8.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-DhcTP_kw.d.ts → analytics-facade-CgURkjpP.d.ts} +236 -2
- package/dist/{analytics-facade-ATGUv2-f.d.cts → analytics-facade-DfJ420F5.d.cts} +236 -2
- package/dist/{chunk-XQZ53URR.js → chunk-AWVNDAMG.js} +690 -18
- package/dist/chunk-AWVNDAMG.js.map +1 -0
- package/dist/index.cjs +158 -14
- 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 +1 -1
- package/dist/internal.cjs +689 -17
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +3 -3
- package/dist/internal.d.ts +3 -3
- package/dist/internal.js +1 -1
- package/dist/next/client.cjs +679 -14
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +1 -1
- package/dist/next/index.cjs +685 -14
- 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 +1 -1
- package/dist/{pb-VzJvX7Gg.d.cts → pb-BwQwp411.d.cts} +9 -1
- package/dist/{pb-TAUNVyT6.d.ts → pb-C1v9ErPy.d.ts} +9 -1
- package/dist/react/index.cjs +69 -13
- 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 +1 -1
- package/package.json +3 -3
- package/dist/chunk-XQZ53URR.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -161,14 +161,47 @@ function unwrap(res) {
|
|
|
161
161
|
return res.data;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
// src/perf/url-redactor.ts
|
|
165
|
+
var ID_SEGMENT = /^(?:\d+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;
|
|
166
|
+
var EMAIL_SEGMENT = /(?:@|%40)/i;
|
|
167
|
+
function isSensitiveSegment(seg2) {
|
|
168
|
+
return ID_SEGMENT.test(seg2) || EMAIL_SEGMENT.test(seg2);
|
|
169
|
+
}
|
|
170
|
+
function redactUrl(rawUrl) {
|
|
171
|
+
let path = rawUrl;
|
|
172
|
+
try {
|
|
173
|
+
path = new URL(rawUrl).pathname;
|
|
174
|
+
} catch {
|
|
175
|
+
const q = path.indexOf("?");
|
|
176
|
+
if (q >= 0) path = path.slice(0, q);
|
|
177
|
+
}
|
|
178
|
+
return path.split("/").map((seg2) => isSensitiveSegment(seg2) ? ":id" : seg2).join("/");
|
|
179
|
+
}
|
|
180
|
+
|
|
164
181
|
// src/request.ts
|
|
165
182
|
var MUTATING = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
183
|
+
var PERF_EXCLUDED_PREFIX = "/v1/analytics/";
|
|
184
|
+
function isSelfTraced(path) {
|
|
185
|
+
return path.startsWith(PERF_EXCLUDED_PREFIX);
|
|
186
|
+
}
|
|
187
|
+
function nowMs() {
|
|
188
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
189
|
+
}
|
|
190
|
+
function isAbort(e) {
|
|
191
|
+
return e instanceof Error && e.name === "AbortError";
|
|
192
|
+
}
|
|
166
193
|
async function palbeRequest(rt, method, path, spec = {}) {
|
|
167
194
|
const headers = { ...spec.headers };
|
|
168
195
|
const callerHasKey = Object.keys(headers).some((k) => k.toLowerCase() === "idempotency-key");
|
|
169
196
|
if (MUTATING.has(method) && !callerHasKey) {
|
|
170
197
|
headers["Idempotency-Key"] = crypto.randomUUID();
|
|
171
198
|
}
|
|
199
|
+
if (rt.appIdentifier !== "") {
|
|
200
|
+
const callerHasBundle = Object.keys(headers).some(
|
|
201
|
+
(k) => k.toLowerCase() === "x-palbase-bundle"
|
|
202
|
+
);
|
|
203
|
+
if (!callerHasBundle) headers["X-Palbase-Bundle"] = rt.appIdentifier;
|
|
204
|
+
}
|
|
172
205
|
const attempt = async () => {
|
|
173
206
|
try {
|
|
174
207
|
return await rt.http.request(method, path, {
|
|
@@ -181,21 +214,38 @@ async function palbeRequest(rt, method, path, spec = {}) {
|
|
|
181
214
|
throw pe ? fromPalbaseError(pe) : e;
|
|
182
215
|
}
|
|
183
216
|
};
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
217
|
+
const traced = !isSelfTraced(path) && rt.perf !== void 0;
|
|
218
|
+
const startedAt = traced ? nowMs() : 0;
|
|
219
|
+
let recorded = false;
|
|
220
|
+
const record = (status) => {
|
|
221
|
+
if (!traced || recorded) return;
|
|
222
|
+
recorded = true;
|
|
223
|
+
rt.perf.recordNetwork(method, redactUrl(path), status, nowMs() - startedAt);
|
|
224
|
+
};
|
|
225
|
+
let res;
|
|
226
|
+
try {
|
|
227
|
+
res = await attempt();
|
|
228
|
+
if (res.error?.status === 401 && rt.tokenManager.getRefreshToken() && rt.tokenManager.refreshFunction) {
|
|
229
|
+
try {
|
|
230
|
+
await rt.tokenManager.refreshSession();
|
|
231
|
+
} catch (refreshErr) {
|
|
232
|
+
const pe = asPalbaseError(refreshErr);
|
|
233
|
+
const status = pe?.status ?? 0;
|
|
234
|
+
if (status === 400 || status === 401 || status === 403) {
|
|
235
|
+
rt.tokenManager.clearSession();
|
|
236
|
+
record(res.error.status);
|
|
237
|
+
throw fromPalbaseError(res.error);
|
|
238
|
+
}
|
|
239
|
+
record(pe?.status ?? 0);
|
|
240
|
+
throw pe ? fromPalbaseError(pe) : refreshErr;
|
|
194
241
|
}
|
|
195
|
-
|
|
242
|
+
res = await attempt();
|
|
196
243
|
}
|
|
197
|
-
|
|
244
|
+
} catch (e) {
|
|
245
|
+
if (!isAbort(e)) record(asPalbaseError(e)?.status ?? 0);
|
|
246
|
+
throw e;
|
|
198
247
|
}
|
|
248
|
+
record(res.error?.status ?? 200);
|
|
199
249
|
return unwrap(res);
|
|
200
250
|
}
|
|
201
251
|
|
|
@@ -1900,6 +1950,10 @@ var MessagingPaths = {
|
|
|
1900
1950
|
groupMessages: (displayId) => `${GROUPS}/${seg(displayId)}/messages`,
|
|
1901
1951
|
groupCommits: (displayId) => `${GROUPS}/${seg(displayId)}/commits`,
|
|
1902
1952
|
groupRead: (displayId) => `${GROUPS}/${seg(displayId)}/read`,
|
|
1953
|
+
// Server-metadata cluster: the per-(user,group) notify scope (mute toggle, GET/PUT)
|
|
1954
|
+
// + the caller's own unread view (GET). {gid} is the grp_ display_id (M3 #7).
|
|
1955
|
+
groupNotify: (displayId) => `${GROUPS}/${seg(displayId)}/notify`,
|
|
1956
|
+
groupUnread: (displayId) => `${GROUPS}/${seg(displayId)}/unread`,
|
|
1903
1957
|
deviceWelcomes: (deviceId) => `${DEVICES}/${seg(deviceId)}/welcomes`,
|
|
1904
1958
|
deviceQueue: (deviceId) => `${DEVICES}/${seg(deviceId)}/queue`,
|
|
1905
1959
|
deviceQueueAck: (deviceId) => `${DEVICES}/${seg(deviceId)}/queue/ack`,
|
|
@@ -3478,6 +3532,18 @@ var Chat = class {
|
|
|
3478
3532
|
lastSeenAt: ts ? new Date(ts * 1e3) : null
|
|
3479
3533
|
});
|
|
3480
3534
|
this.emit();
|
|
3535
|
+
} else if (event === "delivered" || event === "read") {
|
|
3536
|
+
const seq = typeof payload.up_to_server_seq === "number" ? payload.up_to_server_seq : null;
|
|
3537
|
+
if (seq === null) return;
|
|
3538
|
+
let changed = false;
|
|
3539
|
+
this.messageList = this.messageList.map((m) => {
|
|
3540
|
+
if (m.direction !== "outgoing" || m.serverSeq > seq) return m;
|
|
3541
|
+
const cur = event === "delivered" ? m.deliveredUpTo ?? -1 : m.readUpTo ?? -1;
|
|
3542
|
+
if (seq <= cur) return m;
|
|
3543
|
+
changed = true;
|
|
3544
|
+
return event === "delivered" ? { ...m, deliveredUpTo: seq } : { ...m, readUpTo: seq };
|
|
3545
|
+
});
|
|
3546
|
+
if (changed) this.emit();
|
|
3481
3547
|
}
|
|
3482
3548
|
}
|
|
3483
3549
|
kindOf(incoming) {
|
|
@@ -3726,6 +3792,31 @@ var Chat = class {
|
|
|
3726
3792
|
this.readWatermark = Math.max(this.readWatermark, message.serverSeq);
|
|
3727
3793
|
this.emit();
|
|
3728
3794
|
}
|
|
3795
|
+
// ── Server-metadata cluster: notify scope (mute) + server unread ──
|
|
3796
|
+
/** Set this chat's notify scope (the mute toggle). `'none'` mutes the push wake;
|
|
3797
|
+
* `'all'` unmutes (the default). Materializes a draft first (the scope is a
|
|
3798
|
+
* per-(user,group) server row), PUTs `/notify`, and returns the server-echoed scope.
|
|
3799
|
+
* Mirrors iOS `Chat.setNotifyScope`. */
|
|
3800
|
+
async setNotifyScope(scope) {
|
|
3801
|
+
const group = await this.materializeIfNeeded();
|
|
3802
|
+
return this.backend.setNotifyScope(group, scope);
|
|
3803
|
+
}
|
|
3804
|
+
/** Refresh + return this chat's notify scope from the server (fail-OPEN to `'all'`).
|
|
3805
|
+
* Returns `'all'` for a draft chat (no server row yet). */
|
|
3806
|
+
async getNotifyScope() {
|
|
3807
|
+
if (!this._group) return "all";
|
|
3808
|
+
return this.backend.getNotifyScope(this._group);
|
|
3809
|
+
}
|
|
3810
|
+
/** Fetch the caller's authoritative SERVER unread count (opaque server metadata).
|
|
3811
|
+
* Returns the clamped count (`max(0, …)`); `0` for a draft chat. The local computed
|
|
3812
|
+
* `unreadCount` getter stays the instant, offline best-effort badge — this is the
|
|
3813
|
+
* canonical count on demand. Named distinctly so it does not shadow the observable
|
|
3814
|
+
* `unreadCount` snapshot getter. Mirrors iOS `Chat.refreshUnread`. */
|
|
3815
|
+
async unreadCountFromServer() {
|
|
3816
|
+
if (!this._group) return 0;
|
|
3817
|
+
const v = await this.backend.unread(this._group);
|
|
3818
|
+
return Math.max(0, v.unreadCount);
|
|
3819
|
+
}
|
|
3729
3820
|
// ── Reactions ──
|
|
3730
3821
|
/** Add an emoji reaction to a message. No-op if the message isn't reactable
|
|
3731
3822
|
* (empty clientMsgId — a legacy/system row). The reaction folds locally with
|
|
@@ -4164,7 +4255,7 @@ var MessageDeliverySource = class {
|
|
|
4164
4255
|
if (this.observed.has(group.displayId)) return;
|
|
4165
4256
|
try {
|
|
4166
4257
|
const channel = this.rt.realtime.channel(`messaging:conv:${group.rfcGroupId}`);
|
|
4167
|
-
const subs = ["presence", "typing", "read"].map(
|
|
4258
|
+
const subs = ["presence", "typing", "read", "delivered"].map(
|
|
4168
4259
|
(ev) => channel.on(ev, (payload) => {
|
|
4169
4260
|
this.hub.emitConv(group.displayId, { event: ev, payload });
|
|
4170
4261
|
})
|
|
@@ -4204,6 +4295,40 @@ var MessageDeliverySource = class {
|
|
|
4204
4295
|
body: { read_seq: upToServerSeq, read_epoch: group.currentEpoch, is_private: false }
|
|
4205
4296
|
});
|
|
4206
4297
|
}
|
|
4298
|
+
// ── Server-metadata cluster: notify scope (mute) + unread (HTTP) ──
|
|
4299
|
+
/** PUT `/v1/messaging/groups/{gid}/notify` — set the caller's per-(user,group) notify
|
|
4300
|
+
* scope (`'all'`|`'none'`). The server stores the opaque enum verbatim and stays blind.
|
|
4301
|
+
* Returns the server-echoed scope (fail-OPEN to `'all'` on an unknown value). */
|
|
4302
|
+
async setNotifyScope(group, scope) {
|
|
4303
|
+
const res = await palbeRequest(
|
|
4304
|
+
this.rt,
|
|
4305
|
+
"PUT",
|
|
4306
|
+
MessagingPaths.groupNotify(group.displayId),
|
|
4307
|
+
{ body: { notify_scope: scope } }
|
|
4308
|
+
);
|
|
4309
|
+
return res.notify_scope === "none" ? "none" : "all";
|
|
4310
|
+
}
|
|
4311
|
+
/** GET `/v1/messaging/groups/{gid}/notify` — the caller's notify scope. An absent
|
|
4312
|
+
* server row / unknown value reads as `'all'` (fail-OPEN — never silently mutes). */
|
|
4313
|
+
async getNotifyScope(group) {
|
|
4314
|
+
const res = await palbeRequest(
|
|
4315
|
+
this.rt,
|
|
4316
|
+
"GET",
|
|
4317
|
+
MessagingPaths.groupNotify(group.displayId)
|
|
4318
|
+
);
|
|
4319
|
+
return res.notify_scope === "none" ? "none" : "all";
|
|
4320
|
+
}
|
|
4321
|
+
/** GET `/v1/messaging/groups/{gid}/unread` — the caller's OWN unread view (opaque
|
|
4322
|
+
* server metadata). Maps the snake_case wire → the camelCase `UnreadView`. */
|
|
4323
|
+
async unread(group) {
|
|
4324
|
+
const res = await palbeRequest(this.rt, "GET", MessagingPaths.groupUnread(group.displayId));
|
|
4325
|
+
return {
|
|
4326
|
+
groupMaxSeq: res.group_max_seq,
|
|
4327
|
+
lastReadSeq: res.last_read_seq,
|
|
4328
|
+
deliveredSeq: res.delivered_seq,
|
|
4329
|
+
unreadCount: res.unread_count
|
|
4330
|
+
};
|
|
4331
|
+
}
|
|
4207
4332
|
};
|
|
4208
4333
|
function isOwnEchoOrConsumed(e) {
|
|
4209
4334
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -6361,6 +6486,19 @@ var MessagingCoordinator = class {
|
|
|
6361
6486
|
const r = await this.resolve();
|
|
6362
6487
|
await r.source.markRead(group, upToServerSeq);
|
|
6363
6488
|
}
|
|
6489
|
+
// ── Server-metadata cluster: notify scope (mute) + unread ──
|
|
6490
|
+
async setNotifyScope(group, scope) {
|
|
6491
|
+
const r = await this.resolve();
|
|
6492
|
+
return r.source.setNotifyScope(group, scope);
|
|
6493
|
+
}
|
|
6494
|
+
async getNotifyScope(group) {
|
|
6495
|
+
const r = await this.resolve();
|
|
6496
|
+
return r.source.getNotifyScope(group);
|
|
6497
|
+
}
|
|
6498
|
+
async unread(group) {
|
|
6499
|
+
const r = await this.resolve();
|
|
6500
|
+
return r.source.unread(group);
|
|
6501
|
+
}
|
|
6364
6502
|
subscribeLive(group, chat) {
|
|
6365
6503
|
let offMsg = null;
|
|
6366
6504
|
let offConv = null;
|
|
@@ -7419,7 +7557,7 @@ function localStorageSessionStorage(key = DEFAULT_KEY) {
|
|
|
7419
7557
|
}
|
|
7420
7558
|
|
|
7421
7559
|
// src/version.ts
|
|
7422
|
-
var VERSION = "1.
|
|
7560
|
+
var VERSION = "1.8.0";
|
|
7423
7561
|
|
|
7424
7562
|
// src/internal.ts
|
|
7425
7563
|
function getRuntime() {
|
|
@@ -7588,6 +7726,12 @@ function createClientProxy(resolveRt, nsAccessor) {
|
|
|
7588
7726
|
},
|
|
7589
7727
|
get messaging() {
|
|
7590
7728
|
return resolveRt().messaging;
|
|
7729
|
+
},
|
|
7730
|
+
get perf() {
|
|
7731
|
+
return resolveRt().perf;
|
|
7732
|
+
},
|
|
7733
|
+
setTestDevice(on) {
|
|
7734
|
+
resolveRt().perf.setTestDevice(on);
|
|
7591
7735
|
}
|
|
7592
7736
|
};
|
|
7593
7737
|
return new Proxy(base, {
|