PageSourceSearch

https://wyber.ai/chunk-FYFSQ3DN.js

js wyber.ai collected 2026-10-05 13:22:14 UTC 9,320 bytes, 295 lines download raw bytes

1import {
2  SessionRealtimeService
3} from "./chunk-JVOIRLQM.js";
4import {
5  BehaviorSubject,
6  Injectable,
7  NgZone,
8  Subject,
9  Subscription,
10  inject,
11  setClassMetadata,
12  ɵɵdefineInjectable
13} from "./chunk-6GJQIPT2.js";
14import {
15  __async
16} from "./chunk-RK4NW2NR.js";
17
18// src/app/services/network-status.service.ts
19var NetworkStatusService = class _NetworkStatusService {
20  ngZone = inject(NgZone);
21  realtimeService = inject(SessionRealtimeService);
22  statusSubject = new BehaviorSubject(typeof navigator !== "undefined" && !navigator.onLine ? "offline" : "online");
23  /** Public observable for components to bind to */
24  status$ = this.statusSubject.asObservable();
25  reconnectedSubject = new Subject();
26  /** Emitted whenever connection transitions from offline/reconnecting back to online. Used by components to re-sync stale data. */
27  reconnected$ = this.reconnectedSubject.asObservable();
28  restoredTimeout = null;
29  isProbing = false;
30  wasEverOffline = typeof navigator !== "undefined" && !navigator.onLine;
31  subscriptions = new Subscription();
32  onlineHandler = () => this.handleOnlineEvent();
33  offlineHandler = () => this.handleOfflineEvent();
34  constructor() {
35    this.registerBrowserListeners();
36    this.subscribeToRealtimeHub();
37  }
38  /** Current state snapshot */
39  get currentStatus() {
40    return this.statusSubject.value;
41  }
42  /** True if the user is considered offline or currently reconnecting */
43  get isOffline() {
44    if (typeof navigator !== "undefined" && !navigator.onLine) {
45      return true;
46    }
47    const s = this.statusSubject.value;
48    return s === "offline" || s === "reconnecting";
49  }
50  registerBrowserListeners() {
51    if (typeof window === "undefined")
52      return;
53    window.addEventListener("online", this.onlineHandler);
54    window.addEventListener("offline", this.offlineHandler);
55  }
56  subscribeToRealtimeHub() {
57    this.subscriptions.add(this.realtimeService.status$.subscribe((hubStatus) => {
58      if (typeof navigator !== "undefined" && !navigator.onLine) {
59        return;
60      }
61      if (hubStatus === "reconnecting") {
62        if (this.statusSubject.value !== "reconnecting") {
63          this.statusSubject.next("reconnecting");
64        }
65      } else if (hubStatus === "connected") {
66        if (this.statusSubject.value === "reconnecting" || this.statusSubject.value === "offline") {
67          this.markOnline();
68        }
69      }
70    }));
71  }
72  handleOfflineEvent() {
73    this.ngZone.run(() => {
74      this.clearRestoredTimeout();
75      this.wasEverOffline = true;
76      this.statusSubject.next("offline");
77    });
78  }
79  handleOnlineEvent() {
80    this.ngZone.run(() => {
81      this.statusSubject.next("reconnecting");
82      this.probeReachability();
83    });
84  }
85  /**
86   * Reports an HTTP error from the interceptor.
87   * If forceImmediate is true (e.g. status 0, proxy disconnect, ECONNREFUSED), transitions to offline immediately.
88   * Otherwise launches a background probe.
89   */
90  reportHttpNetworkError(forceImmediate = false) {
91    if (forceImmediate || typeof navigator !== "undefined" && !navigator.onLine) {
92      this.handleOfflineEvent();
93      return;
94    }
95    if (!this.isProbing) {
96      this.probeReachability();
97    }
98  }
99  /**
100   * Active probe to verify actual internet / server reachability.
101   * Uses a cache-busted request with a short timeout.
102   */
103  probeReachability() {
104    return __async(this, null, function* () {
105      if (this.isProbing || typeof window === "undefined") {
106        return this.statusSubject.value === "online";
107      }
108      if (typeof navigator !== "undefined" && !navigator.onLine) {
109        this.handleOfflineEvent();
110        return false;
111      }
112      this.isProbing = true;
113      const controller = new AbortController();
114      const timeoutId = setTimeout(() => controller.abort(), 3500);
115      try {
116        const isLocalhost = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";
117        let reachable = false;
118        if (isLocalhost) {
119          try {
120            yield fetch(`https://dev.wyber.ai/favicon.ico?_nc=${Date.now()}`, {
121              mode: "no-cors",
122              cache: "no-store",
123              signal: controller.signal
124            });
125            reachable = true;
126          } catch {
127            const res = yield fetch(`https://api.ipify.org?format=json&_nc=${Date.now()}`, {
128              method: "GET",
129              cache: "no-store",
130              signal: controller.signal
131            });
132            reachable = res.ok || res.status > 0;
133          }
134        } else {
135          const response = yield fetch(`${window.location.origin}/favicon.ico?_nc=${Date.now()}`, {
136            method: "HEAD",
137            cache: "no-store",
138            signal: controller.signal
139          });
140          reachable = response.ok || response.status > 0 || response.type === "opaque";
141        }
142        clearTimeout(timeoutId);
143        this.ngZone.run(() => {
144          if (reachable) {
145            this.markOnline();
146          } else {
147            this.wasEverOffline = true;
148            this.statusSubject.next("offline");
149          }
150        });
151        return reachable;
152      } catch {
153        clearTimeout(timeoutId);
154        this.ngZone.run(() => {
155          this.wasEverOffline = true;
156          this.statusSubject.next("offline");
157        });
158        return false;
159      } finally {
160        this.isProbing = false;
161      }
162    });
163  }
164  /**
165   * Marks status as online. If previously offline, shows 'restored' for 3.5 seconds before 'online'.
166   */
167  markOnline() {
168    this.clearRestoredTimeout();
169    if (this.wasEverOffline) {
170      this.reconnectedSubject.next();
171      this.statusSubject.next("restored");
172      this.restoredTimeout = setTimeout(() => {
173        this.ngZone.run(() => {
174          this.statusSubject.next("online");
175          this.wasEverOffline = false;
176        });
177      }, 3500);
178    } else {
179      this.statusSubject.next("online");
180    }
181  }
182  /**
183   * Triggered by user clicking "Reconnect now" or manual retry.
184   * Returns true if connection was successfully verified.
185   */
186  retryConnection() {
187    return __async(this, null, function* () {
188      this.statusSubject.next("reconnecting");
189      const isUp = yield this.probeReachability();
190      if (!isUp) {
191        this.statusSubject.next("offline");
192        return false;
193      }
194      return true;
195    });
196  }
197  clearRestoredTimeout() {
198    if (this.restoredTimeout) {
199      clearTimeout(this.restoredTimeout);
200      this.restoredTimeout = null;
201    }
202  }
203  ngOnDestroy() {
204    this.clearRestoredTimeout();
205    this.subscriptions.unsubscribe();
206    if (typeof window !== "undefined") {
207      window.removeEventListener("online", this.onlineHandler);
208      window.removeEventListener("offline", this.offlineHandler);
209    }
210  }
211  static \u0275fac = function NetworkStatusService_Factory(__ngFactoryType__) {
212    return new (__ngFactoryType__ || _NetworkStatusService)();
213  };
214  static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _NetworkStatusService, factory: _NetworkStatusService.\u0275fac, providedIn: "root" });
215};
216(() => {
217  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NetworkStatusService, [{
218    type: Injectable,
219    args: [{
220      providedIn: "root"
221    }]
222  }], () => [], null);
223})();
224
225// src/app/services/toast.service.ts
226var ToastService = class _ToastService {
227  networkService = inject(NetworkStatusService);
228  ngZone = inject(NgZone);
229  toastsSubject = new BehaviorSubject([]);
230  toasts$ = this.toastsSubject.asObservable();
231  show(title, description, variant = "success", duration = 3e3) {
232    const toast = {
233      id: `${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
234      title,
235      description,
236      variant,
237      duration
238    };
239    const currentToasts = this.toastsSubject.value;
240    this.toastsSubject.next([...currentToasts, toast]);
241    if (duration > 0) {
242      setTimeout(() => {
243        this.ngZone.run(() => {
244          this.remove(toast.id);
245        });
246      }, duration);
247    }
248  }
249  success(title, description, duration = 3e3) {
250    this.show(title, description, "success", duration);
251  }
252  error(title, description) {
253    if (this.networkService.isOffline) {
254      return;
255    }
256    this.show(title, description, "error");
257  }
258  info(title, description) {
259    if (this.networkService.isOffline) {
260      return;
261    }
262    this.show(title, description, "info");
263  }
264  warning(title, description) {
265    if (this.networkService.isOffline) {
266      return;
267    }
268    this.show(title, description, "info");
269  }
270  remove(id) {
271    const currentToasts = this.toastsSubject.value;
272    this.toastsSubject.next(currentToasts.filter((t) => t.id !== id));
273  }
274  clear() {
275    this.toastsSubject.next([]);
276  }
277  static \u0275fac = function ToastService_Factory(__ngFactoryType__) {
278    return new (__ngFactoryType__ || _ToastService)();
279  };
280  static \u0275prov = /* @__PURE__ */ \u0275\u0275defineInjectable({ token: _ToastService, factory: _ToastService.\u0275fac, providedIn: "root" });
281};
282(() => {
283  (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ToastService, [{
284    type: Injectable,
285    args: [{
286      providedIn: "root"
287    }]
288  }], null, null);
289})();
290
291export {
292  NetworkStatusService,
293  ToastService
294};
295//# sourceMappingURL=chunk-FYFSQ3DN.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.