c15t 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +15 -15
- package/dist/index.cjs +29 -26
- package/dist/index.js +29 -26
- package/dist-types/client/hosted/init.d.ts +1 -1
- package/dist-types/client/hosted/types.d.ts +1 -1
- package/dist-types/client/offline/types.d.ts +1 -1
- package/dist-types/index.d.ts +1 -1
- package/dist-types/libs/iab-tcf/types.d.ts +4 -4
- package/dist-types/libs/policy.d.ts +8 -5
- package/dist-types/store/type.d.ts +2 -2
- package/dist-types/version.d.ts +1 -1
- package/docs/ai-agents.md +111 -0
- package/docs/iab/overview.md +4 -4
- package/docs/integrations/ahrefs-analytics.md +224 -0
- package/docs/integrations/cloudflare-web-analytics.md +194 -0
- package/docs/integrations/crisp.md +214 -0
- package/docs/integrations/databuddy.md +136 -65
- package/docs/integrations/fathom-analytics.md +221 -0
- package/docs/integrations/google-tag-manager.md +84 -15
- package/docs/integrations/google-tag.md +89 -8
- package/docs/integrations/hotjar.md +211 -0
- package/docs/integrations/intercom.md +214 -0
- package/docs/integrations/linkedin-insights.md +130 -11
- package/docs/integrations/matomo-analytics.md +246 -0
- package/docs/integrations/meta-pixel.md +377 -24
- package/docs/integrations/microsoft-clarity.md +241 -0
- package/docs/integrations/microsoft-uet.md +120 -9
- package/docs/integrations/mixpanel-analytics.md +198 -0
- package/docs/integrations/overview.md +69 -74
- package/docs/integrations/plausible-analytics.md +237 -0
- package/docs/integrations/posthog.md +172 -41
- package/docs/integrations/promptwatch.md +187 -0
- package/docs/integrations/reddit-pixel.md +336 -0
- package/docs/integrations/rybbit-analytics.md +222 -0
- package/docs/integrations/segment.md +213 -0
- package/docs/integrations/snapchat-pixel.md +244 -0
- package/docs/integrations/tiktok-pixel.md +88 -10
- package/docs/integrations/umami-analytics.md +220 -0
- package/docs/integrations/vercel-analytics.md +213 -0
- package/docs/integrations/x-pixel.md +99 -10
- package/docs/script-loader.md +168 -51
- package/package.json +25 -15
- package/readme.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# c15t
|
|
2
2
|
|
|
3
|
+
## 2.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4a89092: Expanded the script loader with a registry-backed provider system and a much
|
|
8
|
+
broader set of consent-aware integrations. New helpers cover analytics,
|
|
9
|
+
advertising pixels, functional tools, and tag managers, including Ahrefs,
|
|
10
|
+
Cloudflare Web Analytics, Fathom, Hotjar, Matomo, Microsoft Clarity, Mixpanel,
|
|
11
|
+
Plausible, PromptWatch, Rybbit, Segment, Umami, Vercel Analytics, Reddit Pixel,
|
|
12
|
+
Snapchat Pixel, and Crisp/Intercom.
|
|
13
|
+
|
|
14
|
+
Provider manifests now share common utilities for script URL resolution, boolean
|
|
15
|
+
data attributes, install-step builders, Google consent mapping, and lifecycle
|
|
16
|
+
execution. The package also includes registry metadata, focused provider tests,
|
|
17
|
+
and engine coverage so script helpers resolve predictable loader URLs,
|
|
18
|
+
attributes, consent callbacks, and queued vendor calls.
|
|
19
|
+
|
|
20
|
+
Google Tag and Google Tag Manager boot timestamps now resolve during script
|
|
21
|
+
lifecycle execution instead of helper construction, which keeps documented setup
|
|
22
|
+
patterns compatible with Next.js Cache Components prerendering.
|
|
23
|
+
|
|
24
|
+
PostHog now supports explicit EU/US region selection, keeps the bootstrap script
|
|
25
|
+
host aligned with an explicit API host, and exposes loading modes for immediate
|
|
26
|
+
cookieless consent sync, consent-gated loading, or disabling the helper without
|
|
27
|
+
issuing a PostHog network request.
|
|
28
|
+
|
|
29
|
+
Updated the docs and CLI generation prompts so these providers are discoverable
|
|
30
|
+
from the integration docs and script-loader setup flows.
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- Updated dependencies [1588a24]
|
|
35
|
+
- Updated dependencies [4a89092]
|
|
36
|
+
- @c15t/translations@2.1.0
|
|
37
|
+
- @c15t/schema@2.1.0
|
|
38
|
+
|
|
39
|
+
## 2.0.4
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- 748536a: Refine policy category scope handling.
|
|
44
|
+
- Updated dependencies [748536a]
|
|
45
|
+
- @c15t/schema@2.0.1
|
|
46
|
+
|
|
3
47
|
## 2.0.0
|
|
4
48
|
|
|
5
49
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<a href="https://c15t.com?utm_source=
|
|
2
|
+
<a href="https://c15t.com?utm_source=npm&utm_medium=readme&utm_campaign=oss_readme&utm_content=c15t" target="_blank" rel="noopener noreferrer">
|
|
3
3
|
<picture>
|
|
4
4
|
<source media="(prefers-color-scheme: dark)" srcset="../../docs/assets/c15t-banner-readme-dark.svg" type="image/svg+xml">
|
|
5
5
|
<img src="../../docs/assets/c15t-banner-readme-light.svg" alt="c15t Banner" type="image/svg+xml">
|
|
6
6
|
</picture>
|
|
7
7
|
</a>
|
|
8
|
-
<br />
|
|
9
|
-
<h1 align="center">c15t: Developer-First Consent Management Platform</h1>
|
|
10
8
|
</p>
|
|
11
9
|
|
|
10
|
+
# c15t: Developer-First Consent Management Platform
|
|
11
|
+
|
|
12
12
|
[](https://github.com/c15t/c15t)
|
|
13
13
|
[](https://github.com/c15t/c15t/actions/workflows/ci.yml)
|
|
14
14
|
[](https://github.com/c15t/c15t/blob/main/LICENSE.md)
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
[](https://github.com/c15t/c15t/commits/main)
|
|
19
19
|
[](https://github.com/c15t/c15t/issues)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Headless cookie banner, consent manager & preference center for JavaScript / TypeScript. GDPR, CCPA, LGPD and IAB TCF compliant.
|
|
22
22
|
|
|
23
23
|
## Key Features
|
|
24
24
|
|
|
@@ -34,18 +34,18 @@ Developer-first CMP for JavaScript: cookie banner, consent manager, preferences
|
|
|
34
34
|
- JavaScript or TypeScript project
|
|
35
35
|
- Node.js 18.17.0 or later
|
|
36
36
|
- npm, pnpm, or yarn package manager
|
|
37
|
-
- A hosted [c15t instance](https://
|
|
37
|
+
- A hosted [c15t instance](https://inth.com) (free sign-up) or [self-hosted deployment](https://c15t.com/docs/self-host/v2)
|
|
38
38
|
|
|
39
39
|
## Quick Start
|
|
40
40
|
|
|
41
41
|
Easiest setup with @c15t/cli:
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
#
|
|
45
|
-
pnpm dlx @c15t/cli
|
|
44
|
+
# Set up c15t in your project
|
|
45
|
+
pnpm dlx @c15t/cli setup
|
|
46
46
|
# Alternatives:
|
|
47
|
-
# npx @c15t/cli
|
|
48
|
-
# bunx --bun @c15t/cli
|
|
47
|
+
# npx @c15t/cli setup
|
|
48
|
+
# bunx --bun @c15t/cli setup
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
The CLI will:
|
|
@@ -88,24 +88,24 @@ For further information, guides, and examples visit the [reference documentation
|
|
|
88
88
|
|
|
89
89
|
- Join our [Discord community](https://c15t.link/discord)
|
|
90
90
|
- Open an issue on our [GitHub repository](https://github.com/c15t/c15t/issues)
|
|
91
|
-
- Visit [
|
|
92
|
-
- Contact our support team via email [support@
|
|
91
|
+
- Visit [inth.com](https://inth.com) and use the chat widget
|
|
92
|
+
- Contact our support team via email [support@inth.com](mailto:support@inth.com)
|
|
93
93
|
|
|
94
94
|
## Contributing
|
|
95
95
|
|
|
96
|
-
- We're open to all community contributions
|
|
96
|
+
- We're open to all community contributions.
|
|
97
97
|
- Read our [Contribution Guidelines](https://c15t.com/docs/oss/contributing)
|
|
98
98
|
- Review our [Code of Conduct](https://c15t.com/docs/oss/code-of-conduct)
|
|
99
99
|
- Fork the repository
|
|
100
100
|
- Create a new branch for your feature
|
|
101
101
|
- Submit a pull request
|
|
102
|
-
- **All contributions, big or small, are welcome and appreciated
|
|
102
|
+
- **All contributions, big or small, are welcome and appreciated.**
|
|
103
103
|
|
|
104
104
|
## Security
|
|
105
105
|
|
|
106
106
|
If you believe you have found a security vulnerability in c15t, we encourage you to **_responsibly disclose this and NOT open a public issue_**. We will investigate all legitimate reports.
|
|
107
107
|
|
|
108
|
-
Our preference is that you make use of GitHub's private vulnerability reporting feature to disclose potential security vulnerabilities in our
|
|
108
|
+
Our preference is that you make use of GitHub's private vulnerability reporting feature to disclose potential security vulnerabilities in our open-source software. To do this, please visit [https://github.com/c15t/c15t/security](https://github.com/c15t/c15t/security) and click the "Report a vulnerability" button.
|
|
109
109
|
|
|
110
110
|
### Security Policy
|
|
111
111
|
|
|
@@ -120,4 +120,4 @@ Our preference is that you make use of GitHub's private vulnerability reporting
|
|
|
120
120
|
|
|
121
121
|
---
|
|
122
122
|
|
|
123
|
-
**Built by [Inth](https://inth.com?utm_source=
|
|
123
|
+
**Built by [Inth](https://inth.com?utm_source=npm&utm_medium=readme&utm_campaign=oss_readme&utm_content=c15t)**
|
package/dist/index.cjs
CHANGED
|
@@ -607,7 +607,7 @@ const initial_state_initialState = {
|
|
|
607
607
|
debug: false,
|
|
608
608
|
config: {
|
|
609
609
|
pkg: 'c15t',
|
|
610
|
-
version: "2.
|
|
610
|
+
version: "2.1.0",
|
|
611
611
|
mode: 'Unknown'
|
|
612
612
|
},
|
|
613
613
|
consents: consent_types_consentTypes.reduce((acc, consent)=>{
|
|
@@ -1796,19 +1796,11 @@ function filterConsentCategoriesByPolicy(categories, allowedPurposeIds) {
|
|
|
1796
1796
|
if (!filtered.includes('necessary')) filtered.unshift('necessary');
|
|
1797
1797
|
return filtered;
|
|
1798
1798
|
}
|
|
1799
|
-
function
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
...allowedPurposeIds.filter(isConsentCategory)
|
|
1805
|
-
]);
|
|
1806
|
-
const next = {
|
|
1807
|
-
...consents
|
|
1808
|
-
};
|
|
1809
|
-
for (const category of allConsentNames)if (!allowedCategories.has(category)) next[category] = true;
|
|
1810
|
-
next.necessary = true;
|
|
1811
|
-
return next;
|
|
1799
|
+
function shouldEnforcePolicyCategoryScope(allowedPurposeIds, scopeMode = 'permissive') {
|
|
1800
|
+
return 'strict' === scopeMode && Array.isArray(allowedPurposeIds) && allowedPurposeIds.length > 0 && !allowedPurposeIds.includes('*');
|
|
1801
|
+
}
|
|
1802
|
+
function applyPolicyScopeForRuntimeGating(consents, _allowedPurposeIds, _scopeMode = 'permissive') {
|
|
1803
|
+
return consents;
|
|
1812
1804
|
}
|
|
1813
1805
|
function getEffectivePolicy(initData) {
|
|
1814
1806
|
return initData?.policy;
|
|
@@ -2811,9 +2803,11 @@ async function saveConsents({ manager, type, get, set, options, emitConsentChang
|
|
|
2811
2803
|
if ('all' === type) {
|
|
2812
2804
|
for (const consent of consentTypes)if (consentCategories.includes(consent.name)) newConsents[consent.name] = true;
|
|
2813
2805
|
} else if ('necessary' === type) for (const consent of consentTypes)newConsents[consent.name] = true === consent.disabled ? consent.defaultValue : false;
|
|
2814
|
-
const
|
|
2815
|
-
const
|
|
2816
|
-
const
|
|
2806
|
+
const effectivePolicy = getEffectivePolicy(lastBannerFetchData);
|
|
2807
|
+
const policyCategories = effectivePolicy?.consent?.categories;
|
|
2808
|
+
const shouldEnforcePolicyScope = shouldEnforcePolicyCategoryScope(policyCategories, effectivePolicy?.consent?.scopeMode ?? null);
|
|
2809
|
+
const effectiveConsents = shouldEnforcePolicyScope ? applyPolicyPurposeAllowlist(newConsents, policyCategories) : newConsents;
|
|
2810
|
+
const requestPreferences = shouldEnforcePolicyScope ? stripDisallowedPreferenceKeys(effectiveConsents, policyCategories) : effectiveConsents;
|
|
2817
2811
|
const didChange = haveConsentsChanged(previousConsents, effectiveConsents, consentTypes);
|
|
2818
2812
|
const nextConsentCategoryLists = getConsentCategoryLists(effectiveConsents, consentCategories, consentTypes);
|
|
2819
2813
|
const previousConsentCategoryLists = getConsentCategoryLists(previousConsents, consentCategories, consentTypes);
|
|
@@ -3014,8 +3008,8 @@ function buildStoreUpdate(data, config, effectiveIABEnabled, initSourceMetadata)
|
|
|
3014
3008
|
update.selectedConsents = autoGrantedConsents;
|
|
3015
3009
|
}
|
|
3016
3010
|
const policyCategories = data.policy?.consent?.categories;
|
|
3017
|
-
const
|
|
3018
|
-
if (
|
|
3011
|
+
const hasStrictPolicyCategoryAllowlist = shouldEnforcePolicyCategoryScope(policyCategories, data.policy?.consent?.scopeMode ?? null);
|
|
3012
|
+
if (hasStrictPolicyCategoryAllowlist) {
|
|
3019
3013
|
const uniqueAllowedCategories = filterConsentCategoriesByPolicy(allConsentNames, policyCategories);
|
|
3020
3014
|
update.consentCategories = uniqueAllowedCategories;
|
|
3021
3015
|
update.consents = applyPolicyPurposeAllowlist(update.consents ?? get().consents, uniqueAllowedCategories);
|
|
@@ -3024,7 +3018,8 @@ function buildStoreUpdate(data, config, effectiveIABEnabled, initSourceMetadata)
|
|
|
3024
3018
|
const preselectedCategories = data.policy?.consent?.preselectedCategories;
|
|
3025
3019
|
const shouldApplyPreselectedCategories = null === consentInfo && !autoGrantedConsents && Array.isArray(preselectedCategories) && preselectedCategories.length > 0;
|
|
3026
3020
|
if (shouldApplyPreselectedCategories) {
|
|
3027
|
-
const
|
|
3021
|
+
const displayedConsentNames = update.consentCategories ?? get().consentCategories;
|
|
3022
|
+
const preselectedScope = hasStrictPolicyCategoryAllowlist ? filterConsentCategoriesByPolicy(displayedConsentNames, policyCategories) : displayedConsentNames;
|
|
3028
3023
|
const allowedPreselectedCategories = filterConsentCategoriesByPolicy(preselectedScope, preselectedCategories);
|
|
3029
3024
|
const preselectedSet = new Set(allowedPreselectedCategories);
|
|
3030
3025
|
const selectedConsentBaseline = update.selectedConsents ?? get().selectedConsents;
|
|
@@ -3737,8 +3732,14 @@ const createConsentManagerStore = (manager, options = {})=>{
|
|
|
3737
3732
|
return resetState;
|
|
3738
3733
|
});
|
|
3739
3734
|
},
|
|
3740
|
-
setConsentCategories: (types)=>set({
|
|
3741
|
-
|
|
3735
|
+
setConsentCategories: (types)=>set(()=>{
|
|
3736
|
+
const { policyCategories, policyScopeMode } = get();
|
|
3737
|
+
if (shouldEnforcePolicyCategoryScope(policyCategories, policyScopeMode)) return {
|
|
3738
|
+
consentCategories: filterConsentCategoriesByPolicy(types, policyCategories)
|
|
3739
|
+
};
|
|
3740
|
+
return {
|
|
3741
|
+
consentCategories: Array.from(new Set(types))
|
|
3742
|
+
};
|
|
3742
3743
|
}),
|
|
3743
3744
|
setCallback: (name, callback)=>{
|
|
3744
3745
|
const currentState = get();
|
|
@@ -3810,13 +3811,15 @@ const createConsentManagerStore = (manager, options = {})=>{
|
|
|
3810
3811
|
});
|
|
3811
3812
|
},
|
|
3812
3813
|
updateConsentCategories: (newCategories)=>{
|
|
3814
|
+
const { consentCategories: currentConsentCategories, policyCategories, policyScopeMode } = get();
|
|
3813
3815
|
const allCategoriesSet = new Set([
|
|
3814
|
-
...
|
|
3816
|
+
...currentConsentCategories,
|
|
3815
3817
|
...newCategories
|
|
3816
3818
|
]);
|
|
3817
|
-
|
|
3819
|
+
let consentCategories;
|
|
3820
|
+
consentCategories = shouldEnforcePolicyCategoryScope(policyCategories, policyScopeMode) ? filterConsentCategoriesByPolicy(Array.from(allCategoriesSet), policyCategories) : Array.from(allCategoriesSet);
|
|
3818
3821
|
set({
|
|
3819
|
-
consentCategories
|
|
3822
|
+
consentCategories
|
|
3820
3823
|
});
|
|
3821
3824
|
},
|
|
3822
3825
|
identifyUser: async (user)=>{
|
|
@@ -4070,7 +4073,7 @@ function getOrCreateConsentRuntime(options, pkgInfo) {
|
|
|
4070
4073
|
config: {
|
|
4071
4074
|
...userConfig ?? {},
|
|
4072
4075
|
pkg: pkgInfo?.pkg || 'c15t',
|
|
4073
|
-
version: pkgInfo?.version || "2.
|
|
4076
|
+
version: pkgInfo?.version || "2.1.0",
|
|
4074
4077
|
mode: normalizedMode,
|
|
4075
4078
|
meta: {
|
|
4076
4079
|
...userConfig?.meta ?? {},
|
package/dist/index.js
CHANGED
|
@@ -531,7 +531,7 @@ const initial_state_initialState = {
|
|
|
531
531
|
debug: false,
|
|
532
532
|
config: {
|
|
533
533
|
pkg: 'c15t',
|
|
534
|
-
version: "2.
|
|
534
|
+
version: "2.1.0",
|
|
535
535
|
mode: 'Unknown'
|
|
536
536
|
},
|
|
537
537
|
consents: consent_types_consentTypes.reduce((acc, consent)=>{
|
|
@@ -1720,19 +1720,11 @@ function filterConsentCategoriesByPolicy(categories, allowedPurposeIds) {
|
|
|
1720
1720
|
if (!filtered.includes('necessary')) filtered.unshift('necessary');
|
|
1721
1721
|
return filtered;
|
|
1722
1722
|
}
|
|
1723
|
-
function
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
...allowedPurposeIds.filter(isConsentCategory)
|
|
1729
|
-
]);
|
|
1730
|
-
const next = {
|
|
1731
|
-
...consents
|
|
1732
|
-
};
|
|
1733
|
-
for (const category of allConsentNames)if (!allowedCategories.has(category)) next[category] = true;
|
|
1734
|
-
next.necessary = true;
|
|
1735
|
-
return next;
|
|
1723
|
+
function shouldEnforcePolicyCategoryScope(allowedPurposeIds, scopeMode = 'permissive') {
|
|
1724
|
+
return 'strict' === scopeMode && Array.isArray(allowedPurposeIds) && allowedPurposeIds.length > 0 && !allowedPurposeIds.includes('*');
|
|
1725
|
+
}
|
|
1726
|
+
function applyPolicyScopeForRuntimeGating(consents, _allowedPurposeIds, _scopeMode = 'permissive') {
|
|
1727
|
+
return consents;
|
|
1736
1728
|
}
|
|
1737
1729
|
function getEffectivePolicy(initData) {
|
|
1738
1730
|
return initData?.policy;
|
|
@@ -2734,9 +2726,11 @@ async function saveConsents({ manager, type, get, set, options, emitConsentChang
|
|
|
2734
2726
|
if ('all' === type) {
|
|
2735
2727
|
for (const consent of consentTypes)if (consentCategories.includes(consent.name)) newConsents[consent.name] = true;
|
|
2736
2728
|
} else if ('necessary' === type) for (const consent of consentTypes)newConsents[consent.name] = true === consent.disabled ? consent.defaultValue : false;
|
|
2737
|
-
const
|
|
2738
|
-
const
|
|
2739
|
-
const
|
|
2729
|
+
const effectivePolicy = getEffectivePolicy(lastBannerFetchData);
|
|
2730
|
+
const policyCategories = effectivePolicy?.consent?.categories;
|
|
2731
|
+
const shouldEnforcePolicyScope = shouldEnforcePolicyCategoryScope(policyCategories, effectivePolicy?.consent?.scopeMode ?? null);
|
|
2732
|
+
const effectiveConsents = shouldEnforcePolicyScope ? applyPolicyPurposeAllowlist(newConsents, policyCategories) : newConsents;
|
|
2733
|
+
const requestPreferences = shouldEnforcePolicyScope ? stripDisallowedPreferenceKeys(effectiveConsents, policyCategories) : effectiveConsents;
|
|
2740
2734
|
const didChange = haveConsentsChanged(previousConsents, effectiveConsents, consentTypes);
|
|
2741
2735
|
const nextConsentCategoryLists = getConsentCategoryLists(effectiveConsents, consentCategories, consentTypes);
|
|
2742
2736
|
const previousConsentCategoryLists = getConsentCategoryLists(previousConsents, consentCategories, consentTypes);
|
|
@@ -2937,8 +2931,8 @@ function buildStoreUpdate(data, config, effectiveIABEnabled, initSourceMetadata)
|
|
|
2937
2931
|
update.selectedConsents = autoGrantedConsents;
|
|
2938
2932
|
}
|
|
2939
2933
|
const policyCategories = data.policy?.consent?.categories;
|
|
2940
|
-
const
|
|
2941
|
-
if (
|
|
2934
|
+
const hasStrictPolicyCategoryAllowlist = shouldEnforcePolicyCategoryScope(policyCategories, data.policy?.consent?.scopeMode ?? null);
|
|
2935
|
+
if (hasStrictPolicyCategoryAllowlist) {
|
|
2942
2936
|
const uniqueAllowedCategories = filterConsentCategoriesByPolicy(allConsentNames, policyCategories);
|
|
2943
2937
|
update.consentCategories = uniqueAllowedCategories;
|
|
2944
2938
|
update.consents = applyPolicyPurposeAllowlist(update.consents ?? get().consents, uniqueAllowedCategories);
|
|
@@ -2947,7 +2941,8 @@ function buildStoreUpdate(data, config, effectiveIABEnabled, initSourceMetadata)
|
|
|
2947
2941
|
const preselectedCategories = data.policy?.consent?.preselectedCategories;
|
|
2948
2942
|
const shouldApplyPreselectedCategories = null === consentInfo && !autoGrantedConsents && Array.isArray(preselectedCategories) && preselectedCategories.length > 0;
|
|
2949
2943
|
if (shouldApplyPreselectedCategories) {
|
|
2950
|
-
const
|
|
2944
|
+
const displayedConsentNames = update.consentCategories ?? get().consentCategories;
|
|
2945
|
+
const preselectedScope = hasStrictPolicyCategoryAllowlist ? filterConsentCategoriesByPolicy(displayedConsentNames, policyCategories) : displayedConsentNames;
|
|
2951
2946
|
const allowedPreselectedCategories = filterConsentCategoriesByPolicy(preselectedScope, preselectedCategories);
|
|
2952
2947
|
const preselectedSet = new Set(allowedPreselectedCategories);
|
|
2953
2948
|
const selectedConsentBaseline = update.selectedConsents ?? get().selectedConsents;
|
|
@@ -3660,8 +3655,14 @@ const createConsentManagerStore = (manager, options = {})=>{
|
|
|
3660
3655
|
return resetState;
|
|
3661
3656
|
});
|
|
3662
3657
|
},
|
|
3663
|
-
setConsentCategories: (types)=>set({
|
|
3664
|
-
|
|
3658
|
+
setConsentCategories: (types)=>set(()=>{
|
|
3659
|
+
const { policyCategories, policyScopeMode } = get();
|
|
3660
|
+
if (shouldEnforcePolicyCategoryScope(policyCategories, policyScopeMode)) return {
|
|
3661
|
+
consentCategories: filterConsentCategoriesByPolicy(types, policyCategories)
|
|
3662
|
+
};
|
|
3663
|
+
return {
|
|
3664
|
+
consentCategories: Array.from(new Set(types))
|
|
3665
|
+
};
|
|
3665
3666
|
}),
|
|
3666
3667
|
setCallback: (name, callback)=>{
|
|
3667
3668
|
const currentState = get();
|
|
@@ -3733,13 +3734,15 @@ const createConsentManagerStore = (manager, options = {})=>{
|
|
|
3733
3734
|
});
|
|
3734
3735
|
},
|
|
3735
3736
|
updateConsentCategories: (newCategories)=>{
|
|
3737
|
+
const { consentCategories: currentConsentCategories, policyCategories, policyScopeMode } = get();
|
|
3736
3738
|
const allCategoriesSet = new Set([
|
|
3737
|
-
...
|
|
3739
|
+
...currentConsentCategories,
|
|
3738
3740
|
...newCategories
|
|
3739
3741
|
]);
|
|
3740
|
-
|
|
3742
|
+
let consentCategories;
|
|
3743
|
+
consentCategories = shouldEnforcePolicyCategoryScope(policyCategories, policyScopeMode) ? filterConsentCategoriesByPolicy(Array.from(allCategoriesSet), policyCategories) : Array.from(allCategoriesSet);
|
|
3741
3744
|
set({
|
|
3742
|
-
consentCategories
|
|
3745
|
+
consentCategories
|
|
3743
3746
|
});
|
|
3744
3747
|
},
|
|
3745
3748
|
identifyUser: async (user)=>{
|
|
@@ -3993,7 +3996,7 @@ function getOrCreateConsentRuntime(options, pkgInfo) {
|
|
|
3993
3996
|
config: {
|
|
3994
3997
|
...userConfig ?? {},
|
|
3995
3998
|
pkg: pkgInfo?.pkg || 'c15t',
|
|
3996
|
-
version: pkgInfo?.version || "2.
|
|
3999
|
+
version: pkgInfo?.version || "2.1.0",
|
|
3997
4000
|
mode: normalizedMode,
|
|
3998
4001
|
meta: {
|
|
3999
4002
|
...userConfig?.meta ?? {},
|
|
@@ -5,7 +5,7 @@ import type { IABFallbackConfig } from './types';
|
|
|
5
5
|
/**
|
|
6
6
|
* Provides offline mode fallback for showConsentBanner API.
|
|
7
7
|
* Simulates the behavior of OfflineClient when API requests fail.
|
|
8
|
-
* In fallback mode, fetches GVL from gvl.
|
|
8
|
+
* In fallback mode, fetches GVL from gvl.inth.app when IAB is enabled.
|
|
9
9
|
* @internal
|
|
10
10
|
*/
|
|
11
11
|
export declare function offlineFallbackForConsentBanner(options?: FetchOptions<InitResponse>, iabConfig?: IABFallbackConfig): Promise<ResponseContext<InitResponse>>;
|
|
@@ -59,7 +59,7 @@ export interface C15tInternalClientOptions {
|
|
|
59
59
|
/**
|
|
60
60
|
* IAB configuration for offline/fallback mode.
|
|
61
61
|
* When the backend is unavailable and IAB is enabled,
|
|
62
|
-
* the client will fetch GVL from gvl.
|
|
62
|
+
* the client will fetch GVL from gvl.inth.app with these settings.
|
|
63
63
|
*/
|
|
64
64
|
iabConfig?: IABFallbackConfig;
|
|
65
65
|
}
|
|
@@ -6,7 +6,7 @@ import type { IABFallbackConfig } from '../hosted/types';
|
|
|
6
6
|
export interface OfflineClientOptions {
|
|
7
7
|
/**
|
|
8
8
|
* IAB configuration for offline mode.
|
|
9
|
-
* When IAB is enabled, the client will fetch GVL from gvl.
|
|
9
|
+
* When IAB is enabled, the client will fetch GVL from gvl.inth.app.
|
|
10
10
|
*/
|
|
11
11
|
iabConfig?: IABFallbackConfig;
|
|
12
12
|
/**
|
package/dist-types/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export { applyPolicyPurposeAllowlist, applyPolicyScopeForRuntimeGating, filterCo
|
|
|
24
24
|
export { flattenPolicyActionGroups, hasPolicyHints, resolvePolicyActionGroups, resolvePolicyAllowedActions, resolvePolicyDirection, resolvePolicyOrderedActions, resolvePolicyPrimaryActions, resolvePolicyUiProfile, shouldFillPolicyActions, } from './libs/policy-actions';
|
|
25
25
|
export type { PrefetchOptions } from './libs/prefetch';
|
|
26
26
|
export { buildPrefetchScript } from './libs/prefetch';
|
|
27
|
-
export { emitScriptDebugEvent, getLoadedScriptIds, isScriptLoaded, loadScripts, type Script, type ScriptDebugAction, type ScriptDebugEvent, type ScriptDebugEventInput, type ScriptDebugListener, type ScriptDebugScope, type ScriptDebugSource, type ScriptLifecycleCallback, subscribeToScriptDebugEvents, unloadScripts, updateScripts, } from './libs/script-loader';
|
|
27
|
+
export { emitScriptDebugEvent, getLoadedScriptIds, isScriptLoaded, loadScripts, type Script, type ScriptCallbackInfo, type ScriptDebugAction, type ScriptDebugEvent, type ScriptDebugEventInput, type ScriptDebugListener, type ScriptDebugScope, type ScriptDebugSource, type ScriptLifecycleCallback, subscribeToScriptDebugEvents, unloadScripts, updateScripts, } from './libs/script-loader';
|
|
28
28
|
export { type ConsentRuntimeOptions, type ConsentRuntimePkgInfo, type ConsentRuntimeResult, clearConsentRuntimeCache, getOrCreateConsentRuntime, } from './runtime';
|
|
29
29
|
export { createConsentManagerStore } from './store';
|
|
30
30
|
export type { ActiveUI, ConsentStoreState, InitDataSource, OfflinePolicyConfig, PolicyScopeMode, PolicySurfaceState, PolicyUiAction, PolicyUiActionDirection, PolicyUiActionGroup, PolicyUiProfile, PolicyUiSurfaceConfig, SSRInitialData, SSRInitRequestContext, SSRInitRequestMetadata, SSRSkippedReason, StoreOptions, UnstableGenericPolicyConsentInput, UnstableLegalDocumentConsentInput, UnstablePolicyConsentInput, } from './store/type';
|
|
@@ -137,7 +137,7 @@ export type IABManager = IABState & IABActions;
|
|
|
137
137
|
* @internal
|
|
138
138
|
*/
|
|
139
139
|
export interface CMPApiConfig {
|
|
140
|
-
/** CMP ID registered with IAB Europe. Provided by the backend (
|
|
140
|
+
/** CMP ID registered with IAB Europe. Provided by the backend (inth.com) or client config. */
|
|
141
141
|
cmpId?: number;
|
|
142
142
|
/** CMP version (default: package version from ~/cmp-defaults) */
|
|
143
143
|
cmpVersion?: number | string;
|
|
@@ -216,7 +216,7 @@ export interface IABConfig {
|
|
|
216
216
|
* Enable IAB TCF 2.3 mode.
|
|
217
217
|
*
|
|
218
218
|
* When enabled, c15t will:
|
|
219
|
-
* - Fetch GVL from gvl.
|
|
219
|
+
* - Fetch GVL from gvl.inth.app
|
|
220
220
|
* - Initialize __tcfapi CMP API
|
|
221
221
|
* - Generate TC Strings for IAB compliance
|
|
222
222
|
*
|
|
@@ -236,7 +236,7 @@ export interface IABConfig {
|
|
|
236
236
|
/**
|
|
237
237
|
* CMP ID registered with IAB Europe.
|
|
238
238
|
*
|
|
239
|
-
* When using
|
|
239
|
+
* When using inth.com as the backend, this is automatically provided
|
|
240
240
|
* via the `/init` endpoint — no client-side configuration needed.
|
|
241
241
|
*
|
|
242
242
|
* Only set this if you self-host and have your own CMP registration.
|
|
@@ -325,4 +325,4 @@ export declare const IAB_STORAGE_KEYS: {
|
|
|
325
325
|
*
|
|
326
326
|
* @internal
|
|
327
327
|
*/
|
|
328
|
-
export declare const GVL_ENDPOINT = "https://gvl.
|
|
328
|
+
export declare const GVL_ENDPOINT = "https://gvl.inth.app";
|
|
@@ -40,12 +40,15 @@ export declare function stripDisallowedPreferenceKeys<T extends Record<string, b
|
|
|
40
40
|
*/
|
|
41
41
|
export declare function filterConsentCategoriesByPolicy(categories: AllConsentNames[], allowedPurposeIds?: string[] | null): AllConsentNames[];
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
* Returns whether policy category scope should be enforced as a hard allowlist.
|
|
44
|
+
*/
|
|
45
|
+
export declare function shouldEnforcePolicyCategoryScope(allowedPurposeIds?: string[] | null, scopeMode?: 'strict' | 'permissive' | null): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* @deprecated No-op retained for API compatibility. Runtime gating respects
|
|
48
|
+
* the current consent state directly; policy scope is enforced at category
|
|
49
|
+
* discovery, render, and save time instead.
|
|
47
50
|
*/
|
|
48
|
-
export declare function applyPolicyScopeForRuntimeGating(consents: ConsentState,
|
|
51
|
+
export declare function applyPolicyScopeForRuntimeGating(consents: ConsentState, _allowedPurposeIds?: string[] | null, _scopeMode?: 'strict' | 'permissive' | null): ConsentState;
|
|
49
52
|
/**
|
|
50
53
|
* Gets the runtime policy returned by /init, if present.
|
|
51
54
|
*/
|
|
@@ -245,7 +245,7 @@ export interface SSRInitialData {
|
|
|
245
245
|
/**
|
|
246
246
|
* Global Vendor List data for IAB TCF mode.
|
|
247
247
|
* - `undefined` means IAB is not active for the request or not enabled on server
|
|
248
|
-
* - `null` means the user is in a non-IAB region (204 response from gvl.
|
|
248
|
+
* - `null` means the user is in a non-IAB region (204 response from gvl.inth.app)
|
|
249
249
|
* - `GlobalVendorList` contains the vendor list data from init response
|
|
250
250
|
*
|
|
251
251
|
* Note: When init returns 200 without gvl, client IAB settings are overridden to disabled.
|
|
@@ -469,7 +469,7 @@ export interface StoreOptions extends Partial<StoreConfig> {
|
|
|
469
469
|
* Note: If the server returns 200 without GVL, client IAB settings are
|
|
470
470
|
* automatically overridden to disabled (server takes precedence).
|
|
471
471
|
*
|
|
472
|
-
* In offline/fallback mode, GVL is fetched from gvl.
|
|
472
|
+
* In offline/fallback mode, GVL is fetched from gvl.inth.app.
|
|
473
473
|
*
|
|
474
474
|
* This is an opt-in feature with zero bundle impact when not enabled.
|
|
475
475
|
*
|
package/dist-types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.
|
|
1
|
+
export declare const version = "2.1.0";
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: AI Agents
|
|
3
|
+
description: Integrate c15t with AI coding assistants using the docs bundled in each package and c15t agent skills. Give agents version-matched local docs for consent management, banners, script loading, callbacks, and integrations.
|
|
4
|
+
lastModified: 2026-03-24
|
|
5
|
+
---
|
|
6
|
+
## Bundled Docs
|
|
7
|
+
|
|
8
|
+
Every supported c15t package now ships docs inside the installed package itself.
|
|
9
|
+
|
|
10
|
+
### Where to find them
|
|
11
|
+
|
|
12
|
+
* `node_modules/c15t/docs/README.md`
|
|
13
|
+
* `node_modules/@c15t/react/docs/README.md`
|
|
14
|
+
* `node_modules/@c15t/nextjs/docs/README.md`
|
|
15
|
+
* `node_modules/@c15t/backend/docs/README.md`
|
|
16
|
+
|
|
17
|
+
Start with the package `README.md`, then follow its linked pages for the relevant workflow.
|
|
18
|
+
|
|
19
|
+
These docs are version-matched to the exact c15t package version in your project, including generated reference content like prop and type tables.
|
|
20
|
+
|
|
21
|
+
### Why use them
|
|
22
|
+
|
|
23
|
+
If your app uses multiple c15t packages, use the docs from each relevant installed package instead of relying on stale model knowledge.
|
|
24
|
+
|
|
25
|
+
### Agent philosophy
|
|
26
|
+
|
|
27
|
+
When an AI tool is helping with c15t behavior, it should read the installed c15t docs first and use model knowledge second. That keeps consent flows, script gating, banner behavior, and integrations aligned with the exact version you have installed.
|
|
28
|
+
|
|
29
|
+
### Customization ladder for agents
|
|
30
|
+
|
|
31
|
+
When an agent is working on consent UI, it should choose the lowest-power tool that solves the task:
|
|
32
|
+
|
|
33
|
+
1. Start with the pre-built component and its existing props or provider options
|
|
34
|
+
2. Use `theme` tokens for semantic visual changes
|
|
35
|
+
3. Use `theme.slots` for targeted styling of specific parts
|
|
36
|
+
4. Use CSS variables or className-level overrides only when integrating with external styles
|
|
37
|
+
5. Use compound components only when the markup order must change
|
|
38
|
+
6. Use `noStyle` only when c15t structure is still correct but all styling must be replaced
|
|
39
|
+
7. Use headless hooks only when markup and behavior both need to be rebuilt
|
|
40
|
+
|
|
41
|
+
For common tasks:
|
|
42
|
+
|
|
43
|
+
* Banner footer background -> `theme.colors.surfaceHover`
|
|
44
|
+
* Banner card background -> `theme.colors.surface`
|
|
45
|
+
* Banner card/footer/title tweaks -> banner slots
|
|
46
|
+
* Stock action styling -> `theme.consentActions`
|
|
47
|
+
* Copy changes -> `ConsentManagerProvider.options.i18n`
|
|
48
|
+
|
|
49
|
+
If a token appears not to work, the agent should verify the token-to-component mapping before suggesting CSS overrides, `!important`, `noStyle`, or headless mode.
|
|
50
|
+
|
|
51
|
+
***
|
|
52
|
+
|
|
53
|
+
## Agent Skills
|
|
54
|
+
|
|
55
|
+
c15t publishes agent skills that give AI coding assistants deep knowledge of c15t's APIs, components, and configuration. Skills are reusable workflows and tool-specific guidance, not version-matched local docs.
|
|
56
|
+
|
|
57
|
+
### Installation
|
|
58
|
+
|
|
59
|
+
Via the c15t CLI:
|
|
60
|
+
|
|
61
|
+
|Package manager|Command|
|
|
62
|
+
|:--|:--|
|
|
63
|
+
|npm|`npx @c15t/cli install-skills`|
|
|
64
|
+
|pnpm|`pnpm dlx @c15t/cli install-skills`|
|
|
65
|
+
|yarn|`yarn dlx @c15t/cli install-skills`|
|
|
66
|
+
|bun|`bunx @c15t/cli install-skills`|
|
|
67
|
+
|
|
68
|
+
Or directly:
|
|
69
|
+
|
|
70
|
+
|Package manager|Command|
|
|
71
|
+
|:--|:--|
|
|
72
|
+
|npm|`npx skills add c15t/skills`|
|
|
73
|
+
|pnpm|`pnpm dlx skills add c15t/skills`|
|
|
74
|
+
|yarn|`yarn dlx skills add c15t/skills`|
|
|
75
|
+
|bun|`bunx skills add c15t/skills`|
|
|
76
|
+
|
|
77
|
+
### What skills provide
|
|
78
|
+
|
|
79
|
+
* **Styling customization** — strict escalation guidance across props, tokens, slots, CSS variables, compound components, `noStyle`, and headless
|
|
80
|
+
* **Internationalization** — translation setup, locale routing integration
|
|
81
|
+
* **Script management** — configuring third-party scripts with consent categories
|
|
82
|
+
* **Component setup** — ConsentBanner, ConsentDialog, provider configuration
|
|
83
|
+
|
|
84
|
+
### Supported tools
|
|
85
|
+
|
|
86
|
+
* Claude Code
|
|
87
|
+
* Cursor
|
|
88
|
+
* GitHub Copilot (via `.github/skills`)
|
|
89
|
+
* Any agent that supports the skills format
|
|
90
|
+
|
|
91
|
+
***
|
|
92
|
+
|
|
93
|
+
## When to use which
|
|
94
|
+
|
|
95
|
+
Use bundled docs when:
|
|
96
|
+
|
|
97
|
+
* Your agent can read files in the local project
|
|
98
|
+
* You want version-matched docs from the installed c15t packages
|
|
99
|
+
* You want a package-local README that tells the agent which detailed docs to read first
|
|
100
|
+
* You want concrete guidance for consent management, cookie banners, consent dialogs, preference centers, script loading, callbacks, and integrations
|
|
101
|
+
|
|
102
|
+
Use agent skills when:
|
|
103
|
+
|
|
104
|
+
* Your tool supports the skills ecosystem
|
|
105
|
+
* You want reusable workflows and tool-specific guidance that can point back to the installed package README files
|
|
106
|
+
|
|
107
|
+
Use both when:
|
|
108
|
+
|
|
109
|
+
* Your tool supports both local file context and skills
|
|
110
|
+
* You want local package docs plus reusable setup and configuration help
|
|
111
|
+
* You want the bundled package docs as the source of truth plus a reusable decision tree for customization
|
package/docs/iab/overview.md
CHANGED
|
@@ -15,12 +15,12 @@ When your site participates in the IAB ecosystem (ad exchanges, SSPs, DSPs, DMPs
|
|
|
15
15
|
|
|
16
16
|
## CMP Registration
|
|
17
17
|
|
|
18
|
-
[
|
|
18
|
+
[Inth](https://inth.com), c15t's hosted platform, is IAB TCF certified. When you use Inth as your backend with c15t's prebuilt IAB UI, the correct CMP ID is automatically provided to your client via the `/init` endpoint — no client-side configuration needed.
|
|
19
19
|
|
|
20
|
-
If you self-host the c15t backend
|
|
20
|
+
If you self-host the c15t backend or want to operate as your own CMP, register your own CMP with IAB Europe and configure your CMP ID on the backend via `advanced.iab.cmpId` or on the client via the `iab.cmpId` option. Registering your own CMP may also involve IAB Europe fees, so check IAB Europe's current CMP registration terms and pricing before choosing this route. A valid (non-zero) CMP ID is required for IAB TCF compliance.
|
|
21
21
|
|
|
22
22
|
> ℹ️ **Info:**
|
|
23
|
-
> If you heavily customize or build your own IAB banner or dialog
|
|
23
|
+
> If you heavily customize or build your own IAB banner or dialog instead of using the default IABConsentBanner and IABConsentDialog components, you cannot use Inth's CMP ID. You must register your own CMP with IAB Europe and use your own CMP ID.
|
|
24
24
|
|
|
25
25
|
## How c15t Implements TCF
|
|
26
26
|
|
|
@@ -95,7 +95,7 @@ Configure IAB mode with `iab({ ... })` from `@c15t/iab`. The factory enables the
|
|
|
95
95
|
|
|
96
96
|
|Option|Type|Description|
|
|
97
97
|
|--|--|--|
|
|
98
|
-
|`cmpId`|`number`|CMP ID registered with IAB Europe. Automatically provided by the backend when using
|
|
98
|
+
|`cmpId`|`number`|CMP ID registered with IAB Europe. Automatically provided by the backend when using Inth with the prebuilt IAB UI. Only set this if you have your own CMP registration.|
|
|
99
99
|
|`vendors`|`number[]`|IAB vendor IDs that your site works with|
|
|
100
100
|
|`customVendors`|`NonIABVendor[]`|Custom vendors not in the IAB registry|
|
|
101
101
|
|