@burdenoff/fe-libs 2026.1021.2 → 2026.1021.4

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.
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Header organization / workspace switches (BOFF-8078), shared by every shell
3
+ * through `useContextSwitchController` (lifted from fluidgrids-app, where the
4
+ * same file was copied into bigconsole-app and healthybowl-app).
5
+ *
6
+ * `selectWorkspace` (microfe-auth) issues a token for the workspace it is
7
+ * given and then lists that workspace's projects. The list request does NOT
8
+ * carry the workspace it was asked about: fe-libs `graphqlFetch` derives the
9
+ * `x-workspace-id` header, and the context the fresh workspace token is
10
+ * validated against, from the stored ActiveContext (`bf-p-<uid>-context`, with
11
+ * the `?workspace=` URL param taking precedence). When the handler calls
12
+ * `selectWorkspace(B)` while ActiveContext still names workspace A, the new
13
+ * token is dropped as "invalid for context A", a token for A is minted in its
14
+ * place, and the request lists A's projects. The handler then selected A's
15
+ * first project, so the stored context paired workspace B with a project from
16
+ * workspace A, and kept that pair across reloads.
17
+ *
18
+ * Measured in prod on 2026-09-28 with the header switcher, A = f423fce9 and
19
+ * B = 089761cb: `MyProjectsForContext` went out with `x-workspace-id: f423fce9`
20
+ * and A's workspace token right after `IssueWorkspaceToken` returned B's token,
21
+ * and answered A's only project (e35d7c3b). VibeControls, which publishes the
22
+ * new workspace first, ends on B's project (e6febd92).
23
+ *
24
+ * So both switches publish the target context to ActiveContext first — the
25
+ * organization and workspace, with the project cleared — and only then ask
26
+ * microfe-auth to select it. Only a project that belongs to the target
27
+ * workspace is ever selected, and a switch that a later switch has superseded
28
+ * is discarded instead of applied.
29
+ */
30
+ export interface SwitchableOrganization {
31
+ id: string;
32
+ tenantId?: unknown;
33
+ }
34
+ export interface SwitchableWorkspace {
35
+ id: string;
36
+ organizationId?: string | null;
37
+ tenantId?: unknown;
38
+ }
39
+ export interface SwitchableProject {
40
+ id: string;
41
+ workspaceId?: string | null;
42
+ }
43
+ export interface WorkspaceSelection<P extends SwitchableProject> {
44
+ success: boolean;
45
+ projects?: ReadonlyArray<P>;
46
+ }
47
+ export interface ContextSwitchDeps<O extends SwitchableOrganization, W extends SwitchableWorkspace, P extends SwitchableProject> {
48
+ /**
49
+ * Monotonic switch counter (a React ref), shared by the organization and
50
+ * workspace switches: the most recent switch of either kind wins.
51
+ */
52
+ requests: {
53
+ current: number;
54
+ };
55
+ setOrganization: (organizationId: string | null) => void;
56
+ setWorkspace: (workspaceId: string | null) => void;
57
+ setProject: (projectId: string | null) => void;
58
+ setTenant: (tenantId: string | null) => void;
59
+ selectOrganization: (organization: O) => Promise<ReadonlyArray<W>>;
60
+ selectWorkspace: (workspace: W) => Promise<WorkspaceSelection<P>>;
61
+ selectProject: (project: P) => void;
62
+ }
63
+ export type ContextSwitchOutcome = {
64
+ status: 'project-selected';
65
+ workspaceId: string;
66
+ projectId: string;
67
+ } | {
68
+ status: 'no-project';
69
+ workspaceId: string;
70
+ } | {
71
+ status: 'no-workspace';
72
+ } | {
73
+ status: 'superseded';
74
+ };
75
+ /** The first project the selection returned that belongs to `workspaceId`. */
76
+ export declare function firstProjectOfWorkspace<P extends SwitchableProject>(selection: WorkspaceSelection<P>, workspaceId: string): P | null;
77
+ /** Header workspace switch: enter `workspace` and select its first project. */
78
+ export declare function switchWorkspace<O extends SwitchableOrganization, W extends SwitchableWorkspace, P extends SwitchableProject>(deps: ContextSwitchDeps<O, W, P>, workspace: W): Promise<ContextSwitchOutcome>;
79
+ /**
80
+ * Header organization switch: clear the workspace and project, load the
81
+ * organization's workspaces, then enter the first one exactly as a workspace
82
+ * switch would.
83
+ */
84
+ export declare function switchOrganization<O extends SwitchableOrganization, W extends SwitchableWorkspace, P extends SwitchableProject>(deps: ContextSwitchDeps<O, W, P>, organization: O): Promise<ContextSwitchOutcome>;
@@ -0,0 +1,31 @@
1
+ //#region src/chrome/contextSwitcher/contextSwitch.ts
2
+ function e(e, t) {
3
+ return e.success ? e.projects?.find((e) => e.workspaceId === t) ?? null : null;
4
+ }
5
+ async function t(t, n, r) {
6
+ n.organizationId && t.setOrganization(n.organizationId), typeof n.tenantId == "string" && t.setTenant(n.tenantId), t.setWorkspace(n.id), t.setProject(null);
7
+ let i = await t.selectWorkspace(n);
8
+ if (r !== t.requests.current) return { status: "superseded" };
9
+ let a = e(i, n.id);
10
+ return a ? (t.selectProject(a), t.setProject(a.id), {
11
+ status: "project-selected",
12
+ workspaceId: n.id,
13
+ projectId: a.id
14
+ }) : {
15
+ status: "no-project",
16
+ workspaceId: n.id
17
+ };
18
+ }
19
+ function n(e, n) {
20
+ return t(e, n, ++e.requests.current);
21
+ }
22
+ async function r(e, n) {
23
+ let r = ++e.requests.current;
24
+ e.setOrganization(n.id), typeof n.tenantId == "string" && e.setTenant(n.tenantId), e.setWorkspace(null), e.setProject(null);
25
+ let i = await e.selectOrganization(n);
26
+ if (r !== e.requests.current) return { status: "superseded" };
27
+ let a = i[0];
28
+ return a ? t(e, a, r) : { status: "no-workspace" };
29
+ }
30
+ //#endregion
31
+ export { e as firstProjectOfWorkspace, r as switchOrganization, n as switchWorkspace };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * resolveSwitchTarget — where to land when the active org/workspace changes.
3
+ *
4
+ * The workspace token is re-issued on every context switch (BOFF-2821). A route
5
+ * that holds a *resource id belonging to the old workspace* (a workflow, a run,
6
+ * an agent, a file …) would then fire queries the new token cannot authorize —
7
+ * the "Unexpected error" banners — or show the old workspace's record under the
8
+ * new workspace. So when the switch starts on such a resource-deep route the
9
+ * shell replace-navigates to that section's list page; the stale id is dropped.
10
+ *
11
+ * List/section pages (no resource id) are NOT redirected: they re-scope in place
12
+ * through the new selection. Each product passes its own rule table, because the
13
+ * route shapes are product-specific (VibeControls' `/vibecontrols/<section>/<id>`,
14
+ * FluidGrids' `/workflows/workflow/<key>/<version>` and `/workflows/runs/<id>`).
15
+ *
16
+ * Lifted from vibecontrols-app `src/routes/resolveSwitchTarget.ts` (BOFF-2821),
17
+ * with VibeControls' hard-coded tables turned into a parameter.
18
+ */
19
+ /** One resource-deep route and the list/landing route that replaces it. */
20
+ export interface ContextSwitchRedirectRule {
21
+ /**
22
+ * Matches a pathname that carries a resource id of the old context. Rules are
23
+ * tried in order; the first match wins. A `g`/`y` flag is ignored (the match
24
+ * is always made from the start of a fresh state), so a shared rule table
25
+ * gives the same answer on every call.
26
+ */
27
+ match: RegExp;
28
+ /** The landing to go to: a fixed path, or one computed from the match. */
29
+ to: string | ((match: RegExpExecArray) => string);
30
+ }
31
+ /**
32
+ * The target pathname to replace-navigate to after a context switch, or `null`
33
+ * to stay on `pathname` (only the context query params change).
34
+ *
35
+ * Returns `null` when no rule matches, and when the matching rule's landing is
36
+ * `pathname` itself, so a rule can never loop on its own landing page.
37
+ */
38
+ export declare function resolveSwitchTarget(pathname: string, rules: ReadonlyArray<ContextSwitchRedirectRule>): string | null;
39
+ /**
40
+ * A rule for a family of `<prefix>/<section>/<id…>` detail routes: anything
41
+ * deeper than a `<prefix>/<section>` landing redirects back to that landing, so
42
+ * current and future detail routes are covered without enumerating each one.
43
+ * The bare `<prefix>/<section>` page and `<prefix>` itself stay in place.
44
+ *
45
+ * `sectionLandingRule('/vibecontrols')` is VibeControls' rule:
46
+ * `/vibecontrols/agents/a1` → `/vibecontrols/agents`,
47
+ * `/vibecontrols/calendar/task/t1` → `/vibecontrols/calendar`.
48
+ *
49
+ * @param prefix a rooted path without a trailing slash, e.g. `/vibecontrols`
50
+ */
51
+ export declare function sectionLandingRule(prefix: string): ContextSwitchRedirectRule;
@@ -0,0 +1,26 @@
1
+ //#region src/chrome/contextSwitcher/resolveSwitchTarget.ts
2
+ function e(e, n) {
3
+ for (let r of n) {
4
+ let n = t(r.match).exec(e);
5
+ if (!n) continue;
6
+ let i = typeof r.to == "string" ? r.to : r.to(n);
7
+ return i === e ? null : i;
8
+ }
9
+ return null;
10
+ }
11
+ function t(e) {
12
+ return !e.global && !e.sticky ? e : new RegExp(e.source, e.flags.replace(/[gy]/g, ""));
13
+ }
14
+ function n(e) {
15
+ return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16
+ }
17
+ function r(e) {
18
+ if (!/^\/[^/]/.test(e) || e.endsWith("/")) throw Error(`sectionLandingRule: prefix must be a rooted path without a trailing slash, got "${e}"`);
19
+ let t = n(e);
20
+ return {
21
+ match: RegExp(`^${t}/([^/]+)/[^/]+`),
22
+ to: (t) => `${e}/${t[1]}`
23
+ };
24
+ }
25
+ //#endregion
26
+ export { e as resolveSwitchTarget, r as sectionLandingRule };
@@ -0,0 +1,50 @@
1
+ import { ContextSwitchOutcome, SwitchableOrganization, SwitchableProject, SwitchableWorkspace, WorkspaceSelection } from './contextSwitch';
2
+ import { ContextSwitchRedirectRule } from './resolveSwitchTarget';
3
+ /** Which header switch produced an outcome. */
4
+ export type ContextSwitchKind = 'organization' | 'workspace';
5
+ export interface ContextSwitchEvent {
6
+ kind: ContextSwitchKind;
7
+ /** The organization or workspace the user picked. */
8
+ id: string;
9
+ outcome: ContextSwitchOutcome;
10
+ }
11
+ export interface UseContextSwitchControllerOptions<O extends SwitchableOrganization, W extends SwitchableWorkspace, P extends SwitchableProject> {
12
+ /** The switcher's lists; a pick is resolved to its full record here. */
13
+ organizations: ReadonlyArray<O>;
14
+ workspaces: ReadonlyArray<W>;
15
+ projects: ReadonlyArray<P>;
16
+ /** microfe-auth: select the organization and list its workspaces. */
17
+ selectOrganization: (organization: O) => Promise<ReadonlyArray<W>>;
18
+ /** microfe-auth: issue the workspace token and list the workspace's projects. */
19
+ selectWorkspace: (workspace: W) => Promise<WorkspaceSelection<P>>;
20
+ /** microfe-auth: select a project. */
21
+ selectProject: (project: P) => void;
22
+ /**
23
+ * The product's resource-deep routes and their list pages. Omitted or empty:
24
+ * a switch never navigates.
25
+ */
26
+ redirectRules?: ReadonlyArray<ContextSwitchRedirectRule>;
27
+ /** Called with every switch's outcome, superseded ones included. */
28
+ onSwitch?: (event: ContextSwitchEvent) => void;
29
+ }
30
+ export interface ContextSwitchController {
31
+ /**
32
+ * Switch to `organization` and enter its first workspace. Resolves to the
33
+ * outcome, or `null` when the id is not in `organizations`.
34
+ */
35
+ onOrganizationChange: (organization: {
36
+ id: string;
37
+ }) => Promise<ContextSwitchOutcome | null>;
38
+ /**
39
+ * Switch to `workspace` and select its first project. Resolves to the
40
+ * outcome, or `null` when the id is not in `workspaces`.
41
+ */
42
+ onWorkspaceChange: (workspace: {
43
+ id: string;
44
+ }) => Promise<ContextSwitchOutcome | null>;
45
+ /** Select `project` (an id missing from `projects` still becomes active). */
46
+ onProjectChange: (project: {
47
+ id: string;
48
+ }) => void;
49
+ }
50
+ export declare function useContextSwitchController<O extends SwitchableOrganization, W extends SwitchableWorkspace, P extends SwitchableProject>(options: UseContextSwitchControllerOptions<O, W, P>): ContextSwitchController;
@@ -0,0 +1,62 @@
1
+ import { useActiveContext as e } from "../../shared/providers/shell/ActiveContextProvider.js";
2
+ import { useLogger as t } from "../../shared/logger/useLogger.js";
3
+ import { switchOrganization as n, switchWorkspace as r } from "./contextSwitch.js";
4
+ import { resolveSwitchTarget as i } from "./resolveSwitchTarget.js";
5
+ import { useRef as a } from "react";
6
+ import { useLocation as o, useNavigate as s } from "react-router";
7
+ //#region src/chrome/contextSwitcher/useContextSwitchController.ts
8
+ function c(c) {
9
+ let { organizations: l, workspaces: u, projects: d, selectOrganization: f, selectWorkspace: p, selectProject: m, redirectRules: h, onSwitch: g } = c, _ = t("ContextSwitcher"), v = s(), { pathname: y } = o(), b = e(), x = {
10
+ requests: a(0),
11
+ setOrganization: b.setOrganization,
12
+ setWorkspace: b.setWorkspace,
13
+ setProject: b.setProject,
14
+ setTenant: b.setTenant,
15
+ selectOrganization: f,
16
+ selectWorkspace: p,
17
+ selectProject: m
18
+ }, S = () => {
19
+ if (!h || h.length === 0) return;
20
+ let e = i(y, h);
21
+ e && (_.info("Leaving a resource of the previous context", {
22
+ from: y,
23
+ to: e
24
+ }), v({
25
+ pathname: e,
26
+ search: typeof window > "u" ? "" : window.location.search
27
+ }, { replace: !0 }));
28
+ }, C = (e, t, n) => {
29
+ n.status === "superseded" && _.info(`Discarding stale ${e}-switch result`, { [`${e}Id`]: t }), g?.({
30
+ kind: e,
31
+ id: t,
32
+ outcome: n
33
+ });
34
+ };
35
+ return {
36
+ onOrganizationChange: async (e) => {
37
+ let t = l.find((t) => t.id === e.id);
38
+ if (!t) return _.warn("Organization switch ignored: not in the switcher list", { organizationId: e.id }), null;
39
+ _.info("Changing organization", { organizationId: t.id });
40
+ let r = t.id !== b.organizationId, i = n(x, t);
41
+ r && S();
42
+ let a = await i;
43
+ return C("organization", t.id, a), a;
44
+ },
45
+ onWorkspaceChange: async (e) => {
46
+ let t = u.find((t) => t.id === e.id);
47
+ if (!t) return _.warn("Workspace switch ignored: not in the switcher list", { workspaceId: e.id }), null;
48
+ _.info("Changing workspace", { workspaceId: t.id });
49
+ let n = t.id !== b.workspaceId, i = r(x, t);
50
+ n && S();
51
+ let a = await i;
52
+ return C("workspace", t.id, a), a;
53
+ },
54
+ onProjectChange: (e) => {
55
+ _.info("Changing project", { projectId: e.id });
56
+ let t = d.find((t) => t.id === e.id);
57
+ t && m(t), b.setProject(e.id);
58
+ }
59
+ };
60
+ }
61
+ //#endregion
62
+ export { c as useContextSwitchController };
@@ -84,6 +84,12 @@ export type { LanguageSwitcherProps, Language } from './LanguageSwitcher';
84
84
  export type { GeographySwitcherProps } from './GeographySwitcher';
85
85
  export type { NotificationCenterProps } from './NotificationCenter';
86
86
  export type { ContextSwitcherProps, Organization, Workspace, Project, } from './ContextSwitcher';
87
+ export { useContextSwitchController } from './contextSwitcher/useContextSwitchController';
88
+ export type { ContextSwitchController, ContextSwitchEvent, ContextSwitchKind, UseContextSwitchControllerOptions, } from './contextSwitcher/useContextSwitchController';
89
+ export { firstProjectOfWorkspace, switchOrganization, switchWorkspace, } from './contextSwitcher/contextSwitch';
90
+ export type { ContextSwitchDeps, ContextSwitchOutcome, SwitchableOrganization, SwitchableProject, SwitchableWorkspace, WorkspaceSelection, } from './contextSwitcher/contextSwitch';
91
+ export { resolveSwitchTarget, sectionLandingRule, } from './contextSwitcher/resolveSwitchTarget';
92
+ export type { ContextSwitchRedirectRule } from './contextSwitcher/resolveSwitchTarget';
87
93
  export type { LanguagePickerSheetProps } from './LanguagePickerSheet';
88
94
  export type { ProductPickerSheetProps } from './ProductPickerSheet';
89
95
  export type { AccountSwitcherProps, Account } from './AccountSwitcher';
package/dist/chrome.js CHANGED
@@ -23,26 +23,29 @@ import { NotificationCenter as D } from "./chrome/NotificationCenter.js";
23
23
  import { LanguagePickerSheet as O } from "./chrome/LanguagePickerSheet.js";
24
24
  import { ProductPickerSheet as k } from "./chrome/ProductPickerSheet.js";
25
25
  import { AccountSwitcher as A } from "./chrome/AccountSwitcher.js";
26
- import { AutoBreadcrumbs as j, registerBreadcrumbName as M, unregisterBreadcrumbName as N } from "./chrome/AutoBreadcrumbs.js";
27
- import { NavCustomizeDialog as P } from "./chrome/NavCustomizeDialog.js";
28
- import { PersonaSimulationPill as F, PersonaSurfaceControls as I } from "./chrome/PersonaSurfaceControls.js";
29
- import { ProductSideNav as L } from "./chrome/ProductSideNav.js";
30
- import { BrandMark as R } from "./chrome/BrandMark.js";
31
- import { DynamicFooter as z } from "./chrome/DynamicFooter.js";
32
- import { PanelTabs as B } from "./chrome/PanelTabs.js";
33
- import { PanelContainer as V } from "./chrome/PanelContainer.js";
34
- import { StatusBar as H } from "./chrome/StatusBar.js";
35
- import { PluginRoutes as U } from "./chrome/PluginRoutes.js";
36
- import { PluginSettingsPage as W } from "./chrome/PluginSettingsPage.js";
37
- import { PluginToolbar as G } from "./chrome/PluginToolbar.js";
38
- import { PluginWidgetGrid as K } from "./chrome/PluginWidgetGrid.js";
39
- import { PluginContextMenuTarget as q } from "./chrome/PluginContextMenuTarget.js";
40
- import { ThemePreferences as J } from "./chrome/ThemePreferences.js";
41
- import { selectMobileTabs as Y } from "./chrome/mobileTabBar/selectTabs.js";
42
- import { useMobileTabBarVisible as X } from "./chrome/mobileTabBar/useMobileTabBarVisible.js";
43
- import { MobileTabBar as Z } from "./chrome/MobileTabBar.js";
44
- import { NativeTopBar as Q } from "./chrome/NativeTopBar.js";
45
- import { SwipeNavigation as $ } from "./chrome/SwipeNavigation.js";
46
- import { NativePageTransition as ee } from "./chrome/NativePageTransition.js";
47
- import { findNavTitle as te, useNativeTopBarTitle as ne } from "./chrome/nativeTopBarTitle.js";
48
- export { A as AccountSwitcher, o as AppShellLayout, j as AutoBreadcrumbs, R as BrandMark, _ as Breadcrumbs, g as CommandPalette, w as ContextSwitcher, d as DEFAULT_FOOTER_COLUMNS, f as DEFAULT_SOCIAL_LINKS, z as DynamicFooter, p as Footer, l as FooterBar, T as GeographySwitcher, h as GlobalSearch, c as HeaderBar, O as LanguagePickerSheet, C as LanguageSwitcher, Z as MobileTabBar, t as NAV_MORE_ID_PREFIX, ee as NativePageTransition, Q as NativeTopBar, P as NavCustomizeDialog, v as NavLink, D as NotificationCenter, s as PageHeader, V as PanelContainer, B as PanelTabs, F as PersonaSimulationPill, I as PersonaSurfaceControls, q as PluginContextMenuTarget, u as PluginFooterStrip, U as PluginRoutes, W as PluginSettingsPage, G as PluginToolbar, K as PluginWidgetGrid, k as ProductPickerSheet, L as ProductSideNav, S as ProductSwitcher, x as ProfileSwitcher, E as PushOptInToggle, e as ShortcutHelpDialog, m as SideNavigation, b as Sidebar, H as StatusBar, $ as SwipeNavigation, y as Tabs, J as ThemePreferences, n as applyNavVisibility, r as applyPermissionFilter, i as applySurfaceVisibility, te as findNavTitle, a as isItemEffectivelyShown, M as registerBreadcrumbName, Y as selectMobileTabs, N as unregisterBreadcrumbName, X as useMobileTabBarVisible, ne as useNativeTopBarTitle };
26
+ import { firstProjectOfWorkspace as j, switchOrganization as M, switchWorkspace as N } from "./chrome/contextSwitcher/contextSwitch.js";
27
+ import { resolveSwitchTarget as P, sectionLandingRule as F } from "./chrome/contextSwitcher/resolveSwitchTarget.js";
28
+ import { useContextSwitchController as I } from "./chrome/contextSwitcher/useContextSwitchController.js";
29
+ import { AutoBreadcrumbs as L, registerBreadcrumbName as R, unregisterBreadcrumbName as z } from "./chrome/AutoBreadcrumbs.js";
30
+ import { NavCustomizeDialog as B } from "./chrome/NavCustomizeDialog.js";
31
+ import { PersonaSimulationPill as V, PersonaSurfaceControls as H } from "./chrome/PersonaSurfaceControls.js";
32
+ import { ProductSideNav as U } from "./chrome/ProductSideNav.js";
33
+ import { BrandMark as W } from "./chrome/BrandMark.js";
34
+ import { DynamicFooter as G } from "./chrome/DynamicFooter.js";
35
+ import { PanelTabs as K } from "./chrome/PanelTabs.js";
36
+ import { PanelContainer as q } from "./chrome/PanelContainer.js";
37
+ import { StatusBar as J } from "./chrome/StatusBar.js";
38
+ import { PluginRoutes as Y } from "./chrome/PluginRoutes.js";
39
+ import { PluginSettingsPage as X } from "./chrome/PluginSettingsPage.js";
40
+ import { PluginToolbar as Z } from "./chrome/PluginToolbar.js";
41
+ import { PluginWidgetGrid as Q } from "./chrome/PluginWidgetGrid.js";
42
+ import { PluginContextMenuTarget as $ } from "./chrome/PluginContextMenuTarget.js";
43
+ import { ThemePreferences as ee } from "./chrome/ThemePreferences.js";
44
+ import { selectMobileTabs as te } from "./chrome/mobileTabBar/selectTabs.js";
45
+ import { useMobileTabBarVisible as ne } from "./chrome/mobileTabBar/useMobileTabBarVisible.js";
46
+ import { MobileTabBar as re } from "./chrome/MobileTabBar.js";
47
+ import { NativeTopBar as ie } from "./chrome/NativeTopBar.js";
48
+ import { SwipeNavigation as ae } from "./chrome/SwipeNavigation.js";
49
+ import { NativePageTransition as oe } from "./chrome/NativePageTransition.js";
50
+ import { findNavTitle as se, useNativeTopBarTitle as ce } from "./chrome/nativeTopBarTitle.js";
51
+ export { A as AccountSwitcher, o as AppShellLayout, L as AutoBreadcrumbs, W as BrandMark, _ as Breadcrumbs, g as CommandPalette, w as ContextSwitcher, d as DEFAULT_FOOTER_COLUMNS, f as DEFAULT_SOCIAL_LINKS, G as DynamicFooter, p as Footer, l as FooterBar, T as GeographySwitcher, h as GlobalSearch, c as HeaderBar, O as LanguagePickerSheet, C as LanguageSwitcher, re as MobileTabBar, t as NAV_MORE_ID_PREFIX, oe as NativePageTransition, ie as NativeTopBar, B as NavCustomizeDialog, v as NavLink, D as NotificationCenter, s as PageHeader, q as PanelContainer, K as PanelTabs, V as PersonaSimulationPill, H as PersonaSurfaceControls, $ as PluginContextMenuTarget, u as PluginFooterStrip, Y as PluginRoutes, X as PluginSettingsPage, Z as PluginToolbar, Q as PluginWidgetGrid, k as ProductPickerSheet, U as ProductSideNav, S as ProductSwitcher, x as ProfileSwitcher, E as PushOptInToggle, e as ShortcutHelpDialog, m as SideNavigation, b as Sidebar, J as StatusBar, ae as SwipeNavigation, y as Tabs, ee as ThemePreferences, n as applyNavVisibility, r as applyPermissionFilter, i as applySurfaceVisibility, se as findNavTitle, j as firstProjectOfWorkspace, a as isItemEffectivelyShown, R as registerBreadcrumbName, P as resolveSwitchTarget, F as sectionLandingRule, te as selectMobileTabs, M as switchOrganization, N as switchWorkspace, z as unregisterBreadcrumbName, I as useContextSwitchController, ne as useMobileTabBarVisible, ce as useNativeTopBarTitle };
@@ -8,18 +8,18 @@ import { installNativeAuthNavigationIntercept as f } from "./auth-navigation.js"
8
8
  import { createDefaultBackButtonHandlers as p, installBackButtonHandler as m } from "./back-button.js";
9
9
  import { installExternalUrlOpener as h } from "./browser.js";
10
10
  import { installNativeBridge as g } from "./bridge.js";
11
- import { installDeepLinkHandler as _ } from "./deep-link.js";
12
- import { populateDeviceInfo as v } from "./device.js";
13
- import { configureKeyboard as y } from "./keyboard.js";
14
- import { installNetworkListener as b } from "./network.js";
15
- import { notifyOtaReady as x } from "./ota.js";
16
- import { installPushNotifications as S } from "./push.js";
17
- import { installPushOptIn as C } from "./push-optin.js";
18
- import { installPwa as w } from "./pwa.js";
19
- import { armSplashSafetyTimer as T, markAppReady as E } from "./splash.js";
20
- import { installStatusBarThemeListener as D } from "./system-bars.js";
11
+ import { armDeepLinkFallback as _, disarmDeepLinkFallback as v, installDeepLinkHandler as y } from "./deep-link.js";
12
+ import { populateDeviceInfo as b } from "./device.js";
13
+ import { configureKeyboard as x } from "./keyboard.js";
14
+ import { installNetworkListener as S } from "./network.js";
15
+ import { notifyOtaReady as C } from "./ota.js";
16
+ import { installPushNotifications as w } from "./push.js";
17
+ import { installPushOptIn as T } from "./push-optin.js";
18
+ import { installPwa as E } from "./pwa.js";
19
+ import { armSplashSafetyTimer as D, markAppReady as O } from "./splash.js";
20
+ import { installStatusBarThemeListener as k } from "./system-bars.js";
21
21
  //#region src/shell-native/boot.ts
22
- function O(e, t) {
22
+ function A(e, t) {
23
23
  try {
24
24
  let n = t();
25
25
  n instanceof Promise && n.catch((t) => {
@@ -29,38 +29,40 @@ function O(e, t) {
29
29
  console.debug(`[shell-native] boot step "${e}" failed:`, t);
30
30
  }
31
31
  }
32
- function k() {
32
+ function j() {
33
33
  if (typeof document > "u") return;
34
34
  let e = document.querySelector("meta[name=\"viewport\"]");
35
35
  if (!e) return;
36
36
  let t = (e.content || "").split(",").map((e) => e.trim()).filter((e) => e.length > 0 && !e.startsWith("maximum-scale") && !e.startsWith("user-scalable") && !e.startsWith("interactive-widget"));
37
37
  t.push("maximum-scale=1", "user-scalable=no"), o() === "android" && t.push("interactive-widget=resizes-content"), e.content = t.join(", ");
38
38
  }
39
- var A = null;
40
- function j(j) {
41
- if (A) return A;
42
- if (d(j.electronGlobalKey ?? `${j.product}Electron`), j.preview?.allow !== !1 && O("preview", () => n(i())), O("notifyOtaReady", () => x(j.plugins ?? {})), O("authIntercept", () => f(j.authIntercept)), O("externalUrlOpener", () => h()), O("nativeBridge", () => {
39
+ var M = null;
40
+ function N(v) {
41
+ if (M) return M;
42
+ d(v.electronGlobalKey ?? `${v.product}Electron`), v.preview?.allow !== !1 && A("preview", () => n(i())), A("notifyOtaReady", () => C(v.plugins ?? {})), A("authIntercept", () => f(v.authIntercept)), A("externalUrlOpener", () => h()), A("nativeBridge", () => {
43
43
  g(), a();
44
- }), O("deviceInfo", () => v()), O("statusBarTheme", () => D()), O("keyboard", () => y()), O("backButton", () => m({
45
- ...p(j.appName),
46
- ...j.backButton
47
- })), O("deepLinks", () => _()), O("network", () => b(j.network ?? {})), O("runtimeHooks", () => u(j.runtimeHooks ?? {})), j.push?.enabled && j.plugins?.pushNotifications) {
48
- let e = j.plugins.pushNotifications, t = j.plugins.localNotifications;
49
- O("push", () => S(e, j.push ?? {}, t));
44
+ }), A("deviceInfo", () => b()), A("statusBarTheme", () => k()), A("keyboard", () => x()), A("backButton", () => m({
45
+ ...p(v.appName),
46
+ ...v.backButton
47
+ })), A("deepLinks", () => y());
48
+ let N = v.deepLinks?.fallbackDelayMs;
49
+ if (N !== !1 && A("deepLinkFallback", () => _(N ?? 4e3)), A("network", () => S(v.network ?? {})), A("runtimeHooks", () => u(v.runtimeHooks ?? {})), v.push?.enabled && v.plugins?.pushNotifications) {
50
+ let e = v.plugins.pushNotifications, t = v.plugins.localNotifications;
51
+ A("push", () => w(e, v.push ?? {}, t));
50
52
  }
51
- return l() && j.splash?.autoHide !== !1 && O("splashSafetyTimer", () => T(j.splash?.maxWaitMs ?? 8e3)), O("pwa", () => w({
53
+ return l() && v.splash?.autoHide !== !1 && A("splashSafetyTimer", () => D(v.splash?.maxWaitMs ?? 8e3)), A("pwa", () => E({
52
54
  enabled: !0,
53
- ...j.pwa
54
- })), O("pushOptIn", () => C()), t() && O("desktopShell", () => e(j.desktop)), s() && O("viewport", () => k()), A = {
55
+ ...v.pwa
56
+ })), A("pushOptIn", () => T()), t() && A("desktopShell", () => e(v.desktop)), s() && A("viewport", () => j()), M = {
55
57
  platform: o(),
56
58
  isNative: s(),
57
59
  isPreview: c(),
58
60
  previewMode: r(),
59
- markAppReady: E
60
- }, A;
61
+ markAppReady: O
62
+ }, M;
61
63
  }
62
- function M() {
63
- A = null;
64
+ function P() {
65
+ M = null, v();
64
66
  }
65
67
  //#endregion
66
- export { j as bootNativeShell, M as resetNativeShellBootForTests };
68
+ export { N as bootNativeShell, P as resetNativeShellBootForTests };
@@ -0,0 +1,44 @@
1
+ import { ActiveContextUrlParams } from '../shared/providers/shell/ActiveContextStorage';
2
+ import { DeepLinkNavigator } from './deep-link';
3
+ /** The `setContext` payload of `ActiveContextProvider` (`useActiveContext().setContext`). */
4
+ export type DeepLinkContextUpdate = Partial<{
5
+ organizationId: string | null;
6
+ workspaceId: string | null;
7
+ projectId: string | null;
8
+ tenantId: string | null;
9
+ }>;
10
+ /**
11
+ * Ids a deep link may set as active context: a UUID or a cuid. Anything else is
12
+ * ignored so a crafted link cannot plant junk in the stored context.
13
+ */
14
+ export declare const DEEP_LINK_CONTEXT_ID_PATTERN: RegExp;
15
+ export interface DeepLinkContextOptions {
16
+ /** Query param names; defaults to `?org=&workspace=&project=&tenant=`. */
17
+ urlParams?: ActiveContextUrlParams;
18
+ }
19
+ /**
20
+ * Read the active-context ids a deep-link path carries. `path` is
21
+ * `pathname + search + hash`; the `#fragment` is ignored. Returns `null` when
22
+ * the link names no (valid) context.
23
+ *
24
+ * A link that names an organization or workspace but no project clears the
25
+ * project, so a project from the previous workspace cannot drive
26
+ * workspace-token issuance.
27
+ */
28
+ export declare function parseDeepLinkContext(path: string, options?: DeepLinkContextOptions): DeepLinkContextUpdate | null;
29
+ export interface DeepLinkNavigatorOptions extends DeepLinkContextOptions {
30
+ /**
31
+ * Commit the link's context before navigating — pass
32
+ * `useActiveContext().setContext`. A deep link arrives through `navigate()`
33
+ * (no reload, no `popstate`), so `ActiveContextProvider` would otherwise never
34
+ * see its `?org/workspace/project/tenant`.
35
+ */
36
+ setContext?: (context: DeepLinkContextUpdate) => void;
37
+ }
38
+ /**
39
+ * Build the navigator `registerDeepLinkNavigator` expects: it drops paths that
40
+ * are not same-origin in-app paths, commits the link's context once, then
41
+ * navigates. This is action-driven (it only runs for an actual deep link or
42
+ * push tap), so it cannot set up a URL ↔ context write-back loop.
43
+ */
44
+ export declare function createDeepLinkNavigator(navigate: (path: string) => void, options?: DeepLinkNavigatorOptions): DeepLinkNavigator;
@@ -0,0 +1,31 @@
1
+ import { DEFAULT_ACTIVE_CONTEXT_URL_PARAMS as e } from "../shared/providers/shell/ActiveContextStorage.js";
2
+ import { isSafeInAppPath as t } from "./deep-link.js";
3
+ //#region src/shell-native/deep-link-context.ts
4
+ var n = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|c[a-z0-9]{8,32})$/i;
5
+ function r(t, r = {}) {
6
+ let i = t.indexOf("?");
7
+ if (i === -1) return null;
8
+ let a = t.indexOf("#", i), o = a === -1 ? t.slice(i) : t.slice(i, a), s = new URLSearchParams(o), c = {
9
+ ...e,
10
+ ...r.urlParams
11
+ }, l = (e) => {
12
+ let t = s.get(e);
13
+ return t && n.test(t) ? t : null;
14
+ }, u = l(c.organization), d = l(c.workspace), f = l(c.project), p = l(c.tenant), m = {};
15
+ return u && (m.organizationId = u), d && (m.workspaceId = d), f ? m.projectId = f : (u || d) && (m.projectId = null), p && (m.tenantId = p), Object.keys(m).length > 0 ? m : null;
16
+ }
17
+ function i(e, n = {}) {
18
+ return (i) => {
19
+ if (!t(i)) {
20
+ console.debug("[shell-native] ignored a deep link that is not an in-app path");
21
+ return;
22
+ }
23
+ if (n.setContext) {
24
+ let e = r(i, n);
25
+ e && n.setContext(e);
26
+ }
27
+ e(i);
28
+ };
29
+ }
30
+ //#endregion
31
+ export { n as DEEP_LINK_CONTEXT_ID_PATTERN, i as createDeepLinkNavigator, r as parseDeepLinkContext };
@@ -1,4 +1,16 @@
1
- type DeepLinkNavigator = (path: string) => void;
1
+ export type DeepLinkNavigator = (path: string) => void;
2
+ /**
3
+ * How long `bootNativeShell` waits for a router to call
4
+ * `registerDeepLinkNavigator` before it drains queued deep links itself
5
+ * through `history.pushState` + `popstate` (see `armDeepLinkFallback`).
6
+ */
7
+ export declare const DEFAULT_DEEP_LINK_FALLBACK_DELAY_MS = 4000;
8
+ /**
9
+ * True for a same-origin, in-app path: `/x`, `/x?y#z`. Refuses protocol-relative
10
+ * (`//host`, `/\host`) and anything that is not a rooted path, so a deep link or
11
+ * push payload can never steer the webview to another origin.
12
+ */
13
+ export declare function isSafeInAppPath(path: string): boolean;
2
14
  /**
3
15
  * Map a deep-link URL to an in-app SPA path. Handles both
4
16
  * `scheme://app/path?x` (custom scheme; host is the synthetic `app`) and
@@ -9,10 +21,25 @@ export declare function deepLinkUrlToPath(rawUrl: string): string | null;
9
21
  export declare function installDeepLinkHandler(): Promise<void>;
10
22
  /**
11
23
  * Register the router's navigate function. Links that arrived before the router
12
- * mounted are queued and flushed here.
24
+ * mounted are queued and flushed here. Returns a disposer that unregisters this
25
+ * navigator only if it is still the active one (a newer registration wins).
26
+ *
27
+ * Prefer `useDeepLinkNavigator` in React shells — it also hydrates the active
28
+ * context from the link's `?org/workspace/project/tenant`.
13
29
  */
14
- export declare function registerDeepLinkNavigator(navigate: DeepLinkNavigator): void;
30
+ export declare function registerDeepLinkNavigator(navigate: DeepLinkNavigator): () => void;
15
31
  export declare function clearDeepLinkNavigator(): void;
16
32
  /** Route a path through the active navigator (or queue it) — used by push taps. */
17
33
  export declare function navigateDeepLinkPath(path: string): void;
18
- export {};
34
+ /**
35
+ * Safety net for shells whose router never registers a navigator: once
36
+ * `delayMs` has passed without a registration, queued links (and every later
37
+ * link while no navigator is registered) navigate through `history.pushState`
38
+ * + `popstate`. A navigator registered later always takes precedence. Calling
39
+ * it again re-arms with the new delay; `bootNativeShell` arms it.
40
+ */
41
+ export declare function armDeepLinkFallback(delayMs?: number): void;
42
+ /** Cancel the fallback timer and leave router-less navigation off. */
43
+ export declare function disarmDeepLinkFallback(): void;
44
+ /** Test-only: drop the queue, the navigator and the fallback. */
45
+ export declare function resetDeepLinksForTests(): void;
@@ -3,14 +3,35 @@ import { isNativePluginAvailable as t } from "./platform.js";
3
3
  import { getLegacyElectronBridge as n } from "./desktop-bridge.js";
4
4
  import { App as r } from "@capacitor/app";
5
5
  //#region src/shell-native/deep-link.ts
6
- var i = [], a = null;
7
- function o() {
8
- if (!a || i.length === 0) return;
9
- let e = i;
10
- i = [];
11
- for (let t of e) a(t);
12
- }
13
- function s(e) {
6
+ var i = 4e3, a = [], o = null, s = null, c = !1;
7
+ function l(e) {
8
+ if (typeof e != "string" || !e.startsWith("/") || e.startsWith("//") || e.startsWith("/\\")) return !1;
9
+ for (let t = 0; t < e.length; t += 1) {
10
+ let n = e.charCodeAt(t);
11
+ if (n < 32 || n === 127) return !1;
12
+ }
13
+ return !0;
14
+ }
15
+ function u(e) {
16
+ if (l(e) && !(typeof window > "u")) try {
17
+ window.history.pushState(null, "", e);
18
+ let t = typeof PopStateEvent == "function" ? new PopStateEvent("popstate", { state: null }) : new Event("popstate");
19
+ window.dispatchEvent(t);
20
+ } catch (e) {
21
+ console.debug("[shell-native] deep-link fallback navigation failed:", e);
22
+ }
23
+ }
24
+ function d() {
25
+ return o || (c ? u : null);
26
+ }
27
+ function f() {
28
+ let e = d();
29
+ if (!e || a.length === 0) return;
30
+ let t = a;
31
+ a = [];
32
+ for (let n of t) e(n);
33
+ }
34
+ function p(e) {
14
35
  let t;
15
36
  try {
16
37
  t = new URL(e);
@@ -19,35 +40,49 @@ function s(e) {
19
40
  }
20
41
  return `${t.pathname || "/"}${t.search}${t.hash}`;
21
42
  }
22
- function c(e) {
23
- let t = s(e);
24
- t !== null && (a ? a(t) : i.push(t));
43
+ function m(e) {
44
+ let t = p(e);
45
+ t !== null && b(t);
25
46
  }
26
- async function l() {
47
+ async function h() {
27
48
  if (t("App")) try {
28
- await r.addListener("appUrlOpen", (e) => c(e.url));
49
+ await r.addListener("appUrlOpen", (e) => m(e.url));
29
50
  } catch {}
30
51
  }
31
- function u() {
52
+ function g() {
32
53
  let t = e();
33
54
  if (t && typeof t.onDeepLink == "function") {
34
- t.onDeepLink((e) => c(e));
55
+ t.onDeepLink((e) => m(e));
35
56
  return;
36
57
  }
37
58
  let r = n();
38
- r && typeof r.onDeepLink == "function" && r.onDeepLink((e) => c(e));
59
+ r && typeof r.onDeepLink == "function" && r.onDeepLink((e) => m(e));
39
60
  }
40
- async function d() {
41
- u(), await l();
61
+ async function _() {
62
+ g(), await h();
42
63
  }
43
- function f(e) {
44
- a = e, o();
64
+ function v(e) {
65
+ return o = e, f(), () => {
66
+ o === e && (o = null);
67
+ };
45
68
  }
46
- function p() {
47
- a = null;
69
+ function y() {
70
+ o = null;
48
71
  }
49
- function m(e) {
50
- a ? a(e) : i.push(e);
72
+ function b(e) {
73
+ let t = d();
74
+ t ? t(e) : a.push(e);
75
+ }
76
+ function x(e = i) {
77
+ S(), s = setTimeout(() => {
78
+ s = null, c = !0, !o && a.length > 0 && console.debug("[shell-native] no deep-link navigator registered; routing queued links via history"), f();
79
+ }, Number.isFinite(e) && e > 0 ? e : 0);
80
+ }
81
+ function S() {
82
+ s !== null && clearTimeout(s), s = null, c = !1;
83
+ }
84
+ function C() {
85
+ S(), a = [], o = null;
51
86
  }
52
87
  //#endregion
53
- export { p as clearDeepLinkNavigator, s as deepLinkUrlToPath, d as installDeepLinkHandler, m as navigateDeepLinkPath, f as registerDeepLinkNavigator };
88
+ export { i as DEFAULT_DEEP_LINK_FALLBACK_DELAY_MS, x as armDeepLinkFallback, y as clearDeepLinkNavigator, p as deepLinkUrlToPath, S as disarmDeepLinkFallback, _ as installDeepLinkHandler, l as isSafeInAppPath, b as navigateDeepLinkPath, v as registerDeepLinkNavigator, C as resetDeepLinksForTests };
@@ -27,7 +27,9 @@
27
27
  */
28
28
  export { bootNativeShell, resetNativeShellBootForTests } from './boot';
29
29
  export { APP_READY_EVENT, armSplashSafetyTimer, isAppReady, markAppReady, resetAppReadyForTests, } from './splash';
30
- export { clearDeepLinkNavigator, deepLinkUrlToPath, installDeepLinkHandler, navigateDeepLinkPath, registerDeepLinkNavigator, } from './deep-link';
30
+ export { armDeepLinkFallback, clearDeepLinkNavigator, DEFAULT_DEEP_LINK_FALLBACK_DELAY_MS, deepLinkUrlToPath, disarmDeepLinkFallback, installDeepLinkHandler, isSafeInAppPath, navigateDeepLinkPath, registerDeepLinkNavigator, resetDeepLinksForTests, type DeepLinkNavigator, } from './deep-link';
31
+ export { createDeepLinkNavigator, DEEP_LINK_CONTEXT_ID_PATTERN, parseDeepLinkContext, type DeepLinkContextOptions, type DeepLinkContextUpdate, type DeepLinkNavigatorOptions, } from './deep-link-context';
32
+ export { useDeepLinkNavigator } from './useDeepLinkNavigator';
31
33
  export { installNativeBridge } from './bridge';
32
34
  export { applyNativePlatformClass, getNativePlatform, isNativePlatform, isNativePluginAvailable, isNativePreview, isRealNativePlatform, } from './platform';
33
35
  export { applyNativePreview, getActiveNativePreview, resolveNativePreview, NATIVE_PREVIEW_QUERY_PARAM, NATIVE_PREVIEW_STORAGE_KEY, } from './preview';
@@ -318,6 +318,15 @@ export interface BootNativeShellOptions {
318
318
  desktop?: {
319
319
  navigate?: (path: string) => void;
320
320
  };
321
+ /**
322
+ * Deep-link queue. Links queue until the router registers a navigator
323
+ * (`useDeepLinkNavigator`). If none registers within `fallbackDelayMs`
324
+ * (default 4000), queued and later links navigate through
325
+ * `history.pushState` + `popstate` instead. `false` disables the fallback.
326
+ */
327
+ deepLinks?: {
328
+ fallbackDelayMs?: number | false;
329
+ };
321
330
  }
322
331
  export interface NativeShellHandle {
323
332
  platform: NativeShellPlatform;
@@ -0,0 +1,18 @@
1
+ import { DeepLinkNavigatorOptions } from './deep-link-context';
2
+ /**
3
+ * Bind the native deep-link queue (Capacitor `appUrlOpen`, the desktop
4
+ * bridge's `onDeepLink`, native and web push taps) to the shell's router.
5
+ * Mount it ONCE, inside the router and inside `ActiveContextProvider`:
6
+ *
7
+ * ```tsx
8
+ * const navigate = useNavigate();
9
+ * const { setContext } = useActiveContext();
10
+ * useDeepLinkNavigator(navigate, { setContext });
11
+ * ```
12
+ *
13
+ * Links that arrived before mount (cold-start OAuth callbacks, the push tap
14
+ * that launched the app) are flushed on mount. The latest `navigate` and
15
+ * options are always used, so callers need not memoise them. On unmount the
16
+ * navigator is unregistered unless a newer registration replaced it.
17
+ */
18
+ export declare function useDeepLinkNavigator(navigate: (path: string) => void, options?: DeepLinkNavigatorOptions): void;
@@ -0,0 +1,19 @@
1
+ import { registerDeepLinkNavigator as e } from "./deep-link.js";
2
+ import { createDeepLinkNavigator as t } from "./deep-link-context.js";
3
+ import { useEffect as n, useRef as r } from "react";
4
+ //#region src/shell-native/useDeepLinkNavigator.ts
5
+ function i(i, a = {}) {
6
+ let o = r(i), s = r(a);
7
+ n(() => {
8
+ o.current = i, s.current = a;
9
+ }), n(() => e(t((e) => o.current(e), {
10
+ get urlParams() {
11
+ return s.current.urlParams;
12
+ },
13
+ get setContext() {
14
+ return s.current.setContext;
15
+ }
16
+ })), []);
17
+ }
18
+ //#endregion
19
+ export { i as useDeepLinkNavigator };
@@ -13,19 +13,21 @@ import { impactFeedback as D, notificationFeedback as O, selectionFeedback as k
13
13
  import { showNativeToast as A } from "./shell-native/toast.js";
14
14
  import { shareContent as j } from "./shell-native/share.js";
15
15
  import { installNativeBridge as M } from "./shell-native/bridge.js";
16
- import { clearDeepLinkNavigator as N, deepLinkUrlToPath as P, installDeepLinkHandler as F, navigateDeepLinkPath as I, registerDeepLinkNavigator as L } from "./shell-native/deep-link.js";
17
- import { populateDeviceInfo as R } from "./shell-native/device.js";
18
- import { KEYBOARD_HEIGHT_CSS_VAR as z, KEYBOARD_OPEN_CLASS as B, configureKeyboard as V, keyboardHandlers as H } from "./shell-native/keyboard.js";
19
- import { NETWORK_STATUS_EVENT as U, getLastNetworkStatus as W, installNetworkListener as G } from "./shell-native/network.js";
20
- import { notifyOtaReady as K } from "./shell-native/ota.js";
21
- import { PUSH_TOKEN_EVENT as q, emitPushToken as J, installPushNotifications as Y, requestPushPermission as X } from "./shell-native/push.js";
22
- import { SW_NOTIFICATION_CLICK_MESSAGE as Z, SW_PUSH_SUBSCRIPTION_CHANGE_MESSAGE as Q, WEB_PUSH_MANIFEST_KEY as $, getWebPushManifestConfig as ee, getWebPushSubscription as te, installWebPushMessageBridge as ne, isWebPushSupported as re, requestWebPushPermission as ie, resetWebPushForTests as ae, resolveVapidPublicKey as oe, serializeWebPushSubscription as se, subscribeWebPush as ce, unsubscribeWebPush as le, urlBase64ToUint8Array as ue } from "./shell-native/web-push.js";
23
- import { installPushOptIn as de, resetPushOptInForTests as fe } from "./shell-native/push-optin.js";
24
- import { PWA_INSTALLED_EVENT as pe, PWA_INSTALL_AVAILABLE_EVENT as me, PWA_UPDATE_READY_EVENT as he, installPwa as ge, installPwaPrompt as _e, isPwaEligibleHost as ve, resetPwaForTests as ye } from "./shell-native/pwa.js";
25
- import { APP_READY_EVENT as be, armSplashSafetyTimer as xe, isAppReady as Se, markAppReady as Ce, resetAppReadyForTests as we } from "./shell-native/splash.js";
26
- import { DARK_FAMILY_THEMES as Te, applyStatusBarTheme as Ee, installStatusBarThemeListener as De, isDarkThemeName as Oe, resolveDocumentIsDark as ke, statusBarDetailIsDark as Ae } from "./shell-native/system-bars.js";
27
- import { bootNativeShell as je, resetNativeShellBootForTests as Me } from "./shell-native/boot.js";
28
- import { defineCapacitorConfig as Ne, isValidAllowNavigationRule as Pe } from "./shell-native/capacitor-config.js";
29
- import { defineWebManifest as Fe } from "./shell-native/web-manifest.js";
30
- import { SERVICE_WORKER_TEMPLATE as Ie, renderServiceWorker as Le } from "./shell-native/service-worker.js";
31
- export { be as APP_READY_EVENT, Te as DARK_FAMILY_THEMES, _ as DEFAULT_AUTH_INTERCEPT_PREFIXES, z as KEYBOARD_HEIGHT_CSS_VAR, B as KEYBOARD_OPEN_CLASS, t as NATIVE_PREVIEW_QUERY_PARAM, n as NATIVE_PREVIEW_STORAGE_KEY, U as NETWORK_STATUS_EVENT, q as PUSH_TOKEN_EVENT, pe as PWA_INSTALLED_EVENT, me as PWA_INSTALL_AVAILABLE_EVENT, he as PWA_UPDATE_READY_EVENT, Ie as SERVICE_WORKER_TEMPLATE, Z as SW_NOTIFICATION_CLICK_MESSAGE, Q as SW_PUSH_SUBSCRIPTION_CHANGE_MESSAGE, $ as WEB_PUSH_MANIFEST_KEY, o as applyNativePlatformClass, r as applyNativePreview, Ee as applyStatusBarTheme, xe as armSplashSafetyTimer, je as bootNativeShell, N as clearDeepLinkNavigator, p as configureElectronGlobalKey, V as configureKeyboard, b as createDefaultBackButtonHandlers, P as deepLinkUrlToPath, Ne as defineCapacitorConfig, Fe as defineWebManifest, J as emitPushToken, i as getActiveNativePreview, e as getDesktopBridge, m as getElectronGlobalKey, W as getLastNetworkStatus, h as getLegacyElectronBridge, s as getNativePlatform, ee as getWebPushManifestConfig, te as getWebPushSubscription, D as impactFeedback, x as installBackButtonHandler, F as installDeepLinkHandler, w as installExternalUrlOpener, f as installMobileRuntimeHooks, v as installNativeAuthNavigationIntercept, M as installNativeBridge, G as installNetworkListener, Y as installPushNotifications, de as installPushOptIn, ge as installPwa, _e as installPwaPrompt, De as installStatusBarThemeListener, ne as installWebPushMessageBridge, Se as isAppReady, Oe as isDarkThemeName, g as isDesktopShell, c as isNativePlatform, l as isNativePluginAvailable, u as isNativePreview, ve as isPwaEligibleHost, d as isRealNativePlatform, Pe as isValidAllowNavigationRule, re as isWebPushSupported, H as keyboardHandlers, Ce as markAppReady, I as navigateDeepLinkPath, O as notificationFeedback, K as notifyOtaReady, T as openExternalUrl, R as populateDeviceInfo, L as registerDeepLinkNavigator, Le as renderServiceWorker, X as requestPushPermission, ie as requestWebPushPermission, we as resetAppReadyForTests, Me as resetNativeShellBootForTests, fe as resetPushOptInForTests, ye as resetPwaForTests, ae as resetWebPushForTests, ke as resolveDocumentIsDark, a as resolveNativePreview, oe as resolveVapidPublicKey, S as runBackButtonPrecedence, k as selectionFeedback, se as serializeWebPushSubscription, j as shareContent, C as showNativeActionSheet, y as showNativeConfirm, A as showNativeToast, Ae as statusBarDetailIsDark, ce as subscribeWebPush, le as unsubscribeWebPush, ue as urlBase64ToUint8Array, E as writeClipboardText };
16
+ import { DEFAULT_DEEP_LINK_FALLBACK_DELAY_MS as N, armDeepLinkFallback as P, clearDeepLinkNavigator as F, deepLinkUrlToPath as I, disarmDeepLinkFallback as L, installDeepLinkHandler as R, isSafeInAppPath as z, navigateDeepLinkPath as B, registerDeepLinkNavigator as V, resetDeepLinksForTests as H } from "./shell-native/deep-link.js";
17
+ import { populateDeviceInfo as U } from "./shell-native/device.js";
18
+ import { KEYBOARD_HEIGHT_CSS_VAR as W, KEYBOARD_OPEN_CLASS as G, configureKeyboard as K, keyboardHandlers as q } from "./shell-native/keyboard.js";
19
+ import { NETWORK_STATUS_EVENT as J, getLastNetworkStatus as Y, installNetworkListener as X } from "./shell-native/network.js";
20
+ import { notifyOtaReady as Z } from "./shell-native/ota.js";
21
+ import { PUSH_TOKEN_EVENT as Q, emitPushToken as $, installPushNotifications as ee, requestPushPermission as te } from "./shell-native/push.js";
22
+ import { SW_NOTIFICATION_CLICK_MESSAGE as ne, SW_PUSH_SUBSCRIPTION_CHANGE_MESSAGE as re, WEB_PUSH_MANIFEST_KEY as ie, getWebPushManifestConfig as ae, getWebPushSubscription as oe, installWebPushMessageBridge as se, isWebPushSupported as ce, requestWebPushPermission as le, resetWebPushForTests as ue, resolveVapidPublicKey as de, serializeWebPushSubscription as fe, subscribeWebPush as pe, unsubscribeWebPush as me, urlBase64ToUint8Array as he } from "./shell-native/web-push.js";
23
+ import { installPushOptIn as ge, resetPushOptInForTests as _e } from "./shell-native/push-optin.js";
24
+ import { PWA_INSTALLED_EVENT as ve, PWA_INSTALL_AVAILABLE_EVENT as ye, PWA_UPDATE_READY_EVENT as be, installPwa as xe, installPwaPrompt as Se, isPwaEligibleHost as Ce, resetPwaForTests as we } from "./shell-native/pwa.js";
25
+ import { APP_READY_EVENT as Te, armSplashSafetyTimer as Ee, isAppReady as De, markAppReady as Oe, resetAppReadyForTests as ke } from "./shell-native/splash.js";
26
+ import { DARK_FAMILY_THEMES as Ae, applyStatusBarTheme as je, installStatusBarThemeListener as Me, isDarkThemeName as Ne, resolveDocumentIsDark as Pe, statusBarDetailIsDark as Fe } from "./shell-native/system-bars.js";
27
+ import { bootNativeShell as Ie, resetNativeShellBootForTests as Le } from "./shell-native/boot.js";
28
+ import { DEEP_LINK_CONTEXT_ID_PATTERN as Re, createDeepLinkNavigator as ze, parseDeepLinkContext as Be } from "./shell-native/deep-link-context.js";
29
+ import { useDeepLinkNavigator as Ve } from "./shell-native/useDeepLinkNavigator.js";
30
+ import { defineCapacitorConfig as He, isValidAllowNavigationRule as Ue } from "./shell-native/capacitor-config.js";
31
+ import { defineWebManifest as We } from "./shell-native/web-manifest.js";
32
+ import { SERVICE_WORKER_TEMPLATE as Ge, renderServiceWorker as Ke } from "./shell-native/service-worker.js";
33
+ export { Te as APP_READY_EVENT, Ae as DARK_FAMILY_THEMES, Re as DEEP_LINK_CONTEXT_ID_PATTERN, _ as DEFAULT_AUTH_INTERCEPT_PREFIXES, N as DEFAULT_DEEP_LINK_FALLBACK_DELAY_MS, W as KEYBOARD_HEIGHT_CSS_VAR, G as KEYBOARD_OPEN_CLASS, t as NATIVE_PREVIEW_QUERY_PARAM, n as NATIVE_PREVIEW_STORAGE_KEY, J as NETWORK_STATUS_EVENT, Q as PUSH_TOKEN_EVENT, ve as PWA_INSTALLED_EVENT, ye as PWA_INSTALL_AVAILABLE_EVENT, be as PWA_UPDATE_READY_EVENT, Ge as SERVICE_WORKER_TEMPLATE, ne as SW_NOTIFICATION_CLICK_MESSAGE, re as SW_PUSH_SUBSCRIPTION_CHANGE_MESSAGE, ie as WEB_PUSH_MANIFEST_KEY, o as applyNativePlatformClass, r as applyNativePreview, je as applyStatusBarTheme, P as armDeepLinkFallback, Ee as armSplashSafetyTimer, Ie as bootNativeShell, F as clearDeepLinkNavigator, p as configureElectronGlobalKey, K as configureKeyboard, ze as createDeepLinkNavigator, b as createDefaultBackButtonHandlers, I as deepLinkUrlToPath, He as defineCapacitorConfig, We as defineWebManifest, L as disarmDeepLinkFallback, $ as emitPushToken, i as getActiveNativePreview, e as getDesktopBridge, m as getElectronGlobalKey, Y as getLastNetworkStatus, h as getLegacyElectronBridge, s as getNativePlatform, ae as getWebPushManifestConfig, oe as getWebPushSubscription, D as impactFeedback, x as installBackButtonHandler, R as installDeepLinkHandler, w as installExternalUrlOpener, f as installMobileRuntimeHooks, v as installNativeAuthNavigationIntercept, M as installNativeBridge, X as installNetworkListener, ee as installPushNotifications, ge as installPushOptIn, xe as installPwa, Se as installPwaPrompt, Me as installStatusBarThemeListener, se as installWebPushMessageBridge, De as isAppReady, Ne as isDarkThemeName, g as isDesktopShell, c as isNativePlatform, l as isNativePluginAvailable, u as isNativePreview, Ce as isPwaEligibleHost, d as isRealNativePlatform, z as isSafeInAppPath, Ue as isValidAllowNavigationRule, ce as isWebPushSupported, q as keyboardHandlers, Oe as markAppReady, B as navigateDeepLinkPath, O as notificationFeedback, Z as notifyOtaReady, T as openExternalUrl, Be as parseDeepLinkContext, U as populateDeviceInfo, V as registerDeepLinkNavigator, Ke as renderServiceWorker, te as requestPushPermission, le as requestWebPushPermission, ke as resetAppReadyForTests, H as resetDeepLinksForTests, Le as resetNativeShellBootForTests, _e as resetPushOptInForTests, we as resetPwaForTests, ue as resetWebPushForTests, Pe as resolveDocumentIsDark, a as resolveNativePreview, de as resolveVapidPublicKey, S as runBackButtonPrecedence, k as selectionFeedback, fe as serializeWebPushSubscription, j as shareContent, C as showNativeActionSheet, y as showNativeConfirm, A as showNativeToast, Fe as statusBarDetailIsDark, pe as subscribeWebPush, me as unsubscribeWebPush, he as urlBase64ToUint8Array, Ve as useDeepLinkNavigator, E as writeClipboardText };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/fe-libs",
3
- "version": "2026.1021.2",
3
+ "version": "2026.1021.4",
4
4
  "description": "Burdenoff frontend primitives and domain libraries",
5
5
  "type": "module",
6
6
  "bin": {