@ohhwells/bridge 0.1.152 → 0.1.154

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
@@ -7161,6 +7161,21 @@ function applyCustomCodeToHead(content) {
7161
7161
  if (beforeNodes.length > 0) document.head.append(...beforeNodes);
7162
7162
  }
7163
7163
 
7164
+ // src/lib/selectNavContainer.ts
7165
+ function isOnScreen(el) {
7166
+ const rect = el.getBoundingClientRect();
7167
+ if (rect.width <= 0 || rect.height <= 0) return false;
7168
+ if (el.offsetParent !== null) return true;
7169
+ return getComputedStyle(el).position === "fixed";
7170
+ }
7171
+ function findSelectableNavContainer(doc = document) {
7172
+ const visible = Array.from(doc.querySelectorAll("[data-ohw-nav-container]")).filter(isOnScreen);
7173
+ if (visible.length === 0) return null;
7174
+ return visible.reduce(
7175
+ (best, el) => el.getBoundingClientRect().top < best.getBoundingClientRect().top ? el : best
7176
+ );
7177
+ }
7178
+
7164
7179
  // src/ui/ai-tree/aiSectionsManager.tsx
7165
7180
  import { flushSync } from "react-dom";
7166
7181
  import { createRoot } from "react-dom/client";
@@ -16910,6 +16925,94 @@ function bindPublishedForms(apiUrl, subdomain, content = {}) {
16910
16925
  );
16911
16926
  }
16912
16927
 
16928
+ // src/lib/outage-beacon.ts
16929
+ var DEDUP_MS = 5 * 60 * 1e3;
16930
+ var lastSent = /* @__PURE__ */ new Map();
16931
+ function apiBase() {
16932
+ return process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
16933
+ }
16934
+ function bridgeVersion() {
16935
+ try {
16936
+ if (true) {
16937
+ return "0.1.154";
16938
+ }
16939
+ } catch {
16940
+ }
16941
+ if (typeof document !== "undefined") {
16942
+ return document.documentElement.getAttribute("data-ohw-bridge-version") || void 0;
16943
+ }
16944
+ return void 0;
16945
+ }
16946
+ function stampBridgeVersionOnDocument() {
16947
+ if (typeof document === "undefined") {
16948
+ return;
16949
+ }
16950
+ try {
16951
+ const v = true ? "0.1.154" : "";
16952
+ if (v) {
16953
+ document.documentElement.setAttribute("data-ohw-bridge-version", v);
16954
+ window.__OHW_BRIDGE_VERSION__ = v;
16955
+ }
16956
+ } catch {
16957
+ }
16958
+ }
16959
+ var RECOVERABLE_PAGE_ERROR = /Minified React error #(?:418|419|421|422|423|425)\b|Hydration failed because the initial UI does not match|Text content does not match server-rendered HTML|There was an error while hydrating|did not match\. Server:/i;
16960
+ function reportOutageBeacon(args) {
16961
+ if (typeof window === "undefined" || !args.subdomain) {
16962
+ return;
16963
+ }
16964
+ if (args.reason === "page_error" && args.detail && RECOVERABLE_PAGE_ERROR.test(args.detail)) {
16965
+ return;
16966
+ }
16967
+ const key = `${args.subdomain}|${args.reason}`;
16968
+ const now = Date.now();
16969
+ const prev = lastSent.get(key) || 0;
16970
+ if (now - prev < DEDUP_MS) {
16971
+ return;
16972
+ }
16973
+ lastSent.set(key, now);
16974
+ const body = JSON.stringify({
16975
+ subdomain: args.subdomain,
16976
+ reason: args.reason,
16977
+ detail: args.detail?.slice(0, 300),
16978
+ templateId: args.templateId,
16979
+ bridgeVersion: bridgeVersion(),
16980
+ href: typeof location !== "undefined" ? location.href : void 0
16981
+ });
16982
+ const url = `${apiBase()}/api/public/sites/outage-beacon`;
16983
+ try {
16984
+ if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
16985
+ const blob = new Blob([body], { type: "application/json" });
16986
+ if (navigator.sendBeacon(url, blob)) {
16987
+ return;
16988
+ }
16989
+ }
16990
+ void fetch(url, {
16991
+ method: "POST",
16992
+ headers: { "Content-Type": "application/json" },
16993
+ body,
16994
+ keepalive: true,
16995
+ cache: "no-store"
16996
+ }).catch(() => {
16997
+ });
16998
+ } catch {
16999
+ }
17000
+ }
17001
+ function armLoaderHangBeacon(subdomain, fetchState, hangMs = 15e3) {
17002
+ if (!subdomain || fetchState !== "loading") {
17003
+ return () => {
17004
+ };
17005
+ }
17006
+ const timer = window.setTimeout(() => {
17007
+ reportOutageBeacon({
17008
+ subdomain,
17009
+ reason: "loader_stuck",
17010
+ detail: `fetchState still loading after ${hangMs}ms`
17011
+ });
17012
+ }, hangMs);
17013
+ return () => window.clearTimeout(timer);
17014
+ }
17015
+
16913
17016
  // src/lib/form-fields.ts
