@coras-io/embed 0.1.2 → 0.3.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.
Files changed (236) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/README.md +8 -7
  3. package/dist/components/accordion/accordion-item/accordion-item.styles.js +1 -1
  4. package/dist/components/alert/alert.styles.js +14 -14
  5. package/dist/components/attraction-card/base/attraction-card.styles.js +7 -3
  6. package/dist/components/attraction-discovery/attraction-discovery.styles.js +1 -1
  7. package/dist/components/attraction-header/attraction-header.styles.js +1 -1
  8. package/dist/components/attraction-option/attraction-option.styles.js +4 -4
  9. package/dist/components/basket/basket.styles.js +27 -26
  10. package/dist/components/basket-empty/basket-empty.styles.js +1 -0
  11. package/dist/components/basket-item/basket-item.component.js +10 -4
  12. package/dist/components/basket-item/basket-item.styles.js +9 -3
  13. package/dist/components/basket-item-ticket/basket-item-ticket.styles.js +1 -3
  14. package/dist/components/basket-mobile/basket-mobile.styles.js +5 -5
  15. package/dist/components/better-carousel/better-carousel.styles.js +2 -0
  16. package/dist/components/bottom-drawer/bottom-drawer.styles.js +6 -4
  17. package/dist/components/button/button.component.d.ts +0 -1
  18. package/dist/components/button/button.component.js +1 -8
  19. package/dist/components/button/button.constants.d.ts +1 -1
  20. package/dist/components/button/button.constants.js +1 -1
  21. package/dist/components/button/button.styles.js +66 -39
  22. package/dist/components/calendar/base/calendar.styles.js +3 -5
  23. package/dist/components/calendar/multi/multi.styles.js +7 -0
  24. package/dist/components/category-tag/category-tag.styles.js +10 -7
  25. package/dist/components/checkbox/checkbox.styles.js +2 -2
  26. package/dist/components/chip/chip.styles.js +2 -2
  27. package/dist/components/collect-fida/collect-fida.component.js +5 -3
  28. package/dist/components/collect-fida/collect-fida.styles.js +9 -16
  29. package/dist/components/crypto-auth-modal/crypto-auth-modal.component.js +6 -4
  30. package/dist/components/crypto-auth-modal/crypto-auth-modal.styles.js +2 -2
  31. package/dist/components/dialog/dialog.styles.js +10 -17
  32. package/dist/components/drawer/drawer.component.js +6 -0
  33. package/dist/components/drawer/drawer.styles.js +17 -3
  34. package/dist/components/dropdown/dropdown.component.js +7 -0
  35. package/dist/components/dropdown/dropdown.styles.js +3 -6
  36. package/dist/components/expiration-timer/expiration-timer.styles.js +3 -3
  37. package/dist/components/explore-card/explore-card.styles.js +1 -1
  38. package/dist/components/filter-chip/filter-chip.styles.js +11 -9
  39. package/dist/components/filters-drawer/filters-drawer.component.js +1 -1
  40. package/dist/components/filters-drawer/filters-drawer.styles.js +6 -5
  41. package/dist/components/image/image.styles.js +3 -3
  42. package/dist/components/image-chip/image-chip.styles.js +1 -1
  43. package/dist/components/input-field/input-field.styles.js +22 -17
  44. package/dist/components/itinerary/itinerary-item/itinerary-item.styles.js +1 -1
  45. package/dist/components/joined-fields/joined-fields.styles.js +10 -6
  46. package/dist/components/loading-spinner/loading-spinner.styles.js +1 -1
  47. package/dist/components/location-image/location-image.component.d.ts +6 -0
  48. package/dist/components/location-image/location-image.component.js +15 -2
  49. package/dist/components/location-image/location-image.styles.js +1 -1
  50. package/dist/components/locations-results/locations-results.styles.js +4 -6
  51. package/dist/components/marketing-tag/marketing-tag.styles.js +1 -1
  52. package/dist/components/message-card/message-card.styles.js +6 -6
  53. package/dist/components/option-group/option-chip/option-chip.styles.js +4 -3
  54. package/dist/components/order-confirmation/order-confirmation-item/order-confirmation-item.styles.js +9 -0
  55. package/dist/components/order-confirmation/order-confirmation.component.js +4 -3
  56. package/dist/components/order-confirmation/order-confirmation.styles.js +1 -1
  57. package/dist/components/otp/otp.styles.js +6 -6
  58. package/dist/components/overlay/overlay.styles.js +5 -1
  59. package/dist/components/pay-in/pay-in.styles.js +2 -2
  60. package/dist/components/payment-fiat-method/payment-fiat-method.styles.js +20 -4
  61. package/dist/components/payment-option/payment-option.styles.js +10 -12
  62. package/dist/components/performance-time/performance-time.styles.js +2 -2
  63. package/dist/components/phone-country-code/phone-country-code.styles.js +1 -1
  64. package/dist/components/points/points.styles.js +1 -1
  65. package/dist/components/search-button/search-button.styles.js +15 -7
  66. package/dist/components/seated-selector/seated-selector.component.d.ts +4 -1
  67. package/dist/components/seated-selector/seated-selector.component.js +90 -36
  68. package/dist/components/seated-selector/seated-selector.styles.js +89 -14
  69. package/dist/components/seats-io/seats-io.component.d.ts +2 -0
  70. package/dist/components/seats-io/seats-io.component.js +35 -2
  71. package/dist/components/seats-io/seats-io.constants.d.ts +5 -0
  72. package/dist/components/seats-io/seats-io.constants.js +12 -0
  73. package/dist/components/seats-io/seats-io.transform.d.ts +8 -2
  74. package/dist/components/seats-io/seats-io.transform.js +12 -4
  75. package/dist/components/select-list/select-list.styles.js +1 -0
  76. package/dist/components/select-list/select-option/select-option.styles.js +3 -3
  77. package/dist/components/sort-dropdown/sort-dropdown.styles.js +1 -1
  78. package/dist/components/sort-list/sort-list.styles.js +1 -1
  79. package/dist/components/sport-options/sport-option/sport-option.styles.js +2 -2
  80. package/dist/components/star-rating/star-rating.component.d.ts +1 -0
  81. package/dist/components/star-rating/star-rating.component.js +19 -23
  82. package/dist/components/star-rating/star-rating.styles.js +32 -6
  83. package/dist/components/tab/tab.styles.js +1 -1
  84. package/dist/components/tabs/tabs.styles.js +1 -1
  85. package/dist/components/tag/tag.styles.js +7 -7
  86. package/dist/components/text-section/text-section.styles.js +2 -1
  87. package/dist/components/time-tag/time-tag.styles.js +1 -2
  88. package/dist/components/tooltip/tooltip.styles.js +3 -3
  89. package/dist/components/tour-ticket-type-selector/tour-ticket-type/tour-ticket-type.styles.js +2 -2
  90. package/dist/components/tour-ticket-type-selector/tour-ticket-type-selector.component.js +1 -1
  91. package/dist/components/tour-ticket-type-selector/tour-ticket-type-selector.styles.js +1 -9
  92. package/dist/components/transfer-modal/transfer-modal.component.js +3 -3
  93. package/dist/components/transfer-modal/transfer-modal.styles.js +6 -8
  94. package/dist/containers/basket/basket.component.d.ts +1 -1
  95. package/dist/containers/basket/basket.component.js +56 -25
  96. package/dist/containers/footer/footer.styles.js +4 -0
  97. package/dist/containers/navbar/navbar.component.d.ts +5 -1
  98. package/dist/containers/navbar/navbar.component.js +38 -9
  99. package/dist/containers/navbar/navbar.styles.js +38 -12
  100. package/dist/contexts/basket/basket.provider.d.ts +1 -0
  101. package/dist/contexts/basket/basket.provider.js +31 -4
  102. package/dist/contexts/properties.context.d.ts +4 -4
  103. package/dist/core/api/middlewares/booking-session/booking-session.constants.d.ts +1 -0
  104. package/dist/core/api/middlewares/booking-session/booking-session.constants.js +1 -0
  105. package/dist/core/api/middlewares/booking-session/booking-session.js +18 -27
  106. package/dist/core/api/middlewares/booking-session/booking-session.utils.d.ts +4 -4
  107. package/dist/core/api/middlewares/booking-session/booking-session.utils.js +13 -10
  108. package/dist/core/api/queries.js +3 -2
  109. package/dist/core/color-scheme.d.ts +14 -0
  110. package/dist/core/color-scheme.js +48 -0
  111. package/dist/core/currency/constants.d.ts +1 -1
  112. package/dist/core/currency/constants.js +4 -3
  113. package/dist/core/i18n/date.js +20 -9
  114. package/dist/core/i18n/locale-codes.d.ts +2 -2
  115. package/dist/core/i18n/locale-codes.js +4 -0
  116. package/dist/core/images/constants.d.ts +0 -5
  117. package/dist/core/images/constants.js +0 -5
  118. package/dist/core/images/helpers.d.ts +8 -0
  119. package/dist/core/images/helpers.js +14 -6
  120. package/dist/core/images/srcset.js +2 -2
  121. package/dist/core/storybook/demo.d.ts +4 -0
  122. package/dist/core/storybook/demo.js +17 -0
  123. package/dist/core/types/locales.d.ts +7 -0
  124. package/dist/core/utils/datetime.js +10 -4
  125. package/dist/core/utils/locale.d.ts +8 -1
  126. package/dist/core/utils/locale.js +10 -0
  127. package/dist/helpers/helpers.d.ts +1 -1
  128. package/dist/index.d.ts +2 -3
  129. package/dist/index.js +1 -2
  130. package/dist/locales/ar-SA.d.ts +279 -0
  131. package/dist/locales/ar-SA.js +288 -0
  132. package/dist/locales/da-DK.d.ts +100 -82
  133. package/dist/locales/da-DK.js +104 -89
  134. package/dist/locales/de-DE.d.ts +100 -82
  135. package/dist/locales/de-DE.js +104 -89
  136. package/dist/locales/en-IE.d.ts +250 -232
  137. package/dist/locales/en-IE.js +253 -238
  138. package/dist/locales/en-US.d.ts +100 -82
  139. package/dist/locales/en-US.js +105 -90
  140. package/dist/locales/es-ES.d.ts +100 -82
  141. package/dist/locales/es-ES.js +104 -89
  142. package/dist/locales/fr-FR.d.ts +100 -82
  143. package/dist/locales/fr-FR.js +104 -89
  144. package/dist/locales/fr-MA.d.ts +100 -82
  145. package/dist/locales/fr-MA.js +104 -89
  146. package/dist/locales/is-IS.d.ts +100 -82
  147. package/dist/locales/is-IS.js +104 -89
  148. package/dist/locales/it-IT.d.ts +100 -82
  149. package/dist/locales/it-IT.js +104 -89
  150. package/dist/locales/ms-MY.d.ts +100 -82
  151. package/dist/locales/ms-MY.js +104 -89
  152. package/dist/locales/nb-NO.d.ts +100 -82
  153. package/dist/locales/nb-NO.js +104 -89
  154. package/dist/locales/pl-PL.d.ts +100 -82
  155. package/dist/locales/pl-PL.js +104 -89
  156. package/dist/locales/pt-PT.d.ts +100 -82
  157. package/dist/locales/pt-PT.js +104 -89
  158. package/dist/locales/sv-SE.d.ts +100 -82
  159. package/dist/locales/sv-SE.js +104 -89
  160. package/dist/locales/th-TH.d.ts +100 -82
  161. package/dist/locales/th-TH.js +104 -89
  162. package/dist/locales/tr-TR.d.ts +100 -82
  163. package/dist/locales/tr-TR.js +104 -89
  164. package/dist/locales/zh-CN.d.ts +100 -82
  165. package/dist/locales/zh-CN.js +105 -90
  166. package/dist/locales/zh-HK.d.ts +279 -0
  167. package/dist/locales/zh-HK.js +288 -0
  168. package/dist/mixins/coinbase.mixin.js +1 -1
  169. package/dist/mixins/error-modal.styles.js +2 -2
  170. package/dist/mixins/location-search/location-search.js +3 -3
  171. package/dist/mixins/location-search/location-search.styles.js +6 -5
  172. package/dist/sdk/chrome.d.ts +9 -3
  173. package/dist/sdk/chrome.js +21 -9
  174. package/dist/sdk/element-adapter.js +11 -8
  175. package/dist/sdk/mount.d.ts +3 -3
  176. package/dist/sdk/mount.js +150 -42
  177. package/dist/sdk/navigation-intent.js +3 -0
  178. package/dist/sdk/page-registry.d.ts +14 -2
  179. package/dist/sdk/page-registry.js +33 -13
  180. package/dist/sdk/theme.d.ts +1 -15
  181. package/dist/sdk/theme.js +4 -39
  182. package/dist/sdk/types.d.ts +55 -24
  183. package/dist/sdk/url.d.ts +24 -3
  184. package/dist/sdk/url.js +66 -27
  185. package/dist/sdk/validation.d.ts +0 -7
  186. package/dist/sdk/validation.js +105 -47
  187. package/dist/styles/contrast-fixtures.d.ts +0 -5
  188. package/dist/styles/contrast-fixtures.js +79 -9
  189. package/dist/styles/contrast.js +4 -3
  190. package/dist/styles/defaults/component.js +17 -1
  191. package/dist/styles/primitives.d.ts +0 -10
  192. package/dist/styles/primitives.js +2 -12
  193. package/dist/styles/tokens.d.ts +1 -1
  194. package/dist/styles/tokens.generated.d.ts +1 -1
  195. package/dist/styles/tokens.generated.js +1 -1
  196. package/dist/styles/tokens.metadata.d.ts +1 -1
  197. package/dist/styles/tokens.metadata.js +360 -90
  198. package/dist/views/details-page/details-page.component.d.ts +5 -5
  199. package/dist/views/details-page/details-page.component.js +97 -71
  200. package/dist/views/details-page/details-page.styles.js +64 -9
  201. package/dist/views/details-page/partials/theater-description/theater-description.component.js +5 -0
  202. package/dist/views/help-page/help-page.component.d.ts +1 -0
  203. package/dist/views/help-page/help-page.component.js +11 -4
  204. package/dist/views/help-page/help-page.styles.js +21 -8
  205. package/dist/views/landing-page/landing-page.component.d.ts +4 -4
  206. package/dist/views/landing-page/landing-page.component.js +65 -65
  207. package/dist/views/landing-page/landing-page.styles.js +70 -3
  208. package/dist/views/payment-page/partials/activity-details/activity-details.styles.js +10 -33
  209. package/dist/views/payment-page/partials/billing-address/billing-address.styles.js +4 -9
  210. package/dist/views/payment-page/partials/confirmation/confirmation.styles.js +6 -6
  211. package/dist/views/payment-page/partials/hotel-pickup-list/hotel-pickup-list.styles.js +4 -4
  212. package/dist/views/payment-page/partials/payment-type-selector/payment-type-selector.styles.js +1 -0
  213. package/dist/views/payment-page/partials/stripe/stripe.component.d.ts +1 -1
  214. package/dist/views/payment-page/partials/stripe/stripe.component.js +11 -12
  215. package/dist/views/payment-page/partials/stripe/stripe.constants.d.ts +2 -15
  216. package/dist/views/payment-page/partials/stripe/stripe.constants.js +4 -17
  217. package/dist/views/payment-page/partials/stripe/stripe.styles.js +16 -23
  218. package/dist/views/payment-page/partials/stripe/stripe.utils.d.ts +25 -14
  219. package/dist/views/payment-page/partials/stripe/stripe.utils.js +111 -62
  220. package/dist/views/payment-page/partials/terms-checkbox/terms-checkbox.styles.js +1 -1
  221. package/dist/views/payment-page/payment-page.component.d.ts +1 -0
  222. package/dist/views/payment-page/payment-page.component.js +19 -9
  223. package/dist/views/payment-page/payment-page.styles.js +37 -32
  224. package/dist/views/payment-page/payment-page.utils.d.ts +7 -0
  225. package/dist/views/payment-page/payment-page.utils.js +29 -14
  226. package/dist/views/payment-page/payment-page.validators.js +9 -3
  227. package/dist/views/payment-page/services/payment-api.service.d.ts +1 -1
  228. package/dist/views/search-page/search-page.component.d.ts +1 -0
  229. package/dist/views/search-page/search-page.component.js +12 -6
  230. package/dist/views/search-page/search-page.styles.js +24 -2
  231. package/dist/views/suggestion-widget/suggestion-widget.styles.js +1 -1
  232. package/package.json +62 -75
  233. package/dist/custom-elements.json +0 -37378
  234. package/dist/sdk/meta.d.ts +0 -7
  235. package/dist/sdk/meta.js +0 -9
  236. package/dist/styles/tokens.css +0 -359
