@hagicode/hagilight-starlight 0.20.1-dev.196.1.d67f4e4 → 0.20.1-dev.200.1.6beb099

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/Footer.astro CHANGED
@@ -18,6 +18,10 @@ interface Props {
18
18
  randomAvatar?: boolean;
19
19
  /** Adds the "Cookie settings" entry. Set only when the site mounts the consent popup. Defaults to `false`. */
20
20
  consent?: boolean;
21
+ /** A regional site: the "Cookie settings" entry is rendered hidden and revealed by the consent popup's decision. */
22
+ regional?: boolean;
23
+ /** A regional site with session replay opt-in: renders the replay entry, hidden until the decision reveals it. */
24
+ replayEntry?: boolean;
21
25
  }
22
26
 
23
27
  const {
@@ -29,6 +33,8 @@ const {
29
33
  avatar,
30
34
  randomAvatar,
31
35
  consent = false,
36
+ regional = false,
37
+ replayEntry = false,
32
38
  } = Astro.props;
33
39
  const consentCopy = resolveConsentCopy(locale);
34
40
  const buildItems = buildInfoItems(buildInfo);
@@ -105,7 +111,23 @@ const resolvedLinks = resolveSiteLinks(currentLanguage ?? locale, {
105
111
  >{link.label}</a>
106
112
  ))}
107
113
  {consent && (
108
- <button type="button" class="hagilight-consent-entry" data-consent-preferences>{consentCopy.footerEntry}</button>
114
+ <button
115
+ type="button"
116
+ class="hagilight-consent-entry"
117
+ data-consent-preferences
118
+ data-consent-entry={regional ? 'settings' : undefined}
119
+ hidden={regional}
120
+ >{consentCopy.footerEntry}</button>
121
+ )}
122
+ {consent && replayEntry && (
123
+ <button
124
+ type="button"
125
+ class="hagilight-consent-entry"
126
+ data-consent-preferences
127
+ data-consent-mode="replay"
128
+ data-consent-entry="replay"
129
+ hidden
130
+ >{consentCopy.replayEntry}</button>
109
131
  )}
110
132
  </nav>
111
133
  </div>
