@flamingo-stack/openframe-frontend-core 0.0.217 → 0.0.218

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.
Files changed (93) hide show
  1. package/dist/{chunk-L6IBKPVM.js → chunk-EKBM4FHK.js} +2 -2
  2. package/dist/{chunk-SWZUZYWR.js → chunk-EWA2NFUR.js} +2 -2
  3. package/dist/{chunk-TYIBMDUZ.cjs → chunk-FZZBCRID.cjs} +7 -7
  4. package/dist/{chunk-TYIBMDUZ.cjs.map → chunk-FZZBCRID.cjs.map} +1 -1
  5. package/dist/{chunk-G2HHSZ3S.cjs → chunk-GE64T3JT.cjs} +9 -9
  6. package/dist/{chunk-G2HHSZ3S.cjs.map → chunk-GE64T3JT.cjs.map} +1 -1
  7. package/dist/{chunk-YWDC5BXM.cjs → chunk-L5RSJE2I.cjs} +1940 -915
  8. package/dist/chunk-L5RSJE2I.cjs.map +1 -0
  9. package/dist/{chunk-BVFRD34B.js → chunk-OHOUSDAY.js} +2 -2
  10. package/dist/{chunk-MVQ3OODK.cjs → chunk-S4SVD5JI.cjs} +9 -9
  11. package/dist/{chunk-MVQ3OODK.cjs.map → chunk-S4SVD5JI.cjs.map} +1 -1
  12. package/dist/{chunk-N5IKPYRL.js → chunk-SWIR5EB2.js} +2 -2
  13. package/dist/{chunk-6DCKL73F.cjs → chunk-TCJ5B2ZD.cjs} +24 -24
  14. package/dist/{chunk-6DCKL73F.cjs.map → chunk-TCJ5B2ZD.cjs.map} +1 -1
  15. package/dist/{chunk-ENBGG2K2.js → chunk-V5JY5RSY.js} +2954 -1929
  16. package/dist/chunk-V5JY5RSY.js.map +1 -0
  17. package/dist/components/chat/embeddable-chat.d.ts +13 -0
  18. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  19. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +104 -10
  20. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  21. package/dist/components/chat/hooks/use-slash-commands.d.ts +6 -0
  22. package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
  23. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  24. package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
  25. package/dist/components/chat/index.cjs +2 -2
  26. package/dist/components/chat/index.js +1 -1
  27. package/dist/components/chat/types/unified-chat-state.types.d.ts +81 -0
  28. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  29. package/dist/components/contact/index.cjs +3 -3
  30. package/dist/components/contact/index.js +2 -2
  31. package/dist/components/features/index.cjs +2 -2
  32. package/dist/components/features/index.js +1 -1
  33. package/dist/components/index.cjs +73 -51
  34. package/dist/components/index.cjs.map +1 -1
  35. package/dist/components/index.js +26 -4
  36. package/dist/components/index.js.map +1 -1
  37. package/dist/components/navigation/app-header.d.ts +7 -0
  38. package/dist/components/navigation/app-header.d.ts.map +1 -1
  39. package/dist/components/navigation/app-layout-drawer.d.ts +65 -0
  40. package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -0
  41. package/dist/components/navigation/app-layout.d.ts +9 -1
  42. package/dist/components/navigation/app-layout.d.ts.map +1 -1
  43. package/dist/components/navigation/header-mingo-button.d.ts +21 -0
  44. package/dist/components/navigation/header-mingo-button.d.ts.map +1 -0
  45. package/dist/components/navigation/index.cjs +24 -2
  46. package/dist/components/navigation/index.cjs.map +1 -1
  47. package/dist/components/navigation/index.d.ts +5 -1
  48. package/dist/components/navigation/index.d.ts.map +1 -1
  49. package/dist/components/navigation/index.js +23 -1
  50. package/dist/components/onboarding-guides/index.cjs +18 -18
  51. package/dist/components/onboarding-guides/index.js +3 -3
  52. package/dist/components/tickets/hooks/use-ticket-engagements.d.ts.map +1 -1
  53. package/dist/components/tickets/index.cjs +80 -66
  54. package/dist/components/tickets/index.cjs.map +1 -1
  55. package/dist/components/tickets/index.js +20 -6
  56. package/dist/components/tickets/index.js.map +1 -1
  57. package/dist/components/ui/index.cjs +2 -2
  58. package/dist/components/ui/index.js +1 -1
  59. package/dist/index.cjs +26 -2
  60. package/dist/index.cjs.map +1 -1
  61. package/dist/index.js +25 -1
  62. package/dist/utils/embed-authed-fetch.d.ts +80 -0
  63. package/dist/utils/embed-authed-fetch.d.ts.map +1 -1
  64. package/dist/utils/index.cjs +70 -5
  65. package/dist/utils/index.cjs.map +1 -1
  66. package/dist/utils/index.d.ts +1 -1
  67. package/dist/utils/index.d.ts.map +1 -1
  68. package/dist/utils/index.js +70 -6
  69. package/dist/utils/index.js.map +1 -1
  70. package/package.json +2 -2
  71. package/src/components/chat/embeddable-chat.tsx +154 -37
  72. package/src/components/chat/hooks/use-nats-chat-adapter.ts +601 -23
  73. package/src/components/chat/hooks/use-slash-commands.ts +10 -1
  74. package/src/components/chat/hooks/use-sse-chat-adapter.ts +45 -0
  75. package/src/components/chat/hooks/use-unified-chat.ts +59 -0
  76. package/src/components/chat/types/unified-chat-state.types.ts +116 -0
  77. package/src/components/navigation/app-header.tsx +23 -0
  78. package/src/components/navigation/app-layout-drawer.tsx +620 -0
  79. package/src/components/navigation/app-layout.tsx +65 -26
  80. package/src/components/navigation/header-mingo-button.tsx +58 -0
  81. package/src/components/navigation/index.ts +17 -1
  82. package/src/components/tickets/hooks/use-ticket-engagements.ts +24 -4
  83. package/src/stories/AppLayoutDrawer.stories.tsx +228 -0
  84. package/src/utils/.embed-authed-fetch.md +7 -0
  85. package/src/utils/__tests__/embed-authed-fetch.test.ts +103 -1
  86. package/src/utils/embed-authed-fetch.ts +247 -7
  87. package/src/utils/index.ts +5 -1
  88. package/dist/chunk-ENBGG2K2.js.map +0 -1
  89. package/dist/chunk-YWDC5BXM.cjs.map +0 -1
  90. /package/dist/{chunk-L6IBKPVM.js.map → chunk-EKBM4FHK.js.map} +0 -0
  91. /package/dist/{chunk-SWZUZYWR.js.map → chunk-EWA2NFUR.js.map} +0 -0
  92. /package/dist/{chunk-BVFRD34B.js.map → chunk-OHOUSDAY.js.map} +0 -0
  93. /package/dist/{chunk-N5IKPYRL.js.map → chunk-SWIR5EB2.js.map} +0 -0
