@c15t/astro 3.0.0-alpha.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.
Files changed (117) hide show
  1. package/AGENTS.md +86 -0
  2. package/SKILL.md +14 -0
  3. package/dist/api/handlers.js +91 -0
  4. package/dist/api/index.js +3 -0
  5. package/dist/api/init.js +10 -0
  6. package/dist/api/manifest-init.js +103 -0
  7. package/dist/api/manifest.js +10 -0
  8. package/dist/browser/iab.js +5 -0
  9. package/dist/browser/inline-scripts.js +47 -0
  10. package/dist/client.js +276 -0
  11. package/dist/index.js +5 -0
  12. package/dist/integration.js +170 -0
  13. package/dist/libs/cookies.js +9 -0
  14. package/dist/middleware-handler.js +31 -0
  15. package/dist/middleware.js +6 -0
  16. package/dist/mode.js +50 -0
  17. package/dist/server.js +291 -0
  18. package/dist/styles.css +2 -0
  19. package/dist/types.js +0 -0
  20. package/dist/ui/adapter.js +37 -0
  21. package/dist/ui/provider-props.js +12 -0
  22. package/dist/ui/react.js +34 -0
  23. package/dist/ui/svelte.js +36 -0
  24. package/dist/ui/vue.js +38 -0
  25. package/dist-types/api/handlers.d.ts +85 -0
  26. package/dist-types/api/index.d.ts +13 -0
  27. package/dist-types/api/init.d.ts +10 -0
  28. package/dist-types/api/manifest-init.d.ts +73 -0
  29. package/dist-types/api/manifest.d.ts +9 -0
  30. package/dist-types/browser/iab.d.ts +14 -0
  31. package/dist-types/browser/inline-scripts.d.ts +38 -0
  32. package/dist-types/client.d.ts +152 -0
  33. package/dist-types/index.d.ts +33 -0
  34. package/dist-types/integration.d.ts +53 -0
  35. package/dist-types/libs/cookies.d.ts +15 -0
  36. package/dist-types/middleware-handler.d.ts +35 -0
  37. package/dist-types/middleware.d.ts +20 -0
  38. package/dist-types/mode.d.ts +75 -0
  39. package/dist-types/server.d.ts +130 -0
  40. package/dist-types/types.d.ts +305 -0
  41. package/dist-types/ui/adapter.d.ts +122 -0
  42. package/dist-types/ui/provider-props.d.ts +37 -0
  43. package/dist-types/ui/react.d.ts +19 -0
  44. package/dist-types/ui/svelte.d.ts +21 -0
  45. package/dist-types/ui/vue.d.ts +19 -0
  46. package/docs/README.md +86 -0
  47. package/docs/assets/v3/brand-bar.png +0 -0
  48. package/docs/assets/v3/brand-card.png +0 -0
  49. package/docs/assets/v3/choice-wall.png +0 -0
  50. package/docs/assets/v3/mobile-card.png +0 -0
  51. package/docs/assets/v3/preferences.png +0 -0
  52. package/docs/customization/overview.md +45 -0
  53. package/docs/customization/recipes.md +79 -0
  54. package/docs/customization/slots.md +55 -0
  55. package/docs/customization/tokens.md +76 -0
  56. package/docs/customization/translations.md +49 -0
  57. package/docs/frameworks/astro/quickstart.md +186 -0
  58. package/docs/guides/consent-state.md +60 -0
  59. package/docs/guides/data-fetching.md +163 -0
  60. package/docs/guides/deployment-modes.md +63 -0
  61. package/docs/guides/troubleshooting.md +68 -0
  62. package/docs/guides/verify-consent.md +62 -0
  63. package/docs/integrations/adobe-analytics.md +294 -0
  64. package/docs/integrations/ahrefs-analytics.md +291 -0
  65. package/docs/integrations/amplitude.md +292 -0
  66. package/docs/integrations/building-integrations.md +47 -0
  67. package/docs/integrations/clearbit.md +291 -0
  68. package/docs/integrations/cloudflare-web-analytics.md +292 -0
  69. package/docs/integrations/crisp.md +294 -0
  70. package/docs/integrations/databuddy.md +311 -0
  71. package/docs/integrations/fathom-analytics.md +293 -0
  72. package/docs/integrations/google-maps.md +384 -0
  73. package/docs/integrations/google-tag-manager.md +306 -0
  74. package/docs/integrations/google-tag.md +305 -0
  75. package/docs/integrations/heap.md +292 -0
  76. package/docs/integrations/hightouch.md +293 -0
  77. package/docs/integrations/hotjar.md +292 -0
  78. package/docs/integrations/intercom.md +285 -0
  79. package/docs/integrations/linkedin-insights.md +291 -0
  80. package/docs/integrations/logrocket.md +293 -0
  81. package/docs/integrations/matomo-analytics.md +316 -0
  82. package/docs/integrations/meta-pixel.md +285 -0
  83. package/docs/integrations/microsoft-clarity.md +292 -0
  84. package/docs/integrations/microsoft-uet.md +291 -0
  85. package/docs/integrations/mixpanel-analytics.md +295 -0
  86. package/docs/integrations/openai-pixel.md +441 -0
  87. package/docs/integrations/overview.md +111 -0
  88. package/docs/integrations/pirsch.md +295 -0
  89. package/docs/integrations/plausible-analytics.md +295 -0
  90. package/docs/integrations/posthog.md +397 -0
  91. package/docs/integrations/promptwatch.md +291 -0
  92. package/docs/integrations/reddit-pixel.md +294 -0
  93. package/docs/integrations/rudderstack.md +328 -0
  94. package/docs/integrations/rybbit-analytics.md +296 -0
  95. package/docs/integrations/segment.md +292 -0
  96. package/docs/integrations/snapchat-pixel.md +293 -0
  97. package/docs/integrations/tiktok-pixel.md +291 -0
  98. package/docs/integrations/umami-analytics.md +295 -0
  99. package/docs/integrations/vercel-analytics.md +293 -0
  100. package/docs/integrations/x-pixel.md +291 -0
  101. package/docs/integrations/youtube.md +405 -0
  102. package/docs/upgrade-v3.md +334 -0
  103. package/package.json +184 -0
  104. package/src/components/branding.astro +152 -0
  105. package/src/components/consent-script.astro +34 -0
  106. package/src/components/iab-panel.astro +43 -0
  107. package/src/components/iab-prompt.astro +316 -0
  108. package/src/components/islands/iab-dialog-surface.svelte +15 -0
  109. package/src/components/islands/iab-dialog-surface.tsx +25 -0
  110. package/src/components/islands/iab-dialog-surface.vue +20 -0
  111. package/src/components/islands/panel-surface.svelte +57 -0
  112. package/src/components/islands/panel-surface.tsx +67 -0
  113. package/src/components/islands/panel-surface.vue +39 -0
  114. package/src/components/panel-trigger.astro +48 -0
  115. package/src/components/panel.astro +65 -0
  116. package/src/components/prompt-deferred.astro +54 -0
  117. package/src/components/prompt.astro +325 -0
