@getvitops/core 8.2.0-alpha.0 → 8.2.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 CHANGED
@@ -1,12 +1,146 @@
1
1
  # @getvitops/core
2
2
 
3
+ ## 8.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c3c1bcb: **New: the design-system editor, mounted inside the EmDash admin.** `vitopsEmdash({
8
+ designSystemEditor: { config } })` (`config` is your `site.json`, e.g. `import config from
9
+ './site.json' with { type: 'json' }`) adds a `/design-system` admin page mounting
10
+ `<wc-theme-editor>` — the same live token editor `@getvitops/vite`'s dev server has always
11
+ offered, now usable by the client directly, no dev server required. An edit is visible
12
+ site-wide immediately (a runtime `:root` override, served via a new plugin API route and
13
+ `page:fragments`) and — once `site.deployment.repo`/`branch` and a `VITOPS_GITHUB_TOKEN`
14
+ secret are set (see the scaffold's `SETUP.html`) — auto-commits to your `dev` branch, so it
15
+ reaches the git-tracked `site.json` through your repo's normal review path rather than
16
+ becoming a second, unreviewed source of truth. Until that's configured, the live half still
17
+ works; only the auto-commit half is skipped (and says so in the admin page). The save route
18
+ runs the exact same accessibility (APCA contrast) gate `generate()` enforces at build time,
19
+ synchronously, before persisting — nothing non-compliant ever goes live even momentarily.
20
+
21
+ Requires `@astrojs/react` + `react` + `react-dom` (already scaffold dependencies, for
22
+ EmDash's own admin) and `@getvitops/core` (added as a new scaffold dependency this release).
23
+ Declares two new plugin capabilities when enabled: `network:request` (for the GitHub
24
+ auto-commit) and `hooks.page-fragments:register` (for the live override) — reviewed once at
25
+ plugin-install time.
26
+
27
+ **Breaking for `@getvitops/emdash`**: `vitopsEmdash()`'s `hooks.page-fragments:register`
28
+ capability is now also granted whenever `designSystemEditor` is set, independent of the
29
+ existing `scripts: 'fragments'` option (which governs something unrelated — web-component
30
+ script delivery). No change if you don't use `designSystemEditor`.
31
+
32
+ Also new, supporting the above:
33
+
34
+ - `@getvitops/generator`: `validateColorContract()` and `buildReverseIndex()` (both
35
+ `/tokens` subpath, Worker-safe) — the monotonicity/contrast gate and the
36
+ `design-manifest.json` reverse-index, each extracted from `generate()`'s `build()` into
37
+ standalone, non-throwing functions. `patchToOverrideCss()` turns a design-system patch
38
+ into the `:root` override `<style>` block. `designSystemPath`/`getAt`/`setAt`/
39
+ `mergePatch`/`formatConfig` moved from `@getvitops/vite` into `@getvitops/generator/config`
40
+ (re-exported from `@getvitops/vite` unchanged) — one "patch a config file's design-system
41
+ subtree" implementation shared by the dev endpoint and the new plugin route. New
42
+ `site.deployment.repo`/`branch` config fields.
43
+ - `@getvitops/utils`: new `/git` subpath — `commitFile()`, a Worker-safe (fetch-only)
44
+ wrapper over GitHub's Contents API.
45
+ - `@getvitops/core`: `<wc-theme-editor>` gains a `save-endpoint` attribute (default
46
+ unchanged — `/__vitops/design-system`) so the same element can target the new plugin
47
+ route instead of the dev-only Vite endpoint.
48
+
49
+ - 88b465a: **Accessibility and layout fixes for the web components.**
50
+
51
+ - **`<wc-entries>`: the column selector is no longer `aria-hidden`.** Its buttons are real and
52
+ focusable, so hiding the `<nav>` left a keyboard user on a control that assistive tech never
53
+ announced. It is now exposed as a `Column selector` navigation. The generated table stays
54
+ `aria-hidden` (it duplicates the `<dl>`, which is what assistive tech reads) and contains nothing
55
+ focusable.
56
+ - **`prefers-reduced-motion` is honoured by programmatic scrolling.** `<wc-carousel>` (prev/next and
57
+ marker clicks) and `<wc-tree>` (deep-link scroll) always passed `behavior: 'smooth'` to
58
+ `scrollIntoView`, which overrides the stylesheet's `scroll-behavior` and so ignored the setting.
59
+ They now scroll instantly when it is set, reading the preference at call time.
60
+ - **Pre-upgrade `display` for six more elements** — `wc-entries`, `wc-image-compare`,
61
+ `wc-split-panel`, `wc-multi-field`, `wc-dismissable` (all `block`), `wc-copy` (`inline-flex`) and
62
+ `wc-marquee` (`block`, as a fallback under the `.marquee` class). Each is declared in its pattern's
63
+ partial inside `:where()` (zero specificity, so any consumer rule wins) and matches the element's
64
+ upgraded `:host`, so registering no longer shifts layout. `wc-split-panel`, which declared no
65
+ display at all, is now a block after upgrade too — a visible change if a layout relied on it being
66
+ inline. `wc-counter` is deliberately left inline.
67
+ - **A dismiss button no longer shows before it can work.**
68
+ `wc-dismissable:not(:defined)` hides `.banner__close`, `.notification__close` and `[data-dismiss]`
69
+ buttons, so with no JS a banner has no dead ✕. (`:not(:defined)` is also true until `elements.js`
70
+ has run, so the button appears on upgrade.)
71
+
72
+ Formats reached for the CSS changes: all formats (the partials are components-layer patterns, inlined
73
+ by the tailwind format as well). Other packages in the `fixed` group: No consumer-facing change in this
74
+ package.
75
+
76
+ ### Patch Changes
77
+
78
+ - 734ab60: EmDash gallery and carousel blocks now derive their element id from the block's `_key`, so two blocks with the same label on one page no longer open each other's dialog. `<wc-entries>` column navigation now honours `prefers-reduced-motion`.
79
+ - 0574388: Bump `emdash`/`@emdash-cms/cloudflare` peers to `>=1.0.0 <2` and `vite-plus` to `^1.0.0`, following
80
+ both projects' GA releases.
81
+
82
+ **Breaking for `@getvitops/emdash` consumers on EmDash 0.x**: the peer range no longer admits
83
+ `emdash@0.31.x`/`@emdash-cms/cloudflare@0.31.x`. Upgrade `emdash` and `@emdash-cms/cloudflare` to
84
+ `^1.0.1` first — nothing in `@getvitops/emdash`'s own plugin surface (`admin.portableTextBlocks`,
85
+ the `hooks.page-fragments:register` capability, `definePlugin()`) changed against EmDash 1.0, so no
86
+ code here needed to change, only the declared range.
87
+
88
+ The `emdash` scaffold template (`vp create @getvitops:emdash`) picks up several fixes alongside
89
+ the version bump:
90
+
91
+ - `tailwindcss`/`@tailwindcss/vite` are now real dependencies — the template selected
92
+ `css.format: 'tailwind'` without installing either, which threw on build.
93
+ - `site.json` now sets `site.icons.sprite: true` plus the full `semantic` list the
94
+ `vitops.actionLink` block's icon picker offers — previously its sprite icons pointed at a file
95
+ the build never generated.
96
+ - `astro.config.mjs` sets `migrations: { runtime: 'check', dev: 'auto' }`, and
97
+ `site-deploy-emdash.yml` runs EmDash 1.0's documented `build → status → apply → deploy → check`
98
+ migration sequence explicitly, rather than relying solely on first-request auto-migration. This
99
+ needs a new GitHub Environment variable, `EMDASH_TARGET_FINGERPRINT` (one per environment),
100
+ which only a human can set — run `emdash migrate --status` against the real deployed database,
101
+ review the printed identity, then `gh variable set EMDASH_TARGET_FINGERPRINT --env <env> --body
102
+ <value>`. Deploys fail clearly on the migration-apply step until it's set.
103
+ - `lint.options.typeAware`/`typeCheck` in the template's own `vite.config.ts` are commented out,
104
+ not deleted: vite-plus 1.0 only honours that option in a project's actual root config, and hard
105
+ errors on it in a nested one (which this file is, sitting inside the `design-system` monorepo).
106
+ Uncomment it once scaffolded into its own repo.
107
+
108
+ No config schema changes. `@getvitops/generator` also switches from the `zod/mini` subpath import
109
+ to the standalone `@zod/mini` package (zod 4.5+; same API) and fixes `validateConfig()` silently
110
+ losing the offending key name on an `unrecognized_keys` error (e.g. a typo'd `site.ads` platform
111
+ now names the bad key instead of reporting a bare "Invalid input").
112
+
113
+ Also fixed: `vp check`'s `tsconfig-error` for `packages/create/templates/emdash/tsconfig.json`
114
+ (`extends: "astro/tsconfigs/strict"`, unresolvable from inside this monorepo since the template
115
+ has no `node_modules` of its own — a pre-existing, structural gap, newly surfaced as a hard error
116
+ by oxlint 1.85's deeper config discovery, and blocking on every commit since this repo's pre-commit
117
+ hook runs `vp check --fix`). Fixed with a new `scripts/link-template-deps.mjs`, wired as the root
118
+ `postinstall` script: symlinks `astro`/`emdash`/`@emdash-cms/cloudflare` into the template's own
119
+ `node_modules/` (resolved fresh from sibling packages that already depend on them for real, not a
120
+ hardcoded `.pnpm` hash) and writes a minimal `astro:content` ambient shim at `.astro/types.d.ts`
121
+ (the same path `astro sync` would generate for real). Both are gitignored and only ever exist
122
+ inside this monorepo's copy of the template — a real scaffold installs its own real copies instead.
123
+ Considered and rejected first: inlining astro's strict preset (drift risk against astro's own
124
+ evolving default) and making the template a real pnpm workspace member (would change how its
125
+ `@getvitops/*: "latest"` deps resolve locally, which `templates.test.ts` exists to protect).
126
+
127
+ **Known gap, not fixed here**: `packages/astro/src/components/container.test.ts` (the
128
+ `AstroContainer`-based render tests) fails to even load under vite-plus 1.0's Vite 8 — direct
129
+ `.astro` imports in a Vitest file need the Astro Vite plugin registered, which nothing in this
130
+ repo's test config actually does (Astro's own docs call for `getViteConfig()`, which doesn't
131
+ compose with vite-plus's own `defineConfig`). A type-level shim
132
+ (`packages/astro/src/components/astro-modules.d.ts`) fixes `vp check`'s type-aware pass, but the
133
+ runtime `vp test` failure for this one file is still open.
134
+
135
+ - fdc3f7e: `<wc-marquee>` adds a "Pause animation" button beside the track (WCAG 2.2.2), and the marquee also pauses while focus is inside it (`data-paused` and `:focus-within`). Pattern CSS: all formats. `<wc-consent>` moves focus into the banner when a visitor opens it from a settings control and returns it on close; an unprompted banner still doesn't take focus. `<wc-image-compare>`'s before/after panes are `role="group"` so their labels are announced. The conversion route format-checks `phone`/`email` beacon payloads and imports the Worker-safe `@getvitops/utils/content` instead of the utils root barrel.
136
+
3
137
  ## 8.2.0-alpha.0
4
138
 
5
139
  ### Minor Changes
6
140
 
7
141
  - c3c1bcb: **New: the design-system editor, mounted inside the EmDash admin.** `vitopsEmdash({
8
- designSystemEditor: { config } })` (`config` is your `site.json`, e.g. `import config from
9
- './site.json' with { type: 'json' }`) adds a `/design-system` admin page mounting
142
+ designSystemEditor: { config } })` (`config` is your `site.json`, e.g. `import config from
143
+ './site.json' with { type: 'json' }`) adds a `/design-system` admin page mounting
10
144
  `<wc-theme-editor>` — the same live token editor `@getvitops/vite`'s dev server has always
11
145
  offered, now usable by the client directly, no dev server required. An edit is visible
12
146
  site-wide immediately (a runtime `:root` override, served via a new plugin API route and
@@ -69,7 +203,7 @@ designSystemEditor: { config } })` (`config` is your `site.json`, e.g. `import c
69
203
  needs a new GitHub Environment variable, `EMDASH_TARGET_FINGERPRINT` (one per environment),
70
204
  which only a human can set — run `emdash migrate --status` against the real deployed database,
71
205
  review the printed identity, then `gh variable set EMDASH_TARGET_FINGERPRINT --env <env> --body
72
- <value>`. Deploys fail clearly on the migration-apply step until it's set.
206
+ <value>`. Deploys fail clearly on the migration-apply step until it's set.
73
207
  - `lint.options.typeAware`/`typeCheck` in the template's own `vite.config.ts` are commented out,
74
208
  not deleted: vite-plus 1.0 only honours that option in a project's actual root config, and hard
75
209
  errors on it in a nested one (which this file is, sitting inside the `design-system` monorepo).
@@ -431,7 +565,7 @@ designSystemEditor: { config } })` (`config` is your `site.json`, e.g. `import c
431
565
  created, with `patterns/counter.css`'s own reduced-motion query as a second belt.
432
566
  - The accessible name stays the final value for the whole animation: `.counter__value` is toggled
433
567
  to the framework's `.sr-only` utility while running, never hidden — `display: none`/`visibility:
434
- hidden` would pull it out of the accessible-name computation along with the visual hiding, and a
568
+ hidden` would pull it out of the accessible-name computation along with the visual hiding, and a
435
569
  screen reader must never be read ~60 intermediate numbers. The animated text lives in a separate
436
570
  `aria-hidden` presentation span it never reaches.
437
571
  - `data-from` (default `0`), `data-duration` (ms, default `1200`), `data-easing` (`linear` |
@@ -978,28 +1112,20 @@ hidden` would pull it out of the accessible-name computation along with the visu
978
1112
  {
979
1113
  "designSystem": {
980
1114
  "themes": {
981
- "default": {
982
- /* … */
983
- },
1115
+ "default": {/* … */},
984
1116
  },