package/dist/index.js CHANGED
@@ -35,6 +35,15 @@ import {
35
35
  AllowedDomainsInput,
36
36
  AppHeader,
37
37
  AppLayout,
38
+ AppLayoutDrawerBody,
39
+ AppLayoutDrawerClose,
40
+ AppLayoutDrawerContent,
41
+ AppLayoutDrawerDescription,
42
+ AppLayoutDrawerFooter,
43
+ AppLayoutDrawerHeader,
44
+ AppLayoutDrawerRoot,
45
+ AppLayoutDrawerTitle,
46
+ AppLayoutDrawerTrigger,
38
47
  ApprovalBatchMessage,
39
48
  ApprovalRequestMessage,
40
49
  ArgRow,
@@ -221,6 +230,7 @@ import {
221
230
  Header,
222
231
  HeaderButton,
223
232
  HeaderGlobalSearch,
233
+ HeaderMingoButton,
224
234
  HeaderOrganizationFilter,
225
235
  HeaderSkeleton,
226
236
  HiddenTagsPopup,
@@ -663,6 +673,7 @@ import {
663
673
  safeHref,
664
674
  sanitizeTitleForChat,
665
675
  scrollElementIntoView,
676
+ setEmbedAuthAdapter,
666
677
  setEmbedProxyAuth,
667
678
  shellLabels,
668
679
  shouldProxyImage,
@@ -679,6 +690,7 @@ import {
679
690
  transformWebinarToProgram,
680
691
  truncateString,
681
692
  urlPathLooksLikeSvg,
693
+ useAppLayoutDrawerContainer,
682
694
  useBoardCollapse,
683
695
  useChat,
684
696
  useChatAttachmentImageGallery,
@@ -713,7 +725,7 @@ import {
713
725
  validateEmailDomain,
714
726
  validateEmailDomainList,
715
727
  validatePhoneNumber
716
- } from "./chunk-ENBGG2K2.js";
728
+ } from "./chunk-V5JY5RSY.js";
717
729
  import "./chunk-LXC6P2EO.js";
718
730
  import "./chunk-EL5YVPD5.js";
719
731
  import {
@@ -1043,6 +1055,15 @@ export {
1043
1055
  AllowedDomainsInput,
1044
1056
  AppHeader,
1045
1057
  AppLayout,
1058
+ AppLayoutDrawerRoot as AppLayoutDrawer,
1059
+ AppLayoutDrawerBody,
1060
+ AppLayoutDrawerClose,
1061
+ AppLayoutDrawerContent,
1062
+ AppLayoutDrawerDescription,
1063
+ AppLayoutDrawerFooter,
1064
+ AppLayoutDrawerHeader,
1065
+ AppLayoutDrawerTitle,
1066
+ AppLayoutDrawerTrigger,
1046
1067
  ApprovalBatchMessage,
1047
1068
  ApprovalRequestMessage,
1048
1069
  ArchiveIcon,
@@ -1306,6 +1327,7 @@ export {
1306
1327
  Header,
1307
1328
  HeaderButton,
1308
1329
  HeaderGlobalSearch,
1330
+ HeaderMingoButton,
1309
1331
  HeaderOrganizationFilter,
1310
1332
  HeaderSkeleton,
1311
1333
  HiddenTagsPopup,
@@ -1899,6 +1921,7 @@ export {
1899
1921
  sanitizeTitleForChat,
1900
1922
  scrollElementIntoView,
1901
1923
  setEmbedProxyAuth as setChatProxyAuth,
1924
+ setEmbedAuthAdapter,
1902
1925
  setEmbedProxyAuth,
1903
1926
  setNestedValue,
1904
1927
  shellLabels,
@@ -1925,6 +1948,7 @@ export {
1925
1948
  urlPathLooksLikeSvg,
1926
1949
  useAccessCodeIntegration,
1927
1950
  useApiParams,
1951
+ useAppLayoutDrawerContainer,
1928
1952
  useAuthenticatedImage,
1929
1953
  useAutoLimitTags,
1930
1954
  useBatchImages,
@@ -1,3 +1,75 @@
1
+ /**
2
+ * Hosts that have their own auth model (cookie sessions, app-specific
3
+ * JWT in localStorage, OAuth access tokens, …) can register an adapter
4
+ * to override the lib's default `embedProxyAuth` flow. When set, the
5
+ * adapter's `getHeaders()` result is merged onto every `embedAuthedFetch`
6
+ * call AFTER the default proxy-auth header step (so adapter headers
7
+ * win over both caller and proxy values), and `credentials` overrides
8
+ * the default `'same-origin'` behaviour.
9
+ *
10
+ * Default (no adapter): MPH-style proxy-impersonation — bearer + act-as
11
+ * read from localStorage, `credentials: 'same-origin'`. No consumer
12
+ * needs to touch this unless they want a different auth model.
13
+ *
14
+ * Use cases:
15
+ * - openframe-frontend has its own JWT in `localStorage.of_access_token`
16
+ * and cookie-based session; register an adapter to attach the JWT
17
+ * and request `credentials: 'include'` so cookies travel cross-origin
18
+ * to the openframe gateway.
19
+ * - Future embed hosts with OAuth access tokens, signed URLs, etc.
20
+ *
21
+ * Lifetime: setter is module-level (intentionally — `embedAuthedFetch`
22
+ * is a plain utility, not a hook, so it can't read React context). Host
23
+ * runtime providers should call `setEmbedAuthAdapter(...)` on mount and
24
+ * `setEmbedAuthAdapter(null)` on unmount. Multiple hosts registering at
25
+ * once is a programming error (one chat panel per app).
26
+ */
27
+ export interface EmbedAuthAdapter {
28
+ /** Headers merged onto every embedded-fetch call. Return `{}` to add
29
+ * nothing. Called per-request so reactive token refresh sees the latest
30
+ * value from your auth store / storage. Values typed as
31
+ * `string | undefined` so the common narrowed shape
32
+ * `{ Authorization: token ? 'Bearer …' : undefined }` (or a conditional
33
+ * `token ? { Authorization: … } : {}`) assigns cleanly — `undefined`
34
+ * values are filtered before being merged into the request headers. */
35
+ getHeaders?: () => Record<string, string | undefined>;
36
+ /** `RequestInit.credentials` mode. Default when no adapter: callers'
37
+ * `init.credentials` or `'same-origin'`. Use `'include'` for cookie
38
+ * auth against a different origin (CORS + `SameSite=None` required). */
39
+ credentials?: RequestCredentials;
40
+ /**
41
+ * Optional 401 self-heal. When a request comes back `401`,
42
+ * `embedAuthedFetch` calls this once, and — if it resolves `true` —
43
+ * retries the SAME request exactly once with freshly-recomputed
44
+ * headers (so a rotated bearer from `getHeaders()` is picked up).
45
+ * Resolve `false` to surface the 401 to the caller unchanged.
46
+ *
47
+ * This is the capability the openframe `apiClient` has had all along
48
+ * (refresh-the-access-token-then-retry); registering it here gives the
49
+ * embedded chat/ticket surfaces the same self-healing auth instead of
50
+ * dying on an expired token. Concurrent 401s are de-duplicated by the
51
+ * wrapper, so this fires at most once per refresh cycle even when a
52
+ * stampede of chat requests all expire together — your implementation
53
+ * does NOT need its own in-flight guard (though a token-refresh manager
54
+ * that already dedups is harmless).
55
+ *
56
+ * Keep it idempotent and side-effect-light: on failure the wrapper just
57
+ * returns the original 401 — logout/redirect decisions belong to the
58
+ * host's own auth layer, not to this fetch wrapper.
59
+ */
60
+ refresh?: () => Promise<boolean>;
61
+ }
62
+ /**
63
+ * Register a host-owned auth adapter for `embedAuthedFetch`. Pass `null`
64
+ * to clear (typically on provider unmount).
65
+ *
66
+ * Module-level state — there is one chat panel per app, so a single
67
+ * registration is sufficient. Calling this twice with different non-null
68
+ * adapters replaces the previous one (the most recent registration wins);
69
+ * a `console.warn` flags the overwrite so duplicate-provider mounts get
70
+ * caught in dev.
71
+ */
72
+ export declare function setEmbedAuthAdapter(adapter: EmbedAuthAdapter | null): void;
1
73
  /**
2
74
  * `fetch` wrapper that attaches embed-proxy bearer headers (when
3
75
  * present in sessionStorage) and forces `credentials: 'same-origin'`
@@ -16,6 +88,14 @@
16
88
  * the current window's origin; cross-origin URLs throw before the bearer
17
89
  * leaves the page. This is a defense-in-depth guard for future call sites
18
90
  * — there is no legitimate cross-origin use of this fetch wrapper.
91
+ *
92
+ * **401 self-heal:** when a registered adapter supplies `refresh`, a `401`
93
+ * response triggers a single token refresh + retry of the same request
94
+ * (see `EmbedAuthAdapter.refresh`). This is the openframe `apiClient`'s
95
+ * refresh-then-retry behaviour, lifted into the lib so embedded surfaces
96
+ * no longer need a host-side `window.fetch` monkey-patch to survive an
97
+ * expired access token mid-chat. With no adapter (or no `refresh`), the
98
+ * 401 passes straight through unchanged.
19
99
  */
20
100
  export declare function embedAuthedFetch(url: string, init?: RequestInit): Promise<Response>;
21
101
  //# sourceMappingURL=embed-authed-fetch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"embed-authed-fetch.d.ts","sourceRoot":"","sources":["../../src/utils/embed-authed-fetch.ts"],"names":[],"mappings":"AAwBA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoCvF"}
1
+ {"version":3,"file":"embed-authed-fetch.d.ts","sourceRoot":"","sources":["../../src/utils/embed-authed-fetch.ts"],"names":[],"mappings":"AA4BA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;4EAMwE;IACxE,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACrD;;6EAEyE;IACzE,WAAW,CAAC,EAAE,kBAAkB,CAAA;IAChC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CACjC;AA0BD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAS1E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkCvF"}
@@ -2599,6 +2599,23 @@ function applyProxyAuth(url, baseHeaders = { "Content-Type": "application/json"
2599
2599
  }
2600
2600
 
2601
2601
  // src/utils/embed-authed-fetch.ts
2602
+ var ADAPTER_GLOBAL_KEY = "__embedAuthedFetchAdapter__";
2603
+ function getRegisteredAuthAdapter() {
2604
+ if (typeof globalThis === "undefined") return null;
2605
+ return globalThis[ADAPTER_GLOBAL_KEY] ?? null;
2606
+ }
2607
+ function storeRegisteredAuthAdapter(adapter2) {
2608
+ if (typeof globalThis === "undefined") return;
2609
+ globalThis[ADAPTER_GLOBAL_KEY] = adapter2;
2610
+ }
2611
+ function setEmbedAuthAdapter(adapter2) {
2612
+ if (adapter2 && getRegisteredAuthAdapter() && process.env.NODE_ENV !== "production") {
2613
+ console.warn(
2614
+ "[setEmbedAuthAdapter] overwriting a previously-registered auth adapter. Two chat-runtime providers should not coexist \u2014 verify mount order and pass `null` from the unmounting provider."
2615
+ );
2616
+ }
2617
+ storeRegisteredAuthAdapter(adapter2);
2618
+ }
2602
2619
  function embedAuthedFetch(url, init = {}) {
2603
2620
  assertSameOrigin(url);
2604
2621
  let baseHeaders;
@@ -2616,14 +2633,55 @@ function embedAuthedFetch(url, init = {}) {
2616
2633
  Object.assign(baseHeaders, init.headers);
2617
2634
  }
2618
2635
  }
2619
- const { url: authedUrl, headers } = applyProxyAuth(url, baseHeaders);
2620
- return fetch(authedUrl, {
2636
+ return fetchWithRefresh(url, init, baseHeaders, false);
2637
+ }
2638
+ var IN_FLIGHT_REFRESH_GLOBAL_KEY = "__embedAuthedFetchInFlightRefresh__";
2639
+ function getInFlightRefresh() {
2640
+ if (typeof globalThis === "undefined") return null;
2641
+ return globalThis[IN_FLIGHT_REFRESH_GLOBAL_KEY] ?? null;
2642
+ }
2643
+ function setInFlightRefresh(refresh) {
2644
+ if (typeof globalThis === "undefined") return;
2645
+ globalThis[IN_FLIGHT_REFRESH_GLOBAL_KEY] = refresh;
2646
+ }
2647
+ function dedupedRefresh() {
2648
+ const adapter2 = getRegisteredAuthAdapter();
2649
+ if (!adapter2?.refresh) return Promise.resolve(false);
2650
+ let inFlightRefresh = getInFlightRefresh();
2651
+ if (!inFlightRefresh) {
2652
+ inFlightRefresh = Promise.resolve().then(() => adapter2.refresh()).catch(() => false).finally(() => {
2653
+ setInFlightRefresh(null);
2654
+ });
2655
+ setInFlightRefresh(inFlightRefresh);
2656
+ }
2657
+ return inFlightRefresh;
2658
+ }
2659
+ async function fetchWithRefresh(url, init, baseHeaders, isRetry) {
2660
+ const { url: authedUrl, headers } = applyProxyAuth(url, { ...baseHeaders });
2661
+ const adapter2 = getRegisteredAuthAdapter();
2662
+ if (adapter2?.getHeaders) {
2663
+ for (const [k, v] of Object.entries(adapter2.getHeaders())) {
2664
+ if (v !== void 0) headers[k] = v;
2665
+ }
2666
+ }
2667
+ const credentials = adapter2?.credentials ?? init.credentials ?? "same-origin";
2668
+ const response = await fetch(authedUrl, {
2621
2669
  ...init,
2622
2670
  headers,
2623
- // Always include Supabase auth cookies. `applyProxyAuth` handles
2624
- // the bearer header layer; cookies are the session-tier carrier.
2625
- credentials: init.credentials ?? "same-origin"
2671
+ // Default `same-origin` carries Supabase cookies for the MPH proxy-
2672
+ // auth model. Hosts on different origins (openframe-frontend
2673
+ // openframe gateway) register `credentials: 'include'` via the
2674
+ // adapter to make their own cookies travel cross-origin (CORS +
2675
+ // `SameSite=None` must be configured server-side for that to work).
2676
+ credentials
2626
2677
  });
2678
+ if (response.status === 401 && !isRetry && adapter2?.refresh) {
2679
+ const refreshed = await dedupedRefresh();
2680
+ if (refreshed) {
2681
+ return fetchWithRefresh(url, init, baseHeaders, true);
2682
+ }
2683
+ }
2684
+ return response;
2627
2685
  }
2628
2686
  function assertSameOrigin(url) {
2629
2687
  if (typeof window === "undefined") return;
@@ -2641,6 +2699,12 @@ function assertSameOrigin(url) {
2641
2699
  );
2642
2700
  }
2643
2701
  if (target.origin !== pageOrigin) {
2702
+ if (process.env.NODE_ENV !== "production") {
2703
+ console.warn(
2704
+ `[embedAuthedFetch] cross-origin fetch to ${target.origin} allowed in dev (NODE_ENV !== 'production'). Production builds will reject this \u2014 wire a same-origin proxy before shipping.`
2705
+ );
2706
+ return;
2707
+ }
2644
2708
  throw new Error(
2645
2709
  `embedAuthedFetch: refusing cross-origin fetch to ${target.origin} \u2014 pass a relative /api/* path instead`
2646
2710
  );
@@ -3291,6 +3355,7 @@ exports.platformSlogans = platformSlogans;
3291
3355
  exports.readLeadingDecisionFrame = readLeadingDecisionFrame;
3292
3356
  exports.sanitizeTitleForChat = sanitizeTitleForChat;
3293
3357
  exports.scrollElementIntoView = scrollElementIntoView;
3358
+ exports.setEmbedAuthAdapter = setEmbedAuthAdapter;
3294
3359
  exports.setEmbedProxyAuth = setEmbedProxyAuth;
3295
3360
  exports.shouldProxyImage = shouldProxyImage;
3296
3361
  exports.stripChatAttachmentMarkdown = stripChatAttachmentMarkdown;