@emplugins/emprivacy 3.0.0 → 3.1.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/README.md +11 -5
- package/dist/index.d.mts +56 -2
- package/dist/index.mjs +503 -55
- package/docs/GETTING_STARTED.md +5 -3
- package/docs/PLUGIN_SETTINGS.md +42 -5
- package/docs/RELEASE_NOTES.md +13 -1
- package/docs/TESTING.md +8 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -87,7 +87,7 @@ The legacy unscoped package `emprivacy` is deprecated; migrate imports to `@empl
|
|
|
87
87
|
|
|
88
88
|
5. **Create a Privacy Policy Page** (and optional Cookie Policy Page) in EmDash, then in admin (shield → **EmPrivacy**) set the public path (`/privacy`) or an `https://` URL.
|
|
89
89
|
|
|
90
|
-
6. Pick an analytics preset (Cloudflare, Plausible, Fathom, Umami, Simple Analytics, GA4, GTM, None, or Custom), optional marketing script URLs, and **Save**.
|
|
90
|
+
6. Pick an analytics preset (Cloudflare, Plausible, Fathom, Umami, Simple Analytics, GA4, GTM, Microsoft Clarity, Microsoft UET, None, or Custom), optional marketing script URLs, and **Save**.
|
|
91
91
|
|
|
92
92
|
Visitors see a banner on first visit, when **Policy / consent version** changes, or when an older cookie is missing the functional flag (`f`). Existing `{ v, a, m }` cookies are treated as expired so visitors choose the new category.
|
|
93
93
|
|
|
@@ -97,7 +97,8 @@ After the banner script runs:
|
|
|
97
97
|
|
|
98
98
|
```ts
|
|
99
99
|
window.emprivacy.get();
|
|
100
|
-
// { v, essential: true, functional, analytics, marketing } | null
|
|
100
|
+
// { v, essential: true, functional, analytics, marketing, gpc } | null
|
|
101
|
+
// gpc is true when Global Privacy Control forced marketing off
|
|
101
102
|
|
|
102
103
|
window.emprivacy.has("analytics"); // boolean
|
|
103
104
|
window.emprivacy.open(); // reopen the panel
|
|
@@ -113,16 +114,21 @@ stop();
|
|
|
113
114
|
|--------|-------------|
|
|
114
115
|
| Categories | Essential (always on), **Functional**, **Analytics**, **Marketing** |
|
|
115
116
|
| Privacy / cookie links | EmDash **Page** path or `https://` URL; resolved with `ctx.url()` |
|
|
116
|
-
| Analytics presets | Cloudflare Web Analytics, Plausible, Fathom, Umami, Simple Analytics, GA4, **GTM (Marketing)**, None, Custom `https` URLs (+ optional SRI / host allowlist) |
|
|
117
|
+
| Analytics presets | Cloudflare Web Analytics, Plausible, Fathom, Umami, Simple Analytics, GA4, **GTM (Marketing)**, **Microsoft Clarity (Analytics)**, **Microsoft UET (Marketing)**, None, Custom `https` URLs (+ optional SRI / host allowlist) |
|
|
117
118
|
| Marketing scripts | Only **https** URLs you list, injected as `<script src>` after marketing consent |
|
|
118
119
|
| Official embeds | Optional placeholders for `@emdash-cms/plugin-embeds` block types; YouTube/Vimeo use allowlisted player URLs (`youtube-nocookie`, `player.vimeo.com`); client re-checks URLs before mount |
|
|
119
120
|
| Re-open preferences | Cookie button (bottom-left); saving reloads so script loading matches the latest choice |
|
|
120
121
|
| Consent cookie | `emprivacy_cc` (`path=/`, `SameSite=Lax`, `Secure` on HTTPS, configurable `Max-Age`, default 180 days), JSON `{ v, f, a, m }` |
|
|
121
122
|
| Vendor list | Derived from settings; shown under Customize; JSON at `/_emdash/api/plugins/emprivacy/vendors` (no tokens) |
|
|
122
|
-
| Optional server log | POST `{ policyVersion, functional, analytics, marketing }` to `/_emdash/api/plugins/emprivacy/record` (Origin / Sec-Fetch-Site, rate-limited, no IP) |
|
|
123
|
+
| Optional server log | POST `{ policyVersion, functional, analytics, marketing, gpc }` to `/_emdash/api/plugins/emprivacy/record` (Origin / Sec-Fetch-Site, rate-limited, no IP). Signed-in admins can download `/_emdash/api/plugins/emprivacy/consent-export` as CSV |
|
|
124
|
+
| Global Privacy Control | When `navigator.globalPrivacyControl` is true, Marketing stays off (including Accept all) until the signal is gone. Analytics is unchanged. Visitors can still open preferences |
|
|
125
|
+
| Cookie cleanup | Turning Analytics or Marketing off expires a fixed list of first-party cookie names for the presets EmPrivacy loads. The consent cookie is kept. HttpOnly and third-party cookies cannot be cleared from the page |
|
|
126
|
+
| Keyboard | Focus moves into the banner. Tab stays inside it. Escape closes the reopen panel and returns focus to the cookie button. The first visit stays open until a choice |
|
|
123
127
|
| Google Consent Mode v2 | Optional denied defaults in `<head>` + `gtag('consent','update',…)` including `functionality_storage` |
|
|
128
|
+
| Microsoft consent | Clarity and UET presets queue denied signals in `<head>`. The Clarity tag loads only after Analytics consent; the UET tag loads only after Marketing consent |
|
|
124
129
|
| i18n | Built-in chrome for common locales; optional JSON title/message overrides |
|
|
125
|
-
| Theme |
|
|
130
|
+
| Theme | Starts on the Slate profile. Also Paper, Ink, Indigo, Primer, or custom hex. Custom opens with the colors already in effect. Profiles use fixed colors only |
|
|
131
|
+
| Banner position | Fixed to the bottom (default) or top of the viewport. The fragment still renders at `body:end` |
|
|
126
132
|
|
|
127
133
|
## Capabilities
|
|
128
134
|
|
package/dist/index.d.mts
CHANGED
|
@@ -50,6 +50,16 @@ type AnalyticsLoader = {
|
|
|
50
50
|
{
|
|
51
51
|
type: "gtm";
|
|
52
52
|
containerId: string;
|
|
53
|
+
} |
|
|
54
|
+
/** Loaded only after Analytics consent. Advertising storage follows Marketing. */
|
|
55
|
+
{
|
|
56
|
+
type: "clarity";
|
|
57
|
+
projectId: string;
|
|
58
|
+
} |
|
|
59
|
+
/** Loaded only after Marketing consent. */
|
|
60
|
+
{
|
|
61
|
+
type: "uet";
|
|
62
|
+
tagId: string;
|
|
53
63
|
};
|
|
54
64
|
export declare function buildAnalyticsLoader(cfg: EmprivacyConfig): AnalyticsLoader;
|
|
55
65
|
export declare function buildVendorList(cfg: EmprivacyConfig): VendorRow[];
|
|
@@ -74,6 +84,7 @@ interface EmprivacyChromeStrings {
|
|
|
74
84
|
embedNeedConsent: string;
|
|
75
85
|
vendorsHeading: string;
|
|
76
86
|
whatWeUse: string;
|
|
87
|
+
gpcNote: string;
|
|
77
88
|
}
|
|
78
89
|
interface EmprivacyLocaleCopy {
|
|
79
90
|
bannerTitle: string;
|
|
@@ -88,11 +99,46 @@ interface EmprivacyTheme {
|
|
|
88
99
|
accent: string;
|
|
89
100
|
radiusPx: number;
|
|
90
101
|
}
|
|
102
|
+
/** Fixed banner palettes. Colors are constants, never taken from the admin form. */
|
|
103
|
+
declare const THEME_PROFILES: {
|
|
104
|
+
readonly slate: {
|
|
105
|
+
readonly bg: "#0f172a";
|
|
106
|
+
readonly text: "#f8fafc";
|
|
107
|
+
readonly accent: "#3b82f6";
|
|
108
|
+
readonly radiusPx: 8;
|
|
109
|
+
};
|
|
110
|
+
readonly paper: {
|
|
111
|
+
readonly bg: "#f7f6f3";
|
|
112
|
+
readonly text: "#37352f";
|
|
113
|
+
readonly accent: "#1d4ed8";
|
|
114
|
+
readonly radiusPx: 6;
|
|
115
|
+
};
|
|
116
|
+
readonly ink: {
|
|
117
|
+
readonly bg: "#000000";
|
|
118
|
+
readonly text: "#ededed";
|
|
119
|
+
readonly accent: "#0070f3";
|
|
120
|
+
readonly radiusPx: 8;
|
|
121
|
+
};
|
|
122
|
+
readonly indigo: {
|
|
123
|
+
readonly bg: "#ffffff";
|
|
124
|
+
readonly text: "#0f172a";
|
|
125
|
+
readonly accent: "#4f46e5";
|
|
126
|
+
readonly radiusPx: 12;
|
|
127
|
+
};
|
|
128
|
+
readonly primer: {
|
|
129
|
+
readonly bg: "#f6f8fa";
|
|
130
|
+
readonly text: "#1f2328";
|
|
131
|
+
readonly accent: "#0969da";
|
|
132
|
+
readonly radiusPx: 6;
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
type ThemeProfileId = keyof typeof THEME_PROFILES;
|
|
136
|
+
type ThemeChoice = ThemeProfileId | "custom";
|
|
91
137
|
//#endregion
|
|
92
138
|
//#region src/config.d.ts
|
|
93
139
|
export declare const KV_KEY: "consent:config";
|
|
94
140
|
export declare const COOKIE_NAME: "emprivacy_cc";
|
|
95
|
-
type AnalyticsProvider = "cloudflare" | "none" | "custom" | "plausible" | "fathom" | "umami" | "simpleanalytics" | "ga4" | "gtm";
|
|
141
|
+
type AnalyticsProvider = "cloudflare" | "none" | "custom" | "plausible" | "fathom" | "umami" | "simpleanalytics" | "ga4" | "gtm" | "clarity" | "uet";
|
|
96
142
|
type EmbedCategory = "functional" | "marketing";
|
|
97
143
|
interface EmprivacyConfig {
|
|
98
144
|
bannerTitle: string;
|
|
@@ -141,10 +187,15 @@ interface EmprivacyConfig {
|
|
|
141
187
|
gateEmbeds: boolean;
|
|
142
188
|
/** Hide the first-visit banner on privacy/cookie policy paths */
|
|
143
189
|
hideBannerOnPolicyPages: boolean;
|
|
190
|
+
/** Viewport edge for the fixed consent banner. Fragment placement stays `body:end`. */
|
|
191
|
+
bannerPosition: BannerPosition;
|
|
144
192
|
defaultLocale: string;
|
|
145
193
|
localeOverrides: LocaleOverrides;
|
|
194
|
+
/** `custom` uses `theme`. A profile id replaces `theme` with that profile’s fixed colors. */
|
|
195
|
+
themeProfile: ThemeChoice;
|
|
146
196
|
theme: EmprivacyTheme;
|
|
147
197
|
}
|
|
198
|
+
type BannerPosition = "bottom" | "top";
|
|
148
199
|
export declare const DEFAULT_CONFIG: EmprivacyConfig;
|
|
149
200
|
/** Reject characters that would break JSON or attributes; allow typical Cloudflare site token strings. */
|
|
150
201
|
export declare function assertValidCloudflareToken(t: string): void;
|
|
@@ -167,6 +218,8 @@ interface ConsentRecordPayload {
|
|
|
167
218
|
functional: boolean;
|
|
168
219
|
analytics: boolean;
|
|
169
220
|
marketing: boolean;
|
|
221
|
+
/** Present when the browser's Global Privacy Control signal forced marketing off. */
|
|
222
|
+
gpc?: boolean;
|
|
170
223
|
}
|
|
171
224
|
interface ConsentState {
|
|
172
225
|
v: string;
|
|
@@ -195,6 +248,7 @@ interface EmprivacyPublicRuntimeConfig {
|
|
|
195
248
|
embedCategory: EmbedCategory;
|
|
196
249
|
gateEmbeds: boolean;
|
|
197
250
|
hideBanner: boolean;
|
|
251
|
+
bannerPosition: BannerPosition;
|
|
198
252
|
theme: EmprivacyTheme;
|
|
199
253
|
ui: EmprivacyChromeStrings;
|
|
200
254
|
loader: AnalyticsLoader;
|
|
@@ -259,4 +313,4 @@ export declare function resolveEmbed(node: {
|
|
|
259
313
|
*/
|
|
260
314
|
export declare function emprivacyPlugin(): PluginDescriptor;
|
|
261
315
|
//#endregion
|
|
262
|
-
export { type AnalyticsLoader, type AnalyticsProvider, type ConsentRecordPayload, type ConsentState, type EmbedCategory, type EmprivacyBrowserApi, type EmprivacyCategory, type EmprivacyConfig, type EmprivacyPublicRuntimeConfig, type ResolvedEmbed, type VendorPublicRow, type VendorRow, createPlugin as default };
|
|
316
|
+
export { type AnalyticsLoader, type AnalyticsProvider, type BannerPosition, type ConsentRecordPayload, type ConsentState, type EmbedCategory, type EmprivacyBrowserApi, type EmprivacyCategory, type EmprivacyConfig, type EmprivacyPublicRuntimeConfig, type ResolvedEmbed, type VendorPublicRow, type VendorRow, createPlugin as default };
|