985
1117
  },
986
1118
  "organization": {
987
1119
  "name": "Acme",
988
1120
  "contact": "hq",
989
1121
  "locations": {
990
- "hq": {
991
- /* … */
992
- },
1122
+ "hq": {/* … */},
993
1123
  },
994
1124
  },
995
1125
  "site": {
996
1126
  "defaultLocale": "en",
997
- "domains": {
998
- /* … */
999
- },
1000
- "analytics": {
1001
- /* … */
1002
- },
1127
+ "domains": {/* … */},
1128
+ "analytics": {/* … */},
1003
1129
  },
1004
1130
  }
1005
1131
  ```
@@ -1093,7 +1219,7 @@ hidden` would pull it out of the accessible-name computation along with the visu
1093
1219
  `animate-view` / `animate-scroll` / `animate-trigger` / `transition`, and every effect
1094
1220
  gained an `open-<fx>` state variant. Overlays now state _where they start_
1095
1221
  (`--translate-x-from`) instead of owning a keyframe: `class="drawer drawer--right
1096
- open-fade-in"` composes slide and fade. Applied at zero specificity with identity
1222
+ open-fade-in"` composes slide and fade. Applied at zero specificity with identity
1097
1223
  defaults, so a popover that sets no effect vars is unchanged.
1098
1224
  - **One scrim.** `--scrim` / `--scrim-filter` tokens and `.no-scrim` in `popover.css`,
