@the-portland-company/shell 0.35.4 → 0.36.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## 0.36.0 — 2026-07-24
2
+
3
+ ### Added — cross-subdomain active-org + favicon cache-busting
4
+
5
+ `ChromeStateProvider` now seeds the active org from a shared cross-subdomain
6
+ signal (`active-org` module: URL `?org=<uuid>` → `.politogyvrm.com` cookie
7
+ `politogy_active_org`) before falling back to host-only localStorage, and stamps
8
+ `?org=<full-uuid>` into the address bar on every page. Previously each app +
9
+ subdomain restored from its own host-only localStorage and otherwise defaulted
10
+ to the earliest-joined org, so apps diverged onto different orgs and campaign
11
+ apps could land on a Relationship-only org and lock themselves. `setOrgId` now
12
+ writes the shared cookie so the last-used org follows the user across every app,
13
+ and cross-app/core links carry the FULL org uuid (not the lossy last-6 suffix),
14
+ the only collision-free selector for contacts-api membership matching.
15
+
16
+ `toMetadataIcons` stamps the Brand revision onto every favicon URL as
17
+ `?v=<version>` (falling back to `updatedAt`) so an aggressively-cached browser
18
+ picks up a new Brand publish even when the stored filename is unchanged.
19
+
1
20
  ## 0.32.0 — 2026-07-23
2
21
 
