@chromahq/core 1.0.51 → 1.0.53

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.
@@ -261,6 +261,139 @@ function getNonceService() {
261
261
  return nonceServiceInstance;
262
262
  }
263
263
 
264
+ const _PopupVisibilityService = class _PopupVisibilityService {
265
+ /**
266
+ * Private constructor - use PopupVisibilityService.instance instead.
267
+ */
268
+ constructor() {
269
+ /** Number of currently connected ports (popup views) */
270
+ this.connectedPortCount = 0;
271
+ /** Listeners for visibility changes */
272
+ this.listeners = /* @__PURE__ */ new Set();
273
+ /** Timestamp of last visibility change */
274
+ this.lastVisibilityChangeAt = 0;
275
+ }
276
+ /**
277
+ * Get the singleton instance of the service.
278
+ */
279
+ static get instance() {
280
+ if (!_PopupVisibilityService._instance) {
281
+ _PopupVisibilityService._instance = new _PopupVisibilityService();
282
+ }
283
+ return _PopupVisibilityService._instance;
284
+ }
285
+ /**
286
+ * Reset the singleton instance (primarily for testing).
287
+ * @internal
288
+ */
289
+ static resetInstance() {
290
+ if (_PopupVisibilityService._instance) {
291
+ _PopupVisibilityService._instance.listeners.clear();
292
+ }
293
+ _PopupVisibilityService._instance = null;
294
+ }
295
+ // ─────────────────────────────────────────────────────────────────────────────
296
+ // Public API
297
+ // ─────────────────────────────────────────────────────────────────────────────
298
+ /**
299
+ * Check if the popup (or any extension view) is currently visible.
300
+ *
301
+ * @returns true if at least one port is connected (popup is open)
302
+ */
303
+ isPopupVisible() {
304
+ return this.connectedPortCount > 0;
305
+ }
306
+ /**
307
+ * Get the number of connected ports.
308
+ *
309
+ * @returns The current count of connected extension views
310
+ */
311
+ getConnectedPortCount() {
312
+ return this.connectedPortCount;
313
+ }
314
+ /**
315
+ * Get the timestamp of the last visibility change.
316
+ *
317
+ * @returns Unix timestamp in milliseconds, or 0 if never changed
318
+ */
319
+ getLastVisibilityChangeAt() {
320
+ return this.lastVisibilityChangeAt;
321
+ }
322
+ /**
323
+ * Register a callback to be notified when visibility changes.
324
+ *
325
+ * @param callback - Function to call when visibility changes
326
+ * @returns Unsubscribe function to remove the listener
327
+ */
328
+ onVisibilityChange(callback) {
329
+ this.listeners.add(callback);
330
+ return () => {
331
+ this.listeners.delete(callback);
332
+ };
333
+ }
334
+ // ─────────────────────────────────────────────────────────────────────────────
335
+ // Internal API (called by BridgeRuntime)
336
+ // ─────────────────────────────────────────────────────────────────────────────
337
+ /**
338
+ * Called when a port connects (popup opens).
339
+ * @internal
340
+ */
341
+ onPortConnected() {
342
+ const wasVisible = this.isPopupVisible();
343
+ this.connectedPortCount++;
344
+ if (!wasVisible && this.isPopupVisible()) {
345
+ this.lastVisibilityChangeAt = Date.now();
346
+ this.notifyListeners(true);
347
+ }
348
+ }
349
+ /**
350
+ * Called when a port disconnects (popup closes).
351
+ * @internal
352
+ */
353
+ onPortDisconnected() {
354
+ const wasVisible = this.isPopupVisible();
355
+ this.connectedPortCount = Math.max(0, this.connectedPortCount - 1);
356
+ if (wasVisible && !this.isPopupVisible()) {
357
+ this.lastVisibilityChangeAt = Date.now();
358
+ this.notifyListeners(false);
359
+ }
360
+ }
361
+ /**
362
+ * Sync the port count with the actual connected ports set.
363
+ * Called by BridgeRuntime to ensure consistency.
364
+ * @internal
365
+ */
366
+ syncPortCount(count) {
367
+ const wasVisible = this.isPopupVisible();
368
+ this.connectedPortCount = count;
369
+ const isNowVisible = this.isPopupVisible();
370
+ if (wasVisible !== isNowVisible) {
371
+ this.lastVisibilityChangeAt = Date.now();
372
+ this.notifyListeners(isNowVisible);
373
+ }
374
+ }
375
+ // ─────────────────────────────────────────────────────────────────────────────
376
+ // Private Methods
377
+ // ─────────────────────────────────────────────────────────────────────────────
378
+ /**
379
+ * Notify all registered listeners of a visibility change.
380
+ */
381
+ notifyListeners(isVisible) {
382
+ this.listeners.forEach((callback) => {
383
+ try {
384
+ callback(isVisible);
385
+ } catch (error) {
386
+ console.error("[PopupVisibilityService] Listener error:", error);
387
+ }
388
+ });
389
+ }
390
+ };
391
+ _PopupVisibilityService._instance = null;
392
+ let PopupVisibilityService = _PopupVisibilityService;
393
+ function getPopupVisibilityService() {
394
+ return PopupVisibilityService.instance;
395
+ }
396
+
264
397
  const DEFAULT_PORT_NAME$1 = "chroma-bridge";