1099
1225
  replacing 18 `::backdrop` blocks across seven partials. `.drawer--modeless` is aliased.
@@ -1296,8 +1422,8 @@ open-fade-in"` composes slide and fade. Applied at zero specificity with identit
1296
1422
  replace it with `--anim-range`. `--stagger-range-step` is now a viewport length (`5vh`) to match.
1297
1423
 
1298
1424
  **Fixed: every journey ran on a truncated range.** The generator emitted `animation-range: entry
1299
- exit`; lightningcss parsed the end of that shorthand as `exit 0%` rather than the spec's `exit
1300
- 100%`, so the bundle shipped `entry exit 0%`. Journeys reach their `100%` keyframe — the hidden
1425
+ exit`; lightningcss parsed the end of that shorthand as `exit 0%` rather than the spec's `exit
1426
+ 100%`, so the bundle shipped `entry exit 0%`. Journeys reach their `100%` keyframe — the hidden
1301
1427
  `from` state — as the element hits the top of the viewport, which is to say they faded out while
1302
1428
  still fully on screen. Journeys now run `entry calc(50% + 10vh) → exit 100%`: they start on the same
1303
1429
  midpoint pivot as everything else and keep the full entry → hold → exit arc, so the hold sits in the
@@ -1310,7 +1436,7 @@ exit`; lightningcss parsed the end of that shorthand as `exit 0%` rather than th
1310
1436
  so one knob tunes both. If your own config has an empty `slide` base, add the same line.