16914
17017
  var FIELD_DEFAULTS = {
16915
17018
  "short-text": { label: "Short text", placeholder: "Type placeholder text..." },
@@ -25301,6 +25404,34 @@ function OhhwellsBridge() {
25301
25404
  const [fetchState, setFetchState] = useState18("idle");
25302
25405
  const [showBranding, setShowBranding] = useState18(false);
25303
25406
  const autoSaveTimers = useRef14(/* @__PURE__ */ new Map());
25407
+ useEffect17(() => {
25408
+ stampBridgeVersionOnDocument();
25409
+ }, []);
25410
+ useEffect17(() => {
25411
+ if (isEditMode) return;
25412
+ return armLoaderHangBeacon(subdomain, fetchState);
25413
+ }, [isEditMode, subdomain, fetchState]);
25414
+ useEffect17(() => {
25415
+ if (isEditMode || !subdomain) return;
25416
+ const onError = (event) => {
25417
+ reportOutageBeacon({
25418
+ subdomain,
25419
+ reason: "page_error",
25420
+ detail: event.message || "window error"
25421
+ });
25422
+ };
25423
+ const onRejection = (event) => {
25424
+ const reason = event.reason;
25425
+ const detail = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "unhandledrejection";
25426
+ reportOutageBeacon({ subdomain, reason: "page_error", detail });
25427
+ };
25428
+ window.addEventListener("error", onError);
25429
+ window.addEventListener("unhandledrejection", onRejection);
25430
+ return () => {
25431
+ window.removeEventListener("error", onError);
25432
+ window.removeEventListener("unhandledrejection", onRejection);
25433
+ };
25434
+ }, [isEditMode, subdomain]);
25304
25435
  const activeElRef = useRef14(null);
25305
25436
  const pointerHeldRef = useRef14(false);
25306
25437
  const selectedElRef = useRef14(null);
@@ -27557,7 +27688,17 @@ function OhhwellsBridge() {
27557
27688
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
27558
27689
  const initialPath = pathname;
27559
27690
  fetchedContentPaths.add(`${subdomain}::${initialPath}`);
27560
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
27691
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => {
27692
+ if (!r2.ok) {
27693
+ reportOutageBeacon({
27694
+ subdomain,
27695
+ reason: "content_fetch_failed",
27696
+ detail: `HTTP ${r2.status}`
27697
+ });
27698
+ return null;
27699
+ }
27700
+ return r2.json();
27701
+ }).then((data) => {
27561
27702
  if (cancelled) return;
27562
27703
  const content = data?.content ?? {};
27563
27704
  const branding = Boolean(data?.showBranding);
@@ -27566,7 +27707,12 @@ function OhhwellsBridge() {
27566
27707
  recordOverriddenPaths(subdomain, data?.overriddenPaths);
27567
27708
  setShowBranding(branding);
27568
27709
  return applyContent(content);
27569
- }).catch(() => {
27710
+ }).catch((err) => {
27711
+ reportOutageBeacon({
27712
+ subdomain,
27713
+ reason: "content_fetch_failed",
27714
+ detail: err instanceof Error ? err.message : "fetch failed"
27715
+ });
27570
27716
  }).finally(() => {
27571
27717
  if (!cancelled) setFetchState("done");
27572
27718
  });
@@ -30614,6 +30760,12 @@ function OhhwellsBridge() {
30614
30760
  clearIconSelectionRef.current();
30615
30761
  };
30616
30762
  window.addEventListener("message", handleDeactivate);
30763
+ const handleSelectNav = (e) => {
30764
+ if (e.data?.type !== "ow:select-nav") return;
30765
+ const navContainer = findSelectableNavContainer();
30766
+ if (navContainer) selectFrameRef.current(navContainer);
30767
+ };
30768
+ window.addEventListener("message", handleSelectNav);
30617
30769
  const handleToastAction = (e) => {
30618
30770
  if (e.data?.type !== "ow:toast-action") return;
30619
30771
  const actionId = typeof e.data.actionId === "string" ? e.data.actionId : null;
@@ -31430,6 +31582,7 @@ function OhhwellsBridge() {
31430
31582
  window.removeEventListener("message", handleDeleteSection);
31431
31583
  window.removeEventListener("message", handleDuplicateSection);
31432
31584
  window.removeEventListener("message", handleDeactivate);
31585
+ window.removeEventListener("message", handleSelectNav);
31433
31586
  document.documentElement.removeAttribute("data-ohw-panel-dragging");
31434
31587
  window.removeEventListener("message", handleToastAction);
31435
31588
  window.removeEventListener("message", handleFormCount);
@@ -31727,7 +31880,7 @@ function OhhwellsBridge() {
31727
31880
  postToParent2({
31728
31881
  type: "ow:ready",
31729
31882
  version: "1",
31730
- bridgeVersion: "0.1.152",
31883
+ bridgeVersion: "0.1.154",
31731
31884
  path: pathname,
31732
31885
  nodes: collectEditableNodes(editContentRef.current),
31733
31886
  sections