@dittolive/ditto-chat-core 0.0.1 → 0.1.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.
@@ -0,0 +1,1764 @@
1
+ import k, { useMemo as Fe } from "react";
2
+ const ze = {
3
+ canCreateRoom: !0,
4
+ canEditOwnMessage: !0,
5
+ canDeleteOwnMessage: !0,
6
+ canAddReaction: !0,
7
+ canRemoveOwnReaction: !0,
8
+ canMentionUsers: !0,
9
+ canSubscribeToRoom: !0
10
+ }, He = (e, t, { rbacConfig: r = {} }) => ({
11
+ rbacConfig: r,
12
+ /**
13
+ * Check if a specific permission action is allowed.
14
+ *
15
+ * This method evaluates the RBAC configuration to determine
16
+ * if an action is permitted. Uses a permissive-by-default approach.
17
+ *
18
+ * Workflow:
19
+ * 1. Gets current RBAC config from state
20
+ * 2. Checks if permission is explicitly set
21
+ * 3. Returns explicit value or default (true)
22
+ *
23
+ * @param action - Permission key to check (e.g., 'canCreateRoom')
24
+ * @returns boolean indicating if action is allowed
25
+ */
26
+ canPerformAction: (s) => {
27
+ const a = t().rbacConfig[s];
28
+ return a !== void 0 ? a : ze[s];
29
+ },
30
+ /**
31
+ * Update the RBAC permission configuration.
32
+ *
33
+ * This method merges new permissions with the existing config,
34
+ * allowing partial updates without overwriting unrelated permissions.
35
+ *
36
+ * Workflow:
37
+ * 1. Merges provided config with existing rbacConfig
38
+ * 2. Updates state with combined configuration
39
+ *
40
+ * @param config - Partial RBAC config with permissions to update
41
+ */
42
+ updateRBACConfig: (s) => {
43
+ e((o) => ({
44
+ ...o,
45
+ rbacConfig: {
46
+ ...o.rbacConfig,
47
+ ...s
48
+ }
49
+ }));
50
+ }
51
+ });
52
+ var Ae = /* @__PURE__ */ Symbol.for("immer-nothing"), ye = /* @__PURE__ */ Symbol.for("immer-draftable"), O = /* @__PURE__ */ Symbol.for("immer-state"), Le = process.env.NODE_ENV !== "production" ? [
53
+ // All error codes, starting by 0:
54
+ function(e) {
55
+ return `The plugin for '${e}' has not been loaded into Immer. To enable the plugin, import and call \`enable${e}()\` when initializing your application.`;
56
+ },
57
+ function(e) {
58
+ return `produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '${e}'`;
59
+ },
60
+ "This object has been frozen and should not be mutated",
61
+ function(e) {
62
+ return "Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + e;
63
+ },
64
+ "An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.",
65
+ "Immer forbids circular references",
66
+ "The first or second argument to `produce` must be a function",
67
+ "The third argument to `produce` must be a function or undefined",
68
+ "First argument to `createDraft` must be a plain object, an array, or an immerable object",
69
+ "First argument to `finishDraft` must be a draft returned by `createDraft`",
70
+ function(e) {
71
+ return `'current' expects a draft, got: ${e}`;
72
+ },
73
+ "Object.defineProperty() cannot be used on an Immer draft",
74
+ "Object.setPrototypeOf() cannot be used on an Immer draft",
75
+ "Immer only supports deleting array indices",
76
+ "Immer only supports setting array indices and the 'length' property",
77
+ function(e) {
78
+ return `'original' expects a draft, got: ${e}`;
79
+ }
80
+ // Note: if more errors are added, the errorOffset in Patches.ts should be increased
81
+ // See Patches.ts for additional errors
82
+ ] : [];
83
+ function T(e, ...t) {
84
+ if (process.env.NODE_ENV !== "production") {
85
+ const r = Le[e], n = typeof r == "function" ? r.apply(null, t) : r;
86
+ throw new Error(`[Immer] ${n}`);
87
+ }
88
+ throw new Error(
89
+ `[Immer] minified error nr: ${e}. Full error at: https://bit.ly/3cXEKWf`
90
+ );
91
+ }
92
+ var B = Object.getPrototypeOf;
93
+ function N(e) {
94
+ return !!e && !!e[O];
95
+ }
96
+ function U(e) {
97
+ return e ? ve(e) || Array.isArray(e) || !!e[ye] || !!e.constructor?.[ye] || H(e) || X(e) : !1;
98
+ }
99
+ var $e = Object.prototype.constructor.toString(), ge = /* @__PURE__ */ new WeakMap();
100
+ function ve(e) {
101
+ if (!e || typeof e != "object")
102
+ return !1;
103
+ const t = Object.getPrototypeOf(e);
104
+ if (t === null || t === Object.prototype)
105
+ return !0;
106
+ const r = Object.hasOwnProperty.call(t, "constructor") && t.constructor;
107
+ if (r === Object)
108
+ return !0;
109
+ if (typeof r != "function")
110
+ return !1;
111
+ let n = ge.get(r);
112
+ return n === void 0 && (n = Function.toString.call(r), ge.set(r, n)), n === $e;
113
+ }
114
+ function q(e, t, r = !0) {
115
+ V(e) === 0 ? (r ? Reflect.ownKeys(e) : Object.keys(e)).forEach((s) => {
116
+ t(s, e[s], e);
117
+ }) : e.forEach((n, s) => t(s, n, e));
118
+ }
119
+ function V(e) {
120
+ const t = e[O];
121
+ return t ? t.type_ : Array.isArray(e) ? 1 : H(e) ? 2 : X(e) ? 3 : 0;
122
+ }
123
+ function oe(e, t) {
124
+ return V(e) === 2 ? e.has(t) : Object.prototype.hasOwnProperty.call(e, t);
125
+ }
126
+ function Me(e, t, r) {
127
+ const n = V(e);
128
+ n === 2 ? e.set(t, r) : n === 3 ? e.add(r) : e[t] = r;
129
+ }
130
+ function je(e, t) {
131
+ return e === t ? e !== 0 || 1 / e === 1 / t : e !== e && t !== t;
132
+ }
133
+ function H(e) {
134
+ return e instanceof Map;
135
+ }
136
+ function X(e) {
137
+ return e instanceof Set;
138
+ }
139
+ function M(e) {
140
+ return e.copy_ || e.base_;
141
+ }
142
+ function ie(e, t) {
143
+ if (H(e))
144
+ return new Map(e);
145
+ if (X(e))
146
+ return new Set(e);
147
+ if (Array.isArray(e))
148
+ return Array.prototype.slice.call(e);
149
+ const r = ve(e);
150
+ if (t === !0 || t === "class_only" && !r) {
151
+ const n = Object.getOwnPropertyDescriptors(e);
152
+ delete n[O];
153
+ let s = Reflect.ownKeys(n);
154
+ for (let o = 0; o < s.length; o++) {
155
+ const a = s[o], c = n[a];
156
+ c.writable === !1 && (c.writable = !0, c.configurable = !0), (c.get || c.set) && (n[a] = {
157
+ configurable: !0,
158
+ writable: !0,
159
+ // could live with !!desc.set as well here...
160
+ enumerable: c.enumerable,
161
+ value: e[a]
162
+ });
163
+ }
164
+ return Object.create(B(e), n);
165
+ } else {
166
+ const n = B(e);
167
+ if (n !== null && r)
168
+ return { ...e };
169
+ const s = Object.create(n);
170
+ return Object.assign(s, e);
171
+ }
172
+ }
173
+ function fe(e, t = !1) {
174
+ return Z(e) || N(e) || !U(e) || (V(e) > 1 && Object.defineProperties(e, {
175
+ set: W,
176
+ add: W,
177
+ clear: W,
178
+ delete: W
179
+ }), Object.freeze(e), t && Object.values(e).forEach((r) => fe(r, !0))), e;
180
+ }
181
+ function We() {
182
+ T(2);
183
+ }
184
+ var W = {
185
+ value: We
186
+ };
187
+ function Z(e) {
188
+ return e === null || typeof e != "object" ? !0 : Object.isFrozen(e);
189
+ }
190
+ var Ge = {};
191
+ function P(e) {
192
+ const t = Ge[e];
193
+ return t || T(0, e), t;
194
+ }
195
+ var F;
196
+ function Ce() {
197
+ return F;
198
+ }
199
+ function qe(e, t) {
200
+ return {
201
+ drafts_: [],
202
+ parent_: e,
203
+ immer_: t,
204
+ // Whenever the modified draft contains a draft from another scope, we
205
+ // need to prevent auto-freezing so the unowned draft can be finalized.
206
+ canAutoFreeze_: !0,
207
+ unfinalizedDrafts_: 0
208
+ };
209
+ }
210
+ function he(e, t) {
211
+ t && (P("Patches"), e.patches_ = [], e.inversePatches_ = [], e.patchListener_ = t);
212
+ }
213
+ function ae(e) {
214
+ ce(e), e.drafts_.forEach(Qe), e.drafts_ = null;
215
+ }
216
+ function ce(e) {
217
+ e === F && (F = e.parent_);
218
+ }
219
+ function be(e) {
220
+ return F = qe(F, e);
221
+ }
222
+ function Qe(e) {
223
+ const t = e[O];
224
+ t.type_ === 0 || t.type_ === 1 ? t.revoke_() : t.revoked_ = !0;
225
+ }
226
+ function _e(e, t) {
227
+ t.unfinalizedDrafts_ = t.drafts_.length;
228
+ const r = t.drafts_[0];
229
+ return e !== void 0 && e !== r ? (r[O].modified_ && (ae(t), T(4)), U(e) && (e = Q(t, e), t.parent_ || Y(t, e)), t.patches_ && P("Patches").generateReplacementPatches_(
230
+ r[O].base_,
231
+ e,
232
+ t.patches_,
233
+ t.inversePatches_
234
+ )) : e = Q(t, r, []), ae(t), t.patches_ && t.patchListener_(t.patches_, t.inversePatches_), e !== Ae ? e : void 0;
235
+ }
236
+ function Q(e, t, r) {
237
+ if (Z(t))
238
+ return t;
239
+ const n = e.immer_.shouldUseStrictIteration(), s = t[O];
240
+ if (!s)
241
+ return q(
242
+ t,
243
+ (o, a) => we(e, s, t, o, a, r),
244
+ n
245
+ ), t;
246
+ if (s.scope_ !== e)
247
+ return t;
248
+ if (!s.modified_)
249
+ return Y(e, s.base_, !0), s.base_;
250
+ if (!s.finalized_) {
251
+ s.finalized_ = !0, s.scope_.unfinalizedDrafts_--;
252
+ const o = s.copy_;
253
+ let a = o, c = !1;
254
+ s.type_ === 3 && (a = new Set(o), o.clear(), c = !0), q(
255
+ a,
256
+ (d, y) => we(
257
+ e,
258
+ s,
259
+ o,
260
+ d,
261
+ y,
262
+ r,
263
+ c
264
+ ),
265
+ n
266
+ ), Y(e, o, !1), r && e.patches_ && P("Patches").generatePatches_(
267
+ s,
268
+ r,
269
+ e.patches_,
270
+ e.inversePatches_
271
+ );
272
+ }
273
+ return s.copy_;
274
+ }
275
+ function we(e, t, r, n, s, o, a) {
276
+ if (s == null || typeof s != "object" && !a)
277
+ return;
278
+ const c = Z(s);
279
+ if (!(c && !a)) {
280
+ if (process.env.NODE_ENV !== "production" && s === r && T(5), N(s)) {
281
+ const d = o && t && t.type_ !== 3 && // Set objects are atomic since they have no keys.
282
+ !oe(t.assigned_, n) ? o.concat(n) : void 0, y = Q(e, s, d);
283
+ if (Me(r, n, y), N(y))
284
+ e.canAutoFreeze_ = !1;
285
+ else
286
+ return;
287
+ } else a && r.add(s);
288
+ if (U(s) && !c) {
289
+ if (!e.immer_.autoFreeze_ && e.unfinalizedDrafts_ < 1 || t && t.base_ && t.base_[n] === s && c)
290
+ return;
291
+ Q(e, s), (!t || !t.scope_.parent_) && typeof n != "symbol" && (H(r) ? r.has(n) : Object.prototype.propertyIsEnumerable.call(r, n)) && Y(e, s);
292
+ }
293
+ }
294
+ }
295
+ function Y(e, t, r = !1) {
296
+ !e.parent_ && e.immer_.autoFreeze_ && e.canAutoFreeze_ && fe(t, r);
297
+ }
298
+ function Ye(e, t) {
299
+ const r = Array.isArray(e), n = {
300
+ type_: r ? 1 : 0,
301
+ // Track which produce call this is associated with.
302
+ scope_: t ? t.scope_ : Ce(),
303
+ // True for both shallow and deep changes.
304
+ modified_: !1,
305
+ // Used during finalization.
306
+ finalized_: !1,
307
+ // Track which properties have been assigned (true) or deleted (false).
308
+ assigned_: {},
309
+ // The parent draft state.
310
+ parent_: t,
311
+ // The base state.
312
+ base_: e,
313
+ // The base proxy.
314
+ draft_: null,
315
+ // set below
316
+ // The base copy with any updated values.
317
+ copy_: null,
318
+ // Called by the `produce` function.
319
+ revoke_: null,
320
+ isManual_: !1
321
+ };
322
+ let s = n, o = me;
323
+ r && (s = [n], o = z);
324
+ const { revoke: a, proxy: c } = Proxy.revocable(s, o);
325
+ return n.draft_ = c, n.revoke_ = a, c;
326
+ }
327
+ var me = {
328
+ get(e, t) {
329
+ if (t === O)
330
+ return e;
331
+ const r = M(e);
332
+ if (!oe(r, t))
333
+ return Ve(e, r, t);
334
+ const n = r[t];
335
+ return e.finalized_ || !U(n) ? n : n === ee(e.base_, t) ? (te(e), e.copy_[t] = le(n, e)) : n;
336
+ },
337
+ has(e, t) {
338
+ return t in M(e);
339
+ },
340
+ ownKeys(e) {
341
+ return Reflect.ownKeys(M(e));
342
+ },
343
+ set(e, t, r) {
344
+ const n = Ue(M(e), t);
345
+ if (n?.set)
346
+ return n.set.call(e.draft_, r), !0;
347
+ if (!e.modified_) {
348
+ const s = ee(M(e), t), o = s?.[O];
349
+ if (o && o.base_ === r)
350
+ return e.copy_[t] = r, e.assigned_[t] = !1, !0;
351
+ if (je(r, s) && (r !== void 0 || oe(e.base_, t)))
352
+ return !0;
353
+ te(e), ue(e);
354
+ }
355
+ return e.copy_[t] === r && // special case: handle new props with value 'undefined'
356
+ (r !== void 0 || t in e.copy_) || // special case: NaN
357
+ Number.isNaN(r) && Number.isNaN(e.copy_[t]) || (e.copy_[t] = r, e.assigned_[t] = !0), !0;
358
+ },
359
+ deleteProperty(e, t) {
360
+ return ee(e.base_, t) !== void 0 || t in e.base_ ? (e.assigned_[t] = !1, te(e), ue(e)) : delete e.assigned_[t], e.copy_ && delete e.copy_[t], !0;
361
+ },
362
+ // Note: We never coerce `desc.value` into an Immer draft, because we can't make
363
+ // the same guarantee in ES5 mode.
364
+ getOwnPropertyDescriptor(e, t) {
365
+ const r = M(e), n = Reflect.getOwnPropertyDescriptor(r, t);
366
+ return n && {
367
+ writable: !0,
368
+ configurable: e.type_ !== 1 || t !== "length",
369
+ enumerable: n.enumerable,
370
+ value: r[t]
371
+ };
372
+ },
373
+ defineProperty() {
374
+ T(11);
375
+ },
376
+ getPrototypeOf(e) {
377
+ return B(e.base_);
378
+ },
379
+ setPrototypeOf() {
380
+ T(12);
381
+ }
382
+ }, z = {};
383
+ q(me, (e, t) => {
384
+ z[e] = function() {
385
+ return arguments[0] = arguments[0][0], t.apply(this, arguments);
386
+ };
387
+ });
388
+ z.deleteProperty = function(e, t) {
389
+ return process.env.NODE_ENV !== "production" && isNaN(parseInt(t)) && T(13), z.set.call(this, e, t, void 0);
390
+ };
391
+ z.set = function(e, t, r) {
392
+ return process.env.NODE_ENV !== "production" && t !== "length" && isNaN(parseInt(t)) && T(14), me.set.call(this, e[0], t, r, e[0]);
393
+ };
394
+ function ee(e, t) {
395
+ const r = e[O];
396
+ return (r ? M(r) : e)[t];
397
+ }
398
+ function Ve(e, t, r) {
399
+ const n = Ue(t, r);
400
+ return n ? "value" in n ? n.value : (
401
+ // This is a very special case, if the prop is a getter defined by the
402
+ // prototype, we should invoke it with the draft as context!
403
+ n.get?.call(e.draft_)
404
+ ) : void 0;
405
+ }
406
+ function Ue(e, t) {
407
+ if (!(t in e))
408
+ return;
409
+ let r = B(e);
410
+ for (; r; ) {
411
+ const n = Object.getOwnPropertyDescriptor(r, t);
412
+ if (n)
413
+ return n;
414
+ r = B(r);
415
+ }
416
+ }
417
+ function ue(e) {
418
+ e.modified_ || (e.modified_ = !0, e.parent_ && ue(e.parent_));
419
+ }
420
+ function te(e) {
421
+ e.copy_ || (e.copy_ = ie(
422
+ e.base_,
423
+ e.scope_.immer_.useStrictShallowCopy_
424
+ ));
425
+ }
426
+ var Xe = class {
427
+ constructor(e) {
428
+ this.autoFreeze_ = !0, this.useStrictShallowCopy_ = !1, this.useStrictIteration_ = !0, this.produce = (t, r, n) => {
429
+ if (typeof t == "function" && typeof r != "function") {
430
+ const o = r;
431
+ r = t;
432
+ const a = this;
433
+ return function(d = o, ...y) {
434
+ return a.produce(d, (p) => r.call(this, p, ...y));
435
+ };
436
+ }
437
+ typeof r != "function" && T(6), n !== void 0 && typeof n != "function" && T(7);
438
+ let s;
439
+ if (U(t)) {
440
+ const o = be(this), a = le(t, void 0);
441
+ let c = !0;
442
+ try {
443
+ s = r(a), c = !1;
444
+ } finally {
445
+ c ? ae(o) : ce(o);
446
+ }
447
+ return he(o, n), _e(s, o);
448
+ } else if (!t || typeof t != "object") {
449
+ if (s = r(t), s === void 0 && (s = t), s === Ae && (s = void 0), this.autoFreeze_ && fe(s, !0), n) {
450
+ const o = [], a = [];
451
+ P("Patches").generateReplacementPatches_(t, s, o, a), n(o, a);
452
+ }
453
+ return s;
454
+ } else
455
+ T(1, t);
456
+ }, this.produceWithPatches = (t, r) => {
457
+ if (typeof t == "function")
458
+ return (a, ...c) => this.produceWithPatches(a, (d) => t(d, ...c));
459
+ let n, s;
460
+ return [this.produce(t, r, (a, c) => {
461
+ n = a, s = c;
462
+ }), n, s];
463
+ }, typeof e?.autoFreeze == "boolean" && this.setAutoFreeze(e.autoFreeze), typeof e?.useStrictShallowCopy == "boolean" && this.setUseStrictShallowCopy(e.useStrictShallowCopy), typeof e?.useStrictIteration == "boolean" && this.setUseStrictIteration(e.useStrictIteration);
464
+ }
465
+ createDraft(e) {
466
+ U(e) || T(8), N(e) && (e = Ze(e));
467
+ const t = be(this), r = le(e, void 0);
468
+ return r[O].isManual_ = !0, ce(t), r;
469
+ }
470
+ finishDraft(e, t) {
471
+ const r = e && e[O];
472
+ (!r || !r.isManual_) && T(9);
473
+ const { scope_: n } = r;
474
+ return he(n, t), _e(void 0, n);
475
+ }
476
+ /**
477
+ * Pass true to automatically freeze all copies created by Immer.
478
+ *
479
+ * By default, auto-freezing is enabled.
480
+ */
481
+ setAutoFreeze(e) {
482
+ this.autoFreeze_ = e;
483
+ }
484
+ /**
485
+ * Pass true to enable strict shallow copy.
486
+ *
487
+ * By default, immer does not copy the object descriptors such as getter, setter and non-enumrable properties.
488
+ */
489
+ setUseStrictShallowCopy(e) {
490
+ this.useStrictShallowCopy_ = e;
491
+ }
492
+ /**
493
+ * Pass false to use faster iteration that skips non-enumerable properties
494
+ * but still handles symbols for compatibility.
495
+ *
496
+ * By default, strict iteration is enabled (includes all own properties).
497
+ */
498
+ setUseStrictIteration(e) {
499
+ this.useStrictIteration_ = e;
500
+ }
501
+ shouldUseStrictIteration() {
502
+ return this.useStrictIteration_;
503
+ }
504
+ applyPatches(e, t) {
505
+ let r;
506
+ for (r = t.length - 1; r >= 0; r--) {
507
+ const s = t[r];
508
+ if (s.path.length === 0 && s.op === "replace") {
509
+ e = s.value;
510
+ break;
511
+ }
512
+ }
513
+ r > -1 && (t = t.slice(r + 1));
514
+ const n = P("Patches").applyPatches_;
515
+ return N(e) ? n(e, t) : this.produce(
516
+ e,
517
+ (s) => n(s, t)
518
+ );
519
+ }
520
+ };
521
+ function le(e, t) {
522
+ const r = H(e) ? P("MapSet").proxyMap_(e, t) : X(e) ? P("MapSet").proxySet_(e, t) : Ye(e, t);
523
+ return (t ? t.scope_ : Ce()).drafts_.push(r), r;
524
+ }
525
+ function Ze(e) {
526
+ return N(e) || T(10, e), Pe(e);
527
+ }
528
+ function Pe(e) {
529
+ if (!U(e) || Z(e))
530
+ return e;
531
+ const t = e[O];
532
+ let r, n = !0;
533
+ if (t) {
534
+ if (!t.modified_)
535
+ return t.base_;
536
+ t.finalized_ = !0, r = ie(e, t.scope_.immer_.useStrictShallowCopy_), n = t.scope_.immer_.shouldUseStrictIteration();
537
+ } else
538
+ r = ie(e, !0);
539
+ return q(
540
+ r,
541
+ (s, o) => {
542
+ Me(r, s, Pe(o));
543
+ },
544
+ n
545
+ ), t && (t.finalized_ = !1), r;
546
+ }
547
+ var Je = new Xe(), C = Je.produce;
548
+ const E = [];
549
+ for (let e = 0; e < 256; ++e)
550
+ E.push((e + 256).toString(16).slice(1));
551
+ function Ke(e, t = 0) {
552
+ return (E[e[t + 0]] + E[e[t + 1]] + E[e[t + 2]] + E[e[t + 3]] + "-" + E[e[t + 4]] + E[e[t + 5]] + "-" + E[e[t + 6]] + E[e[t + 7]] + "-" + E[e[t + 8]] + E[e[t + 9]] + "-" + E[e[t + 10]] + E[e[t + 11]] + E[e[t + 12]] + E[e[t + 13]] + E[e[t + 14]] + E[e[t + 15]]).toLowerCase();
553
+ }
554
+ let re;
555
+ const et = new Uint8Array(16);
556
+ function tt() {
557
+ if (!re) {
558
+ if (typeof crypto > "u" || !crypto.getRandomValues)
559
+ throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
560
+ re = crypto.getRandomValues.bind(crypto);
561
+ }
562
+ return re(et);
563
+ }
564
+ const rt = typeof crypto < "u" && crypto.randomUUID && crypto.randomUUID.bind(crypto), pe = { randomUUID: rt };
565
+ function nt(e, t, r) {
566
+ e = e || {};
567
+ const n = e.random ?? e.rng?.() ?? tt();
568
+ if (n.length < 16)
569
+ throw new Error("Random bytes length must be >= 16");
570
+ return n[6] = n[6] & 15 | 64, n[8] = n[8] & 63 | 128, Ke(n);
571
+ }
572
+ function Ne(e, t, r) {
573
+ return pe.randomUUID && !e ? pe.randomUUID() : nt(e);
574
+ }
575
+ function Ee(e, t, r) {
576
+ if (r.items.length === 0)
577
+ return;
578
+ const n = r.items.map((s) => {
579
+ const o = t().messagesPublisher;
580
+ return o(s.value), s.value;
581
+ });
582
+ e((s) => C(s, (o) => {
583
+ const a = s.rooms.filter(
584
+ (c) => c.collectionId !== n[0].collectionId
585
+ );
586
+ return o.rooms = a.concat(n), o.roomsLoading = !1, o;
587
+ }));
588
+ }
589
+ async function ne({
590
+ ditto: e,
591
+ currentUserId: t,
592
+ name: r,
593
+ collectionId: n,
594
+ messagesId: s,
595
+ participants: o = [],
596
+ retentionDays: a,
597
+ isGenerated: c = !1,
598
+ id: d
599
+ }) {
600
+ if (e)
601
+ try {
602
+ const p = {
603
+ _id: d || Ne(),
604
+ name: r,
605
+ messagesId: s,
606
+ collectionId: n,
607
+ isGenerated: c,
608
+ createdBy: t,
609
+ createdOn: (/* @__PURE__ */ new Date()).toISOString(),
610
+ participants: o || void 0,
611
+ ...a !== void 0 && { retentionDays: a }
612
+ }, S = `INSERT INTO \`${n}\` DOCUMENTS (:newDoc) ON ID CONFLICT DO UPDATE`;
613
+ return await e.store.execute(S, { newDoc: p }), p;
614
+ } catch (y) {
615
+ console.error(`Error creating ${n}:`, y);
616
+ }
617
+ }
618
+ const st = (e, t, { ditto: r, userId: n }) => {
619
+ const s = {
620
+ rooms: [],
621
+ dmRooms: [],
622
+ generatedRooms: [],
623
+ roomsLoading: !0,
624
+ roomsObserver: null,
625
+ roomsSubscription: null,
626
+ dmRoomsObserver: null,
627
+ dmRoomsSubscription: null,
628
+ /**
629
+ * Creates a new public room with RBAC permission checks.
630
+ *
631
+ * This function creates a new regular (non-DM) room in the chat application.
632
+ * The creation process includes:
633
+ *
634
+ * 1. **Permission Check**:
635
+ * - Verifies the user has "canCreateRoom" permission via RBAC
636
+ * - Returns undefined immediately if permission is denied
637
+ * - Logs a warning for debugging permission issues
638
+ *
639
+ * 2. **User Context**:
640
+ * - Retrieves the current user from state
641
+ * - Falls back to the userId parameter if currentUser is not available
642
+ * - Ensures proper creator attribution
643
+ *
644
+ * 3. **Room Creation**:
645
+ * - Delegates to createRoomBase with appropriate parameters
646
+ * - Sets collectionId to "rooms" for regular rooms
647
+ * - Sets messagesId to "messages" collection
648
+ * - Passes through optional retentionDays parameter
649
+ *
650
+ * This approach ensures:
651
+ * - RBAC integration prevents unauthorized room creation
652
+ * - Consistent room creation through shared base function
653
+ * - Optional custom retention policies per room
654
+ *
655
+ * @param name - Display name for the new room
656
+ * @param options - Optional configuration for the room
657
+ * @param options.retentionDays - Optional custom message retention period (overrides global default)
658
+ * @param options.isGenerated - Optional flag to mark room as generated (hidden from main list)
659
+ * @returns Promise resolving to the created Room object, or undefined if permission denied
660
+ */
661
+ createRoom(o, a) {
662
+ if (!t().canPerformAction("canCreateRoom"))
663
+ return console.warn("Permission denied: canCreateRoom is false"), Promise.resolve(void 0);
664
+ const c = t().currentUser;
665
+ return ne({
666
+ ditto: r,
667
+ currentUserId: c?._id || n,
668
+ name: o,
669
+ collectionId: "rooms",
670
+ messagesId: "messages",
671
+ retentionDays: a?.retentionDays,
672
+ isGenerated: a?.isGenerated ?? !1
673
+ });
674
+ },
675
+ /**
676
+ * Creates a new direct message (DM) room between two users.
677
+ *
678
+ * This function creates a private one-on-one chat room between the current
679
+ * user and another user. The creation process includes:
680
+ *
681
+ * 1. **Validation**:
682
+ * - Ensures both current user and DM user have valid IDs
683
+ * - Throws an error if either user ID is missing
684
+ * - Prevents creation of invalid DM rooms
685
+ *
686
+ * 2. **Room Naming**:
687
+ * - Generates a room name combining both users' names
688
+ * - Format: "CurrentUser & OtherUser"
689
+ * - Provides clear identification of DM participants
690
+ *
691
+ * 3. **DM Room Creation**:
692
+ * - Delegates to createRoomBase with DM-specific parameters
693
+ * - Sets collectionId to "dm_rooms" for DM storage
694
+ * - Sets messagesId to "dm_messages" collection
695
+ * - Includes both user IDs in participants array
696
+ *
697
+ * This approach ensures:
698
+ * - Proper participant tracking for DM rooms
699
+ * - Separation of DM rooms from regular rooms in storage
700
+ * - User-friendly room naming for DM conversations
701
+ *
702
+ * @param dmUser - The ChatUser to create a DM room with
703
+ * @returns Promise resolving to the created DM Room object
704
+ * @throws Error if either user ID is invalid or missing
705
+ */
706
+ createDMRoom(o) {
707
+ const a = t().currentUser;
708
+ if (!a?._id || !o?._id)
709
+ throw Error("Invalid users");
710
+ return ne({
711
+ ditto: r,
712
+ currentUserId: a?._id || n,
713
+ name: `${a?.name} & ${o.name}`,
714
+ collectionId: "dm_rooms",
715
+ messagesId: "dm_messages",
716
+ participants: [a?._id, o._id]
717
+ });
718
+ },
719
+ /**
720
+ * Creates a generated room (comment room) with a specific ID.
721
+ *
722
+ * Generated rooms are used for programmatic use cases like comment threads
723
+ * on products, articles, or other entities. They are hidden from the main
724
+ * chat list but can be accessed directly when needed.
725
+ *
726
+ * @param id - Custom ID for the room (e.g., "comments-product-123")
727
+ * @param name - Display name for the room
728
+ * @returns Promise resolving to the created Room object
729
+ */
730
+ async createGeneratedRoom(o, a) {
731
+ const c = t().currentUser, d = await ne({
732
+ ditto: r,
733
+ currentUserId: c?._id || n,
734
+ name: a,
735
+ collectionId: "rooms",
736
+ messagesId: "messages",
737
+ isGenerated: !0,
738
+ id: o
739
+ });
740
+ return d && e((y) => y.generatedRooms.some((S) => S._id === d._id) ? y : {
741
+ ...y,
742
+ generatedRooms: [...y.generatedRooms, d]
743
+ }), d;
744
+ },
745
+ /**
746
+ * Returns all public rooms, filtering out generated rooms.
747
+ *
748
+ * This selector provides the list of rooms that should be displayed
749
+ * in the main chat list, excluding generated/comment rooms.
750
+ *
751
+ * @returns Array of non-generated rooms
752
+ */
753
+ getPublicRooms() {
754
+ return t().rooms.filter((o) => !o.isGenerated);
755
+ }
756
+ };
757
+ if (r) {
758
+ const o = "SELECT * from rooms WHERE isGenerated = false OR isGenerated IS NULL", a = "SELECT * from dm_rooms where (array_contains(participants, :userId))";
759
+ s.roomsSubscription = r.sync.registerSubscription(o), s.dmRoomsSubscription = r.sync.registerSubscription(a, {
760
+ userId: n
761
+ }), s.roomsObserver = r.store.registerObserver(
762
+ o,
763
+ (c) => {
764
+ Ee(e, t, c);
765
+ }
766
+ ), s.dmRoomsObserver = r.store.registerObserver(
767
+ a,
768
+ (c) => {
769
+ Ee(e, t, c);
770
+ },
771
+ {
772
+ userId: n
773
+ }
774
+ );
775
+ }
776
+ return s;
777
+ }, Te = (e) => {
778
+ let t;
779
+ const r = /* @__PURE__ */ new Set(), n = (y, p) => {
780
+ const S = typeof y == "function" ? y(t) : y;
781
+ if (!Object.is(S, t)) {
782
+ const x = t;
783
+ t = p ?? (typeof S != "object" || S === null) ? S : Object.assign({}, t, S), r.forEach((L) => L(t, x));
784
+ }
785
+ }, s = () => t, c = { setState: n, getState: s, getInitialState: () => d, subscribe: (y) => (r.add(y), () => r.delete(y)) }, d = t = e(n, s, c);
786
+ return c;
787
+ }, ot = ((e) => e ? Te(e) : Te), it = (e) => e;
788
+ function xe(e, t = it) {
789
+ const r = k.useSyncExternalStore(
790
+ e.subscribe,
791
+ k.useCallback(() => t(e.getState()), [e, t]),
792
+ k.useCallback(() => t(e.getInitialState()), [e, t])
793
+ );
794
+ return k.useDebugValue(r), r;
795
+ }
796
+ const Se = (e) => Symbol.iterator in e, Oe = (e) => (
797
+ // HACK: avoid checking entries type
798
+ "entries" in e
799
+ ), Re = (e, t) => {
800
+ const r = e instanceof Map ? e : new Map(e.entries()), n = t instanceof Map ? t : new Map(t.entries());
801
+ if (r.size !== n.size)
802
+ return !1;
803
+ for (const [s, o] of r)
804
+ if (!n.has(s) || !Object.is(o, n.get(s)))
805
+ return !1;
806
+ return !0;
807
+ }, at = (e, t) => {
808
+ const r = e[Symbol.iterator](), n = t[Symbol.iterator]();
809
+ let s = r.next(), o = n.next();
810
+ for (; !s.done && !o.done; ) {
811
+ if (!Object.is(s.value, o.value))
812
+ return !1;
813
+ s = r.next(), o = n.next();
814
+ }
815
+ return !!s.done && !!o.done;
816
+ };
817
+ function ct(e, t) {
818
+ return Object.is(e, t) ? !0 : typeof e != "object" || e === null || typeof t != "object" || t === null || Object.getPrototypeOf(e) !== Object.getPrototypeOf(t) ? !1 : Se(e) && Se(t) ? Oe(e) && Oe(t) ? Re(e, t) : at(e, t) : Re(
819
+ { entries: () => Object.entries(e) },
820
+ { entries: () => Object.entries(t) }
821
+ );
822
+ }
823
+ function ut(e) {
824
+ const t = k.useRef(void 0);
825
+ return (r) => {
826
+ const n = e(r);
827
+ return ct(t.current, n) ? t.current : t.current = n;
828
+ };
829
+ }
830
+ const lt = (e, t, { ditto: r, userId: n, userCollectionKey: s }) => {
831
+ const o = {
832
+ usersLoading: !0,
833
+ currentUser: null,
834
+ userObserver: null,
835
+ userSubscription: null,
836
+ allUsers: [],
837
+ allUsersObserver: null,
838
+ allUsersSubscription: null,
839
+ /**
840
+ * Add a new user to the users collection.
841
+ *
842
+ * This method creates or updates a user document in the Ditto store.
843
+ * Uses upsert behavior to handle both new and existing users.
844
+ *
845
+ * Workflow:
846
+ * 1. Validates Ditto is initialized
847
+ * 2. Executes INSERT with ON ID CONFLICT DO UPDATE
848
+ * 3. Creates new user or updates existing if ID matches
849
+ *
850
+ * @param user - User object (with optional _id for upsert)
851
+ */
852
+ async addUser(a) {
853
+ if (r)
854
+ try {
855
+ await r.store.execute(
856
+ `INSERT INTO ${s} DOCUMENTS (:newUser) ON ID CONFLICT DO UPDATE`,
857
+ { newUser: a }
858
+ );
859
+ } catch (c) {
860
+ console.error("Error in addUser:", c);
861
+ }
862
+ },
863
+ /**
864
+ * Update an existing user's fields.
865
+ *
866
+ * This method merges patch data with existing user data and persists.
867
+ * Only the specified fields are updated; others remain unchanged.
868
+ *
869
+ * Workflow:
870
+ * 1. Validates Ditto and user ID
871
+ * 2. Fetches current user data
872
+ * 3. Merges patch with existing user
873
+ * 4. Persists updated user document
874
+ *
875
+ * @param params - Object with _id and fields to update
876
+ */
877
+ async updateUser({ _id: a, ...c }) {
878
+ if (r && a)
879
+ try {
880
+ const d = await t().findUserById(a);
881
+ if (!d)
882
+ return;
883
+ const y = { ...d, ...c };
884
+ await r.store.execute(
885
+ `INSERT INTO ${s} DOCUMENTS (:newUser) ON ID CONFLICT DO UPDATE`,
886
+ { newUser: y }
887
+ );
888
+ } catch (d) {
889
+ console.error("Error in updateUser:", d);
890
+ }
891
+ },
892
+ /**
893
+ * Find a user by their unique ID.
894
+ *
895
+ * This method queries the users collection for a specific user.
896
+ *
897
+ * Workflow:
898
+ * 1. Validates Ditto is initialized
899
+ * 2. Executes SELECT query with user ID
900
+ * 3. Returns user object or null if not found
901
+ *
902
+ * @param userId - The unique identifier of the user
903
+ * @returns Promise resolving to ChatUser or null
904
+ */
905
+ async findUserById(a) {
906
+ if (!r)
907
+ return null;
908
+ try {
909
+ return (await r.store.execute(
910
+ `SELECT * FROM ${s} WHERE _id = :id`,
911
+ { id: a }
912
+ )).items?.[0]?.value;
913
+ } catch (c) {
914
+ return console.error("Error in findUserById:", c), null;
915
+ }
916
+ },
917
+ /**
918
+ * Mark a room's messages as read for the current user.
919
+ *
920
+ * This method updates the user's subscription timestamp and clears
921
+ * any pending mentions for the specified room.
922
+ *
923
+ * Workflow:
924
+ * 1. Validates Ditto and user ID
925
+ * 2. Fetches current user data
926
+ * 3. Updates subscription timestamp if subscribed
927
+ * 4. Clears mentions array for the room
928
+ * 5. Persists updated user document
929
+ *
930
+ * @param roomId - ID of the room to mark as read
931
+ */
932
+ async markRoomAsRead(a) {
933
+ if (!(!r || !n))
934
+ try {
935
+ let c = !1;
936
+ const d = await t().findUserById(n);
937
+ if (!d)
938
+ return;
939
+ const y = d.subscriptions || {}, p = d.mentions || {};
940
+ if (y[a]) {
941
+ const S = (/* @__PURE__ */ new Date()).toISOString();
942
+ y[a] = S, c = !0;
943
+ }
944
+ p[a] && (p[a] = [], c = !0), c && await t().updateUser({ _id: n, subscriptions: y, mentions: p });
945
+ } catch (c) {
946
+ console.error("Error in markRoomAsRead:", c);
947
+ }
948
+ },
949
+ /**
950
+ * Toggle subscription status for a room.
951
+ *
952
+ * This method subscribes to or unsubscribes from a room's notifications.
953
+ * When subscribed, user receives notifications for new messages.
954
+ *
955
+ * Workflow:
956
+ * 1. Checks canSubscribeToRoom permission
957
+ * 2. Fetches current user data
958
+ * 3. Toggles subscription (null = unsubscribed, timestamp = subscribed)
959
+ * 4. Persists updated subscriptions map
960
+ *
961
+ * @param roomId - ID of the room to toggle subscription for
962
+ */
963
+ async toggleRoomSubscription(a) {
964
+ if (!(!r || !n)) {
965
+ if (!t().canPerformAction("canSubscribeToRoom")) {
966
+ console.warn("Permission denied: canSubscribeToRoom is false");
967
+ return;
968
+ }
969
+ try {
970
+ const c = await t().findUserById(n);
971
+ if (!c)
972
+ return;
973
+ const d = { ...c.subscriptions };
974
+ if (a in d && d[a] !== null)
975
+ d[a] = null;
976
+ else {
977
+ const y = (/* @__PURE__ */ new Date()).toISOString();
978
+ d[a] = y;
979
+ }
980
+ await t().updateUser({
981
+ _id: n,
982
+ subscriptions: d
983
+ });
984
+ } catch (c) {
985
+ console.error("Error in toggleRoomSubscription:", c);
986
+ }
987
+ }
988
+ }
989
+ };
990
+ if (r) {
991
+ const a = `SELECT * FROM ${s} WHERE _id = :id`, c = `SELECT * FROM ${s}`, d = { id: n };
992
+ o.userSubscription = r.sync.registerSubscription(
993
+ a,
994
+ d
995
+ ), o.userObserver = r.store.registerObserver(
996
+ a,
997
+ (y) => {
998
+ e({ currentUser: y.items?.[0]?.value });
999
+ },
1000
+ d
1001
+ ), o.allUsersSubscription = r.sync.registerSubscription(c), o.allUsersObserver = r.store.registerObserver(
1002
+ c,
1003
+ (y) => {
1004
+ e({
1005
+ allUsers: y.items.map((p) => p.value),
1006
+ usersLoading: !1
1007
+ });
1008
+ }
1009
+ );
1010
+ }
1011
+ return o;
1012
+ }, Ie = 30, ft = 1e4, G = 282, mt = (e, t, {
1013
+ ditto: r,
1014
+ userId: n,
1015
+ userCollectionKey: s,
1016
+ retentionDays: o,
1017
+ notificationHandler: a
1018
+ }) => {
1019
+ const c = async (i) => {
1020
+ if (!r)
1021
+ throw new Error("Ditto not initialized");
1022
+ const u = await r.store.execute(
1023
+ `SELECT * FROM ${i.collectionId || "rooms"} WHERE _id = :id`,
1024
+ { id: i._id }
1025
+ );
1026
+ if (u.items.length === 0)
1027
+ throw new Error("Room not found");
1028
+ return u.items[0].value;
1029
+ }, d = async () => {
1030
+ if (!r || !n)
1031
+ throw new Error("Ditto not initialized or user not found");
1032
+ const u = (await r.store.execute(
1033
+ `SELECT * FROM ${s} WHERE _id = :id`,
1034
+ { id: n }
1035
+ )).items?.[0]?.value;
1036
+ return {
1037
+ id: n,
1038
+ name: u?.name ?? n
1039
+ };
1040
+ }, y = () => {
1041
+ e((i) => C(i, (u) => {
1042
+ u.messagesLoading = !u.rooms.every(
1043
+ (l) => l._id in u.messagesByRoom
1044
+ );
1045
+ }));
1046
+ }, p = (i, u, l, f) => {
1047
+ const m = {
1048
+ message: l,
1049
+ user: f,
1050
+ id: l._id
1051
+ }, g = {
1052
+ ...m,
1053
+ message: m.message,
1054
+ user: m.user
1055
+ }, b = i.findIndex((h) => h.id === l._id);
1056
+ if (b === -1 && !l.archivedMessage && S(l, u) && a && t().activeRoomId !== u._id) {
1057
+ const _ = f?.name || "Unknown User", w = u.name, D = u.collectionId === "dm_rooms" ? `New message from ${_}` : `#${w}: ${_}`, A = l.text ? l.text.substring(0, 30) + (l.text.length > 30 ? "..." : "") : "Sent an attachment";
1058
+ a(D, A);
1059
+ }
1060
+ if (l.archivedMessage) {
1061
+ const h = i.findIndex(
1062
+ (_) => _.id === l.archivedMessage
1063
+ );
1064
+ h !== -1 ? i[h] = g : b === -1 ? i.push(g) : i[b] = g;
1065
+ } else
1066
+ b === -1 ? i.push(g) : i[b] = g;
1067
+ }, S = (i, u) => {
1068
+ const f = t().currentUser, m = i.userId === f?._id, g = new Date(i.createdOn).getTime() > Date.now() - ft, b = f?.subscriptions?.[u._id], h = i.mentions?.some(
1069
+ (w) => w.userId === f?._id
1070
+ ), _ = u.collectionId === "dm_rooms";
1071
+ return !m && g && (b || _ || h);
1072
+ }, x = async (i, u, l = "") => {
1073
+ if (!r || !n)
1074
+ throw new Error("Ditto not initialized or user not found");
1075
+ const f = await c(i), m = u._id || Ne(), g = {
1076
+ _id: m,
1077
+ roomId: i._id,
1078
+ userId: n,
1079
+ createdOn: (/* @__PURE__ */ new Date()).toISOString(),
1080
+ isArchived: !1,
1081
+ archivedMessage: null,
1082
+ largeImageToken: null,
1083
+ thumbnailImageToken: null,
1084
+ fileAttachmentToken: null,
1085
+ isEdited: !1,
1086
+ isDeleted: !1,
1087
+ ...u,
1088
+ mentions: u.mentions?.map((h) => ({ ...h })) || [],
1089
+ reactions: u.reactions?.map((h) => ({ ...h })) || []
1090
+ }, b = l ? `COLLECTION ${f.messagesId} ${l}` : f.messagesId;
1091
+ if (await r.store.execute(
1092
+ `INSERT INTO ${b} DOCUMENTS (:newDoc) ON ID CONFLICT DO UPDATE`,
1093
+ { newDoc: g }
1094
+ ), u.mentions && u.mentions.length > 0) {
1095
+ const h = t().allUsers;
1096
+ await Promise.all(
1097
+ u.mentions.map((_) => {
1098
+ const w = h.find((D) => D._id === _.userId);
1099
+ if (!w)
1100
+ return;
1101
+ const R = {
1102
+ ...w.mentions || {},
1103
+ [i._id]: [...w.mentions?.[i._id] || [], m]
1104
+ };
1105
+ return r.store.execute(
1106
+ "UPDATE users SET mentions = :mentions WHERE _id = :id",
1107
+ { id: w._id, mentions: R }
1108
+ );
1109
+ })
1110
+ );
1111
+ }
1112
+ return g;
1113
+ }, L = async (i, u, l) => {
1114
+ if (!r)
1115
+ throw new Error("Ditto not initialized");
1116
+ return await r.store.execute(
1117
+ `UPDATE ${u.messagesId} SET isArchived = :isArchived WHERE _id = :id`,
1118
+ { id: i._id, isArchived: !0 }
1119
+ ), x(u, {
1120
+ text: "",
1121
+ createdOn: i.createdOn,
1122
+ archivedMessage: i._id,
1123
+ ...l
1124
+ });
1125
+ }, de = async (i, u, l, f) => {
1126
+ if (!r || !n)
1127
+ throw new Error("Ditto not initialized or user not found");
1128
+ const m = await d(), g = {
1129
+ thumbnailImageToken: null,
1130
+ largeImageToken: null,
1131
+ fileAttachmentToken: null
1132
+ };
1133
+ if (f === "image") {
1134
+ const h = await dt(u), _ = await De(h), w = new Uint8Array(await _.arrayBuffer());
1135
+ g.thumbnailImageToken = await r.store.newAttachment(
1136
+ w,
1137
+ se(m.id, m.name, "thumbnail", u)
1138
+ );
1139
+ const R = await De(await ke(u)), D = new Uint8Array(await R.arrayBuffer());
1140
+ g.largeImageToken = await r.store.newAttachment(
1141
+ D,
1142
+ se(m.id, m.name, "large", u)
1143
+ );
1144
+ } else {
1145
+ const h = new Uint8Array(await u.arrayBuffer());
1146
+ g.fileAttachmentToken = await r.store.newAttachment(
1147
+ h,
1148
+ se(m.id, m.name, "file", u)
1149
+ );
1150
+ }
1151
+ const b = f === "image" ? "(thumbnailImageToken ATTACHMENT, largeImageToken ATTACHMENT)" : "(fileAttachmentToken ATTACHMENT)";
1152
+ await x(
1153
+ i,
1154
+ {
1155
+ text: l || (f === "file" ? u.name : ""),
1156
+ ...g
1157
+ },
1158
+ b
1159
+ );
1160
+ };
1161
+ return {
1162
+ messagesLoading: !0,
1163
+ messagesByRoom: {},
1164
+ messageObserversByRoom: {},
1165
+ messageSubscriptionsByRoom: {},
1166
+ notificationHandler: null,
1167
+ /**
1168
+ * Register a notification handler for new messages.
1169
+ *
1170
+ * This method sets up a callback to be invoked when new messages
1171
+ * arrive and meet notification criteria (subscribed rooms, mentions, DMs).
1172
+ *
1173
+ * Use case: Displaying toast notifications or browser notifications
1174
+ *
1175
+ * @param handler - Callback function receiving (title, preview) parameters
1176
+ */
1177
+ registerNotificationHandler(i) {
1178
+ e({ notificationHandler: i });
1179
+ },
1180
+ /**
1181
+ * Subscribe to and observe messages for a room.
1182
+ *
1183
+ * This method sets up real-time sync and observation for messages in a room.
1184
+ * It's automatically called for each room when the chat store initializes.
1185
+ *
1186
+ * Workflow:
1187
+ * 1. Validates Ditto is initialized and no existing subscription
1188
+ * 2. Calculates retention date based on room/global settings
1189
+ * 3. Creates sync subscription for message documents
1190
+ * 4. Sets up store observer to update state on changes
1191
+ * 5. Triggers notifications for new messages from other users
1192
+ *
1193
+ * @param room - Room to subscribe to messages for
1194
+ * @param retentionDays - Optional override for message retention period
1195
+ */
1196
+ async messagesPublisher(i, u) {
1197
+ if (!r)
1198
+ return;
1199
+ const l = i._id;
1200
+ if (t().messageSubscriptionsByRoom[l])
1201
+ return;
1202
+ const f = u ?? i.retentionDays ?? o ?? Ie, m = new Date(
1203
+ Date.now() - f * 24 * 60 * 60 * 1e3
1204
+ ), g = `SELECT * FROM COLLECTION ${i.messagesId} (thumbnailImageToken ATTACHMENT, largeImageToken ATTACHMENT, fileAttachmentToken ATTACHMENT)
1205
+ WHERE roomId = :roomId AND createdOn >= :date AND isArchived = false
1206
+ ORDER BY createdOn ASC`, b = {
1207
+ roomId: i._id,
1208
+ date: m.toISOString()
1209
+ };
1210
+ try {
1211
+ const h = r.sync.registerSubscription(g, b), _ = t().allUsers, w = r.store.registerObserver(
1212
+ g,
1213
+ async (R) => {
1214
+ e((D) => C(D, (A) => {
1215
+ if (A.messagesByRoom[i._id] || (A.messagesByRoom[i._id] = []), R.items.length === 0)
1216
+ return A;
1217
+ for (const v of R.items) {
1218
+ const $ = v.value, j = A.messagesByRoom[i._id], J = _.find((K) => K._id === $.userId);
1219
+ p(j, i, $, J);
1220
+ }
1221
+ return A;
1222
+ })), y();
1223
+ },
1224
+ b
1225
+ );
1226
+ e({
1227
+ messageSubscriptionsByRoom: {
1228
+ ...t().messageSubscriptionsByRoom,
1229
+ [l]: h
1230
+ },
1231
+ messageObserversByRoom: {
1232
+ ...t().messageObserversByRoom,
1233
+ [l]: w
1234
+ }
1235
+ });
1236
+ } catch (h) {
1237
+ console.error("Error in messagesPublisher:", h);
1238
+ }
1239
+ },
1240
+ /**
1241
+ * Subscribe to messages for a specific room on-demand.
1242
+ *
1243
+ * This method enables dynamic subscription management for generated rooms.
1244
+ * Unlike messagesPublisher which is called automatically for all rooms,
1245
+ * this method is explicitly called when a component mounts to view a room.
1246
+ *
1247
+ * Workflow:
1248
+ * 1. Validates Ditto is initialized
1249
+ * 2. Checks for existing subscription (prevents duplicates)
1250
+ * 3. Creates subscription and observer using same logic as messagesPublisher
1251
+ * 4. Stores subscription/observer references in state
1252
+ *
1253
+ * Use case: ChatView component mounting to display a generated room
1254
+ *
1255
+ * @param roomId - Room ID to subscribe to
1256
+ * @param messagesId - Collection ID for messages ("messages" or "dm_messages")
1257
+ * @param retentionDays - Optional message retention override
1258
+ */
1259
+ async subscribeToRoomMessages(i, u, l) {
1260
+ if (!r || t().messageSubscriptionsByRoom[i])
1261
+ return;
1262
+ const f = l ?? o ?? Ie, m = new Date(
1263
+ Date.now() - f * 24 * 60 * 60 * 1e3
1264
+ ), g = `SELECT * FROM COLLECTION ${u} (thumbnailImageToken ATTACHMENT, largeImageToken ATTACHMENT, fileAttachmentToken ATTACHMENT)
1265
+ WHERE roomId = :roomId AND createdOn >= :date AND isArchived = false
1266
+ ORDER BY createdOn ASC`, b = {
1267
+ roomId: i,
1268
+ date: m.toISOString()
1269
+ };
1270
+ try {
1271
+ const h = r.sync.registerSubscription(g, b), _ = t().allUsers, w = r.store.registerObserver(
1272
+ g,
1273
+ async (R) => {
1274
+ const D = {
1275
+ _id: i,
1276
+ name: "",
1277
+ messagesId: u,
1278
+ collectionId: "rooms",
1279
+ createdBy: "",
1280
+ createdOn: "",
1281
+ isGenerated: !0
1282
+ };
1283
+ e((A) => C(A, (v) => {
1284
+ if (v.messagesByRoom[i] || (v.messagesByRoom[i] = []), R.items.length === 0)
1285
+ return v;
1286
+ for (const $ of R.items) {
1287
+ const j = $.value, J = v.messagesByRoom[i], K = _.find((Be) => Be._id === j.userId);
1288
+ p(J, D, j, K);
1289
+ }
1290
+ return v;
1291
+ })), y();
1292
+ },
1293
+ b
1294
+ );
1295
+ e({
1296
+ messageSubscriptionsByRoom: {
1297
+ ...t().messageSubscriptionsByRoom,
1298
+ [i]: h
1299
+ },
1300
+ messageObserversByRoom: {
1301
+ ...t().messageObserversByRoom,
1302
+ [i]: w
1303
+ }
1304
+ });
1305
+ } catch (h) {
1306
+ console.error("Error in subscribeToRoomMessages:", h);
1307
+ }
1308
+ },
1309
+ /**
1310
+ * Unsubscribe from messages for a specific room.
1311
+ *
1312
+ * This method cleans up subscriptions when a component unmounts.
1313
+ * Essential for preventing memory leaks with dynamic subscriptions.
1314
+ *
1315
+ * Workflow:
1316
+ * 1. Retrieves subscription and observer from state
1317
+ * 2. Cancels subscription (stops sync)
1318
+ * 3. Cancels observer (stops state updates)
1319
+ * 4. Removes messages from state
1320
+ * 5. Removes subscription/observer references from state
1321
+ *
1322
+ * Use case: ChatView component unmounting after viewing a generated room
1323
+ *
1324
+ * @param roomId - Room ID to unsubscribe from
1325
+ */
1326
+ unsubscribeFromRoomMessages(i) {
1327
+ const u = t().messageSubscriptionsByRoom[i], l = t().messageObserversByRoom[i];
1328
+ u && !u.isCancelled && u.cancel(), l && !l.isCancelled && l.cancel(), e((f) => C(f, (m) => (delete m.messagesByRoom[i], delete m.messageSubscriptionsByRoom[i], delete m.messageObserversByRoom[i], m)));
1329
+ },
1330
+ /**
1331
+ * Create a new text message in a room.
1332
+ *
1333
+ * This method creates and persists a new message document with optional mentions.
1334
+ * Mention permission is checked before including mentions in the message.
1335
+ *
1336
+ * Workflow:
1337
+ * 1. Validates Ditto connection and user
1338
+ * 2. Checks mention permission if mentions provided
1339
+ * 3. Creates message document with text and filtered mentions
1340
+ * 4. Updates mentioned users' mention records
1341
+ *
1342
+ * @param room - Room to create message in
1343
+ * @param text - Message text content
1344
+ * @param mentions - Optional array of user mentions
1345
+ */
1346
+ async createMessage(i, u, l = []) {
1347
+ if (!r || !n)
1348
+ return;
1349
+ const f = t().canPerformAction("canMentionUsers"), m = l.length > 0 && !f ? [] : l;
1350
+ l.length > 0 && !f && console.warn("Permission denied: canMentionUsers is false");
1351
+ try {
1352
+ await x(i, {
1353
+ text: u,
1354
+ mentions: m
1355
+ });
1356
+ } catch (g) {
1357
+ console.error("Error in createMessage:", g);
1358
+ }
1359
+ },
1360
+ /**
1361
+ * Save edits to an existing message.
1362
+ *
1363
+ * This method archives the original message and creates a new version
1364
+ * with updated content while preserving attachments and reactions.
1365
+ *
1366
+ * Workflow:
1367
+ * 1. Checks canEditOwnMessage permission
1368
+ * 2. Archives original message (sets isArchived = true)
1369
+ * 3. Creates new message with archivedMessage reference
1370
+ * 4. Preserves attachments, mentions, and reactions
1371
+ *
1372
+ * @param message - Message with updated text content
1373
+ * @param room - Room containing the message
1374
+ */
1375
+ async saveEditedTextMessage(i, u) {
1376
+ if (!t().canPerformAction("canEditOwnMessage")) {
1377
+ console.warn("Permission denied: canEditOwnMessage is false");
1378
+ return;
1379
+ }
1380
+ try {
1381
+ await L(i, u, {
1382
+ text: i.text,
1383
+ largeImageToken: i.largeImageToken || null,
1384
+ thumbnailImageToken: i.thumbnailImageToken || null,
1385
+ fileAttachmentToken: i.fileAttachmentToken || null,
1386
+ isEdited: !0,
1387
+ isDeleted: !1,
1388
+ mentions: i.mentions,
1389
+ reactions: i.reactions
1390
+ });
1391
+ } catch (l) {
1392
+ console.error("Error in saveEditedTextMessage:", l);
1393
+ }
1394
+ },
1395
+ /**
1396
+ * Soft-delete a message by replacing its content.
1397
+ *
1398
+ * This method archives the original message and creates a placeholder
1399
+ * indicating deletion. Attachments are removed and content is replaced.
1400
+ *
1401
+ * Workflow:
1402
+ * 1. Checks canDeleteOwnMessage permission
1403
+ * 2. Archives original message
1404
+ * 3. Creates replacement with "[deleted ...]" text
1405
+ * 4. Removes all attachments and mentions
1406
+ *
1407
+ * @param message - Message to delete
1408
+ * @param room - Room containing the message
1409
+ * @param type - Type of content: 'text', 'image', or 'file'
1410
+ */
1411
+ async saveDeletedMessage(i, u, l = "text") {
1412
+ if (!t().canPerformAction("canDeleteOwnMessage")) {
1413
+ console.warn("Permission denied: canDeleteOwnMessage is false");
1414
+ return;
1415
+ }
1416
+ const f = {
1417
+ text: "[deleted message]",
1418
+ image: "[deleted image]",
1419
+ file: "[deleted file]"
1420
+ }[l];
1421
+ try {
1422
+ await L(i, u, {
1423
+ text: f,
1424
+ createdOn: (/* @__PURE__ */ new Date()).toISOString(),
1425
+ largeImageToken: null,
1426
+ thumbnailImageToken: null,
1427
+ fileAttachmentToken: null,
1428
+ isEdited: !1,
1429
+ isDeleted: !0,
1430
+ mentions: []
1431
+ });
1432
+ } catch (m) {
1433
+ console.error("Error in saveDeletedMessage:", m);
1434
+ }
1435
+ },
1436
+ /**
1437
+ * Create a message with an image attachment.
1438
+ *
1439
+ * This method creates a message with both thumbnail and full-size
1440
+ * image attachments for efficient loading in the UI.
1441
+ *
1442
+ * Workflow:
1443
+ * 1. Creates thumbnail version of image
1444
+ * 2. Uploads both thumbnail and large image as attachments
1445
+ * 3. Creates message document with attachment tokens
1446
+ *
1447
+ * @param room - Room to create message in
1448
+ * @param imageFile - Image file to attach
1449
+ * @param text - Optional caption text
1450
+ */
1451
+ async createImageMessage(i, u, l) {
1452
+ try {
1453
+ await de(i, u, l, "image");
1454
+ } catch (f) {
1455
+ console.error("Error in createImageMessage:", f);
1456
+ }
1457
+ },
1458
+ /**
1459
+ * Create a message with a file attachment.
1460
+ *
1461
+ * This method uploads a file and creates a message with the
1462
+ * file attachment token. The filename is used as default text.
1463
+ *
1464
+ * Workflow:
1465
+ * 1. Uploads file as attachment with metadata
1466
+ * 2. Creates message document with file attachment token
1467
+ * 3. Uses filename as message text if not provided
1468
+ *
1469
+ * @param room - Room to create message in
1470
+ * @param file - File to attach
1471
+ * @param text - Optional text (defaults to filename)
1472
+ */
1473
+ async createFileMessage(i, u, l) {
1474
+ try {
1475
+ await de(i, u, l, "file");
1476
+ } catch (f) {
1477
+ console.error("Error in createFileMessage:", f);
1478
+ }
1479
+ },
1480
+ /**
1481
+ * Fetch attachment data with progress tracking.
1482
+ *
1483
+ * This method downloads attachment data from Ditto's attachment store,
1484
+ * providing progress updates during download and handling various states.
1485
+ *
1486
+ * Workflow:
1487
+ * 1. Validates Ditto and token are available
1488
+ * 2. Initiates attachment fetch with event handler
1489
+ * 3. Reports progress via onProgress callback
1490
+ * 4. Returns data and metadata on completion
1491
+ * 5. Handles deletion and error states
1492
+ *
1493
+ * @param token - Attachment token from message
1494
+ * @param onProgress - Callback receiving progress (0-1)
1495
+ * @param onComplete - Callback receiving AttachmentResult
1496
+ */
1497
+ fetchAttachment(i, u, l) {
1498
+ if (!r) {
1499
+ l({
1500
+ success: !1,
1501
+ error: new Error("Ditto not initialized")
1502
+ });
1503
+ return;
1504
+ }
1505
+ if (!i) {
1506
+ l({
1507
+ success: !1,
1508
+ error: new Error("No attachment token provided")
1509
+ });
1510
+ return;
1511
+ }
1512
+ try {
1513
+ r.store.fetchAttachment(i, async (f) => {
1514
+ switch (f.type) {
1515
+ case "Progress": {
1516
+ const m = Number(f.downloadedBytes) / (Number(f.totalBytes) || 1);
1517
+ u(m);
1518
+ break;
1519
+ }
1520
+ case "Completed":
1521
+ try {
1522
+ const m = f.attachment.getData(), g = m instanceof Promise ? await m : m;
1523
+ l({
1524
+ success: !0,
1525
+ data: g,
1526
+ metadata: f.attachment.metadata || {}
1527
+ });
1528
+ } catch (m) {
1529
+ l({
1530
+ success: !1,
1531
+ error: m instanceof Error ? m : new Error("Unknown error getting attachment data")
1532
+ });
1533
+ }
1534
+ break;
1535
+ case "Deleted":
1536
+ l({
1537
+ success: !1,
1538
+ error: new Error("Attachment was deleted")
1539
+ });
1540
+ break;
1541
+ }
1542
+ });
1543
+ } catch (f) {
1544
+ l({
1545
+ success: !1,
1546
+ error: f instanceof Error ? f : new Error("Failed to fetch attachment")
1547
+ });
1548
+ }
1549
+ },
1550
+ /**
1551
+ * Updates message reactions using an optimistic update pattern.
1552
+ *
1553
+ * This function implements optimistic UI updates to provide instant feedback
1554
+ * to users without waiting for database operations to complete. The pattern
1555
+ * works in two phases:
1556
+ *
1557
+ * 1. **Optimistic Update (Immediate)**:
1558
+ * - Updates the local state immediately with the new reactions
1559
+ * - Provides instant UI feedback for a responsive user experience
1560
+ * - Users see their reaction changes without any delay
1561
+ *
1562
+ * 2. **Database Persistence (Async)**:
1563
+ * - Asynchronously persists the changes to the Ditto database
1564
+ * - If the database update fails, automatically rolls back the UI changes
1565
+ * - Restores the previous reactions state to maintain data consistency
1566
+ *
1567
+ * This approach ensures:
1568
+ * - Quick UI interactions without perceived latency
1569
+ * - Data consistency through automatic rollback on errors
1570
+ * - Better user experience compared to waiting for DB operations
1571
+ *
1572
+ * @param message - The message whose reactions are being updated
1573
+ * @param room - The room containing the message
1574
+ * @param reactions - The new array of reactions to apply
1575
+ */
1576
+ async updateMessageReactions(i, u, l) {
1577
+ if (!r || !n)
1578
+ return;
1579
+ const f = u._id, m = t().messagesByRoom[f] || [], g = m.findIndex((_) => _.id === i._id);
1580
+ if (g === -1)
1581
+ throw new Error("Message not found");
1582
+ const b = m[g].message, h = b.reactions || [];
1583
+ e(
1584
+ (_) => C(_, (w) => {
1585
+ w.messagesByRoom[f][g].message.reactions = l;
1586
+ })
1587
+ ), setTimeout(async () => {
1588
+ try {
1589
+ await r.store.execute(
1590
+ `UPDATE ${u.messagesId} SET reactions = :reactions WHERE _id = :id`,
1591
+ { id: b._id, reactions: l }
1592
+ );
1593
+ } catch (_) {
1594
+ console.error("Error updating reactions, rolling back:", _), e(
1595
+ (w) => C(w, (R) => {
1596
+ R.messagesByRoom[f][g].message.reactions = h;
1597
+ })
1598
+ );
1599
+ }
1600
+ }, 0);
1601
+ },
1602
+ /**
1603
+ * Add a reaction to a message.
1604
+ *
1605
+ * This method adds a new reaction to a message's reactions array
1606
+ * using optimistic updates for instant UI feedback.
1607
+ *
1608
+ * Workflow:
1609
+ * 1. Checks canAddReaction permission
1610
+ * 2. Finds message in state
1611
+ * 3. Appends reaction to existing reactions
1612
+ * 4. Calls updateMessageReactions for optimistic update
1613
+ *
1614
+ * @param message - Message to add reaction to
1615
+ * @param room - Room containing the message
1616
+ * @param reaction - Reaction object with emoji and userId
1617
+ */
1618
+ async addReactionToMessage(i, u, l) {
1619
+ if (!r || !n)
1620
+ return;
1621
+ if (!t().canPerformAction("canAddReaction")) {
1622
+ console.warn("Permission denied: canAddReaction is false");
1623
+ return;
1624
+ }
1625
+ const f = t().messagesByRoom[u._id], m = f.findIndex((h) => h.id === i._id);
1626
+ if (m === -1)
1627
+ throw new Error("Message not found");
1628
+ const g = f[m].message, b = [...g.reactions || [], l];
1629
+ await t().updateMessageReactions(g, u, b);
1630
+ },
1631
+ /**
1632
+ * Remove a reaction from a message.
1633
+ *
1634
+ * This method removes a user's reaction from a message using
1635
+ * optimistic updates for instant UI feedback.
1636
+ *
1637
+ * Workflow:
1638
+ * 1. Checks canRemoveOwnReaction permission
1639
+ * 2. Finds message in state
1640
+ * 3. Filters out matching reaction (emoji + userId)
1641
+ * 4. Calls updateMessageReactions for optimistic update
1642
+ *
1643
+ * @param message - Message to remove reaction from
1644
+ * @param room - Room containing the message
1645
+ * @param reaction - Reaction to remove (matched by emoji and userId)
1646
+ */
1647
+ async removeReactionFromMessage(i, u, l) {
1648
+ if (!r || !n)
1649
+ return;
1650
+ if (!t().canPerformAction("canRemoveOwnReaction")) {
1651
+ console.warn("Permission denied: canRemoveOwnReaction is false");
1652
+ return;
1653
+ }
1654
+ const f = t().messagesByRoom[u._id], m = f.findIndex((h) => h.id === i._id);
1655
+ if (m === -1)
1656
+ throw new Error("Message not found");
1657
+ const g = f[m].message, b = (g.reactions || []).filter(
1658
+ (h) => !(h.emoji === l.emoji && h.userId === l.userId)
1659
+ );
1660
+ await t().updateMessageReactions(g, u, b);
1661
+ }
1662
+ };
1663
+ };
1664
+ async function dt(e) {
1665
+ const t = await ke(e), r = document.createElement("canvas"), n = r.getContext("2d");
1666
+ if (!n)
1667
+ throw new Error("Failed to get 2D context");
1668
+ let { width: s, height: o } = t;
1669
+ return s > o ? (o = G / s * o, s = G) : (s = G / o * s, o = G), r.width = s, r.height = o, n.drawImage(t, 0, 0, s, o), r;
1670
+ }
1671
+ function ke(e) {
1672
+ return new Promise((t, r) => {
1673
+ const n = new FileReader();
1674
+ n.onload = (s) => {
1675
+ const o = new Image();
1676
+ o.onload = () => t(o), o.onerror = r, o.src = String(s.target?.result);
1677
+ }, n.onerror = r, n.readAsDataURL(e);
1678
+ });
1679
+ }
1680
+ async function De(e) {
1681
+ return new Promise((t, r) => {
1682
+ const n = (s) => {
1683
+ s.toBlob(
1684
+ (o) => o ? t(o) : r(new Error("Failed to convert to blob")),
1685
+ "image/jpeg",
1686
+ 1
1687
+ );
1688
+ };
1689
+ if (e instanceof HTMLCanvasElement)
1690
+ n(e);
1691
+ else {
1692
+ const s = document.createElement("canvas");
1693
+ s.width = e.width, s.height = e.height;
1694
+ const o = s.getContext("2d");
1695
+ if (!o) {
1696
+ r(new Error("Failed to get 2D context"));
1697
+ return;
1698
+ }
1699
+ o.drawImage(e, 0, 0), n(s);
1700
+ }
1701
+ });
1702
+ }
1703
+ function se(e, t, r, n) {
1704
+ const s = (/* @__PURE__ */ new Date()).toISOString(), o = t.replace(/\s/g, "-"), a = s.replace(/:/g, "-"), c = r === "file" ? n.name.split(".").pop() || "bin" : "jpg";
1705
+ return {
1706
+ filename: `${o}_${r}_${a}.${c}`,
1707
+ userId: e,
1708
+ username: t,
1709
+ fileformat: `.${c}`,
1710
+ filesize: n.size.toString(),
1711
+ timestamp: s,
1712
+ originalName: n.name
1713
+ };
1714
+ }
1715
+ function I(e) {
1716
+ e && !e.isCancelled && e.cancel();
1717
+ }
1718
+ function ht(e) {
1719
+ const t = Fe(() => (globalThis.__DITTO_CHAT_STORE__ ? console.log("Reusing EXISTING global chatStore instance") : (console.log("Creating NEW global chatStore instance"), globalThis.__DITTO_CHAT_STORE__ = ot()(
1720
+ (r, n) => ({
1721
+ ...st(r, n, e),
1722
+ ...lt(r, n, e),
1723
+ ...mt(r, n, e),
1724
+ ...He(r, n, e),
1725
+ activeRoomId: null,
1726
+ setActiveRoomId: (s) => r({ activeRoomId: s }),
1727
+ chatLogout: () => {
1728
+ const s = n();
1729
+ I(s.roomsSubscription), I(s.roomsObserver), I(s.dmRoomsSubscription), I(s.dmRoomsObserver), Object.values(s.messageSubscriptionsByRoom || {}).map(
1730
+ (o) => I(o)
1731
+ ), Object.values(s.messageObserversByRoom || {}).map(
1732
+ (o) => I(o)
1733
+ ), I(s.userObserver), I(s.userSubscription), I(s.allUsersObserver), I(s.allUsersSubscription);
1734
+ }
1735
+ })
1736
+ )), globalThis.__DITTO_CHAT_STORE__), [e]);
1737
+ return xe(t);
1738
+ }
1739
+ function bt(e) {
1740
+ if (!globalThis.__DITTO_CHAT_STORE__)
1741
+ throw new Error(
1742
+ "chatStore must be initialized before useDittoChatStore. use useDittoChat for initialization"
1743
+ );
1744
+ const t = ut(
1745
+ e || ((n) => n)
1746
+ );
1747
+ return xe(globalThis.__DITTO_CHAT_STORE__, t);
1748
+ }
1749
+ function _t() {
1750
+ return globalThis.__DITTO_CHAT_STORE__;
1751
+ }
1752
+ function wt() {
1753
+ globalThis.__DITTO_CHAT_STORE__ = void 0;
1754
+ }
1755
+ export {
1756
+ ze as DEFAULT_PERMISSIONS,
1757
+ I as cancelSubscriptionOrObserver,
1758
+ He as createRBACSlice,
1759
+ st as createRoomSlice,
1760
+ _t as getChatStore,
1761
+ wt as resetChatStore,
1762
+ ht as useDittoChat,
1763
+ bt as useDittoChatStore
1764
+ };