@palbase/web 1.1.1 → 1.2.1

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.
@@ -7,7 +7,7 @@ import {
7
7
  __configure,
8
8
  endpointRefFromApiKey,
9
9
  getRuntime
10
- } from "../chunk-EMQGOKW6.js";
10
+ } from "../chunk-KJXRY4S3.js";
11
11
 
12
12
  // src/next/client.ts
13
13
  var SESSION_MAX_AGE_S = 2592e3;
@@ -2767,6 +2767,18 @@ async function listDevices(rt, userId) {
2767
2767
  }
2768
2768
 
2769
2769
  // src/messaging/group-messaging.ts
2770
+ function encodeReaction(args) {
2771
+ return encodeUtf8(
2772
+ JSON.stringify({
2773
+ v: 1,
2774
+ type: "reaction",
2775
+ client_msg_id: args.clientMsgId,
2776
+ target_client_msg_id: args.targetClientMsgId,
2777
+ emoji: args.emoji,
2778
+ op: args.op
2779
+ })
2780
+ );
2781
+ }
2770
2782
  function encodeEnvelope(args) {
2771
2783
  const env = {
2772
2784
  v: 1,
@@ -2781,18 +2793,32 @@ function decodeEnvelope(bytes) {
2781
2793
  const s = decodeUtf8(bytes);
2782
2794
  try {
2783
2795
  const o = JSON.parse(s);
2796
+ if (typeof o === "object" && o !== null && o.type === "reaction") {
2797
+ return {
2798
+ type: "reaction",
2799
+ text: null,
2800
+ clientMsgId: o.client_msg_id ?? "",
2801
+ replyTo: null,
2802
+ reaction: {
2803
+ targetClientMsgId: o.target_client_msg_id ?? "",
2804
+ emoji: o.emoji ?? "",
2805
+ op: o.op ?? "add"
2806
+ }
2807
+ };
2808
+ }
2784
2809
  if (typeof o === "object" && o !== null && o.type === "text" && typeof o.v === "number") {
2785
2810
  return {
2811
+ type: "text",
2786
2812
  text: o.text ?? null,
2787
2813
  clientMsgId: o.client_msg_id ?? "",
2788
2814
  replyTo: o.reply_to ?? null
2789
2815
  };
2790
2816
  }
2791
2817
  if (typeof o === "object" && o !== null && o.type === "text") {
2792
- return { text: o.text ?? null, clientMsgId: "", replyTo: null };
2818
+ return { type: "text", text: o.text ?? null, clientMsgId: "", replyTo: null };
2793
2819
  }
2794
2820
  if (typeof o === "object" && o !== null && o.type === "media")
2795
- return { text: null, clientMsgId: "", replyTo: null };
2821
+ return { type: "media", text: null, clientMsgId: "", replyTo: null };
2796
2822
  } catch {
2797
2823
  }
2798
2824
  return { text: s, clientMsgId: "", replyTo: null };
@@ -3044,6 +3070,56 @@ var GroupMessaging = class {
3044
3070
  }
3045
3071
  return { receipt: { serverSeq: wire.server_seq, epoch: wire.epoch }, clientMsgId };
3046
3072
  }
3073
+ /** Send a reaction (add/remove of an emoji on a target message). Encrypts a
3074
+ * `type:'reaction'` envelope at the current epoch and sends through the SAME
3075
+ * MLS application path as `sendText` (the server stays blind — a reaction is
3076
+ * just another application message). Persists the outgoing reaction row so it
3077
+ * re-folds onto its target after a reload (the own-send half of the reload
3078
+ * parity). NEVER rebases (epoch-bound like any application message). */
3079
+ async sendReaction(group, args) {
3080
+ const plaintext = encodeReaction({
3081
+ clientMsgId: args.clientMsgId,
3082
+ targetClientMsgId: args.targetClientMsgId,
3083
+ emoji: args.emoji,
3084
+ op: args.op
3085
+ });
3086
+ const ct = await this.engine.encryptApplication(fromBase64(group.rfcGroupId), plaintext);
3087
+ const body = {
3088
+ ciphertext_b64: toBase64(ct),
3089
+ client_idem_key: randomId()
3090
+ };
3091
+ const wire = await palbeRequest(
3092
+ this.rt,
3093
+ "POST",
3094
+ MessagingPaths.groupMessages(group.displayId),
3095
+ { body }
3096
+ );
3097
+ const stored = {
3098
+ id: `${group.rfcGroupId}#${wire.server_seq}`,
3099
+ direction: "outgoing",
3100
+ text: null,
3101
+ senderDeviceId: this.selfDeviceId,
3102
+ epoch: wire.epoch,
3103
+ serverSeq: wire.server_seq,
3104
+ at: Date.now(),
3105
+ clientMsgId: args.clientMsgId,
3106
+ replyTo: null,
3107
+ envelopeType: "reaction",
3108
+ reaction: {
3109
+ targetClientMsgId: args.targetClientMsgId,
3110
+ emoji: args.emoji,
3111
+ op: args.op
3112
+ }
3113
+ };
3114
+ try {
3115
+ await this.messageStore.append(group.rfcGroupId, stored);
3116
+ } catch {
3117
+ }
3118
+ return {
3119
+ receipt: { serverSeq: wire.server_seq, epoch: wire.epoch },
3120
+ clientMsgId: args.clientMsgId
3121
+ };
3122
+ }
3047
3123
  // ── The rebase loop ──
3048
3124
  async commitWithRebase(rfcGroupId, build) {
3049
3125
  const gidBytes = fromBase64(rfcGroupId);
@@ -3105,6 +3181,61 @@ var GroupMessaging = class {
3105
3181
  }
3106
3182
  };
3107
3183
 
3184
+ // src/messaging/reaction-fold.ts
3185
+ function orderLte(aEpoch, aSeq, bEpoch, bSeq) {
3186
+ if (aEpoch !== bEpoch) return aEpoch < bEpoch;
3187
+ return aSeq <= bSeq;
3188
+ }
3189
+ var ReactionFold = class {
3190
+ // Map<targetClientMsgId, Map<actorUserId, UserState>>
3191
+ states = /* @__PURE__ */ new Map();
3192
+ // Dedup set: eventClientMsgId values already applied.
3193
+ seenEvents = /* @__PURE__ */ new Set();
3194
+ ingest(e) {
3195
+ if (this.seenEvents.has(e.eventClientMsgId)) return;
3196
+ this.seenEvents.add(e.eventClientMsgId);
3197
+ let byUser = this.states.get(e.targetClientMsgId);
3198
+ if (byUser === void 0) {
3199
+ byUser = /* @__PURE__ */ new Map();
3200
+ this.states.set(e.targetClientMsgId, byUser);
3201
+ }
3202
+ const prev = byUser.get(e.actorUserId);
3203
+ if (prev !== void 0) {
3204
+ if (orderLte(e.epoch, e.serverSeq, prev.orderEpoch, prev.orderSeq)) return;
3205
+ }
3206
+ byUser.set(e.actorUserId, {
3207
+ orderEpoch: e.epoch,
3208
+ orderSeq: e.serverSeq,
3209
+ emoji: e.op === "add" ? e.emoji : null
3210
+ });
3211
+ }
3212
+ /**
3213
+ * Returns a tally of `{ emoji → sorted userIds[] }` for the given target
3214
+ * message. Users whose last event was a `remove` (emoji === null) are
3215
+ * excluded. userId arrays are sorted for deterministic output (render
3216
+ * stability + golden tests).
3217
+ */
3218
+ tally(targetClientMsgId) {
3219
+ const byUser = this.states.get(targetClientMsgId);
3220
+ if (byUser === void 0) return {};
3221
+ const out = /* @__PURE__ */ new Map();
3222
+ for (const [userId, st] of byUser) {
3223
+ if (st.emoji === null) continue;
3224
+ let users = out.get(st.emoji);
3225
+ if (users === void 0) {
3226
+ users = [];
3227
+ out.set(st.emoji, users);
3228
+ }
3229
+ users.push(userId);
3230
+ }
3231
+ const result = {};
3232
+ for (const [emoji, users] of out) {
3233
+ result[emoji] = users.sort();
3234
+ }
3235
+ return result;
3236
+ }
3237
+ };
3238
+
3108
3239
  // src/messaging/chat.ts
3109
3240
  var Chat = class {
3110
3241
  /** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
@@ -3123,6 +3254,8 @@ var Chat = class {
3123
3254
  seenKeys = /* @__PURE__ */ new Set();
3124
3255
  /** Index: clientMsgId → { text, senderUserId } for resolveReply lookups. */
3125
3256
  byClientMsgId = /* @__PURE__ */ new Map();
3257
+ /** The single authoritative reaction fold for this chat (live + own-send + history). */
3258
+ reactionFold = new ReactionFold();
3126
3259
  loadedEarliestSeq = null;
3127
3260
  historyLoaded = false;
3128
3261
  wired = false;
@@ -3229,7 +3362,7 @@ var Chat = class {
3229
3362
  const key = this.internalKey(m.serverSeq);
3230
3363
  if (this.seenKeys.has(key)) continue;
3231
3364
  this.seenKeys.add(key);
3232
- this.messageList.push(m);
3365
+ this.messageList.push(this.applyReactionTally(m));
3233
3366
  changed = true;
3234
3367
  this.loadedEarliestSeq = Math.min(this.loadedEarliestSeq ?? m.serverSeq, m.serverSeq);
3235
3368
  }
@@ -3254,6 +3387,22 @@ var Chat = class {
3254
3387
  senderUser = await this.backend.userIdForDevice(this._group, incoming.senderDeviceId);
3255
3388
  }
3256
3389
  const direction = senderUser !== null && senderUser === this.backend.selfUserId ? "outgoing" : "incoming";
3390
+ if (incoming.envelopeType === "reaction" && incoming.reaction) {
3391
+ const actorUserId = direction === "outgoing" ? this.backend.selfUserId : senderUser;
3392
+ if (actorUserId !== null) {
3393
+ this.reactionFold.ingest({
3394
+ targetClientMsgId: incoming.reaction.targetClientMsgId,
3395
+ actorUserId,
3396
+ emoji: incoming.reaction.emoji,
3397
+ op: incoming.reaction.op,
3398
+ epoch: incoming.epoch,
3399
+ serverSeq: incoming.serverSeq,
3400
+ eventClientMsgId: incoming.clientMsgId
3401
+ });
3402
+ this.recomputeReactions(incoming.reaction.targetClientMsgId);
3403
+ }
3404
+ return;
3405
+ }
3257
3406
  const incomingClientMsgId = incoming.clientMsgId;
3258
3407
  const incomingReplyRef = incoming.replyRef;
3259
3408
  let resolvedReplyTo = null;
@@ -3269,7 +3418,10 @@ var Chat = class {
3269
3418
  serverSeq: incoming.serverSeq,
3270
3419
  sentAt: incoming.receivedAt,
3271
3420
  clientMsgId: incomingClientMsgId,
3272
- replyTo: resolvedReplyTo
3421
+ replyTo: resolvedReplyTo,
3422
+ // Attach any tally already folded for this message (a reaction that arrived
3423
+ // BEFORE its target — the dangling case — renders the moment the target lands).
3424
+ reactions: incomingClientMsgId ? this.reactionFold.tally(incomingClientMsgId) : {}
3273
3425
  };
3274
3426
  if (incomingClientMsgId && incoming.text !== null) {
3275
3427
  this.byClientMsgId.set(incomingClientMsgId, {
@@ -3285,6 +3437,35 @@ var Chat = class {
3285
3437
  );
3286
3438
  this.emit();
3287
3439
  }
3440
+ /**
3441
+ * Rebuild the target message's `reactions` from the authoritative fold and
3442
+ * re-emit. No-op when the target isn't present yet (its tally is attached the
3443
+ * moment it lands, via the append/merge paths) or when the tally is unchanged.
3444
+ */
3445
+ recomputeReactions(targetClientMsgId) {
3446
+ if (!targetClientMsgId) return;
3447
+ const tally = this.reactionFold.tally(targetClientMsgId);
3448
+ let changed = false;
3449
+ this.messageList = this.messageList.map((m) => {
3450
+ if (m.clientMsgId !== targetClientMsgId) return m;
3451
+ if (sameReactions(m.reactions, tally)) return m;
3452
+ changed = true;
3453
+ return { ...m, reactions: tally };
3454
+ });
3455
+ if (changed) this.emit();
3456
+ }
3457
+ /**
3458
+ * Overlay the authoritative fold's tally onto a message as it is appended/merged.
3459
+ * The Chat fold WINS when it has a non-empty tally; otherwise the tally already
3460
+ * attached upstream (the coordinator's page-local history fold) is preserved.
3461
+ */
3462
+ applyReactionTally(m) {
3463
+ if (!m.clientMsgId) return m;
3464
+ const tally = this.reactionFold.tally(m.clientMsgId);
3465
+ if (Object.keys(tally).length === 0) return m;
3466
+ if (sameReactions(m.reactions, tally)) return m;
3467
+ return { ...m, reactions: tally };
3468
+ }
3288
3469
  /** @internal — called by the backend's conv subscription. */
3289
3470
  applyConv(event, payload) {
3290
3471
  const userId = typeof payload.user_id === "string" ? payload.user_id : null;
@@ -3427,7 +3608,10 @@ var Chat = class {
3427
3608
  serverSeq: receipt.serverSeq,
3428
3609
  sentAt: /* @__PURE__ */ new Date(),
3429
3610
  clientMsgId,
3430
- replyTo: resolvedReplyTo
3611
+ replyTo: resolvedReplyTo,
3612
+ // Attach any tally already folded for this own-sent message (rare, but keeps
3613
+ // the dangling-target invariant uniform across every append path).
3614
+ reactions: clientMsgId ? this.reactionFold.tally(clientMsgId) : {}
3431
3615
  });
3432
3616
  this.messageList.sort((a, b) => a.serverSeq - b.serverSeq);
3433
3617
  this.emit();
@@ -3474,7 +3658,53 @@ var Chat = class {
3474
3658
  this.readWatermark = Math.max(this.readWatermark, message.serverSeq);
3475
3659
  this.emit();
3476
3660
  }
3661
+ // ── Reactions ──
3662
+ /** Add an emoji reaction to a message. No-op if the message isn't reactable
3663
+ * (empty clientMsgId — a legacy/system row). The reaction folds locally with
3664
+ * the server receipt's `(epoch, serverSeq)` so the target's tally updates
3665
+ * instantly; the durable echo on the next pump is a fold no-op (dedup on the
3666
+ * SAME wire clientMsgId). Never appends a bubble. */
3667
+ async react(message, emoji) {
3668
+ await this.sendReaction(message, emoji, "add");
3669
+ }
3670
+ /** Remove this user's emoji reaction from a message (op:'remove'). */
3671
+ async unreact(message, emoji) {
3672
+ await this.sendReaction(message, emoji, "remove");
3673
+ }
3674
+ async sendReaction(message, emoji, op) {
3675
+ if (!message.clientMsgId) return;
3676
+ const group = await this.materializeIfNeeded();
3677
+ const clientMsgId = mintClientMsgId();
3678
+ const { receipt } = await this.backend.sendReaction(group, {
3679
+ clientMsgId,
3680
+ targetClientMsgId: message.clientMsgId,
3681
+ emoji,
3682
+ op
3683
+ });
3684
+ this.reactionFold.ingest({
3685
+ targetClientMsgId: message.clientMsgId,
3686
+ actorUserId: this.backend.selfUserId,
3687
+ emoji,
3688
+ op,
3689
+ epoch: receipt.epoch,
3690
+ serverSeq: receipt.serverSeq,
3691
+ eventClientMsgId: clientMsgId
3692
+ });
3693
+ this.recomputeReactions(message.clientMsgId);
3694
+ }
3477
3695
  };
3696
+ function sameReactions(a, b) {
3697
+ const ak = Object.keys(a);
3698
+ const bk = Object.keys(b);
3699
+ if (ak.length !== bk.length) return false;
3700
+ for (const k of ak) {
3701
+ const av = a[k];
3702
+ const bv = b[k];
3703
+ if (!bv || av === void 0 || av.length !== bv.length) return false;
3704
+ for (let i = 0; i < av.length; i++) if (av[i] !== bv[i]) return false;
3705
+ }
3706
+ return true;
3707
+ }
3478
3708
 
3479
3709
  // src/messaging/delivery-source.ts
3480
3710
  var MessageHub = class {
@@ -3504,6 +3734,11 @@ var MessageHub = class {
3504
3734
  };
3505
3735
  }
3506
3736
  };
3737
+ function decodeSenderDeviceId(sender) {
3738
+ if (sender.length === 0) return null;
3739
+ const id = decodeUtf8(sender);
3740
+ return id.length > 0 ? id : null;
3741
+ }
3507
3742
  var MessageDeliverySource = class {
3508
3743
  constructor(rt, engine, hub, registry, messageStore, deviceId, selfUserId) {
3509
3744
  this.rt = rt;
@@ -3633,12 +3868,15 @@ var MessageDeliverySource = class {
3633
3868
  try {
3634
3869
  const received = await this.engine.processIncoming(fromBase64(group.rfcGroupId), blob);
3635
3870
  if (received.type === "application") {
3636
- const { text, clientMsgId, replyTo } = decodeEnvelope(received.data);
3871
+ const decoded = decodeEnvelope(received.data);
3872
+ const { text, clientMsgId, replyTo } = decoded;
3873
+ const isReaction = decoded.type === "reaction" && decoded.reaction != null;
3874
+ const senderDeviceId = row.sender_device_id ?? decodeSenderDeviceId(received.sender);
3637
3875
  const stored = {
3638
3876
  id: `${group.rfcGroupId}#${row.server_seq}`,
3639
3877
  direction: "incoming",
3640
3878
  text,
3641
- senderDeviceId: row.sender_device_id ?? null,
3879
+ senderDeviceId,
3642
3880
  epoch: row.epoch,
3643
3881
  serverSeq: row.server_seq,
3644
3882
  at: Date.now(),
@@ -3648,7 +3886,19 @@ var MessageDeliverySource = class {
3648
3886
  previewBody: replyTo.preview?.body ?? null,
3649
3887
  previewAuthorUserId: replyTo.preview?.author_user_id ?? null,
3650
3888
  previewKind: replyTo.preview?.kind ?? "text"
3651
- } : null
3889
+ } : null,
3890
+ // Thread the reaction discriminator + fields through the persisted row so
3891
+ // a reaction folded LIVE re-folds onto its target after a reload (the
3892
+ // reload-parity boundary — mirrors iOS T3). Omitted for non-reactions →
3893
+ // old rows hydrate as `'text'`/no-reaction (backward-compat).
3894
+ ...isReaction && decoded.reaction ? {
3895
+ envelopeType: "reaction",
3896
+ reaction: {
3897
+ targetClientMsgId: decoded.reaction.targetClientMsgId,
3898
+ emoji: decoded.reaction.emoji,
3899
+ op: decoded.reaction.op
3900
+ }
3901
+ } : {}
3652
3902
  };
3653
3903
  try {
3654
3904
  await this.messageStore.append(group.rfcGroupId, stored);
@@ -3659,12 +3909,14 @@ var MessageDeliverySource = class {
3659
3909
  kind: "application",
3660
3910
  group,
3661
3911
  text,
3662
- senderDeviceId: row.sender_device_id ?? null,
3912
+ senderDeviceId,
3663
3913
  epoch: row.epoch,
3664
3914
  serverSeq: row.server_seq,
3665
3915
  receivedAt: /* @__PURE__ */ new Date(),
3666
3916
  clientMsgId,
3667
- replyRef: replyTo
3917
+ replyRef: replyTo,
3918
+ envelopeType: decoded.type ?? "text",
3919
+ reaction: isReaction ? decoded.reaction : null
3668
3920
  });
3669
3921
  return true;
3670
3922
  }
@@ -5668,46 +5920,14 @@ var MessagingCoordinator = class {
5668
5920
  const r = await this.resolve();
5669
5921
  return r.groups.sendText(group, text, replyTo);
5670
5922
  }
5923
+ async sendReaction(group, args) {
5924
+ const r = await this.resolve();
5925
+ return r.groups.sendReaction(group, args);
5926
+ }
5671
5927
  async history(group, limit, before) {
5672
5928
  const r = await this.resolve();
5673
5929
  const rows = await r.messageStore.history(group.rfcGroupId, limit, before);
5674
- const lookup = /* @__PURE__ */ new Map();
5675
- for (const s of rows) {
5676
- const cid = s.clientMsgId ?? "";
5677
- if (cid && s.text !== null) {
5678
- const senderUserId = s.direction === "outgoing" ? this.selfUserId : "";
5679
- lookup.set(cid, { text: s.text, senderUserId });
5680
- }
5681
- }
5682
- return rows.map((s) => this.toChatMessage(group, s, (id) => lookup.get(id) ?? null));
5683
- }
5684
- toChatMessage(group, s, lookup) {
5685
- const clientMsgId = s.clientMsgId ?? "";
5686
- let replyTo = null;
5687
- if (s.replyTo && lookup) {
5688
- const ref = {
5689
- v: 1,
5690
- client_msg_id: s.replyTo.clientMsgId,
5691
- preview: {
5692
- kind: s.replyTo.previewKind,
5693
- author_user_id: s.replyTo.previewAuthorUserId ?? "",
5694
- body: s.replyTo.previewBody ?? void 0,
5695
- body_truncated: false
5696
- }
5697
- };
5698
- replyTo = resolveReply(ref, lookup);
5699
- }
5700
- return {
5701
- id: `${group.displayId}#${s.serverSeq}`,
5702
- kind: s.text != null ? "text" : "system",
5703
- direction: s.direction,
5704
- senderUserId: s.direction === "outgoing" ? this.selfUserId : null,
5705
- text: s.text,
5706
- serverSeq: s.serverSeq,
5707
- sentAt: new Date(s.at),
5708
- clientMsgId,
5709
- replyTo
5710
- };
5930
+ return projectHistory(group.displayId, rows, this.selfUserId);
5711
5931
  }
5712
5932
  async members(group) {
5713
5933
  const r = await this.resolve();
@@ -5784,6 +6004,64 @@ var MessagingCoordinator = class {
5784
6004
  return res.devices.map((d) => d.device_id);
5785
6005
  }
5786
6006
  };
6007
+ function projectHistory(displayId, rows, selfUserId, resolveActor) {
6008
+ const fold = new ReactionFold();
6009
+ for (const s of rows) {
6010
+ if (s.envelopeType !== "reaction" || !s.reaction) continue;
6011
+ const actor = s.direction === "outgoing" ? selfUserId : resolveActor?.(s.senderDeviceId ?? null) ?? null;
6012
+ if (actor === null) continue;
6013
+ fold.ingest({
6014
+ targetClientMsgId: s.reaction.targetClientMsgId,
6015
+ actorUserId: actor,
6016
+ emoji: s.reaction.emoji,
6017
+ op: s.reaction.op,
6018
+ epoch: s.epoch,
6019
+ serverSeq: s.serverSeq,
6020
+ eventClientMsgId: s.clientMsgId ?? `${s.id}`
6021
+ });
6022
+ }
6023
+ const lookup = /* @__PURE__ */ new Map();
6024
+ for (const s of rows) {
6025
+ if (s.envelopeType === "reaction") continue;
6026
+ const cid = s.clientMsgId ?? "";
6027
+ if (cid && s.text !== null) {
6028
+ const senderUserId = s.direction === "outgoing" ? selfUserId : "";
6029
+ lookup.set(cid, { text: s.text, senderUserId });
6030
+ }
6031
+ }
6032
+ const out = [];
6033
+ for (const s of rows) {
6034
+ if (s.envelopeType === "reaction") continue;
6035
+ const clientMsgId = s.clientMsgId ?? "";
6036
+ let replyTo = null;
6037
+ if (s.replyTo) {
6038
+ const ref = {
6039
+ v: 1,
6040
+ client_msg_id: s.replyTo.clientMsgId,
6041
+ preview: {
6042
+ kind: s.replyTo.previewKind,
6043
+ author_user_id: s.replyTo.previewAuthorUserId ?? "",
6044
+ body: s.replyTo.previewBody ?? void 0,
6045
+ body_truncated: false
6046
+ }
6047
+ };
6048
+ replyTo = resolveReply(ref, (id) => lookup.get(id) ?? null);
6049
+ }
6050
+ out.push({
6051
+ id: `${displayId}#${s.serverSeq}`,
6052
+ kind: s.text != null ? "text" : "system",
6053
+ direction: s.direction,
6054
+ senderUserId: s.direction === "outgoing" ? selfUserId : null,
6055
+ text: s.text,
6056
+ serverSeq: s.serverSeq,
6057
+ sentAt: new Date(s.at),
6058
+ clientMsgId,
6059
+ replyTo,
6060
+ reactions: clientMsgId ? fold.tally(clientMsgId) : {}
6061
+ });
6062
+ }
6063
+ return out;
6064
+ }
5787
6065
 
5788
6066
  // src/messaging/facade.ts
5789
6067
  var PalbeMessaging = class {
@@ -6517,7 +6795,7 @@ function defaultSessionStorage(key) {
6517
6795
  }
6518
6796
 
6519
6797
  // src/version.ts
6520
- var VERSION = "1.1.1";
6798
+ var VERSION = "1.2.1";
6521
6799
 
6522
6800
  // src/runtime.ts
6523
6801
  function buildRuntime(config) {