@coras-io/embed 0.2.0 → 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.
- package/CHANGELOG.md +127 -0
- package/README.md +8 -7
- package/dist/components/accordion/accordion-item/accordion-item.styles.js +1 -1
- package/dist/components/alert/alert.styles.js +14 -14
- package/dist/components/attraction-card/base/attraction-card.styles.js +7 -3
- package/dist/components/attraction-discovery/attraction-discovery.styles.js +1 -1
- package/dist/components/attraction-header/attraction-header.styles.js +1 -1
- package/dist/components/attraction-option/attraction-option.styles.js +4 -4
- package/dist/components/basket/basket.styles.js +27 -26
- package/dist/components/basket-empty/basket-empty.styles.js +1 -0
- package/dist/components/basket-item/basket-item.component.js +10 -4
- package/dist/components/basket-item/basket-item.styles.js +9 -3
- package/dist/components/basket-item-ticket/basket-item-ticket.styles.js +1 -3
- package/dist/components/basket-mobile/basket-mobile.styles.js +5 -5
- package/dist/components/better-carousel/better-carousel.styles.js +2 -0
- package/dist/components/bottom-drawer/bottom-drawer.styles.js +6 -4
- package/dist/components/button/button.component.d.ts +0 -1
- package/dist/components/button/button.component.js +1 -8
- package/dist/components/button/button.constants.d.ts +1 -1
- package/dist/components/button/button.constants.js +1 -1
- package/dist/components/button/button.styles.js +66 -39
- package/dist/components/calendar/base/calendar.styles.js +3 -5
- package/dist/components/calendar/multi/multi.styles.js +7 -0
- package/dist/components/category-tag/category-tag.styles.js +10 -7
- package/dist/components/checkbox/checkbox.styles.js +2 -2
- package/dist/components/chip/chip.styles.js +2 -2
- package/dist/components/collect-fida/collect-fida.component.js +5 -3
- package/dist/components/collect-fida/collect-fida.styles.js +9 -16
- package/dist/components/crypto-auth-modal/crypto-auth-modal.component.js +6 -4
- package/dist/components/crypto-auth-modal/crypto-auth-modal.styles.js +2 -2
- package/dist/components/dialog/dialog.styles.js +10 -17
- package/dist/components/drawer/drawer.component.js +6 -0
- package/dist/components/drawer/drawer.styles.js +17 -3
- package/dist/components/dropdown/dropdown.component.js +7 -0
- package/dist/components/dropdown/dropdown.styles.js +3 -6
- package/dist/components/expiration-timer/expiration-timer.styles.js +3 -3
- package/dist/components/explore-card/explore-card.styles.js +1 -1
- package/dist/components/filter-chip/filter-chip.styles.js +11 -9
- package/dist/components/filters-drawer/filters-drawer.component.js +1 -1
- package/dist/components/filters-drawer/filters-drawer.styles.js +6 -5
- package/dist/components/image/image.styles.js +3 -3
- package/dist/components/image-chip/image-chip.styles.js +1 -1
- package/dist/components/input-field/input-field.styles.js +22 -17
- package/dist/components/itinerary/itinerary-item/itinerary-item.styles.js +1 -1
- package/dist/components/joined-fields/joined-fields.styles.js +10 -6
- package/dist/components/loading-spinner/loading-spinner.styles.js +1 -1
- package/dist/components/location-image/location-image.component.d.ts +6 -0
- package/dist/components/location-image/location-image.component.js +15 -2
- package/dist/components/location-image/location-image.styles.js +1 -1
- package/dist/components/locations-results/locations-results.styles.js +4 -6
- package/dist/components/marketing-tag/marketing-tag.styles.js +1 -1
- package/dist/components/message-card/message-card.styles.js +6 -6
- package/dist/components/option-group/option-chip/option-chip.styles.js +4 -3
- package/dist/components/order-confirmation/order-confirmation-item/order-confirmation-item.styles.js +9 -0
- package/dist/components/order-confirmation/order-confirmation.component.js +4 -3
- package/dist/components/order-confirmation/order-confirmation.styles.js +1 -1
- package/dist/components/otp/otp.styles.js +6 -6
- package/dist/components/overlay/overlay.styles.js +5 -1
- package/dist/components/pay-in/pay-in.styles.js +2 -2
- package/dist/components/payment-fiat-method/payment-fiat-method.styles.js +20 -4
- package/dist/components/payment-option/payment-option.styles.js +10 -12
- package/dist/components/performance-time/performance-time.styles.js +2 -2
- package/dist/components/phone-country-code/phone-country-code.styles.js +1 -1
- package/dist/components/points/points.styles.js +1 -1
- package/dist/components/search-button/search-button.styles.js +15 -7
- package/dist/components/seated-selector/seated-selector.component.d.ts +2 -0
- package/dist/components/seated-selector/seated-selector.component.js +53 -10
- package/dist/components/seated-selector/seated-selector.styles.js +77 -14
- package/dist/components/seats-io/seats-io.component.d.ts +2 -0
- package/dist/components/seats-io/seats-io.component.js +25 -2
- package/dist/components/seats-io/seats-io.constants.d.ts +2 -1
- package/dist/components/seats-io/seats-io.constants.js +6 -1
- package/dist/components/seats-io/seats-io.transform.d.ts +8 -2
- package/dist/components/seats-io/seats-io.transform.js +12 -4
- package/dist/components/select-list/select-list.styles.js +1 -0
- package/dist/components/select-list/select-option/select-option.styles.js +3 -3
- package/dist/components/sort-dropdown/sort-dropdown.styles.js +1 -1
- package/dist/components/sort-list/sort-list.styles.js +1 -1
- package/dist/components/sport-options/sport-option/sport-option.styles.js +2 -2
- package/dist/components/star-rating/star-rating.component.d.ts +1 -0
- package/dist/components/star-rating/star-rating.component.js +19 -23
- package/dist/components/star-rating/star-rating.styles.js +32 -6
- package/dist/components/tab/tab.styles.js +1 -1
- package/dist/components/tabs/tabs.styles.js +1 -1
- package/dist/components/tag/tag.styles.js +7 -7
- package/dist/components/text-section/text-section.styles.js +2 -1
- package/dist/components/time-tag/time-tag.styles.js +1 -2
- package/dist/components/tooltip/tooltip.styles.js +3 -3
- package/dist/components/tour-ticket-type-selector/tour-ticket-type/tour-ticket-type.styles.js +2 -2
- package/dist/components/tour-ticket-type-selector/tour-ticket-type-selector.component.js +1 -1
- package/dist/components/tour-ticket-type-selector/tour-ticket-type-selector.styles.js +1 -10
- package/dist/components/transfer-modal/transfer-modal.component.js +3 -3
- package/dist/components/transfer-modal/transfer-modal.styles.js +6 -8
- package/dist/containers/basket/basket.component.d.ts +1 -1
- package/dist/containers/basket/basket.component.js +56 -25
- package/dist/containers/footer/footer.styles.js +4 -0
- package/dist/containers/navbar/navbar.component.d.ts +5 -1
- package/dist/containers/navbar/navbar.component.js +38 -9
- package/dist/containers/navbar/navbar.styles.js +38 -12
- package/dist/contexts/basket/basket.provider.d.ts +1 -0
- package/dist/contexts/basket/basket.provider.js +31 -4
- package/dist/contexts/properties.context.d.ts +4 -4
- package/dist/core/api/middlewares/booking-session/booking-session.constants.d.ts +1 -0
- package/dist/core/api/middlewares/booking-session/booking-session.constants.js +1 -0
- package/dist/core/api/middlewares/booking-session/booking-session.js +18 -27
- package/dist/core/api/middlewares/booking-session/booking-session.utils.d.ts +4 -4
- package/dist/core/api/middlewares/booking-session/booking-session.utils.js +13 -10
- package/dist/core/api/queries.js +3 -2
- package/dist/core/color-scheme.d.ts +14 -0
- package/dist/core/color-scheme.js +48 -0
- package/dist/core/currency/constants.d.ts +1 -1
- package/dist/core/currency/constants.js +4 -3
- package/dist/core/i18n/date.js +20 -9
- package/dist/core/i18n/locale-codes.d.ts +2 -2
- package/dist/core/i18n/locale-codes.js +4 -0
- package/dist/core/images/constants.d.ts +0 -5
- package/dist/core/images/constants.js +0 -5
- package/dist/core/images/helpers.d.ts +8 -0
- package/dist/core/images/helpers.js +14 -6
- package/dist/core/images/srcset.js +2 -2
- package/dist/core/storybook/demo.d.ts +4 -0
- package/dist/core/storybook/demo.js +17 -0
- package/dist/core/types/locales.d.ts +7 -0
- package/dist/core/utils/datetime.js +10 -4
- package/dist/core/utils/locale.d.ts +8 -1
- package/dist/core/utils/locale.js +10 -0
- package/dist/helpers/helpers.d.ts +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +1 -2
- package/dist/locales/ar-SA.d.ts +279 -0
- package/dist/locales/ar-SA.js +288 -0
- package/dist/locales/da-DK.d.ts +100 -82
- package/dist/locales/da-DK.js +104 -89
- package/dist/locales/de-DE.d.ts +100 -82
- package/dist/locales/de-DE.js +104 -89
- package/dist/locales/en-IE.d.ts +250 -232
- package/dist/locales/en-IE.js +253 -238
- package/dist/locales/en-US.d.ts +100 -82
- package/dist/locales/en-US.js +105 -90
- package/dist/locales/es-ES.d.ts +100 -82
- package/dist/locales/es-ES.js +104 -89
- package/dist/locales/fr-FR.d.ts +100 -82
- package/dist/locales/fr-FR.js +104 -89
- package/dist/locales/fr-MA.d.ts +100 -82
- package/dist/locales/fr-MA.js +104 -89
- package/dist/locales/is-IS.d.ts +100 -82
- package/dist/locales/is-IS.js +104 -89
- package/dist/locales/it-IT.d.ts +100 -82
- package/dist/locales/it-IT.js +104 -89
- package/dist/locales/ms-MY.d.ts +100 -82
- package/dist/locales/ms-MY.js +104 -89
- package/dist/locales/nb-NO.d.ts +100 -82
- package/dist/locales/nb-NO.js +104 -89
- package/dist/locales/pl-PL.d.ts +100 -82
- package/dist/locales/pl-PL.js +104 -89
- package/dist/locales/pt-PT.d.ts +100 -82
- package/dist/locales/pt-PT.js +104 -89
- package/dist/locales/sv-SE.d.ts +100 -82
- package/dist/locales/sv-SE.js +104 -89
- package/dist/locales/th-TH.d.ts +100 -82
- package/dist/locales/th-TH.js +104 -89
- package/dist/locales/tr-TR.d.ts +100 -82
- package/dist/locales/tr-TR.js +104 -89
- package/dist/locales/zh-CN.d.ts +100 -82
- package/dist/locales/zh-CN.js +105 -90
- package/dist/locales/zh-HK.d.ts +279 -0
- package/dist/locales/zh-HK.js +288 -0
- package/dist/mixins/coinbase.mixin.js +1 -1
- package/dist/mixins/error-modal.styles.js +2 -2
- package/dist/mixins/location-search/location-search.js +3 -3
- package/dist/mixins/location-search/location-search.styles.js +6 -5
- package/dist/sdk/chrome.d.ts +9 -3
- package/dist/sdk/chrome.js +21 -9
- package/dist/sdk/element-adapter.js +11 -8
- package/dist/sdk/mount.d.ts +3 -3
- package/dist/sdk/mount.js +150 -42
- package/dist/sdk/navigation-intent.js +3 -0
- package/dist/sdk/page-registry.d.ts +14 -2
- package/dist/sdk/page-registry.js +33 -13
- package/dist/sdk/theme.d.ts +1 -15
- package/dist/sdk/theme.js +4 -39
- package/dist/sdk/types.d.ts +55 -24
- package/dist/sdk/url.d.ts +24 -3
- package/dist/sdk/url.js +66 -27
- package/dist/sdk/validation.d.ts +0 -7
- package/dist/sdk/validation.js +105 -47
- package/dist/styles/contrast-fixtures.d.ts +0 -5
- package/dist/styles/contrast-fixtures.js +79 -9
- package/dist/styles/contrast.js +4 -3
- package/dist/styles/defaults/component.js +17 -1
- package/dist/styles/primitives.d.ts +0 -10
- package/dist/styles/primitives.js +2 -12
- package/dist/styles/tokens.d.ts +1 -1
- package/dist/styles/tokens.generated.d.ts +1 -1
- package/dist/styles/tokens.generated.js +1 -1
- package/dist/styles/tokens.metadata.d.ts +1 -1
- package/dist/styles/tokens.metadata.js +360 -90
- package/dist/views/details-page/details-page.component.d.ts +5 -5
- package/dist/views/details-page/details-page.component.js +72 -79
- package/dist/views/details-page/details-page.styles.js +64 -9
- package/dist/views/help-page/help-page.component.d.ts +1 -0
- package/dist/views/help-page/help-page.component.js +11 -4
- package/dist/views/help-page/help-page.styles.js +21 -8
- package/dist/views/landing-page/landing-page.component.d.ts +4 -4
- package/dist/views/landing-page/landing-page.component.js +65 -65
- package/dist/views/landing-page/landing-page.styles.js +70 -3
- package/dist/views/payment-page/partials/activity-details/activity-details.styles.js +10 -33
- package/dist/views/payment-page/partials/billing-address/billing-address.styles.js +4 -9
- package/dist/views/payment-page/partials/confirmation/confirmation.styles.js +6 -6
- package/dist/views/payment-page/partials/hotel-pickup-list/hotel-pickup-list.styles.js +4 -4
- package/dist/views/payment-page/partials/payment-type-selector/payment-type-selector.styles.js +1 -0
- package/dist/views/payment-page/partials/stripe/stripe.component.d.ts +1 -1
- package/dist/views/payment-page/partials/stripe/stripe.component.js +11 -12
- package/dist/views/payment-page/partials/stripe/stripe.constants.d.ts +2 -15
- package/dist/views/payment-page/partials/stripe/stripe.constants.js +4 -17
- package/dist/views/payment-page/partials/stripe/stripe.styles.js +16 -23
- package/dist/views/payment-page/partials/stripe/stripe.utils.d.ts +25 -14
- package/dist/views/payment-page/partials/stripe/stripe.utils.js +111 -62
- package/dist/views/payment-page/partials/terms-checkbox/terms-checkbox.styles.js +1 -1
- package/dist/views/payment-page/payment-page.component.d.ts +1 -0
- package/dist/views/payment-page/payment-page.component.js +19 -9
- package/dist/views/payment-page/payment-page.styles.js +37 -32
- package/dist/views/payment-page/payment-page.utils.d.ts +7 -0
- package/dist/views/payment-page/payment-page.utils.js +29 -14
- package/dist/views/payment-page/payment-page.validators.js +9 -3
- package/dist/views/payment-page/services/payment-api.service.d.ts +1 -1
- package/dist/views/search-page/search-page.component.d.ts +1 -0
- package/dist/views/search-page/search-page.component.js +12 -6
- package/dist/views/search-page/search-page.styles.js +24 -2
- package/dist/views/suggestion-widget/suggestion-widget.styles.js +1 -1
- package/package.json +62 -75
- package/dist/custom-elements.json +0 -37397
- package/dist/sdk/meta.d.ts +0 -7
- package/dist/sdk/meta.js +0 -9
- 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).
|
|
42
|
-
//
|
|
43
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
205
|
-
//
|
|
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
|
-
|
|
210
|
-
|
|
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
|
|
229
|
-
|
|
230
|
-
|
|
284
|
+
const targetPage = pageChanged
|
|
285
|
+
? patch.page
|
|
286
|
+
: currentPage;
|
|
287
|
+
let nextParams = currentParams;
|
|
231
288
|
if (pageChanged) {
|
|
232
|
-
|
|
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
|
-
|
|
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
|
-
|
|
252
|
-
|
|
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
|
-
/**
|
|
18
|
-
|
|
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
|
-
/**
|
|
44
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
package/dist/sdk/theme.d.ts
CHANGED
|
@@ -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,
|
|
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.
|
|
44
|
-
const
|
|
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]")
|
package/dist/sdk/types.d.ts
CHANGED
|
@@ -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" | "
|
|
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
|
-
|
|
161
|
-
page:
|
|
162
|
-
params?:
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
readonly
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
|
255
|
-
page: CorasPageName;
|
|
256
|
-
/** Internal custom-element tag used to render the page. */
|
|
257
|
-
tagName: string;
|
|
258
|
-
};
|
|
289
|
+
export {};
|