265
398
  const earlyPorts = [];
266
399
  let listenerSetup = false;
@@ -515,6 +648,7 @@ const _BridgeRuntimeManager = class _BridgeRuntimeManager {
515
648
  */
516
649
  setupMessageHandler(port) {
517
650
  this.connectedPorts.add(port);
651
+ PopupVisibilityService.instance.onPortConnected();
518
652
  if (this.keepAlive && this.connectedPorts.size === 1) {
519
653
  this.startKeepAlive();
520
654
  }
@@ -550,6 +684,7 @@ const _BridgeRuntimeManager = class _BridgeRuntimeManager {
550
684
  });
551
685
  port.onDisconnect.addListener(() => {
552
686
  this.connectedPorts.delete(port);
687
+ PopupVisibilityService.instance.onPortDisconnected();
553
688
  const runtimeErrorMessage = chrome.runtime.lastError?.message;
554
689
  if (runtimeErrorMessage) {
555
690
  this.logger.warn(`\u{1F4F4} Port disconnected with error: ${runtimeErrorMessage}`);
@@ -838,7 +973,8 @@ const _BridgeRuntimeManager = class _BridgeRuntimeManager {
838
973
  }
839
974
  }
840
975
  };
841
- _BridgeRuntimeManager.KEEP_ALIVE_INTERVAL_MS = 25e3;
976
+ _BridgeRuntimeManager.KEEP_ALIVE_INTERVAL_MS = 5e4;
977
+ // 50 seconds (increased from 25s to reduce wake-ups)
842
978
  _BridgeRuntimeManager.KEEP_ALIVE_ALARM_NAME = "chroma-bridge-keep-alive";
843
979
  _BridgeRuntimeManager.KEEP_ALIVE_ALARM_PERIOD_MINUTES = 1;
844
980
  let BridgeRuntimeManager = _BridgeRuntimeManager;
@@ -1883,6 +2019,7 @@ class Scheduler {
1883
2019
  async execute(id) {
1884
2020
  const job = this.registry.resolve(id);
1885
2021
  const context = this.registry.getContext(id);
2022
+ const options = this.registry.meta(id);
1886
2023
  if (!job || !context) {
1887
2024
  this.logger.debug(`Job ${id} not found or no context`);
1888
2025
  return;
@@ -1891,6 +2028,16 @@ class Scheduler {
1891
2028
  this.logger.debug(`Job ${id} is paused or stopped, skipping execution`);
1892
2029
  return;
1893
2030
  }
2031
+ if (options?.requiresPopup) {
2032
+ const isPopupVisible = PopupVisibilityService.instance.isPopupVisible();
2033
+ if (!isPopupVisible) {
2034
+ this.logger.debug(`Job ${id} requires popup but popup is not visible, skipping`);
2035
+ if (options?.cron || options?.recurring) {
2036
+ this.schedule(id, options);
2037
+ }
2038
+ return;
2039
+ }
2040
+ }
1894
2041
  try {
1895
2042
  this.registry.updateState(id, JobState.RUNNING);
1896
2043
  this.logger.info(`Executing job ${id}`);
@@ -1899,7 +2046,6 @@ class Scheduler {
1899
2046
  await jobInstance.handle.bind(jobInstance).call(jobInstance, context);
1900
2047
  if (!context.isStopped() && !context.isPaused()) {
1901
2048
  this.registry.updateState(id, JobState.COMPLETED);
1902
- const options = this.registry.meta(id);
1903
2049
  if (options?.cron || options?.recurring) {
1904
2050
  this.registry.updateState(id, JobState.SCHEDULED);
1905
2051
  this.schedule(id, options);
@@ -1908,7 +2054,6 @@ class Scheduler {
1908
2054
  } catch (error) {
1909
2055
  this.logger.error(`Job ${id} execution failed:`, error);
1910
2056
  context.fail(error);
1911
- const options = this.registry.meta(id);
1912
2057
  if (options?.cron || options?.recurring) {
1913
2058
  this.logger.info(`Rescheduling failed recurring job ${id}`);
1914
2059
  this.registry.updateState(id, JobState.SCHEDULED);
@@ -2483,5 +2628,5 @@ class BootstrapBuilder {
2483
2628
  }
2484
2629
  }
2485
2630
 
2486
- export { JobRegistry as J, NonceService as N, Scheduler as S, arePortsClaimed as a, container as b, claimEarlyPorts as c, create as d, bootstrap as e, JobState as f, getNonceService as g, isEarlyListenerSetup as i, setupEarlyListener as s };
2487
- //# sourceMappingURL=boot-CskDM5Yw.js.map
2631
+ export { JobRegistry as J, NonceService as N, PopupVisibilityService as P, Scheduler as S, getPopupVisibilityService as a, arePortsClaimed as b, claimEarlyPorts as c, container as d, create as e, bootstrap as f, getNonceService as g, JobState as h, isEarlyListenerSetup as i, setupEarlyListener as s };
2632
+ //# sourceMappingURL=boot-DrOND1Zi.js.map