@escape-game-over/atlas 0.1.106 → 0.1.108
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/package.json +3 -2
- package/src/analytics/storage.ts +95 -29
- package/src/analytics/umami.ts +15 -1
- package/src/astro/BookingWidget.astro +12 -1
- package/src/astro/EscapeNavigator.astro +39 -0
- package/src/astro/escape-navigator-consent.ts +38 -0
- package/src/booking/escape-navigator.ts +57 -0
- package/src/booking/widget.ts +8 -1
- package/src/index.ts +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@escape-game-over/atlas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.108",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Typed, data-driven machinery for static multi-locale, multi-deployment Astro sites.",
|
|
6
6
|
"private": false,
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"./astro/consent-banner": "./src/astro/ConsentBanner.astro",
|
|
23
23
|
"./astro/document": "./src/astro/Document.astro",
|
|
24
24
|
"./astro/element": "./src/astro/AtlasElement.astro",
|
|
25
|
+
"./astro/escape-navigator": "./src/astro/EscapeNavigator.astro",
|
|
25
26
|
"./astro/four-escape": "./src/astro/FourEscape.astro",
|
|
26
27
|
"./astro/google-map": "./src/astro/GoogleMap.astro",
|
|
27
28
|
"./astro/image": "./src/astro/Image.astro",
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
"@biomejs/biome": "2.6.6",
|
|
66
67
|
"@types/node": "26.6.6",
|
|
67
68
|
"@vitest/coverage-istanbul": "5.1.0",
|
|
68
|
-
"astro": "7.4.
|
|
69
|
+
"astro": "7.4.10",
|
|
69
70
|
"typescript": "6.0.3",
|
|
70
71
|
"vitest": "5.1.0"
|
|
71
72
|
}
|
package/src/analytics/storage.ts
CHANGED
|
@@ -52,7 +52,9 @@ export const STORAGE_VENDOR_NAMES = {
|
|
|
52
52
|
drip: "Drip",
|
|
53
53
|
clarity: "Microsoft Clarity",
|
|
54
54
|
"4escape": "4escape",
|
|
55
|
-
|
|
55
|
+
escapenavigator: "Escape Navigator",
|
|
56
|
+
stripe: "Stripe",
|
|
57
|
+
} as const satisfies Record<StorageVendor | BookingVendor | "stripe", string>;
|
|
56
58
|
|
|
57
59
|
/** A span `Intl.NumberFormat` can word in any locale: `{ 2, "year" }`. */
|
|
58
60
|
export interface Lifetime {
|
|
@@ -69,15 +71,23 @@ export type AtlasStoredId = "atlas-locale" | "atlas-campaign" | "atlas-consent";
|
|
|
69
71
|
/** A booking widget's rows: what it keeps, and what it keeps for the tab. */
|
|
70
72
|
type BookingRow = BookingVendor | `${BookingVendor}-tab`;
|
|
71
73
|
|
|
74
|
+
/** Stripe's, for a booking widget that takes payment through it. */
|
|
75
|
+
type StripeRow = "stripe" | "stripe-own";
|
|
76
|
+
|
|
72
77
|
/** One row's id: what a project keys the row's wording by. */
|
|
73
|
-
export type StoredId =
|
|
78
|
+
export type StoredId =
|
|
79
|
+
| AtlasStoredId
|
|
80
|
+
| StorageVendor
|
|
81
|
+
| OwnRow
|
|
82
|
+
| BookingRow
|
|
83
|
+
| StripeRow;
|
|
74
84
|
|
|
75
85
|
export interface StoredItem {
|
|
76
86
|
readonly id: StoredId;
|
|
77
87
|
/** As the browser shows them; a pattern like `_ga_<id>` where the vendor appends one. */
|
|
78
88
|
readonly names: readonly string[];
|
|
79
89
|
/** Who sets it: this site, or the vendor. */
|
|
80
|
-
readonly provider: "site" | StorageVendor | BookingVendor;
|
|
90
|
+
readonly provider: "site" | StorageVendor | BookingVendor | "stripe";
|
|
81
91
|
/** Set on the vendor's own domains rather than the site's; absent for the site's. */
|
|
82
92
|
readonly domains?: readonly string[];
|
|
83
93
|
/** A span; `tab` for session storage; `kept` until the visitor clears it. */
|
|
@@ -267,11 +277,22 @@ const VENDOR_ORDER: readonly StorageVendor[] = [
|
|
|
267
277
|
];
|
|
268
278
|
|
|
269
279
|
/** The vendors a deployment's settings load, in catalog order. */
|
|
280
|
+
/** What a booking widget keeps, and what it brings with it. */
|
|
281
|
+
interface BookingStorage {
|
|
282
|
+
/** In local storage on the site's domain, until cleared. */
|
|
283
|
+
readonly kept: readonly string[];
|
|
284
|
+
/** In session storage, for the tab. */
|
|
285
|
+
readonly tab: readonly string[];
|
|
286
|
+
/** Takes payment through Stripe, whose fraud check sets cookies unasked. */
|
|
287
|
+
readonly stripe?: true;
|
|
288
|
+
/** Tags it loads itself, with the store's own ids, once consent allows. */
|
|
289
|
+
readonly loads?: readonly StorageVendor[];
|
|
290
|
+
}
|
|
291
|
+
|
|
270
292
|
/**
|
|
271
|
-
* Each booking widget's storage
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* has to be granted first.
|
|
293
|
+
* Each booking widget's storage: what its script left after rendering, seen
|
|
294
|
+
* live 2026-11-11, since neither vendor documents it. Its own keys are
|
|
295
|
+
* functional — the language, the cart — so nothing has to be granted first.
|
|
275
296
|
*/
|
|
276
297
|
const BOOKING = {
|
|
277
298
|
// `forescapeAnalytics` stayed empty, with no tracker loaded; listed because
|
|
@@ -280,10 +301,43 @@ const BOOKING = {
|
|
|
280
301
|
kept: ["forescapeLayout", "forescapeStoreOrder", "forescapeAnalytics"],
|
|
281
302
|
tab: ["forescape"],
|
|
282
303
|
},
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
304
|
+
// docs.escapenavigator.com/guides-and-tips/google-analytics-4 for the tags:
|
|
305
|
+
// the store's own, from its Escape Navigator settings.
|
|
306
|
+
escapenavigator: {
|
|
307
|
+
kept: [
|
|
308
|
+
"theme",
|
|
309
|
+
"escapeNavigatorBookingState_v1_<id>",
|
|
310
|
+
"lastExternalReferrer",
|
|
311
|
+
"lastExternalReferrerTime",
|
|
312
|
+
],
|
|
313
|
+
tab: ["escapenavigator:consent-warned:<state>"],
|
|
314
|
+
stripe: true,
|
|
315
|
+
loads: ["google-analytics", "google-ads", "meta"],
|
|
316
|
+
},
|
|
317
|
+
} as const satisfies Record<BookingVendor, BookingStorage>;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Stripe's fraud-prevention cookies, which Stripe.js sets whether or not
|
|
321
|
+
* anything is paid: stripe.com/legal/cookies-policy. Seen live 2026-11-11;
|
|
322
|
+
* `m` lasts two years as Stripe states it, which Chrome caps at 400 days.
|
|
323
|
+
*/
|
|
324
|
+
const STRIPE_ROWS: readonly StoredItem[] = [
|
|
325
|
+
{
|
|
326
|
+
id: "stripe",
|
|
327
|
+
names: ["__stripe_mid", "__stripe_sid"],
|
|
328
|
+
provider: "stripe",
|
|
329
|
+
lasts: year(1),
|
|
330
|
+
consent: "none",
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
id: "stripe-own",
|
|
334
|
+
names: ["m"],
|
|
335
|
+
provider: "stripe",
|
|
336
|
+
domains: ["m.stripe.com"],
|
|
337
|
+
lasts: year(2),
|
|
338
|
+
consent: "none",
|
|
339
|
+
},
|
|
340
|
+
];
|
|
287
341
|
|
|
288
342
|
function loadedVendors(
|
|
289
343
|
analytics: AnalyticsSettings | undefined
|
|
@@ -391,7 +445,35 @@ export function storedItems(
|
|
|
391
445
|
});
|
|
392
446
|
}
|
|
393
447
|
|
|
394
|
-
|
|
448
|
+
// Each widget's own rows, and what it brings: tags it loads, which join
|
|
449
|
+
// the site's own below, and Stripe's cookies, listed once however many.
|
|
450
|
+
const vendors = new Set(loadedVendors(analytics));
|
|
451
|
+
const widgetRows: StoredItem[] = [];
|
|
452
|
+
let stripe = false;
|
|
453
|
+
for (const widget of new Set(booking)) {
|
|
454
|
+
const entry: BookingStorage = BOOKING[widget];
|
|
455
|
+
widgetRows.push(
|
|
456
|
+
{
|
|
457
|
+
id: widget,
|
|
458
|
+
names: entry.kept,
|
|
459
|
+
provider: widget,
|
|
460
|
+
lasts: "kept",
|
|
461
|
+
consent: "none",
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
id: `${widget}-tab`,
|
|
465
|
+
names: entry.tab,
|
|
466
|
+
provider: widget,
|
|
467
|
+
lasts: "tab",
|
|
468
|
+
consent: "none",
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
for (const vendor of entry.loads ?? []) vendors.add(vendor);
|
|
472
|
+
if (entry.stripe) stripe = true;
|
|
473
|
+
}
|
|
474
|
+
if (stripe) widgetRows.push(...STRIPE_ROWS);
|
|
475
|
+
|
|
476
|
+
for (const vendor of vendors) {
|
|
395
477
|
const entry: VendorStorage = CATALOG[vendor];
|
|
396
478
|
rows.push({
|
|
397
479
|
id: vendor,
|
|
@@ -412,23 +494,7 @@ export function storedItems(
|
|
|
412
494
|
}
|
|
413
495
|
}
|
|
414
496
|
|
|
415
|
-
|
|
416
|
-
const entry = BOOKING[widget];
|
|
417
|
-
rows.push({
|
|
418
|
-
id: widget,
|
|
419
|
-
names: entry.kept,
|
|
420
|
-
provider: widget,
|
|
421
|
-
lasts: "kept",
|
|
422
|
-
consent: "none",
|
|
423
|
-
});
|
|
424
|
-
rows.push({
|
|
425
|
-
id: `${widget}-tab`,
|
|
426
|
-
names: entry.tab,
|
|
427
|
-
provider: widget,
|
|
428
|
-
lasts: "tab",
|
|
429
|
-
consent: "none",
|
|
430
|
-
});
|
|
431
|
-
}
|
|
497
|
+
rows.push(...widgetRows);
|
|
432
498
|
|
|
433
499
|
return sort ? rows.toSorted(compareStoredItems) : rows;
|
|
434
500
|
}
|
package/src/analytics/umami.ts
CHANGED
|
@@ -47,6 +47,19 @@ export interface UmamiTracker {
|
|
|
47
47
|
* `false` turns it off, and writes the word rather than omitting it.
|
|
48
48
|
*/
|
|
49
49
|
readonly excludeHash?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Record uncaught exceptions and unhandled promise rejections, grouped into
|
|
52
|
+
* issues in Umami's dashboard. **Defaults to on here.**
|
|
53
|
+
*
|
|
54
|
+
* Umami's own default is off — the tracker reads `config('errors') ===
|
|
55
|
+
* 'true'`. Overridden for the reason `performance` is: a script error on a
|
|
56
|
+
* visitor's phone happens nowhere else, and this is the only place it can
|
|
57
|
+
* be seen. Needs Umami 3.5 on the server; an older one serves a tracker that
|
|
58
|
+
* ignores the attribute, so it is harmless before the upgrade.
|
|
59
|
+
*
|
|
60
|
+
* `false` turns it off, and writes the word rather than omitting it.
|
|
61
|
+
*/
|
|
62
|
+
readonly errors?: boolean;
|
|
50
63
|
/** Honour the browser's Do Not Track setting. */
|
|
51
64
|
readonly doNotTrack?: boolean;
|
|
52
65
|
/**
|
|
@@ -276,7 +289,7 @@ export function umamiScripts(
|
|
|
276
289
|
src: at(umami.tracker?.path ?? "/script.js"),
|
|
277
290
|
attrs: stated({
|
|
278
291
|
...common,
|
|
279
|
-
// The
|
|
292
|
+
// The three defaults lib overrides — see `UmamiTracker`. The
|
|
280
293
|
// others are left to Umami: `auto-track` and `auto-pageview`
|
|
281
294
|
// are already on unless the word `false` appears, so stating
|
|
282
295
|
// them would be bytes that change nothing, and whether to
|
|
@@ -284,6 +297,7 @@ export function umamiScripts(
|
|
|
284
297
|
// this business rather than about this kind of site.
|
|
285
298
|
"data-performance": flag(umami.tracker?.performance ?? true),
|
|
286
299
|
"data-exclude-hash": flag(umami.tracker?.excludeHash ?? true),
|
|
300
|
+
"data-errors": flag(umami.tracker?.errors ?? true),
|
|
287
301
|
"data-exclude-search": flag(umami.tracker?.excludeSearch),
|
|
288
302
|
"data-do-not-track": flag(umami.tracker?.doNotTrack),
|
|
289
303
|
"data-auto-track": flag(umami.tracker?.autoTrack),
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { HTMLAttributes } from "astro/types";
|
|
16
16
|
import type { BookingWidget, BookingWidgetVars } from "../booking/widget.ts";
|
|
17
|
+
import EscapeNavigator from "./EscapeNavigator.astro";
|
|
17
18
|
import FourEscape from "./FourEscape.astro";
|
|
18
19
|
|
|
19
20
|
type Props<W extends BookingWidget = BookingWidget> = HTMLAttributes<"div"> & {
|
|
@@ -22,9 +23,11 @@ type Props<W extends BookingWidget = BookingWidget> = HTMLAttributes<"div"> & {
|
|
|
22
23
|
readonly locale: string;
|
|
23
24
|
/** The widget's vendor's CSS variables, set on its widgets. */
|
|
24
25
|
readonly vars?: BookingWidgetVars<W>;
|
|
26
|
+
/** The site's light or dark look, for a vendor that draws both. */
|
|
27
|
+
readonly colorScheme?: "light" | "dark";
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
const { widget, locale, vars, ...attrs } = Astro.props;
|
|
30
|
+
const { widget, locale, vars, colorScheme, ...attrs } = Astro.props;
|
|
28
31
|
---
|
|
29
32
|
|
|
30
33
|
{widget.vendor === "4escape" && (
|
|
@@ -36,3 +39,11 @@ const { widget, locale, vars, ...attrs } = Astro.props;
|
|
|
36
39
|
vars={vars}
|
|
37
40
|
/>
|
|
38
41
|
)}
|
|
42
|
+
{widget.vendor === "escapenavigator" && (
|
|
43
|
+
<EscapeNavigator
|
|
44
|
+
{...attrs}
|
|
45
|
+
widgetId={widget.widgetId}
|
|
46
|
+
locale={locale}
|
|
47
|
+
colorScheme={colorScheme}
|
|
48
|
+
/>
|
|
49
|
+
)}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* An Escape Navigator schedule: the store's rooms and their free slots,
|
|
4
|
+
* booked in a drawer on the page. See `escape-navigator.ts` for what the
|
|
5
|
+
* widget loads, and how it learns the visitor's consent.
|
|
6
|
+
*
|
|
7
|
+
* `locale` is the page's; `colorScheme` picks the widget's light or dark look,
|
|
8
|
+
* the site's own. Attributes go on the widget's element.
|
|
9
|
+
*
|
|
10
|
+
* ```astro
|
|
11
|
+
* <EscapeNavigator widgetId="W0000000AB" locale={locale} colorScheme="dark" />
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
import type { HTMLAttributes } from "astro/types";
|
|
15
|
+
import {
|
|
16
|
+
checkEscapeNavigator,
|
|
17
|
+
type EscapeNavigatorWidget,
|
|
18
|
+
escapeNavigatorLanguage,
|
|
19
|
+
} from "../booking/escape-navigator.ts";
|
|
20
|
+
|
|
21
|
+
type Props = HTMLAttributes<"div"> &
|
|
22
|
+
EscapeNavigatorWidget & {
|
|
23
|
+
readonly locale: string;
|
|
24
|
+
readonly colorScheme?: "light" | "dark";
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const { widgetId, locale, colorScheme = "light", ...attrs } = Astro.props;
|
|
28
|
+
|
|
29
|
+
checkEscapeNavigator({ widgetId }, Astro.url.pathname);
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<script src="./escape-navigator-consent.ts"></script>
|
|
33
|
+
<div
|
|
34
|
+
{...attrs}
|
|
35
|
+
data-widget-id={widgetId}
|
|
36
|
+
data-widget-type="schedule"
|
|
37
|
+
data-language={escapeNavigatorLanguage(locale)}
|
|
38
|
+
data-theme={colorScheme}
|
|
39
|
+
></div>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ESCAPE_NAVIGATOR_LOADER } from "../booking/escape-navigator.ts";
|
|
2
|
+
import { appendScript } from "./append-script.ts";
|
|
3
|
+
import { CONSENT_EVENT, type ConsentChoices, readConsent } from "./consent.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Escape Navigator's consent, from the visitor's answer on this site: its
|
|
7
|
+
* analytics tags with ours, its ad tags with marketing. Denied until answered.
|
|
8
|
+
*/
|
|
9
|
+
function escapeNavigatorConsent(choices: ConsentChoices | undefined) {
|
|
10
|
+
return {
|
|
11
|
+
analytics: choices?.analytics === "granted",
|
|
12
|
+
ads: choices?.marketing === "granted",
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface EscapeNavigatorGlobal {
|
|
17
|
+
__pendingConsent?: { analytics: boolean; ads: boolean };
|
|
18
|
+
setConsent?: (consent: { analytics: boolean; ads: boolean }) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const host = window as unknown as { EscapeNavigator?: EscapeNavigatorGlobal };
|
|
22
|
+
|
|
23
|
+
// Before the loader runs, which reads the pending answer as it starts: its
|
|
24
|
+
// tags never load on an answer the visitor has not given.
|
|
25
|
+
host.EscapeNavigator ??= {};
|
|
26
|
+
host.EscapeNavigator.__pendingConsent = escapeNavigatorConsent(readConsent());
|
|
27
|
+
|
|
28
|
+
addEventListener(CONSENT_EVENT, (event) => {
|
|
29
|
+
const consent = escapeNavigatorConsent(
|
|
30
|
+
(event as CustomEvent<ConsentChoices>).detail
|
|
31
|
+
);
|
|
32
|
+
const navigator = host.EscapeNavigator ?? {};
|
|
33
|
+
if (navigator.setConsent) navigator.setConsent(consent);
|
|
34
|
+
else navigator.__pendingConsent = consent;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Once per page, however many widgets: this module runs once.
|
|
38
|
+
appendScript(ESCAPE_NAVIGATOR_LOADER);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { invalidId, UUID } from "../analytics/ids.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Escape Navigator's booking widget: a store's schedule, every room's free
|
|
5
|
+
* slots, booked in a drawer on the page and paid through Stripe.
|
|
6
|
+
*
|
|
7
|
+
* Plain markup and one loader script. The widget runs the store's own Google
|
|
8
|
+
* Analytics, Google Ads and Meta tags, set up in Escape Navigator, and sends
|
|
9
|
+
* the visitor's email and phone to Google before a purchase (Enhanced
|
|
10
|
+
* Conversions). It loads them only once told the visitor's consent: the
|
|
11
|
+
* component tells it the answer given on this site, before the loader runs
|
|
12
|
+
* and on every change. Stripe's fraud check sets its cookies without asking.
|
|
13
|
+
* All of it is in `storedItems`.
|
|
14
|
+
*
|
|
15
|
+
* The embed is Escape Navigator's documented one. The consent call
|
|
16
|
+
* (`EscapeNavigator.setConsent`) is not documented: it is its loader's own,
|
|
17
|
+
* read 2026-11-11, and seen to hold the tags back until consent is given.
|
|
18
|
+
*
|
|
19
|
+
* @see https://docs.escapenavigator.com/guides-and-tips/use-widgets-like-a-pro
|
|
20
|
+
* @see https://docs.escapenavigator.com/guides-and-tips/google-analytics-4
|
|
21
|
+
*/
|
|
22
|
+
export interface EscapeNavigatorWidget {
|
|
23
|
+
/**
|
|
24
|
+
* The widget's id, from its card in Escape Navigator: a UUID, or `W` and
|
|
25
|
+
* nine capitals and digits as older widgets have. A widget for one room is
|
|
26
|
+
* a widget of its own, with its own id.
|
|
27
|
+
*/
|
|
28
|
+
readonly widgetId: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Escape Navigator's own CSS variables: none it documents, so none taken. */
|
|
32
|
+
export type EscapeNavigatorVars = Readonly<Partial<Record<string, never>>>;
|
|
33
|
+
|
|
34
|
+
/** The loader every Escape Navigator widget on a page shares. */
|
|
35
|
+
export const ESCAPE_NAVIGATOR_LOADER =
|
|
36
|
+
"https://widget.escapenavigator.com/widget-loader.js";
|
|
37
|
+
|
|
38
|
+
/** The older form of widget id; newer ones are UUIDs. */
|
|
39
|
+
const SHORT_ID = /^W[A-Z0-9]{9}$/;
|
|
40
|
+
|
|
41
|
+
/** Fails the build on an id that is not a widget's: it would render nothing. */
|
|
42
|
+
export function checkEscapeNavigator(
|
|
43
|
+
widget: EscapeNavigatorWidget,
|
|
44
|
+
at: string
|
|
45
|
+
): void {
|
|
46
|
+
if (!(UUID.test(widget.widgetId) || SHORT_ID.test(widget.widgetId))) {
|
|
47
|
+
invalidId(
|
|
48
|
+
at,
|
|
49
|
+
`Escape Navigator widget id "${widget.widgetId}" is neither a UUID nor "W" and nine capitals and digits, so it would render nothing. Copy it from the widget's card in Escape Navigator.`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The widget's language for a page's locale: its language subtag. */
|
|
55
|
+
export function escapeNavigatorLanguage(locale: string): string {
|
|
56
|
+
return new Intl.Locale(locale).language;
|
|
57
|
+
}
|
package/src/booking/widget.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EscapeNavigatorVars,
|
|
3
|
+
EscapeNavigatorWidget,
|
|
4
|
+
} from "./escape-navigator.ts";
|
|
1
5
|
import type { FourEscapeVars, FourEscapeWidget } from "./four-escape.ts";
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* A booking widget a site embeds, by its vendor: what a site's config names,
|
|
5
9
|
* and what picks the component that draws it.
|
|
6
10
|
*/
|
|
7
|
-
export type BookingWidget =
|
|
11
|
+
export type BookingWidget =
|
|
12
|
+
| ({ readonly vendor: "4escape" } & FourEscapeWidget)
|
|
13
|
+
| ({ readonly vendor: "escapenavigator" } & EscapeNavigatorWidget);
|
|
8
14
|
|
|
9
15
|
/** Who makes a booking widget: what `storedItems` takes for their rows. */
|
|
10
16
|
export type BookingVendor = BookingWidget["vendor"];
|
|
@@ -12,6 +18,7 @@ export type BookingVendor = BookingWidget["vendor"];
|
|
|
12
18
|
/** Each vendor's CSS variables, as a page sets them on its widgets. */
|
|
13
19
|
export interface BookingVendorVars {
|
|
14
20
|
readonly "4escape": FourEscapeVars;
|
|
21
|
+
readonly escapenavigator: EscapeNavigatorVars;
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
/** The CSS variables a widget takes: its own vendor's, and no other's. */
|
package/src/index.ts
CHANGED
|
@@ -55,6 +55,13 @@ export {
|
|
|
55
55
|
storedItems,
|
|
56
56
|
withPurposes,
|
|
57
57
|
} from "./analytics/storage.ts";
|
|
58
|
+
export {
|
|
59
|
+
checkEscapeNavigator,
|
|
60
|
+
ESCAPE_NAVIGATOR_LOADER,
|
|
61
|
+
type EscapeNavigatorVars,
|
|
62
|
+
type EscapeNavigatorWidget,
|
|
63
|
+
escapeNavigatorLanguage,
|
|
64
|
+
} from "./booking/escape-navigator.ts";
|
|
58
65
|
export {
|
|
59
66
|
checkFourEscape,
|
|
60
67
|
FOUR_ESCAPE_LANGUAGES,
|