@inneropen/marvin-astro 2.0.0 → 2.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 +114 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +117 -3
- package/dist/index.js.map +1 -1
- package/dist/types-4VdGN9e_.d.ts +243 -0
- package/dist/types.d.ts +3 -162
- package/package.json +1 -1
- package/src/components/ConsentBanner.astro +87 -0
- package/src/components/ConsentBannerView.astro +104 -0
- package/src/components/CookieSettingsLink.astro +28 -0
- package/src/components/CookieTable.astro +97 -0
- package/src/components/PrivacyPolicy.astro +118 -0
- package/src/components/ProcessorTable.astro +62 -0
- package/src/components/consent.css +192 -0
- package/src/components/consent.ts +246 -0
- package/src/components/index.ts +12 -0
- package/src/components/privacy-text.ts +149 -0
- package/src/components/privacy.css +123 -0
- package/src/index.ts +8 -0
- package/src/privacy.ts +210 -0
- package/src/site.ts +8 -0
- package/src/types.ts +15 -0
package/README.md
CHANGED
|
@@ -258,6 +258,8 @@ this needs a newer SDK.
|
|
|
258
258
|
|---|---|
|
|
259
259
|
| `FormRenderer` | a public form from a submittable entry type's `formSchema`; unstyled, progressive-enhancement submit |
|
|
260
260
|
| `Embed`, `LinkCard`, `EmbedLoader` | media embeds (above) |
|
|
261
|
+
| `PrivacyPolicy`, `CookieTable`, `ProcessorTable` | privacy & cookie policy rendering ([below](#privacy--cookies)) |
|
|
262
|
+
| `ConsentBanner`, `CookieSettingsLink` | cookie-consent banner and settings link ([below](#consent-banner)) |
|
|
261
263
|
| `EntryRenderer` | picks the renderer for an entry from its entry type's `rendering` |
|
|
262
264
|
| `PageRenderer`, `ArticleRenderer`, `FaqRenderer`, `NavigationRenderer` | unstyled, semantic core renderers with `data-renderer`/`data-role` hooks |
|
|
263
265
|
|
|
@@ -288,6 +290,116 @@ Options: `registry` (required), `apiUrl`, `siteToken` (default to the `MARVIN_*`
|
|
|
288
290
|
Marvin's system entry types name their renderer package `@inneropen/marvin-renderers-core`; the
|
|
289
291
|
core renderers still answer to that name (and to `@inneropen/marvin-astro`).
|
|
290
292
|
|
|
293
|
+
## Privacy & cookies
|
|
294
|
+
|
|
295
|
+
Marvin keeps the facts a privacy/cookie policy needs (`site.privacy`: the controller, processors and
|
|
296
|
+
the cookies/storage the site sets). `site.privacy` is read tolerantly (typed field or
|
|
297
|
+
`metadata.privacy`, camelCase or snake_case), so it needs no SDK bump; it is `undefined` until a
|
|
298
|
+
workspace configures it. Not legal advice: you decide the text and the classification.
|
|
299
|
+
|
|
300
|
+
```astro
|
|
301
|
+
---
|
|
302
|
+
import { PrivacyPolicy } from '@inneropen/marvin-astro/components';
|
|
303
|
+
const site = await marvin.getSite();
|
|
304
|
+
const entry = await policies.bySlug('privacy'); // your policy entry (prose)
|
|
305
|
+
---
|
|
306
|
+
<PrivacyPolicy privacy={site.privacy} headingLevel={2}>
|
|
307
|
+
<Fragment set:html={entry.bodyHtml} />
|
|
308
|
+
</PrivacyPolicy>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
| Component | Renders |
|
|
312
|
+
|---|---|
|
|
313
|
+
| `PrivacyPolicy` | the entry prose (default slot), controller block, processors, cookies, "Last reviewed" |
|
|
314
|
+
| `CookieTable` | `cookies`, one table per category (`category` narrows it); adds the `marvin-consent` storage row as necessary when a banner is required (`includeConsentEntry={false}` to skip) |
|
|
315
|
+
| `ProcessorTable` | `processors`: region, transfer basis, DPA/privacy links (http(s) only) |
|
|
316
|
+
|
|
317
|
+
All server-rendered with zero JavaScript. Styling is `privacy.css`: zero-specificity `:where()` rules
|
|
318
|
+
you override freely, themed with `--marvin-privacy-gap | -border | -head-bg | -muted | -pad`; tables
|
|
319
|
+
stack below 600px. Every string has a neutral English default; pass `labels={{ ... }}` to change it.
|
|
320
|
+
|
|
321
|
+
`sitePrivacy(raw)` and `consentVersion(privacy)` are exported from the package root. The version is
|
|
322
|
+
a hash of the sorted non-necessary `category|name|provider` triples, so rewording a purpose or
|
|
323
|
+
moving `lastReviewed` never re-asks, while adding, removing or re-categorising a cookie does.
|
|
324
|
+
|
|
325
|
+
### Consent banner
|
|
326
|
+
|
|
327
|
+
`<ConsentBanner>` asks only when it has to. It renders **nothing, and ships no script or CSS**, unless
|
|
328
|
+
`site.privacy.bannerRequired` (at least one listed cookie/storage entry is not `necessary`), so a site
|
|
329
|
+
with no optional cookies carries zero banner bytes.
|
|
330
|
+
|
|
331
|
+
```astro
|
|
332
|
+
---
|
|
333
|
+
// src/layouts/BaseLayout.astro
|
|
334
|
+
import { ConsentBanner, CookieSettingsLink } from '@inneropen/marvin-astro/components';
|
|
335
|
+
const site = await marvin.getSite();
|
|
336
|
+
---
|
|
337
|
+
<html>
|
|
338
|
+
<body>
|
|
339
|
+
<ConsentBanner privacy={site.privacy} policyHref="/privacy" />
|
|
340
|
+
<main>...</main>
|
|
341
|
+
<footer>
|
|
342
|
+
<CookieSettingsLink href="/privacy" privacy={site.privacy} />
|
|
343
|
+
</footer>
|
|
344
|
+
</body>
|
|
345
|
+
</html>
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
- **Fail closed.** The markup is `hidden` and the runtime reveals it only when the visitor has no valid
|
|
349
|
+
choice; without JavaScript there is no dead banner and nothing optional loads. The link in
|
|
350
|
+
`<CookieSettingsLink>` is a real link to your policy page; with JavaScript it opens the dialog.
|
|
351
|
+
- **Equal choices.** Accept all and Reject all share one class and size; Customise opens a native
|
|
352
|
+
`<dialog>` with one unticked checkbox per category listed in `site.privacy.cookies`. A non-modal bottom
|
|
353
|
+
bar, no focus stealing; after a choice focus moves to `<main>`, and the dialog returns it to its opener.
|
|
354
|
+
- **Remembered for 6 months**, refusals too (`maxAgeDays`, default 180), in
|
|
355
|
+
`localStorage['marvin-consent']`. The choice is bound to `consentVersion(privacy)`: adding, removing or
|
|
356
|
+
re-categorising an optional cookie asks everyone again; rewording does not. `consent.version` in
|
|
357
|
+
Marvin's privacy block is a manual re-ask bump. Safari caps script-written storage at 7 days without
|
|
358
|
+
interaction, so some visitors are asked sooner.
|
|
359
|
+
- **Disclosure.** `marvin-consent` is listed automatically by `<CookieTable>` / `<PrivacyPolicy>`.
|
|
360
|
+
|
|
361
|
+
Gate optional scripts and markup so they ship inert and run only after consent:
|
|
362
|
+
|
|
363
|
+
```html
|
|
364
|
+
<script type="text/plain" data-consent="analytics" data-src="https://stats.example/script.js" defer></script>
|
|
365
|
+
<script type="text/plain" data-consent="analytics">window.track?.();</script>
|
|
366
|
+
<template data-consent="marketing"><iframe src="https://ads.example/widget"></iframe></template>
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
`data-consent` takes one or more categories (`preferences`, `analytics`, `marketing`; all must be
|
|
370
|
+
allowed). Other attributes on the script are copied over, `data-type="module"` restores a module type.
|
|
371
|
+
An unknown category never runs. A site whose `bannerRequired` is false never activates anything, so a
|
|
372
|
+
gated script with no matching entry in Marvin stays inert; a dev build logs a console warning for that.
|
|
373
|
+
Prefer `data-src` over inline code if you use a CSP (inline gated code needs a hash or `unsafe-inline`).
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
window.marvinConsent.get(); // { v, version, choices, at } | null while undecided
|
|
377
|
+
window.marvinConsent.has('analytics'); // 'necessary' is always true
|
|
378
|
+
window.marvinConsent.set({ analytics: true });
|
|
379
|
+
window.marvinConsent.open(); // the Customise dialog
|
|
380
|
+
document.addEventListener('marvin:consent', (e) => e.detail); // { choices, version, initial }
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
`marvin:consent` fires on load when a valid choice exists (`initial: true`, after the page's module
|
|
384
|
+
scripts have run, so late listeners see it) and on every change. Withdrawing a category deletes the
|
|
385
|
+
site's listed first-party cookies for it (a trailing `*` is a prefix: `_ga*`; the host and its parent
|
|
386
|
+
domains) and reloads the page so nothing keeps running.
|
|
387
|
+
|
|
388
|
+
**Embeds** stay click-to-load: the click on a facade is the visitor's consent, and they do not make
|
|
389
|
+
`bannerRequired` true. Opt in to auto-loading them after a category is allowed with
|
|
390
|
+
`<ConsentBanner embeds="marketing" ... />` (off by default; needs `<EmbedLoader />`).
|
|
391
|
+
|
|
392
|
+
Text defaults to neutral English: override per site with `labels={{ title, body, acceptAll, rejectAll,
|
|
393
|
+
customise, save, settingsLink, policyLink, dialogTitle, dialogBody, categories, categoryDescriptions }}`.
|
|
394
|
+
Theme with `--marvin-consent-bg | -fg | -accent | -radius | -font | -shadow | -z` (zero-specificity
|
|
395
|
+
`consent.css`; dark mode follows `prefers-color-scheme`). No consent log is kept anywhere.
|
|
396
|
+
|
|
397
|
+
`consent.css` is inlined as a `<style>` next to the banner markup, and the runtime `<script>` is only
|
|
398
|
+
emitted with it, so a page without a banner carries neither (Astro 5 ships a component's `<script>` only
|
|
399
|
+
where it renders but ships imported stylesheets wherever the component is merely imported, hence the
|
|
400
|
+
inline style). Under a CSP without `style-src 'unsafe-inline'`, pass `inlineStyles={false}` and import
|
|
401
|
+
`@inneropen/marvin-astro/components/consent.css` yourself. Check your build output on other Astro versions.
|
|
402
|
+
|
|
291
403
|
## Migrating from @inneropen/marvin-renderers-core
|
|
292
404
|
|
|
293
405
|
The components and logic moved here with their names unchanged. Swap the import paths, then
|
|
@@ -354,7 +466,7 @@ verification tags. No styling, no site coupling.
|
|
|
354
466
|
| `@inneropen/marvin-astro` | `createMarvinContent` and every helper below it |
|
|
355
467
|
| `@inneropen/marvin-astro/types` | resolved types only (`ApiSite`, `ApiSeo`, `ApiSiteChrome`, …) |
|
|
356
468
|
| `@inneropen/marvin-astro/astro` | `SeoHead` |
|
|
357
|
-
| `@inneropen/marvin-astro/components` | `FormRenderer`, `Embed`, `LinkCard`, `EmbedLoader`, `EntryRenderer`, the core renderers, registry, `marvinIntegration` |
|
|
469
|
+
| `@inneropen/marvin-astro/components` | `FormRenderer`, `Embed`, `LinkCard`, `EmbedLoader`, `PrivacyPolicy`, `CookieTable`, `ProcessorTable`, `ConsentBanner`, `CookieSettingsLink`, `EntryRenderer`, the core renderers, registry, `marvinIntegration` |
|
|
358
470
|
| `@inneropen/marvin-astro/components/registry` | `getRenderer`, `astroRegistry`, `coreRendererPackage` |
|
|
359
471
|
| `@inneropen/marvin-astro/components/integration` | `marvinIntegration` (for `astro.config.mjs`) |
|
|
360
472
|
| `@inneropen/marvin-astro/components/*` | individual components, `renderers/*.astro`, `embeds.css` |
|
|
@@ -362,7 +474,7 @@ verification tags. No styling, no site coupling.
|
|
|
362
474
|
|
|
363
475
|
Beyond `createMarvinContent`, the pieces are usable on their own: `createBackend`,
|
|
364
476
|
`createFetcher`, `createRepository`, `createSiteLoader`, `createChromeLoader`,
|
|
365
|
-
`createFieldAccessor`, `createMarkdownRenderer`, `entryEmbeds`, `findEmbed`, `siteEmbeds`,
|
|
477
|
+
`createFieldAccessor`, `createMarkdownRenderer`, `entryEmbeds`, `findEmbed`, `siteEmbeds`, `sitePrivacy`, `consentVersion`,
|
|
366
478
|
`formatDisplayDate`, `selectValuesForPage`, and the whole `normalize` surface.
|
|
367
479
|
|
|
368
480
|
## Development
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { M as MarvinEmbeds, a as MarvinEmbed } from './embeds-BTNryQA6.js';
|
|
|
3
3
|
export { b as MarvinEmbedIframe, c as MarvinEmbedKind, d as MarvinEmbedLink, e as MarvinEmbedMode, f as MarvinEmbedStatus, g as MarvinSiteEmbeds, h as entryEmbeds, i as findEmbed, s as siteEmbeds } from './embeds-BTNryQA6.js';
|
|
4
4
|
import * as _inneropen_marvin_sdk from '@inneropen/marvin-sdk';
|
|
5
5
|
import { MarvinClient, MarvinEntry, Entry, PublishedCollectionSummary, Collection, CollectionEntry, MarvinSite, Workspace } from '@inneropen/marvin-sdk';
|
|
6
|
-
import { MarvinContentEntry, ApiSite, ApiSeo, ApiSiteChrome, ApiNavigationLink, ApiSocialLink, AssetSelectOptions, ApiImage, ApiResourceLink, SectionLanding } from './types.js';
|
|
7
|
-
export { ApiActionVariant, ApiHero, ApiHeroAction, ApiPageAction } from './types.js';
|
|
6
|
+
import { M as MarvinContentEntry, A as ApiSite, a as ApiSeo, b as ApiSiteChrome, c as ApiNavigationLink, d as ApiSocialLink, e as AssetSelectOptions, f as ApiImage, g as ApiResourceLink, S as SectionLanding } from './types-4VdGN9e_.js';
|
|
7
|
+
export { h as ApiActionVariant, i as ApiHero, j as ApiHeroAction, k as ApiPageAction, C as CONSENT_CATEGORIES, l as COOKIE_CATEGORIES, m as ConsentCategory, n as CookieCategory, P as PrivacyConsent, o as PrivacyController, p as PrivacyCookie, q as PrivacyProcessor, r as SitePrivacy, s as consentCategories, t as consentVersion, u as cookiesNeedConsent, v as sitePrivacy } from './types-4VdGN9e_.js';
|
|
8
8
|
|
|
9
9
|
type MarkdownOptions = MarkedOptions;
|
|
10
10
|
/** Per-call options for a {@link MarkdownRenderer}. */
|
package/dist/index.js
CHANGED
|
@@ -602,9 +602,9 @@ var EMBEDS = /* @__PURE__ */ Symbol("marvin.embeds");
|
|
|
602
602
|
var BARE_URL = /^https?:\/\/\S+$/i;
|
|
603
603
|
function paragraphEmbedHtml(token, embeds) {
|
|
604
604
|
if (!embeds) return void 0;
|
|
605
|
-
const
|
|
606
|
-
if (!BARE_URL.test(
|
|
607
|
-
const html = findEmbed(embeds,
|
|
605
|
+
const text2 = token.text.trim();
|
|
606
|
+
if (!BARE_URL.test(text2)) return void 0;
|
|
607
|
+
const html = findEmbed(embeds, text2)?.html;
|
|
608
608
|
return typeof html === "string" && html.trim() ? html : void 0;
|
|
609
609
|
}
|
|
610
610
|
function createMarkdownRenderer(options = {}) {
|
|
@@ -984,6 +984,109 @@ async function loadSectionLanding(fetcher, slug, renderMarkdown = createMarkdown
|
|
|
984
984
|
};
|
|
985
985
|
}
|
|
986
986
|
|
|
987
|
+
// src/privacy.ts
|
|
988
|
+
var COOKIE_CATEGORIES = [
|
|
989
|
+
"necessary",
|
|
990
|
+
"preferences",
|
|
991
|
+
"analytics",
|
|
992
|
+
"marketing"
|
|
993
|
+
];
|
|
994
|
+
var CONSENT_CATEGORIES = [
|
|
995
|
+
"preferences",
|
|
996
|
+
"analytics",
|
|
997
|
+
"marketing"
|
|
998
|
+
];
|
|
999
|
+
function field2(record, camel) {
|
|
1000
|
+
if (record[camel] !== void 0) return record[camel];
|
|
1001
|
+
return record[camel.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)];
|
|
1002
|
+
}
|
|
1003
|
+
function text(record, camel) {
|
|
1004
|
+
const value = asString(field2(record, camel));
|
|
1005
|
+
return value?.trim();
|
|
1006
|
+
}
|
|
1007
|
+
function records(value) {
|
|
1008
|
+
return Array.isArray(value) ? value.filter((item) => !!item && typeof item === "object") : [];
|
|
1009
|
+
}
|
|
1010
|
+
function readController(raw) {
|
|
1011
|
+
const value = asRecord(raw);
|
|
1012
|
+
const legalName = text(value, "legalName");
|
|
1013
|
+
if (!legalName) return void 0;
|
|
1014
|
+
return {
|
|
1015
|
+
legalName,
|
|
1016
|
+
contactEmail: text(value, "contactEmail"),
|
|
1017
|
+
address: text(value, "address"),
|
|
1018
|
+
euRepresentative: text(value, "euRepresentative"),
|
|
1019
|
+
dpoEmail: text(value, "dpoEmail")
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
function readProcessor(value) {
|
|
1023
|
+
const name = text(value, "name");
|
|
1024
|
+
if (!name) return void 0;
|
|
1025
|
+
const categories = field2(value, "dataCategories");
|
|
1026
|
+
return {
|
|
1027
|
+
name,
|
|
1028
|
+
purpose: text(value, "purpose"),
|
|
1029
|
+
dataCategories: Array.isArray(categories) ? categories.filter((item) => typeof item === "string" && item.trim() !== "") : [],
|
|
1030
|
+
region: text(value, "region"),
|
|
1031
|
+
transferBasis: text(value, "transferBasis"),
|
|
1032
|
+
dpaUrl: text(value, "dpaUrl"),
|
|
1033
|
+
privacyUrl: text(value, "privacyUrl")
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
function readCookie(value) {
|
|
1037
|
+
const name = text(value, "name");
|
|
1038
|
+
if (!name) return void 0;
|
|
1039
|
+
const category = text(value, "category");
|
|
1040
|
+
return {
|
|
1041
|
+
name,
|
|
1042
|
+
provider: text(value, "provider") ?? "",
|
|
1043
|
+
purpose: text(value, "purpose") ?? "",
|
|
1044
|
+
duration: text(value, "duration"),
|
|
1045
|
+
// A category a newer server invented is treated as the strictest optional one, never as
|
|
1046
|
+
// `necessary`: it must be asked about before anything it covers loads.
|
|
1047
|
+
category: COOKIE_CATEGORIES.includes(category) ? category : "marketing"
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
function cookiesNeedConsent(cookies) {
|
|
1051
|
+
return cookies.some((cookie) => cookie.category !== "necessary");
|
|
1052
|
+
}
|
|
1053
|
+
function sitePrivacy(raw) {
|
|
1054
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
1055
|
+
const value = raw;
|
|
1056
|
+
const consent = asRecord(field2(value, "consent"));
|
|
1057
|
+
const cookies = records(value.cookies).map(readCookie).filter((cookie) => cookie !== void 0);
|
|
1058
|
+
const declared = field2(value, "bannerRequired");
|
|
1059
|
+
return {
|
|
1060
|
+
controller: readController(value.controller),
|
|
1061
|
+
processors: records(value.processors).map(readProcessor).filter((processor) => processor !== void 0),
|
|
1062
|
+
cookies,
|
|
1063
|
+
consent: {
|
|
1064
|
+
policyEntrySlug: text(consent, "policyEntrySlug"),
|
|
1065
|
+
cookiePolicyEntrySlug: text(consent, "cookiePolicyEntrySlug"),
|
|
1066
|
+
lastReviewed: text(consent, "lastReviewed"),
|
|
1067
|
+
version: text(consent, "version")
|
|
1068
|
+
},
|
|
1069
|
+
bannerRequired: typeof declared === "boolean" ? declared : cookiesNeedConsent(cookies)
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
function fnv1a(input) {
|
|
1073
|
+
let hash = 2166136261;
|
|
1074
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
1075
|
+
hash ^= input.charCodeAt(index);
|
|
1076
|
+
hash = Math.imul(hash, 16777619);
|
|
1077
|
+
}
|
|
1078
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
1079
|
+
}
|
|
1080
|
+
function consentVersion(privacy) {
|
|
1081
|
+
const triples = (privacy?.cookies ?? []).filter((cookie) => cookie.category !== "necessary").map((cookie) => `${cookie.category}|${cookie.name}|${cookie.provider}`).sort();
|
|
1082
|
+
const bump = privacy?.consent.version;
|
|
1083
|
+
return fnv1a(bump ? [...triples, `v:${bump}`].join("\n") : triples.join("\n"));
|
|
1084
|
+
}
|
|
1085
|
+
function consentCategories(privacy) {
|
|
1086
|
+
const present = new Set((privacy?.cookies ?? []).map((cookie) => cookie.category));
|
|
1087
|
+
return CONSENT_CATEGORIES.filter((category) => present.has(category));
|
|
1088
|
+
}
|
|
1089
|
+
|
|
987
1090
|
// src/site.ts
|
|
988
1091
|
function buildSeo(raw, identity) {
|
|
989
1092
|
const seo = raw ?? {};
|
|
@@ -1071,6 +1174,11 @@ function transformMarvinSite(marvinSite, fallback) {
|
|
|
1071
1174
|
embeds: siteEmbeds(
|
|
1072
1175
|
config.embeds ?? marvinSite.embeds
|
|
1073
1176
|
),
|
|
1177
|
+
// Typed `config.privacy` when the SDK declares it (SDK 7.2+), else the raw blob under
|
|
1178
|
+
// `metadata.privacy`; this package's SDK peer range predates the typed field, so read both.
|
|
1179
|
+
privacy: sitePrivacy(
|
|
1180
|
+
config.privacy ?? marvinSite.privacy ?? metadata.privacy
|
|
1181
|
+
),
|
|
1074
1182
|
metadata
|
|
1075
1183
|
};
|
|
1076
1184
|
}
|
|
@@ -1161,6 +1269,8 @@ function createMarvinContent(options = {}) {
|
|
|
1161
1269
|
};
|
|
1162
1270
|
}
|
|
1163
1271
|
export {
|
|
1272
|
+
CONSENT_CATEGORIES,
|
|
1273
|
+
COOKIE_CATEGORIES,
|
|
1164
1274
|
DEFAULT_DEV_RETRY_MS,
|
|
1165
1275
|
DEFAULT_HYDRATE_CONCURRENCY,
|
|
1166
1276
|
ENV_KEYS,
|
|
@@ -1175,6 +1285,9 @@ export {
|
|
|
1175
1285
|
buildSeo,
|
|
1176
1286
|
collectionRole,
|
|
1177
1287
|
collectionSlugs,
|
|
1288
|
+
consentCategories,
|
|
1289
|
+
consentVersion,
|
|
1290
|
+
cookiesNeedConsent,
|
|
1178
1291
|
createBackend,
|
|
1179
1292
|
createChromeLoader,
|
|
1180
1293
|
createFetcher,
|
|
@@ -1213,6 +1326,7 @@ export {
|
|
|
1213
1326
|
selectImageAsset,
|
|
1214
1327
|
selectValuesForPage,
|
|
1215
1328
|
siteEmbeds,
|
|
1329
|
+
sitePrivacy,
|
|
1216
1330
|
socialLinkFromKey,
|
|
1217
1331
|
toNavigationLink
|
|
1218
1332
|
};
|