@liveblocks/core 3.2.1 → 3.3.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/index.js CHANGED
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
 
7
7
  // src/version.ts
8
8
  var PKG_NAME = "@liveblocks/core";
9
- var PKG_VERSION = "3.2.1";
9
+ var PKG_VERSION = "3.3.0";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -52,151 +52,101 @@ function detectDupes(pkgName, pkgVersion, pkgFormat) {
52
52
  }
53
53
  }
54
54
 
55
- // src/convert-plain-data.ts
56
- function convertToCommentData(data) {
57
- const editedAt = data.editedAt ? new Date(data.editedAt) : void 0;
58
- const createdAt = new Date(data.createdAt);
59
- const reactions = data.reactions.map((reaction) => ({
60
- ...reaction,
61
- createdAt: new Date(reaction.createdAt)
62
- }));
63
- if (data.body) {
64
- return {
65
- ...data,
66
- reactions,
67
- createdAt,
68
- editedAt
69
- };
70
- } else {
71
- const deletedAt = new Date(data.deletedAt);
72
- return {
73
- ...data,
74
- reactions,
75
- createdAt,
76
- editedAt,
77
- deletedAt
78
- };
55
+ // src/lib/EventSource.ts
56
+ function makeEventSource() {
57
+ const _observers = /* @__PURE__ */ new Set();
58
+ function subscribe(callback) {
59
+ _observers.add(callback);
60
+ return () => _observers.delete(callback);
79
61
  }
80
- }
81
- function convertToThreadData(data) {
82
- const createdAt = new Date(data.createdAt);
83
- const updatedAt = new Date(data.updatedAt);
84
- const comments = data.comments.map(
85
- (comment) => convertToCommentData(comment)
86
- );
87
- return {
88
- ...data,
89
- createdAt,
90
- updatedAt,
91
- comments
92
- };
93
- }
94
- function convertToCommentUserReaction(data) {
95
- return {
96
- ...data,
97
- createdAt: new Date(data.createdAt)
98
- };
99
- }
100
- function convertToInboxNotificationData(data) {
101
- const notifiedAt = new Date(data.notifiedAt);
102
- const readAt = data.readAt ? new Date(data.readAt) : null;
103
- if ("activities" in data) {
104
- const activities = data.activities.map((activity) => ({
105
- ...activity,
106
- createdAt: new Date(activity.createdAt)
107
- }));
108
- return {
109
- ...data,
110
- notifiedAt,
111
- readAt,
112
- activities
113
- };
62
+ function subscribeOnce(callback) {
63
+ const unsub = subscribe((event) => {
64
+ unsub();
65
+ return callback(event);
66
+ });
67
+ return unsub;
68
+ }
69
+ async function waitUntil(predicate) {
70
+ let unsub;
71
+ return new Promise((res) => {
72
+ unsub = subscribe((event) => {
73
+ if (predicate === void 0 || predicate(event)) {
74
+ res(event);
75
+ }
76
+ });
77
+ }).finally(() => unsub?.());
78
+ }
79
+ function notify(event) {
80
+ let called = false;
81
+ for (const callback of _observers) {
82
+ callback(event);
83
+ called = true;
84
+ }
85
+ return called;
86
+ }
87
+ function count() {
88
+ return _observers.size;
114
89
  }
115
90
  return {
116
- ...data,
117
- notifiedAt,
118
- readAt
119
- };
120
- }
121
- function convertToSubscriptionData(data) {
122
- const createdAt = new Date(data.createdAt);
123
- return {
124
- ...data,
125
- createdAt
126
- };
127
- }
128
- function convertToUserSubscriptionData(data) {
129
- const createdAt = new Date(data.createdAt);
130
- return {
131
- ...data,
132
- createdAt
133
- };
134
- }
135
- function convertToThreadDeleteInfo(data) {
136
- const deletedAt = new Date(data.deletedAt);
137
- return {
138
- ...data,
139
- deletedAt
140
- };
141
- }
142
- function convertToInboxNotificationDeleteInfo(data) {
143
- const deletedAt = new Date(data.deletedAt);
144
- return {
145
- ...data,
146
- deletedAt
91
+ // Private/internal control over event emission
92
+ notify,
93
+ subscribe,
94
+ subscribeOnce,
95
+ count,
96
+ waitUntil,
97
+ dispose() {
98
+ _observers.clear();
99
+ },
100
+ // Publicly exposable subscription API
101
+ observable: {
102
+ subscribe,
103
+ subscribeOnce,
104
+ waitUntil
105
+ }
147
106
  };
148
107
  }
149
- function convertToSubscriptionDeleteInfo(data) {
150
- const deletedAt = new Date(data.deletedAt);
108
+ function makeBufferableEventSource() {
109
+ const eventSource2 = makeEventSource();
110
+ let _buffer = null;
111
+ function pause() {
112
+ _buffer = [];
113
+ }
114
+ function unpause() {
115
+ if (_buffer === null) {
116
+ return;
117
+ }
118
+ for (const event of _buffer) {
119
+ eventSource2.notify(event);
120
+ }
121
+ _buffer = null;
122
+ }
123
+ function notifyOrBuffer(event) {
124
+ if (_buffer !== null) {
125
+ _buffer.push(event);
126
+ return false;
127
+ } else {
128
+ return eventSource2.notify(event);
129
+ }
130
+ }
151
131
  return {
152
- ...data,
153
- deletedAt
132
+ ...eventSource2,
133
+ notify: notifyOrBuffer,
134
+ pause,
135
+ unpause,
136
+ dispose() {
137
+ eventSource2.dispose();
138
+ if (_buffer !== null) {
139
+ _buffer.length = 0;
140
+ }
141
+ }
154
142
  };
155
143
  }
156
144
 
157
- // src/lib/fancy-console.ts
158
- var fancy_console_exports = {};
159
- __export(fancy_console_exports, {
160
- error: () => error2,
161
- errorWithTitle: () => errorWithTitle,
162
- warn: () => warn,
163
- warnWithTitle: () => warnWithTitle
164
- });
165
- var badge = "background:#0e0d12;border-radius:9999px;color:#fff;padding:3px 7px;font-family:sans-serif;font-weight:600;";
166
- var bold = "font-weight:600";
167
- function wrap(method) {
168
- return typeof window === "undefined" || process.env.NODE_ENV === "test" ? console[method] : (
169
- /* istanbul ignore next */
170
- (message, ...args) => console[method]("%cLiveblocks", badge, message, ...args)
171
- );
172
- }
173
- var warn = wrap("warn");
174
- var error2 = wrap("error");
175
- function wrapWithTitle(method) {
176
- return typeof window === "undefined" || process.env.NODE_ENV === "test" ? console[method] : (
177
- /* istanbul ignore next */
178
- (title, message, ...args) => console[method](
179
- `%cLiveblocks%c ${title}`,
180
- badge,
181
- bold,
182
- message,
183
- ...args
184
- )
185
- );
186
- }
187
- var warnWithTitle = wrapWithTitle("warn");
188
- var errorWithTitle = wrapWithTitle("error");
189
-
190
- // src/lib/guards.ts
191
- function isDefined(value) {
192
- return value !== null && value !== void 0;
193
- }
194
- function isPlainObject(blob) {
195
- return blob !== null && typeof blob === "object" && Object.prototype.toString.call(blob) === "[object Object]";
196
- }
197
- function isStartsWithOperator(blob) {
198
- return isPlainObject(blob) && typeof blob.startsWith === "string";
199
- }
145
+ // src/lib/freeze.ts
146
+ var freeze = process.env.NODE_ENV === "production" ? (
147
+ /* istanbul ignore next */
148
+ (x) => x
149
+ ) : Object.freeze;
200
150
 
201
151
  // src/lib/utils.ts
202
152
  function raise(msg) {
@@ -294,201 +244,22 @@ function memoizeOnSuccess(factoryFn) {
294
244
  };
295
245
  }
296
246
 
297
- // src/lib/autoRetry.ts
298
- var HttpError = class _HttpError extends Error {
299
- response;
300
- details;
301
- constructor(message, response, details) {
302
- super(message);
303
- this.name = "HttpError";
304
- this.response = response;
305
- this.details = details;
247
+ // src/lib/signals.ts
248
+ var kSinks = Symbol("kSinks");
249
+ var kTrigger = Symbol("kTrigger");
250
+ var signalsToTrigger = null;
251
+ var trackedReads = null;
252
+ function batch(callback) {
253
+ if (signalsToTrigger !== null) {
254
+ callback();
255
+ return;
306
256
  }
307
- static async fromResponse(response) {
308
- let bodyAsText;
309
- try {
310
- bodyAsText = await response.text();
311
- } catch {
312
- }
313
- const bodyAsJson = bodyAsText ? tryParseJson(bodyAsText) : void 0;
314
- let bodyAsJsonObject;
315
- if (isPlainObject(bodyAsJson)) {
316
- bodyAsJsonObject = bodyAsJson;
317
- }
318
- let message = "";
319
- message ||= typeof bodyAsJsonObject?.message === "string" ? bodyAsJsonObject.message : "";
320
- message ||= typeof bodyAsJsonObject?.error === "string" ? bodyAsJsonObject.error : "";
321
- if (bodyAsJson === void 0) {
322
- message ||= bodyAsText || "";
323
- }
324
- message ||= response.statusText;
325
- let path;
326
- try {
327
- path = new URL(response.url).pathname;
328
- } catch {
329
- }
330
- message += path !== void 0 ? ` (got status ${response.status} from ${path})` : ` (got status ${response.status})`;
331
- const details = bodyAsJsonObject;
332
- return new _HttpError(message, response, details);
333
- }
334
- /**
335
- * Convenience accessor for response.status.
336
- */
337
- get status() {
338
- return this.response.status;
339
- }
340
- };
341
- var DONT_RETRY_4XX = (x) => x instanceof HttpError && x.status >= 400 && x.status < 500;
342
- async function autoRetry(promiseFn, maxTries, backoff, shouldStopRetrying = DONT_RETRY_4XX) {
343
- const fallbackBackoff = backoff.length > 0 ? backoff[backoff.length - 1] : 0;
344
- let attempt = 0;
345
- while (true) {
346
- attempt++;
347
- try {
348
- return await promiseFn();
349
- } catch (err) {
350
- if (shouldStopRetrying(err)) {
351
- throw err;
352
- }
353
- if (attempt >= maxTries) {
354
- throw new Error(`Failed after ${maxTries} attempts: ${String(err)}`);
355
- }
356
- }
357
- const delay = backoff[attempt - 1] ?? fallbackBackoff;
358
- warn(
359
- `Attempt ${attempt} was unsuccessful. Retrying in ${delay} milliseconds.`
360
- );
361
- await wait(delay);
362
- }
363
- }
364
-
365
- // src/lib/controlledPromise.ts
366
- function controlledPromise() {
367
- let resolve;
368
- let reject;
369
- const promise = new Promise((res, rej) => {
370
- resolve = res;
371
- reject = rej;
372
- });
373
- return [promise, resolve, reject];
374
- }
375
- function Promise_withResolvers() {
376
- const [promise, resolve, reject] = controlledPromise();
377
- return { promise, resolve, reject };
378
- }
379
-
380
- // src/lib/EventSource.ts
381
- function makeEventSource() {
382
- const _observers = /* @__PURE__ */ new Set();
383
- function subscribe(callback) {
384
- _observers.add(callback);
385
- return () => _observers.delete(callback);
386
- }
387
- function subscribeOnce(callback) {
388
- const unsub = subscribe((event) => {
389
- unsub();
390
- return callback(event);
391
- });
392
- return unsub;
393
- }
394
- async function waitUntil(predicate) {
395
- let unsub;
396
- return new Promise((res) => {
397
- unsub = subscribe((event) => {
398
- if (predicate === void 0 || predicate(event)) {
399
- res(event);
400
- }
401
- });
402
- }).finally(() => unsub?.());
403
- }
404
- function notify(event) {
405
- let called = false;
406
- for (const callback of _observers) {
407
- callback(event);
408
- called = true;
409
- }
410
- return called;
411
- }
412
- function count() {
413
- return _observers.size;
414
- }
415
- return {
416
- // Private/internal control over event emission
417
- notify,
418
- subscribe,
419
- subscribeOnce,
420
- count,
421
- waitUntil,
422
- dispose() {
423
- _observers.clear();
424
- },
425
- // Publicly exposable subscription API
426
- observable: {
427
- subscribe,
428
- subscribeOnce,
429
- waitUntil
430
- }
431
- };
432
- }
433
- function makeBufferableEventSource() {
434
- const eventSource2 = makeEventSource();
435
- let _buffer = null;
436
- function pause() {
437
- _buffer = [];
438
- }
439
- function unpause() {
440
- if (_buffer === null) {
441
- return;
442
- }
443
- for (const event of _buffer) {
444
- eventSource2.notify(event);
445
- }
446
- _buffer = null;
447
- }
448
- function notifyOrBuffer(event) {
449
- if (_buffer !== null) {
450
- _buffer.push(event);
451
- return false;
452
- } else {
453
- return eventSource2.notify(event);
454
- }
455
- }
456
- return {
457
- ...eventSource2,
458
- notify: notifyOrBuffer,
459
- pause,
460
- unpause,
461
- dispose() {
462
- eventSource2.dispose();
463
- if (_buffer !== null) {
464
- _buffer.length = 0;
465
- }
466
- }
467
- };
468
- }
469
-
470
- // src/lib/freeze.ts
471
- var freeze = process.env.NODE_ENV === "production" ? (
472
- /* istanbul ignore next */
473
- (x) => x
474
- ) : Object.freeze;
475
-
476
- // src/lib/signals.ts
477
- var kSinks = Symbol("kSinks");
478
- var kTrigger = Symbol("kTrigger");
479
- var signalsToTrigger = null;
480
- var trackedReads = null;
481
- function batch(callback) {
482
- if (signalsToTrigger !== null) {
483
- callback();
484
- return;
485
- }
486
- signalsToTrigger = /* @__PURE__ */ new Set();
487
- try {
488
- callback();
489
- } finally {
490
- for (const signal of signalsToTrigger) {
491
- signal[kTrigger]();
257
+ signalsToTrigger = /* @__PURE__ */ new Set();
258
+ try {
259
+ callback();
260
+ } finally {
261
+ for (const signal of signalsToTrigger) {
262
+ signal[kTrigger]();
492
263
  }
493
264
  signalsToTrigger = null;
494
265
  }
@@ -695,64 +466,510 @@ var DerivedSignal = class _DerivedSignal extends AbstractSignal {
695
466
  this.#dirty = true;
696
467
  this.markSinksDirty();
697
468
  }
698
- }
699
- get() {
700
- if (this.#dirty) {
701
- this.#recompute();
469
+ }
470
+ get() {
471
+ if (this.#dirty) {
472
+ this.#recompute();
473
+ }
474
+ trackedReads?.add(this);
475
+ return this.#prevValue;
476
+ }
477
+ /**
478
+ * Called by the Signal system if one or more of the dependent signals have
479
+ * changed. In the case of a DerivedSignal, we'll only want to re-evaluate
480
+ * the actual value if it's being watched, or any of their sinks are being
481
+ * watched actively.
482
+ */
483
+ [kTrigger]() {
484
+ if (!this.hasWatchers) {
485
+ return;
486
+ }
487
+ const updated = this.#recompute();
488
+ if (updated) {
489
+ super[kTrigger]();
490
+ }
491
+ }
492
+ };
493
+ var MutableSignal = class extends AbstractSignal {
494
+ #state;
495
+ constructor(initialState) {
496
+ super();
497
+ this.#state = initialState;
498
+ }
499
+ dispose() {
500
+ super.dispose();
501
+ this.#state = "(disposed)";
502
+ }
503
+ get() {
504
+ trackedReads?.add(this);
505
+ return this.#state;
506
+ }
507
+ /**
508
+ * Invokes a callback function that is allowed to mutate the given state
509
+ * value. Do not change the value outside of the callback.
510
+ *
511
+ * If the callback explicitly returns `false`, it's assumed that the state
512
+ * was not changed.
513
+ */
514
+ mutate(callback) {
515
+ batch(() => {
516
+ const result = callback ? callback(this.#state) : true;
517
+ if (result !== null && typeof result === "object" && "then" in result) {
518
+ raise("MutableSignal.mutate() does not support async callbacks");
519
+ }
520
+ if (result !== false) {
521
+ this.markSinksDirty();
522
+ enqueueTrigger(this);
523
+ }
524
+ });
525
+ }
526
+ };
527
+
528
+ // src/lib/SortedList.ts
529
+ function bisectRight(arr, x, lt) {
530
+ let lo = 0;
531
+ let hi = arr.length;
532
+ while (lo < hi) {
533
+ const mid = lo + (hi - lo >> 1);
534
+ if (lt(x, arr[mid])) {
535
+ hi = mid;
536
+ } else {
537
+ lo = mid + 1;
538
+ }
539
+ }
540
+ return lo;
541
+ }
542
+ var SortedList = class _SortedList {
543
+ #data;
544
+ #lt;
545
+ constructor(alreadySortedList, lt) {
546
+ this.#lt = lt;
547
+ this.#data = alreadySortedList;
548
+ }
549
+ static with(lt) {
550
+ return _SortedList.fromAlreadySorted([], lt);
551
+ }
552
+ static from(arr, lt) {
553
+ const sorted = new _SortedList([], lt);
554
+ for (const item of arr) {
555
+ sorted.add(item);
556
+ }
557
+ return sorted;
558
+ }
559
+ static fromAlreadySorted(alreadySorted, lt) {
560
+ return new _SortedList(alreadySorted, lt);
561
+ }
562
+ /**
563
+ * Clones the sorted list to a new instance.
564
+ */
565
+ clone() {
566
+ return new _SortedList(this.#data.slice(), this.#lt);
567
+ }
568
+ /**
569
+ * Adds a new item to the sorted list, such that it remains sorted.
570
+ */
571
+ add(value) {
572
+ const idx = bisectRight(this.#data, value, this.#lt);
573
+ this.#data.splice(idx, 0, value);
574
+ }
575
+ /**
576
+ * Removes all values from the sorted list, making it empty again.
577
+ * Returns whether the list was mutated or not.
578
+ */
579
+ clear() {
580
+ const hadData = this.#data.length > 0;
581
+ this.#data.length = 0;
582
+ return hadData;
583
+ }
584
+ /**
585
+ * Removes the first value matching the predicate.
586
+ * Returns whether the list was mutated or not.
587
+ */
588
+ removeBy(predicate, limit = Number.POSITIVE_INFINITY) {
589
+ let deleted = 0;
590
+ for (let i = 0; i < this.#data.length; i++) {
591
+ if (predicate(this.#data[i])) {
592
+ this.#data.splice(i, 1);
593
+ deleted++;
594
+ if (deleted >= limit) {
595
+ break;
596
+ } else {
597
+ i--;
598
+ }
599
+ }
600
+ }
601
+ return deleted > 0;
602
+ }
603
+ /**
604
+ * Removes the given value from the sorted list, if it exists. The given
605
+ * value must be `===` to one of the list items. Only the first entry will be
606
+ * removed if the element exists in the sorted list multiple times.
607
+ *
608
+ * Returns whether the list was mutated or not.
609
+ */
610
+ remove(value) {
611
+ const idx = this.#data.indexOf(value);
612
+ if (idx >= 0) {
613
+ this.#data.splice(idx, 1);
614
+ return true;
615
+ }
616
+ return false;
617
+ }
618
+ at(index) {
619
+ return this.#data[index];
620
+ }
621
+ get length() {
622
+ return this.#data.length;
623
+ }
624
+ *filter(predicate) {
625
+ for (const item of this.#data) {
626
+ if (predicate(item)) {
627
+ yield item;
628
+ }
629
+ }
630
+ }
631
+ // XXXX If we keep this, add unit tests. Or remove it.
632
+ *findAllRight(predicate) {
633
+ for (let i = this.#data.length - 1; i >= 0; i--) {
634
+ const item = this.#data[i];
635
+ if (predicate(item, i)) {
636
+ yield item;
637
+ }
638
+ }
639
+ }
640
+ [Symbol.iterator]() {
641
+ return this.#data[Symbol.iterator]();
642
+ }
643
+ *iterReversed() {
644
+ for (let i = this.#data.length - 1; i >= 0; i--) {
645
+ yield this.#data[i];
646
+ }
647
+ }
648
+ /** Finds the leftmost item that matches the predicate. */
649
+ find(predicate, start) {
650
+ const idx = this.findIndex(predicate, start);
651
+ return idx > -1 ? this.#data.at(idx) : void 0;
652
+ }
653
+ /** Finds the leftmost index that matches the predicate. */
654
+ findIndex(predicate, start = 0) {
655
+ for (let i = Math.max(0, start); i < this.#data.length; i++) {
656
+ if (predicate(this.#data[i], i)) {
657
+ return i;
658
+ }
659
+ }
660
+ return -1;
661
+ }
662
+ /** Finds the rightmost item that matches the predicate. */
663
+ findRight(predicate, start) {
664
+ const idx = this.findIndexRight(predicate, start);
665
+ return idx > -1 ? this.#data.at(idx) : void 0;
666
+ }
667
+ /** Finds the rightmost index that matches the predicate. */
668
+ findIndexRight(predicate, start = this.#data.length - 1) {
669
+ for (let i = Math.min(start, this.#data.length - 1); i >= 0; i--) {
670
+ if (predicate(this.#data[i], i)) {
671
+ return i;
672
+ }
673
+ }
674
+ return -1;
675
+ }
676
+ get rawArray() {
677
+ return this.#data;
678
+ }
679
+ };
680
+
681
+ // src/AiChatDB.ts
682
+ var AiChatDB = class {
683
+ #byId;
684
+ // A map of chat id to chat details
685
+ #chats;
686
+ // Sorted list of non-deleted chats, most recent first
687
+ signal;
688
+ constructor() {
689
+ this.#byId = /* @__PURE__ */ new Map();
690
+ this.#chats = SortedList.from([], (c1, c2) => {
691
+ const d2 = c2.lastMessageAt ?? c2.createdAt;
692
+ const d1 = c1.lastMessageAt ?? c1.createdAt;
693
+ return d2 < d1 ? true : d2 === d1 ? c2.id < c1.id : false;
694
+ });
695
+ this.signal = new MutableSignal(this);
696
+ }
697
+ getEvenIfDeleted(chatId) {
698
+ return this.#byId.get(chatId);
699
+ }
700
+ markDeleted(chatId) {
701
+ const chat = this.#byId.get(chatId);
702
+ if (chat === void 0 || chat.deletedAt !== void 0) return;
703
+ this.upsert({
704
+ ...chat,
705
+ deletedAt: (/* @__PURE__ */ new Date()).toISOString()
706
+ });
707
+ }
708
+ upsert(chat) {
709
+ this.signal.mutate(() => {
710
+ const existingThread = this.#byId.get(chat.id);
711
+ if (existingThread !== void 0) {
712
+ if (existingThread.deletedAt !== void 0) return false;
713
+ this.#chats.remove(existingThread);
714
+ this.#byId.delete(existingThread.id);
715
+ }
716
+ if (chat.deletedAt === void 0) {
717
+ this.#chats.add(chat);
718
+ }
719
+ this.#byId.set(chat.id, chat);
720
+ return true;
721
+ });
722
+ }
723
+ findMany(query) {
724
+ return Array.from(
725
+ this.#chats.filter((chat) => {
726
+ if (query.metadata === void 0) return true;
727
+ for (const [key, value] of Object.entries(query.metadata)) {
728
+ if (value === null) {
729
+ if (key in chat.metadata) return false;
730
+ } else if (typeof value === "string") {
731
+ if (chat.metadata[key] !== value) return false;
732
+ } else {
733
+ const chatValue = chat.metadata[key];
734
+ if (!Array.isArray(chatValue) || !value.every((v) => chatValue.includes(v))) {
735
+ return false;
736
+ }
737
+ }
738
+ }
739
+ return true;
740
+ })
741
+ );
742
+ }
743
+ };
744
+
745
+ // src/convert-plain-data.ts
746
+ function convertToCommentData(data) {
747
+ const editedAt = data.editedAt ? new Date(data.editedAt) : void 0;
748
+ const createdAt = new Date(data.createdAt);
749
+ const reactions = data.reactions.map((reaction) => ({
750
+ ...reaction,
751
+ createdAt: new Date(reaction.createdAt)
752
+ }));
753
+ if (data.body) {
754
+ return {
755
+ ...data,
756
+ reactions,
757
+ createdAt,
758
+ editedAt
759
+ };
760
+ } else {
761
+ const deletedAt = new Date(data.deletedAt);
762
+ return {
763
+ ...data,
764
+ reactions,
765
+ createdAt,
766
+ editedAt,
767
+ deletedAt
768
+ };
769
+ }
770
+ }
771
+ function convertToThreadData(data) {
772
+ const createdAt = new Date(data.createdAt);
773
+ const updatedAt = new Date(data.updatedAt);
774
+ const comments = data.comments.map(
775
+ (comment) => convertToCommentData(comment)
776
+ );
777
+ return {
778
+ ...data,
779
+ createdAt,
780
+ updatedAt,
781
+ comments
782
+ };
783
+ }
784
+ function convertToCommentUserReaction(data) {
785
+ return {
786
+ ...data,
787
+ createdAt: new Date(data.createdAt)
788
+ };
789
+ }
790
+ function convertToInboxNotificationData(data) {
791
+ const notifiedAt = new Date(data.notifiedAt);
792
+ const readAt = data.readAt ? new Date(data.readAt) : null;
793
+ if ("activities" in data) {
794
+ const activities = data.activities.map((activity) => ({
795
+ ...activity,
796
+ createdAt: new Date(activity.createdAt)
797
+ }));
798
+ return {
799
+ ...data,
800
+ notifiedAt,
801
+ readAt,
802
+ activities
803
+ };
804
+ }
805
+ return {
806
+ ...data,
807
+ notifiedAt,
808
+ readAt
809
+ };
810
+ }
811
+ function convertToSubscriptionData(data) {
812
+ const createdAt = new Date(data.createdAt);
813
+ return {
814
+ ...data,
815
+ createdAt
816
+ };
817
+ }
818
+ function convertToUserSubscriptionData(data) {
819
+ const createdAt = new Date(data.createdAt);
820
+ return {
821
+ ...data,
822
+ createdAt
823
+ };
824
+ }
825
+ function convertToThreadDeleteInfo(data) {
826
+ const deletedAt = new Date(data.deletedAt);
827
+ return {
828
+ ...data,
829
+ deletedAt
830
+ };
831
+ }
832
+ function convertToInboxNotificationDeleteInfo(data) {
833
+ const deletedAt = new Date(data.deletedAt);
834
+ return {
835
+ ...data,
836
+ deletedAt
837
+ };
838
+ }
839
+ function convertToSubscriptionDeleteInfo(data) {
840
+ const deletedAt = new Date(data.deletedAt);
841
+ return {
842
+ ...data,
843
+ deletedAt
844
+ };
845
+ }
846
+
847
+ // src/lib/fancy-console.ts
848
+ var fancy_console_exports = {};
849
+ __export(fancy_console_exports, {
850
+ error: () => error2,
851
+ errorWithTitle: () => errorWithTitle,
852
+ warn: () => warn,
853
+ warnWithTitle: () => warnWithTitle
854
+ });
855
+ var badge = "background:#0e0d12;border-radius:9999px;color:#fff;padding:3px 7px;font-family:sans-serif;font-weight:600;";
856
+ var bold = "font-weight:600";
857
+ function wrap(method) {
858
+ return typeof window === "undefined" || process.env.NODE_ENV === "test" ? console[method] : (
859
+ /* istanbul ignore next */
860
+ (message, ...args) => console[method]("%cLiveblocks", badge, message, ...args)
861
+ );
862
+ }
863
+ var warn = wrap("warn");
864
+ var error2 = wrap("error");
865
+ function wrapWithTitle(method) {
866
+ return typeof window === "undefined" || process.env.NODE_ENV === "test" ? console[method] : (
867
+ /* istanbul ignore next */
868
+ (title, message, ...args) => console[method](
869
+ `%cLiveblocks%c ${title}`,
870
+ badge,
871
+ bold,
872
+ message,
873
+ ...args
874
+ )
875
+ );
876
+ }
877
+ var warnWithTitle = wrapWithTitle("warn");
878
+ var errorWithTitle = wrapWithTitle("error");
879
+
880
+ // src/lib/guards.ts
881
+ function isDefined(value) {
882
+ return value !== null && value !== void 0;
883
+ }
884
+ function isPlainObject(blob) {
885
+ return blob !== null && typeof blob === "object" && Object.prototype.toString.call(blob) === "[object Object]";
886
+ }
887
+ function isStartsWithOperator(blob) {
888
+ return isPlainObject(blob) && typeof blob.startsWith === "string";
889
+ }
890
+
891
+ // src/lib/autoRetry.ts
892
+ var HttpError = class _HttpError extends Error {
893
+ response;
894
+ details;
895
+ constructor(message, response, details) {
896
+ super(message);
897
+ this.name = "HttpError";
898
+ this.response = response;
899
+ this.details = details;
900
+ }
901
+ static async fromResponse(response) {
902
+ let bodyAsText;
903
+ try {
904
+ bodyAsText = await response.text();
905
+ } catch {
906
+ }
907
+ const bodyAsJson = bodyAsText ? tryParseJson(bodyAsText) : void 0;
908
+ let bodyAsJsonObject;
909
+ if (isPlainObject(bodyAsJson)) {
910
+ bodyAsJsonObject = bodyAsJson;
911
+ }
912
+ let message = "";
913
+ message ||= typeof bodyAsJsonObject?.message === "string" ? bodyAsJsonObject.message : "";
914
+ message ||= typeof bodyAsJsonObject?.error === "string" ? bodyAsJsonObject.error : "";
915
+ if (bodyAsJson === void 0) {
916
+ message ||= bodyAsText || "";
917
+ }
918
+ message ||= response.statusText;
919
+ let path;
920
+ try {
921
+ path = new URL(response.url).pathname;
922
+ } catch {
702
923
  }
703
- trackedReads?.add(this);
704
- return this.#prevValue;
924
+ message += path !== void 0 ? ` (got status ${response.status} from ${path})` : ` (got status ${response.status})`;
925
+ const details = bodyAsJsonObject;
926
+ return new _HttpError(message, response, details);
705
927
  }
706
928
  /**
707
- * Called by the Signal system if one or more of the dependent signals have
708
- * changed. In the case of a DerivedSignal, we'll only want to re-evaluate
709
- * the actual value if it's being watched, or any of their sinks are being
710
- * watched actively.
929
+ * Convenience accessor for response.status.
711
930
  */
712
- [kTrigger]() {
713
- if (!this.hasWatchers) {
714
- return;
715
- }
716
- const updated = this.#recompute();
717
- if (updated) {
718
- super[kTrigger]();
719
- }
931
+ get status() {
932
+ return this.response.status;
720
933
  }
721
934
  };
722
- var MutableSignal = class extends AbstractSignal {
723
- #state;
724
- constructor(initialState) {
725
- super();
726
- this.#state = initialState;
727
- }
728
- dispose() {
729
- super.dispose();
730
- this.#state = "(disposed)";
731
- }
732
- get() {
733
- trackedReads?.add(this);
734
- return this.#state;
735
- }
736
- /**
737
- * Invokes a callback function that is allowed to mutate the given state
738
- * value. Do not change the value outside of the callback.
739
- *
740
- * If the callback explicitly returns `false`, it's assumed that the state
741
- * was not changed.
742
- */
743
- mutate(callback) {
744
- batch(() => {
745
- const result = callback ? callback(this.#state) : true;
746
- if (result !== null && typeof result === "object" && "then" in result) {
747
- raise("MutableSignal.mutate() does not support async callbacks");
935
+ var DONT_RETRY_4XX = (x) => x instanceof HttpError && x.status >= 400 && x.status < 500;
936
+ async function autoRetry(promiseFn, maxTries, backoff, shouldStopRetrying = DONT_RETRY_4XX) {
937
+ const fallbackBackoff = backoff.length > 0 ? backoff[backoff.length - 1] : 0;
938
+ let attempt = 0;
939
+ while (true) {
940
+ attempt++;
941
+ try {
942
+ return await promiseFn();
943
+ } catch (err) {
944
+ if (shouldStopRetrying(err)) {
945
+ throw err;
748
946
  }
749
- if (result !== false) {
750
- this.markSinksDirty();
751
- enqueueTrigger(this);
947
+ if (attempt >= maxTries) {
948
+ throw new Error(`Failed after ${maxTries} attempts: ${String(err)}`);
752
949
  }
753
- });
950
+ }
951
+ const delay = backoff[attempt - 1] ?? fallbackBackoff;
952
+ warn(
953
+ `Attempt ${attempt} was unsuccessful. Retrying in ${delay} milliseconds.`
954
+ );
955
+ await wait(delay);
754
956
  }
755
- };
957
+ }
958
+
959
+ // src/lib/controlledPromise.ts
960
+ function controlledPromise() {
961
+ let resolve;
962
+ let reject;
963
+ const promise = new Promise((res, rej) => {
964
+ resolve = res;
965
+ reject = rej;
966
+ });
967
+ return [promise, resolve, reject];
968
+ }
969
+ function Promise_withResolvers() {
970
+ const [promise, resolve, reject] = controlledPromise();
971
+ return { promise, resolve, reject };
972
+ }
756
973
 
757
974
  // src/lib/stringify.ts
758
975
  function replacer(_key, value) {
@@ -3398,159 +3615,6 @@ function shallow2(a, b) {
3398
3615
  );
3399
3616
  }
3400
3617
 
3401
- // src/lib/SortedList.ts
3402
- function bisectRight(arr, x, lt) {
3403
- let lo = 0;
3404
- let hi = arr.length;
3405
- while (lo < hi) {
3406
- const mid = lo + (hi - lo >> 1);
3407
- if (lt(x, arr[mid])) {
3408
- hi = mid;
3409
- } else {
3410
- lo = mid + 1;
3411
- }
3412
- }
3413
- return lo;
3414
- }
3415
- var SortedList = class _SortedList {
3416
- #data;
3417
- #lt;
3418
- constructor(alreadySortedList, lt) {
3419
- this.#lt = lt;
3420
- this.#data = alreadySortedList;
3421
- }
3422
- static with(lt) {
3423
- return _SortedList.fromAlreadySorted([], lt);
3424
- }
3425
- static from(arr, lt) {
3426
- const sorted = new _SortedList([], lt);
3427
- for (const item of arr) {
3428
- sorted.add(item);
3429
- }
3430
- return sorted;
3431
- }
3432
- static fromAlreadySorted(alreadySorted, lt) {
3433
- return new _SortedList(alreadySorted, lt);
3434
- }
3435
- /**
3436
- * Clones the sorted list to a new instance.
3437
- */
3438
- clone() {
3439
- return new _SortedList(this.#data.slice(), this.#lt);
3440
- }
3441
- /**
3442
- * Adds a new item to the sorted list, such that it remains sorted.
3443
- */
3444
- add(value) {
3445
- const idx = bisectRight(this.#data, value, this.#lt);
3446
- this.#data.splice(idx, 0, value);
3447
- }
3448
- /**
3449
- * Removes all values from the sorted list, making it empty again.
3450
- * Returns whether the list was mutated or not.
3451
- */
3452
- clear() {
3453
- const hadData = this.#data.length > 0;
3454
- this.#data.length = 0;
3455
- return hadData;
3456
- }
3457
- /**
3458
- * Removes the first value matching the predicate.
3459
- * Returns whether the list was mutated or not.
3460
- */
3461
- removeBy(predicate, limit = Number.POSITIVE_INFINITY) {
3462
- let deleted = 0;
3463
- for (let i = 0; i < this.#data.length; i++) {
3464
- if (predicate(this.#data[i])) {
3465
- this.#data.splice(i, 1);
3466
- deleted++;
3467
- if (deleted >= limit) {
3468
- break;
3469
- } else {
3470
- i--;
3471
- }
3472
- }
3473
- }
3474
- return deleted > 0;
3475
- }
3476
- /**
3477
- * Removes the given value from the sorted list, if it exists. The given
3478
- * value must be `===` to one of the list items. Only the first entry will be
3479
- * removed if the element exists in the sorted list multiple times.
3480
- *
3481
- * Returns whether the list was mutated or not.
3482
- */
3483
- remove(value) {
3484
- const idx = this.#data.indexOf(value);
3485
- if (idx >= 0) {
3486
- this.#data.splice(idx, 1);
3487
- return true;
3488
- }
3489
- return false;
3490
- }
3491
- at(index) {
3492
- return this.#data[index];
3493
- }
3494
- get length() {
3495
- return this.#data.length;
3496
- }
3497
- *filter(predicate) {
3498
- for (const item of this.#data) {
3499
- if (predicate(item)) {
3500
- yield item;
3501
- }
3502
- }
3503
- }
3504
- // XXXX If we keep this, add unit tests. Or remove it.
3505
- *findAllRight(predicate) {
3506
- for (let i = this.#data.length - 1; i >= 0; i--) {
3507
- const item = this.#data[i];
3508
- if (predicate(item, i)) {
3509
- yield item;
3510
- }
3511
- }
3512
- }
3513
- [Symbol.iterator]() {
3514
- return this.#data[Symbol.iterator]();
3515
- }
3516
- *iterReversed() {
3517
- for (let i = this.#data.length - 1; i >= 0; i--) {
3518
- yield this.#data[i];
3519
- }
3520
- }
3521
- /** Finds the leftmost item that matches the predicate. */
3522
- find(predicate, start) {
3523
- const idx = this.findIndex(predicate, start);
3524
- return idx > -1 ? this.#data.at(idx) : void 0;
3525
- }
3526
- /** Finds the leftmost index that matches the predicate. */
3527
- findIndex(predicate, start = 0) {
3528
- for (let i = Math.max(0, start); i < this.#data.length; i++) {
3529
- if (predicate(this.#data[i], i)) {
3530
- return i;
3531
- }
3532
- }
3533
- return -1;
3534
- }
3535
- /** Finds the rightmost item that matches the predicate. */
3536
- findRight(predicate, start) {
3537
- const idx = this.findIndexRight(predicate, start);
3538
- return idx > -1 ? this.#data.at(idx) : void 0;
3539
- }
3540
- /** Finds the rightmost index that matches the predicate. */
3541
- findIndexRight(predicate, start = this.#data.length - 1) {
3542
- for (let i = Math.min(start, this.#data.length - 1); i >= 0; i--) {
3543
- if (predicate(this.#data[i], i)) {
3544
- return i;
3545
- }
3546
- }
3547
- return -1;
3548
- }
3549
- get rawArray() {
3550
- return this.#data;
3551
- }
3552
- };
3553
-
3554
3618
  // src/lib/TreePool.ts
3555
3619
  var TreePool = class {
3556
3620
  #_items;
@@ -4151,39 +4215,29 @@ function createStore_forChatMessages(toolsStore, setToolResultFn) {
4151
4215
  };
4152
4216
  }
4153
4217
  function createStore_forUserAiChats() {
4154
- const allChatsInclDeleted\u03A3 = new MutableSignal(
4155
- SortedList.with((x, y) => y.createdAt < x.createdAt)
4156
- );
4157
- const nonDeletedChats\u03A3 = DerivedSignal.from(
4158
- () => Array.from(allChatsInclDeleted\u03A3.get()).filter((c) => !c.deletedAt)
4159
- );
4218
+ const chatsDB = new AiChatDB();
4160
4219
  function upsertMany(chats) {
4161
- allChatsInclDeleted\u03A3.mutate((list) => {
4220
+ batch(() => {
4162
4221
  for (const chat of chats) {
4163
- list.removeBy((c) => c.id === chat.id, 1);
4164
- list.add(chat);
4222
+ chatsDB.upsert(chat);
4165
4223
  }
4166
4224
  });
4167
4225
  }
4168
4226
  function upsert(chat) {
4169
- upsertMany([chat]);
4227
+ chatsDB.upsert(chat);
4170
4228
  }
4171
4229
  function markDeleted(chatId) {
4172
- allChatsInclDeleted\u03A3.mutate((list) => {
4173
- const chat = list.find((c) => c.id === chatId);
4174
- if (!chat) return false;
4175
- upsert({ ...chat, deletedAt: now() });
4176
- return void 0;
4177
- });
4230
+ chatsDB.markDeleted(chatId);
4178
4231
  }
4179
4232
  function getChatById(chatId) {
4180
- return Array.from(allChatsInclDeleted\u03A3.get()).find(
4181
- (chat) => chat.id === chatId
4182
- );
4233
+ return chatsDB.getEvenIfDeleted(chatId);
4234
+ }
4235
+ function findMany(query) {
4236
+ return chatsDB.signal.get().findMany(query);
4183
4237
  }
4184
4238
  return {
4185
- chats\u03A3: nonDeletedChats\u03A3,
4186
4239
  getChatById,
4240
+ findMany,
4187
4241
  // Mutations
4188
4242
  upsert,
4189
4243
  upsertMany,
@@ -4396,7 +4450,8 @@ function createAi(config) {
4396
4450
  function getChats(options = {}) {
4397
4451
  return sendClientMsgWithResponse({
4398
4452
  cmd: "get-chats",
4399
- cursor: options.cursor
4453
+ cursor: options.cursor,
4454
+ query: options.query
4400
4455
  });
4401
4456
  }
4402
4457
  function getOrCreateChat(id, options) {
@@ -4461,7 +4516,9 @@ function createAi(config) {
4461
4516
  deleteMessage: (chatId, messageId) => sendClientMsgWithResponse({ cmd: "delete-message", chatId, messageId }),
4462
4517
  clearChat: (chatId) => sendClientMsgWithResponse({ cmd: "clear-chat", chatId }),
4463
4518
  askUserMessageInChat: async (chatId, userMessage, targetMessageId, options) => {
4464
- const knowledge = context.knowledge.get();
4519
+ const globalKnowledge = context.knowledge.get();
4520
+ const requestKnowledge = options?.knowledge || [];
4521
+ const combinedKnowledge = [...globalKnowledge, ...requestKnowledge];
4465
4522
  const tools = context.toolsStore.getToolDescriptions(chatId);
4466
4523
  const resp = await sendClientMsgWithResponse({
4467
4524
  cmd: "ask-in-chat",
@@ -4472,9 +4529,8 @@ function createAi(config) {
4472
4529
  copilotId: options?.copilotId,
4473
4530
  stream: options?.stream,
4474
4531
  timeout: options?.timeout,
4475
- // Knowledge and tools aren't coming from the options, but retrieved
4476
- // from the global context
4477
- knowledge: knowledge.length > 0 ? knowledge : void 0,
4532
+ // Combine global knowledge with request-specific knowledge
4533
+ knowledge: combinedKnowledge.length > 0 ? combinedKnowledge : void 0,
4478
4534
  tools: tools.length > 0 ? tools : void 0
4479
4535
  }
4480
4536
  });
@@ -4485,11 +4541,11 @@ function createAi(config) {
4485
4541
  setToolResult,
4486
4542
  getStatus: () => managedSocket.getStatus(),
4487
4543
  signals: {
4488
- chats\u03A3: context.chatsStore.chats\u03A3,
4489
4544
  getChatMessagesForBranch\u03A3: context.messagesStore.getChatMessagesForBranch\u03A3,
4490
4545
  getTool\u03A3: context.toolsStore.getTool\u03A3
4491
4546
  },
4492
4547
  getChatById: context.chatsStore.getChatById,
4548
+ queryChats: context.chatsStore.findMany,
4493
4549
  registerKnowledgeLayer,
4494
4550
  deregisterKnowledgeLayer,
4495
4551
  updateKnowledge,