@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
@@ -0,0 +1,170 @@
1
+ const VIRTUAL_ID = 'virtual:c15t/options';
2
+ const RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_ID}`;
3
+ const DEFAULT_INIT_PATH = '/api/c15t/init';
4
+ const DEFAULT_MANIFEST_PATH = '/api/c15t/manifest';
5
+ const UI_ADAPTERS = {
6
+ react: {
7
+ adapterExport: 'reactDialogAdapter',
8
+ adapterModule: '@c15t/astro/ui/react',
9
+ astroIntegration: '@astrojs/react',
10
+ packages: [
11
+ '@astrojs/react',
12
+ '@c15t/react',
13
+ 'react',
14
+ 'react-dom'
15
+ ],
16
+ surfaceModule: '@c15t/astro/islands/panel-surface.tsx'
17
+ },
18
+ svelte: {
19
+ adapterExport: 'svelteDialogAdapter',
20
+ adapterModule: '@c15t/astro/ui/svelte',
21
+ astroIntegration: '@astrojs/svelte',
22
+ packages: [
23
+ '@astrojs/svelte',
24
+ 'svelte'
25
+ ],
26
+ surfaceModule: '@c15t/astro/islands/panel-surface.svelte'
27
+ },
28
+ vue: {
29
+ adapterExport: 'vueDialogAdapter',
30
+ adapterModule: '@c15t/astro/ui/vue',
31
+ astroIntegration: '@astrojs/vue',
32
+ packages: [
33
+ '@astrojs/vue',
34
+ '@c15t/vue',
35
+ 'vue'
36
+ ],
37
+ surfaceModule: '@c15t/astro/islands/panel-surface.vue'
38
+ }
39
+ };
40
+ const UI_ADAPTER_NAMES = Object.keys(UI_ADAPTERS);
41
+ const SUGGESTIBLE_ADAPTERS = [
42
+ 'react',
43
+ 'vue'
44
+ ];
45
+ const integration_resolveMiddleware = function(options) {
46
+ const raw = 'boolean' == typeof options.middleware ? {
47
+ enabled: options.middleware
48
+ } : options.middleware ?? {};
49
+ return {
50
+ enabled: raw.enabled ?? true,
51
+ skip: raw.skip ?? []
52
+ };
53
+ };
54
+ const integration_resolveEndpoints = function(options) {
55
+ const raw = 'boolean' == typeof options.endpoints ? {
56
+ enabled: options.endpoints
57
+ } : options.endpoints ?? {};
58
+ return {
59
+ enabled: raw.enabled ?? 'manifest' === options.mode.type,
60
+ initPath: raw.initPath ?? DEFAULT_INIT_PATH,
61
+ manifestPath: raw.manifestPath ?? DEFAULT_MANIFEST_PATH
62
+ };
63
+ };
64
+ const integration_backendURLFromEnv = function() {
65
+ if ("u" < typeof process) return;
66
+ const env = process.env;
67
+ return env?.C15T_BACKEND_URL ?? env?.PUBLIC_C15T_BACKEND_URL;
68
+ };
69
+ const integration_resolveOptions = function(options) {
70
+ if (!options?.mode || 'object' != typeof options.mode) throw new Error('@c15t/astro: `mode` is required. Use hosted({ url }), offline() or manifest().');
71
+ if ('manifest' === options.mode.type && options.mode.manifestURL && !options.mode.backendURL && !integration_backendURLFromEnv()) throw new Error('@c15t/astro: manifest mode with a `manifestURL` also needs a `backendURL` (or C15T_BACKEND_URL) — that is where consent is saved, and the injected routes only serve init and manifest.');
72
+ if (void 0 !== options.ui && !(options.ui in UI_ADAPTERS)) throw new Error(`@c15t/astro: unknown \`ui\` ${JSON.stringify(options.ui)}. Supported adapters: ${UI_ADAPTER_NAMES.join(', ')}.`);
73
+ const { endpoints: _endpoints, middleware: _middleware, requireUIIntegration: _requireUIIntegration, ...rest } = options;
74
+ return {
75
+ ...rest,
76
+ colorScheme: options.colorScheme ?? 'system',
77
+ endpoints: integration_resolveEndpoints(options),
78
+ middleware: integration_resolveMiddleware(options),
79
+ ui: options.ui ?? 'svelte'
80
+ };
81
+ };
82
+ const integration_createVirtualOptionsPlugin = function(resolved) {
83
+ const serialized = JSON.stringify(resolved);
84
+ return {
85
+ load (id) {
86
+ if (id !== RESOLVED_VIRTUAL_ID) return;
87
+ return `export default ${serialized};`;
88
+ },
89
+ name: 'c15t:options',
90
+ resolveId (id) {
91
+ return id === VIRTUAL_ID ? RESOLVED_VIRTUAL_ID : void 0;
92
+ }
93
+ };
94
+ };
95
+ const integration_buildBootScript = function(options, ui) {
96
+ const adapter = UI_ADAPTERS[ui];
97
+ const lines = [
98
+ `import options from '${VIRTUAL_ID}';`,
99
+ "import { boot, registerDialogAdapter, registerDialogSurface } from '@c15t/astro/client';",
100
+ `registerDialogAdapter('${ui}', async () => (await import('${adapter.adapterModule}')).${adapter.adapterExport});`,
101
+ `registerDialogSurface('${ui}', () => import('${adapter.surfaceModule}'));`
102
+ ];
103
+ if (options.clientEntrypoint) lines.push(`import clientOptions from '${options.clientEntrypoint}';`, 'boot(options, clientOptions);');
104
+ else lines.push('boot(options);');
105
+ return lines.join('\n');
106
+ };
107
+ const integration_buildVitePlugins = async function(resolved) {
108
+ const plugins = [
109
+ integration_createVirtualOptionsPlugin(resolved)
110
+ ];
111
+ if ('vue' === resolved.ui) try {
112
+ const { default: shimVueImports } = await import("@c15t/vue/vite");
113
+ plugins.push(shimVueImports());
114
+ } catch (cause) {
115
+ throw new Error(`@c15t/astro: \`ui: 'vue'\` needs ${UI_ADAPTERS.vue.packages.join(', ')} installed. Install them, or pick another \`ui\`.`, {
116
+ cause
117
+ });
118
+ }
119
+ return plugins;
120
+ };
121
+ const integration_suggestUIAdapter = function(options, installed, logger) {
122
+ if (void 0 !== options.ui) return;
123
+ const match = SUGGESTIBLE_ADAPTERS.find((name)=>installed.has(UI_ADAPTERS[name].astroIntegration));
124
+ if (!match) return;
125
+ logger.warn(`this site already loads ${UI_ADAPTERS[match].astroIntegration}; \`ui: '${match}'\` would render the consent dialog with it instead of adding the Svelte runtime. Set \`ui: 'svelte'\` to silence this.`);
126
+ };
127
+ const integration_c15t = function(options) {
128
+ const resolved = integration_resolveOptions(options);
129
+ return {
130
+ hooks: {
131
+ 'astro:config:done' ({ config, logger }) {
132
+ const installed = new Set(config.integrations.map((integration)=>integration.name));
133
+ integration_suggestUIAdapter(options, installed, logger);
134
+ if (false === options.requireUIIntegration) return;
135
+ const adapter = UI_ADAPTERS[resolved.ui];
136
+ if (installed.has(adapter.astroIntegration)) return;
137
+ logger.error(`the preference-centre and IAB dialogs render as ${resolved.ui} islands, so ${adapter.astroIntegration} must be installed and listed in astro.config before c15t(). Install ${adapter.packages.join(', ')} and run \`npx astro add ${resolved.ui}\`, or pass \`requireUIIntegration: false\` if you only use the server-rendered banner.`);
138
+ throw new Error(`@c15t/astro: ui: ${JSON.stringify(resolved.ui)} needs ${adapter.astroIntegration}. Install it, or set \`requireUIIntegration: false\` for a banner-only site.`);
139
+ },
140
+ async 'astro:config:setup' ({ addMiddleware, injectRoute, injectScript, updateConfig }) {
141
+ updateConfig({
142
+ vite: {
143
+ plugins: await integration_buildVitePlugins(resolved)
144
+ }
145
+ });
146
+ if (resolved.middleware.enabled) addMiddleware({
147
+ entrypoint: '@c15t/astro/middleware',
148
+ order: 'pre'
149
+ });
150
+ injectScript('page', integration_buildBootScript(options, resolved.ui));
151
+ if (resolved.endpoints.enabled) {
152
+ injectRoute({
153
+ entrypoint: '@c15t/astro/api/init',
154
+ pattern: resolved.endpoints.initPath,
155
+ prerender: false
156
+ });
157
+ injectRoute({
158
+ entrypoint: '@c15t/astro/api/manifest',
159
+ pattern: resolved.endpoints.manifestPath,
160
+ prerender: false
161
+ });
162
+ }
163
+ }
164
+ },
165
+ name: '@c15t/astro'
166
+ };
167
+ };
168
+ const src_integration = integration_c15t;
169
+ export default src_integration;
170
+ export { integration_c15t as c15t, integration_resolveOptions as resolveOptions };
@@ -0,0 +1,9 @@
1
+ const cookies_filterCookieHeader = function(cookieHeader, names) {
2
+ const allowed = new Set(names);
3
+ const kept = cookieHeader.split(';').map((pair)=>pair.trim()).filter((pair)=>{
4
+ const name = pair.split('=')[0]?.trim();
5
+ return void 0 !== name && allowed.has(name);
6
+ });
7
+ return kept.length > 0 ? kept.join('; ') : void 0;
8
+ };
9
+ export { cookies_filterCookieHeader as filterCookieHeader };
@@ -0,0 +1,31 @@
1
+ import { waitUntilFromLocals } from "./api/handlers.js";
2
+ import { resolveConsentContext } from "./server.js";
3
+ const middleware_handler_matchesPrefix = function(pathname, prefix) {
4
+ if (!prefix) return false;
5
+ const normalized = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
6
+ return pathname === normalized || pathname.startsWith(`${normalized}/`);
7
+ };
8
+ const middleware_handler_resolveSkipPaths = function(options) {
9
+ return [
10
+ options.endpoints.initPath,
11
+ options.endpoints.manifestPath,
12
+ ...options.middleware?.skip ?? []
13
+ ].filter(Boolean);
14
+ };
15
+ const middleware_handler_createConsentMiddleware = function(options, middlewareOptions = {}) {
16
+ const skipPaths = middleware_handler_resolveSkipPaths(options);
17
+ return async (context, next)=>{
18
+ const { pathname } = new URL(context.request.url);
19
+ if (skipPaths.some((prefix)=>middleware_handler_matchesPrefix(pathname, prefix))) return await next();
20
+ context.locals.c15t = await resolveConsentContext({
21
+ fetch: middlewareOptions.fetch,
22
+ headers: context.request.headers,
23
+ onBackgroundRevalidate: (revalidation)=>waitUntilFromLocals(revalidation, context.locals),
24
+ options,
25
+ skipPrefetch: true === context.isPrerendered,
26
+ url: context.request.url
27
+ });
28
+ return await next();
29
+ };
30
+ };
31
+ export { middleware_handler_createConsentMiddleware as createConsentMiddleware };
@@ -0,0 +1,6 @@
1
+ import options from "virtual:c15t/options";
2
+ import { createConsentMiddleware } from "./middleware-handler.js";
3
+ const onRequest = createConsentMiddleware(options);
4
+ const middleware = onRequest;
5
+ export default middleware;
6
+ export { createConsentMiddleware, onRequest };
package/dist/mode.js ADDED
@@ -0,0 +1,50 @@
1
+ import { createHostedTransport, createOfflineTransport, custom, hosted } from "@c15t/core";
2
+ const mode_hostedMode = function(options) {
3
+ return {
4
+ ...options,
5
+ type: 'hosted'
6
+ };
7
+ };
8
+ const mode_offlineMode = function(options = {}) {
9
+ return {
10
+ ...options,
11
+ type: 'offline'
12
+ };
13
+ };
14
+ const mode_manifestMode = function(options = {}) {
15
+ return {
16
+ ...options,
17
+ type: 'manifest'
18
+ };
19
+ };
20
+ const createOfflineFactory = (descriptor)=>Object.assign((context)=>createOfflineTransport({
21
+ iabEnabled: context.iabEnabled,
22
+ policyRules: descriptor.policyRules ?? context.policyRules,
23
+ translations: context.translations
24
+ }), {
25
+ kind: 'offline'
26
+ });
27
+ const mode_resolveTransportFactory = function(descriptor, endpoints = {
28
+ initPath: '/api/c15t/init'
29
+ }) {
30
+ if ('hosted' === descriptor.type) return hosted({
31
+ domain: descriptor.domain,
32
+ headers: descriptor.headers,
33
+ url: descriptor.url
34
+ });
35
+ if ('offline' === descriptor.type) return createOfflineFactory(descriptor);
36
+ if ('manifest' === descriptor.type) {
37
+ const base = endpoints.initPath.replace(/\/init$/u, '');
38
+ const backendURL = endpoints.backendURL ?? descriptor.backendURL ?? base;
39
+ const transport = createHostedTransport({
40
+ assertDecisionInputs: true,
41
+ backendURL,
42
+ initURL: endpoints.initPath
43
+ });
44
+ return Object.assign(()=>transport, {
45
+ kind: 'hosted'
46
+ });
47
+ }
48
+ throw new Error(`@c15t/astro: unknown mode ${JSON.stringify(descriptor.type)}. Use hosted(), offline() or manifest().`);
49
+ };
50
+ export { custom, mode_hostedMode as hostedMode, mode_manifestMode as manifestMode, mode_offlineMode as offlineMode, mode_resolveTransportFactory as resolveTransportFactory };
package/dist/server.js ADDED
@@ -0,0 +1,291 @@
1
+ import { buildPrefetchScript, c15tProtocolHeaders, createConsentKernel, createOfflineTransport, defaultTranslationConfig, deferInitGvl, deferInitGvlToRoute, mergeInitOutputIntoKernelConfig, mergeInitResponseIntoKernelConfig } from "@c15t/core";
2
+ import { CONSENT_STORAGE_KEY, readStoredRecordsFromCookieHeader } from "@c15t/core/modules/persistence";
3
+ import { isIABConfigured } from "@c15t/core/runtime";
4
+ import { fetchCachedGvl } from "@c15t/core/server";
5
+ import { readProducerPolicyContract } from "@c15t/core/transports";
6
+ import { CONSENT_REQUEST_HEADER_NAMES, consentInputsToOverrides, extractConsentRequestInputs, resolveBackendURL } from "@c15t/schema/types";
7
+ import { baseTranslations } from "@c15t/translations/all";
8
+ import { loadConsentManifest, resolveManifestInit } from "./api/manifest-init.js";
9
+ import { filterCookieHeader } from "./libs/cookies.js";
10
+ const server_resolveTranslations = function(options, inputs) {
11
+ const detect = options.i18n?.detectLanguage !== false;
12
+ const language = options.i18n?.locale ?? (detect ? inputs.language : void 0) ?? defaultTranslationConfig.defaultLanguage ?? 'en';
13
+ const catalogue = baseTranslations;
14
+ const base = catalogue[language] ?? defaultTranslationConfig.translations.en;
15
+ const overrides = options.i18n?.messages?.[language];
16
+ return {
17
+ language,
18
+ translations: overrides ? {
19
+ ...base,
20
+ ...overrides
21
+ } : base
22
+ };
23
+ };
24
+ const server_readInitialConsentConfig = function(headers, options) {
25
+ const now = Date.now();
26
+ const initialRecords = readStoredRecordsFromCookieHeader(headers.get('cookie') ?? void 0, options.storageConfig, now);
27
+ const inputs = extractConsentRequestInputs(headers, {
28
+ language: options.i18n?.locale
29
+ });
30
+ const config = {
31
+ initialPolicyPending: true,
32
+ initialPrivacySignals: {
33
+ gpc: inputs.gpc
34
+ },
35
+ initialRecords,
36
+ now
37
+ };
38
+ const overrides = consentInputsToOverrides({
39
+ country: inputs.country,
40
+ language: inputs.language,
41
+ region: inputs.region
42
+ });
43
+ if (Object.keys(overrides).length > 0) config.initialOverrides = overrides;
44
+ return {
45
+ config,
46
+ inputs
47
+ };
48
+ };
49
+ const LOOPBACK_HOSTS = new Set([
50
+ 'localhost',
51
+ '127.0.0.1',
52
+ '[::1]',
53
+ '::1'
54
+ ]);
55
+ const server_consentCookieName = function(options) {
56
+ return options.storageConfig?.storageKey ?? CONSENT_STORAGE_KEY;
57
+ };
58
+ const server_resolveAgainstRequest = function(url, headers, requestURL) {
59
+ if (requestURL) try {
60
+ const parsed = new URL(requestURL);
61
+ return resolveBackendURL(url, {
62
+ host: parsed.host,
63
+ 'x-forwarded-proto': parsed.protocol.replace(':', '')
64
+ });
65
+ } catch {
66
+ return null;
67
+ }
68
+ const host = headers.get('host');
69
+ return host ? resolveBackendURL(url, {
70
+ host
71
+ }) : null;
72
+ };
73
+ const server_mayForwardCookie = function(absolute, requestURL) {
74
+ let target;
75
+ try {
76
+ target = new URL(absolute);
77
+ } catch {
78
+ return false;
79
+ }
80
+ if ('https:' === target.protocol) return true;
81
+ if (LOOPBACK_HOSTS.has(target.hostname)) return true;
82
+ if (!requestURL) return false;
83
+ try {
84
+ const from = new URL(requestURL);
85
+ return 'http:' === from.protocol && from.host === target.host;
86
+ } catch {
87
+ return false;
88
+ }
89
+ };
90
+ const INIT_HEADER_ALLOWLIST = new Set(CONSENT_REQUEST_HEADER_NAMES);
91
+ const server_configuredInitHeaders = function(headers) {
92
+ const allowed = {};
93
+ for (const [name, value] of Object.entries(headers ?? {})){
94
+ const normalized = name.toLowerCase();
95
+ if (INIT_HEADER_ALLOWLIST.has(normalized)) allowed[normalized] = value;
96
+ }
97
+ return allowed;
98
+ };
99
+ const server_forwardHeaders = function(headers, overrides, options) {
100
+ const forward = {
101
+ ...c15tProtocolHeaders,
102
+ accept: 'application/json'
103
+ };
104
+ const detectedGpc = headers.get('sec-gpc');
105
+ if (null !== detectedGpc) forward['sec-gpc'] = detectedGpc;
106
+ const cookie = headers.get('cookie');
107
+ const scoped = cookie && false !== options.allowCookie ? filterCookieHeader(cookie, [
108
+ options.cookieName
109
+ ]) : void 0;
110
+ if (scoped) forward.cookie = scoped;
111
+ if (overrides.country) forward['x-c15t-country'] = overrides.country;
112
+ if (overrides.region) forward['x-c15t-region'] = overrides.region;
113
+ if (overrides.language) forward['accept-language'] = overrides.language;
114
+ if (void 0 !== overrides.gpc) forward['x-c15t-gpc'] = overrides.gpc ? '1' : '0';
115
+ return forward;
116
+ };
117
+ const server_prefetchHosted = async function(input) {
118
+ const absolute = server_resolveAgainstRequest(input.backendURL, input.headers, input.url);
119
+ const fetchImpl = input.fetch ?? globalThis.fetch?.bind(globalThis);
120
+ if (!absolute || !fetchImpl) return input.base;
121
+ const allowCookie = server_mayForwardCookie(absolute, input.url);
122
+ const forwarded = {
123
+ ...server_forwardHeaders(input.headers, input.base.initialOverrides ?? {}, {
124
+ allowCookie,
125
+ cookieName: server_consentCookieName(input.options)
126
+ }),
127
+ ...server_configuredInitHeaders(input.configuredHeaders)
128
+ };
129
+ try {
130
+ const response = await fetchImpl(`${absolute}/init`, {
131
+ cache: 'no-store',
132
+ credentials: allowCookie ? 'include' : 'omit',
133
+ headers: forwarded,
134
+ method: 'GET'
135
+ });
136
+ if (!response.ok) return input.base;
137
+ const payload = await response.json();
138
+ return mergeInitOutputIntoKernelConfig(input.base, input.fetch || forwarded.cookie ? payload : deferInitGvl(payload, `${absolute}/init`, 'init', forwarded), {}, {
139
+ producerContract: readProducerPolicyContract(response.headers)
140
+ });
141
+ } catch {
142
+ return input.base;
143
+ }
144
+ };
145
+ const server_prefetchManifest = async function(input, mode) {
146
+ const source = {
147
+ headers: input.headers,
148
+ url: input.url
149
+ };
150
+ const target = mode.manifestURL ?? mode.backendURL;
151
+ const absoluteTarget = target ? server_resolveAgainstRequest(target, input.headers, input.url) : null;
152
+ try {
153
+ const manifest = await loadConsentManifest({
154
+ fetch: input.fetch,
155
+ onBackgroundRevalidate: input.onBackgroundRevalidate,
156
+ options: input.options,
157
+ source
158
+ });
159
+ const payload = await resolveManifestInit({
160
+ fetch: input.fetch,
161
+ fetchGvl: async ({ reference, language, fetch: fetchImpl })=>await fetchCachedGvl({
162
+ fetch: fetchImpl,
163
+ language,
164
+ url: reference.url
165
+ }),
166
+ inputs: input.inputs,
167
+ manifest
168
+ });
169
+ return mergeInitOutputIntoKernelConfig(input.base, !input.fetch && manifest.iab?.gvl ? deferInitGvlToRoute(payload, input.options.endpoints.initPath) : payload, server_forwardHeaders(input.headers, input.base.initialOverrides ?? {}, {
170
+ allowCookie: absoluteTarget ? server_mayForwardCookie(absoluteTarget, input.url) : false,
171
+ cookieName: server_consentCookieName(input.options)
172
+ }));
173
+ } catch {
174
+ return input.base;
175
+ }
176
+ };
177
+ const server_prefetchOffline = async function(input) {
178
+ const { options } = input;
179
+ const policyRules = 'offline' === options.mode.type ? options.mode.policyRules : void 0;
180
+ const transport = createOfflineTransport({
181
+ iabEnabled: isIABConfigured(options.iab),
182
+ policyRules,
183
+ translations: input.translations
184
+ });
185
+ try {
186
+ const response = await transport.init?.({
187
+ overrides: input.base.initialOverrides ?? {},
188
+ user: input.base.initialUser ?? null
189
+ });
190
+ if (!response) return input.base;
191
+ return mergeInitResponseIntoKernelConfig(input.base, response);
192
+ } catch {
193
+ return input.base;
194
+ }
195
+ };
196
+ const server_prefetchLocal = function(input) {
197
+ const { mode } = input.options;
198
+ return 'manifest' === mode.type ? server_prefetchManifest(input, mode) : server_prefetchOffline(input);
199
+ };
200
+ const server_withResolvedGvl = async function(input) {
201
+ const { iab } = input.options;
202
+ if (!(isIABConfigured(iab) && iab)) return input.config;
203
+ if (input.config.initialIab?.gvl || input.config.initialIab?.gvlReference) return input.config;
204
+ let gvl = iab.gvl ?? null;
205
+ if (!gvl && iab.gvlURL) try {
206
+ gvl = await fetchCachedGvl({
207
+ fetch: input.fetch,
208
+ language: input.language,
209
+ url: iab.gvlURL
210
+ });
211
+ } catch {
212
+ gvl = null;
213
+ }
214
+ if (!gvl) return input.config;
215
+ return {
216
+ ...input.config,
217
+ initialIab: {
218
+ ...input.config.initialIab ?? {},
219
+ cmpId: input.config.initialIab?.cmpId ?? iab.cmpId ?? null,
220
+ enabled: true,
221
+ gvl
222
+ }
223
+ };
224
+ };
225
+ const server_snapshotFromConfig = function(config) {
226
+ const kernel = createConsentKernel(config);
227
+ const snapshot = kernel.getServerSnapshot();
228
+ kernel.dispose();
229
+ return snapshot;
230
+ };
231
+ const server_resolveConsentContext = async function(input) {
232
+ const { headers, options } = input;
233
+ const { config: base, inputs } = server_readInitialConsentConfig(headers, options);
234
+ const translations = server_resolveTranslations(options, inputs);
235
+ let config = {
236
+ ...base,
237
+ initialTranslations: translations
238
+ };
239
+ if (!input.skipPrefetch) config = 'hosted' === options.mode.type ? await server_prefetchHosted({
240
+ backendURL: options.mode.url,
241
+ base: config,
242
+ configuredHeaders: options.mode.headers,
243
+ fetch: input.fetch,
244
+ headers,
245
+ options,
246
+ url: input.url
247
+ }) : await server_prefetchLocal({
248
+ base: config,
249
+ fetch: input.fetch,
250
+ headers,
251
+ inputs,
252
+ onBackgroundRevalidate: input.onBackgroundRevalidate,
253
+ options,
254
+ translations,
255
+ url: input.url
256
+ });
257
+ config = await server_withResolvedGvl({
258
+ config,
259
+ fetch: input.fetch,
260
+ language: translations.language.split('-')[0] || 'en',
261
+ options
262
+ });
263
+ config.initialPolicyPending = void 0 === config.initialPolicyResolution;
264
+ const snapshot = server_snapshotFromConfig(config);
265
+ return {
266
+ config,
267
+ decision: snapshot.resolution,
268
+ hasConsentUi: 'matched' === snapshot.resolution.status && ('none' !== snapshot.policyRule.prompt || snapshot.policyRule.rights.length > 0),
269
+ hasPolicy: 'matched' === snapshot.resolution.status,
270
+ inputs,
271
+ options,
272
+ shouldShowBanner: !snapshot.policyPending && 'banner' === snapshot.activeUI,
273
+ snapshot
274
+ };
275
+ };
276
+ const server_buildConfigScript = function(config) {
277
+ const json = JSON.stringify(config ?? {}).replace(/</gu, '\\u003c');
278
+ return `window.__c15tAstroConfig=${json};`;
279
+ };
280
+ const server_buildColorSchemeScript = function(colorScheme) {
281
+ if ('light' === colorScheme) return '';
282
+ if ('dark' === colorScheme) return "document.documentElement.classList.add('c15t-dark');";
283
+ return "try{document.documentElement.classList.toggle('c15t-dark',matchMedia('(prefers-color-scheme:dark)').matches)}catch(e){}";
284
+ };
285
+ const emitted = new WeakSet();
286
+ const server_markConfigEmitted = function(locals) {
287
+ if (emitted.has(locals)) return false;
288
+ emitted.add(locals);
289
+ return true;
290
+ };
291
+ export { buildPrefetchScript, server_buildColorSchemeScript as buildColorSchemeScript, server_buildConfigScript as buildConfigScript, server_markConfigEmitted as markConfigEmitted, server_readInitialConsentConfig as readInitialConsentConfig, server_resolveConsentContext as resolveConsentContext, server_resolveTranslations as resolveTranslations, server_snapshotFromConfig as snapshotFromConfig };
@@ -0,0 +1,2 @@
1
+ @import "@c15t/ui/styles.css";
2
+
package/dist/types.js ADDED
File without changes
@@ -0,0 +1,37 @@
1
+ const surfaces = new Map();
2
+ const adapter_registerDialogSurface = function(name, load) {
3
+ surfaces.set(name, load);
4
+ };
5
+ const adapter_requireDialogSurface = function(name) {
6
+ const load = surfaces.get(name);
7
+ if (!load) throw new Error(`@c15t/astro: no ${name} dialog surface registered. The integration registers it from the injected page script, so this usually means c15t() is missing from astro.config, or the page was rendered with \`middleware: false\` and no boot script.`);
8
+ return load;
9
+ };
10
+ const registry = new Map();
11
+ const pending = new Map();
12
+ const adapter_registerDialogAdapter = function(name, load) {
13
+ registry.set(name, load);
14
+ pending.delete(name);
15
+ };
16
+ const adapter_resetDialogRegistriesForTest = function() {
17
+ registry.clear();
18
+ surfaces.clear();
19
+ pending.clear();
20
+ };
21
+ const adapter_loadDialogAdapter = async function(name) {
22
+ const memo = pending.get(name);
23
+ if (memo) return await memo;
24
+ const load = registry.get(name);
25
+ if (!load) throw new Error(`@c15t/astro: no dialog adapter registered for ui: ${JSON.stringify(name)}. The integration registers the configured adapter from the injected page script; register your own with registerDialogAdapter().`);
26
+ const loading = (async ()=>{
27
+ try {
28
+ return await load();
29
+ } catch (error) {
30
+ pending.delete(name);
31
+ throw error;
32
+ }
33
+ })();
34
+ pending.set(name, loading);
35
+ return await loading;
36
+ };
37
+ export { adapter_loadDialogAdapter as loadDialogAdapter, adapter_registerDialogAdapter as registerDialogAdapter, adapter_registerDialogSurface as registerDialogSurface, adapter_requireDialogSurface as requireDialogSurface, adapter_resetDialogRegistriesForTest as resetDialogRegistriesForTest };
@@ -0,0 +1,12 @@
1
+ const provider_props_buildProviderProps = function(runtime, options) {
2
+ return {
3
+ options: {
4
+ consentCategories: options.consentCategories,
5
+ legalLinks: options.legalLinks,
6
+ presentation: options.presentation,
7
+ theme: options.theme
8
+ },
9
+ runtime
10
+ };
11
+ };
12
+ export { provider_props_buildProviderProps as buildProviderProps };
@@ -0,0 +1,34 @@
1
+ import { requireDialogSurface } from "./adapter.js";
2
+ import { buildProviderProps } from "./provider-props.js";
3
+ const reactDialogAdapter = {
4
+ async mount (context) {
5
+ const [{ createElement }, { createRoot }, surface] = await Promise.all([
6
+ import("react"),
7
+ import("react-dom/client"),
8
+ requireDialogSurface('react')()
9
+ ]);
10
+ const root = createRoot(context.target);
11
+ root.render(createElement(surface.default, {
12
+ ...buildProviderProps(context.runtime, context.options),
13
+ kind: context.kind,
14
+ tab: context.tab
15
+ }));
16
+ return {
17
+ close () {
18
+ context.runtime.kernel.set.activeUI('none');
19
+ },
20
+ destroy () {
21
+ root.unmount();
22
+ }
23
+ };
24
+ },
25
+ name: 'react',
26
+ async preload () {
27
+ await Promise.all([
28
+ import("react"),
29
+ import("react-dom/client"),
30
+ requireDialogSurface('react')()
31
+ ]);
32
+ }
33
+ };
34
+ export { reactDialogAdapter };