@flamingo-stack/openframe-frontend-core 0.0.577 → 0.0.578-1950.5326.1

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 (159) hide show
  1. package/dist/{chunk-JQI726T3.js → chunk-3LMYQGAJ.js} +3 -2
  2. package/dist/chunk-3LMYQGAJ.js.map +1 -0
  3. package/dist/{chunk-RUJKOU5F.js → chunk-5IAGT5LB.js} +2 -2
  4. package/dist/{chunk-TU32TSAK.js → chunk-5NG2P2KI.js} +5 -5
  5. package/dist/{chunk-GDWBJFMG.cjs → chunk-ABWA3W3M.cjs} +32 -32
  6. package/dist/{chunk-GDWBJFMG.cjs.map → chunk-ABWA3W3M.cjs.map} +1 -1
  7. package/dist/{chunk-U3SDSA5K.js → chunk-AJ67T7LI.js} +4 -4
  8. package/dist/{chunk-THDGDYJ3.js → chunk-BFJBE4PQ.js} +5 -5
  9. package/dist/{chunk-KE2TCUOH.cjs → chunk-BLPGMX2L.cjs} +190 -190
  10. package/dist/{chunk-KE2TCUOH.cjs.map → chunk-BLPGMX2L.cjs.map} +1 -1
  11. package/dist/{chunk-HSOMYCUG.cjs → chunk-E5QAU63Y.cjs} +11 -11
  12. package/dist/{chunk-HSOMYCUG.cjs.map → chunk-E5QAU63Y.cjs.map} +1 -1
  13. package/dist/{chunk-ZBJ6XSDO.js → chunk-ESXUEMPA.js} +3 -3
  14. package/dist/{chunk-CFTZAZ7R.cjs → chunk-G3AJXLKH.cjs} +28 -28
  15. package/dist/{chunk-CFTZAZ7R.cjs.map → chunk-G3AJXLKH.cjs.map} +1 -1
  16. package/dist/{chunk-XG2WNF4L.cjs → chunk-GOEBT72V.cjs} +19 -19
  17. package/dist/{chunk-XG2WNF4L.cjs.map → chunk-GOEBT72V.cjs.map} +1 -1
  18. package/dist/{chunk-LYOGSVFO.js → chunk-IBSOYTGZ.js} +3 -3
  19. package/dist/{chunk-AQ5MZVYN.js → chunk-IWK2NFVR.js} +3 -3
  20. package/dist/{chunk-2YCUEUIZ.js → chunk-JJH4H4CJ.js} +6 -6
  21. package/dist/{chunk-RSMUAAPW.js → chunk-KLG3MGRP.js} +3 -3
  22. package/dist/{chunk-RNUH5IHZ.js → chunk-KPE277X2.js} +3 -3
  23. package/dist/{chunk-XBBCUC32.cjs → chunk-KQBYQBQN.cjs} +15 -15
  24. package/dist/{chunk-XBBCUC32.cjs.map → chunk-KQBYQBQN.cjs.map} +1 -1
  25. package/dist/{chunk-IQFXDS5O.js → chunk-LEUJNBWU.js} +7 -7
  26. package/dist/{chunk-4D2BR756.js → chunk-LPFA56WY.js} +52 -31
  27. package/dist/chunk-LPFA56WY.js.map +1 -0
  28. package/dist/{chunk-75X6BRID.cjs → chunk-M7FNT5AC.cjs} +453 -432
  29. package/dist/chunk-M7FNT5AC.cjs.map +1 -0
  30. package/dist/{chunk-JCH6HGQD.js → chunk-MCWAZ5XI.js} +2 -2
  31. package/dist/{chunk-ET3Q4FIZ.cjs → chunk-NJDL7F7K.cjs} +10 -10
  32. package/dist/{chunk-ET3Q4FIZ.cjs.map → chunk-NJDL7F7K.cjs.map} +1 -1
  33. package/dist/{chunk-MIY5FYX3.cjs → chunk-NNFQLTJ3.cjs} +4 -3
  34. package/dist/chunk-NNFQLTJ3.cjs.map +1 -0
  35. package/dist/{chunk-XBSW7IF6.cjs → chunk-NPNYQSCG.cjs} +21 -21
  36. package/dist/{chunk-XBSW7IF6.cjs.map → chunk-NPNYQSCG.cjs.map} +1 -1
  37. package/dist/{chunk-NQCGDZJ3.cjs → chunk-OIJWSYKP.cjs} +3 -3
  38. package/dist/{chunk-NQCGDZJ3.cjs.map → chunk-OIJWSYKP.cjs.map} +1 -1
  39. package/dist/{chunk-WOHHZZJK.cjs → chunk-POB6GNIR.cjs} +57 -57
  40. package/dist/{chunk-WOHHZZJK.cjs.map → chunk-POB6GNIR.cjs.map} +1 -1
  41. package/dist/{chunk-NEKT67O5.js → chunk-QHGARH6P.js} +6 -6
  42. package/dist/{chunk-5DPCIIJ7.cjs → chunk-R7OJZCE6.cjs} +80 -80
  43. package/dist/{chunk-5DPCIIJ7.cjs.map → chunk-R7OJZCE6.cjs.map} +1 -1
  44. package/dist/{chunk-6FJABKPX.cjs → chunk-SETRICAM.cjs} +6 -6
  45. package/dist/{chunk-6FJABKPX.cjs.map → chunk-SETRICAM.cjs.map} +1 -1
  46. package/dist/{chunk-4KR7JWKB.js → chunk-SHJXC7A4.js} +4 -4
  47. package/dist/{chunk-NP3XN6ZX.js → chunk-SNZZ6Q6G.js} +4 -4
  48. package/dist/chunk-SNZZ6Q6G.js.map +1 -0
  49. package/dist/{chunk-APFGR2KK.cjs → chunk-UQPA2XT7.cjs} +28 -28
  50. package/dist/{chunk-APFGR2KK.cjs.map → chunk-UQPA2XT7.cjs.map} +1 -1
  51. package/dist/{chunk-GPWU3PPR.cjs → chunk-VEHZNQ77.cjs} +30 -30
  52. package/dist/{chunk-GPWU3PPR.cjs.map → chunk-VEHZNQ77.cjs.map} +1 -1
  53. package/dist/{chunk-RCLJSENK.cjs → chunk-WUZQYZXI.cjs} +6 -6
  54. package/dist/{chunk-RCLJSENK.cjs.map → chunk-WUZQYZXI.cjs.map} +1 -1
  55. package/dist/{chunk-CIUYHBAY.cjs → chunk-YOYHJQX3.cjs} +5 -5
  56. package/dist/{chunk-CIUYHBAY.cjs.map → chunk-YOYHJQX3.cjs.map} +1 -1
  57. package/dist/{chunk-FCVAVOXS.js → chunk-ZBVMPVJF.js} +3 -3
  58. package/dist/components/case-studies/index.cjs +10 -10
  59. package/dist/components/case-studies/index.js +4 -4
  60. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  61. package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
  62. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  63. package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
  64. package/dist/components/chat/index.cjs +3 -3
  65. package/dist/components/chat/index.js +2 -2
  66. package/dist/components/chat/nav-link-anchor-via-runtime.d.ts.map +1 -1
  67. package/dist/components/chat/types/unified-chat-state.types.d.ts +9 -2
  68. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  69. package/dist/components/chat/utils/chat-nav-resolution.d.ts +9 -3
  70. package/dist/components/chat/utils/chat-nav-resolution.d.ts.map +1 -1
  71. package/dist/components/chat/utils/execute-navigation.d.ts +11 -0
  72. package/dist/components/chat/utils/execute-navigation.d.ts.map +1 -1
  73. package/dist/components/chat/utils/index.d.ts +1 -1
  74. package/dist/components/chat/utils/index.d.ts.map +1 -1
  75. package/dist/components/chat/utils/nav-anchor-props.d.ts +4 -0
  76. package/dist/components/chat/utils/nav-anchor-props.d.ts.map +1 -1
  77. package/dist/components/chat/utils/resolve-fetched-card-href.d.ts +42 -3
  78. package/dist/components/chat/utils/resolve-fetched-card-href.d.ts.map +1 -1
  79. package/dist/components/contact/index.cjs +5 -5
  80. package/dist/components/contact/index.js +4 -4
  81. package/dist/components/docs/index.cjs +8 -8
  82. package/dist/components/docs/index.js +7 -7
  83. package/dist/components/embeds/index.cjs +5 -5
  84. package/dist/components/embeds/index.js +4 -4
  85. package/dist/components/faq/index.cjs +5 -5
  86. package/dist/components/faq/index.js +4 -4
  87. package/dist/components/features/index.cjs +4 -4
  88. package/dist/components/features/index.js +3 -3
  89. package/dist/components/help-center-pages/index.cjs +24 -24
  90. package/dist/components/help-center-pages/index.js +15 -15
  91. package/dist/components/index.cjs +136 -136
  92. package/dist/components/index.js +15 -15
  93. package/dist/components/meeting-scheduler/index.cjs +31 -31
  94. package/dist/components/meeting-scheduler/index.js +3 -3
  95. package/dist/components/navigation/index.cjs +7 -7
  96. package/dist/components/navigation/index.js +6 -6
  97. package/dist/components/onboarding-guides/index.cjs +7 -7
  98. package/dist/components/onboarding-guides/index.js +6 -6
  99. package/dist/components/related-content/index.cjs +3 -3
  100. package/dist/components/related-content/index.js +2 -2
  101. package/dist/components/tickets/index.cjs +8 -8
  102. package/dist/components/tickets/index.js +7 -7
  103. package/dist/components/ui/index.cjs +4 -4
  104. package/dist/components/ui/index.js +3 -3
  105. package/dist/index.cjs +8 -8
  106. package/dist/index.js +7 -7
  107. package/dist/utils/content-href.d.ts +26 -5
  108. package/dist/utils/content-href.d.ts.map +1 -1
  109. package/dist/utils/index.cjs +1 -1
  110. package/dist/utils/index.cjs.map +1 -1
  111. package/dist/utils/index.d.ts +1 -1
  112. package/dist/utils/index.d.ts.map +1 -1
  113. package/dist/utils/index.js +1 -1
  114. package/dist/utils/index.js.map +1 -1
  115. package/package.json +1 -1
  116. package/src/components/chat/embeddable-chat.tsx +9 -2
  117. package/src/components/chat/entity-cards/.dispatch.md +1 -1
  118. package/src/components/chat/entity-cards/dispatch.tsx +43 -23
  119. package/src/components/chat/hooks/.use-unified-chat.md +5 -1
  120. package/src/components/chat/hooks/use-nats-chat-adapter.ts +4 -7
  121. package/src/components/chat/hooks/use-unified-chat.ts +8 -2
  122. package/src/components/chat/nav-link-anchor-via-runtime.tsx +8 -10
  123. package/src/components/chat/types/.unified-chat-state.types.md +2 -1
  124. package/src/components/chat/types/unified-chat-state.types.ts +9 -2
  125. package/src/components/chat/utils/__tests__/execute-navigation.test.ts +190 -0
  126. package/src/components/chat/utils/__tests__/resolve-fetched-card-href.test.ts +75 -2
  127. package/src/components/chat/utils/chat-nav-resolution.ts +9 -3
  128. package/src/components/chat/utils/execute-navigation.ts +32 -2
  129. package/src/components/chat/utils/index.ts +1 -1
  130. package/src/components/chat/utils/nav-anchor-props.ts +4 -2
  131. package/src/components/chat/utils/resolve-fetched-card-href.ts +50 -2
  132. package/src/utils/.content-href.md +1 -1
  133. package/src/utils/__tests__/content-href.test.ts +3 -0
  134. package/src/utils/content-href.ts +30 -3
  135. package/src/utils/index.ts +1 -0
  136. package/dist/chunk-4D2BR756.js.map +0 -1
  137. package/dist/chunk-75X6BRID.cjs.map +0 -1
  138. package/dist/chunk-JQI726T3.js.map +0 -1
  139. package/dist/chunk-MIY5FYX3.cjs.map +0 -1
  140. package/dist/chunk-NP3XN6ZX.js.map +0 -1
  141. package/dist/components/chat/utils/nav-click-handler.d.ts +0 -24
  142. package/dist/components/chat/utils/nav-click-handler.d.ts.map +0 -1
  143. package/src/components/chat/utils/.nav-click-handler.md +0 -43
  144. package/src/components/chat/utils/nav-click-handler.ts +0 -34
  145. /package/dist/{chunk-RUJKOU5F.js.map → chunk-5IAGT5LB.js.map} +0 -0
  146. /package/dist/{chunk-TU32TSAK.js.map → chunk-5NG2P2KI.js.map} +0 -0
  147. /package/dist/{chunk-U3SDSA5K.js.map → chunk-AJ67T7LI.js.map} +0 -0
  148. /package/dist/{chunk-THDGDYJ3.js.map → chunk-BFJBE4PQ.js.map} +0 -0
  149. /package/dist/{chunk-ZBJ6XSDO.js.map → chunk-ESXUEMPA.js.map} +0 -0
  150. /package/dist/{chunk-LYOGSVFO.js.map → chunk-IBSOYTGZ.js.map} +0 -0
  151. /package/dist/{chunk-AQ5MZVYN.js.map → chunk-IWK2NFVR.js.map} +0 -0
  152. /package/dist/{chunk-2YCUEUIZ.js.map → chunk-JJH4H4CJ.js.map} +0 -0
  153. /package/dist/{chunk-RSMUAAPW.js.map → chunk-KLG3MGRP.js.map} +0 -0
  154. /package/dist/{chunk-RNUH5IHZ.js.map → chunk-KPE277X2.js.map} +0 -0
  155. /package/dist/{chunk-IQFXDS5O.js.map → chunk-LEUJNBWU.js.map} +0 -0
  156. /package/dist/{chunk-JCH6HGQD.js.map → chunk-MCWAZ5XI.js.map} +0 -0
  157. /package/dist/{chunk-NEKT67O5.js.map → chunk-QHGARH6P.js.map} +0 -0
  158. /package/dist/{chunk-4KR7JWKB.js.map → chunk-SHJXC7A4.js.map} +0 -0
  159. /package/dist/{chunk-FCVAVOXS.js.map → chunk-ZBVMPVJF.js.map} +0 -0