@@ -176,6 +198,10 @@ const resolvedLinks = resolveSiteLinks(currentLanguage ?? locale, {
176
198
  cursor: pointer;
177
199
  }
178
200
 
201
+ .hagilight-consent-entry[hidden] {
202
+ display: none;
203
+ }
204
+
179
205
  /* Footer columns: four level, purpose-based columns. Each column owns a rule that
180
206
  draws in as it scrolls into view and runs the full width while the column is
181
207
  used; the other columns' headings step back. */
package/README.md CHANGED
@@ -190,3 +190,30 @@ See the [repository documentation](https://github.com/HagiCode-org/hagilight#rea
190
190
  ### Consent
191
191
 
192
192
  `analytics.consent` (`enabled`, `privacyUrl`, `policyVersion`) is on by default whenever a provider is enabled. The plugin mounts the popup through its generated Footer and Head, on every page including 404, and passes `consent` to the Google Analytics, 51LA and PostHog components. `enabled: false` restores immediate loading; invalid `privacyUrl` or `policyVersion` values fail with the option named. See the core README, "Analytics consent", for the storage names and rules.
193
+
194
+ #### Regional consent
195
+
196
+ Set `analytics.consent.regions` to let visitors outside the consent-required countries start analytics without a click. Without it every visitor sees the popup and the output is unchanged. All options sit under `analytics.consent`, with the same names, defaults and errors as the Docusaurus and Astro integrations:
197
+
198
+ | Option | Default | Values and errors |
199
+ | --- | --- | --- |
200
+ | `regions` | absent (global consent) | `'default'`, or a non-empty array of upper-case ISO 3166-1 alpha-2 codes that replaces the default set. Other values fail with an error naming `analytics.consent.regions`, which also fails when `enabled` is `false`. |
201
+ | `countryCode` | none | One upper-case ISO code that skips detection. Needs `regions`. |
202
+ | `regionHook` | none | Name of a function on `window` that returns `'required'`, `'notRequired'`, `'unknown'`, a boolean or a promise of one of these. Needs `regions`. |
203
+ | `settingsEntry` | `'requiredOnly'` | `always` also shows Cookie settings to visitors who do not need consent. Needs `regions`. |
204
+ | `sessionReplay` | `'optIn'` | `projectDefault` follows the PostHog project. `off` never runs replay in these regions and renders no entry. Needs `regions`. |
205
+
206
+ A regional-only option set without `regions` fails with an error that names the option and says `regions` is required.
207
+
208
+ ```js
209
+ hagilight({ analytics: { consent: { regions: 'default' } } });
210
+ hagilight({ analytics: { consent: { regions: ['DE', 'FR'], settingsEntry: 'always' } } });
211
+ ```
212
+
213
+ The default consent-required set is the EU27 (AT BE BG HR CY CZ DK EE FI FR DE GR HU IE IT LV LT LU MT NL PL PT RO SK SI ES SE), the EEA (IS LI NO), GB, CH and BR. It is a product configuration, not legal advice, and needs confirmation by counsel. A site list replaces it entirely.
214
+
215
+ Resolution runs in this order: `countryCode`, `regionHook`, the same-origin `/cdn-cgi/trace` `loc` line (1200 ms timeout, no credentials, the IP line is never read), then a time-zone fallback. The whole decision is bounded by 1800 ms. **Fail-closed:** an unknown location, a failing hook, a timeout or an invalid code means consent is required, so the popup appears and no provider loads until the visitor accepts.
216
+
217
+ `/cdn-cgi/trace` answers only for hostnames proxied through Cloudflare. On `www` it returned no `loc` in verification, so the time-zone fallback decides there unless the site sets `countryCode` or `regionHook`.
218
+
219
+ With `sessionReplay: 'optIn'`, replay stays off in not-required regions until the visitor allows it from the footer entry "Allow session replay". Google Consent Mode and cookieless pings stay off in every region.
package/dist/index.d.ts CHANGED
@@ -40,6 +40,19 @@ export interface HagilightConsentOptions {
40
40
  privacyUrl?: string;
41
41
  /** Defaults to `1`. Changing it asks every visitor again. 1 to 32 letters, digits, dots, underscores or hyphens. */
42
42
  policyVersion?: string;
43
+ /**
44
+ * Regional consent. `default` uses the built-in consent-required set; an array of upper-case ISO 3166-1 alpha-2 codes
45
+ * replaces it. Absent keeps the global popup. Unknown location means consent is required.
46
+ */
47
+ regions?: 'default' | readonly string[];
48
+ /** An ISO 3166-1 alpha-2 code that fixes every visitor's region and skips detection. Needs `regions`. */
49
+ countryCode?: string;
50
+ /** The name of a function defined on `window` that returns the visitor's region. Needs `regions`. */
51
+ regionHook?: string;
52
+ /** Defaults to `requiredOnly`, which shows the Cookie settings entry only where consent is required. `always` shows it everywhere. */
53
+ settingsEntry?: 'requiredOnly' | 'always';
54
+ /** Defaults to `optIn`, which offers session replay to visitors where consent is not required. `projectDefault` or `off` change that. */
55
+ sessionReplay?: 'optIn' | 'projectDefault' | 'off';
43
56
  }
44
57
  export interface HagilightPostHogOptions {
45
58
  /** Defaults to true when the provider options are omitted, otherwise to whether apiKey is set. */
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { resolveBuildInfo } from '@hagicode/hagilight-core/build-info-resolver';
6
6
  import { resolveFooterAvatarOptions } from '@hagicode/hagilight-core/footer-avatar';
7
7
  import { checkContentImageRegistration, registerContentImages } from '@hagicode/hagilight-core/content-images';
8
8
  import { DEFAULT_POSTHOG_API_KEY, POSTHOG_API_KEY_PATTERN } from '@hagicode/hagilight-core/posthog';
9
- import { validatePolicyVersion, validatePrivacyUrl, } from '@hagicode/hagilight-core/consent-markup';
9
+ import { resolveRegionalOptions, validatePolicyVersion, validatePrivacyUrl, } from '@hagicode/hagilight-core/consent-markup';
10
10
  import { resolveRssLocales } from '@hagicode/hagilight-core/rss';
11
11
  import { RSS_OWNER, registerStarlightRssOwner, } from '@hagicode/hagilight-core/rss-ownership';
12
12
  import { isValidSeoImageReference, resolveSeoImageUrl } from '@hagicode/hagilight-core/seo';
@@ -59,15 +59,18 @@ const contentWidthHeadScript = `(() => {
59
59
  })();`;
60
60
  /**
61
61
  * Consent is on by default whenever a provider is enabled, and `enabled: false` restores immediate loading. Invalid
62
- * values throw from the core validators, which name the option.
62
+ * values throw from the core validators, which name the option. Regional options are validated by the core too, so
63
+ * every integration reports the same messages; they resolve to `undefined` on a global site.
63
64
  */
64
65
  function resolveConsentOptions(config, providers) {
65
66
  const record = optionalRecord(config, 'analytics consent');
66
67
  const requested = optionalBoolean(record, 'enabled', 'analytics consent enabled');
68
+ const enabled = providers.length > 0 && (requested ?? true);
67
69
  return {
68
- enabled: providers.length > 0 && (requested ?? true),
70
+ enabled,
69
71
  privacyUrl: validatePrivacyUrl(record?.privacyUrl),
70
72
  policyVersion: validatePolicyVersion(record?.policyVersion),
73
+ regional: resolveRegionalOptions(record, record?.enabled !== false),
71
74
  providers,
72
75
  };
73
76
  }
@@ -283,8 +286,16 @@ import options from '${optionsId}';
283
286
  const consentImport = serializedOptions.consentEnabled
284
287
  ? `import CookieConsent from ${JSON.stringify(coreExport('CookieConsent'))};`
285
288
  : '';
289
+ // Regional sites also pass their options to the popup, whose descriptor the footer entries read at runtime.
290
+ const consentRegionalAttribute = serializedOptions.consentRegional ? ' regional={options.consentRegional}' : '';
286
291
  const consentRender = serializedOptions.consentEnabled
287
- ? '<CookieConsent providers={options.consentProviders} privacyUrl={options.consentPrivacyUrl} policyVersion={options.consentPolicyVersion} locale={Astro.locals?.starlightRoute?.locale} />'
292
+ ? `<CookieConsent providers={options.consentProviders} privacyUrl={options.consentPrivacyUrl} policyVersion={options.consentPolicyVersion} locale={Astro.locals?.starlightRoute?.locale}${consentRegionalAttribute} />`
293
+ : '';
294
+ // Regional entries render hidden; the popup reveals them from its descriptor. The replay entry needs opt-in.
295
+ const footerRegionalAttributes = serializedOptions.consentRegional
296
+ ? ` regional={true}
297
+ replayEntry={${serializedOptions.consentRegional.sessionReplay === 'optIn'}}
298
+ `
288
299
  : '';
289
300
  const footerSource = `---
290
301
  import Footer from ${JSON.stringify(footerPath)};
@@ -303,7 +314,7 @@ import options from '${optionsId}';
303
314
  avatar={options.footerAvatar.avatar}
304
315
  randomAvatar={options.footerAvatar.randomAvatar}
305
316
  ${consentAttribute ? ` consent={true}
306
- ` : ''}/>
317
+ ` : ''}${footerRegionalAttributes}/>
307
318
  ${promotionRender}
308
319
  ${analyticsRender}
309
320
  ${consentRender}
@@ -509,6 +520,7 @@ export default function hagilight(options = {}) {
509
520
  consentProviders: consent.providers,
510
521
  consentPrivacyUrl: consent.privacyUrl,
511
522
  consentPolicyVersion: consent.policyVersion,
523
+ consentRegional: consent.regional,
512
524
  posthogSearchQuery,
513
525
  aiDisclosures,
514
526
  seoEnabled: seo.enabled,
@@ -612,7 +624,12 @@ export default function hagilight(options = {}) {
612
624
  fiftyOneLa: serializedOptions.fiftyOneLaId !== undefined,
613
625
  posthog: serializedOptions.posthogApiKey !== undefined,
614
626
  },
615
- features: { seo: seo.enabled, promoto: promotoEnabled, consent: consent.enabled },
627
+ features: {
628
+ seo: seo.enabled,
629
+ promoto: promotoEnabled,
630
+ consent: consent.enabled,
631
+ consentMode: consent.enabled ? (consent.regional ? 'regional' : 'globalRequired') : undefined,
632
+ },
616
633
  }, unregisterSiteMetadataOwner));
617
634
  updateConfig({
618
635
  ...(config.logo === undefined ? { logo: { src: defaultLogo, alt: 'HagiCode' } } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hagicode/hagilight-starlight",
3
- "version": "0.20.1-dev.196.1.d67f4e4",
3
+ "version": "0.20.1-dev.200.1.6beb099",
4
4
  "type": "module",
5
5
  "description": "Starlight plugin and components for HagiCode documentation",
6
6
  "repository": {
@@ -53,7 +53,7 @@
53
53
  "assets/showcase/*"
54
54
  ],
55
55
  "dependencies": {
56
- "@hagicode/hagilight-core": "0.20.1-dev.196.1.d67f4e4"
56
+ "@hagicode/hagilight-core": "0.20.1-dev.200.1.6beb099"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "@astrojs/starlight": "^0.42.4",