@comergehq/studio 0.1.21 → 0.1.22
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 +177 -94
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +177 -94
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- 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);
|
|
@@ -7357,42 +7442,40 @@ var EditQueueRepositoryImpl = class extends BaseRepository {
|
|
|
7357
7442
|
return this.unwrapOrThrow(res);
|
|
7358
7443
|
}
|
|
7359
7444
|
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
|
-
};
|
|
7445
|
+
return subscribeManagedChannel(`edit-queue:app:${appId}`, (channel) => {
|
|
7446
|
+
channel.on(
|
|
7447
|
+
"postgres_changes",
|
|
7448
|
+
{ event: "INSERT", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7449
|
+
(payload) => {
|
|
7450
|
+
var _a;
|
|
7451
|
+
const row = payload.new;
|
|
7452
|
+
if (row.kind !== "edit") return;
|
|
7453
|
+
const item = mapQueueItem(row);
|
|
7454
|
+
if (!ACTIVE_STATUSES.includes(item.status)) return;
|
|
7455
|
+
(_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, item);
|
|
7456
|
+
}
|
|
7457
|
+
).on(
|
|
7458
|
+
"postgres_changes",
|
|
7459
|
+
{ event: "UPDATE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7460
|
+
(payload) => {
|
|
7461
|
+
var _a, _b;
|
|
7462
|
+
const row = payload.new;
|
|
7463
|
+
if (row.kind !== "edit") return;
|
|
7464
|
+
const item = mapQueueItem(row);
|
|
7465
|
+
if (ACTIVE_STATUSES.includes(item.status)) (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, item);
|
|
7466
|
+
else (_b = handlers.onDelete) == null ? void 0 : _b.call(handlers, item);
|
|
7467
|
+
}
|
|
7468
|
+
).on(
|
|
7469
|
+
"postgres_changes",
|
|
7470
|
+
{ event: "DELETE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7471
|
+
(payload) => {
|
|
7472
|
+
var _a;
|
|
7473
|
+
const row = payload.old;
|
|
7474
|
+
if (row.kind !== "edit") return;
|
|
7475
|
+
(_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapQueueItem(row));
|
|
7476
|
+
}
|
|
7477
|
+
);
|
|
7478
|
+
});
|
|
7396
7479
|
}
|
|
7397
7480
|
};
|
|
7398
7481
|
var editQueueRepository = new EditQueueRepositoryImpl(
|