@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.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);
|
|
@@ -7323,42 +7408,40 @@ var EditQueueRepositoryImpl = class extends BaseRepository {
|
|
|
7323
7408
|
return this.unwrapOrThrow(res);
|
|
7324
7409
|
}
|
|
7325
7410
|
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
|
-
};
|
|
7411
|
+
return subscribeManagedChannel(`edit-queue:app:${appId}`, (channel) => {
|
|
7412
|
+
channel.on(
|
|
7413
|
+
"postgres_changes",
|
|
7414
|
+
{ event: "INSERT", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7415
|
+
(payload) => {
|
|
7416
|
+
var _a;
|
|
7417
|
+
const row = payload.new;
|
|
7418
|
+
if (row.kind !== "edit") return;
|
|
7419
|
+
const item = mapQueueItem(row);
|
|
7420
|
+
if (!ACTIVE_STATUSES.includes(item.status)) return;
|
|
7421
|
+
(_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, item);
|
|
7422
|
+
}
|
|
7423
|
+
).on(
|
|
7424
|
+
"postgres_changes",
|
|
7425
|
+
{ event: "UPDATE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7426
|
+
(payload) => {
|
|
7427
|
+
var _a, _b;
|
|
7428
|
+
const row = payload.new;
|
|
7429
|
+
if (row.kind !== "edit") return;
|
|
7430
|
+
const item = mapQueueItem(row);
|
|
7431
|
+
if (ACTIVE_STATUSES.includes(item.status)) (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, item);
|
|
7432
|
+
else (_b = handlers.onDelete) == null ? void 0 : _b.call(handlers, item);
|
|
7433
|
+
}
|
|
7434
|
+
).on(
|
|
7435
|
+
"postgres_changes",
|
|
7436
|
+
{ event: "DELETE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7437
|
+
(payload) => {
|
|
7438
|
+
var _a;
|
|
7439
|
+
const row = payload.old;
|
|
7440
|
+
if (row.kind !== "edit") return;
|
|
7441
|
+
(_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapQueueItem(row));
|
|
7442
|
+
}
|
|
7443
|
+
);
|
|
7444
|
+
});
|
|
7362
7445
|
}
|
|
7363
7446
|
};
|
|
7364
7447
|
var editQueueRepository = new EditQueueRepositoryImpl(
|