@comergehq/studio 0.1.21 → 0.1.23
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 +180 -100
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +180 -100
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/bubble/Bubble.tsx +2 -5
- package/src/core/services/supabase/realtimeManager.ts +112 -0
- package/src/data/apps/edit-queue/repository.ts +35 -40
- package/src/data/apps/repository.ts +28 -33
- package/src/data/messages/repository.ts +28 -33
package/dist/index.mjs
CHANGED
|
@@ -537,6 +537,95 @@ var BaseRepository = class {
|
|
|
537
537
|
}
|
|
538
538
|
};
|
|
539
539
|
|
|
540
|
+
// src/core/services/supabase/realtimeManager.ts
|
|
541
|
+
var INITIAL_BACKOFF_MS = 1e3;
|
|
542
|
+
var MAX_BACKOFF_MS = 3e4;
|
|
543
|
+
var realtimeLog = log.extend("realtime");
|
|
544
|
+
var entries = /* @__PURE__ */ new Map();
|
|
545
|
+
var subscriberIdCounter = 0;
|
|
546
|
+
function clearTimer(entry) {
|
|
547
|
+
if (!entry.timer) return;
|
|
548
|
+
clearTimeout(entry.timer);
|
|
549
|
+
entry.timer = null;
|
|
550
|
+
}
|
|
551
|
+
function buildChannel(entry) {
|
|
552
|
+
const supabase = getSupabaseClient();
|
|
553
|
+
const channel = supabase.channel(entry.key);
|
|
554
|
+
entry.subscribers.forEach((configure) => {
|
|
555
|
+
configure(channel);
|
|
556
|
+
});
|
|
557
|
+
return channel;
|
|
558
|
+
}
|
|
559
|
+
function scheduleResubscribe(entry, reason) {
|
|
560
|
+
if (entry.timer) return;
|
|
561
|
+
const delay = entry.backoffMs;
|
|
562
|
+
entry.backoffMs = Math.min(entry.backoffMs * 2, MAX_BACKOFF_MS);
|
|
563
|
+
realtimeLog.warn(`[realtime] channel ${entry.key} ${reason}; resubscribe in ${delay}ms`);
|
|
564
|
+
entry.timer = setTimeout(() => {
|
|
565
|
+
entry.timer = null;
|
|
566
|
+
if (!entries.has(entry.key)) return;
|
|
567
|
+
if (entry.subscribers.size === 0) return;
|
|
568
|
+
subscribeChannel(entry);
|
|
569
|
+
}, delay);
|
|
570
|
+
}
|
|
571
|
+
function handleStatus(entry, status) {
|
|
572
|
+
if (status === "SUBSCRIBED") {
|
|
573
|
+
entry.backoffMs = INITIAL_BACKOFF_MS;
|
|
574
|
+
clearTimer(entry);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (status === "CLOSED" || status === "TIMED_OUT" || status === "CHANNEL_ERROR") {
|
|
578
|
+
scheduleResubscribe(entry, status);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
function subscribeChannel(entry) {
|
|
582
|
+
try {
|
|
583
|
+
const supabase = getSupabaseClient();
|
|
584
|
+
if (entry.channel) supabase.removeChannel(entry.channel);
|
|
585
|
+
const channel = buildChannel(entry);
|
|
586
|
+
entry.channel = channel;
|
|
587
|
+
channel.subscribe((status) => handleStatus(entry, status));
|
|
588
|
+
} catch (error) {
|
|
589
|
+
realtimeLog.warn("[realtime] subscribe failed", error);
|
|
590
|
+
scheduleResubscribe(entry, "SUBSCRIBE_FAILED");
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
function subscribeManagedChannel(key, configure) {
|
|
594
|
+
let entry = entries.get(key);
|
|
595
|
+
if (!entry) {
|
|
596
|
+
entry = {
|
|
597
|
+
key,
|
|
598
|
+
channel: null,
|
|
599
|
+
subscribers: /* @__PURE__ */ new Map(),
|
|
600
|
+
backoffMs: INITIAL_BACKOFF_MS,
|
|
601
|
+
timer: null
|
|
602
|
+
};
|
|
603
|
+
entries.set(key, entry);
|
|
604
|
+
}
|
|
605
|
+
const subscriberId = ++subscriberIdCounter;
|
|
606
|
+
entry.subscribers.set(subscriberId, configure);
|
|
607
|
+
if (!entry.channel) {
|
|
608
|
+
subscribeChannel(entry);
|
|
609
|
+
} else {
|
|
610
|
+
configure(entry.channel);
|
|
611
|
+
}
|
|
612
|
+
return () => {
|
|
613
|
+
const current = entries.get(key);
|
|
614
|
+
if (!current) return;
|
|
615
|
+
current.subscribers.delete(subscriberId);
|
|
616
|
+
if (current.subscribers.size === 0) {
|
|
617
|
+
clearTimer(current);
|
|
618
|
+
try {
|
|
619
|
+
if (current.channel) getSupabaseClient().removeChannel(current.channel);
|
|
620
|
+
} finally {
|
|
621
|
+
entries.delete(key);
|
|
622
|
+
}
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
subscribeChannel(current);
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
540
629
|
// src/data/apps/repository.ts
|
|
541
630
|
function mapDbAppRow(row) {
|
|
542
631
|
return {
|
|
@@ -618,35 +707,33 @@ var AppsRepositoryImpl = class extends BaseRepository {
|
|
|
618
707
|
return this.subscribeToAppChannel(`apps:id:${appId}`, `id=eq.${appId}`, handlers);
|
|
619
708
|
}
|
|
620
709
|
subscribeToAppChannel(channelKey, filter, handlers) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
supabase.removeChannel(channel);
|
|
649
|
-
};
|
|
710
|
+
return subscribeManagedChannel(channelKey, (channel) => {
|
|
711
|
+
channel.on(
|
|
712
|
+
"postgres_changes",
|
|
713
|
+
{ event: "INSERT", schema: "public", table: "app", filter },
|
|
714
|
+
(payload) => {
|
|
715
|
+
var _a;
|
|
716
|
+
console.log("[subscribeToAppChannel] onInsert", payload);
|
|
717
|
+
(_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.new));
|
|
718
|
+
}
|
|
719
|
+
).on(
|
|
720
|
+
"postgres_changes",
|
|
721
|
+
{ event: "UPDATE", schema: "public", table: "app", filter },
|
|
722
|
+
(payload) => {
|
|
723
|
+
var _a;
|
|
724
|
+
console.log("[subscribeToAppChannel] onUpdate", payload);
|
|
725
|
+
(_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.new));
|
|
726
|
+
}
|
|
727
|
+
).on(
|
|
728
|
+
"postgres_changes",
|
|
729
|
+
{ event: "DELETE", schema: "public", table: "app", filter },
|
|
730
|
+
(payload) => {
|
|
731
|
+
var _a;
|
|
732
|
+
console.log("[subscribeToAppChannel] onDelete", payload);
|
|
733
|
+
(_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.old));
|
|
734
|
+
}
|
|
735
|
+
);
|
|
736
|
+
});
|
|
650
737
|
}
|
|
651
738
|
};
|
|
652
739
|
var appsRepository = new AppsRepositoryImpl(appsRemoteDataSource);
|
|
@@ -777,35 +864,33 @@ var MessagesRepositoryImpl = class extends BaseRepository {
|
|
|
777
864
|
return this.unwrapOrThrow(res);
|
|
778
865
|
}
|
|
779
866
|
subscribeThread(threadId, handlers) {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
supabase.removeChannel(channel);
|
|
808
|
-
};
|
|
867
|
+
return subscribeManagedChannel(`messages:thread:${threadId}`, (channel) => {
|
|
868
|
+
channel.on(
|
|
869
|
+
"postgres_changes",
|
|
870
|
+
{ event: "INSERT", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
|
|
871
|
+
(payload) => {
|
|
872
|
+
var _a;
|
|
873
|
+
const row = payload.new;
|
|
874
|
+
(_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
|
|
875
|
+
}
|
|
876
|
+
).on(
|
|
877
|
+
"postgres_changes",
|
|
878
|
+
{ event: "UPDATE", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
|
|
879
|
+
(payload) => {
|
|
880
|
+
var _a;
|
|
881
|
+
const row = payload.new;
|
|
882
|
+
(_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
|
|
883
|
+
}
|
|
884
|
+
).on(
|
|
885
|
+
"postgres_changes",
|
|
886
|
+
{ event: "DELETE", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
|
|
887
|
+
(payload) => {
|
|
888
|
+
var _a;
|
|
889
|
+
const row = payload.old;
|
|
890
|
+
(_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
|
|
891
|
+
}
|
|
892
|
+
);
|
|
893
|
+
});
|
|
809
894
|
}
|
|
810
895
|
};
|
|
811
896
|
var messagesRepository = new MessagesRepositoryImpl(messagesRemoteDataSource);
|
|
@@ -2386,18 +2471,15 @@ function Bubble({
|
|
|
2386
2471
|
[height, rotation, scale, size, translateX, translateY]
|
|
2387
2472
|
);
|
|
2388
2473
|
const animateOut = useCallback8(() => {
|
|
2474
|
+
var _a;
|
|
2389
2475
|
if (isAnimatingOut.current) return;
|
|
2390
2476
|
isAnimatingOut.current = true;
|
|
2391
2477
|
try {
|
|
2392
2478
|
void Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
|
2393
2479
|
} catch {
|
|
2394
2480
|
}
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
var _a;
|
|
2398
|
-
(_a = onPressRef.current) == null ? void 0 : _a.call(onPressRef);
|
|
2399
|
-
}
|
|
2400
|
-
});
|
|
2481
|
+
(_a = onPressRef.current) == null ? void 0 : _a.call(onPressRef);
|
|
2482
|
+
animateToHidden();
|
|
2401
2483
|
}, [animateToHidden]);
|
|
2402
2484
|
useEffect11(() => {
|
|
2403
2485
|
if (isLoading) {
|
|
@@ -7357,42 +7439,40 @@ var EditQueueRepositoryImpl = class extends BaseRepository {
|
|
|
7357
7439
|
return this.unwrapOrThrow(res);
|
|
7358
7440
|
}
|
|
7359
7441
|
subscribeEditQueue(appId, handlers) {
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
supabase.removeChannel(channel);
|
|
7395
|
-
};
|
|
7442
|
+
return subscribeManagedChannel(`edit-queue:app:${appId}`, (channel) => {
|
|
7443
|
+
channel.on(
|
|
7444
|
+
"postgres_changes",
|
|
7445
|
+
{ event: "INSERT", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7446
|
+
(payload) => {
|
|
7447
|
+
var _a;
|
|
7448
|
+
const row = payload.new;
|
|
7449
|
+
if (row.kind !== "edit") return;
|
|
7450
|
+
const item = mapQueueItem(row);
|
|
7451
|
+
if (!ACTIVE_STATUSES.includes(item.status)) return;
|
|
7452
|
+
(_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, item);
|
|
7453
|
+
}
|
|
7454
|
+
).on(
|
|
7455
|
+
"postgres_changes",
|
|
7456
|
+
{ event: "UPDATE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7457
|
+
(payload) => {
|
|
7458
|
+
var _a, _b;
|
|
7459
|
+
const row = payload.new;
|
|
7460
|
+
if (row.kind !== "edit") return;
|
|
7461
|
+
const item = mapQueueItem(row);
|
|
7462
|
+
if (ACTIVE_STATUSES.includes(item.status)) (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, item);
|
|
7463
|
+
else (_b = handlers.onDelete) == null ? void 0 : _b.call(handlers, item);
|
|
7464
|
+
}
|
|
7465
|
+
).on(
|
|
7466
|
+
"postgres_changes",
|
|
7467
|
+
{ event: "DELETE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7468
|
+
(payload) => {
|
|
7469
|
+
var _a;
|
|
7470
|
+
const row = payload.old;
|
|
7471
|
+
if (row.kind !== "edit") return;
|
|
7472
|
+
(_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapQueueItem(row));
|
|
7473
|
+
}
|
|
7474
|
+
);
|
|
7475
|
+
});
|
|
7396
7476
|
}
|
|
7397
7477
|
};
|
|
7398
7478
|
var editQueueRepository = new EditQueueRepositoryImpl(
|