@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,75 @@
1
+ /**
2
+ * Transport selection for `@c15t/astro`.
3
+ *
4
+ * Astro evaluates `astro.config.mjs` at build time, but the browser boot
5
+ * script is a string the integration injects. A transport factory cannot
6
+ * cross that boundary, so the integration takes a plain descriptor and both
7
+ * sides turn it into a {@link ProviderTransportFactory} themselves.
8
+ */
9
+ import { custom } from '@c15t/core';
10
+ import type { ProviderTransportFactory } from '@c15t/core';
11
+ import type { C15tHostedDescriptor, C15tManifestDescriptor, C15tModeDescriptor, C15tOfflineDescriptor } from './types';
12
+ /**
13
+ * Talk to a c15t backend over HTTP.
14
+ *
15
+ * @param options - Backend URL and request options.
16
+ * @returns A serializable hosted-mode descriptor.
17
+ * @example
18
+ * ```ts
19
+ * import { c15t, hosted } from '@c15t/astro';
20
+ *
21
+ * export default defineConfig({
22
+ * integrations: [c15t({ mode: hosted({ url: 'https://consent.example.com' }) })],
23
+ * });
24
+ * ```
25
+ */
26
+ export declare const hostedMode: (options: Omit<C15tHostedDescriptor, "type">) => C15tHostedDescriptor;
27
+ /**
28
+ * Resolve policies locally with no backend.
29
+ *
30
+ * @param options - Explicit policy rules to resolve against.
31
+ * @returns A serializable offline-mode descriptor.
32
+ * @example
33
+ * ```ts
34
+ * c15t({ mode: offline() })
35
+ * ```
36
+ */
37
+ export declare const offlineMode: (options?: Omit<C15tOfflineDescriptor, "type">) => C15tOfflineDescriptor;
38
+ /**
39
+ * Resolve `/init` from a cached consent manifest.
40
+ *
41
+ * The server resolves it in-process; the browser goes through the injected
42
+ * `/api/c15t/init` route, so no manifest or translation bundle reaches the
43
+ * client.
44
+ *
45
+ * @param options - Manifest URL, inline manifest, or backend URL.
46
+ * @returns A serializable manifest-mode descriptor.
47
+ * @example
48
+ * ```ts
49
+ * c15t({ mode: manifest({ backendURL: process.env.C15T_BACKEND_URL }) })
50
+ * ```
51
+ */
52
+ export declare const manifestMode: (options?: Omit<C15tManifestDescriptor, "type">) => C15tManifestDescriptor;
53
+ /** Where the browser reaches manifest-resolved init data. */
54
+ export interface ManifestClientEndpoints {
55
+ /** Route that returns a resolved `InitOutput`. */
56
+ initPath: string;
57
+ /** Backend base URL used for `POST /subjects`. */
58
+ backendURL?: string;
59
+ }
60
+ /**
61
+ * Turn a mode descriptor into the transport factory a kernel needs.
62
+ *
63
+ * `manifest` resolves through `endpoints.initPath` so the browser never
64
+ * downloads a manifest or the full translation catalogue. Server code that
65
+ * wants in-process manifest resolution uses
66
+ * `createServerManifestFactory` from `@c15t/astro/server` instead.
67
+ *
68
+ * @param descriptor - The serialized mode descriptor.
69
+ * @param endpoints - Route paths used by `manifest` mode.
70
+ * @returns A transport factory for `createConsentKernel`.
71
+ * @throws {Error} When the descriptor carries an unknown `type`.
72
+ */
73
+ export declare const resolveTransportFactory: (descriptor: C15tModeDescriptor, endpoints?: ManifestClientEndpoints) => ProviderTransportFactory;
74
+ /** Escape hatch for a caller-supplied transport. */
75
+ export { custom };
@@ -0,0 +1,130 @@
1
+ import type { ConsentSnapshot, KernelConfig, KernelTranslations } from '@c15t/core';
2
+ import type { ConsentRequestHeaderInputs } from '@c15t/schema/types';
3
+ import type { C15tColorScheme, C15tLocals, C15tResolvedOptions } from './types';
4
+ /** Input for {@link resolveConsentContext}. */
5
+ export interface ResolveConsentContextOptions {
6
+ /** The incoming request headers. */
7
+ headers: Headers;
8
+ /**
9
+ * The absolute request URL. Used to resolve a relative `backendURL` or
10
+ * `manifestURL` against this request's own origin and protocol; without
11
+ * it the shared resolver assumes `https`.
12
+ */
13
+ url?: string;
14
+ /** The integration options, already normalized. */
15
+ options: C15tResolvedOptions;
16
+ /** Override fetch, mainly for tests. */
17
+ fetch?: typeof globalThis.fetch;
18
+ /**
19
+ * Skip the server-side init roundtrip and return cookie + geo only.
20
+ * Useful for static output where every request shares one render.
21
+ */
22
+ skipPrefetch?: boolean;
23
+ /**
24
+ * Receives the promise of a background manifest revalidation started by
25
+ * this render, so the host can keep it alive past the response on
26
+ * runtimes that stop detached work once a response is sent. The
27
+ * middleware passes the adapter's `waitUntil` from `locals.runtime.ctx`
28
+ * when there is one. The promise never rejects.
29
+ */
30
+ onBackgroundRevalidate?: (revalidation: Promise<void>) => void;
31
+ }
32
+ /**
33
+ * Resolve the language the surfaces should render in.
34
+ *
35
+ * @param options - Integration options.
36
+ * @param inputs - Request inputs from the geo/language headers.
37
+ * @returns The kernel translations for this request.
38
+ */
39
+ export declare const resolveTranslations: (options: C15tResolvedOptions, inputs: ConsentRequestHeaderInputs) => KernelTranslations;
40
+ /**
41
+ * Read cookies and geo headers into a baseline `KernelConfig`.
42
+ *
43
+ * Does no network work and sets no cookies, so it is safe on every runtime
44
+ * including static prerenders.
45
+ *
46
+ * @param headers - The incoming request headers.
47
+ * @param options - The integration options.
48
+ * @returns A config seeded with stored consent and request overrides.
49
+ */
50
+ export declare const readInitialConsentConfig: (headers: Headers, options: C15tResolvedOptions) => {
51
+ config: KernelConfig;
52
+ inputs: ConsentRequestHeaderInputs;
53
+ };
54
+ /**
55
+ * Derive the kernel snapshot the server would hand a freshly booted page.
56
+ *
57
+ * Kernel construction is pure — no DOM, no network, no storage — so this is
58
+ * safe to run per request.
59
+ *
60
+ * @param config - The resolved kernel configuration.
61
+ * @returns The snapshot, including derived `activeUI` and policy UI hints.
62
+ */
63
+ export declare const snapshotFromConfig: (config: KernelConfig) => ConsentSnapshot;
64
+ /**
65
+ * Resolve everything the page needs about consent for one request.
66
+ *
67
+ * Reads the consent cookie and the geo/GPC headers, prefetches the policy
68
+ * decision through the configured mode, and derives whether the banner
69
+ * should render server-side.
70
+ *
71
+ * @param input - Request headers and integration options.
72
+ * @returns The value the middleware stores on `Astro.locals.c15t`.
73
+ * @example
74
+ * ```ts
75
+ * const c15t = await resolveConsentContext({ headers: request.headers, options });
76
+ * ```
77
+ */
78
+ export declare const resolveConsentContext: (input: ResolveConsentContextOptions) => Promise<C15tLocals>;
79
+ /**
80
+ * Build the inline `<script>` body that hands the browser its boot payload.
81
+ *
82
+ * The payload is the already-resolved `KernelConfig`, not a fetch: the
83
+ * browser starts with the same decision the server rendered, so there is no
84
+ * network init per page and no banner flicker.
85
+ *
86
+ * @param config - The resolved kernel configuration.
87
+ * @returns JavaScript safe for inline `<script>` injection.
88
+ */
89
+ export declare const buildConfigScript: (config: KernelConfig) => string;
90
+ /**
91
+ * Build the first-paint colour-scheme script.
92
+ *
93
+ * Dark mode is the `c15t-dark` class on `<html>`, and the client boot sets
94
+ * it — but that runs after the stylesheet has already painted the
95
+ * server-rendered banner in the light palette. This runs in `<head>`,
96
+ * before the browser has anything to paint, so a system-dark visitor never
97
+ * sees the flash. It is deliberately framework-free and unbundled: a
98
+ * module script would be deferred and lose the race.
99
+ *
100
+ * `'light'` emits nothing. Light is the absence of the class, so there is
101
+ * nothing to do before paint.
102
+ *
103
+ * @param colorScheme - The resolved colour scheme.
104
+ * @returns Script source, or an empty string when none is needed.
105
+ * @example
106
+ * ```astro
107
+ * <script is:inline set:html={buildColorSchemeScript('system')} />
108
+ * ```
109
+ */
110
+ export declare const buildColorSchemeScript: (colorScheme: C15tColorScheme) => string;
111
+ export { buildPrefetchScript } from '@c15t/core';
112
+ export type { KernelConfig } from '@c15t/core';
113
+ /**
114
+ * The per-request identity the emission guard keys off — in practice
115
+ * `Astro.locals`, which Astro creates fresh for every request.
116
+ */
117
+ export interface ConfigEmissionScope {
118
+ c15t?: C15tLocals;
119
+ }
120
+ /**
121
+ * Claim the one-per-request emission of the inline config script.
122
+ *
123
+ * `<ConsentScript />` and `<ConsentBanner />` both want to inline the boot
124
+ * payload, and a page may well contain both. The first caller for a given
125
+ * request wins; every later caller renders nothing.
126
+ *
127
+ * @param locals - The current `Astro.locals`, used as the request identity.
128
+ * @returns `true` for the first caller of this request.
129
+ */
130
+ export declare const markConfigEmitted: (locals: ConfigEmissionScope) => boolean;
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Public option and locals types for `@c15t/astro`.
3
+ *
4
+ * Everything the integration accepts must survive `JSON.stringify`: the
5
+ * options are serialized once at build time into a virtual module that the
6
+ * middleware, the `.astro` components and the injected client boot script
7
+ * all import. Callbacks and other live values belong in the module named
8
+ * by {@link C15tAstroOptions.clientEntrypoint}.
9
+ */
10
+ import type { AllConsentNames, ConsentSnapshot, ConsentPresentation, KernelConfig, LegalLinks, Script, StorageConfig } from '@c15t/core';
11
+ import type { PolicyRule, PolicyResolution, ConsentManifest, GlobalVendorList } from '@c15t/schema/types';
12
+ import type { Theme } from '@c15t/ui/theme';
13
+ /** Transport selection, in a form that survives serialization. */
14
+ export type C15tModeDescriptor = C15tHostedDescriptor | C15tOfflineDescriptor | C15tManifestDescriptor;
15
+ /** Talk to a c15t backend over HTTP. */
16
+ export interface C15tHostedDescriptor {
17
+ type: 'hosted';
18
+ /** Backend base URL. Absolute, or same-origin like `/api/c15t`. */
19
+ url: string;
20
+ /** Domain recorded when consent is saved. */
21
+ domain?: string;
22
+ /** Extra headers forwarded to the backend. */
23
+ headers?: Record<string, string>;
24
+ }
25
+ /** Resolve policies locally with no backend at all. */
26
+ export interface C15tOfflineDescriptor {
27
+ type: 'offline';
28
+ /** Policy packs resolved locally. */
29
+ policyRules?: PolicyRule[];
30
+ }
31
+ /**
32
+ * Resolve `/init` from a cached consent manifest.
33
+ *
34
+ * The server resolves the manifest per request; the browser talks to the
35
+ * injected `/api/c15t/init` route, which is manifest-backed and cached.
36
+ */
37
+ export interface C15tManifestDescriptor {
38
+ type: 'manifest';
39
+ /** `GET /manifest` URL. Defaults to `${backendURL}/manifest`. */
40
+ manifestURL?: string;
41
+ /** Backend base URL used for `POST /subjects`. */
42
+ backendURL?: string;
43
+ /** Inline manifest. Takes precedence over `manifestURL`. */
44
+ manifest?: ConsentManifest;
45
+ }
46
+ /**
47
+ * Which framework renders the on-demand dialog islands.
48
+ *
49
+ * Svelte is the default because it is the smallest: its runtime costs
50
+ * roughly 14 KB gzipped against React's ~45 KB. A site already shipping
51
+ * React or Vue should say so and reuse what it has instead of downloading
52
+ * a second framework for one dialog. The choice is never inferred — a
53
+ * silent change to what a page downloads is worse than an explicit one.
54
+ */
55
+ export type C15tUIAdapterName = 'svelte' | 'react' | 'vue';
56
+ /**
57
+ * How the consent surfaces pick light or dark.
58
+ *
59
+ * Dark mode is the `c15t-dark` class on `<html>`, not a
60
+ * `prefers-color-scheme` block, so something has to set it. `'system'`
61
+ * follows `prefers-color-scheme` and keeps following it; `'light'` and
62
+ * `'dark'` pin it.
63
+ */
64
+ export type C15tColorScheme = 'light' | 'dark' | 'system';
65
+ /** Route paths the integration can inject. */
66
+ export interface C15tEndpointOptions {
67
+ /**
68
+ * Inject `GET /api/c15t/init` and `GET /api/c15t/manifest`.
69
+ *
70
+ * Required for `mode: manifest()` unless you write the routes yourself.
71
+ *
72
+ * @default true when `mode.type === 'manifest'`, otherwise false
73
+ */
74
+ enabled?: boolean;
75
+ /** @default '/api/c15t/init' */
76
+ initPath?: string;
77
+ /** @default '/api/c15t/manifest' */
78
+ manifestPath?: string;
79
+ }
80
+ /** How the integration registers its `pre`-order middleware. */
81
+ export interface C15tMiddlewareOptions {
82
+ /**
83
+ * Register `@c15t/astro/middleware` at all.
84
+ *
85
+ * @default true
86
+ */
87
+ enabled?: boolean;
88
+ /**
89
+ * Extra path prefixes the middleware leaves alone.
90
+ *
91
+ * The integration's own init and manifest routes are always skipped, so
92
+ * this is only for routes of your own that must not resolve consent —
93
+ * health checks, webhooks, anything that would otherwise pay for a
94
+ * decision it never renders. A path matches when it is the pathname
95
+ * exactly or a parent segment of it, so `'/api'` covers `/api/health`.
96
+ *
97
+ * `Astro.locals.c15t` is left unset on a skipped route.
98
+ *
99
+ * @example ['/api/webhooks', '/healthz']
100
+ */
101
+ skip?: string[];
102
+ }
103
+ /** Options accepted by the `c15t()` Astro integration. */
104
+ export interface C15tAstroOptions {
105
+ /** Host layout and styling constrained by the active policy. */
106
+ presentation?: ConsentPresentation;
107
+ /**
108
+ * Transport selection. Build it with `hosted()`, `offline()` or
109
+ * `manifest()` so the descriptor stays well-formed.
110
+ */
111
+ mode: C15tModeDescriptor;
112
+ /** Categories offered in the banner and preference centre. */
113
+ consentCategories?: AllConsentNames[];
114
+ /** Consent-gated scripts handed to the core script loader. */
115
+ scripts?: Script[];
116
+ /**
117
+ * IAB TCF configuration. `false` disables it.
118
+ *
119
+ * Only the serializable fields are accepted here; a live GVL fetcher
120
+ * belongs in {@link C15tAstroOptions.clientEntrypoint}.
121
+ */
122
+ iab?: C15tIABOptions | false;
123
+ /** Cookie/localStorage configuration for persisted consent. */
124
+ storageConfig?: StorageConfig;
125
+ /** Locale and message overrides. */
126
+ i18n?: C15tI18nOptions;
127
+ /** Theme tokens applied to the banner and dialog surfaces. */
128
+ theme?: Theme;
129
+ /**
130
+ * Light or dark for the banner and dialogs.
131
+ *
132
+ * `'system'` follows `prefers-color-scheme` and keeps following it as
133
+ * the visitor changes it. `<ConsentScript />` writes the class from a
134
+ * tiny inline script in `<head>`, so the server-rendered banner is
135
+ * already dark on its first paint rather than flashing light.
136
+ *
137
+ * @default 'system'
138
+ */
139
+ colorScheme?: C15tColorScheme;
140
+ /** Legal links rendered inline in the banner and dialog. */
141
+ legalLinks?: LegalLinks;
142
+ /**
143
+ * Framework used to render the on-demand dialog islands.
144
+ *
145
+ * `'svelte'` ships the least JavaScript and is the default. Pick
146
+ * `'react'` or `'vue'` when the site already loads that runtime, so the
147
+ * dialog reuses it instead of adding a second framework. Whichever you
148
+ * pick, install the matching Astro integration — `@astrojs/svelte`,
149
+ * `@astrojs/react` or `@astrojs/vue` — and list it before `c15t()`.
150
+ *
151
+ * @default 'svelte'
152
+ */
153
+ ui?: C15tUIAdapterName;
154
+ /** Injected API routes. */
155
+ endpoints?: C15tEndpointOptions | boolean;
156
+ /**
157
+ * Module specifier whose default export is a
158
+ * {@link C15tClientOptionsExtension}. Use it for anything that cannot be
159
+ * serialized — callbacks, a custom GVL fetcher, scripts with lifecycle
160
+ * hooks.
161
+ *
162
+ * @example './src/c15t.client.ts'
163
+ */
164
+ clientEntrypoint?: string;
165
+ /**
166
+ * Register the `pre`-order middleware that populates `Astro.locals.c15t`.
167
+ *
168
+ * `false` is the same as `{ enabled: false }`. The middleware already
169
+ * skips the integration's own init and manifest routes, so a site that
170
+ * serves its own manifest does not have to hand-roll one to break the
171
+ * cycle; use `skip` to add routes of your own.
172
+ *
173
+ * @default true
174
+ */
175
+ middleware?: boolean | C15tMiddlewareOptions;
176
+ /**
177
+ * Fail the build when the Astro integration for {@link C15tAstroOptions.ui}
178
+ * is missing. Set to `false` for banner-only sites, which render no
179
+ * island at all.
180
+ *
181
+ * @default true
182
+ */
183
+ requireUIIntegration?: boolean;
184
+ }
185
+ /**
186
+ * IAB TCF options accepted by the integration.
187
+ *
188
+ * The serializable subset of the runtime's `RuntimeIABOptions`: a custom
189
+ * fetcher cannot survive the trip into the injected boot script, so that
190
+ * belongs in {@link C15tAstroOptions.clientEntrypoint} instead. A vendor
191
+ * list itself is plain JSON and does travel — see {@link C15tIABOptions.gvl}.
192
+ */
193
+ export interface C15tIABOptions {
194
+ /** Set `false` to keep IAB configured but inert. */
195
+ enabled?: boolean;
196
+ /**
197
+ * A vendor list to use as-is, instead of fetching one.
198
+ *
199
+ * The server needs a GVL to render `<IABConsentBanner />` at all — the
200
+ * banner names the purposes and counts the vendors — so hosted and
201
+ * manifest mode get theirs from `/init`. Offline mode has no backend to
202
+ * ask, which is what this is for: a pinned list, or a fixture in a
203
+ * demo. It is inlined into the page's boot payload, so keep it trimmed
204
+ * to the vendors the site actually works with.
205
+ */
206
+ gvl?: GlobalVendorList;
207
+ /** IAB-registered CMP ID. A hosted backend can supply it through `/init`. */
208
+ cmpId?: number;
209
+ /** CMP version reported through `__tcfapi`. */
210
+ cmpVersion?: number;
211
+ /** Restricts the vendor list to these vendor IDs. */
212
+ vendors?: number[];
213
+ /** Publisher country code used in the TC string. */
214
+ publisherCountryCode?: string;
215
+ /** Whether the CMP is service-specific rather than global. */
216
+ isServiceSpecific?: boolean;
217
+ /**
218
+ * Fetch the vendor list from this URL on the server.
219
+ *
220
+ * Goes through the shared in-process cache in `@c15t/core/server`, so
221
+ * concurrent renders collapse onto one download. Ignored when
222
+ * {@link C15tIABOptions.gvl} is set.
223
+ */
224
+ gvlURL?: string;
225
+ }
226
+ /** Locale configuration accepted by the integration. */
227
+ export interface C15tI18nOptions {
228
+ /** Force a locale instead of negotiating `Accept-Language`. */
229
+ locale?: string;
230
+ /** Per-language message overrides, deep-merged over the defaults. */
231
+ messages?: Record<string, unknown>;
232
+ /**
233
+ * Negotiate the locale from the request's `Accept-Language` header.
234
+ *
235
+ * @default true
236
+ */
237
+ detectLanguage?: boolean;
238
+ }
239
+ /**
240
+ * Non-serializable additions merged over the integration options in the
241
+ * browser. Default-export this from
242
+ * {@link C15tAstroOptions.clientEntrypoint}.
243
+ */
244
+ export interface C15tClientOptionsExtension {
245
+ scripts?: Script[];
246
+ callbacks?: Record<string, unknown>;
247
+ /** Merged over the serialized theme. */
248
+ theme?: Theme;
249
+ }
250
+ /**
251
+ * The serialized options shape shared by the middleware, the components and
252
+ * the client boot script.
253
+ *
254
+ * @internal
255
+ */
256
+ export interface C15tResolvedOptions extends Omit<C15tAstroOptions, 'endpoints' | 'middleware' | 'requireUIIntegration'> {
257
+ ui: C15tUIAdapterName;
258
+ colorScheme: C15tColorScheme;
259
+ endpoints: Required<Omit<C15tEndpointOptions, 'enabled'>> & {
260
+ enabled: boolean;
261
+ };
262
+ middleware: Required<C15tMiddlewareOptions>;
263
+ }
264
+ /** Consent context the middleware attaches to every request. */
265
+ export interface C15tLocals {
266
+ /**
267
+ * Server-resolved kernel configuration. Inline it into the page with
268
+ * `<ConsentBanner />` or `buildConfigScript()` so the browser boots with
269
+ * no `/init` roundtrip.
270
+ */
271
+ config: KernelConfig;
272
+ /**
273
+ * The kernel snapshot derived from {@link C15tLocals.config}. Components
274
+ * read translations, policy UI hints and consent state from here so the
275
+ * server and the browser agree on first paint.
276
+ */
277
+ snapshot: ConsentSnapshot;
278
+ /** Whether the server decided this request should see the banner. */
279
+ shouldShowBanner: boolean;
280
+ /**
281
+ * Whether a policy rule is resolved for this request. Every c15t consent
282
+ * surface renders nothing until one is: an unconfigured, failed, or
283
+ * unmatched resolution leaves nothing to consent to, and the browser shows
284
+ * the surfaces on its own once a later init supplies a rule.
285
+ */
286
+ hasPolicy: boolean;
287
+ /**
288
+ * Whether the resolved rule owes any consent UI. A prompt owes a banner
289
+ * and a preference center; rights owe a way back to preferences. A `none`
290
+ * rule with no rights owes neither, so no surface renders while the
291
+ * permissions it grants apply. `false` until a rule is resolved.
292
+ */
293
+ hasConsentUi: boolean;
294
+ /** Resolved policy decision, when the transport produced one. */
295
+ decision: PolicyResolution;
296
+ /** Normalized request inputs (geo, language, GPC). */
297
+ inputs: {
298
+ country?: string;
299
+ region?: string;
300
+ language?: string;
301
+ gpc?: boolean;
302
+ };
303
+ /** The integration options, as the browser will receive them. */
304
+ options: C15tResolvedOptions;
305
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * The dialog surface seam.
3
+ *
4
+ * The banner is server-rendered `.astro` with no framework at all, but the
5
+ * preference centre and the IAB dialog need real component state. Rather
6
+ * than hard-wiring one framework through the whole package, dialogs go
7
+ * through this adapter: `svelte`, `react` and `vue` each implement it, and
8
+ * a site downloads exactly the one its `ui` option names.
9
+ *
10
+ * Nothing framework-specific is named here, and the registry starts empty
11
+ * on purpose. A built-in entry pointing at the React adapter would make
12
+ * every build resolve `@c15t/react` and `react-dom`, which a Svelte-only
13
+ * site does not install. The integration knows `ui` at config time, so it
14
+ * registers the one adapter and the one surface into the page boot script;
15
+ * that is the only place a framework specifier appears.
16
+ */
17
+ import type { ConsentRuntime } from '@c15t/core/runtime';
18
+ import type { C15tResolvedOptions, C15tUIAdapterName } from '../types';
19
+ /** Which dialog a caller asked for. */
20
+ export type ConsentDialogKind = 'preferences' | 'iab';
21
+ /** Context handed to an adapter when a dialog opens. */
22
+ export interface ConsentDialogContext {
23
+ /** The page-level runtime that owns the kernel. */
24
+ runtime: ConsentRuntime;
25
+ /** The resolved integration options. */
26
+ options: C15tResolvedOptions;
27
+ /** The element the surface mounts into. */
28
+ target: HTMLElement;
29
+ /** Which dialog to show. */
30
+ kind: ConsentDialogKind;
31
+ /**
32
+ * Which IAB preference-centre tab to open on. Ignored by the
33
+ * preferences dialog, which has no tabs.
34
+ */
35
+ tab?: 'purposes' | 'vendors';
36
+ }
37
+ /**
38
+ * Loads the component a dialog adapter mounts.
39
+ *
40
+ * The loader is registered by the `.astro` dialog component rather than
41
+ * imported here, so the specifier is resolved by the consuming app's build
42
+ * — which is the only build that knows how to compile a `.svelte` file —
43
+ * and the chunk stays out of the page until someone opens a dialog.
44
+ */
45
+ export type ConsentDialogSurfaceLoader = () => Promise<{
46
+ default: unknown;
47
+ }>;
48
+ /**
49
+ * Register the component a dialog adapter should mount.
50
+ *
51
+ * @param name - The adapter the surface belongs to.
52
+ * @param load - Loader returning the surface module.
53
+ * @example
54
+ * ```ts
55
+ * registerDialogSurface('svelte', () =>
56
+ * import('@c15t/astro/islands/panel-surface.svelte')
57
+ * );
58
+ * ```
59
+ */
60
+ export declare const registerDialogSurface: (name: C15tUIAdapterName, load: ConsentDialogSurfaceLoader) => void;
61
+ /**
62
+ * The registered surface loader for an adapter.
63
+ *
64
+ * @param name - The adapter name.
65
+ * @returns The loader.
66
+ * @throws {Error} When no surface has been registered for that adapter.
67
+ */
68
+ export declare const requireDialogSurface: (name: C15tUIAdapterName) => ConsentDialogSurfaceLoader;
69
+ /** A mounted dialog surface. */
70
+ export interface ConsentDialogHandle {
71
+ /** Hide the dialog without destroying the surface. */
72
+ close: () => void;
73
+ /** Unmount and release everything. */
74
+ destroy: () => Promise<void> | void;
75
+ }
76
+ /** A dialog surface implementation. */
77
+ export interface ConsentDialogAdapter {
78
+ /** Adapter name, matching the `ui` integration option. */
79
+ readonly name: C15tUIAdapterName;
80
+ /**
81
+ * Mount the surface. Called on the first open only; later opens reuse
82
+ * the returned handle.
83
+ *
84
+ * @param context - Runtime, options and mount target.
85
+ * @returns A handle controlling the mounted surface.
86
+ */
87
+ mount: (context: ConsentDialogContext) => Promise<ConsentDialogHandle>;
88
+ /**
89
+ * Warm the surface's chunks without mounting anything, so the first
90
+ * open does not wait on a download.
91
+ */
92
+ preload?: () => Promise<void>;
93
+ }
94
+ /**
95
+ * Register a dialog surface implementation.
96
+ *
97
+ * @param name - The adapter name, matched against the `ui` option.
98
+ * @param load - Loader returning the adapter. Called at most once.
99
+ */
100
+ export declare const registerDialogAdapter: (name: C15tUIAdapterName, load: () => Promise<ConsentDialogAdapter>) => void;
101
+ /**
102
+ * Empties both registries.
103
+ *
104
+ * The registries are module state, so a test that registers an adapter
105
+ * would otherwise decide what the next one sees.
106
+ *
107
+ * Tests only.
108
+ *
109
+ * @internal
110
+ */
111
+ export declare const resetDialogRegistriesForTest: () => void;
112
+ /**
113
+ * Load the adapter named by the `ui` option.
114
+ *
115
+ * The import is dynamic so a page that never opens a dialog never
116
+ * downloads the framework behind it.
117
+ *
118
+ * @param name - The adapter name.
119
+ * @returns The adapter.
120
+ * @throws {Error} When no adapter is registered under that name.
121
+ */
122
+ export declare const loadDialogAdapter: (name: C15tUIAdapterName) => Promise<ConsentDialogAdapter>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Props for the Svelte dialog island.
3
+ *
4
+ * The page runtime owns the kernel and every side-effecting module, so the
5
+ * provider is handed it through `runtime` and neither starts nor disposes
6
+ * it. Everything else it gets here is presentation: categories, legal links
7
+ * and theme tokens the banner and dialog both read.
8
+ */
9
+ import type { ConsentRuntime } from '@c15t/core/runtime';
10
+ import type { C15tResolvedOptions } from '../types';
11
+ /**
12
+ * The slice of the integration options a dialog island renders with.
13
+ *
14
+ * Named so every framework surface can state the same shape instead of
15
+ * widening to `Record<string, unknown>` and casting it back.
16
+ */
17
+ export interface DialogPresentationOptions {
18
+ consentCategories?: C15tResolvedOptions['consentCategories'];
19
+ legalLinks?: C15tResolvedOptions['legalLinks'];
20
+ presentation?: C15tResolvedOptions['presentation'];
21
+ theme?: C15tResolvedOptions['theme'];
22
+ }
23
+ /** Props handed to `ConsentManagerProvider` by the Svelte dialog surface. */
24
+ export interface DialogProviderProps {
25
+ /** The page-level runtime. The provider borrows it, it does not own it. */
26
+ runtime: ConsentRuntime;
27
+ /** Presentation options forwarded to the provider. */
28
+ options: DialogPresentationOptions;
29
+ }
30
+ /**
31
+ * Build the provider props for a dialog island.
32
+ *
33
+ * @param runtime - The page runtime that owns the kernel.
34
+ * @param options - The resolved integration options.
35
+ * @returns Props for `ConsentManagerProvider`.
36
+ */
37
+ export declare const buildProviderProps: (runtime: ConsentRuntime, options: C15tResolvedOptions) => DialogProviderProps;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The React dialog surface.
3
+ *
4
+ * For a site that already ships React, mounting the preference centre in
5
+ * React costs nothing new: the runtime is on the page already, and the
6
+ * dialog reuses it instead of pulling a second framework alongside it. A
7
+ * site that does not ship React should stay on the Svelte default, which
8
+ * is roughly a third of the runtime weight.
9
+ *
10
+ * Mounting happens on the first open through `mount()`, so the island's
11
+ * chunks are paid for only by visitors who click "Customize".
12
+ *
13
+ * The island itself is registered by the integration rather than imported
14
+ * here: a `.tsx` file can only be compiled by the consuming app's build,
15
+ * and only that build knows whether `@astrojs/react` is present.
16
+ */
17
+ import type { ConsentDialogAdapter } from './adapter';
18
+ /** The React 18/19 dialog surface implementation. */
19
+ export declare const reactDialogAdapter: ConsentDialogAdapter;