3
22
  ### Added — mode-aware navigation (Campaign Mode) with zero new chrome config
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ // src/active-org.ts
4
+ var ACTIVE_ORG_COOKIE = "politogy_active_org";
5
+ var ACTIVE_ORG_QUERY_PARAM = "org";
6
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7
+ function isOrgId(v) {
8
+ return typeof v === "string" && UUID_RE.test(v.trim());
9
+ }
10
+ function readActiveOrgCookie() {
11
+ if (typeof document === "undefined") return null;
12
+ try {
13
+ const m = document.cookie.match(
14
+ new RegExp(`(?:^|;\\s*)${ACTIVE_ORG_COOKIE}=([^;]+)`)
15
+ );
16
+ const v = m ? decodeURIComponent(m[1]) : null;
17
+ return isOrgId(v) ? v : null;
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+ function writeActiveOrgCookie(orgId) {
23
+ if (typeof document === "undefined" || typeof window === "undefined") return;
24
+ if (!isOrgId(orgId)) return;
25
+ const host = window.location.hostname;
26
+ const domainAttr = /(^|\.)politogyvrm\.com$/i.test(host) ? "; Domain=.politogyvrm.com" : "";
27
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
28
+ document.cookie = `${ACTIVE_ORG_COOKIE}=${encodeURIComponent(
29
+ orgId
30
+ )}; Path=/; Max-Age=31536000; SameSite=Lax${domainAttr}${secure}`;
31
+ }
32
+ function readActiveOrgFromUrl(search) {
33
+ if (!search) return null;
34
+ try {
35
+ const qs = search.includes("?") ? search.slice(search.indexOf("?") + 1) : search;
36
+ const v = new URLSearchParams(qs).get(ACTIVE_ORG_QUERY_PARAM);
37
+ return isOrgId(v) ? v : null;
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+ function resolveActiveOrg(search) {
43
+ const fromUrl = readActiveOrgFromUrl(
44
+ search ?? (typeof window !== "undefined" ? window.location.search : null)
45
+ );
46
+ if (fromUrl) return fromUrl;
47
+ return readActiveOrgCookie();
48
+ }
49
+ function activeOrgParam(orgId) {
50
+ return isOrgId(orgId) ? `${ACTIVE_ORG_QUERY_PARAM}=${orgId}` : "";
51
+ }
52
+ function withActiveOrgParam(url, orgId) {
53
+ if (!isOrgId(orgId)) return url;
54
+ try {
55
+ const base = typeof window !== "undefined" ? window.location.origin : "https://politogyvrm.com";
56
+ const u = new URL(url, base);
57
+ u.searchParams.set(ACTIVE_ORG_QUERY_PARAM, orgId);
58
+ return /^https?:\/\//i.test(url) ? u.toString() : `${u.pathname}${u.search}${u.hash}`;
59
+ } catch {
60
+ return url;
61
+ }
62
+ }
63
+
64
+ exports.ACTIVE_ORG_COOKIE = ACTIVE_ORG_COOKIE;
65
+ exports.ACTIVE_ORG_QUERY_PARAM = ACTIVE_ORG_QUERY_PARAM;
66
+ exports.activeOrgParam = activeOrgParam;
67
+ exports.isOrgId = isOrgId;
68
+ exports.readActiveOrgCookie = readActiveOrgCookie;
69
+ exports.readActiveOrgFromUrl = readActiveOrgFromUrl;
70
+ exports.resolveActiveOrg = resolveActiveOrg;
71
+ exports.withActiveOrgParam = withActiveOrgParam;
72
+ exports.writeActiveOrgCookie = writeActiveOrgCookie;
73
+ //# sourceMappingURL=active-org.cjs.map
74
+ //# sourceMappingURL=active-org.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/active-org.ts"],"names":[],"mappings":"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","file":"active-org.cjs","sourcesContent":["// Universal active-organization selection — the ONE cross-app contract.\n//\n// A user belongs to many orgs; ONE active-org choice must follow them across\n// every Politogy app + subdomain so all apps resolve the SAME org (and campaign\n// apps don't land on a rel-only org and lock themselves). Layers cooperate,\n// mirroring the theme-preference contract:\n// 1. URL `?org=<uuid>` — the authoritative per-request selector. The shared\n// chrome stamps it onto every route so it is bookmarkable + shareable, and\n// each app forwards it to the chrome API as `x-contacts-org`.\n// 2. `.politogyvrm.com` cookie `politogy_active_org` — cross-subdomain vehicle\n// (auth session cookies are host-only) that remembers the LAST USED org so\n// a bare URL in the next app opens on the same org.\n//\n// The stored/most-authoritative value is the FULL org UUID: contacts-api\n// (auth/vrmMembership.selectRequestedMembership) matches the selector by EXACT\n// id first, then falls back to a short suffix that can COLLIDE across a user's\n// orgs — so the full UUID is the only collision-free selector.\n//\n// Server-safe (no 'use client'): constants/resolve helpers are importable from\n// server layouts; the cookie read/write helpers no-op on the server.\n\nexport const ACTIVE_ORG_COOKIE = 'politogy_active_org'\nexport const ACTIVE_ORG_QUERY_PARAM = 'org'\n\n// Loose UUID shape check — we only ever store ids that came from the user's own\n// authoritative membership list, so this just rejects obvious garbage.\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nexport function isOrgId(v: unknown): v is string {\n return typeof v === 'string' && UUID_RE.test(v.trim())\n}\n\n/** Read the shared active-org cookie (browser only; null on server or unset). */\nexport function readActiveOrgCookie(): string | null {\n if (typeof document === 'undefined') return null\n try {\n const m = document.cookie.match(\n new RegExp(`(?:^|;\\\\s*)${ACTIVE_ORG_COOKIE}=([^;]+)`),\n )\n const v = m ? decodeURIComponent(m[1]) : null\n return isOrgId(v) ? v : null\n } catch {\n return null\n }\n}\n\n// Write the cross-subdomain cookie with the SAME scoping rules as the theme\n// cookie: on a *.politogyvrm.com host scope to the parent domain so every app\n// subdomain reads it; on any other host (localhost, *.pages.dev preview) fall\n// back to a host-only cookie. 1-year, SameSite=Lax, Secure on https.\nexport function writeActiveOrgCookie(orgId: string): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return\n if (!isOrgId(orgId)) return\n const host = window.location.hostname\n const domainAttr = /(^|\\.)politogyvrm\\.com$/i.test(host) ? '; Domain=.politogyvrm.com' : ''\n const secure = window.location.protocol === 'https:' ? '; Secure' : ''\n document.cookie = `${ACTIVE_ORG_COOKIE}=${encodeURIComponent(\n orgId,\n )}; Path=/; Max-Age=31536000; SameSite=Lax${domainAttr}${secure}`\n}\n\n/**\n * Read the `?org=` selector from a URL search string (full UUID only). Accepts a\n * `location.search`-style string or a full URL.\n */\nexport function readActiveOrgFromUrl(search: string | null | undefined): string | null {\n if (!search) return null\n try {\n const qs = search.includes('?') ? search.slice(search.indexOf('?') + 1) : search\n const v = new URLSearchParams(qs).get(ACTIVE_ORG_QUERY_PARAM)\n return isOrgId(v) ? v : null\n } catch {\n return null\n }\n}\n\n/**\n * Resolve the active org with the cross-app precedence:\n * 1. `?org=<uuid>` in the current URL (explicit per-request selector)\n * 2. the `.politogyvrm.com` cookie (last used, cross-subdomain)\n * 3. null → let the app fall back to the chrome-API default (earliest-joined)\n * Pass `search` on the server (from the request URL); it defaults to\n * `window.location.search` in the browser.\n */\nexport function resolveActiveOrg(search?: string | null): string | null {\n const fromUrl = readActiveOrgFromUrl(\n search ?? (typeof window !== 'undefined' ? window.location.search : null),\n )\n if (fromUrl) return fromUrl\n return readActiveOrgCookie()\n}\n\n/** The canonical `?org=` query fragment for a full org id (no leading `?`/`&`). */\nexport function activeOrgParam(orgId: string | null | undefined): string {\n return isOrgId(orgId) ? `${ACTIVE_ORG_QUERY_PARAM}=${orgId}` : ''\n}\n\n/**\n * Return `url` with `?org=<uuid>` set/replaced. Used by the shared chrome to\n * stamp the active org onto every route so it is always present in the address\n * bar. Leaves the URL unchanged when `orgId` is not a valid id.\n */\nexport function withActiveOrgParam(url: string, orgId: string | null | undefined): string {\n if (!isOrgId(orgId)) return url\n try {\n // Support both absolute and relative URLs.\n const base = typeof window !== 'undefined' ? window.location.origin : 'https://politogyvrm.com'\n const u = new URL(url, base)\n u.searchParams.set(ACTIVE_ORG_QUERY_PARAM, orgId as string)\n // Preserve relative form when the input was relative.\n return /^https?:\\/\\//i.test(url) ? u.toString() : `${u.pathname}${u.search}${u.hash}`\n } catch {\n return url\n }\n}\n"]}
@@ -0,0 +1,30 @@
1
+ declare const ACTIVE_ORG_COOKIE = "politogy_active_org";
2
+ declare const ACTIVE_ORG_QUERY_PARAM = "org";
3
+ declare function isOrgId(v: unknown): v is string;
4
+ /** Read the shared active-org cookie (browser only; null on server or unset). */
5
+ declare function readActiveOrgCookie(): string | null;
6
+ declare function writeActiveOrgCookie(orgId: string): void;
7
+ /**
8
+ * Read the `?org=` selector from a URL search string (full UUID only). Accepts a
9
+ * `location.search`-style string or a full URL.
10
+ */
11
+ declare function readActiveOrgFromUrl(search: string | null | undefined): string | null;
12
+ /**
13
+ * Resolve the active org with the cross-app precedence:
14
+ * 1. `?org=<uuid>` in the current URL (explicit per-request selector)
15
+ * 2. the `.politogyvrm.com` cookie (last used, cross-subdomain)
16
+ * 3. null → let the app fall back to the chrome-API default (earliest-joined)
17
+ * Pass `search` on the server (from the request URL); it defaults to
18
+ * `window.location.search` in the browser.
19
+ */
20
+ declare function resolveActiveOrg(search?: string | null): string | null;
21
+ /** The canonical `?org=` query fragment for a full org id (no leading `?`/`&`). */
22
+ declare function activeOrgParam(orgId: string | null | undefined): string;
23
+ /**
24
+ * Return `url` with `?org=<uuid>` set/replaced. Used by the shared chrome to
25
+ * stamp the active org onto every route so it is always present in the address
26
+ * bar. Leaves the URL unchanged when `orgId` is not a valid id.
27
+ */
28
+ declare function withActiveOrgParam(url: string, orgId: string | null | undefined): string;
29
+
30
+ export { ACTIVE_ORG_COOKIE, ACTIVE_ORG_QUERY_PARAM, activeOrgParam, isOrgId, readActiveOrgCookie, readActiveOrgFromUrl, resolveActiveOrg, withActiveOrgParam, writeActiveOrgCookie };
@@ -0,0 +1,30 @@
1
+ declare const ACTIVE_ORG_COOKIE = "politogy_active_org";
2
+ declare const ACTIVE_ORG_QUERY_PARAM = "org";
3
+ declare function isOrgId(v: unknown): v is string;
4
+ /** Read the shared active-org cookie (browser only; null on server or unset). */
5
+ declare function readActiveOrgCookie(): string | null;
6
+ declare function writeActiveOrgCookie(orgId: string): void;
7
+ /**
8
+ * Read the `?org=` selector from a URL search string (full UUID only). Accepts a
9
+ * `location.search`-style string or a full URL.
10
+ */
11
+ declare function readActiveOrgFromUrl(search: string | null | undefined): string | null;
12
+ /**
13
+ * Resolve the active org with the cross-app precedence:
14
+ * 1. `?org=<uuid>` in the current URL (explicit per-request selector)
15
+ * 2. the `.politogyvrm.com` cookie (last used, cross-subdomain)
16
+ * 3. null → let the app fall back to the chrome-API default (earliest-joined)
17
+ * Pass `search` on the server (from the request URL); it defaults to
18
+ * `window.location.search` in the browser.
19
+ */
20
+ declare function resolveActiveOrg(search?: string | null): string | null;
21
+ /** The canonical `?org=` query fragment for a full org id (no leading `?`/`&`). */
22
+ declare function activeOrgParam(orgId: string | null | undefined): string;
23
+ /**
24
+ * Return `url` with `?org=<uuid>` set/replaced. Used by the shared chrome to
25
+ * stamp the active org onto every route so it is always present in the address
26
+ * bar. Leaves the URL unchanged when `orgId` is not a valid id.
27
+ */
28
+ declare function withActiveOrgParam(url: string, orgId: string | null | undefined): string;
29
+
30
+ export { ACTIVE_ORG_COOKIE, ACTIVE_ORG_QUERY_PARAM, activeOrgParam, isOrgId, readActiveOrgCookie, readActiveOrgFromUrl, resolveActiveOrg, withActiveOrgParam, writeActiveOrgCookie };
@@ -0,0 +1,3 @@
1
+ export { ACTIVE_ORG_COOKIE, ACTIVE_ORG_QUERY_PARAM, activeOrgParam, isOrgId, readActiveOrgCookie, readActiveOrgFromUrl, resolveActiveOrg, withActiveOrgParam, writeActiveOrgCookie } from './chunk-W2HITMV5.js';
2
+ //# sourceMappingURL=active-org.js.map
3
+ //# sourceMappingURL=active-org.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"active-org.js"}
@@ -1397,7 +1397,7 @@ function standardPolitogyNav(opts) {
1397
1397
  href: abs(c.path),
1398
1398
  children: c.children ? leaves(c.children) : void 0
1399
1399
  }));
1400
- const orgParam = opts.activeOrgId ? `org=${opts.activeOrgId.slice(-6)}` : "";
1400
+ const orgParam = opts.activeOrgId ? `org=${opts.activeOrgId}` : "";
1401
1401
  const withOrg = (url) => orgParam ? `${url}${url.includes("?") ? "&" : "?"}${orgParam}` : url;
1402
1402
  const buildRelationship = () => {
1403
1403
  const contacts = opts.contactsItem ? {
@@ -2486,6 +2486,53 @@ function NavAppShell({
2486
2486
  ] });
2487
2487
  }
2488
2488
 
2489
+ // src/active-org.ts
2490
+ var ACTIVE_ORG_COOKIE = "politogy_active_org";
2491
+ var ACTIVE_ORG_QUERY_PARAM = "org";
2492
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2493
+ function isOrgId(v) {
2494
+ return typeof v === "string" && UUID_RE.test(v.trim());
2495
+ }
2496
+ function readActiveOrgCookie() {
2497
+ if (typeof document === "undefined") return null;
2498
+ try {
2499
+ const m = document.cookie.match(
2500
+ new RegExp(`(?:^|;\\s*)${ACTIVE_ORG_COOKIE}=([^;]+)`)
2501
+ );
2502
+ const v = m ? decodeURIComponent(m[1]) : null;
2503
+ return isOrgId(v) ? v : null;
2504
+ } catch {
2505
+ return null;
2506
+ }
2507
+ }
2508
+ function writeActiveOrgCookie(orgId) {
2509
+ if (typeof document === "undefined" || typeof window === "undefined") return;
2510
+ if (!isOrgId(orgId)) return;
2511
+ const host = window.location.hostname;
2512
+ const domainAttr = /(^|\.)politogyvrm\.com$/i.test(host) ? "; Domain=.politogyvrm.com" : "";
2513
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
2514
+ document.cookie = `${ACTIVE_ORG_COOKIE}=${encodeURIComponent(
2515
+ orgId
2516
+ )}; Path=/; Max-Age=31536000; SameSite=Lax${domainAttr}${secure}`;
2517
+ }
2518
+ function readActiveOrgFromUrl(search) {
2519
+ if (!search) return null;
2520
+ try {
2521
+ const qs = search.includes("?") ? search.slice(search.indexOf("?") + 1) : search;
2522
+ const v = new URLSearchParams(qs).get(ACTIVE_ORG_QUERY_PARAM);
2523
+ return isOrgId(v) ? v : null;
2524
+ } catch {
2525
+ return null;
2526
+ }
2527
+ }
2528
+ function resolveActiveOrg(search) {
2529
+ const fromUrl = readActiveOrgFromUrl(
2530
+ (typeof window !== "undefined" ? window.location.search : null)
2531
+ );
2532
+ if (fromUrl) return fromUrl;
2533
+ return readActiveOrgCookie();
2534
+ }
2535
+
2489
2536
  // src/lib/breadcrumbs.ts
2490
2537
  function deriveBreadcrumbs(items, pathname, opts) {
2491
2538
  const search = opts?.search ?? (typeof window !== "undefined" ? window.location.search : "");
@@ -2772,7 +2819,7 @@ function ChromeStateProvider({
2772
2819
  const [navDisplay, setNavDisplayState] = react.useState("both");
2773
2820
  react.useEffect(() => {
2774
2821
  try {
2775
- const storedOrg = localStorage.getItem(ORG_STORAGE_KEY);
2822
+ const storedOrg = resolveActiveOrg() ?? localStorage.getItem(ORG_STORAGE_KEY);
2776
2823
  if (storedOrg) setOrgIdState(storedOrg);
2777
2824
  const storedNav = localStorage.getItem(NAV_DISPLAY_KEY);
2778
2825
  if (storedNav === "menu" || storedNav === "tabs" || storedNav === "both") {
@@ -2791,6 +2838,7 @@ function ChromeStateProvider({
2791
2838
  localStorage.setItem(ORG_STORAGE_KEY, id);
2792
2839
  } catch {
2793
2840
  }
2841
+ writeActiveOrgCookie(id);
2794
2842
  }, []);
2795
2843
  const setNavDisplay = react.useCallback((m) => {
2796
2844
  setNavDisplayState(m);
@@ -2835,6 +2883,19 @@ function ChromeStateProvider({
2835
2883
  setNavDisplay
2836
2884
  };
2837
2885
  }, [controlledOrg, orgId, setOrgId, orgs, orgsFetching, orgsError, navDisplay, setNavDisplay]);
2886
+ const activeOrgId = value.orgId;
2887
+ react.useEffect(() => {
2888
+ if (!isOrgId(activeOrgId)) return;
2889
+ writeActiveOrgCookie(activeOrgId);
2890
+ try {
2891
+ const url = new URL(window.location.href);
2892
+ if (url.searchParams.get(ACTIVE_ORG_QUERY_PARAM) !== activeOrgId) {
2893
+ url.searchParams.set(ACTIVE_ORG_QUERY_PARAM, activeOrgId);
2894
+ window.history.replaceState(window.history.state, "", url.toString());
2895
+ }
2896
+ } catch {
2897
+ }
2898
+ }, [activeOrgId]);
2838
2899
  return /* @__PURE__ */ jsxRuntime.jsx(ChromeStateContext.Provider, { value, children });
2839
2900
  }
2840
2901
  function AppDevNotes({
@@ -2984,7 +3045,7 @@ function useChromeUserMenu({
2984
3045
  const state = useChromeState();
2985
3046
  const theme = useThemePreference();
2986
3047
  const activeOrg = state?.orgs.find((o) => o.id === state.orgId) ?? null;
2987
- const orgSuffix = activeOrg ? `?org=${activeOrg.id.slice(-6)}` : "";
3048
+ const orgSuffix = activeOrg ? `?org=${activeOrg.id}` : "";
2988
3049
  return {
2989
3050
  // My Profile is global — NOT org-suffixed (main-app ShellChromeBridge).
2990
3051
  myProfileHref: `${CORE_BASE_URL}/my-profile`,
@@ -3051,7 +3112,7 @@ function useChromeMode(arg = {}) {
3051
3112
  const pinned = pinnedModeId ? MODE_DEFS.find((m) => m.id === pinnedModeId) ?? null : null;
3052
3113
  const selected = selectedModeId ? options.find((m) => m.id === selectedModeId) ?? null : null;
3053
3114
  const current = pinned ?? selected ?? options[0] ?? MODE_DEFS.find((m) => m.id === fallbackModeId) ?? MODE_DEFS[0];
3054
- const suffix = orgId ? `?org=${orgId.slice(-6)}` : "";
3115
+ const suffix = orgId ? `?org=${orgId}` : "";
3055
3116
  return {
3056
3117
  label: current.label,
3057
3118
  icon: current.icon,