@c15t/scripts 3.0.0-alpha.2 → 3.0.0-alpha.4

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 (300) hide show
  1. package/AGENTS.md +129 -63
  2. package/README.md +8 -29
  3. package/SKILL.md +33 -0
  4. package/dist/adobe-analytics.js +2 -0
  5. package/dist/ahrefs-analytics.js +2 -0
  6. package/dist/amplitude.js +2 -0
  7. package/dist/clearbit.js +2 -0
  8. package/dist/cloudflare-web-analytics.js +2 -0
  9. package/dist/cloudflare-zaraz.js +2 -0
  10. package/dist/crisp.js +2 -0
  11. package/dist/databuddy.js +2 -0
  12. package/dist/e2e-test-utils.js +2 -139
  13. package/dist/engine/compile.js +2 -89
  14. package/dist/engine/runtime.js +2 -448
  15. package/dist/events.js +2 -218
  16. package/dist/fathom-analytics.js +2 -0
  17. package/dist/front-chat.js +2 -0
  18. package/dist/google-tag-manager.js +2 -0
  19. package/dist/google-tag.js +2 -0
  20. package/dist/heap.js +2 -0
  21. package/dist/hightouch.js +2 -0
  22. package/dist/hotjar.js +2 -0
  23. package/dist/intercom.js +2 -0
  24. package/dist/klaviyo.js +2 -0
  25. package/dist/linkedin-insights.js +2 -0
  26. package/dist/logrocket.js +2 -0
  27. package/dist/matomo-analytics.js +2 -0
  28. package/dist/meta-pixel.js +2 -0
  29. package/dist/microsoft-clarity.js +2 -0
  30. package/dist/microsoft-uet.js +2 -0
  31. package/dist/mixpanel-analytics.js +2 -0
  32. package/dist/one-dollar-stats.js +2 -0
  33. package/dist/openai-pixel.js +2 -0
  34. package/dist/pinterest-tag.js +2 -0
  35. package/dist/pirsch.js +2 -0
  36. package/dist/plausible-analytics.js +2 -0
  37. package/dist/posthog.js +2 -0
  38. package/dist/promptwatch.js +2 -0
  39. package/dist/reddit-pixel.js +2 -0
  40. package/dist/registry.js +2 -422
  41. package/dist/resolve.js +2 -33
  42. package/dist/rudderstack.js +2 -0
  43. package/dist/rybbit-analytics.js +2 -0
  44. package/dist/segment.js +2 -0
  45. package/dist/snapchat-pixel.js +2 -0
  46. package/dist/tiktok-pixel.js +2 -0
  47. package/dist/types.js +2 -16
  48. package/dist/umami-analytics.js +2 -0
  49. package/dist/vendors/_shared/attributes.js +2 -14
  50. package/dist/vendors/_shared/google-consent.js +2 -27
  51. package/dist/vendors/_shared/install-builders.js +2 -21
  52. package/dist/vendors/_shared/required-id.js +2 -0
  53. package/dist/vendors/_shared/script-url.js +2 -28
  54. package/dist/vendors/ads-and-pixels/linkedin-insights.js +2 -48
  55. package/dist/vendors/ads-and-pixels/meta-pixel.js +2 -153
  56. package/dist/vendors/ads-and-pixels/microsoft-uet.js +2 -110
  57. package/dist/vendors/ads-and-pixels/openai-pixel.js +2 -88
  58. package/dist/vendors/ads-and-pixels/pinterest-tag.js +2 -123
  59. package/dist/vendors/ads-and-pixels/reddit-pixel.js +2 -107
  60. package/dist/vendors/ads-and-pixels/snapchat-pixel.js +2 -87
  61. package/dist/vendors/ads-and-pixels/tiktok-pixel.js +2 -89
  62. package/dist/vendors/ads-and-pixels/x-pixel.js +2 -48
  63. package/dist/vendors/analytics/adobe-analytics.js +2 -49
  64. package/dist/vendors/analytics/ahrefs-analytics.js +2 -27
  65. package/dist/vendors/analytics/amplitude.js +2 -134
  66. package/dist/vendors/analytics/clearbit.js +2 -28
  67. package/dist/vendors/analytics/cloudflare-web-analytics.js +2 -32
  68. package/dist/vendors/analytics/databuddy.js +2 -103
  69. package/dist/vendors/analytics/fathom-analytics.js +2 -35
  70. package/dist/vendors/analytics/google-tag.js +2 -78
  71. package/dist/vendors/analytics/heap.js +2 -134
  72. package/dist/vendors/analytics/hightouch.js +2 -109
  73. package/dist/vendors/analytics/hotjar.js +2 -44
  74. package/dist/vendors/analytics/logrocket.js +2 -58
  75. package/dist/vendors/analytics/matomo-analytics.js +2 -191
  76. package/dist/vendors/analytics/microsoft-clarity.js +2 -100
  77. package/dist/vendors/analytics/mixpanel-analytics.js +2 -93
  78. package/dist/vendors/analytics/one-dollar-stats.js +2 -30
  79. package/dist/vendors/analytics/pirsch.js +2 -67
  80. package/dist/vendors/analytics/plausible-analytics.js +2 -81
  81. package/dist/vendors/analytics/posthog.js +2 -200
  82. package/dist/vendors/analytics/promptwatch.js +2 -29
  83. package/dist/vendors/analytics/rudderstack.js +2 -183
  84. package/dist/vendors/analytics/rybbit-analytics.js +2 -63
  85. package/dist/vendors/analytics/segment.js +2 -65
  86. package/dist/vendors/analytics/umami-analytics.js +2 -39
  87. package/dist/vendors/analytics/vercel-analytics.js +2 -53
  88. package/dist/vendors/email-and-sms/klaviyo.js +2 -0
  89. package/dist/vendors/functional/crisp.js +2 -100
  90. package/dist/vendors/functional/front-chat.js +2 -64
  91. package/dist/vendors/functional/intercom.js +2 -45
  92. package/dist/vendors/tag-managers/cloudflare-zaraz.js +2 -98
  93. package/dist/vendors/tag-managers/google-tag-manager.js +2 -73
  94. package/dist/vercel-analytics.js +2 -0
  95. package/dist/x-pixel.js +2 -0
  96. package/dist-types/adobe-analytics.d.ts +2 -0
  97. package/dist-types/ahrefs-analytics.d.ts +2 -0
  98. package/dist-types/amplitude.d.ts +2 -0
  99. package/dist-types/clearbit.d.ts +2 -0
  100. package/dist-types/cloudflare-web-analytics.d.ts +2 -0
  101. package/dist-types/cloudflare-zaraz.d.ts +2 -0
  102. package/dist-types/crisp.d.ts +2 -0
  103. package/dist-types/databuddy.d.ts +2 -0
  104. package/dist-types/e2e-test-utils.d.ts +2 -0
  105. package/dist-types/engine/compile.d.ts +2 -3
  106. package/dist-types/engine/runtime.d.ts +2 -3
  107. package/dist-types/events.d.ts +2 -46
  108. package/dist-types/fathom-analytics.d.ts +2 -0
  109. package/dist-types/front-chat.d.ts +2 -0
  110. package/dist-types/google-tag-manager.d.ts +2 -0
  111. package/dist-types/google-tag.d.ts +2 -0
  112. package/dist-types/heap.d.ts +2 -0
  113. package/dist-types/hightouch.d.ts +2 -0
  114. package/dist-types/hotjar.d.ts +2 -0
  115. package/dist-types/intercom.d.ts +2 -0
  116. package/dist-types/klaviyo.d.ts +2 -0
  117. package/dist-types/linkedin-insights.d.ts +2 -0
  118. package/dist-types/logrocket.d.ts +2 -0
  119. package/dist-types/matomo-analytics.d.ts +2 -0
  120. package/dist-types/meta-pixel.d.ts +2 -0
  121. package/dist-types/microsoft-clarity.d.ts +2 -0
  122. package/dist-types/microsoft-uet.d.ts +2 -0
  123. package/dist-types/mixpanel-analytics.d.ts +2 -0
  124. package/dist-types/one-dollar-stats.d.ts +2 -0
  125. package/dist-types/openai-pixel.d.ts +2 -0
  126. package/dist-types/pinterest-tag.d.ts +2 -0
  127. package/dist-types/pirsch.d.ts +2 -0
  128. package/dist-types/plausible-analytics.d.ts +2 -0
  129. package/dist-types/posthog.d.ts +2 -0
  130. package/dist-types/promptwatch.d.ts +2 -0
  131. package/dist-types/reddit-pixel.d.ts +2 -0
  132. package/dist-types/registry.d.ts +2 -485
  133. package/dist-types/resolve.d.ts +2 -9
  134. package/dist-types/rudderstack.d.ts +2 -0
  135. package/dist-types/rybbit-analytics.d.ts +2 -0
  136. package/dist-types/segment.d.ts +2 -0
  137. package/dist-types/snapchat-pixel.d.ts +2 -0
  138. package/dist-types/tiktok-pixel.d.ts +2 -0
  139. package/dist-types/types.d.ts +2 -314
  140. package/dist-types/umami-analytics.d.ts +2 -0
  141. package/dist-types/vendors/_shared/attributes.d.ts +2 -35
  142. package/dist-types/vendors/_shared/google-consent.d.ts +2 -47
  143. package/dist-types/vendors/_shared/install-builders.d.ts +2 -30
  144. package/dist-types/vendors/_shared/required-id.d.ts +2 -0
  145. package/dist-types/vendors/_shared/script-url.d.ts +2 -75
  146. package/dist-types/vendors/ads-and-pixels/linkedin-insights.d.ts +2 -92
  147. package/dist-types/vendors/ads-and-pixels/meta-pixel.d.ts +2 -289
  148. package/dist-types/vendors/ads-and-pixels/microsoft-uet.d.ts +2 -105
  149. package/dist-types/vendors/ads-and-pixels/openai-pixel.d.ts +2 -211
  150. package/dist-types/vendors/ads-and-pixels/pinterest-tag.d.ts +2 -295
  151. package/dist-types/vendors/ads-and-pixels/reddit-pixel.d.ts +2 -210
  152. package/dist-types/vendors/ads-and-pixels/snapchat-pixel.d.ts +2 -171
  153. package/dist-types/vendors/ads-and-pixels/tiktok-pixel.d.ts +2 -106
  154. package/dist-types/vendors/ads-and-pixels/x-pixel.d.ts +2 -183
  155. package/dist-types/vendors/analytics/adobe-analytics.d.ts +2 -75
  156. package/dist-types/vendors/analytics/ahrefs-analytics.d.ts +2 -62
  157. package/dist-types/vendors/analytics/amplitude.d.ts +2 -234
  158. package/dist-types/vendors/analytics/clearbit.d.ts +2 -60
  159. package/dist-types/vendors/analytics/cloudflare-web-analytics.d.ts +2 -67
  160. package/dist-types/vendors/analytics/databuddy.d.ts +2 -147
  161. package/dist-types/vendors/analytics/fathom-analytics.d.ts +2 -90
  162. package/dist-types/vendors/analytics/google-tag.d.ts +2 -95
  163. package/dist-types/vendors/analytics/heap.d.ts +2 -316
  164. package/dist-types/vendors/analytics/hightouch.d.ts +2 -285
  165. package/dist-types/vendors/analytics/hotjar.d.ts +2 -73
  166. package/dist-types/vendors/analytics/logrocket.d.ts +2 -101
  167. package/dist-types/vendors/analytics/matomo-analytics.d.ts +2 -41
  168. package/dist-types/vendors/analytics/microsoft-clarity.d.ts +2 -97
  169. package/dist-types/vendors/analytics/mixpanel-analytics.d.ts +2 -113
  170. package/dist-types/vendors/analytics/one-dollar-stats.d.ts +2 -39
  171. package/dist-types/vendors/analytics/pirsch.d.ts +2 -96
  172. package/dist-types/vendors/analytics/plausible-analytics.d.ts +2 -122
  173. package/dist-types/vendors/analytics/posthog.d.ts +2 -175
  174. package/dist-types/vendors/analytics/promptwatch.d.ts +2 -36
  175. package/dist-types/vendors/analytics/rudderstack.d.ts +2 -330
  176. package/dist-types/vendors/analytics/rybbit-analytics.d.ts +2 -82
  177. package/dist-types/vendors/analytics/segment.d.ts +2 -164
  178. package/dist-types/vendors/analytics/umami-analytics.d.ts +2 -93
  179. package/dist-types/vendors/analytics/vercel-analytics.d.ts +2 -66
  180. package/dist-types/vendors/email-and-sms/klaviyo.d.ts +2 -0
  181. package/dist-types/vendors/functional/crisp.d.ts +2 -78
  182. package/dist-types/vendors/functional/front-chat.d.ts +2 -62
  183. package/dist-types/vendors/functional/intercom.d.ts +2 -135
  184. package/dist-types/vendors/tag-managers/cloudflare-zaraz.d.ts +2 -39
  185. package/dist-types/vendors/tag-managers/google-tag-manager.d.ts +2 -96
  186. package/dist-types/vercel-analytics.d.ts +2 -0
  187. package/dist-types/x-pixel.d.ts +2 -0
  188. package/docs/README.md +129 -63
  189. package/docs/assets/v3/bottom-bar.png +0 -0
  190. package/docs/assets/v3/brand-card.png +0 -0
  191. package/docs/assets/v3/brand-preferences.png +0 -0
  192. package/docs/assets/v3/choice-wall.png +0 -0
  193. package/docs/assets/v3/headless-bar-html.png +0 -0
  194. package/docs/assets/v3/headless-bar-mobile.png +0 -0
  195. package/docs/assets/v3/headless-bar.png +0 -0
  196. package/docs/assets/v3/slim-bar.png +0 -0
  197. package/docs/concepts/choose-your-setup.md +87 -0
  198. package/docs/concepts/consent-categories.md +84 -0
  199. package/docs/{guides → concepts}/consent-state.md +89 -105
  200. package/docs/{guides → concepts}/data-fetching.md +31 -27
  201. package/docs/concepts/how-consent-works.md +123 -0
  202. package/docs/concepts/policies.md +71 -0
  203. package/docs/customization/class-names.md +202 -0
  204. package/docs/customization/dark-mode.md +157 -0
  205. package/docs/customization/motion.md +119 -0
  206. package/docs/customization/overview.md +67 -34
  207. package/docs/customization/recipes.md +839 -49
  208. package/docs/customization/slots.md +216 -35
  209. package/docs/customization/stylesheets.md +147 -0
  210. package/docs/customization/tailwind.md +842 -0
  211. package/docs/customization/tokens.md +163 -96
  212. package/docs/customization/translations.md +60 -3
  213. package/docs/frameworks/astro/embeds.md +160 -0
  214. package/docs/frameworks/astro/network-blocker.md +86 -0
  215. package/docs/frameworks/astro/scripts.md +155 -0
  216. package/docs/frameworks/html/embeds.md +142 -0
  217. package/docs/frameworks/html/network-blocker.md +105 -0
  218. package/docs/frameworks/html/scripts.md +164 -0
  219. package/docs/frameworks/javascript/scripts.md +137 -0
  220. package/docs/frameworks/next/embeds.md +90 -0
  221. package/docs/frameworks/next/network-blocker.md +153 -0
  222. package/docs/frameworks/next/scripts.md +196 -0
  223. package/docs/frameworks/nuxt/embeds.md +81 -0
  224. package/docs/frameworks/nuxt/network-blocker.md +97 -0
  225. package/docs/frameworks/nuxt/scripts.md +89 -0
  226. package/docs/frameworks/react/embeds.md +89 -0
  227. package/docs/frameworks/react/network-blocker.md +140 -0
  228. package/docs/frameworks/react/scripts.md +115 -0
  229. package/docs/frameworks/svelte/embeds.md +96 -0
  230. package/docs/frameworks/svelte/network-blocker.md +141 -0
  231. package/docs/frameworks/svelte/scripts.md +144 -0
  232. package/docs/frameworks/sveltekit/embeds.md +103 -0
  233. package/docs/frameworks/sveltekit/network-blocker.md +159 -0
  234. package/docs/frameworks/sveltekit/scripts.md +172 -0
  235. package/docs/frameworks/tanstack-start/embeds.md +96 -0
  236. package/docs/frameworks/tanstack-start/network-blocker.md +145 -0
  237. package/docs/frameworks/tanstack-start/scripts.md +103 -0
  238. package/docs/frameworks/vue/embeds.md +84 -0
  239. package/docs/frameworks/vue/network-blocker.md +99 -0
  240. package/docs/frameworks/vue/scripts.md +93 -0
  241. package/docs/guides/banner-experiments.md +654 -0
  242. package/docs/guides/troubleshooting.md +120 -47
  243. package/docs/guides/verify-consent.md +81 -49
  244. package/docs/integrations/adobe-analytics.md +167 -159
  245. package/docs/integrations/ahrefs-analytics.md +152 -154
  246. package/docs/integrations/amplitude.md +162 -156
  247. package/docs/integrations/building-integrations.md +136 -37
  248. package/docs/integrations/clearbit.md +154 -154
  249. package/docs/integrations/cloudflare-web-analytics.md +156 -156
  250. package/docs/integrations/cloudflare-zaraz.md +209 -261
  251. package/docs/integrations/crisp.md +164 -158
  252. package/docs/integrations/databuddy.md +157 -173
  253. package/docs/integrations/fathom-analytics.md +158 -156
  254. package/docs/integrations/front-chat.md +167 -167
  255. package/docs/integrations/google-maps.md +118 -83
  256. package/docs/integrations/google-tag-manager.md +178 -163
  257. package/docs/integrations/google-tag.md +163 -160
  258. package/docs/integrations/heap.md +163 -155
  259. package/docs/integrations/hightouch.md +161 -157
  260. package/docs/integrations/hotjar.md +159 -155
  261. package/docs/integrations/intercom.md +183 -153
  262. package/docs/integrations/klaviyo.md +486 -0
  263. package/docs/integrations/linkedin-insights.md +174 -150
  264. package/docs/integrations/logrocket.md +160 -156
  265. package/docs/integrations/matomo-analytics.md +188 -178
  266. package/docs/integrations/meta-pixel.md +188 -150
  267. package/docs/integrations/microsoft-clarity.md +163 -155
  268. package/docs/integrations/microsoft-uet.md +148 -154
  269. package/docs/integrations/mixpanel-analytics.md +155 -160
  270. package/docs/integrations/one-dollar-stats.md +166 -165
  271. package/docs/integrations/openai-pixel.md +204 -301
  272. package/docs/integrations/overview.md +137 -80
  273. package/docs/integrations/pinterest-tag.md +191 -183
  274. package/docs/integrations/pirsch.md +169 -159
  275. package/docs/integrations/plausible-analytics.md +172 -158
  276. package/docs/integrations/posthog.md +313 -240
  277. package/docs/integrations/promptwatch.md +154 -154
  278. package/docs/integrations/reddit-pixel.md +185 -157
  279. package/docs/integrations/rudderstack.md +201 -186
  280. package/docs/integrations/rybbit-analytics.md +171 -160
  281. package/docs/integrations/segment.md +182 -154
  282. package/docs/integrations/snapchat-pixel.md +186 -156
  283. package/docs/integrations/tiktok-pixel.md +171 -150
  284. package/docs/integrations/umami-analytics.md +163 -158
  285. package/docs/integrations/vercel-analytics.md +166 -157
  286. package/docs/integrations/x-pixel.md +176 -150
  287. package/docs/integrations/youtube.md +121 -86
  288. package/docs/upgrade-v3.md +496 -467
  289. package/package.json +10 -257
  290. package/dist-types/__tests__/helpers.d.ts +0 -141
  291. package/docs/assets/v3/brand-bar.png +0 -0
  292. package/docs/assets/v3/mobile-card.png +0 -0
  293. package/docs/assets/v3/preferences.png +0 -0
  294. package/docs/frameworks/javascript/script-loader.md +0 -100
  295. package/docs/frameworks/next/script-loader.md +0 -216
  296. package/docs/frameworks/react/script-loader.md +0 -69
  297. package/docs/guides/deployment-modes.md +0 -75
  298. package/docs/guides/shared-consent-controls.md +0 -158
  299. package/docs/integrations/clear-on-revocation.md +0 -167
  300. package/docs/integrations/granular-consent.md +0 -210