package/dist/sdk/mount.js CHANGED
@@ -3,7 +3,7 @@ import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from "../views/constants.js";
3
3
  import { createDiagnostics, themeHasNoColours } from "./diagnostics.js";
4
4
  import { attachCallbackBridge, } from "./element-adapter.js";
5
5
  import { createChrome } from "./chrome.js";
6
- import { applyConfig, applyParams, captureParamDefaults, defaultPageRegistry, } from "./page-registry.js";
6
+ import { applyConfig, applyParams, captureConfigDefaults, captureParamDefaults, defaultPageRegistry, } from "./page-registry.js";
7
7
  import { applyColorScheme, applyTheme, clearTheme } from "./theme.js";
8
8
  import { safeUrl } from "@coras-io/brand-tokens";
9
9
  import { ensureTokensInjected } from "../styles/tokens.js";
@@ -14,6 +14,23 @@ function isThenable(value) {
14
14
  value !== null &&
15
15
  typeof value.then === "function");
16
16
  }
17
+ // `apiUrl` and `distributorId` fix the mount's identity: the API client is
18
+ // built once around them (base.mixin), so an update cannot change them. The
19
+ // public type already omits them; this guards JavaScript callers.
20
+ const IMMUTABLE_CONFIG_KEYS = ["apiUrl", "distributorId"];
21
+ // Reject an update only when it would actually change an identity key, not when
22
+ // it merely restates the mounted value. Hosts commonly rebuild one config object
23
+ // and pass it to both `mount()` and `update()` (every framework wrapper does),
24
+ // so re-sending the unchanged `apiUrl`/`distributorId` must be a no-op rather
25
+ // than an error; only a genuinely different value is rejected.
26
+ function assertIdentityUnchanged(patch, current) {
27
+ const provided = patch;
28
+ for (const key of IMMUTABLE_CONFIG_KEYS) {
29
+ if (key in provided && provided[key] !== current[key]) {
30
+ throw new CorasValidationError("invalid_config", `config.${key} cannot be changed after mount; unmount and mount again to switch ${key === "apiUrl" ? "the API endpoint" : "distributor"}.`, `config.${key}`);
31
+ }
32
+ }
33
+ }
17
34
  /**
18
35
  * Create a `mount` function bound to a page registry. The public `mount` uses
19
36
  * the real registry; tests inject a lightweight registry so lifecycle behavior
@@ -38,9 +55,15 @@ export function createMountWith(registry) {
38
55
  const root = document.createElement("div");
39
56
  root.setAttribute("data-coras-root", "");
40
57
  // Token system is defined on the container scope and inherits into every
41
- // nested Web Component's shadow tree (plan F-004). Adopting into the
42
- // document is idempotent and shared across mounts.
43
- ensureTokensInjected(container.ownerDocument ?? document);
58
+ // nested Web Component's shadow tree (plan F-004). Adopt into the container's
59
+ // root node, which is its host ShadowRoot when mounted inside one - adopting
60
+ // into ownerDocument would leave a shadow-hosted mount with only the
61
+ // @property initials, losing the entire token system. `ensureTokensInjected`
62
+ // already accepts a ShadowRoot; give it the real root.
63
+ const containerRoot = container.getRootNode();
64
+ ensureTokensInjected(containerRoot instanceof ShadowRoot
65
+ ? containerRoot
66
+ : (container.ownerDocument ?? document));
44
67
  let currentPage = validated.page;
45
68
  let currentConfig = validated.config;
46
69
  let currentParams = validated.params;
@@ -53,8 +76,10 @@ export function createMountWith(registry) {
53
76
  container.appendChild(root);
54
77
  let destroyed = false;
55
78
  let generation = 0;
79
+ let chromeGeneration = 0;
56
80
  let pageElement = null;
57
81
  let pageParamDefaults = {};
82
+ let pageConfigDefaults = {};
58
83
  let chromeElements = [];
59
84
  let readySettled = false;
60
85
  let resolveReady;
@@ -93,7 +118,9 @@ export function createMountWith(registry) {
93
118
  const detail = toPublicErrorDetail(error, currentPage);
94
119
  // Per-mount logging only; the SDK never writes unconditional console output.
95
120
  options.logger?.error?.(error);
96
- options.onError?.(detail);
121
+ // Deliver to `onError` via the `coras:error` event only: the callback
122
+ // bridge routes it to `onError`, so calling `onError` here as well would
123
+ // fire it twice for one error.
97
124
  fireEvent(root, "coras:error", detail);
98
125
  emitObservability("coras.error", { code: detail.code });
99
126
  }
@@ -103,13 +130,31 @@ export function createMountWith(registry) {
103
130
  }
104
131
  chromeElements = [];
105
132
  }
133
+ // Push `currentConfig.theme` onto the root and any live navbar. Chrome is
134
+ // not rebuilt on a theme-only update, so the default logo (light + dark)
135
+ // goes straight to the navbar.
136
+ function syncThemeToRoot() {
137
+ clearTheme(root);
138
+ applyTheme(root, currentConfig.theme);
139
+ for (const element of chromeElements) {
140
+ if (element.tagName === "CORAS-NAVBAR") {
141
+ const nav = element;
142
+ nav.logo = safeUrl(currentConfig.theme?.logo);
143
+ nav.logoDark = safeUrl(currentConfig.theme?.logoDark);
144
+ nav.navbarLogo = safeUrl(currentConfig.theme?.navbarLogo);
145
+ }
146
+ }
147
+ }
106
148
  async function renderPage(targetPage, generationToken) {
107
149
  const entry = registry[targetPage];
108
150
  await entry.load();
109
151
  if (destroyed || generationToken !== generation)
110
152
  return;
111
153
  const element = document.createElement(entry.tagName);
112
- applyConfig(element, currentConfig);
154
+ // Capture element defaults before applyConfig writes to them, so a later
155
+ // update that clears an optional config value can restore the default.
156
+ const configDefaults = captureConfigDefaults(element);
157
+ applyConfig(element, currentConfig, configDefaults);
113
158
  // Preview is a mount-time rendering mode, not config or a param, so it is
114
159
  // set directly on the element; pages that honor it read their own property.
115
160
  element.preview = preview;
@@ -120,6 +165,8 @@ export function createMountWith(registry) {
120
165
  // context consumer across providers leaves those bindings stale.
121
166
  const chrome = await createChrome(currentChrome, {
122
167
  logo: currentConfig.theme?.logo,
168
+ logoDark: currentConfig.theme?.logoDark,
169
+ navbarLogo: currentConfig.theme?.navbarLogo,
123
170
  });
124
171
  if (destroyed || generationToken !== generation)
125
172
  return;
@@ -131,6 +178,7 @@ export function createMountWith(registry) {
131
178
  }
132
179
  pageElement = element;
133
180
  pageParamDefaults = paramDefaults;
181
+ pageConfigDefaults = configDefaults;
134
182
  chromeElements = chrome;
135
183
  root.appendChild(element);
136
184
  root.dataset.corasPage = targetPage;
@@ -138,28 +186,49 @@ export function createMountWith(registry) {
138
186
  try {
139
187
  await element.updateComplete;
140
188
  }
141
- catch {
142
- // Page render errors are surfaced through the page's own error paths.
189
+ catch (error) {
190
+ if (destroyed || generationToken !== generation)
191
+ return;
192
+ // A rejected first render must not resolve `ready` as a success or
193
+ // emit `coras:ready`; surface the failure instead.
194
+ reportError(error);
195
+ if (!readySettled) {
196
+ readySettled = true;
197
+ rejectReady(error);
198
+ }
199
+ return;
143
200
  }
144
201
  }
145
202
  if (destroyed || generationToken !== generation)
146
203
  return;
147
204
  root.dataset.corasMounted = "true";
205
+ // `coras:ready` is the first-completed-render signal, so emit it (and
206
+ // resolve `ready`) exactly once; later navigations re-render without
207
+ // re-firing it.
148
208
  if (!readySettled) {
149
209
  readySettled = true;
150
210
  resolveReady();
211
+ fireEvent(root, "coras:ready", { page: targetPage });
151
212
  }
152
- fireEvent(root, "coras:ready", { page: targetPage });
153
213
  emitObservability("coras.page-loaded", { page: targetPage });
154
214
  }
155
215
  const bridge = attachCallbackBridge(root, options, {
156
216
  onListenerError: (error) => options.logger?.error?.(error),
157
217
  });
158
- function startRender(targetPage) {
218
+ function startRender(targetPage, rollback) {
159
219
  const token = ++generation;
220
+ // A fresh render owns chrome from here on; invalidate any in-flight
221
+ // chrome-only update so its late resolve can't append to the new page.
222
+ chromeGeneration += 1;
160
223
  renderPage(targetPage, token).catch((error) => {
161
224
  if (destroyed || token !== generation)
162
225
  return;
226
+ // `renderPage` only throws before it swaps the DOM (the page chunk or
227
+ // the chrome never loaded), so the previous page is still on screen.
228
+ // Restore the state that describes it, or `app.state` would report a
229
+ // page that never rendered. Roll back before reporting, so the error
230
+ // and any state read from its handler agree.
231
+ rollback?.();
163
232
  reportError(error);
164
233
  if (!readySettled) {
165
234
  readySettled = true;
@@ -201,61 +270,98 @@ export function createMountWith(registry) {
201
270
  function applyUpdate(patch) {
202
271
  if (destroyed)
203
272
  return;
204
- // Reject an unknown page before mutating any state, so a bad update or
205
- // navigate can't leave currentPage pointing at a page the registry lacks.
273
+ // Phase 1 - validate the whole patch before mutating any state, so a
274
+ // failure (unknown page, invalid params/config, immutable field) leaves
275
+ // the mount's state and DOM exactly as they were.
206
276
  if (patch.page != null)
207
277
  assertValidPage(patch.page);
278
+ let nextConfig = currentConfig;
208
279
  if (patch.config) {
209
- currentConfig = validateConfig({ ...currentConfig, ...patch.config }, strict);
210
- if ("theme" in patch.config) {
211
- clearTheme(root);
212
- applyTheme(root, currentConfig.theme);
213
- // Chrome is not rebuilt on a theme-only update, so push the (possibly
214
- // changed) default logo to the live navbar directly.
215
- for (const element of chromeElements) {
216
- if (element.tagName === "CORAS-NAVBAR") {
217
- element.logo = safeUrl(currentConfig.theme?.logo);
218
- }
219
- }
220
- }
221
- // A colour-scheme flip is just the root's `color-scheme`; the
222
- // `light-dark()` pairs already carry both modes.
223
- if ("colorScheme" in patch.config) {
224
- applyColorScheme(root, currentConfig.colorScheme);
225
- }
280
+ assertIdentityUnchanged(patch.config, currentConfig);
281
+ nextConfig = validateConfig({ ...currentConfig, ...patch.config }, strict);
226
282
  }
227
283
  const pageChanged = patch.page != null && patch.page !== currentPage;
228
- const chromeChanged = patch.chrome !== undefined;
229
- if (chromeChanged)
230
- currentChrome = patch.chrome;
284
+ const targetPage = pageChanged
285
+ ? patch.page
286
+ : currentPage;
287
+ let nextParams = currentParams;
231
288
  if (pageChanged) {
232
- currentPage = patch.page;
233
- currentParams = validatePageParams(currentPage, patch.params, strict);
234
- startRender(currentPage);
235
- return;
289
+ nextParams = validatePageParams(targetPage, patch.params, strict);
236
290
  }
237
- if (patch.params) {
291
+ else if (patch.params) {
238
292
  // Params are the full desired state for the page: replace rather than
239
293
  // merge so params the host dropped (cleared) are reset to defaults by
240
294
  // applyParams instead of lingering.
241
- currentParams = validatePageParams(currentPage, patch.params, strict);
295
+ nextParams = validatePageParams(currentPage, patch.params, strict);
296
+ }
297
+ // Phase 2 - commit. Nothing below throws on invalid input.
298
+ const themeChanged = patch.config != null && "theme" in patch.config;
299
+ const colorSchemeChanged = patch.config != null && "colorScheme" in patch.config;
300
+ const chromeChanged = patch.chrome !== undefined;
301
+ const previous = {
302
+ page: currentPage,
303
+ params: currentParams,
304
+ config: currentConfig,
305
+ chrome: currentChrome,
306
+ };
307
+ currentConfig = nextConfig;
308
+ currentParams = nextParams;
309
+ if (chromeChanged)
310
+ currentChrome = patch.chrome;
311
+ if (themeChanged)
312
+ syncThemeToRoot();
313
+ // A colour-scheme flip is just the root's `color-scheme`; the
314
+ // `light-dark()` pairs already carry both modes.
315
+ if (colorSchemeChanged) {
316
+ applyColorScheme(root, currentConfig.colorScheme);
317
+ }
318
+ if (pageChanged) {
319
+ currentPage = targetPage;
320
+ // A page change renders asynchronously, so the rest of the patch is not
321
+ // durable until it lands: the new config reaches the DOM only through
322
+ // the new page element. If the render fails, undo the whole patch so an
323
+ // `update()` stays all-or-nothing rather than half-applied.
324
+ startRender(currentPage, () => {
325
+ currentPage = previous.page;
326
+ currentParams = previous.params;
327
+ currentConfig = previous.config;
328
+ currentChrome = previous.chrome;
329
+ if (themeChanged)
330
+ syncThemeToRoot();
331
+ if (colorSchemeChanged) {
332
+ applyColorScheme(root, currentConfig.colorScheme);
333
+ }
334
+ });
335
+ return;
242
336
  }
243
337
  // No page change: update the existing element in place when possible.
244
338
  if (pageElement) {
245
- applyConfig(pageElement, currentConfig);
339
+ applyConfig(pageElement, currentConfig, pageConfigDefaults);
246
340
  applyParams(pageElement, registry[currentPage], currentParams, pageParamDefaults);
247
341
  if (chromeChanged) {
248
342
  removeChrome();
343
+ const chromeToken = ++chromeGeneration;
249
344
  void createChrome(currentChrome, {
250
345
  logo: currentConfig.theme?.logo,
251
- }).then((chrome) => {
252
- if (destroyed || !pageElement)
346
+ logoDark: currentConfig.theme?.logoDark,
347
+ navbarLogo: currentConfig.theme?.navbarLogo,
348
+ })
349
+ .then((chrome) => {
350
+ // A newer chrome update or a page render has superseded this one;
351
+ // discard the stale chrome instead of appending it.
352
+ if (destroyed ||
353
+ !pageElement ||
354
+ chromeToken !== chromeGeneration) {
355
+ for (const chromeElement of chrome)
356
+ chromeElement.remove();
253
357
  return;
358
+ }
254
359
  for (const chromeElement of chrome) {
255
360
  pageElement.appendChild(chromeElement);
256
361
  }
257
362
  chromeElements = chrome;
258
- });
363
+ })
364
+ .catch(reportError);
259
365
  }
260
366
  }
261
367
  else if (chromeChanged) {
@@ -283,6 +389,8 @@ export function createMountWith(registry) {
283
389
  if (navigateOptions?.emitNavigate) {
284
390
  // Programmatic navigation is tagged `host` so onNavigate handlers can
285
391
  // avoid re-driving the host router (loop prevention, ADR 0014).
392
+ // `page` and `params` are correlated by this method's own signature;
393
+ // the cast only re-states that for the un-narrowed event map.
286
394
  fireEvent(root, "coras:navigate", {
287
395
  page,
288
396
  params,
@@ -3,6 +3,9 @@ export function classifyPageUpdate(input, context) {
3
3
  const currency = input.currency ?? context.currency;
4
4
  const isNavigate = (input.page != null && input.page !== context.currentPage) ||
5
5
  input.href != null;
6
+ // The public details correlate `page` with `params`; here the two arrive as
7
+ // separate values whose pairing was already established by `validatePageParams`
8
+ // upstream, so the cast re-states an invariant rather than assuming one.
6
9
  if (isNavigate) {
7
10
  return {
8
11
  kind: "navigate",
@@ -14,8 +14,20 @@ export type PageEntry = {
14
14
  };
15
15
  export type PageRegistry = Record<CorasPageName, PageEntry>;
16
16
  export declare const defaultPageRegistry: PageRegistry;
17
- /** Apply normalized SDK config to a page (or chrome) element's properties. */
18
- export declare function applyConfig(element: ConfigurableElement, config: CorasConfig): void;
17
+ /**
18
+ * Snapshot the element's default values for every property an optional config
19
+ * field drives. Taken right after creation (before `applyConfig`) so a later
20
+ * update that drops an optional value resets the property to its default - i.e.
21
+ * "clears" it - instead of leaving the stale value in place. Mirrors
22
+ * `captureParamDefaults` for params.
23
+ */
24
+ export declare function captureConfigDefaults(element: ConfigurableElement): Record<string, unknown>;
25
+ /**
26
+ * Apply normalized SDK config to a page (or chrome) element's properties. An
27
+ * optional field the config omits resets its property to the captured default,
28
+ * so clearing `locale`/`currency`/etc. on an update actually takes effect.
29
+ */
30
+ export declare function applyConfig(element: ConfigurableElement, config: CorasConfig, defaults: Record<string, unknown>): void;
19
31
  /**
20
32
  * Snapshot the element's default values for every param the page exposes. Taken
21
33
  * right after creation (before any params are applied) so a later update can
@@ -40,22 +40,42 @@ export const defaultPageRegistry = {
40
40
  paramToProperty: identity("limit", "showExploreCard", "country", "city"),
41
41
  },
42
42
  };
43
- /** Apply normalized SDK config to a page (or chrome) element's properties. */
44
- export function applyConfig(element, config) {
43
+ /** Element properties driven by an optional config field, in config -> property form. */
44
+ const OPTIONAL_CONFIG_PROPERTIES = [
45
+ "locale",
46
+ "locales",
47
+ "currency",
48
+ "currencies",
49
+ "loyaltyPoints",
50
+ ];
51
+ /**
52
+ * Snapshot the element's default values for every property an optional config
53
+ * field drives. Taken right after creation (before `applyConfig`) so a later
54
+ * update that drops an optional value resets the property to its default - i.e.
55
+ * "clears" it - instead of leaving the stale value in place. Mirrors
56
+ * `captureParamDefaults` for params.
57
+ */
58
+ export function captureConfigDefaults(element) {
59
+ const defaults = {};
60
+ for (const property of OPTIONAL_CONFIG_PROPERTIES) {
61
+ defaults[property] = element[property];
62
+ }
63
+ return defaults;
64
+ }
65
+ /**
66
+ * Apply normalized SDK config to a page (or chrome) element's properties. An
67
+ * optional field the config omits resets its property to the captured default,
68
+ * so clearing `locale`/`currency`/etc. on an update actually takes effect.
69
+ */
70
+ export function applyConfig(element, config, defaults) {
45
71
  element.apiHost = config.apiUrl;
46
72
  element.distributorID = config.distributorId;
47
73
  element.assetsURL = config.assetsUrl;
48
- if (config.locale != null)
49
- element.locale = config.locale;
50
- if (config.allowedLocales != null)
51
- element.locales = config.allowedLocales;
52
- if (config.currency != null)
53
- element.currency = config.currency;
54
- if (config.allowedCurrencies != null)
55
- element.currencies = config.allowedCurrencies;
56
- if (config.loyaltyPointsEnabled != null) {
57
- element.loyaltyPoints = config.loyaltyPointsEnabled;
58
- }
74
+ element.locale = config.locale ?? defaults.locale;
75
+ element.locales = config.allowedLocales ?? defaults.locales;
76
+ element.currency = config.currency ?? defaults.currency;
77
+ element.currencies = config.allowedCurrencies ?? defaults.currencies;
78
+ element.loyaltyPoints = config.loyaltyPointsEnabled ?? defaults.loyaltyPoints;
59
79
  }
60
80
  /**
61
81
  * Snapshot the element's default values for every param the page exposes. Taken
@@ -1,19 +1,5 @@
1
1
  import type { CorasColorScheme, CorasTheme } from "./types.js";
2
- /**
3
- * The brand `theme` keys that map 1:1 to a public `--coras-brand-*` custom
4
- * property. Sourced from the shared engine so the SDK, plugin, and Studio agree.
5
- * `navbarShadow`, `logo`, and `fontFaces` are applied specially (below).
6
- */
7
- export declare const THEME_CSS_VARS: Record<"primary" | "secondary" | "background" | "surface" | "text" | "textMuted" | "radius" | "font" | "textButton" | "bodyIcon" | "buttonRadius" | "chipRadius" | "navbar" | "navbarText" | "navbarIcon" | "navbarBorder" | "footer" | "footerText" | "footerLink" | "error" | "success" | "warning" | "info", string>;
8
- /**
9
- * Set the mount root's colour scheme so the token sheet's `light-dark()` pairs
10
- * resolve correctly. `"system"` maps to `light dark` (follows the OS via
11
- * `prefers-color-scheme`); `"inherit"` overrides the sheet's default `light` so
12
- * the root inherits the host page's `color-scheme`. Cheap enough to call on its
13
- * own for a mode flip.
14
- */
2
+ export declare const THEME_CSS_VARS: Record<"primary" | "secondary" | "pageBackground" | "text" | "textMuted" | "link" | "bodyIcon" | "focusRing" | "scrim" | "cardBackground" | "cardBorder" | "fieldBackground" | "fieldBorder" | "menuBackground" | "menuBorder" | "divider" | "cardShadow" | "softShadow" | "mainButton" | "mainButtonText" | "mainButtonHover" | "mainButtonPressed" | "secondaryButton" | "secondaryButtonText" | "secondaryButtonHover" | "secondaryButtonPressed" | "textButton" | "mainButtonBorder" | "secondaryButtonBorder" | "disabled" | "buttonShadow" | "buttonHoverLight" | "buttonHoverDark" | "radius" | "buttonRadius" | "chipRadius" | "fieldRadius" | "searchRadius" | "font" | "headingFont" | "headingScale" | "logoHeight" | "navbarBackground" | "navbarText" | "navbarIcon" | "navbarBorder" | "navbarShadow" | "footerBackground" | "footerText" | "footerLink" | "footerBorder" | "footerPaddingBottom" | "error" | "errorBackground" | "success" | "successBackground" | "warning" | "warningBackground" | "info" | "infoBackground" | "primaryTint" | "secondaryTint" | "neutral50" | "neutral100" | "neutral200" | "neutral300" | "neutral400" | "neutral500" | "neutral600" | "neutral700" | "neutral800" | "neutral900", string>;
15
3
  export declare function applyColorScheme(element: HTMLElement, scheme: CorasColorScheme | undefined): void;
16
- /** Apply a brand to an element as scoped `--coras-*` variables (P-052/P-061). */
17
4
  export declare function applyTheme(element: HTMLElement, theme: CorasTheme | undefined): void;
18
- /** Remove any brand variables/fonts previously set on an element. */
19
5
  export declare function clearTheme(element: HTMLElement): void;
package/dist/sdk/theme.js CHANGED
@@ -1,37 +1,13 @@
1
- import { BRAND_CSS_VARS, brandVarValue, fontFacesToCSS, sanitizeBrandValue, } from "@coras-io/brand-tokens";
2
- /**
3
- * The brand `theme` keys that map 1:1 to a public `--coras-brand-*` custom
4
- * property. Sourced from the shared engine so the SDK, plugin, and Studio agree.
5
- * `navbarShadow`, `logo`, and `fontFaces` are applied specially (below).
6
- */
1
+ import { BRAND_CSS_VARS, brandVarValue, fontFacesToCSS, } from "@coras-io/brand-tokens";
7
2
  export const THEME_CSS_VARS = BRAND_CSS_VARS;
8
3
  const THEME_KEYS = Object.keys(THEME_CSS_VARS);
9
- /**
10
- * Set the mount root's colour scheme so the token sheet's `light-dark()` pairs
11
- * resolve correctly. `"system"` maps to `light dark` (follows the OS via
12
- * `prefers-color-scheme`); `"inherit"` overrides the sheet's default `light` so
13
- * the root inherits the host page's `color-scheme`. Cheap enough to call on its
14
- * own for a mode flip.
15
- */
16
4
  export function applyColorScheme(element, scheme) {
17
5
  element.style.colorScheme =
18
- scheme === "dark"
19
- ? "dark"
20
- : scheme === "system"
21
- ? "light dark"
22
- : scheme === "inherit"
23
- ? "inherit"
24
- : "light";
6
+ scheme === "dark" ? "dark" : scheme === "inherit" ? "inherit" : "light";
25
7
  }
26
- /** Apply a brand to an element as scoped `--coras-*` variables (P-052/P-061). */
27
8
  export function applyTheme(element, theme) {
28
9
  if (!theme)
29
10
  return;
30
- // Each themeable var is emitted as a `light-dark()` pair (or a single value
31
- // when the field has no dark variant) via the shared engine, which sanitizes
32
- // both sides - an invalid value yields "" and is skipped, so the Coras default
33
- // (itself a light-dark pair) shows through. The dark side falls back to the
34
- // Coras dark default when the brand sets only the light value.
35
11
  const dark = (theme.dark ?? {});
36
12
  for (const key of THEME_KEYS) {
37
13
  if (theme[key] == null && dark[key] == null)
@@ -40,17 +16,8 @@ export function applyTheme(element, theme) {
40
16
  if (value)
41
17
  element.style.setProperty(THEME_CSS_VARS[key], value);
42
18
  }
43
- if (theme.navbarShadow != null) {
44
- const shadow = sanitizeBrandValue("navbarShadow", theme.navbarShadow);
45
- if (shadow) {
46
- element.style.setProperty("--coras-brand-navbar-shadow", shadow);
47
- }
48
- }
49
- // The page background is opt-in (see the root rule in tokens.css): only an
50
- // explicit `background` paints the embed surface, so the embed otherwise
51
- // adopts the host page's background.
52
- if (theme.background != null || dark.background != null) {
53
- const pageBg = brandVarValue("background", theme.background, dark.background);
19
+ if (theme.pageBackground != null || dark.pageBackground != null) {
20
+ const pageBg = brandVarValue("pageBackground", theme.pageBackground, dark.pageBackground);
54
21
  if (pageBg)
55
22
  element.style.setProperty("--coras-page-background", pageBg);
56
23
  }
@@ -62,12 +29,10 @@ export function applyTheme(element, theme) {
62
29
  element.appendChild(style);
63
30
  }
64
31
  }
65
- /** Remove any brand variables/fonts previously set on an element. */
66
32
  export function clearTheme(element) {
67
33
  for (const key of THEME_KEYS) {
68
34
  element.style.removeProperty(THEME_CSS_VARS[key]);
69
35
  }
70
- element.style.removeProperty("--coras-brand-navbar-shadow");
71
36
  element.style.removeProperty("--coras-page-background");
72
37
  element
73
38
  .querySelectorAll(":scope > [data-coras-fontfaces]")
@@ -70,11 +70,12 @@ export type CorasTheme = BrandConfig;
70
70
  *
71
71
  * - `"light"` (default) - always light, ignoring the host page.
72
72
  * - `"dark"` - always dark.
73
- * - `"system"` - follows the OS via `prefers-color-scheme`.
74
73
  * - `"inherit"` - follows the host page's own `color-scheme` (the SDK sets none),
75
- * so the embed behaves like a native element on a light or dark site.
74
+ * so the embed behaves like a native element on a light or dark site. A page
75
+ * that declares `color-scheme: light dark` hands the embed the visitor's OS
76
+ * preference through the same route.
76
77
  */
77
- export type CorasColorScheme = "light" | "dark" | "system" | "inherit";
78
+ export type CorasColorScheme = "light" | "dark" | "inherit";
78
79
  export type CorasConfig = {
79
80
  apiUrl: string;
80
81
  distributorId: string;
@@ -157,21 +158,33 @@ export type CorasNavigationSource = "embed" | "host";
157
158
  export type CorasReadyDetail = {
158
159
  page: CorasPageName;
159
160
  };
160
- export type CorasNavigateDetail = {
161
- page: CorasPageName;
162
- params?: CorasPageParams;
161
+ type NavigateDetailFor<P extends CorasPageName> = {
162
+ page: P;
163
+ params?: CorasPageParamsByPage[P];
163
164
  locale?: SupportedLocales;
164
165
  currency?: SupportedCurrencies;
165
166
  href?: string;
166
167
  source: CorasNavigationSource;
167
168
  };
168
- export type CorasStateChangeDetail = {
169
- page: CorasPageName;
170
- params: CorasPageParams;
169
+ /**
170
+ * Distributes over the page union, so narrowing on `detail.page` narrows
171
+ * `detail.params` with it and a handler cannot read a param the page never has.
172
+ */
173
+ export type CorasNavigateDetail<P extends CorasPageName = CorasPageName> = P extends CorasPageName ? NavigateDetailFor<P> : never;
174
+ type StateChangeDetailFor<P extends CorasPageName> = {
175
+ page: P;
176
+ params: CorasPageParamsByPage[P];
171
177
  locale?: SupportedLocales;
172
178
  currency?: SupportedCurrencies;
173
179
  };
174
- export type CorasPaymentStatus = "pending" | "succeeded" | "failed" | "cancelled";
180
+ /** Distributes over the page union (see `CorasNavigateDetail`). */
181
+ export type CorasStateChangeDetail<P extends CorasPageName = CorasPageName> = P extends CorasPageName ? StateChangeDetailFor<P> : never;
182
+ /**
183
+ * Terminal payment outcomes the SDK emits. Only settled results are surfaced:
184
+ * a `pending` in-flight state is internal, and a user cancellation returns the
185
+ * form to its editable state rather than emitting a distinct status.
186
+ */
187
+ export type CorasPaymentStatus = "succeeded" | "failed";
175
188
  export type CorasPaymentStatusDetail = {
176
189
  status: CorasPaymentStatus;
177
190
  /** Public reference only; never provider secrets or card data. */
@@ -208,6 +221,16 @@ export type CorasCallbacks = {
208
221
  onError?: (detail: CorasErrorDetail) => void;
209
222
  onObservability?: (event: CorasObservabilityEvent) => void;
210
223
  };
224
+ /**
225
+ * `mount()` is generic over `P`, so a call passing a literal page gets its own
226
+ * params checked against it. The alias is deliberately *not* distributed over
227
+ * the page union: a host that owns routing reads `page` and `params` out of a
228
+ * URL as separate values, and no annotation can prove that pair correlates.
229
+ * Distributing here would force every routed host to cast - the SDK's primary
230
+ * integration - to buy a check the call site already makes. The read-side types
231
+ * (`CorasState`, `CorasNavigateDetail`, `CorasStateChangeDetail`) do distribute,
232
+ * because narrowing there is free.
233
+ */
211
234
  export type CorasMountOptions<P extends CorasPageName = CorasPageName> = {
212
235
  container: HTMLElement;
213
236
  page: P;
@@ -226,17 +249,29 @@ export type CorasMountOptions<P extends CorasPageName = CorasPageName> = {
226
249
  */
227
250
  preview?: boolean;
228
251
  } & CorasCallbacks;
229
- /** Frozen snapshot of the mount's current public state. */
230
- export type CorasState = {
231
- readonly page: CorasPageName;
232
- readonly params: CorasPageParams;
252
+ type StateFor<P extends CorasPageName> = {
253
+ readonly page: P;
254
+ readonly params: CorasPageParamsByPage[P];
233
255
  readonly locale: SupportedLocales;
234
256
  readonly currency: SupportedCurrencies;
235
257
  };
236
- export type CorasUpdate = {
237
- page?: CorasPageName;
238
- params?: CorasPageParams;
239
- config?: Partial<CorasConfig>;
258
+ /**
259
+ * Frozen snapshot of the mount's current public state. Distributes over the
260
+ * page union, so `state.page` narrows `state.params` with it.
261
+ */
262
+ export type CorasState<P extends CorasPageName = CorasPageName> = P extends CorasPageName ? StateFor<P> : never;
263
+ /**
264
+ * Config an update may change. `apiUrl` and `distributorId` are the mount's
265
+ * fixed identity (the API client is built once around them), so they are not
266
+ * updatable; unmount and mount again to point at a different endpoint or
267
+ * distributor.
268
+ */
269
+ export type CorasConfigUpdate = Partial<Omit<CorasConfig, "apiUrl" | "distributorId">>;
270
+ /** Generic over `P`, not distributed - see `CorasMountOptions` for why. */
271
+ export type CorasUpdate<P extends CorasPageName = CorasPageName> = {
272
+ page?: P;
273
+ params?: CorasPageParamsByPage[P];
274
+ config?: CorasConfigUpdate;
240
275
  chrome?: CorasChrome;
241
276
  };
242
277
  export type CorasNavigateOptions = {
@@ -246,13 +281,9 @@ export type CorasNavigateOptions = {
246
281
  export type CorasApp = {
247
282
  readonly ready: Promise<void>;
248
283
  readonly state: CorasState;
249
- update(patch: CorasUpdate): void;
284
+ update<P extends CorasPageName = CorasPageName>(patch: CorasUpdate<P>): void;
250
285
  navigate<P extends CorasPageName>(page: P, params?: CorasPageParamsByPage[P], options?: CorasNavigateOptions): void;
251
286
  prefetch(page: CorasPageName): Promise<void>;
252
287
  unmount(): void;
253
288
  };
254
- export type CorasPageMeta = {
255
- page: CorasPageName;
256
- /** Internal custom-element tag used to render the page. */
257
- tagName: string;
258
- };
289
+ export {};