@@ -59,7 +59,7 @@ export {
59
59
  resolveExternalNavigation,
60
60
  type ExternalNavResolution,
61
61
  } from './chat-nav-resolution';
62
- export { handleChatNavClick, type ChatNavClickInput } from './nav-click-handler';
62
+ export { handleChatNavClick, type ChatNavClickInput } from './execute-navigation';
63
63
  // The unified navigation-execution primitive — single source of truth for
64
64
  // new-tab/same-tab + internal/external + embed/host + the runtime→router→window
65
65
  // fallback. Every surface routes through it (handleChatNavClick is now a wrapper).
@@ -33,11 +33,13 @@ import { decideNewTab as libDecideNewTab } from './decide-new-tab';
33
33
  /** An ABSOLUTE `http(s)` URL on the current page's origin. Relative hrefs return
34
34
  * false (in embed mode they're hub-relative, not in-app), as do cross-origin
35
35
  * and non-http URLs. SSR-safe: no `window` → false. */
36
- function isSameOriginAbsoluteHref(href: string): boolean {
36
+ export function isSameOriginAbsoluteHref(href: string): boolean {
37
37
  if (typeof window === 'undefined') return false;
38
38
  if (!/^https?:\/\//i.test(href)) return false;
39
39
  try {
40
- return new URL(href).origin === window.location.origin;
40
+ // Origin off `location.href`, not `location.origin`: jsdom leaves the latter
41
+ // undefined, which reads as "every URL is foreign" under test.
42
+ return new URL(href).origin === new URL(window.location.href).origin;
41
43
  } catch {
42
44
  return false;
43
45
  }
@@ -31,7 +31,7 @@
31
31
  * Pure — no React, no DOM. The caller owns `safeHref` validation.
32
32
  */
33
33
 
34
- import type { ComposeContentUrl } from '../../../utils/content-href';
34
+ import type { ComposeContentUrl, ComposedContentUrl } from '../../../utils/content-href';
35
35
  import { canonicalContentRefType } from '../../../utils/list-url';
36
36
 
37
37
  export interface FetchedCardHrefInput {
@@ -90,12 +90,60 @@ function readPlatforms(item: unknown, canonicalType: string): Array<{ name?: str
90
90
  * when the host wired no seam (the caller then keeps the card unlinked, which
91
91
  * is the pre-existing behavior).
92
92
  */
93
+ /**
94
+ * Where a fetch-mode card's destination came from. The loader needs the source,
95
+ * not just the string: only the `item` branch must leave the ref's own
96
+ * `targetPlatform` untouched.
97
+ */
98
+ export type FetchedCardHrefChoice =
99
+ | { source: 'hostOverride'; href: string; targetPlatform: string | null }
100
+ | { source: 'item'; href: string; targetPlatform?: undefined }
101
+ | { source: 'composed'; href: string; targetPlatform: string | null };
102
+
103
+ /**
104
+ * Decide a fetched card's destination between the two producers that can claim
105
+ * it. Pure, so the precedence is testable on its own — it used to live as
106
+ * inline ternaries in `ChatCardLoader`.
107
+ *
108
+ * 1. an EXPLICIT host override — the host said where this type lives in its
109
+ * own app, which beats everything;
110
+ * 2. the fetched row's own url (`registry.fallbackHref`) — minted by the
111
+ * content host for ITS surface, authoritative in the absence of (1);
112
+ * 3. the composer's synthesized href, unless the registry opted out
113
+ * (`noComposedHref`) because the synthesis would be a 404.
114
+ *
115
+ * (2) beating (3) is why `noComposedHref` exists; (1) beating (2) is what keeps
116
+ * a re-homed type (an embedder serving hub tickets under `/help-center/tickets`)
117
+ * from following the content host's path into an unrelated route of its own.
118
+ */
119
+ export function pickFetchedCardHref({
120
+ composed,
121
+ itemHref,
122
+ allowComposed,
123
+ }: {
124
+ /** Result of {@link resolveFetchedCardHref}, or null when unresolvable. */
125
+ composed: ComposedContentUrl | null;
126
+ /** `registry.fallbackHref(item)` — the fetched row's own destination. */
127
+ itemHref: string | null;
128
+ /** False for registry entries flagged `noComposedHref`. */
129
+ allowComposed: boolean;
130
+ }): FetchedCardHrefChoice | null {
131
+ if (composed?.hostOverride) {
132
+ return { source: 'hostOverride', href: composed.href, targetPlatform: composed.targetPlatform };
133
+ }
134
+ if (itemHref) return { source: 'item', href: itemHref };
135
+ if (composed && allowComposed) {
136
+ return { source: 'composed', href: composed.href, targetPlatform: composed.targetPlatform };
137
+ }
138
+ return null;
139
+ }
140
+
93
141
  export function resolveFetchedCardHref({
94
142
  contentRefType,
95
143
  id,
96
144
  item,
97
145
  composeContentUrl,
98
- }: FetchedCardHrefInput): { href: string; targetPlatform: string | null } | null {
146
+ }: FetchedCardHrefInput): ComposedContentUrl | null {
99
147
  if (!composeContentUrl || !contentRefType || !id) return null;
100
148
  const canonicalType = canonicalContentRefType(contentRefType);
101
149
  return composeContentUrl({
@@ -15,7 +15,7 @@ Unified content-href authority for all embeddable surfaces — resolves content
15
15
 
16
16
  ### Functions
17
17
 
18
- - **`makeComposeContentUrl(opts)`** — Factory that builds the embedder's `composeContentUrl`. Resolution order: explicit `overrides` → hosted in-app route → verbatim `externalUrl` → hub-origin fallback.
18
+ - **`makeComposeContentUrl(opts)`** — Factory that builds the embedder's `composeContentUrl`. Resolution order: explicit `overrides` → hosted in-app route → verbatim `externalUrl` → hub-origin fallback. Only the `overrides` branch returns `hostOverride: true`; fetch-mode chat cards rank that above the fetched row's own url (`pickFetchedCardHref`), and everything else below it.
19
19
  - **`buildDefaultHref(basePath, slug)`** — Fallback href when no `composeContentUrl` is wired (single-platform embedders).
20
20
  - **`resolveContentHref(composeContentUrl, args)`** — Ternary helper used by catalog/detail views: delegates to `composeContentUrl` when wired, otherwise calls `buildDefaultHref`.
21
21
 
@@ -188,6 +188,9 @@ describe('makeComposeContentUrl (unified single-object seam)', () => {
188
188
  expect(c({ type: 'product_release', identifier: 'v2', externalUrl: `${HUB}/releases/v2` })).toEqual({
189
189
  href: '/custom/v2',
190
190
  targetPlatform: 'openframe',
191
+ // Only this branch is flagged — fetch-mode cards rank it above the
192
+ // fetched row's own url (`pickFetchedCardHref`).
193
+ hostOverride: true,
191
194
  });
192
195
  });
193
196
 
@@ -112,10 +112,34 @@ export interface ContentHrefOptions {
112
112
  overrides?: Record<string, (identifier: string) => { href: string; targetPlatform: string | null }>;
113
113
  }
114
114
 
115
+ /** What the seam resolves a content reference to. */
116
+ export interface ComposedContentUrl {
117
+ href: string;
118
+ targetPlatform: string | null;
119
+ /**
120
+ * Set ONLY when the href came from the host's EXPLICIT per-type `overrides`
121
+ * entry — i.e. the host stated where this type lives in ITS app, rather than
122
+ * the composer falling through to a hosted-suffix or `<origin>/<type>/<id>`
123
+ * synthesis.
124
+ *
125
+ * The distinction is load-bearing for fetch-mode chat cards whose fetched row
126
+ * already carries a destination (`refHydratedEntry`'s `item.url`, minted by
127
+ * the CONTENT HOST for its OWN surface). That url must beat a synthesized
128
+ * guess, but must NOT beat a host that has explicitly re-homed the type:
129
+ * a HubSpot ticket's `item.url` is the hub's `/tickets?ticket=…`, which in an
130
+ * embedder that serves the same ticket at `/help-center/tickets` silently
131
+ * resolves against the embedder's own unrelated `/tickets` route.
132
+ *
133
+ * Optional and absent by default, so a hand-written composer (the hub's
134
+ * cross-platform one) keeps the pre-existing "row url wins" behavior.
135
+ */
136
+ hostOverride?: boolean;
137
+ }
138
+
115
139
  /** The unified `composeContentUrl` seam shape on `ChatRuntime`. ALWAYS returns
116
140
  * a tuple (never null) — the seam type is non-nullable and callers read
117
141
  * `.href` unconditionally. */
118
- export type ComposeContentUrl = (input: ComposeContentUrlInput) => { href: string; targetPlatform: string | null };
142
+ export type ComposeContentUrl = (input: ComposeContentUrlInput) => ComposedContentUrl;
119
143
 
120
144
  /**
121
145
  * Owning platform of a content row, from the hydrated junction the list APIs
@@ -162,7 +186,8 @@ function lastPathSegment(url: string): string | null {
162
186
  * Build the embedder's `composeContentUrl` for the unified seam.
163
187
  *
164
188
  * Resolution order (the merged rule used by BOTH page views and chat cards):
165
- * 1. `overrides[type]` — explicit per-type href.
189
+ * 1. `overrides[type]` — explicit per-type href, returned with
190
+ * `hostOverride: true` (the only branch that sets it).
166
191
  * 2. `hostedTypes.has(type)` → relative `/<suffix>/<slug>` (in-app, soft-nav).
167
192
  * Chat rows carry the hub URL not the slug, so the slug is recovered from
168
193
  * `externalUrl`; page views pass the slug as `identifier`.
@@ -184,7 +209,9 @@ export function makeComposeContentUrl(opts: ContentHrefOptions): ComposeContentU
184
209
  const type = canonicalContentRefType(rawType);
185
210
  const override =
186
211
  opts.overrides && Object.prototype.hasOwnProperty.call(opts.overrides, type) ? opts.overrides[type] : undefined;
187
- if (override) return override(identifier);
212
+ // Flagged as an explicit host decision — see `ComposedContentUrl.hostOverride`
213
+ // for why a fetch-mode card has to tell this apart from the branches below.
214
+ if (override) return { ...override(identifier), hostOverride: true };
188
215
 
189
216
  const seg = (Object.prototype.hasOwnProperty.call(suffixes, type) ? suffixes[type] : undefined) ?? type;
190
217
 
@@ -340,6 +340,7 @@ export {
340
340
  buildDefaultHref,
341
341
  type ContentHrefOptions,
342
342
  type ComposeContentUrl,
343
+ type ComposedContentUrl,
343
344
  } from './content-href';
344
345
 
345
346
  // Invisible bot-protection signals (honeypot + timing) — pure + server-safe so