@@ -0,0 +1,141 @@
1
+ ---
2
+ title: Network blocker
3
+ description: Hold fetch and XMLHttpRequest calls to tracking domains in a Svelte
4
+ app until their consent category is allowed, with the provider's
5
+ networkBlocker option.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## When to use the network blocker
10
+
11
+ The network blocker stops `fetch` and `XMLHttpRequest` calls to domains you
12
+ list until their consent category is allowed. Use it as a backstop for
13
+ tracking calls from code that is already on the page, such as your own
14
+ analytics wrapper or an SDK you import. It is off until you configure it.
15
+
16
+ Load vendor SDKs through the provider's `scripts` prop first; see
17
+ [scripts](./scripts.md). A script that never loads sends nothing, which is
18
+ stronger than blocking its requests one by one.
19
+
20
+ ## Configure the rules
21
+
22
+ Write the configuration in its own module:
23
+
24
+ ```ts title="src/lib/network-blocker.ts"
25
+ import type { UseNetworkBlockerOptions } from '@c15t/svelte';
26
+
27
+ // Pass as `networkBlocker={networkBlocker}` on ConsentManagerProvider.
28
+ export const networkBlocker: UseNetworkBlockerOptions = {
29
+ onRequestBlocked: ({ method, url }) => {
30
+ console.info('Blocked until consent', method, url);
31
+ },
32
+ rules: [
33
+ {
34
+ category: 'measurement',
35
+ domain: 'google-analytics.com',
36
+ id: 'google-analytics',
37
+ },
38
+ {
39
+ category: 'marketing',
40
+ domain: 'connect.facebook.net',
41
+ id: 'meta-pixel',
42
+ pathIncludes: '/signals',
43
+ },
44
+ ],
45
+ };
46
+ ```
47
+
48
+ Pass it to the provider:
49
+
50
+ ```svelte
51
+ <ConsentManagerProvider {mode} {scripts} {networkBlocker}>
52
+ ```
53
+
54
+ The provider reads `networkBlocker` once, when it is created. Remount the
55
+ provider to change the rules.
56
+
57
+ ## Match requests with rules
58
+
59
+ Each rule names a `domain` and the consent `category` a request needs. The
60
+ domain also matches its subdomains: `google-analytics.com` covers
61
+ `www.google-analytics.com`. `pathIncludes` narrows the rule to paths that
62
+ contain a substring, and `methods` narrows it to HTTP methods. A request is
63
+ blocked when a matching rule's condition is not met by the visitor's
64
+ effective permissions.
65
+
66
+ `category` takes the same conditions as scripts:
67
+
68
+ ```ts
69
+ { category: 'measurement' }
70
+ { category: { and: ['measurement', 'marketing'] } }
71
+ { category: { or: ['measurement', 'marketing'] } }
72
+ ```
73
+
74
+ Add `vendor` to also block the request while the visitor has turned that
75
+ vendor off. IAB TCF rules use `vendorId` and the `iab*` purpose fields
76
+ instead.
77
+
78
+ | Option | Default | Purpose |
79
+ | -------------------- | -------- | ------------------------------------------------------------ |
80
+ | `rules` | required | Rules described above |
81
+ | `enabled` | `true` | Set `false` to keep the rules but stop blocking |
82
+ | `logBlockedRequests` | `true` | Log each blocked request with `console.warn` |
83
+ | `onRequestBlocked` | none | Called with `{ method, url, rule }` for each blocked request |
84
+
85
+ ## What a blocked request looks like
86
+
87
+ A blocked `fetch` resolves to a response with status `451` and the status
88
+ text `Request blocked by consent`, and nothing is sent. A blocked
89
+ `XMLHttpRequest` is aborted and fires an `error` event. Requests that match no
90
+ rule are not delayed. With `logBlockedRequests` on, the default, each blocked
91
+ request is logged with `console.warn`; `onRequestBlocked` receives
92
+ `{ method, url, rule }`.
93
+
94
+ ## When blocking starts
95
+
96
+ The provider starts holding matching requests when it is created in the
97
+ browser, before its children run their own code. The blocker module loads
98
+ when the provider mounts and then decides each held request:
99
+
100
+ * While the policy is still loading, a matching request waits instead of
101
+ failing. When the policy arrives, with the visitor's stored choice applied,
102
+ the request is sent if its category is allowed and blocked otherwise.
103
+ * If the policy fails to load, optional categories stay denied and the
104
+ waiting requests are blocked.
105
+ * A synchronous XHR cannot wait. Before the blocker module loads, a matching
106
+ one throws a `NetworkError` from `send()`.
107
+
108
+ When the visitor allows a category later, new requests to its domains go
109
+ through. Requests blocked earlier are not replayed.
110
+
111
+ ## What it cannot stop
112
+
113
+ The blocker sees only `fetch` and `XMLHttpRequest` calls made after the
114
+ provider is created in the browser. It cannot stop:
115
+
116
+ * Scripts that run before the provider, such as tags in `index.html` or
117
+ `app.html` and code at the top level of modules that load first.
118
+ * Code that kept its own reference to `fetch` or `XMLHttpRequest` from before
119
+ the provider was created.
120
+ * `navigator.sendBeacon`, `WebSocket`, `EventSource`, and requests made by
121
+ `<img>`, `<script>` and `<iframe>` elements, web workers and service
122
+ workers.
123
+ * Requests your server makes, such as from a SvelteKit `load` or endpoint.
124
+
125
+ Keep tracking calls out of that window. Send them from event handlers or
126
+ effects, not at a module's top level, and check
127
+ `getConsentManager().has('measurement')` before you call a vendor from your
128
+ own code.
129
+
130
+ ## Verify the blocker
131
+
132
+ Open DevTools, clear site data for your origin and reload:
133
+
134
+ 1. Before you choose, requests matching a rule do not reach the network, and
135
+ the console logs each blocked request.
136
+ 2. Allow the rule's category. New matching requests appear in the Network
137
+ panel.
138
+ 3. Reject, reload and confirm they stay blocked.
139
+
140
+ `fetch('https://www.google-analytics.com/g/collect')` in the console returns a
141
+ response with status `451` while measurement is denied.
@@ -0,0 +1,144 @@
1
+ ---
2
+ title: Scripts
3
+ description: Load vendor scripts, iframes and network requests in a Svelte app
4
+ only after the visitor allows their consent category, and stop them when
5
+ consent is withdrawn.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## Register scripts on the provider
10
+
11
+ Pass the array from `src/scripts.ts` to `ConsentManagerProvider` as the
12
+ `scripts` prop. The [quickstart](https://c15t.com/docs/frameworks/svelte/quickstart) sets up
13
+ this file:
14
+
15
+ ```ts title="src/scripts.ts"
16
+ import { posthog } from '@c15t/integrations/posthog';
17
+ import { xPixel } from '@c15t/integrations/x-pixel';
18
+
19
+ export const scripts = [
20
+ posthog({
21
+ id: 'phc_your_project_key',
22
+ initOptions: { cookieless_mode: 'never' },
23
+ loadMode: 'after-consent',
24
+ }),
25
+ xPixel({ pixelId: 'your-pixel-id' }),
26
+ ];
27
+ ```
28
+
29
+ Replace the placeholder PostHog project key and X Pixel ID with your own.
30
+
31
+ The provider loads the script loader as a separate chunk, shared with the
32
+ [network blocker](./network-blocker.md), and only when `scripts` is not empty or
33
+ the blocker has rules, so an app with neither never downloads it. A
34
+ single-page app has no server render to link the chunk from, so the browser
35
+ requests it after your app's JavaScript has run. A SvelteKit app can preload
36
+ it from the server-rendered page; the SvelteKit scripts guide shows how.
37
+
38
+ ## How registered scripts load
39
+
40
+ The provider's `scripts` prop takes an array of script configurations. Each
41
+ has a category. The loader adds a script to the page when its category becomes
42
+ allowed and removes it when the category is withdrawn. Nothing optional loads
43
+ while the policy is still resolving, or when it fails.
44
+
45
+ Helpers in `@c15t/integrations`, such as `posthog()` from `@c15t/integrations/posthog`,
46
+ return a configuration with the right category and the vendor's own consent
47
+ calls. [Integrations](../../integrations/overview.md) lists every helper. For an
48
+ SDK without a helper, write a configuration with an `id`, `category` and `src`
49
+ as shown in [building integrations](../../integrations/building-integrations.md).
50
+
51
+ Remove the vendor's original `<script>` tag, `app.html` snippet or SDK import
52
+ before you register it. A banner does not block code that loads some other
53
+ way, and a vendor loaded twice sends events twice.
54
+
55
+ The `scripts` array is read when the provider is created. Build it once, at
56
+ the top level of the component, not inside an effect.
57
+
58
+ ## Script options
59
+
60
+ Helpers set these for you. For a script without a helper, write the object
61
+ yourself:
62
+
63
+ | Option | Default | Behavior |
64
+ | ------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
65
+ | `id` | required | A unique name. The loader uses it to add and remove the script once. |
66
+ | `category` | required | The category or condition, such as `'measurement'` or `{ and: ['measurement', 'marketing'] }`, that must be allowed. |
67
+ | `src` or `textContent` | none | The script's URL, or inline code. |
68
+ | `callbackOnly` | `false` | Adds no `<script>` element and only runs the callbacks. Use it to switch an SDK you load yourself on and off. |
69
+ | `alwaysLoad` | `false` | Loads at once, whatever the consent state. Only for scripts that apply consent themselves, such as a tag manager with Consent Mode. |
70
+ | `persistAfterConsentRevoked` | `false` | Keeps the element after withdrawal instead of removing it. |
71
+ | `target` | `'head'` | Where the element goes: `'head'` or `'body'`. |
72
+ | `async`, `defer`, `fetchPriority`, `attributes`, `nonce` | none | Set on the `<script>` element. |
73
+ | `anonymizeId` | `true` | Gives the element a random `id`, so ad blockers do not match it by name. |
74
+ | `vendor` | none | Also waits for this vendor to be allowed, for vendor-level consent outside IAB. |
75
+ | `onBeforeLoad`, `onLoad`, `onError`, `onConsentChange`, `onDispose` | none | Lifecycle callbacks. See [callbacks](https://c15t.com/docs/frameworks/svelte/callbacks#script-callbacks). |
76
+
77
+ ## Gate embeds
78
+
79
+ Iframes are not scripts. Wrap them in `ConsentGate`, which keeps the iframe
80
+ out of the DOM until its category is allowed:
81
+
82
+ ```svelte title="src/YouTubeEmbed.svelte"
83
+ <script lang="ts">
84
+ import { ConsentDialogLink, ConsentGate } from '@c15t/svelte';
85
+ </script>
86
+
87
+ <!-- The iframe mounts only while measurement is allowed. -->
88
+ <ConsentGate category="measurement">
89
+ {#snippet placeholder()}<div class="placeholder">
90
+ <p>Allow measurement to load this YouTube video.</p>
91
+ <ConsentDialogLink>Choose video permissions</ConsentDialogLink>
92
+ </div>{/snippet}
93
+ <iframe
94
+ title="YouTube video"
95
+ src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y?playsinline=1"
96
+ allow="encrypted-media; picture-in-picture"
97
+ allowfullscreen
98
+ ></iframe>
99
+ </ConsentGate>
100
+ ```
101
+
102
+ For iframes from a CMS or Markdown, which you cannot wrap, use the iframe
103
+ blocker's `data-category` and `data-src` attributes. [Embeds](./embeds.md) covers
104
+ both.
105
+
106
+ ## Block requests from code already on the page
107
+
108
+ The provider's `networkBlocker` option holds `fetch` and `XMLHttpRequest`
109
+ calls to the domains you list until their category is allowed. It is a
110
+ backstop for code you cannot move into `scripts`. [Network blocker](./network-blocker.md)
111
+ covers the rules and what it cannot stop.
112
+
113
+ ## What happens when consent is withdrawn
114
+
115
+ Removing a script tag cannot stop code that already ran. So when a save
116
+ withdraws a category that was granted, the provider reloads the page after the
117
+ save request, and the new page starts with only the permitted code. Set
118
+ `reloadOnConsentRevoked: false` on the provider if you handle withdrawal
119
+ yourself, for example through a vendor's own opt-out call in
120
+ `onConsentChange`.
121
+
122
+ `ConsentGate` content unmounts without a reload. To delete first-party cookies
123
+ a vendor set, configure `clearOnRevocation`; see
124
+ [clearing data on revocation](https://c15t.com/docs/frameworks/svelte/clear-on-revocation).
125
+
126
+ ## Let visitors turn off one vendor
127
+
128
+ A visitor can allow marketing and still switch off one vendor in it. Pass
129
+ `vendors` to the provider; helpers from `@c15t/integrations` already carry their
130
+ vendor slug. See [vendor consent](https://c15t.com/docs/frameworks/svelte/vendor-consent).
131
+
132
+ ## Verify vendor loading
133
+
134
+ Open DevTools, clear site data for your origin and reload:
135
+
136
+ 1. With the banner showing, the Network panel has no requests to your vendors,
137
+ and gated iframes are absent from the Elements panel.
138
+ 2. Allow one category in preferences. Only that category's vendors load, and
139
+ its iframes appear.
140
+ 3. Reject, reload, and confirm the vendor requests stay absent.
141
+ 4. Withdraw a category you allowed. The page reloads and its vendors no longer
142
+ load.
143
+
144
+ [Verify consent](../../guides/verify-consent.md) covers automated checks.
@@ -0,0 +1,103 @@
1
+ ---
2
+ title: Embeds
3
+ description: Keep YouTube videos, maps and other iframes out of SvelteKit server
4
+ HTML and the browser until their consent category is allowed.
5
+ group: frameworks
6
+ ---
7
+
8
+ ## Pick a way to gate the embed
9
+
10
+ A YouTube video, a map or a social post in an iframe contacts its vendor as
11
+ soon as it loads. c15t offers two ways to keep it from loading before
12
+ consent:
13
+
14
+ | Your markup | Use | Placeholder |
15
+ | -------------------------------------------------------- | ------------------------------------------------------ | ----------------------------- |
16
+ | A Svelte component you write | `ConsentGate` around the iframe | Built in, or your own snippet |
17
+ | HTML you do not control, such as CMS or Markdown content | The iframe blocker with `data-category` and `data-src` | None; the iframe stays empty |
18
+
19
+ Both use the visitor's effective permission for one category, and both
20
+ remove the embed again when the visitor withdraws that category.
21
+
22
+ ## Wrap the iframe in ConsentGate
23
+
24
+ ```svelte title="src/YouTubeEmbed.svelte"
25
+ <script lang="ts">
26
+ import { ConsentDialogLink, ConsentGate } from '@c15t/svelte';
27
+ </script>
28
+
29
+ <!-- The iframe mounts only while measurement is allowed. -->
30
+ <ConsentGate category="measurement">
31
+ {#snippet placeholder()}<div class="placeholder">
32
+ <p>Allow measurement to load this YouTube video.</p>
33
+ <ConsentDialogLink>Choose video permissions</ConsentDialogLink>
34
+ </div>{/snippet}
35
+ <iframe
36
+ title="YouTube video"
37
+ src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y?playsinline=1"
38
+ allow="encrypted-media; picture-in-picture"
39
+ allowfullscreen
40
+ ></iframe>
41
+ </ConsentGate>
42
+ ```
43
+
44
+ The iframe is absent from the DOM until measurement is allowed, so the
45
+ browser never requests it. The placeholder snippet replaces the built-in one
46
+ and includes a `ConsentDialogLink`, so a visitor can allow the category from
47
+ the embed's own spot. Keep `ConsentDialog` mounted for that link.
48
+ [ConsentGate](https://c15t.com/docs/frameworks/sveltekit/components/consent-gate) lists its props.
49
+
50
+ Pick the category the vendor's embed needs. YouTube and maps usually fit
51
+ `measurement` or `marketing`; check what each vendor sets.
52
+
53
+ ## Gate iframes you do not render
54
+
55
+ The provider's iframe blocker is on by default. It watches the page for
56
+ iframes with a `data-category` attribute:
57
+
58
+ ```html
59
+ <iframe
60
+ title="Store locations"
61
+ data-category="functionality"
62
+ data-src="https://www.google.com/maps/embed?pb=..."
63
+ ></iframe>
64
+ ```
65
+
66
+ * While the category is denied, the iframe has no `src`, so it loads nothing.
67
+ * When the category is allowed, the blocker copies `data-src` to `src`.
68
+ Only `http` and `https` URLs are used.
69
+ * When the category is withdrawn, the blocker removes `src` again.
70
+
71
+ Iframes without `data-category` or `data-vendor` are never touched. Put
72
+ `data-src` in the HTML instead of `src`; an iframe that arrives with `src`
73
+ already starts loading before the blocker sees it. Add `data-vendor` with a
74
+ vendor slug to also keep the iframe empty while the visitor has that vendor
75
+ turned off.
76
+
77
+ The blocker has no placeholder. Style the empty iframe, or place a note and a
78
+ `ConsentDialogLink` next to it.
79
+
80
+ Pass `iframeBlocker={false}` on the provider to turn the blocker off.
81
+
82
+ ## Vendor guides
83
+
84
+ The [YouTube](../../integrations/youtube.md) and
85
+ [Google Maps](../../integrations/google-maps.md) guides have ready embed
86
+ configurations. [Integrations](../../integrations/overview.md) lists the rest.
87
+
88
+ ## Verify the embeds
89
+
90
+ Clear site data and reload with DevTools open:
91
+
92
+ 1. The Network panel has no request to the embed's host, and the Elements
93
+ panel shows the placeholder or an iframe without `src`.
94
+ 2. Allow the category in preferences. The embed loads without a page reload.
95
+ 3. Withdraw the category. The embed disappears, or its iframe loses `src`.
96
+
97
+ ## Embeds in server HTML
98
+
99
+ `ConsentGate` renders an empty wrapper on the server, so a gated embed is
100
+ never in the server HTML, whatever the visitor chose. It appears in the
101
+ browser after hydration. An iframe with `data-category` and `data-src` is in
102
+ the server HTML without `src`, so it loads nothing until the iframe blocker
103
+ starts in the browser and finds its category allowed.
@@ -0,0 +1,159 @@
1
+ ---
2
+ title: Network blocker
3
+ description: Hold browser fetch and XMLHttpRequest calls to tracking domains in
4
+ a SvelteKit app until their consent category is allowed, with the
5
+ networkBlocker option.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## When to use the network blocker
10
+
11
+ The network blocker stops `fetch` and `XMLHttpRequest` calls to domains you
12
+ list until their consent category is allowed. Use it as a backstop for
13
+ tracking calls from code that is already on the page, such as your own
14
+ analytics wrapper or an SDK you import. It is off until you configure it.
15
+
16
+ Load vendor SDKs through the provider's `scripts` prop first; see
17
+ [scripts](./scripts.md). A script that never loads sends nothing, which is
18
+ stronger than blocking its requests one by one.
19
+
20
+ ## Configure the rules
21
+
22
+ Write the configuration in its own module:
23
+
24
+ ```ts title="src/lib/network-blocker.ts"
25
+ import type { UseNetworkBlockerOptions } from '@c15t/svelte';
26
+
27
+ // Pass as `networkBlocker={networkBlocker}` on ConsentManagerProvider.
28
+ export const networkBlocker: UseNetworkBlockerOptions = {
29
+ onRequestBlocked: ({ method, url }) => {
30
+ console.info('Blocked until consent', method, url);
31
+ },
32
+ rules: [
33
+ {
34
+ category: 'measurement',
35
+ domain: 'google-analytics.com',
36
+ id: 'google-analytics',
37
+ },
38
+ {
39
+ category: 'marketing',
40
+ domain: 'connect.facebook.net',
41
+ id: 'meta-pixel',
42
+ pathIncludes: '/signals',
43
+ },
44
+ ],
45
+ };
46
+ ```
47
+
48
+ Pass it to the provider:
49
+
50
+ ```svelte
51
+ <ConsentManagerProvider {mode} {scripts} {networkBlocker}>
52
+ ```
53
+
54
+ The provider reads `networkBlocker` once, when it is created. Remount the
55
+ provider to change the rules.
56
+
57
+ ## Match requests with rules
58
+
59
+ Each rule names a `domain` and the consent `category` a request needs. The
60
+ domain also matches its subdomains: `google-analytics.com` covers
61
+ `www.google-analytics.com`. `pathIncludes` narrows the rule to paths that
62
+ contain a substring, and `methods` narrows it to HTTP methods. A request is
63
+ blocked when a matching rule's condition is not met by the visitor's
64
+ effective permissions.
65
+
66
+ `category` takes the same conditions as scripts:
67
+
68
+ ```ts
69
+ { category: 'measurement' }
70
+ { category: { and: ['measurement', 'marketing'] } }
71
+ { category: { or: ['measurement', 'marketing'] } }
72
+ ```
73
+
74
+ Add `vendor` to also block the request while the visitor has turned that
75
+ vendor off. IAB TCF rules use `vendorId` and the `iab*` purpose fields
76
+ instead.
77
+
78
+ | Option | Default | Purpose |
79
+ | -------------------- | -------- | ------------------------------------------------------------ |
80
+ | `rules` | required | Rules described above |
81
+ | `enabled` | `true` | Set `false` to keep the rules but stop blocking |
82
+ | `logBlockedRequests` | `true` | Log each blocked request with `console.warn` |
83
+ | `onRequestBlocked` | none | Called with `{ method, url, rule }` for each blocked request |
84
+
85
+ ## What a blocked request looks like
86
+
87
+ A blocked `fetch` resolves to a response with status `451` and the status
88
+ text `Request blocked by consent`, and nothing is sent. A blocked
89
+ `XMLHttpRequest` is aborted and fires an `error` event. Requests that match no
90
+ rule are not delayed. With `logBlockedRequests` on, the default, each blocked
91
+ request is logged with `console.warn`; `onRequestBlocked` receives
92
+ `{ method, url, rule }`.
93
+
94
+ ## When blocking starts
95
+
96
+ The provider starts holding matching requests when it is created in the
97
+ browser, before its children run their own code. The blocker module loads
98
+ when the provider mounts and then decides each held request:
99
+
100
+ * While the policy is still loading, a matching request waits instead of
101
+ failing. When the policy arrives, with the visitor's stored choice applied,
102
+ the request is sent if its category is allowed and blocked otherwise.
103
+ * If the policy fails to load, optional categories stay denied and the
104
+ waiting requests are blocked.
105
+ * A synchronous XHR cannot wait. Before the blocker module loads, a matching
106
+ one throws a `NetworkError` from `send()`.
107
+
108
+ When the visitor allows a category later, new requests to its domains go
109
+ through. Requests blocked earlier are not replayed.
110
+
111
+ ## What it cannot stop
112
+
113
+ The blocker sees only `fetch` and `XMLHttpRequest` calls made after the
114
+ provider is created in the browser. It cannot stop:
115
+
116
+ * Scripts that run before the provider, such as tags in `index.html` or
117
+ `app.html` and code at the top level of modules that load first.
118
+ * Code that kept its own reference to `fetch` or `XMLHttpRequest` from before
119
+ the provider was created.
120
+ * `navigator.sendBeacon`, `WebSocket`, `EventSource`, and requests made by
121
+ `<img>`, `<script>` and `<iframe>` elements, web workers and service
122
+ workers.
123
+ * Requests your server makes, such as from a SvelteKit `load` or endpoint.
124
+
125
+ Keep tracking calls out of that window. Send them from event handlers or
126
+ effects, not at a module's top level, and check
127
+ `getConsentManager().has('measurement')` before you call a vendor from your
128
+ own code.
129
+
130
+ ## Verify the blocker
131
+
132
+ Open DevTools, clear site data for your origin and reload:
133
+
134
+ 1. Before you choose, requests matching a rule do not reach the network, and
135
+ the console logs each blocked request.
136
+ 2. Allow the rule's category. New matching requests appear in the Network
137
+ panel.
138
+ 3. Reject, reload and confirm they stay blocked.
139
+
140
+ `fetch('https://www.google-analytics.com/g/collect')` in the console returns a
141
+ response with status `451` while measurement is denied.
142
+
143
+ ## Preload the blocker
144
+
145
+ The blocker shares a separate chunk with the script loader. The chunk loads
146
+ only on pages with `networkBlocker` rules or `scripts`, and matching requests
147
+ wait until it has loaded. Add the `c15tPreload()` Vite plugin so `c15tHandle`
148
+ links the chunk from the page's `<head>` and the browser fetches it with the
149
+ app's own code.
150
+ [Preload the script loader](./scripts.md#preload-the-script-loader) shows the
151
+ setup.
152
+
153
+ ## Server requests are not blocked
154
+
155
+ The network blocker runs in the browser. A `fetch` in a SvelteKit `load`,
156
+ `+server.ts` or `hooks.server.ts` is never blocked, even when the visitor has
157
+ denied the category. If server code sends data to a vendor, check the
158
+ visitor's stored choice yourself before it does; `event.locals.c15t.config`
159
+ from [`c15tHandle`](https://c15t.com/docs/frameworks/sveltekit/server-api#c15thandle) holds it.