@burdenoff/fe-libs 2026.1023.11 → 2026.1023.13

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.
@@ -8,6 +8,12 @@ export interface DynamicFooterProps {
8
8
  tagline: string;
9
9
  /** Version string */
10
10
  version?: string;
11
+ /**
12
+ * Copyright line, without the leading "©" (forwarded to `Footer`). Defaults
13
+ * to the shared "<year> Burdenoff. All rights reserved." label; products
14
+ * held by another entity pass their holder here (BUR-SOP-029).
15
+ */
16
+ copyright?: string;
11
17
  /** Custom footer columns (overrides defaults) */
12
18
  columns?: FooterColumn[];
13
19
  /** Custom social links (overrides API-fetched) */
@@ -22,4 +28,4 @@ export interface DynamicFooterProps {
22
28
  */
23
29
  hideOnInstalledApp?: boolean;
24
30
  }
25
- export declare function DynamicFooter({ productSlug, productName, tagline, version, columns: customColumns, socialLinks: customSocialLinks, legalPageOrder, deferRemoteData, hideOnInstalledApp, }: DynamicFooterProps): import('react/jsx-runtime').JSX.Element;
31
+ export declare function DynamicFooter({ productSlug, productName, tagline, version, copyright, columns: customColumns, socialLinks: customSocialLinks, legalPageOrder, deferRemoteData, hideOnInstalledApp, }: DynamicFooterProps): import('react/jsx-runtime').JSX.Element;
@@ -60,12 +60,12 @@ var y = _`
60
60
  function w(e) {
61
61
  return e.trim().replace(/[^A-Za-z0-9 ]/g, "").split(/\s+/).map((e, t) => t === 0 ? e.toLowerCase() : e.charAt(0).toUpperCase() + e.slice(1).toLowerCase()).join("");
62
62
  }
63
- function T({ productSlug: u, productName: f, tagline: p, version: m = "v1.0.0", columns: h, socialLinks: _, legalPageOrder: T = C, deferRemoteData: E = !1, hideOnInstalledApp: D = !0 }) {
64
- let { workspaceId: O, projectId: k } = n(), A = e("global"), { t: j } = t(), [M, N] = l(null), [P, F] = l(null);
63
+ function T({ productSlug: u, productName: f, tagline: p, version: m = "v1.0.0", copyright: h, columns: _, socialLinks: T, legalPageOrder: E = C, deferRemoteData: D = !1, hideOnInstalledApp: O = !0 }) {
64
+ let { workspaceId: k, projectId: A } = n(), j = e("global"), { t: M } = t(), [N, P] = l(null), [F, I] = l(null);
65
65
  s(() => {
66
- if (!A || E) return;
66
+ if (!j || D) return;
67
67
  let e = !1;
68
- return A.query({
68
+ return j.query({
69
69
  query: y,
70
70
  variables: { slug: u },
71
71
  fetchPolicy: "cache-first"
@@ -73,7 +73,7 @@ function T({ productSlug: u, productName: f, tagline: p, version: m = "v1.0.0",
73
73
  if (e) return;
74
74
  let n = t.data?.publicProduct;
75
75
  if (n) {
76
- if (!_) {
76
+ if (!T) {
77
77
  let e = n.socialLinks;
78
78
  if (e && Object.keys(e).length > 0) {
79
79
  let t = Object.entries(e).map(([e, t]) => ({
@@ -82,10 +82,10 @@ function T({ productSlug: u, productName: f, tagline: p, version: m = "v1.0.0",
82
82
  href: t,
83
83
  icon: S[e.toLowerCase()] || /* @__PURE__ */ g(d, { className: "size-5" })
84
84
  }));
85
- t.length > 0 && F(t);
85
+ t.length > 0 && I(t);
86
86
  }
87
87
  }
88
- return A.query({
88
+ return j.query({
89
89
  query: b,
90
90
  variables: {
91
91
  productId: n.id,
@@ -98,28 +98,28 @@ function T({ productSlug: u, productName: f, tagline: p, version: m = "v1.0.0",
98
98
  if (e || !t) return;
99
99
  let n = t.data?.publicContentPages?.items;
100
100
  if (!n) return;
101
- let r = T.map((e) => n.find((t) => t.slug === e)).filter((e) => e != null).map((e) => ({
101
+ let r = E.map((e) => n.find((t) => t.slug === e)).filter((e) => e != null).map((e) => ({
102
102
  id: e.slug,
103
103
  label: e.title,
104
104
  href: `/legal/${x[e.slug] || e.slug}`
105
105
  }));
106
- r.length > 0 && N(r);
106
+ r.length > 0 && P(r);
107
107
  }).catch(() => {}), () => {
108
108
  e = !0;
109
109
  };
110
110
  }, [
111
- _,
112
- E,
113
- A,
114
111
  T,
112
+ D,
113
+ j,
114
+ E,
115
115
  u
116
116
  ]);
117
- let I = c(() => {
118
- let e = h ?? r, t = M ? e.map((e) => e.title === "Legal" ? {
117
+ let L = c(() => {
118
+ let e = _ ?? r, t = N ? e.map((e) => e.title === "Legal" ? {
119
119
  ...e,
120
- links: M
120
+ links: N
121
121
  } : e) : e, n = (e, t) => {
122
- let n = j(e);
122
+ let n = M(e);
123
123
  return n === e ? t : n;
124
124
  };
125
125
  return t.map((e) => ({
@@ -131,21 +131,22 @@ function T({ productSlug: u, productName: f, tagline: p, version: m = "v1.0.0",
131
131
  })
132
132
  }));
133
133
  }, [
134
- M,
135
- h,
136
- j
134
+ N,
135
+ _,
136
+ M
137
137
  ]);
138
138
  return /* @__PURE__ */ g(a, {
139
139
  logo: /* @__PURE__ */ g(o, { size: "sidenav" }),
140
140
  productName: f,
141
141
  tagline: p,
142
- columns: I,
143
- socialLinks: _ ?? P ?? i,
142
+ columns: L,
143
+ socialLinks: T ?? F ?? i,
144
144
  version: m,
145
- hideOnInstalledApp: D,
145
+ copyright: h,
146
+ hideOnInstalledApp: O,
146
147
  context: {
147
- workspace: O ?? void 0,
148
- project: k ?? void 0
148
+ workspace: k ?? void 0,
149
+ project: A ?? void 0
149
150
  },
150
151
  renderLink: ({ href: e, external: t, children: n, className: r }) => t ? /* @__PURE__ */ g("a", {
151
152
  href: e,
@@ -2,14 +2,16 @@ import { useHasAuthToken as e } from "../../shared/graphql/useHasAuthToken.js";
2
2
  import { COUNTS_BY_APP_QUERY as t, DELETE_NOTIFICATION_MUTATION as n, MARK_ALL_READ_MUTATION as r, MARK_READ_MUTATION as i, MY_UNREAD_COUNT_SUBSCRIPTION as a, NOTIFICATIONS_QUERY as o, UNREAD_COUNT_QUERY as s } from "./gql.js";
3
3
  import { useCallback as c, useEffect as l, useRef as u, useState as d } from "react";
4
4
  //#region src/chrome/notificationCenter/useNotificationCenter.ts
5
- function f({ client: f, isOpen: p, lazy: m, filterTab: h, typeFilter: g, appFilter: _ }) {
6
- let [v, y] = d([]), [b, x] = d(0), [S, C] = d({}), [w, T] = d(0), [E, D] = d(!1), [O, k] = d(null), A = u(f);
5
+ var f = { preferWorkspaceAuth: !0 };
6
+ function p({ client: p, isOpen: m, lazy: h, filterTab: g, typeFilter: _, appFilter: v }) {
7
+ let [y, b] = d([]), [x, S] = d(0), [C, w] = d({}), [T, E] = d(0), [D, O] = d(!1), [k, A] = d(null), j = u(p);
7
8
  l(() => {
8
- A.current = f;
9
- }, [f]);
10
- let j = c(async () => {
9
+ j.current = p;
10
+ }, [p]);
11
+ let M = c(async () => {
11
12
  try {
12
- let e = (await A.current.query({
13
+ let e = (await j.current.query({
14
+ context: f,
13
15
  query: s,
14
16
  variables: { filter: {
15
17
  channel: "IN_APP",
@@ -17,11 +19,11 @@ function f({ client: f, isOpen: p, lazy: m, filterTab: h, typeFilter: g, appFilt
17
19
  } },
18
20
  fetchPolicy: "network-only"
19
21
  })).data;
20
- x(e?.myUnreadNotificationCount ?? 0);
22
+ S(e?.myUnreadNotificationCount ?? 0);
21
23
  } catch {}
22
- }, []), [M, N] = d(!1);
24
+ }, []), [N, P] = d(!1);
23
25
  l(() => {
24
- let e = () => N(!0);
26
+ let e = () => P(!0);
25
27
  if (typeof window < "u" && "requestIdleCallback" in window) {
26
28
  let t = window.requestIdleCallback(e, { timeout: 2e3 });
27
29
  return () => {
@@ -31,11 +33,12 @@ function f({ client: f, isOpen: p, lazy: m, filterTab: h, typeFilter: g, appFilt
31
33
  let t = setTimeout(e, 500);
32
34
  return () => clearTimeout(t);
33
35
  }, []);
34
- let P = e();
36
+ let F = e();
35
37
  l(() => {
36
- if (m || !M || !P) return;
37
- j();
38
- let e = A.current.subscribe({
38
+ if (h || !N || !F) return;
39
+ M();
40
+ let e = j.current.subscribe({
41
+ context: f,
39
42
  query: a,
40
43
  variables: { filter: {
41
44
  channel: "IN_APP",
@@ -45,28 +48,29 @@ function f({ client: f, isOpen: p, lazy: m, filterTab: h, typeFilter: g, appFilt
45
48
  }).subscribe({
46
49
  next: (e) => {
47
50
  let t = e.data;
48
- typeof t?.myUnreadNotificationCountStream == "number" && x(t.myUnreadNotificationCountStream);
51
+ typeof t?.myUnreadNotificationCountStream == "number" && S(t.myUnreadNotificationCountStream);
49
52
  },
50
53
  error: () => {}
51
- }), t = setInterval(j, 300 * 1e3);
54
+ }), t = setInterval(M, 300 * 1e3);
52
55
  return () => {
53
56
  e.unsubscribe(), clearInterval(t);
54
57
  };
55
58
  }, [
56
- j,
57
- m,
58
59
  M,
59
- P
60
+ h,
61
+ N,
62
+ F
60
63
  ]);
61
- let F = c(async () => {
62
- D(!0), k(null);
64
+ let I = c(async () => {
65
+ O(!0), A(null);
63
66
  try {
64
67
  let e = {
65
68
  channel: "IN_APP",
66
69
  archived: !1
67
70
  };
68
- h === "unread" && (e.isRead = !1), h === "read" && (e.isRead = !0), g !== "ALL" && (e.type = g), _ !== "ALL" && (e.appName = _);
69
- let t = (await A.current.query({
71
+ g === "unread" && (e.isRead = !1), g === "read" && (e.isRead = !0), _ !== "ALL" && (e.type = _), v !== "ALL" && (e.appName = v);
72
+ let t = (await j.current.query({
73
+ context: f,
70
74
  query: o,
71
75
  variables: {
72
76
  filter: e,
@@ -75,79 +79,85 @@ function f({ client: f, isOpen: p, lazy: m, filterTab: h, typeFilter: g, appFilt
75
79
  },
76
80
  fetchPolicy: "network-only"
77
81
  })).data?.notifications;
78
- t && (y(t.items ?? []), T(t.total ?? 0));
82
+ t && (b(t.items ?? []), E(t.total ?? 0));
79
83
  } catch (e) {
80
- k(e instanceof Error ? e.message : "Failed to load notifications");
84
+ A(e instanceof Error ? e.message : "Failed to load notifications");
81
85
  } finally {
82
- D(!1);
86
+ O(!1);
83
87
  }
84
88
  }, [
85
- h,
86
89
  g,
87
- _
88
- ]), I = c(async () => {
90
+ _,
91
+ v
92
+ ]), L = c(async () => {
89
93
  try {
90
- let e = (await A.current.query({
94
+ let e = (await j.current.query({
95
+ context: f,
91
96
  query: t,
92
97
  fetchPolicy: "network-only"
93
98
  })).data;
94
- C(e?.myNotificationCountsByApp ?? {});
99
+ w(e?.myNotificationCountsByApp ?? {});
95
100
  } catch {}
96
101
  }, []);
97
102
  return l(() => {
98
- p && (m && j(), F(), I(), j());
103
+ m && (h && M(), I(), L(), M());
99
104
  }, [
100
- p,
101
- F,
105
+ m,
102
106
  I,
103
- j,
104
- m
107
+ L,
108
+ M,
109
+ h
105
110
  ]), {
106
- notifications: v,
107
- unreadCount: b,
108
- countsByApp: S,
109
- totalCount: w,
110
- loading: E,
111
- error: O,
112
- fetchNotifications: F,
111
+ notifications: y,
112
+ unreadCount: x,
113
+ countsByApp: C,
114
+ totalCount: T,
115
+ loading: D,
116
+ error: k,
117
+ fetchNotifications: I,
113
118
  handleMarkAsRead: c(async (e) => {
114
119
  try {
115
- await A.current.mutate({
120
+ await j.current.mutate({
121
+ context: f,
116
122
  mutation: i,
117
123
  variables: { id: e }
118
- }), y((t) => t.map((t) => t.id === e ? {
124
+ }), b((t) => t.map((t) => t.id === e ? {
119
125
  ...t,
120
126
  isRead: !0,
121
127
  readAt: (/* @__PURE__ */ new Date()).toISOString()
122
- } : t)), x((e) => Math.max(0, e - 1));
128
+ } : t)), S((e) => Math.max(0, e - 1));
123
129
  } catch (e) {
124
130
  console.error("Failed to mark notification as read:", e);
125
131
  }
126
132
  }, []),
127
133
  handleMarkAllAsRead: c(async () => {
128
134
  try {
129
- await A.current.mutate({ mutation: r }), y((e) => e.map((e) => ({
135
+ await j.current.mutate({
136
+ context: f,
137
+ mutation: r
138
+ }), b((e) => e.map((e) => ({
130
139
  ...e,
131
140
  isRead: !0,
132
141
  readAt: e.readAt || (/* @__PURE__ */ new Date()).toISOString()
133
- }))), x(0);
142
+ }))), S(0);
134
143
  } catch (e) {
135
144
  console.error("Failed to mark all notifications as read:", e);
136
145
  }
137
146
  }, []),
138
147
  handleDelete: c(async (e) => {
139
148
  try {
140
- await A.current.mutate({
149
+ await j.current.mutate({
150
+ context: f,
141
151
  mutation: n,
142
152
  variables: { id: e }
143
153
  });
144
- let t = v.find((t) => t.id === e);
145
- y((t) => t.filter((t) => t.id !== e)), T((e) => Math.max(0, e - 1)), t && !t.isRead && x((e) => Math.max(0, e - 1));
154
+ let t = y.find((t) => t.id === e);
155
+ b((t) => t.filter((t) => t.id !== e)), E((e) => Math.max(0, e - 1)), t && !t.isRead && S((e) => Math.max(0, e - 1));
146
156
  } catch (e) {
147
157
  console.error("Failed to delete notification:", e);
148
158
  }
149
- }, [v])
159
+ }, [y])
150
160
  };
151
161
  }
152
162
  //#endregion
153
- export { f as useNotificationCenter };
163
+ export { p as useNotificationCenter };
@@ -70,7 +70,7 @@ export { resolveAuthBridgeConfig, resolveShellAuthBridgeConfig } from './authBri
70
70
  export type { RuntimeAuthBridgeConfig } from './authBridgeUrls';
71
71
  export { CANONICAL_DOCS_HOSTS, buildProductDocsUrl, canonicalizeDocsOrigin, getCatalogDocsOrigin, normalizeDocsPath, resolveDocsEnvironment, resolveProductDocsOrigin, toEnvironmentDocsOrigin, toTrustedDocsOrigin, } from './docsUrls';
72
72
  export type { DocsEnvironment, ResolveDocsOriginOptions } from './docsUrls';
73
- export { PRODUCTS, PRODUCT_URLS, BOTMANOR_PRODUCT_SLUG, getProductUrl, getProductsWithUrls, findProductConfig, } from './products';
73
+ export { PRODUCTS, PRODUCT_URLS, BOTMANOR_PRODUCT_SLUG, getProductUrl, getProductsWithUrls, findProductConfig, isAlphaAppShell, toAlphaAppUrl, toCurrentEstateAppUrl, } from './products';
74
74
  export type { ProductConfig } from './products';
75
75
  export { PRODUCT_SLUG_RENAMES, PRODUCT_SLUG_EMIT, normalizeProductSlug, legacyProductSlug, isSameProductSlug, emitProductSlug, } from './productSlug';
76
76
  export type { ProductSlugRename } from './productSlug';
@@ -72,6 +72,28 @@ export declare const PRODUCT_URLS: {
72
72
  };
73
73
  };
74
74
  export declare const PRODUCTS: ProductConfig[];
75
+ /**
76
+ * Whether the running shell belongs to the alpha estate.
77
+ *
78
+ * - Desktop (Electron): the baked runtime config's `environment`.
79
+ * - Capacitor: the build's deploy target (the WebView origin is always localhost).
80
+ * - Web: the `alphaapp.*` / `alphaadmin.*` hosts.
81
+ */
82
+ export declare function isAlphaAppShell(hostname?: string): boolean;
83
+ /**
84
+ * The alpha twin of a production app-shell URL: `https://app.<domain>/…` →
85
+ * `https://alphaapp.<domain>/…` for a known product domain. Any other URL
86
+ * (already alpha, a non-product host, unparseable) is returned unchanged, so
87
+ * the call is idempotent and never invents a host outside the product estate.
88
+ */
89
+ export declare function toAlphaAppUrl(url: string): string;
90
+ /**
91
+ * A product's app-shell URL in the estate the current shell runs in: alpha
92
+ * shells link to the other products' alpha shells (cross-product SSO stays on
93
+ * the alpha session), prod shells to prod. Accepts the registry's `appUrl`
94
+ * as well as catalog URLs.
95
+ */
96
+ export declare function toCurrentEstateAppUrl(url: string): string;
75
97
  export declare function getProductUrl(productId: string): string;
76
98
  /** Catalog entry for a product id, accepting either spelling of a renamed slug. */
77
99
  export declare function findProductConfig(productId: string): ProductConfig | undefined;
@@ -1,6 +1,9 @@
1
- import { emitProductSlug as e, isSameProductSlug as t, legacyProductSlug as n } from "./productSlug.js";
1
+ import { isCapacitorNative as e } from "../native/capacitor.js";
2
+ import { getDesktopRuntimeConfig as t } from "../native/desktop.js";
3
+ import { ALL_PRODUCT_DOMAINS as n, ALPHA_HOSTS as r, getCurrentHostname as i, resolveNativeDeployTarget as a } from "./gatewayUrls.js";
4
+ import { emitProductSlug as o, isSameProductSlug as s, legacyProductSlug as c } from "./productSlug.js";
2
5
  //#region src/shared/config/products.ts
3
- var r = e("botmanor"), i = {
6
+ var l = o("botmanor"), u = {
4
7
  development: {
5
8
  fluidgrids: "http://localhost:5147/",
6
9
  bigconsole: "http://localhost:5148/",
@@ -48,7 +51,7 @@ var r = e("botmanor"), i = {
48
51
  theculturepost: "https://app.theculturepost.com/",
49
52
  theglobalfuel: "https://app.theglobalfuel.com/"
50
53
  }
51
- }, a = [
54
+ }, d = [
52
55
  {
53
56
  id: "vibecontrols",
54
57
  name: "VibeControls",
@@ -78,7 +81,7 @@ var r = e("botmanor"), i = {
78
81
  description: "AI development console"
79
82
  },
80
83
  {
81
- id: r,
84
+ id: l,
82
85
  name: "BotManor",
83
86
  icon: "BM",
84
87
  color: "#f59e0b",
@@ -317,27 +320,47 @@ var r = e("botmanor"), i = {
317
320
  description: "Energy, minerals & resources platform"
318
321
  }
319
322
  ];
320
- function o() {
323
+ function f() {
321
324
  return "production";
322
325
  }
323
- function s(e, t) {
324
- return e[t] ?? e[n(t)];
326
+ function p(e, t) {
327
+ return e[t] ?? e[c(t)];
325
328
  }
326
- function c(e) {
327
- return s(i[o()], e) || "#";
329
+ function m(n = i()) {
330
+ let o = t()?.environment;
331
+ return o ? o === "alpha" : e() ? a() === "alpha" : r.has(n.toLowerCase());
328
332
  }
329
- function l(e) {
330
- return a.find((n) => t(n.id, e));
333
+ function h(e) {
334
+ let t;
335
+ try {
336
+ t = new URL(e);
337
+ } catch {
338
+ return e;
339
+ }
340
+ let r = t.hostname.toLowerCase();
341
+ if (t.protocol !== "https:" || !r.startsWith("app.")) return e;
342
+ let i = r.slice(4);
343
+ return n.includes(i) ? (t.hostname = `alphaapp.${i}`, t.toString()) : e;
344
+ }
345
+ function g(e) {
346
+ return m() ? h(e) : e;
347
+ }
348
+ function _(e) {
349
+ let t = f(), n = p(u[t], e);
350
+ return n ? t === "production" ? g(n) : n : "#";
351
+ }
352
+ function v(e) {
353
+ return d.find((t) => s(t.id, e));
331
354
  }
332
- function u(e) {
333
- let t = s(i.production, e);
355
+ function y(e) {
356
+ let t = p(u.production, e);
334
357
  return t ? `${t}icons/icon.svg` : void 0;
335
358
  }
336
- function d() {
337
- return a.map((e) => ({
359
+ function b() {
360
+ return d.map((e) => ({
338
361
  ...e,
339
- url: c(e.id)
362
+ url: _(e.id)
340
363
  }));
341
364
  }
342
365
  //#endregion
343
- export { r as BOTMANOR_PRODUCT_SLUG, a as PRODUCTS, i as PRODUCT_URLS, l as findProductConfig, u as getProductLogoUrl, c as getProductUrl, d as getProductsWithUrls };
366
+ export { l as BOTMANOR_PRODUCT_SLUG, d as PRODUCTS, u as PRODUCT_URLS, v as findProductConfig, y as getProductLogoUrl, _ as getProductUrl, b as getProductsWithUrls, m as isAlphaAppShell, h as toAlphaAppUrl, g as toCurrentEstateAppUrl };