@escape-game-over/atlas 0.1.116 → 0.1.117
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/README.md +1 -1
- package/package.json +2 -2
- package/src/analytics/ids.ts +1 -13
- package/src/analytics/index.ts +18 -18
- package/src/analytics/vendor-settings.ts +162 -0
- package/src/astro/axon-event.ts +2 -4
- package/src/astro/axon-pixel.ts +3 -6
- package/src/astro/clarity-event.ts +2 -4
- package/src/astro/clarity.ts +3 -6
- package/src/astro/consent.ts +3 -6
- package/src/astro/drip-event.ts +2 -4
- package/src/astro/drip.ts +2 -7
- package/src/astro/escape-navigator-consent.ts +2 -6
- package/src/astro/escapeall-calendar.ts +2 -5
- package/src/astro/google-event.ts +3 -7
- package/src/astro/google-map.ts +3 -5
- package/src/astro/meta-event.ts +2 -4
- package/src/astro/meta-pixel.ts +4 -8
- package/src/astro/snap-event.ts +2 -4
- package/src/astro/snap-pixel.ts +3 -6
- package/src/astro/tiktok-event.ts +2 -4
- package/src/astro/tiktok-pixel.ts +70 -39
- package/src/astro/umami-event.ts +2 -6
- package/src/astro/vendors.ts +64 -0
- package/src/index.ts +1 -6
- package/src/routes/family.ts +135 -3
- package/src/site/define.ts +13 -9
- package/src/warn.ts +6 -6
- package/src/analytics/axon-pixel.ts +0 -24
- package/src/analytics/clarity.ts +0 -29
- package/src/analytics/drip.ts +0 -20
- package/src/analytics/meta-pixel.ts +0 -29
- package/src/analytics/snap-pixel.ts +0 -28
- package/src/analytics/tiktok-pixel.ts +0 -29
package/README.md
CHANGED
|
@@ -398,7 +398,7 @@ src/ the package — see the export map above for what is reachable
|
|
|
398
398
|
meta/ head tags: canonical, hreflang, Open Graph, Twitter, robots
|
|
399
399
|
jsonld/ the @graph — one file per node, each linked to Google's docs
|
|
400
400
|
i18n/ defineMessages(), t(), placeholder extraction
|
|
401
|
-
routes/ defineRoutes(),
|
|
401
|
+
routes/ defineRoutes(), route families, merging and collisions
|
|
402
402
|
analytics/ Umami and Google, the Consent Mode defaults, and whether a
|
|
403
403
|
visitor has to be asked anything at all
|
|
404
404
|
astro/ the only framework-aware code, plus the one component
|
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.117",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Typed, data-driven machinery for static multi-locale, multi-deployment Astro sites.",
|
|
6
6
|
"private": false,
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@biomejs/biome": "2.6.7",
|
|
68
68
|
"@types/node": "26.6.6",
|
|
69
69
|
"@vitest/coverage-istanbul": "5.1.0",
|
|
70
|
-
"astro": "7.4.
|
|
70
|
+
"astro": "7.4.12",
|
|
71
71
|
"typescript": "6.0.3",
|
|
72
72
|
"vitest": "5.1.0"
|
|
73
73
|
}
|
package/src/analytics/ids.ts
CHANGED
|
@@ -24,18 +24,6 @@ export function invalidId(at: string, message: string): never {
|
|
|
24
24
|
throw new Error(`${at}: ${message}`);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/** A
|
|
28
|
-
export const META_PIXEL_ID = /^\d{15,16}$/;
|
|
29
|
-
|
|
30
|
-
/** A TikTok pixel id: 20 capital letters and digits. */
|
|
31
|
-
export const TIKTOK_PIXEL_ID = /^[A-Z0-9]{20}$/;
|
|
32
|
-
|
|
33
|
-
/** A Drip account id: digits. */
|
|
34
|
-
export const DRIP_ACCOUNT_ID = /^\d+$/;
|
|
35
|
-
|
|
36
|
-
/** A Clarity project id: 10 lowercase letters and digits. */
|
|
37
|
-
export const CLARITY_PROJECT_ID = /^[a-z0-9]{10}$/;
|
|
38
|
-
|
|
39
|
-
/** A UUID, as Umami, Snap and Axon issue them. */
|
|
27
|
+
/** A UUID, as Umami, Snap, Axon and the booking vendors issue them. */
|
|
40
28
|
export const UUID =
|
|
41
29
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
package/src/analytics/index.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import type { MetaTag } from "../meta/tag.ts";
|
|
2
2
|
import type { HttpsUrl } from "../url.ts";
|
|
3
|
-
import { type AxonPixelSettings, checkAxonPixel } from "./axon-pixel.ts";
|
|
4
|
-
import { type ClaritySettings, checkClarity } from "./clarity.ts";
|
|
5
|
-
import { checkDrip, type DripSettings } from "./drip.ts";
|
|
6
3
|
import { type GoogleSettings, googleEmits, googleScripts } from "./google.ts";
|
|
7
|
-
import { checkMetaPixel, type MetaPixelSettings } from "./meta-pixel.ts";
|
|
8
|
-
import { checkSnapPixel, type SnapPixelSettings } from "./snap-pixel.ts";
|
|
9
4
|
import type { AnalyticsTags } from "./tags.ts";
|
|
10
|
-
import { checkTikTokPixel, type TikTokPixelSettings } from "./tiktok-pixel.ts";
|
|
11
5
|
import {
|
|
12
6
|
checkUmamiDomains,
|
|
13
7
|
type UmamiSettings,
|
|
14
8
|
umamiScripts,
|
|
15
9
|
} from "./umami.ts";
|
|
10
|
+
import {
|
|
11
|
+
type AxonPixelSettings,
|
|
12
|
+
type ClaritySettings,
|
|
13
|
+
checkVendorIds,
|
|
14
|
+
type DripSettings,
|
|
15
|
+
type MetaPixelSettings,
|
|
16
|
+
type SnapPixelSettings,
|
|
17
|
+
type TikTokPixelSettings,
|
|
18
|
+
} from "./vendor-settings.ts";
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Analytics, as head tags.
|
|
@@ -29,9 +32,6 @@ import {
|
|
|
29
32
|
* project states ids; lib decides everything that follows from them.
|
|
30
33
|
*/
|
|
31
34
|
|
|
32
|
-
export type { AxonPixelSettings } from "./axon-pixel.ts";
|
|
33
|
-
export type { ClaritySettings } from "./clarity.ts";
|
|
34
|
-
export type { DripSettings } from "./drip.ts";
|
|
35
35
|
export {
|
|
36
36
|
CONSENT_UPDATE_GLOBAL,
|
|
37
37
|
type ConsentDefaults,
|
|
@@ -39,15 +39,20 @@ export {
|
|
|
39
39
|
type GoogleSettings,
|
|
40
40
|
} from "./google.ts";
|
|
41
41
|
export type { GoogleTagId, NumericId, TagManagerId, Uuid } from "./ids.ts";
|
|
42
|
-
export type { MetaPixelSettings } from "./meta-pixel.ts";
|
|
43
|
-
export type { SnapPixelSettings } from "./snap-pixel.ts";
|
|
44
42
|
export type { AnalyticsTags } from "./tags.ts";
|
|
45
|
-
export type { TikTokPixelSettings } from "./tiktok-pixel.ts";
|
|
46
43
|
export type {
|
|
47
44
|
UmamiReplay,
|
|
48
45
|
UmamiSettings,
|
|
49
46
|
UmamiTracker,
|
|
50
47
|
} from "./umami.ts";
|
|
48
|
+
export type {
|
|
49
|
+
AxonPixelSettings,
|
|
50
|
+
ClaritySettings,
|
|
51
|
+
DripSettings,
|
|
52
|
+
MetaPixelSettings,
|
|
53
|
+
SnapPixelSettings,
|
|
54
|
+
TikTokPixelSettings,
|
|
55
|
+
} from "./vendor-settings.ts";
|
|
51
56
|
|
|
52
57
|
/** What a project may switch on. A vendor is a key here. */
|
|
53
58
|
export interface AnalyticsSettings {
|
|
@@ -85,12 +90,7 @@ export function analyticsScripts(
|
|
|
85
90
|
if (analytics === undefined) return { head: [], body: [] };
|
|
86
91
|
|
|
87
92
|
checkUmamiDomains(analytics.umami, origin);
|
|
88
|
-
|
|
89
|
-
checkTikTokPixel(analytics.tiktok, origin);
|
|
90
|
-
checkSnapPixel(analytics.snapchat, origin);
|
|
91
|
-
checkAxonPixel(analytics.axon, origin);
|
|
92
|
-
checkDrip(analytics.drip, origin);
|
|
93
|
-
checkClarity(analytics.clarity, origin);
|
|
93
|
+
checkVendorIds(analytics, origin);
|
|
94
94
|
const google = googleScripts(analytics.google, origin);
|
|
95
95
|
return {
|
|
96
96
|
head: [...google.head, ...umamiScripts(analytics.umami)],
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { NonEmpty } from "../types.ts";
|
|
2
|
+
import { invalidId, type NumericId, UUID, type Uuid } from "./ids.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Meta's pixel. Marketing: it loads only once a visitor grants that.
|
|
6
|
+
*
|
|
7
|
+
* @see https://developers.facebook.com/docs/meta-pixel/get-started
|
|
8
|
+
* @see https://developers.facebook.com/docs/meta-pixel/reference
|
|
9
|
+
* @see https://developers.facebook.com/docs/meta-pixel/implementation/gdpr
|
|
10
|
+
*/
|
|
11
|
+
export interface MetaPixelSettings {
|
|
12
|
+
/** From Events Manager: 15 or 16 digits. Several share one script. */
|
|
13
|
+
readonly pixelIds: NonEmpty<NumericId>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* TikTok's pixel. Marketing: it loads only once a visitor grants that.
|
|
18
|
+
*
|
|
19
|
+
* @see https://ads.tiktok.com/help/article/get-started-pixel
|
|
20
|
+
* @see https://ads.tiktok.com/help/article/standard-events-parameters
|
|
21
|
+
* @see https://ads.tiktok.com/help/article/how-to-use-cookies-with-tiktok-pixel
|
|
22
|
+
*/
|
|
23
|
+
export interface TikTokPixelSettings {
|
|
24
|
+
/** From TikTok Events Manager: 20 capital letters and digits. */
|
|
25
|
+
readonly pixelIds: NonEmpty<string>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Snapchat's pixel. Marketing: it loads only once a visitor grants that.
|
|
30
|
+
*
|
|
31
|
+
* @see https://businesshelp.snapchat.com/s/article/pixel-direct-implementation
|
|
32
|
+
* @see https://forbusiness.snapchat.com/en-US/blog/the-snap-pixel-how-it-works-and-how-to-install-it
|
|
33
|
+
*/
|
|
34
|
+
export interface SnapPixelSettings {
|
|
35
|
+
/** From Snap's Events Manager: a UUID. */
|
|
36
|
+
readonly pixelIds: NonEmpty<Uuid>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* AppLovin's Axon pixel. Marketing: it loads only once a visitor grants that.
|
|
41
|
+
*
|
|
42
|
+
* @see https://support.applovin.com/en/growth/promoting-your-websites/axon-pixel-integration/axon-pixel-native-js
|
|
43
|
+
* @see https://support.applovin.com/en/growth/promoting-your-websites/axon-pixel-integration/events-and-objects
|
|
44
|
+
*/
|
|
45
|
+
export interface AxonPixelSettings {
|
|
46
|
+
/** From the Axon dashboard: a UUID. */
|
|
47
|
+
readonly eventKey: Uuid;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Drip's site tracking. Marketing: it loads only once a visitor grants that.
|
|
52
|
+
*
|
|
53
|
+
* @see https://help.drip.com/hc/en-us/articles/4424702539789-Install-Your-JavaScript-Snippet
|
|
54
|
+
*/
|
|
55
|
+
export interface DripSettings {
|
|
56
|
+
/** From Drip's Site Setup: digits, the `_dcs.account` of its snippet. */
|
|
57
|
+
readonly accountId: NumericId;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Microsoft Clarity. It has a cookieless mode, so like Google it loads for
|
|
62
|
+
* every visitor: without consent it records each page view on its own and sets
|
|
63
|
+
* no cookie, and the visitor's answer switches its cookies on or off.
|
|
64
|
+
*
|
|
65
|
+
* @see https://learn.microsoft.com/en-us/clarity/setup-and-installation/clarity-setup
|
|
66
|
+
* @see https://learn.microsoft.com/en-us/clarity/setup-and-installation/consent-mode
|
|
67
|
+
* @see https://learn.microsoft.com/en-us/clarity/setup-and-installation/clarity-consent-api-v2
|
|
68
|
+
*/
|
|
69
|
+
export interface ClaritySettings {
|
|
70
|
+
/** From the Clarity project's settings: 10 lowercase letters and digits. */
|
|
71
|
+
readonly projectId: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** What these vendors' checks read: their slice of `AnalyticsSettings`. */
|
|
75
|
+
interface VendorSettings {
|
|
76
|
+
readonly meta?: MetaPixelSettings;
|
|
77
|
+
readonly tiktok?: TikTokPixelSettings;
|
|
78
|
+
readonly snapchat?: SnapPixelSettings;
|
|
79
|
+
readonly axon?: AxonPixelSettings;
|
|
80
|
+
readonly drip?: DripSettings;
|
|
81
|
+
readonly clarity?: ClaritySettings;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** One vendor's id: where it is set, its shape, and where to copy it from. */
|
|
85
|
+
interface IdCheck {
|
|
86
|
+
readonly ids: (settings: VendorSettings) => readonly string[];
|
|
87
|
+
/** What the id is called, as the vendor calls it. */
|
|
88
|
+
readonly name: string;
|
|
89
|
+
readonly shape: RegExp;
|
|
90
|
+
/** The shape in words, for the error. */
|
|
91
|
+
readonly is: string;
|
|
92
|
+
readonly from: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Each vendor's id check: one row each, read in one loop. Only the shape, the
|
|
97
|
+
* words and the place to copy it from differ between vendors.
|
|
98
|
+
*/
|
|
99
|
+
const ID_CHECKS: readonly IdCheck[] = [
|
|
100
|
+
{
|
|
101
|
+
ids: (settings) => settings.meta?.pixelIds ?? [],
|
|
102
|
+
name: "Meta pixel id",
|
|
103
|
+
shape: /^\d{15,16}$/,
|
|
104
|
+
is: "15 or 16 digits",
|
|
105
|
+
from: "Events Manager",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
ids: (settings) => settings.tiktok?.pixelIds ?? [],
|
|
109
|
+
name: "TikTok pixel id",
|
|
110
|
+
shape: /^[A-Z0-9]{20}$/,
|
|
111
|
+
is: "20 capital letters and digits",
|
|
112
|
+
from: "TikTok Events Manager",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
ids: (settings) => settings.snapchat?.pixelIds ?? [],
|
|
116
|
+
name: "Snap pixel id",
|
|
117
|
+
shape: UUID,
|
|
118
|
+
is: "a UUID",
|
|
119
|
+
from: "Snap's Events Manager",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
ids: (settings) =>
|
|
123
|
+
settings.axon === undefined ? [] : [settings.axon.eventKey],
|
|
124
|
+
name: "Axon event key",
|
|
125
|
+
shape: UUID,
|
|
126
|
+
is: "a UUID",
|
|
127
|
+
from: "the Axon dashboard",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
ids: (settings) =>
|
|
131
|
+
settings.drip === undefined ? [] : [settings.drip.accountId],
|
|
132
|
+
name: "Drip account id",
|
|
133
|
+
shape: /^\d+$/,
|
|
134
|
+
is: "all digits",
|
|
135
|
+
from: "Drip's Site Setup",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
ids: (settings) =>
|
|
139
|
+
settings.clarity === undefined ? [] : [settings.clarity.projectId],
|
|
140
|
+
name: "Clarity project id",
|
|
141
|
+
shape: /^[a-z0-9]{10}$/,
|
|
142
|
+
is: "10 lowercase letters and digits",
|
|
143
|
+
from: "the Clarity project's settings",
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Fails the build on an id that is not what its vendor issues: configured,
|
|
149
|
+
* emitted, and reporting nowhere is the failure it prevents.
|
|
150
|
+
*/
|
|
151
|
+
export function checkVendorIds(settings: VendorSettings, at: string): void {
|
|
152
|
+
for (const check of ID_CHECKS) {
|
|
153
|
+
for (const id of check.ids(settings)) {
|
|
154
|
+
if (!check.shape.test(id)) {
|
|
155
|
+
invalidId(
|
|
156
|
+
at,
|
|
157
|
+
`${check.name} "${id}" is not ${check.is}, so it would report nowhere. Copy it from ${check.from}.`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
package/src/astro/axon-event.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CurrencyCode } from "../currency.ts";
|
|
2
2
|
import { readConsent } from "./consent.ts";
|
|
3
|
+
import { vendors } from "./vendors.ts";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Axon's events that a site without a cart sends. The cart and purchase events
|
|
@@ -15,9 +16,6 @@ export interface AxonEventData {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/** What `axon-pixel.ts` defines once marketing is granted. */
|
|
18
|
-
interface AxonWindow {
|
|
19
|
-
axon?: (...args: unknown[]) => void;
|
|
20
|
-
}
|
|
21
19
|
|
|
22
20
|
/**
|
|
23
21
|
* Records an event with Axon's pixel — a lead sent, an account made.
|
|
@@ -28,7 +26,7 @@ interface AxonWindow {
|
|
|
28
26
|
*/
|
|
29
27
|
export function axonEvent(event: AxonEvent, data?: AxonEventData): void {
|
|
30
28
|
if (readConsent()?.marketing !== "granted") return;
|
|
31
|
-
const axon = (
|
|
29
|
+
const axon = vendors().axon;
|
|
32
30
|
if (data === undefined) axon?.("track", event);
|
|
33
31
|
else axon?.("track", event, data);
|
|
34
32
|
}
|
package/src/astro/axon-pixel.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { appendScript } from "./append-script.ts";
|
|
2
2
|
import type { ConsentGated } from "./consent-gate.ts";
|
|
3
|
+
import { vendors } from "./vendors.ts";
|
|
3
4
|
|
|
4
5
|
/** Axon's `axon`: a queue until its scripts arrive and replay it. */
|
|
5
|
-
interface Axon {
|
|
6
|
+
export interface Axon {
|
|
6
7
|
(...args: unknown[]): void;
|
|
7
8
|
performOperation?: (...args: unknown[]) => void;
|
|
8
9
|
operationQueue: unknown[];
|
|
@@ -10,10 +11,6 @@ interface Axon {
|
|
|
10
11
|
eventKey: string;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
interface PixelWindow {
|
|
14
|
-
axon?: Axon;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
14
|
const SCRIPTS = [
|
|
18
15
|
"https://s.axon.ai/pixel.js",
|
|
19
16
|
"https://res4.applovin.com/p/l/loader.iife.js",
|
|
@@ -21,7 +18,7 @@ const SCRIPTS = [
|
|
|
21
18
|
|
|
22
19
|
/** Axon's own base code, as it defines `axon` before the scripts load. */
|
|
23
20
|
function defineAxon(eventKey: string): Axon {
|
|
24
|
-
const global =
|
|
21
|
+
const global = vendors();
|
|
25
22
|
if (global.axon !== undefined) return global.axon;
|
|
26
23
|
// `arguments` is the list of whatever a call passed, which every regular
|
|
27
24
|
// `function` gets without declaring it (an arrow function does not). The
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DeclaredEvent } from "./event-data.ts";
|
|
2
|
+
import { vendors } from "./vendors.ts";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The project's Clarity events. Clarity carries a name and nothing else, so
|
|
@@ -17,9 +18,6 @@ import type { DeclaredEvent } from "./event-data.ts";
|
|
|
17
18
|
export interface ClarityEvents {}
|
|
18
19
|
|
|
19
20
|
/** What `clarity.ts` defines once the page has loaded. */
|
|
20
|
-
interface ClarityWindow {
|
|
21
|
-
clarity?: (...args: unknown[]) => void;
|
|
22
|
-
}
|
|
23
21
|
|
|
24
22
|
/**
|
|
25
23
|
* Marks the current Clarity recording with a named event — a form sent, a
|
|
@@ -29,5 +27,5 @@ interface ClarityWindow {
|
|
|
29
27
|
* sets no cookie. Dropped before Clarity loads, or where it is not configured.
|
|
30
28
|
*/
|
|
31
29
|
export function clarityEvent(name: DeclaredEvent<ClarityEvents>): void {
|
|
32
|
-
(
|
|
30
|
+
vendors().clarity?.("event", name);
|
|
33
31
|
}
|
package/src/astro/clarity.ts
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { appendScript } from "./append-script.ts";
|
|
2
2
|
import type { ConsentChoices } from "./consent.ts";
|
|
3
|
+
import { vendors } from "./vendors.ts";
|
|
3
4
|
|
|
4
5
|
/** Clarity's `clarity`: a queue until its script arrives and replays it. */
|
|
5
|
-
interface Clarity {
|
|
6
|
+
export interface Clarity {
|
|
6
7
|
(...args: unknown[]): void;
|
|
7
8
|
q?: unknown[];
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
interface ClarityWindow {
|
|
11
|
-
clarity?: Clarity;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
11
|
/** Clarity's own base code, as it defines `clarity` before the script loads. */
|
|
15
12
|
function defineClarity(): Clarity {
|
|
16
|
-
const global =
|
|
13
|
+
const global = vendors();
|
|
17
14
|
if (global.clarity !== undefined) return global.clarity;
|
|
18
15
|
// `arguments` is the list of whatever a call passed, which every regular
|
|
19
16
|
// `function` gets without declaring it (an arrow function does not). The
|
package/src/astro/consent.ts
CHANGED
|
@@ -12,6 +12,8 @@ export type {
|
|
|
12
12
|
ConsentChoices,
|
|
13
13
|
} from "../analytics/consent-storage.ts";
|
|
14
14
|
|
|
15
|
+
import { vendors } from "./vendors.ts";
|
|
16
|
+
|
|
15
17
|
/**
|
|
16
18
|
* The browser half of consent: remembering an answer, expiring it, and handing
|
|
17
19
|
* it to Google.
|
|
@@ -120,12 +122,7 @@ export function recordConsent(choices: ConsentChoices): void {
|
|
|
120
122
|
* copy of that list.
|
|
121
123
|
*/
|
|
122
124
|
export function applyConsent(choices: ConsentChoices): void {
|
|
123
|
-
const update = (
|
|
124
|
-
window as unknown as Record<
|
|
125
|
-
string,
|
|
126
|
-
((c: ConsentChoices) => void) | undefined
|
|
127
|
-
>
|
|
128
|
-
)[CONSENT_UPDATE_GLOBAL];
|
|
125
|
+
const update = vendors()[CONSENT_UPDATE_GLOBAL];
|
|
129
126
|
// Only the categories, so a record's date does not ride along.
|
|
130
127
|
update?.({ analytics: choices.analytics, marketing: choices.marketing });
|
|
131
128
|
}
|
package/src/astro/drip-event.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readConsent } from "./consent.ts";
|
|
2
2
|
import type { DeclaredEvent, EventDataArgs } from "./event-data.ts";
|
|
3
|
+
import { vendors } from "./vendors.ts";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The project's Drip events — the actions its workflows start on — each with
|
|
@@ -18,9 +19,6 @@ import type { DeclaredEvent, EventDataArgs } from "./event-data.ts";
|
|
|
18
19
|
export interface DripEvents {}
|
|
19
20
|
|
|
20
21
|
/** What `drip.ts` defines once marketing is granted. */
|
|
21
|
-
interface DripWindow {
|
|
22
|
-
_dcq?: unknown[][];
|
|
23
|
-
}
|
|
24
22
|
|
|
25
23
|
/**
|
|
26
24
|
* Records an event with Drip, to start a workflow — a lead sent, a voucher
|
|
@@ -38,7 +36,7 @@ export function dripEvent<E extends DeclaredEvent<DripEvents>>(
|
|
|
38
36
|
>
|
|
39
37
|
): void {
|
|
40
38
|
if (readConsent()?.marketing !== "granted") return;
|
|
41
|
-
const queue = (
|
|
39
|
+
const queue = vendors()._dcq;
|
|
42
40
|
if (properties === undefined) queue?.push(["track", action]);
|
|
43
41
|
else queue?.push(["track", action, properties]);
|
|
44
42
|
}
|
package/src/astro/drip.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { appendScript } from "./append-script.ts";
|
|
2
2
|
import type { ConsentGated } from "./consent-gate.ts";
|
|
3
|
-
|
|
4
|
-
/** Drip's globals: a queue of calls, and the account its script reports to. */
|
|
5
|
-
interface DripWindow {
|
|
6
|
-
_dcq?: unknown[][];
|
|
7
|
-
_dcs?: { account?: string };
|
|
8
|
-
}
|
|
3
|
+
import { vendors } from "./vendors.ts";
|
|
9
4
|
|
|
10
5
|
/**
|
|
11
6
|
* Drip, loaded on the first grant. Its script records visits by itself, so
|
|
@@ -19,7 +14,7 @@ export function drip(accountId: string): ConsentGated {
|
|
|
19
14
|
grant: () => {
|
|
20
15
|
if (loaded) return;
|
|
21
16
|
loaded = true;
|
|
22
|
-
const global =
|
|
17
|
+
const global = vendors();
|
|
23
18
|
global._dcq ??= [];
|
|
24
19
|
global._dcs ??= {};
|
|
25
20
|
global._dcs.account = accountId;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ESCAPE_NAVIGATOR_LOADER } from "../booking/escape-navigator.ts";
|
|
2
2
|
import { appendScript } from "./append-script.ts";
|
|
3
3
|
import { CONSENT_EVENT, type ConsentChoices, readConsent } from "./consent.ts";
|
|
4
|
+
import { vendors } from "./vendors.ts";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Escape Navigator's consent, from the visitor's answer on this site: its
|
|
@@ -13,12 +14,7 @@ function escapeNavigatorConsent(choices: ConsentChoices | undefined) {
|
|
|
13
14
|
};
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
|
|
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 };
|
|
17
|
+
const host = vendors();
|
|
22
18
|
|
|
23
19
|
// Before the loader runs, which reads the pending answer as it starts: its
|
|
24
20
|
// tags never load on an answer the visitor has not given.
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { appendScript } from "./append-script.ts";
|
|
2
|
+
import { vendors } from "./vendors.ts";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Draws each EscapeAll calendar on the page, once EscapeAll's script has
|
|
5
6
|
* loaded: the call its store scripts make, with the settings the component
|
|
6
7
|
* wrote on the element.
|
|
7
8
|
*/
|
|
8
|
-
|
|
9
|
-
// calendar is named by its id, not handed over as an element.
|
|
10
|
-
type Calendar = (selector: string, settings: Record<string, unknown>) => void;
|
|
11
|
-
|
|
12
|
-
const host = window as unknown as { GetBookingCalendra?: Calendar };
|
|
9
|
+
const host = vendors();
|
|
13
10
|
|
|
14
11
|
const elements = [...document.querySelectorAll("[data-escapeall-company]")];
|
|
15
12
|
const [first] = elements;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
GOOGLE_EVENT_GLOBAL,
|
|
3
|
-
type GoogleEventSink,
|
|
4
|
-
} from "../analytics/google.ts";
|
|
1
|
+
import { GOOGLE_EVENT_GLOBAL } from "../analytics/google.ts";
|
|
5
2
|
import type { CurrencyCode } from "../currency.ts";
|
|
6
3
|
import type { DeclaredEvent, EventDataArgs } from "./event-data.ts";
|
|
4
|
+
import { vendors } from "./vendors.ts";
|
|
7
5
|
|
|
8
6
|
/** GA4's recommended events that carry items and revenue. */
|
|
9
7
|
const ECOMMERCE_EVENTS = [
|
|
@@ -110,9 +108,7 @@ export function googleEvent<E extends GoogleEventName>(
|
|
|
110
108
|
event: E,
|
|
111
109
|
...[data]: GoogleEventArgs<E>
|
|
112
110
|
): void {
|
|
113
|
-
const send = (
|
|
114
|
-
window as unknown as Record<string, GoogleEventSink | undefined>
|
|
115
|
-
)[GOOGLE_EVENT_GLOBAL];
|
|
111
|
+
const send = vendors()[GOOGLE_EVENT_GLOBAL];
|
|
116
112
|
send?.(
|
|
117
113
|
event,
|
|
118
114
|
data,
|
package/src/astro/google-map.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { reportDevError } from "./dev-log.ts";
|
|
|
6
6
|
import { element } from "./element.ts";
|
|
7
7
|
import { triggered } from "./load-script.ts";
|
|
8
8
|
import { data } from "./ref.ts";
|
|
9
|
+
import { GOOGLE_MAPS_CALLBACK, vendors } from "./vendors.ts";
|
|
9
10
|
|
|
10
11
|
/** What `GoogleMap.astro` hands the element, serialised. */
|
|
11
12
|
export interface GoogleMapConfig {
|
|
@@ -27,20 +28,17 @@ export interface GoogleMapConfig {
|
|
|
27
28
|
}[];
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
const CALLBACK = "__atlasGoogleMaps";
|
|
31
|
-
|
|
32
31
|
let loading: Promise<typeof google.maps> | undefined;
|
|
33
32
|
|
|
34
33
|
/** The Maps JavaScript API, fetched once per page however many maps ask. */
|
|
35
34
|
export function loadGoogleMaps(apiKey: string): Promise<typeof google.maps> {
|
|
36
35
|
loading ??= new Promise((resolve, reject) => {
|
|
37
|
-
(
|
|
38
|
-
resolve(google.maps);
|
|
36
|
+
vendors()[GOOGLE_MAPS_CALLBACK] = () => resolve(google.maps);
|
|
39
37
|
const params = {
|
|
40
38
|
key: apiKey,
|
|
41
39
|
v: "weekly",
|
|
42
40
|
loading: "async",
|
|
43
|
-
callback:
|
|
41
|
+
callback: GOOGLE_MAPS_CALLBACK,
|
|
44
42
|
};
|
|
45
43
|
appendScript(
|
|
46
44
|
`https://maps.googleapis.com/maps/api/js?${new URLSearchParams(params)}`
|
package/src/astro/meta-event.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CurrencyCode } from "../currency.ts";
|
|
2
2
|
import type { DeclaredEvent, EventDataArgs } from "./event-data.ts";
|
|
3
|
+
import { vendors } from "./vendors.ts";
|
|
3
4
|
|
|
4
5
|
/** Meta's standard events, which its ad optimisation understands by name. */
|
|
5
6
|
const META_STANDARD_EVENTS = [
|
|
@@ -66,9 +67,6 @@ type MetaEventArgs<E extends MetaEventName> = E extends MetaStandardEvent
|
|
|
66
67
|
: EventDataArgs<MetaCustomEvents[E & keyof MetaCustomEvents]>;
|
|
67
68
|
|
|
68
69
|
/** What `meta-pixel.ts` defines once marketing is granted. */
|
|
69
|
-
interface MetaWindow {
|
|
70
|
-
fbq?: (...args: unknown[]) => void;
|
|
71
|
-
}
|
|
72
70
|
|
|
73
71
|
/**
|
|
74
72
|
* Records an event with Meta's pixel — a lead sent, a booking made. A declared
|
|
@@ -83,7 +81,7 @@ export function metaEvent<E extends MetaEventName>(
|
|
|
83
81
|
event: E,
|
|
84
82
|
...[data]: MetaEventArgs<E>
|
|
85
83
|
): void {
|
|
86
|
-
const fbq = (
|
|
84
|
+
const fbq = vendors().fbq;
|
|
87
85
|
const method = (META_STANDARD_EVENTS as readonly string[]).includes(event)
|
|
88
86
|
? "track"
|
|
89
87
|
: "trackCustom";
|
package/src/astro/meta-pixel.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { appendScript } from "./append-script.ts";
|
|
2
2
|
import type { ConsentGated } from "./consent-gate.ts";
|
|
3
|
+
import { vendors } from "./vendors.ts";
|
|
3
4
|
|
|
4
5
|
/** Meta's `fbq`: a queue until `fbevents.js` arrives and replays it. */
|
|
5
|
-
interface Fbq {
|
|
6
|
+
export interface Fbq {
|
|
6
7
|
(...args: unknown[]): void;
|
|
7
8
|
callMethod?: (...args: unknown[]) => void;
|
|
8
9
|
queue: unknown[];
|
|
@@ -11,16 +12,11 @@ interface Fbq {
|
|
|
11
12
|
version: string;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
interface PixelWindow {
|
|
15
|
-
fbq?: Fbq;
|
|
16
|
-
_fbq?: Fbq;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
15
|
const SCRIPT = "https://connect.facebook.net/en_US/fbevents.js";
|
|
20
16
|
|
|
21
17
|
/** Meta's own base code, as it defines `fbq` before the script loads. */
|
|
22
18
|
function defineFbq(): Fbq {
|
|
23
|
-
const global =
|
|
19
|
+
const global = vendors();
|
|
24
20
|
if (global.fbq !== undefined) return global.fbq;
|
|
25
21
|
// `arguments` is the list of whatever a call passed, which every regular
|
|
26
22
|
// `function` gets without declaring it (an arrow function does not). The
|
|
@@ -57,7 +53,7 @@ export function metaPixel(pixelIds: readonly string[]): ConsentGated {
|
|
|
57
53
|
appendScript(SCRIPT);
|
|
58
54
|
},
|
|
59
55
|
revoke: () => {
|
|
60
|
-
(
|
|
56
|
+
vendors().fbq?.("consent", "revoke");
|
|
61
57
|
},
|
|
62
58
|
};
|
|
63
59
|
}
|
package/src/astro/snap-event.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CurrencyCode } from "../currency.ts";
|
|
2
2
|
import { readConsent } from "./consent.ts";
|
|
3
|
+
import { vendors } from "./vendors.ts";
|
|
3
4
|
|
|
4
5
|
/** Snap's standard events that a website sends. */
|
|
5
6
|
export type SnapStandardEvent =
|
|
@@ -37,9 +38,6 @@ type SnapEventArgs<E extends SnapStandardEvent> = E extends "PURCHASE"
|
|
|
37
38
|
: [data?: SnapEventData];
|
|
38
39
|
|
|
39
40
|
/** What `snap-pixel.ts` defines once marketing is granted. */
|
|
40
|
-
interface SnapWindow {
|
|
41
|
-
snaptr?: (...args: unknown[]) => void;
|
|
42
|
-
}
|
|
43
41
|
|
|
44
42
|
/**
|
|
45
43
|
* Records a standard event with Snap's pixel — a lead sent, a booking made.
|
|
@@ -53,7 +51,7 @@ export function snapEvent<E extends SnapStandardEvent>(
|
|
|
53
51
|
...[data]: SnapEventArgs<E>
|
|
54
52
|
): void {
|
|
55
53
|
if (readConsent()?.marketing !== "granted") return;
|
|
56
|
-
const snaptr = (
|
|
54
|
+
const snaptr = vendors().snaptr;
|
|
57
55
|
if (data === undefined) snaptr?.("track", event);
|
|
58
56
|
else snaptr?.("track", event, data);
|
|
59
57
|
}
|