1311
1437
 
1312
1438
  **Fixed: `reveal-*` on hover was unreachable.** A `hover-reveal-left` element rests at `clip-path:
1313
- inset(0 100% 0 0)`, and `clip-path` clips **hit-testing** as well as painting — so it had zero
1439
+ inset(0 100% 0 0)`, and `clip-path` clips **hit-testing** as well as painting — so it had zero
1314
1440
  hittable area and could never receive the hover that would reveal it. The state variants now match
1315
1441
  the element **or its direct parent**, mirroring what `animation.css` already did for the trigger
1316
1442
  driver (`:is(.is-active, [data-active]) > .animate-trigger`):
@@ -69,3 +69,23 @@
69
69
  .banner[hidden] {
70
70
  display: none;
71
71
  }
72
+
73
+ /*
74
+ * `<wc-dismissable>` wraps a banner or notification — block content — and animates its own
75
+ * opacity/scale on dismiss, so it needs a box of its own: `display: contents` would drop
76
+ * that animation. Pre-upgrade it would otherwise be an inline box around a block child.
77
+ */
78
+ :where(wc-dismissable) {
79
+ display: block;
80
+ }
81
+
82
+ /*
83
+ * A dismiss button does nothing until `<wc-dismissable>` upgrades — an inert control that
84
+ * looks live is worse than none. `:not(:defined)` is true with no JS (and until elements.js
85
+ * has run), so the close affordance only appears once something can act on it. The banner
86
+ * itself stays fully readable. Markup that puts a `[data-dismiss]` button inside the element
87
+ * gets the same treatment, whatever its class.
88
+ */
89
+ wc-dismissable:not(:defined) :is(.banner__close, .notification__close, [data-dismiss]) {
90
+ display: none;
91
+ }
@@ -80,3 +80,8 @@
80
80
  visibility: visible;
81
81
  }
82
82
  }
83
+
84
+ /* Matches `<wc-copy>`'s upgraded `:host` (inline-flex) so registering causes no reflow. */
85
+ :where(wc-copy) {
86
+ display: inline-flex;
87
+ }
@@ -364,3 +364,8 @@ select.custom-select {
364
364
  outline-offset: 2px;
365
365
  }
366
366
  }
367
+
368
+ /* `<wc-multi-field>` is a group of controls, a block. Its shadow `:host` declares no display. */
369
+ :where(wc-multi-field) {
370
+ display: block;
371
+ }
@@ -56,6 +56,13 @@
56
56
  animation-play-state: paused;
57
57
  }
58
58
 
59
+ /* Keyboard users can't hover; focusing anything inside (a link in an item) also pauses, and
60
+ `<wc-marquee>`'s pause button sets `data-paused` (WCAG 2.2.2). */
61
+ &:focus-within .marquee__content,
62
+ &[data-paused] .marquee__content {
63
+ animation-play-state: paused;
64
+ }
65
+
59
66
  &.marquee--reverse {
60
67
  --_marquee-direction: reverse;
61
68
  }
@@ -100,3 +107,12 @@
100
107
  translate: 0 calc(-1 * var(--_marquee-distance, calc(100% + var(--marquee-gap, 1rem))));
101
108
  }
102
109
  }
110
+
111
+ /*
112
+ * The `.marquee` class carries `display: flex` when present (the Astro component always sets
113
+ * it). This is the fallback for a hand-written `<wc-marquee>` without the class, and it is
114
+ * zero specificity so `.marquee` still wins.
115
+ */
116
+ :where(wc-marquee) {
117
+ display: block;
118
+ }
@@ -210,3 +210,14 @@
210
210
  }
211
211
  }
212
212
  }
213
+
214
+ /*
215
+ * Pre-upgrade display for the two elements that live in this partial — see the note on
216
+ * `wc-entries` in table.css. Both are a framed block of content (two panes; two images),
217
+ * and `wc-image-compare`'s upgraded `:host` is already `display: block`, so declaring it
218
+ * here removes the layout shift when it registers. `wc-split-panel` has no `:host` display
219
+ * at all, so this also fixes it after upgrade.
220
+ */
221
+ :where(wc-split-panel, wc-image-compare) {
222
+ display: block;
223
+ }
@@ -104,3 +104,13 @@ table,
104
104
  min-inline-size: var(--table-min-inline-size, 100%);
105
105
  }
106
106
  }
107
+
108
+ /*
109
+ * Before upgrade (or with elements.js absent) an unregistered custom element is
110
+ * `display: inline`, forming an inline box around block content. The default-view
111
+ * `<h3>` + `<dl>` pairs are block content, as is the table the element builds.
112
+ * Zero specificity, so any consumer rule wins.
113
+ */
114
+ :where(wc-entries) {
115
+ display: block;
116
+ }
package/dist/consent.js CHANGED
@@ -1 +1 @@
1
- const e=[`necessary`,`analytics`,`marketing`,`preferences`],t=`vitops:consent`,n=`vitops:consent-open`,r=`vitops_consent`,i=[`analytics`,`marketing`,`preferences`];function a(e){return{analytics:e,marketing:e,preferences:e}}function o(){return{ts:null,choices:a(null)}}function s(e,t){for(let n of e.split(`;`)){let e=n.trim();if(e.slice(0,t.length+1)===`${t}=`)return e.slice(t.length+1)}return null}function c(e){if(!e)return o();let t;try{t=JSON.parse(decodeURIComponent(e))}catch{return o()}if(typeof t!=`object`||!t)return o();let n=t;if(n.v!==2||typeof n.c!=`object`||n.c===null)return o();let r=n.c,s=a(null);for(let e of i){let t=r[e];typeof t==`boolean`&&(s[e]=t)}return{ts:typeof n.ts==`number`?n.ts:null,choices:s}}function l(e){let t={};for(let n of i){let r=e.choices[n];typeof r==`boolean`&&(t[n]=r)}return encodeURIComponent(JSON.stringify({v:2,ts:e.ts??0,c:t}))}function u(e,t){return t===`necessary`||e.choices[t]===!0}function d(e,t){return t===`necessary`||e.choices[t]!==null}function f(e,t){for(let n of t)if(!d(e,n))return!0;return!1}function p(e,t){return{ts:t,choices:a(e)}}function m(e,t,n){let r={...e.choices};for(let e of i){let n=t[e];typeof n==`boolean`&&(r[e]=n)}return{ts:n,choices:r}}function h(e,t){return i.filter(n=>u(e,n)&&!u(t,n))}function g(e){return`; path=/; max-age=15552000; SameSite=Lax${e?`; Secure`:``}`}const _=location.protocol===`https:`;let v=c(s(document.cookie,r));const y=/* @__PURE__ */ new Set,b=/* @__PURE__ */ new WeakSet,x=/* @__PURE__ */ new Set,S=/* @__PURE__ */ new Set,C=/* @__PURE__ */ new Map,w=/* @__PURE__ */ new Set;function T(e){w.has(e)||(w.add(e),queueMicrotask(()=>{document.querySelector(`wc-consent`)||console.warn(`[vitops] something requested the "${e}" consent category, but this page has no <wc-consent> element to ask with — so the request can never be granted and whatever waited on it will not run. Render <CookieConsent /> on every page that gates anything.`)}))}function E(e){S.has(e)||(S.add(e),T(e),B())}function D(){for(let[e,t]of C)if(d(v,e)){C.delete(e);for(let n of t)n(u(v,e))}}const O={idle:[],async:[],interaction:[]},k={idle:!1,async:!0,interaction:!1};function A(e){if(k[e])return;k[e]=!0;let t=O[e];O[e]=[];for(let e of t)e()}function j(e,t){k[e]?t():O[e].push(t)}function M(e){document.readyState===`complete`?e():addEventListener(`load`,e,{once:!0})}M(()=>{let e=globalThis.requestIdleCallback;e?e(()=>A(`idle`),{timeout:3e3}):setTimeout(()=>A(`idle`),1)});{let e=[`pointerdown`,`keydown`,`scroll`,`touchstart`],t=()=>{for(let n of e)removeEventListener(n,t);A(`interaction`)};for(let n of e)addEventListener(n,t,{once:!0,passive:!0});M(()=>setTimeout(()=>A(`interaction`),8e3))}const N=/* @__PURE__ */ new Set([`type`,`src`,`data-vitops-tag`,`data-src`,`data-strategy`,`data-consent`,`data-consent-src`,`data-consent-cookies`]);function P(t){let n=t.getAttribute(`data-consent`);return e.includes(n??``)?n:`analytics`}function F(e){let t=e.textContent??``,n=e.getAttribute(`data-src`),r=t=>{let n=document.createElement(`script`);for(let{name:t,value:r}of Array.from(e.attributes))N.has(t)||n.setAttribute(t,r);t(n),e.parentNode?.insertBefore(n,e.nextSibling)};t.trim()&&r(e=>e.text=t),n&&r(e=>{e.async=!0,e.src=n})}function I(e){if(!b.has(e)){if(b.add(e),x.add(P(e)),e instanceof HTMLScriptElement)F(e);else{let t=e.getAttribute(`data-consent-src`);t&&e.setAttribute(`src`,t)}e.setAttribute(`data-vitops-activated`,``)}}function L(){let e=document.querySelectorAll(`script[data-vitops-tag]:not([data-vitops-activated]),[data-consent-src]:not([data-vitops-activated])`);for(let t of e){let e=t.getAttribute(`data-strategy`);j(e===`async`||e===`interaction`?e:`idle`,()=>{let e=P(t);u(v,e)?I(t):E(e)})}}function R(e){let t=/* @__PURE__ */ new Set;for(let n of e)for(let e of document.querySelectorAll(`[data-consent="${n}"]`))for(let n of(e.getAttribute(`data-consent-cookies`)??``).split(`,`))n.trim()&&t.add(n.trim());if(!t.size)return;let n=document.cookie.split(`;`).map(e=>e.trim().split(`=`)[0]??``),r=location.hostname.split(`.`).slice(-2).join(`.`),i=[``,`; domain=${location.hostname}`,`; domain=.${r}`];for(let e of n)if([...t].some(t=>t.endsWith(`*`)?e.startsWith(t.slice(0,-1)):e===t))for(let t of i)document.cookie=`${e}=; path=/; max-age=0${t}`}function z(e,t){let n=h(v,e),i=v;v=e,t?document.cookie=`${r}=${l(e)}${g(_)}`:document.cookie=`${r}=; path=/; max-age=0`,B(),L(),n.length&&(R(n),n.some(e=>x.has(e)&&u(i,e))&&V.reloadOnRevoke&&location.reload())}function B(){D();for(let e of y)e(v);document.dispatchEvent(new CustomEvent(t,{detail:v}))}const V={categories:e,reloadOnRevoke:!0,get:()=>v,granted:e=>u(v,e),needed:()=>f(v,S),demanded:()=>e.filter(e=>S.has(e)),require(e){return E(e),u(v,e)},request(e){return E(e),d(v,e)?Promise.resolve(u(v,e)):new Promise(t=>{let n=C.get(e);n?n.push(t):C.set(e,[t])})},set:e=>z(m(v,e,Date.now()),!0),acceptAll:()=>z(p(!0,Date.now()),!0),rejectAll:()=>z(p(!1,Date.now()),!0),reset:()=>z(o(),!1),open:()=>document.dispatchEvent(new CustomEvent(n)),subscribe(e){return y.add(e),e(v),()=>void y.delete(e)}},H=window.vitopsConsent?.q;if(window.vitopsConsent=V,H)for(let[e,t]of H)t?V.request(e).then(t):V.require(e);document.addEventListener(`click`,e=>{e.target?.closest?.(`[data-consent-open]`)&&(e.preventDefault(),V.open())}),document.addEventListener(`astro:page-load`,L),L(),B();var U=class extends HTMLElement{#e;#t=`prompt`;#n=!1;get#r(){return window.vitopsConsent}get#i(){return this.querySelector(`[popover]`)}connectedCallback(){let e=this.#r;if(!e)return;let t=this.#i;t&&typeof t.showPopover!=`function`&&t.removeAttribute(`popover`),this.getAttribute(`reload-on-revoke`)===`false`&&(e.reloadOnRevoke=!1),this.addEventListener(`click`,this.#p),this.addEventListener(`submit`,this.#o),document.addEventListener(n,this.#a),this.#e=e.subscribe(()=>{this.#n&&this.#t===`settings`?this.#f():e.needed()?this.#u(`prompt`):this.#d()})}disconnectedCallback(){this.removeEventListener(`click`,this.#p),this.removeEventListener(`submit`,this.#o),document.removeEventListener(n,this.#a),this.#e?.()}#a=()=>this.#u(`settings`);#o=e=>e.preventDefault();#s(e){return this.querySelector(`[data-consent-row="${e}"]`)??this.#c(e)?.closest(`label`)??null}#c(e){return this.querySelector(`input[name="${e}"]`)}#l(e){let t=this.#r;if(!t)return[];if(e===`settings`){let e=i.filter(e=>this.#s(e));return e.length?e:[...i]}let n=t.get();return i.filter(e=>t.demanded().includes(e)&&!d(n,e))}#u(e){if(!this.#l(e).length)return;this.#t=e,this.#f(),this.hidden=!1,this.#n=!0;let t=this.#i;t?.isConnected&&!t.matches(`:popover-open`)&&t.showPopover()}#d(){this.#n=!1,this.#i?.hidePopover?.(),this.hidden=!0}#f(){let e=this.#r;if(!e)return;let t=this.#l(this.#t);for(let n of i){let r=this.#s(n);r&&(r.hidden=!t.includes(n));let i=this.#c(n);i&&(i.checked=e.granted(n))}this.#i?.classList.toggle(`consent--single`,t.length<2)}#p=e=>{let t=this.#r,n=e.target?.closest?.(`[data-consent-accept],[data-consent-reject],[data-consent-save]`);if(!t||!n)return;e.preventDefault();let r=this.#l(this.#t);n.hasAttribute(`data-consent-accept`)?t.set(this.#m(r,()=>!0)):n.hasAttribute(`data-consent-reject`)?t.set(this.#m(r,()=>!1)):t.set(this.#m(r,e=>this.#c(e)?.checked===!0)),this.#d()};#m(e,t){let n={};for(let r of e)n[r]=t(r);return n}};customElements.get(`wc-consent`)||customElements.define(`wc-consent`,U);export{e as CATEGORIES,t as CONSENT_EVENT,n as CONSENT_OPEN_EVENT,r as COOKIE_NAME,V as consent};
1
+ const e=[`necessary`,`analytics`,`marketing`,`preferences`],t=`vitops:consent`,n=`vitops:consent-open`,r=`vitops_consent`,i=[`analytics`,`marketing`,`preferences`];function a(e){return{analytics:e,marketing:e,preferences:e}}function o(){return{ts:null,choices:a(null)}}function s(e,t){for(let n of e.split(`;`)){let e=n.trim();if(e.slice(0,t.length+1)===`${t}=`)return e.slice(t.length+1)}return null}function c(e){if(!e)return o();let t;try{t=JSON.parse(decodeURIComponent(e))}catch{return o()}if(typeof t!=`object`||!t)return o();let n=t;if(n.v!==2||typeof n.c!=`object`||n.c===null)return o();let r=n.c,s=a(null);for(let e of i){let t=r[e];typeof t==`boolean`&&(s[e]=t)}return{ts:typeof n.ts==`number`?n.ts:null,choices:s}}function l(e){let t={};for(let n of i){let r=e.choices[n];typeof r==`boolean`&&(t[n]=r)}return encodeURIComponent(JSON.stringify({v:2,ts:e.ts??0,c:t}))}function u(e,t){return t===`necessary`||e.choices[t]===!0}function d(e,t){return t===`necessary`||e.choices[t]!==null}function f(e,t){for(let n of t)if(!d(e,n))return!0;return!1}function p(e,t){return{ts:t,choices:a(e)}}function m(e,t,n){let r={...e.choices};for(let e of i){let n=t[e];typeof n==`boolean`&&(r[e]=n)}return{ts:n,choices:r}}function h(e,t){return i.filter(n=>u(e,n)&&!u(t,n))}function g(e){return`; path=/; max-age=15552000; SameSite=Lax${e?`; Secure`:``}`}const _=location.protocol===`https:`;let v=c(s(document.cookie,r));const y=/* @__PURE__ */ new Set,b=/* @__PURE__ */ new WeakSet,x=/* @__PURE__ */ new Set,S=/* @__PURE__ */ new Set,C=/* @__PURE__ */ new Map,w=/* @__PURE__ */ new Set;function T(e){w.has(e)||(w.add(e),queueMicrotask(()=>{document.querySelector(`wc-consent`)||console.warn(`[vitops] something requested the "${e}" consent category, but this page has no <wc-consent> element to ask with — so the request can never be granted and whatever waited on it will not run. Render <CookieConsent /> on every page that gates anything.`)}))}function E(e){S.has(e)||(S.add(e),T(e),B())}function D(){for(let[e,t]of C)if(d(v,e)){C.delete(e);for(let n of t)n(u(v,e))}}const O={idle:[],async:[],interaction:[]},k={idle:!1,async:!0,interaction:!1};function A(e){if(k[e])return;k[e]=!0;let t=O[e];O[e]=[];for(let e of t)e()}function j(e,t){k[e]?t():O[e].push(t)}function M(e){document.readyState===`complete`?e():addEventListener(`load`,e,{once:!0})}M(()=>{let e=globalThis.requestIdleCallback;e?e(()=>A(`idle`),{timeout:3e3}):setTimeout(()=>A(`idle`),1)});{let e=[`pointerdown`,`keydown`,`scroll`,`touchstart`],t=()=>{for(let n of e)removeEventListener(n,t);A(`interaction`)};for(let n of e)addEventListener(n,t,{once:!0,passive:!0});M(()=>setTimeout(()=>A(`interaction`),8e3))}const N=/* @__PURE__ */ new Set([`type`,`src`,`data-vitops-tag`,`data-src`,`data-strategy`,`data-consent`,`data-consent-src`,`data-consent-cookies`]);function P(t){let n=t.getAttribute(`data-consent`);return e.includes(n??``)?n:`analytics`}function F(e){let t=e.textContent??``,n=e.getAttribute(`data-src`),r=t=>{let n=document.createElement(`script`);for(let{name:t,value:r}of Array.from(e.attributes))N.has(t)||n.setAttribute(t,r);t(n),e.parentNode?.insertBefore(n,e.nextSibling)};t.trim()&&r(e=>e.text=t),n&&r(e=>{e.async=!0,e.src=n})}function I(e){if(!b.has(e)){if(b.add(e),x.add(P(e)),e instanceof HTMLScriptElement)F(e);else{let t=e.getAttribute(`data-consent-src`);t&&e.setAttribute(`src`,t)}e.setAttribute(`data-vitops-activated`,``)}}function L(){let e=document.querySelectorAll(`script[data-vitops-tag]:not([data-vitops-activated]),[data-consent-src]:not([data-vitops-activated])`);for(let t of e){let e=t.getAttribute(`data-strategy`);j(e===`async`||e===`interaction`?e:`idle`,()=>{let e=P(t);u(v,e)?I(t):E(e)})}}function R(e){let t=/* @__PURE__ */ new Set;for(let n of e)for(let e of document.querySelectorAll(`[data-consent="${n}"]`))for(let n of(e.getAttribute(`data-consent-cookies`)??``).split(`,`))n.trim()&&t.add(n.trim());if(!t.size)return;let n=document.cookie.split(`;`).map(e=>e.trim().split(`=`)[0]??``),r=location.hostname.split(`.`).slice(-2).join(`.`),i=[``,`; domain=${location.hostname}`,`; domain=.${r}`];for(let e of n)if([...t].some(t=>t.endsWith(`*`)?e.startsWith(t.slice(0,-1)):e===t))for(let t of i)document.cookie=`${e}=; path=/; max-age=0${t}`}function z(e,t){let n=h(v,e),i=v;v=e,t?document.cookie=`${r}=${l(e)}${g(_)}`:document.cookie=`${r}=; path=/; max-age=0`,B(),L(),n.length&&(R(n),n.some(e=>x.has(e)&&u(i,e))&&V.reloadOnRevoke&&location.reload())}function B(){D();for(let e of y)e(v);document.dispatchEvent(new CustomEvent(t,{detail:v}))}const V={categories:e,reloadOnRevoke:!0,get:()=>v,granted:e=>u(v,e),needed:()=>f(v,S),demanded:()=>e.filter(e=>S.has(e)),require(e){return E(e),u(v,e)},request(e){return E(e),d(v,e)?Promise.resolve(u(v,e)):new Promise(t=>{let n=C.get(e);n?n.push(t):C.set(e,[t])})},set:e=>z(m(v,e,Date.now()),!0),acceptAll:()=>z(p(!0,Date.now()),!0),rejectAll:()=>z(p(!1,Date.now()),!0),reset:()=>z(o(),!1),open:()=>document.dispatchEvent(new CustomEvent(n)),subscribe(e){return y.add(e),e(v),()=>void y.delete(e)}},H=window.vitopsConsent?.q;if(window.vitopsConsent=V,H)for(let[e,t]of H)t?V.request(e).then(t):V.require(e);document.addEventListener(`click`,e=>{e.target?.closest?.(`[data-consent-open]`)&&(e.preventDefault(),V.open())}),document.addEventListener(`astro:page-load`,L),L(),B();var U=class extends HTMLElement{#e;#t=`prompt`;#n=!1;get#r(){return window.vitopsConsent}get#i(){return this.querySelector(`[popover]`)}connectedCallback(){let e=this.#r;if(!e)return;let t=this.#i;t&&typeof t.showPopover!=`function`&&t.removeAttribute(`popover`),this.getAttribute(`reload-on-revoke`)===`false`&&(e.reloadOnRevoke=!1),this.addEventListener(`click`,this.#m),this.addEventListener(`submit`,this.#o),document.addEventListener(n,this.#a),this.#e=e.subscribe(()=>{this.#n&&this.#t===`settings`?this.#p():e.needed()?this.#u(`prompt`):this.#f()})}disconnectedCallback(){this.removeEventListener(`click`,this.#m),this.removeEventListener(`submit`,this.#o),document.removeEventListener(n,this.#a),this.#e?.()}#a=()=>this.#u(`settings`);#o=e=>e.preventDefault();#s(e){return this.querySelector(`[data-consent-row="${e}"]`)??this.#c(e)?.closest(`label`)??null}#c(e){return this.querySelector(`input[name="${e}"]`)}#l(e){let t=this.#r;if(!t)return[];if(e===`settings`){let e=i.filter(e=>this.#s(e));return e.length?e:[...i]}let n=t.get();return i.filter(e=>t.demanded().includes(e)&&!d(n,e))}#u(e){if(!this.#l(e).length)return;this.#t=e,this.#p(),this.hidden=!1,this.#n=!0;let t=this.#i;t?.isConnected&&!t.matches(`:popover-open`)&&t.showPopover(),e===`settings`&&t&&(this.#d=document.activeElement instanceof HTMLElement?document.activeElement:null,t.tabIndex=-1,t.focus({preventScroll:!0}))}#d=null;#f(){this.#n=!1,this.#i?.hidePopover?.(),this.hidden=!0;let e=this.#d;this.#d=null,e?.isConnected&&e.focus({preventScroll:!0})}#p(){let e=this.#r;if(!e)return;let t=this.#l(this.#t);for(let n of i){let r=this.#s(n);r&&(r.hidden=!t.includes(n));let i=this.#c(n);i&&(i.checked=e.granted(n))}this.#i?.classList.toggle(`consent--single`,t.length<2)}#m=e=>{let t=this.#r,n=e.target?.closest?.(`[data-consent-accept],[data-consent-reject],[data-consent-save]`);if(!t||!n)return;e.preventDefault();let r=this.#l(this.#t);n.hasAttribute(`data-consent-accept`)?t.set(this.#h(r,()=>!0)):n.hasAttribute(`data-consent-reject`)?t.set(this.#h(r,()=>!1)):t.set(this.#h(r,e=>this.#c(e)?.checked===!0)),this.#f()};#h(e,t){let n={};for(let r of e)n[r]=t(r);return n}};customElements.get(`wc-consent`)||customElements.define(`wc-consent`,U);export{e as CATEGORIES,t as CONSENT_EVENT,n as CONSENT_OPEN_EVENT,r as COOKIE_NAME,V as consent};