@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.js
CHANGED
|
@@ -567,6 +567,95 @@ var BaseRepository = class {
|
|
|
567
567
|
}
|
|
568
568
|
};
|
|
569
569
|
|
|
570
|
+
// src/core/services/supabase/realtimeManager.ts
|
|
571
|
+
var INITIAL_BACKOFF_MS = 1e3;
|
|
572
|
+
var MAX_BACKOFF_MS = 3e4;
|
|
573
|
+
var realtimeLog = log.extend("realtime");
|
|
574
|
+
var entries = /* @__PURE__ */ new Map();
|
|
575
|
+
var subscriberIdCounter = 0;
|
|
576
|
+
function clearTimer(entry) {
|
|
577
|
+
if (!entry.timer) return;
|
|
578
|
+
clearTimeout(entry.timer);
|
|
579
|
+
entry.timer = null;
|
|
580
|
+
}
|
|
581
|
+
function buildChannel(entry) {
|
|
582
|
+
const supabase = getSupabaseClient();
|
|
583
|
+
const channel = supabase.channel(entry.key);
|
|
584
|
+
entry.subscribers.forEach((configure) => {
|
|
585
|
+
configure(channel);
|
|
586
|
+
});
|
|
587
|
+
return channel;
|
|
588
|
+
}
|
|
589
|
+
function scheduleResubscribe(entry, reason) {
|
|
590
|
+
if (entry.timer) return;
|
|
591
|
+
const delay = entry.backoffMs;
|
|
592
|
+
entry.backoffMs = Math.min(entry.backoffMs * 2, MAX_BACKOFF_MS);
|
|
593
|
+
realtimeLog.warn(`[realtime] channel ${entry.key} ${reason}; resubscribe in ${delay}ms`);
|
|
594
|
+
entry.timer = setTimeout(() => {
|
|
595
|
+
entry.timer = null;
|
|
596
|
+
if (!entries.has(entry.key)) return;
|
|
597
|
+
if (entry.subscribers.size === 0) return;
|
|
598
|
+
subscribeChannel(entry);
|
|
599
|
+
}, delay);
|
|
600
|
+
}
|
|
601
|
+
function handleStatus(entry, status) {
|
|
602
|
+
if (status === "SUBSCRIBED") {
|
|
603
|
+
entry.backoffMs = INITIAL_BACKOFF_MS;
|
|
604
|
+
clearTimer(entry);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (status === "CLOSED" || status === "TIMED_OUT" || status === "CHANNEL_ERROR") {
|
|
608
|
+
scheduleResubscribe(entry, status);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
function subscribeChannel(entry) {
|
|
612
|
+
try {
|
|
613
|
+
const supabase = getSupabaseClient();
|
|
614
|
+
if (entry.channel) supabase.removeChannel(entry.channel);
|
|
615
|
+
const channel = buildChannel(entry);
|
|
616
|
+
entry.channel = channel;
|
|
617
|
+
channel.subscribe((status) => handleStatus(entry, status));
|
|
618
|
+
} catch (error) {
|
|
619
|
+
realtimeLog.warn("[realtime] subscribe failed", error);
|
|
620
|
+
scheduleResubscribe(entry, "SUBSCRIBE_FAILED");
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function subscribeManagedChannel(key, configure) {
|
|
624
|
+
let entry = entries.get(key);
|
|
625
|
+
if (!entry) {
|
|
626
|
+
entry = {
|
|
627
|
+
key,
|
|
628
|
+
channel: null,
|
|
629
|
+
subscribers: /* @__PURE__ */ new Map(),
|
|
630
|
+
backoffMs: INITIAL_BACKOFF_MS,
|
|
631
|
+
timer: null
|
|
632
|
+
};
|
|
633
|
+
entries.set(key, entry);
|
|
634
|
+
}
|
|
635
|
+
const subscriberId = ++subscriberIdCounter;
|
|
636
|
+
entry.subscribers.set(subscriberId, configure);
|
|
637
|
+
if (!entry.channel) {
|
|
638
|
+
subscribeChannel(entry);
|
|
639
|
+
} else {
|
|
640
|
+
configure(entry.channel);
|
|
641
|
+
}
|
|
642
|
+
return () => {
|
|
643
|
+
const current = entries.get(key);
|
|
644
|
+
if (!current) return;
|
|
645
|
+
current.subscribers.delete(subscriberId);
|
|
646
|
+
if (current.subscribers.size === 0) {
|
|
647
|
+
clearTimer(current);
|
|
648
|
+
try {
|
|
649
|
+
if (current.channel) getSupabaseClient().removeChannel(current.channel);
|
|
650
|
+
} finally {
|
|
651
|
+
entries.delete(key);
|
|
652
|
+
}
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
subscribeChannel(current);
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
570
659
|
// src/data/apps/repository.ts
|
|
571
660
|
function mapDbAppRow(row) {
|
|
572
661
|
return {
|
|
@@ -648,35 +737,33 @@ var AppsRepositoryImpl = class extends BaseRepository {
|
|
|
648
737
|
return this.subscribeToAppChannel(`apps:id:${appId}`, `id=eq.${appId}`, handlers);
|
|
649
738
|
}
|
|
650
739
|
subscribeToAppChannel(channelKey, filter, handlers) {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
supabase.removeChannel(channel);
|
|
679
|
-
};
|
|
740
|
+
return subscribeManagedChannel(channelKey, (channel) => {
|
|
741
|
+
channel.on(
|
|
742
|
+
"postgres_changes",
|
|
743
|
+
{ event: "INSERT", schema: "public", table: "app", filter },
|
|
744
|
+
(payload) => {
|
|
745
|
+
var _a;
|
|
746
|
+
console.log("[subscribeToAppChannel] onInsert", payload);
|
|
747
|
+
(_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.new));
|
|
748
|
+
}
|
|
749
|
+
).on(
|
|
750
|
+
"postgres_changes",
|
|
751
|
+
{ event: "UPDATE", schema: "public", table: "app", filter },
|
|
752
|
+
(payload) => {
|
|
753
|
+
var _a;
|
|
754
|
+
console.log("[subscribeToAppChannel] onUpdate", payload);
|
|
755
|
+
(_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.new));
|
|
756
|
+
}
|
|
757
|
+
).on(
|
|
758
|
+
"postgres_changes",
|
|
759
|
+
{ event: "DELETE", schema: "public", table: "app", filter },
|
|
760
|
+
(payload) => {
|
|
761
|
+
var _a;
|
|
762
|
+
console.log("[subscribeToAppChannel] onDelete", payload);
|
|
763
|
+
(_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.old));
|
|
764
|
+
}
|
|
765
|
+
);
|
|
766
|
+
});
|
|
680
767
|
}
|
|
681
768
|
};
|
|
682
769
|
var appsRepository = new AppsRepositoryImpl(appsRemoteDataSource);
|
|
@@ -807,35 +894,33 @@ var MessagesRepositoryImpl = class extends BaseRepository {
|
|
|
807
894
|
return this.unwrapOrThrow(res);
|
|
808
895
|
}
|
|
809
896
|
subscribeThread(threadId, handlers) {
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
supabase.removeChannel(channel);
|
|
838
|
-
};
|
|
897
|
+
return subscribeManagedChannel(`messages:thread:${threadId}`, (channel) => {
|
|
898
|
+
channel.on(
|
|
899
|
+
"postgres_changes",
|
|
900
|
+
{ event: "INSERT", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
|
|
901
|
+
(payload) => {
|
|
902
|
+
var _a;
|
|
903
|
+
const row = payload.new;
|
|
904
|
+
(_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
|
|
905
|
+
}
|
|
906
|
+
).on(
|
|
907
|
+
"postgres_changes",
|
|
908
|
+
{ event: "UPDATE", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
|
|
909
|
+
(payload) => {
|
|
910
|
+
var _a;
|
|
911
|
+
const row = payload.new;
|
|
912
|
+
(_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
|
|
913
|
+
}
|
|
914
|
+
).on(
|
|
915
|
+
"postgres_changes",
|
|
916
|
+
{ event: "DELETE", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
|
|
917
|
+
(payload) => {
|
|
918
|
+
var _a;
|
|
919
|
+
const row = payload.old;
|
|
920
|
+
(_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
|
|
921
|
+
}
|
|
922
|
+
);
|
|
923
|
+
});
|
|
839
924
|
}
|
|
840
925
|
};
|
|
841
926
|
var messagesRepository = new MessagesRepositoryImpl(messagesRemoteDataSource);
|
|
@@ -2399,18 +2484,15 @@ function Bubble({
|
|
|
2399
2484
|
[height, rotation, scale, size, translateX, translateY]
|
|
2400
2485
|
);
|
|
2401
2486
|
const animateOut = (0, import_react.useCallback)(() => {
|
|
2487
|
+
var _a;
|
|
2402
2488
|
if (isAnimatingOut.current) return;
|
|
2403
2489
|
isAnimatingOut.current = true;
|
|
2404
2490
|
try {
|
|
2405
2491
|
void Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
|
2406
2492
|
} catch {
|
|
2407
2493
|
}
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
var _a;
|
|
2411
|
-
(_a = onPressRef.current) == null ? void 0 : _a.call(onPressRef);
|
|
2412
|
-
}
|
|
2413
|
-
});
|
|
2494
|
+
(_a = onPressRef.current) == null ? void 0 : _a.call(onPressRef);
|
|
2495
|
+
animateToHidden();
|
|
2414
2496
|
}, [animateToHidden]);
|
|
2415
2497
|
(0, import_react.useEffect)(() => {
|
|
2416
2498
|
if (isLoading) {
|
|
@@ -7323,42 +7405,40 @@ var EditQueueRepositoryImpl = class extends BaseRepository {
|
|
|
7323
7405
|
return this.unwrapOrThrow(res);
|
|
7324
7406
|
}
|
|
7325
7407
|
subscribeEditQueue(appId, handlers) {
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
supabase.removeChannel(channel);
|
|
7361
|
-
};
|
|
7408
|
+
return subscribeManagedChannel(`edit-queue:app:${appId}`, (channel) => {
|
|
7409
|
+
channel.on(
|
|
7410
|
+
"postgres_changes",
|
|
7411
|
+
{ event: "INSERT", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7412
|
+
(payload) => {
|
|
7413
|
+
var _a;
|
|
7414
|
+
const row = payload.new;
|
|
7415
|
+
if (row.kind !== "edit") return;
|
|
7416
|
+
const item = mapQueueItem(row);
|
|
7417
|
+
if (!ACTIVE_STATUSES.includes(item.status)) return;
|
|
7418
|
+
(_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, item);
|
|
7419
|
+
}
|
|
7420
|
+
).on(
|
|
7421
|
+
"postgres_changes",
|
|
7422
|
+
{ event: "UPDATE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7423
|
+
(payload) => {
|
|
7424
|
+
var _a, _b;
|
|
7425
|
+
const row = payload.new;
|
|
7426
|
+
if (row.kind !== "edit") return;
|
|
7427
|
+
const item = mapQueueItem(row);
|
|
7428
|
+
if (ACTIVE_STATUSES.includes(item.status)) (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, item);
|
|
7429
|
+
else (_b = handlers.onDelete) == null ? void 0 : _b.call(handlers, item);
|
|
7430
|
+
}
|
|
7431
|
+
).on(
|
|
7432
|
+
"postgres_changes",
|
|
7433
|
+
{ event: "DELETE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7434
|
+
(payload) => {
|
|
7435
|
+
var _a;
|
|
7436
|
+
const row = payload.old;
|
|
7437
|
+
if (row.kind !== "edit") return;
|
|
7438
|
+
(_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapQueueItem(row));
|
|
7439
|
+
}
|
|
7440
|
+
);
|
|
7441
|
+
});
|
|
7362
7442
|
}
|
|
7363
7443
|
};
|
|
7364
7444
|
var editQueueRepository = new EditQueueRepositoryImpl(
|