@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.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
- const supabase = getSupabaseClient();
622
- const channel = supabase.channel(channelKey).on(
623
- "postgres_changes",
624
- { event: "INSERT", schema: "public", table: "app", filter },
625
- (payload) => {
626
- var _a;
627
- console.log("[subscribeToAppChannel] onInsert", payload);
628
- (_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.new));
629
- }
630
- ).on(
631
- "postgres_changes",
632
- { event: "UPDATE", schema: "public", table: "app", filter },
633
- (payload) => {
634
- var _a;
635
- console.log("[subscribeToAppChannel] onUpdate", payload);
636
- (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.new));
637
- }
638
- ).on(
639
- "postgres_changes",
640
- { event: "DELETE", schema: "public", table: "app", filter },
641
- (payload) => {
642
- var _a;
643
- console.log("[subscribeToAppChannel] onDelete", payload);
644
- (_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapDbAppRow(payload.old));
645
- }
646
- ).subscribe();
647
- return () => {
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
- const supabase = getSupabaseClient();
781
- const channel = supabase.channel(`messages:thread:${threadId}`).on(
782
- "postgres_changes",
783
- { event: "INSERT", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
784
- (payload) => {
785
- var _a;
786
- const row = payload.new;
787
- (_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
788
- }
789
- ).on(
790
- "postgres_changes",
791
- { event: "UPDATE", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
792
- (payload) => {
793
- var _a;
794
- const row = payload.new;
795
- (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
796
- }
797
- ).on(
798
- "postgres_changes",
799
- { event: "DELETE", schema: "public", table: "message", filter: `thread_id=eq.${threadId}` },
800
- (payload) => {
801
- var _a;
802
- const row = payload.old;
803
- (_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapDbRowToMessage(row));
804
- }
805
- ).subscribe();
806
- return () => {
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
- animateToHidden({
2396
- onFinish: () => {
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
- const supabase = getSupabaseClient();
7361
- const channel = supabase.channel(`edit-queue:app:${appId}`).on(
7362
- "postgres_changes",
7363
- { event: "INSERT", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
7364
- (payload) => {
7365
- var _a;
7366
- const row = payload.new;
7367
- if (row.kind !== "edit") return;
7368
- const item = mapQueueItem(row);
7369
- if (!ACTIVE_STATUSES.includes(item.status)) return;
7370
- (_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, item);
7371
- }
7372
- ).on(
7373
- "postgres_changes",
7374
- { event: "UPDATE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
7375
- (payload) => {
7376
- var _a, _b;
7377
- const row = payload.new;
7378
- if (row.kind !== "edit") return;
7379
- const item = mapQueueItem(row);
7380
- if (ACTIVE_STATUSES.includes(item.status)) (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, item);
7381
- else (_b = handlers.onDelete) == null ? void 0 : _b.call(handlers, item);
7382
- }
7383
- ).on(
7384
- "postgres_changes",
7385
- { event: "DELETE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
7386
- (payload) => {
7387
- var _a;
7388
- const row = payload.old;
7389
- if (row.kind !== "edit") return;
7390
- (_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapQueueItem(row));
7391
- }
7392
- ).subscribe();
7393
- return () => {
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(