@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 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
- const supabase = getSupabaseClient();
652
- const channel = supabase.channel(channelKey).on(
653
- "postgres_changes",
654
- { event: "INSERT", schema: "public", table: "app", filter },
655
- (payload) => {
656
- var _a;
657
- console.log("[subscribeToAppChannel] onInsert", payload);
658
- (_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.new));
659
- }
660
- ).on(
661
- "postgres_changes",
662
- { event: "UPDATE", schema: "public", table: "app", filter },
663
- (payload) => {
664
- var _a;
665
- console.log("[subscribeToAppChannel] onUpdate", payload);
666
- (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.new));
667
- }
668
- ).on(
669
- "postgres_changes",
670
- { event: "DELETE", schema: "public", table: "app", filter },
671
- (payload) => {
672
- var _a;
673
- console.log("[subscribeToAppChannel] onDelete", payload);
674
- (_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.old));
675
- }
676
- ).subscribe();
677
- return () => {
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
- const supabase = getSupabaseClient();
811
- const channel = supabase.channel(`messages:thread:${threadId}`).on(
812
- "postgres_changes",
813
- { event: "INSERT", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
814
- (payload) => {
815
- var _a;
816
- const row = payload.new;
817
- (_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
818
- }
819
- ).on(
820
- "postgres_changes",
821
- { event: "UPDATE", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
822
- (payload) => {
823
- var _a;
824
- const row = payload.new;
825
- (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
826
- }
827
- ).on(
828
- "postgres_changes",
829
- { event: "DELETE", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
830
- (payload) => {
831
- var _a;
832
- const row = payload.old;
833
- (_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
834
- }
835
- ).subscribe();
836
- return () => {
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
- const supabase = getSupabaseClient();
7327
- const channel = supabase.channel(`edit-queue:app:${appId}`).on(
7328
- "postgres_changes",
7329
- { event: "INSERT", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
7330
- (payload) => {
7331
- var _a;
7332
- const row = payload.new;
7333
- if (row.kind !== "edit") return;
7334
- const item = mapQueueItem(row);
7335
- if (!ACTIVE_STATUSES.includes(item.status)) return;
7336
- (_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, item);
7337
- }
7338
- ).on(
7339
- "postgres_changes",
7340
- { event: "UPDATE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
7341
- (payload) => {
7342
- var _a, _b;
7343
- const row = payload.new;
7344
- if (row.kind !== "edit") return;
7345
- const item = mapQueueItem(row);
7346
- if (ACTIVE_STATUSES.includes(item.status)) (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, item);
7347
- else (_b = handlers.onDelete) == null ? void 0 : _b.call(handlers, item);
7348
- }
7349
- ).on(
7350
- "postgres_changes",
7351
- { event: "DELETE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
7352
- (payload) => {
7353
- var _a;
7354
- const row = payload.old;
7355
- if (row.kind !== "edit") return;
7356
- (_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapQueueItem(row));
7357
- }
7358
- ).subscribe();
7359
- return () => {
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(