package/AGENTS.md ADDED
@@ -0,0 +1,86 @@
1
+ # @c15t/astro
2
+
3
+ > Astro v3 static and server integration, dialog adapters and runtime ownership.
4
+
5
+ These docs ship inside the package so coding agents can read them offline. Open the topic file you need from the list below — paths are relative to this file.
6
+
7
+ ## Using these docs
8
+
9
+ These docs describe v3. Start with Inth hosted setup, identify the framework, router and deployment, then read its quickstart. Static sites can use Inth directly. Use page Markdown and package-bundled docs for targeted context. Verify scripts, rejection, reload and preferences; banner visibility alone is insufficient.
10
+
11
+ ## Start here
12
+
13
+ - [Astro quickstart](./docs/frameworks/astro/quickstart.md)
14
+ - [Customize your consent interface](./docs/customization/overview.md): Choose presentation, theme tokens, slots or custom markup for the change you need.
15
+ - [Verify consent before shipping](./docs/guides/verify-consent.md): Test requests, policy resolution, persistence, navigation and preference changes in a production build.
16
+ - [Upgrade to v3 policies](./docs/upgrade-v3.md): Migrate policy configuration, consent records, callbacks, and custom transports to the v3 policy system.
17
+
18
+ ## More documentation
19
+
20
+ [Documentation index](https://c15t.com/docs/llms.txt) · [Full Markdown context](https://c15t.com/llms-full.txt). Prefer the index and individual pages for focused tasks.
21
+
22
+ ## Frameworks
23
+
24
+ - [Quickstart](./docs/frameworks/astro/quickstart.md): Add c15t to static or server-rendered Astro pages and choose the framework used by preference dialogs.
25
+
26
+ ## Guides
27
+
28
+ - [Understand consent state](./docs/guides/consent-state.md): Distinguish policy resolution, effective permissions, explicit choices, notices and privacy signals.
29
+ - [Data fetching and transports](./docs/guides/data-fetching.md): Choose cached manifests, backend init or offline policy resolution, and understand where consent records are saved.
30
+ - [Choose a deployment mode](./docs/guides/deployment-modes.md): Choose who runs your consent backend, then select manifest, init or offline resolution for your deployment.
31
+ - [Troubleshoot consent](./docs/guides/troubleshooting.md): Diagnose missing banners, early vendor requests, lost choices and hydration differences.
32
+ - [Verify consent before shipping](./docs/guides/verify-consent.md): Test requests, policy resolution, persistence, navigation and preference changes in a production build.
33
+
34
+ ## Customization
35
+
36
+ - [Customize your consent interface](./docs/customization/overview.md): Choose presentation, theme tokens, slots or custom markup for the change you need.
37
+ - [Banner styling recipes](./docs/customization/recipes.md): See real v3 components and reuse the exact theme configuration behind the examples.
38
+ - [Style component slots](./docs/customization/slots.md): Target a specific c15t component part without replacing its markup or behavior.
39
+ - [Theme tokens and CSS](./docs/customization/tokens.md): Style c15t with semantic tokens and use the stylesheet that matches your CSS tooling.
40
+ - [Copy and translations](./docs/customization/translations.md): Change consent wording through i18n and test the complete prompt and preferences flow.
41
+
42
+ ## Integrations
43
+
44
+ - [Adobe Analytics](./docs/integrations/adobe-analytics.md): Configure Adobe Analytics with c15t v3, understand measurement permission and verify loading and revocation.
45
+ - [Ahrefs Analytics](./docs/integrations/ahrefs-analytics.md): Configure Ahrefs Analytics with c15t v3, understand measurement permission and verify loading and revocation.
46
+ - [Amplitude](./docs/integrations/amplitude.md): Configure Amplitude with c15t v3, understand measurement permission and verify loading and revocation.
47
+ - [Custom integrations](./docs/integrations/building-integrations.md): Define loading, initialization and consent-change behavior for a vendor without a helper.
48
+ - [Clearbit](./docs/integrations/clearbit.md): Configure Clearbit with c15t v3, understand marketing permission and verify loading and revocation.
49
+ - [Cloudflare Web Analytics](./docs/integrations/cloudflare-web-analytics.md): Configure Cloudflare Web Analytics with c15t v3, understand measurement permission and verify loading and revocation.
50
+ - [Crisp](./docs/integrations/crisp.md): Configure Crisp with c15t v3, understand functionality permission and verify loading and revocation.
51
+ - [Databuddy](./docs/integrations/databuddy.md): Configure Databuddy's initial and updated consent state with c15t v3.
52
+ - [Fathom Analytics](./docs/integrations/fathom-analytics.md): Configure Fathom Analytics with c15t v3, understand measurement permission and verify loading and revocation.
53
+ - [Google Maps](./docs/integrations/google-maps.md): Prevent a map iframe from mounting before the required permission.
54
+ - [Google Tag](./docs/integrations/google-tag.md): Configure gtag with c15t Consent Mode signals and understand its loading behavior.
55
+ - [Google Tag Manager](./docs/integrations/google-tag-manager.md): Load GTM with c15t consent signals and verify the tags inside your container.
56
+ - [Heap](./docs/integrations/heap.md): Configure Heap with c15t v3, understand measurement permission and verify loading and revocation.
57
+ - [Hightouch](./docs/integrations/hightouch.md): Configure Hightouch with c15t v3, understand measurement permission and verify loading and revocation.
58
+ - [Hotjar](./docs/integrations/hotjar.md): Configure Hotjar with c15t v3, understand measurement permission and verify loading and revocation.
59
+ - [Intercom](./docs/integrations/intercom.md): Load the Intercom messenger with functionality permission and configure its region.
60
+ - [LinkedIn Insight Tag](./docs/integrations/linkedin-insights.md): Configure LinkedIn Insight Tag with c15t v3, understand marketing permission and verify loading and revocation.
61
+ - [LogRocket](./docs/integrations/logrocket.md): Configure LogRocket with c15t v3, understand measurement permission and verify loading and revocation.
62
+ - [Matomo Analytics](./docs/integrations/matomo-analytics.md): Choose gated loading or Matomo consent signaling and configure the correct tracker endpoints.
63
+ - [Meta Pixel](./docs/integrations/meta-pixel.md): Register the Meta Pixel under marketing permission and verify event calls after revocation.
64
+ - [Microsoft Clarity](./docs/integrations/microsoft-clarity.md): Configure Microsoft Clarity with c15t v3, understand measurement permission and verify loading and revocation.
65
+ - [Microsoft UET](./docs/integrations/microsoft-uet.md): Configure Microsoft UET with c15t v3, understand marketing permission and verify loading and revocation.
66
+ - [Mixpanel](./docs/integrations/mixpanel-analytics.md): Configure Mixpanel with c15t v3, understand measurement permission and verify loading and revocation.
67
+ - [OpenAI Pixel](./docs/integrations/openai-pixel.md): Configure the OpenAI Measurement Pixel for ChatGPT Ads with c15t v3, manage marketing permission and verify conversion delivery.
68
+ - [Overview](./docs/integrations/overview.md): Find all c15t integrations for analytics, tag managers, advertising, chat and embedded content.
69
+ - [Pirsch](./docs/integrations/pirsch.md): Configure Pirsch with c15t v3, understand measurement permission and verify loading and revocation.
70
+ - [Plausible Analytics](./docs/integrations/plausible-analytics.md): Configure Plausible Analytics with c15t v3, understand measurement permission and verify loading and revocation.
71
+ - [PostHog](./docs/integrations/posthog.md): Choose PostHog loading and cookieless behavior, configure the region, and synchronize v3 permissions.
72
+ - [Promptwatch](./docs/integrations/promptwatch.md): Configure Promptwatch with c15t v3, understand measurement permission and verify loading and revocation.
73
+ - [Reddit Pixel](./docs/integrations/reddit-pixel.md): Configure Reddit Pixel with c15t v3, understand marketing permission and verify loading and revocation.
74
+ - [RudderStack](./docs/integrations/rudderstack.md): Gate the RudderStack browser SDK or map c15t categories to destination consent IDs.
75
+ - [Rybbit Analytics](./docs/integrations/rybbit-analytics.md): Configure Rybbit Analytics with c15t v3, understand measurement permission and verify loading and revocation.
76
+ - [Segment](./docs/integrations/segment.md): Configure Segment with c15t v3, understand measurement permission and verify loading and revocation.
77
+ - [Snapchat Pixel](./docs/integrations/snapchat-pixel.md): Configure Snapchat Pixel with c15t v3, understand marketing permission and verify loading and revocation.
78
+ - [TikTok Pixel](./docs/integrations/tiktok-pixel.md): Configure TikTok Pixel with c15t v3, understand marketing permission and verify loading and revocation.
79
+ - [Umami Analytics](./docs/integrations/umami-analytics.md): Configure Umami Analytics with c15t v3, understand measurement permission and verify loading and revocation.
80
+ - [Vercel Analytics](./docs/integrations/vercel-analytics.md): Configure Vercel Analytics with c15t v3, understand measurement permission and verify loading and revocation.
81
+ - [X Pixel](./docs/integrations/x-pixel.md): Configure X Pixel with c15t v3, understand marketing permission and verify loading and revocation.
82
+ - [YouTube](./docs/integrations/youtube.md): Gate YouTube embeds with c15t v3 in Next.js, TanStack Start, React, Nuxt, Vue, Astro, Svelte, SvelteKit or JavaScript.
83
+
84
+ ## Reference
85
+
86
+ - [Upgrade to v3 policies](./docs/upgrade-v3.md): Migrate policy configuration, consent records, callbacks, and custom transports to the v3 policy system.
package/SKILL.md ADDED
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: c15t-astro-docs
3
+ description: Read and search the @c15t/astro documentation. Use when working with @c15t/astro — its setup, configuration, API, and behavior.
4
+ metadata:
5
+ source: leadtype
6
+ ---
7
+ # @c15t/astro documentation
8
+
9
+ Astro v3 static and server integration, dialog adapters and runtime ownership.
10
+
11
+ To work with @c15t/astro, read its bundled docs — they ship with the package and are version-matched to the installed code:
12
+
13
+ - Start with `./AGENTS.md`; it links every per-topic Markdown file under `./docs/`.
14
+ - Prefer these local files over fetching anything over the network.
@@ -0,0 +1,91 @@
1
+ import { serveGvlReference } from "@c15t/core";
2
+ import { MANIFEST_PASSTHROUGH_HEADERS, fetchCachedGvl, fetchCachedManifest, getManifestAge } from "@c15t/core/server";
3
+ import { extractConsentRequestInputs } from "@c15t/schema/types";
4
+ import { loadConsentManifest, resolveManifestInit, resolveManifestSourceFrom } from "./manifest-init.js";
5
+ const INIT_CACHE_CONTROL = 'private, no-store';
6
+ const MANIFEST_ROUTE_SUFFIX = '/manifest';
7
+ const handlers_waitUntilFromLocals = function(revalidation, locals) {
8
+ const ctx = locals?.runtime?.ctx;
9
+ if (ctx && 'function' == typeof ctx.waitUntil) ctx.waitUntil.call(ctx, revalidation);
10
+ };
11
+ const handlers_bindBackgroundRevalidate = function(handlerOptions, lifetime) {
12
+ if (handlerOptions.onBackgroundRevalidate) return handlerOptions.onBackgroundRevalidate;
13
+ if (!lifetime) return;
14
+ return (revalidation)=>handlers_waitUntilFromLocals(revalidation, lifetime.locals);
15
+ };
16
+ const handlers_resolveManifestSourceURL = function(request, options) {
17
+ return resolveManifestSourceFrom({
18
+ headers: request.headers,
19
+ url: request.url
20
+ }, options);
21
+ };
22
+ const handlers_createConsentRouteHandlers = function(handlerOptions) {
23
+ const init = async function(request, lifetime) {
24
+ const manifest = await loadConsentManifest({
25
+ fetch: handlerOptions.fetch,
26
+ onBackgroundRevalidate: handlers_bindBackgroundRevalidate(handlerOptions, lifetime),
27
+ options: handlerOptions.options,
28
+ source: {
29
+ headers: request.headers,
30
+ url: request.url
31
+ }
32
+ });
33
+ const listResponse = await serveGvlReference(request, (language)=>!manifest.iab?.gvl || handlerOptions.fetch || handlerOptions.fetchGvl ? Promise.resolve(null) : fetchCachedGvl({
34
+ language,
35
+ url: manifest.iab.gvl.url
36
+ }));
37
+ if (listResponse) return listResponse;
38
+ const payload = await resolveManifestInit({
39
+ fetch: handlerOptions.fetch,
40
+ fetchGvl: handlerOptions.fetchGvl,
41
+ gvlRoute: new URL(request.url).pathname,
42
+ inputs: extractConsentRequestInputs(request.headers, {
43
+ language: handlerOptions.options.i18n?.locale
44
+ }),
45
+ manifest
46
+ });
47
+ return Response.json(payload, {
48
+ headers: {
49
+ 'cache-control': INIT_CACHE_CONTROL
50
+ }
51
+ });
52
+ };
53
+ const manifest = async function(request, lifetime) {
54
+ const manifestURL = handlers_resolveManifestSourceURL(request, handlerOptions.options);
55
+ const query = new URL(request.url).searchParams.toString();
56
+ const result = await fetchCachedManifest({
57
+ config: {
58
+ manifestURL
59
+ },
60
+ fetch: handlerOptions.fetch,
61
+ onBackgroundRevalidate: handlers_bindBackgroundRevalidate(handlerOptions, lifetime),
62
+ query
63
+ });
64
+ const headers = new Headers({
65
+ 'content-type': 'application/json'
66
+ });
67
+ for (const name of MANIFEST_PASSTHROUGH_HEADERS){
68
+ const value = result.headers[name];
69
+ if (value) headers.set(name, value);
70
+ }
71
+ headers.set('age', String(getManifestAge(result)));
72
+ const { etag } = result.headers;
73
+ if (etag && request.headers.get('if-none-match') === etag) return new Response(null, {
74
+ headers,
75
+ status: 304
76
+ });
77
+ return new Response(JSON.stringify(result.manifest), {
78
+ headers,
79
+ status: 200
80
+ });
81
+ };
82
+ const GET = function(request) {
83
+ return new URL(request.url).pathname.endsWith(MANIFEST_ROUTE_SUFFIX) ? manifest(request) : init(request);
84
+ };
85
+ return {
86
+ GET,
87
+ init,
88
+ manifest
89
+ };
90
+ };
91
+ export { handlers_createConsentRouteHandlers as createConsentRouteHandlers, handlers_resolveManifestSourceURL as resolveManifestSourceURL, handlers_waitUntilFromLocals as waitUntilFromLocals };
@@ -0,0 +1,3 @@
1
+ export { createConsentRouteHandlers, resolveManifestSourceURL, waitUntilFromLocals } from "./handlers.js";
2
+ export { loadConsentManifest, resolveManifestInit, resolveManifestSourceFrom } from "./manifest-init.js";
3
+ export { MANIFEST_DEDUPE_TTL_SECONDS, MANIFEST_PASSTHROUGH_HEADERS, clearManifestCache, createManifestRequestURL, fetchCachedManifest, getManifestSMaxAge, getManifestStaleWhileRevalidate } from "@c15t/core/server";
@@ -0,0 +1,10 @@
1
+ import options from "virtual:c15t/options";
2
+ import { createConsentRouteHandlers } from "./handlers.js";
3
+ const handlers = createConsentRouteHandlers({
4
+ options: options
5
+ });
6
+ const GET = ({ locals, request })=>handlers.init(request, {
7
+ locals
8
+ });
9
+ const prerender = false;
10
+ export { GET, prerender };
@@ -0,0 +1,103 @@
1
+ import { deferInitGvlToRoute } from "@c15t/core";
2
+ import { fetchCachedManifest } from "@c15t/core/server";
3
+ import { consentInputsToOverrides, resolveBackendURL, resolveInitFromManifest } from "@c15t/schema/types";
4
+ import { baseTranslations } from "@c15t/translations/all";
5
+ const MANIFEST_ROUTE_SUFFIX = '/manifest';
6
+ const manifest_init_getEnv = function(name) {
7
+ if ("u" < typeof process) return;
8
+ return process.env?.[name];
9
+ };
10
+ const manifest_init_trimSlash = function(value) {
11
+ return value.endsWith('/') ? value.slice(0, -1) : value;
12
+ };
13
+ const manifest_init_resolveAgainstRequest = function(url, source) {
14
+ if (source.url) {
15
+ const requestURL = new URL(source.url);
16
+ return resolveBackendURL(url, {
17
+ host: requestURL.host,
18
+ 'x-forwarded-proto': requestURL.protocol.replace(':', '')
19
+ });
20
+ }
21
+ const hostHeader = source.headers.get('host');
22
+ return hostHeader ? resolveBackendURL(url, {
23
+ host: hostHeader
24
+ }) : null;
25
+ };
26
+ const manifest_init_resolveManifestSourceFrom = function(source, options) {
27
+ const { mode } = options;
28
+ const manifestURL = ('manifest' === mode.type ? mode.manifestURL : void 0) ?? manifest_init_getEnv('C15T_MANIFEST_URL');
29
+ if (manifestURL) {
30
+ const resolved = manifest_init_resolveAgainstRequest(manifestURL, source);
31
+ if (!resolved) throw new Error('@c15t/astro: invalid manifest URL.');
32
+ return resolved;
33
+ }
34
+ const backendURL = ('manifest' === mode.type ? mode.backendURL : void 0) ?? ('hosted' === mode.type ? mode.url : void 0) ?? manifest_init_getEnv('C15T_BACKEND_URL') ?? manifest_init_getEnv('PUBLIC_C15T_BACKEND_URL');
35
+ if (!backendURL) throw new Error('@c15t/astro: manifest mode requires `backendURL` or `manifestURL` (or the C15T_BACKEND_URL environment variable).');
36
+ const resolved = manifest_init_resolveAgainstRequest(backendURL, source);
37
+ if (!resolved) throw new Error('@c15t/astro: invalid backend URL.');
38
+ return `${manifest_init_trimSlash(resolved)}${MANIFEST_ROUTE_SUFFIX}`;
39
+ };
40
+ const manifest_init_loadConsentManifest = async function(input) {
41
+ const { mode } = input.options;
42
+ if ('manifest' === mode.type && mode.manifest) return mode.manifest;
43
+ const manifestURL = manifest_init_resolveManifestSourceFrom(input.source, input.options);
44
+ const { manifest } = await fetchCachedManifest({
45
+ config: {
46
+ manifestURL
47
+ },
48
+ fetch: input.fetch,
49
+ onBackgroundRevalidate: input.onBackgroundRevalidate,
50
+ query: input.query
51
+ });
52
+ return manifest;
53
+ };
54
+ const manifest_init_shouldFetchGvl = function(manifest, payload) {
55
+ return manifest.iab?.enabled === true && void 0 !== manifest.iab.gvl && payload.policyResolution?.status === 'matched' && 'iab' === payload.policyResolution.policy.model;
56
+ };
57
+ const GVL_FETCH_TIMEOUT_MS = 5000;
58
+ const manifest_init_defaultFetchGvl = async function(input) {
59
+ const response = await input.fetch(input.reference.url, {
60
+ headers: {
61
+ 'accept-language': input.language
62
+ },
63
+ method: 'GET',
64
+ signal: AbortSignal.timeout(GVL_FETCH_TIMEOUT_MS)
65
+ });
66
+ if (204 === response.status) return null;
67
+ if (!response.ok) throw new Error(`@c15t/astro: GVL responded ${response.status} ${response.statusText}`);
68
+ return await response.json();
69
+ };
70
+ const manifest_init_resolveManifestInit = async function(input) {
71
+ const { inputs, manifest } = input;
72
+ const payload = resolveInitFromManifest(manifest, {
73
+ country: inputs.country,
74
+ gpc: inputs.gpc,
75
+ language: inputs.language ?? 'en',
76
+ region: inputs.region
77
+ }, {
78
+ baseTranslations: baseTranslations
79
+ });
80
+ const fetchImpl = input.fetch ?? globalThis.fetch?.bind(globalThis);
81
+ if (manifest_init_shouldFetchGvl(manifest, payload) && manifest.iab?.gvl && fetchImpl) {
82
+ const language = payload.translations.language.split('-')[0] || 'en';
83
+ try {
84
+ payload.gvl = await (input.fetchGvl ?? manifest_init_defaultFetchGvl)({
85
+ fetch: fetchImpl,
86
+ language,
87
+ reference: manifest.iab.gvl
88
+ });
89
+ } catch {
90
+ payload.gvl = null;
91
+ }
92
+ }
93
+ payload.resolvedOverrides = consentInputsToOverrides({
94
+ country: inputs.country,
95
+ language: inputs.language,
96
+ region: inputs.region
97
+ });
98
+ payload.resolvedPrivacySignals = {
99
+ gpc: inputs.gpc
100
+ };
101
+ return !input.gvlRoute || input.fetch || input.fetchGvl ? payload : deferInitGvlToRoute(payload, input.gvlRoute);
102
+ };
103
+ export { manifest_init_defaultFetchGvl as defaultFetchGvl, manifest_init_loadConsentManifest as loadConsentManifest, manifest_init_resolveManifestInit as resolveManifestInit, manifest_init_resolveManifestSourceFrom as resolveManifestSourceFrom };
@@ -0,0 +1,10 @@
1
+ import options from "virtual:c15t/options";
2
+ import { createConsentRouteHandlers } from "./handlers.js";
3
+ const handlers = createConsentRouteHandlers({
4
+ options: options
5
+ });
6
+ const GET = ({ locals, request })=>handlers.manifest(request, {
7
+ locals
8
+ });
9
+ const prerender = false;
10
+ export { GET, prerender };
@@ -0,0 +1,5 @@
1
+ import { createLazyIABFactory } from "@c15t/core/runtime";
2
+ const defaultFactory = createLazyIABFactory(()=>import("@c15t/iab"));
3
+ const lazyCreateIAB = defaultFactory.create;
4
+ const whenIABReady = defaultFactory.whenReady;
5
+ export { lazyCreateIAB, whenIABReady };
@@ -0,0 +1,47 @@
1
+ import { allConsentNames, has } from "@c15t/core";
2
+ const CATEGORY_ATTRIBUTE = 'data-c15t-category';
3
+ const ACTIVATED_ATTRIBUTE = 'data-c15t-activated';
4
+ const COPIED_ATTRIBUTES = [
5
+ 'src',
6
+ 'async',
7
+ 'defer',
8
+ 'crossorigin',
9
+ 'integrity',
10
+ 'referrerpolicy',
11
+ 'nonce',
12
+ 'id'
13
+ ];
14
+ const inline_scripts_activate = function(element) {
15
+ const replacement = document.createElement("script");
16
+ for (const name of COPIED_ATTRIBUTES){
17
+ const value = element.getAttribute(name);
18
+ if (null !== value) replacement.setAttribute(name, value);
19
+ }
20
+ for (const attribute of Array.from(element.attributes))if (attribute.name.startsWith('data-') && attribute.name !== ACTIVATED_ATTRIBUTE) replacement.setAttribute(attribute.name, attribute.value);
21
+ replacement.setAttribute(ACTIVATED_ATTRIBUTE, 'true');
22
+ if (!element.hasAttribute('src')) replacement.textContent = element.textContent;
23
+ element.setAttribute(ACTIVATED_ATTRIBUTE, 'true');
24
+ element.parentNode?.insertBefore(replacement, element.nextSibling);
25
+ element.remove();
26
+ };
27
+ const inline_scripts_activateGatedScripts = function(snapshot, root = document) {
28
+ const selector = `script[${CATEGORY_ATTRIBUTE}]:not([${ACTIVATED_ATTRIBUTE}])`;
29
+ const elements = Array.from(root.querySelectorAll(selector));
30
+ let activated = 0;
31
+ for (const element of elements){
32
+ const category = element.getAttribute(CATEGORY_ATTRIBUTE);
33
+ if (!category) continue;
34
+ if (!allConsentNames.includes(category)) {
35
+ console.warn(`@c15t/astro: unknown ${CATEGORY_ATTRIBUTE} "${category}". Expected one of ${allConsentNames.join(', ')}.`);
36
+ element.setAttribute(ACTIVATED_ATTRIBUTE, 'invalid');
37
+ continue;
38
+ }
39
+ const allowed = has(category, snapshot.effectivePermissions);
40
+ if (allowed) {
41
+ inline_scripts_activate(element);
42
+ activated += 1;
43
+ }
44
+ }
45
+ return activated;
46
+ };
47
+ export { ACTIVATED_ATTRIBUTE, CATEGORY_ATTRIBUTE, inline_scripts_activateGatedScripts as activateGatedScripts };
package/dist/client.js ADDED
@@ -0,0 +1,276 @@
1
+ import { createConsentRuntime } from "@c15t/core/runtime";
2
+ import { setupColorScheme, setupFocusTrap, setupScrollLock } from "@c15t/ui/utils/dom";
3
+ import { lazyCreateIAB, whenIABReady } from "./browser/iab.js";
4
+ import { activateGatedScripts } from "./browser/inline-scripts.js";
5
+ import { resolveTransportFactory } from "./mode.js";
6
+ import { loadDialogAdapter, registerDialogAdapter, registerDialogSurface } from "./ui/adapter.js";
7
+ const GLOBAL_KEY = '__c15tAstro';
8
+ const NO_PAGE_SWAP_LISTENERS = ()=>{};
9
+ let detachPageSwapListeners = NO_PAGE_SWAP_LISTENERS;
10
+ const COLOR_SCHEME_KEY = '__c15tAstroColorScheme';
11
+ const CONFIG_KEY = '__c15tAstroConfig';
12
+ const DIALOG_HOST_ID = 'c15t-dialog-host';
13
+ const ACTION_ATTRIBUTE = 'data-c15t-action';
14
+ const DIALOG_ATTRIBUTE = 'data-c15t-dialog';
15
+ const DIALOG_TAB_ATTRIBUTE = 'data-c15t-tab';
16
+ const client_getWindow = function() {
17
+ return "u" < typeof window ? void 0 : window;
18
+ };
19
+ const client_readInlinedConfig = function() {
20
+ return client_getWindow()?.[CONFIG_KEY] ?? {};
21
+ };
22
+ const client_applyColorScheme = function(colorScheme) {
23
+ const browserWindow = client_getWindow();
24
+ if (!browserWindow) return;
25
+ browserWindow[COLOR_SCHEME_KEY]?.();
26
+ browserWindow[COLOR_SCHEME_KEY] = void 0;
27
+ if ('function' != typeof browserWindow.matchMedia) return void document.documentElement.classList.toggle('c15t-dark', 'dark' === colorScheme);
28
+ browserWindow[COLOR_SCHEME_KEY] = setupColorScheme(colorScheme);
29
+ };
30
+ const client_ensureDialogHost = function() {
31
+ const existing = document.getElementById(DIALOG_HOST_ID);
32
+ if (existing) return existing;
33
+ const host = document.createElement('div');
34
+ host.id = DIALOG_HOST_ID;
35
+ document.body.appendChild(host);
36
+ return host;
37
+ };
38
+ let releaseBlocking = null;
39
+ const client_hasConsentPolicy = function(snapshot) {
40
+ return 'matched' === snapshot.resolution.status;
41
+ };
42
+ const client_hasConsentUi = function(snapshot) {
43
+ return client_hasConsentPolicy(snapshot) && ('none' !== snapshot.policyRule.prompt || snapshot.policyRule.rights.length > 0);
44
+ };
45
+ const client_whenPolicySettled = function(kernel) {
46
+ if (!kernel.getSnapshot().policyPending) return Promise.resolve();
47
+ return new Promise((resolve)=>{
48
+ const unsubscribe = kernel.subscribe((snapshot)=>{
49
+ if (!snapshot.policyPending) {
50
+ unsubscribe();
51
+ resolve();
52
+ }
53
+ });
54
+ });
55
+ };
56
+ const client_syncSurfaceVisibility = function(snapshot) {
57
+ const owesUi = client_hasConsentUi(snapshot);
58
+ for (const control of document.querySelectorAll('[data-c15t-surface="trigger"]'))control.hidden = !owesUi;
59
+ };
60
+ const client_syncBannerVisibility = function(snapshot) {
61
+ const banner = document.querySelector('[data-testid="consent-banner-root"], [data-testid="iab-consent-banner-root"]');
62
+ if (!banner) return void releaseBlocking?.();
63
+ const shouldShow = 'banner' === snapshot.activeUI;
64
+ banner.hidden = !shouldShow;
65
+ banner.setAttribute('data-c15t-visible', shouldShow ? 'true' : 'false');
66
+ const blocking = shouldShow && 'true' === banner.dataset.blocking;
67
+ for (const overlay of document.querySelectorAll('[data-testid="consent-banner-overlay"], [data-testid="iab-consent-banner-overlay"]'))overlay.hidden = !blocking;
68
+ if (!blocking) return void releaseBlocking?.();
69
+ if (releaseBlocking) return;
70
+ const card = banner.querySelector('[data-testid="consent-banner-card"], [data-testid="iab-consent-banner-card"]') ?? banner;
71
+ const unlockScroll = setupScrollLock();
72
+ const releaseFocus = setupFocusTrap(card);
73
+ releaseBlocking = ()=>{
74
+ releaseFocus();
75
+ unlockScroll();
76
+ releaseBlocking = null;
77
+ };
78
+ };
79
+ const client_resolveAction = function(target) {
80
+ if (!(target instanceof Element)) return null;
81
+ const element = target.closest(`[${ACTION_ATTRIBUTE}]`);
82
+ const action = element?.getAttribute(ACTION_ATTRIBUTE);
83
+ if ('accept' !== action && 'reject' !== action && 'customize' !== action && 'dismiss' !== action && 'close' !== action) return null;
84
+ const dialog = element?.getAttribute(DIALOG_ATTRIBUTE);
85
+ const tab = element?.getAttribute(DIALOG_TAB_ATTRIBUTE);
86
+ return {
87
+ action,
88
+ dialog: 'iab' === dialog ? 'iab' : 'preferences',
89
+ tab: 'vendors' === tab || 'purposes' === tab ? tab : void 0
90
+ };
91
+ };
92
+ const client_createClient = function(options, extension = {}) {
93
+ const config = client_readInlinedConfig();
94
+ const scripts = [
95
+ ...options.scripts ?? [],
96
+ ...extension.scripts ?? []
97
+ ];
98
+ const runtime = createConsentRuntime({
99
+ consentCategories: options.consentCategories,
100
+ createIAB: lazyCreateIAB,
101
+ i18n: options.i18n,
102
+ iab: false === options.iab ? false : options.iab,
103
+ mode: resolveTransportFactory(options.mode, {
104
+ backendURL: 'manifest' === options.mode.type ? options.mode.backendURL : void 0,
105
+ initPath: options.endpoints.initPath
106
+ }),
107
+ pkg: '@c15t/astro',
108
+ policyRules: 'offline' === options.mode.type ? options.mode.policyRules : void 0,
109
+ prefetch: config,
110
+ scripts,
111
+ storageConfig: options.storageConfig
112
+ });
113
+ let dialog = null;
114
+ let dialogKind = null;
115
+ let dialogTab;
116
+ let opening = null;
117
+ let disposed = false;
118
+ const client = {
119
+ async acceptAll () {
120
+ await runtime.kernel.commands.save('all');
121
+ },
122
+ closeDialog () {
123
+ runtime.kernel.set.activeUI('none');
124
+ dialog?.close();
125
+ },
126
+ dispose () {
127
+ if (disposed) return;
128
+ disposed = true;
129
+ detachPageSwapListeners();
130
+ releaseBlocking?.();
131
+ dialog?.destroy();
132
+ dialog = null;
133
+ dialogKind = null;
134
+ runtime.dispose();
135
+ const browserWindow = client_getWindow();
136
+ if (browserWindow) {
137
+ browserWindow[COLOR_SCHEME_KEY]?.();
138
+ browserWindow[COLOR_SCHEME_KEY] = void 0;
139
+ browserWindow[GLOBAL_KEY] = void 0;
140
+ }
141
+ },
142
+ getConsent () {
143
+ return runtime.kernel.getSnapshot();
144
+ },
145
+ async identify (user) {
146
+ await runtime.identify(user);
147
+ },
148
+ async openDialog (kind = 'preferences', tab) {
149
+ if (disposed) return;
150
+ await client_whenPolicySettled(runtime.kernel);
151
+ if (disposed || !client_hasConsentUi(runtime.kernel.getSnapshot())) return;
152
+ if (opening) await opening;
153
+ if (disposed) return;
154
+ if (dialog && (dialogKind !== kind || void 0 !== tab && tab !== dialogTab)) {
155
+ await dialog.destroy();
156
+ dialog = null;
157
+ }
158
+ if (!dialog) {
159
+ opening = (async ()=>{
160
+ const adapter = await loadDialogAdapter(options.ui);
161
+ if (disposed) return;
162
+ if ('iab' === kind) {
163
+ await whenIABReady();
164
+ if (disposed) return;
165
+ }
166
+ const handle = await adapter.mount({
167
+ kind,
168
+ options,
169
+ runtime,
170
+ tab,
171
+ target: client_ensureDialogHost()
172
+ });
173
+ if (disposed) return void await handle.destroy();
174
+ dialog = handle;
175
+ dialogKind = kind;
176
+ dialogTab = tab;
177
+ })();
178
+ try {
179
+ await opening;
180
+ } finally{
181
+ opening = null;
182
+ }
183
+ }
184
+ if (disposed) return;
185
+ runtime.kernel.set.activeUI('dialog');
186
+ },
187
+ options,
188
+ async rejectAll () {
189
+ await runtime.kernel.commands.save('none');
190
+ },
191
+ runtime,
192
+ async save (consents) {
193
+ await runtime.kernel.commands.save(consents);
194
+ },
195
+ subscribe (listener) {
196
+ return runtime.kernel.subscribe(listener);
197
+ }
198
+ };
199
+ return client;
200
+ };
201
+ const client_attach = function(client) {
202
+ const snapshot = client.getConsent();
203
+ client_syncBannerVisibility(snapshot);
204
+ client_syncSurfaceVisibility(snapshot);
205
+ activateGatedScripts(snapshot);
206
+ };
207
+ const client_getConsentClient = function() {
208
+ return client_getWindow()?.[GLOBAL_KEY] ?? null;
209
+ };
210
+ const client_attachBannerActions = function() {
211
+ const browserWindow = client_getWindow();
212
+ if (!browserWindow || browserWindow.__c15tAstroActions) return;
213
+ browserWindow.__c15tAstroActions = true;
214
+ document.addEventListener('click', (event)=>{
215
+ const resolved = client_resolveAction(event.target);
216
+ if (!resolved) return;
217
+ const client = client_getConsentClient();
218
+ if (!client) return;
219
+ event.preventDefault();
220
+ if ('dismiss' === resolved.action) return void client.runtime.kernel.commands.dismissNotice();
221
+ if ('accept' === resolved.action) return void client.acceptAll();
222
+ if ('reject' === resolved.action) return void client.rejectAll();
223
+ if ('customize' === resolved.action) return void client.openDialog(resolved.dialog, resolved.tab);
224
+ client.closeDialog();
225
+ });
226
+ };
227
+ const client_boot = function(options, extension = {}) {
228
+ const browserWindow = client_getWindow();
229
+ if (!browserWindow) throw new Error('@c15t/astro: boot() requires a browser environment.');
230
+ const existing = browserWindow[GLOBAL_KEY];
231
+ if (existing) return existing;
232
+ const client = client_createClient(options, extension);
233
+ browserWindow[GLOBAL_KEY] = client;
234
+ client.runtime.start();
235
+ client_applyColorScheme(options.colorScheme);
236
+ client_attachBannerActions();
237
+ client.subscribe((snapshot)=>{
238
+ client_syncBannerVisibility(snapshot);
239
+ client_syncSurfaceVisibility(snapshot);
240
+ activateGatedScripts(snapshot);
241
+ });
242
+ const onAfterSwap = function() {
243
+ client_applyColorScheme(options.colorScheme);
244
+ client_attach(client);
245
+ };
246
+ const onPageLoad = function() {
247
+ client_attach(client);
248
+ };
249
+ document.addEventListener('astro:after-swap', onAfterSwap);
250
+ document.addEventListener('astro:page-load', onPageLoad);
251
+ detachPageSwapListeners = ()=>{
252
+ document.removeEventListener('astro:after-swap', onAfterSwap);
253
+ document.removeEventListener('astro:page-load', onPageLoad);
254
+ detachPageSwapListeners = NO_PAGE_SWAP_LISTENERS;
255
+ };
256
+ client_attach(client);
257
+ return client;
258
+ };
259
+ const client_getConsent = function() {
260
+ return client_getConsentClient()?.getConsent() ?? null;
261
+ };
262
+ const client_subscribe = function(listener) {
263
+ const client = client_getConsentClient();
264
+ if (!client) return ()=>{};
265
+ return client.subscribe(listener);
266
+ };
267
+ const client_openDialog = async function(kind = 'preferences', tab) {
268
+ await client_getConsentClient()?.openDialog(kind, tab);
269
+ };
270
+ const client_preloadDialog = async function() {
271
+ const client = client_getConsentClient();
272
+ if (!client) return;
273
+ const adapter = await loadDialogAdapter(client.options.ui);
274
+ await adapter.preload?.();
275
+ };
276
+ export { ACTION_ATTRIBUTE, DIALOG_ATTRIBUTE, DIALOG_TAB_ATTRIBUTE, activateGatedScripts, client_attachBannerActions as attachBannerActions, client_boot as boot, client_getConsent as getConsent, client_getConsentClient as getConsentClient, client_openDialog as openDialog, client_preloadDialog as preloadDialog, client_subscribe as subscribe, client_syncBannerVisibility as syncBannerVisibility, client_syncSurfaceVisibility as syncSurfaceVisibility, registerDialogAdapter, registerDialogSurface };
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { c15t, c15t as default, resolveOptions } from "./integration.js";
2
+ export { createConsentMiddleware } from "./middleware-handler.js";
3
+ export { custom, hostedMode as hosted, manifestMode as manifest, offlineMode as offline, resolveTransportFactory } from "./mode.js";
4
+ export { registerDialogAdapter, registerDialogSurface } from "./ui/adapter.js";
5
+ export { policyRulePresets } from "@c15t/core";