@c15t/backend 2.0.0-rc.0 → 2.0.0-rc.10

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 (336) hide show
  1. package/README.md +3 -3
  2. package/dist/302.js +473 -0
  3. package/dist/583.js +540 -0
  4. package/dist/915.js +1771 -0
  5. package/dist/cache.cjs +5 -5
  6. package/dist/cache.js +4 -415
  7. package/dist/core.cjs +1356 -120
  8. package/dist/core.js +163 -1981
  9. package/dist/db/adapters/drizzle.cjs +1 -1
  10. package/dist/db/adapters/drizzle.js +1 -2
  11. package/dist/db/adapters/kysely.cjs +1 -1
  12. package/dist/db/adapters/kysely.js +1 -2
  13. package/dist/db/adapters/mongo.cjs +1 -1
  14. package/dist/db/adapters/mongo.js +1 -2
  15. package/dist/db/adapters/prisma.cjs +1 -1
  16. package/dist/db/adapters/prisma.js +1 -2
  17. package/dist/db/adapters/typeorm.cjs +1 -1
  18. package/dist/db/adapters/typeorm.js +1 -2
  19. package/dist/db/adapters.cjs +1 -1
  20. package/dist/db/migrator.cjs +1 -1
  21. package/dist/db/schema.cjs +43 -3
  22. package/dist/db/schema.js +35 -4
  23. package/dist/define-config.cjs +1 -1
  24. package/dist/edge.cjs +1106 -0
  25. package/dist/edge.js +190 -0
  26. package/dist/router.cjs +885 -123
  27. package/dist/router.js +1 -1507
  28. package/dist/{types.cjs → types/index.cjs} +1 -1
  29. package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
  30. package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
  31. package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
  32. package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
  33. package/{dist → dist-types}/cache/gvl-resolver.d.ts +0 -1
  34. package/{dist → dist-types}/cache/index.d.ts +0 -1
  35. package/{dist → dist-types}/cache/keys.d.ts +0 -1
  36. package/{dist → dist-types}/cache/types.d.ts +0 -1
  37. package/{dist → dist-types}/core.d.ts +8 -1
  38. package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
  39. package/dist-types/db/registry/consent-policy.d.ts +78 -0
  40. package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
  41. package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
  42. package/dist-types/db/registry/index.d.ts +118 -0
  43. package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
  44. package/{dist → dist-types}/db/registry/subject.d.ts +0 -2
  45. package/{dist → dist-types}/db/registry/types.d.ts +1 -1
  46. package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
  47. package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
  48. package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
  49. package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
  50. package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
  51. package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
  52. package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +1 -2
  53. package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
  54. package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -32
  55. package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -2
  56. package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +1 -2
  57. package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +3 -3
  58. package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +1 -2
  59. package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +7 -2
  60. package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +1 -2
  61. package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +455 -28
  62. package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
  63. package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +1 -3
  64. package/{dist → dist-types}/db/schema/index.d.ts +908 -86
  65. package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
  66. package/dist-types/define-config.d.ts +17 -0
  67. package/dist-types/edge/index.d.ts +5 -0
  68. package/dist-types/edge/init-handler.d.ts +40 -0
  69. package/dist-types/edge/resolve-consent.d.ts +80 -0
  70. package/dist-types/edge/types.d.ts +13 -0
  71. package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
  72. package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
  73. package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
  74. package/{dist → dist-types}/handlers/init/index.d.ts +2 -3
  75. package/dist-types/handlers/init/policy.d.ts +26 -0
  76. package/dist-types/handlers/init/resolve-init.d.ts +44 -0
  77. package/dist-types/handlers/init/translations.d.ts +48 -0
  78. package/dist-types/handlers/legal-document/current.handler.d.ts +11 -0
  79. package/dist-types/handlers/legal-document/snapshot.d.ts +39 -0
  80. package/dist-types/handlers/policy/snapshot.d.ts +99 -0
  81. package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
  82. package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
  83. package/{dist → dist-types}/handlers/subject/get.handler.d.ts +3 -2
  84. package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
  85. package/{dist → dist-types}/handlers/subject/list.handler.d.ts +3 -2
  86. package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -2
  87. package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
  88. package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +3 -1
  89. package/{dist → dist-types}/init.d.ts +4 -7
  90. package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
  91. package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
  92. package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
  93. package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
  94. package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +0 -1
  95. package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
  96. package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
  97. package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
  98. package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
  99. package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
  100. package/dist-types/policies/builder.d.ts +127 -0
  101. package/dist-types/policies/defaults.d.ts +2 -0
  102. package/dist-types/policies/matchers.d.ts +3 -0
  103. package/{dist → dist-types}/router.d.ts +0 -1
  104. package/{dist → dist-types}/routes/consent.d.ts +0 -1
  105. package/{dist → dist-types}/routes/index.d.ts +1 -1
  106. package/{dist → dist-types}/routes/init.d.ts +0 -1
  107. package/dist-types/routes/legal-document.d.ts +7 -0
  108. package/{dist → dist-types}/routes/status.d.ts +0 -1
  109. package/{dist → dist-types}/routes/subject.d.ts +0 -1
  110. package/{dist → dist-types}/types/api.d.ts +0 -1
  111. package/dist-types/types/index.d.ts +464 -0
  112. package/dist-types/utils/background.d.ts +6 -0
  113. package/{dist → dist-types}/utils/create-telemetry-options.d.ts +1 -2
  114. package/{dist → dist-types}/utils/env.d.ts +0 -1
  115. package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
  116. package/{dist → dist-types}/utils/instrumentation.d.ts +2 -3
  117. package/{dist → dist-types}/utils/logger.d.ts +0 -1
  118. package/{dist → dist-types}/utils/metrics.d.ts +0 -1
  119. package/dist-types/version.d.ts +1 -0
  120. package/docs/README.md +49 -0
  121. package/docs/api/configuration.md +208 -0
  122. package/docs/api/endpoints.md +211 -0
  123. package/docs/guides/caching.md +85 -0
  124. package/docs/guides/database-setup.md +128 -0
  125. package/docs/guides/edge-deployment.md +251 -0
  126. package/docs/guides/framework-integration.md +142 -0
  127. package/docs/guides/iab-tcf.md +89 -0
  128. package/docs/guides/observability.md +96 -0
  129. package/docs/guides/policy-packs.md +396 -0
  130. package/docs/quickstart.md +129 -0
  131. package/package.json +53 -39
  132. package/.turbo/turbo-build.log +0 -49
  133. package/CHANGELOG.md +0 -89
  134. package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
  135. package/dist/cache/adapters/index.d.ts.map +0 -1
  136. package/dist/cache/adapters/memory.d.ts.map +0 -1
  137. package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
  138. package/dist/cache/gvl-resolver.d.ts.map +0 -1
  139. package/dist/cache/index.d.ts.map +0 -1
  140. package/dist/cache/keys.d.ts.map +0 -1
  141. package/dist/cache/types.d.ts.map +0 -1
  142. package/dist/core.d.ts.map +0 -1
  143. package/dist/db/adapters/drizzle.d.ts +0 -2
  144. package/dist/db/adapters/drizzle.d.ts.map +0 -1
  145. package/dist/db/adapters/index.d.ts +0 -2
  146. package/dist/db/adapters/index.d.ts.map +0 -1
  147. package/dist/db/adapters/kysely.d.ts +0 -2
  148. package/dist/db/adapters/kysely.d.ts.map +0 -1
  149. package/dist/db/adapters/mongo.d.ts +0 -2
  150. package/dist/db/adapters/mongo.d.ts.map +0 -1
  151. package/dist/db/adapters/prisma.d.ts +0 -2
  152. package/dist/db/adapters/prisma.d.ts.map +0 -1
  153. package/dist/db/adapters/typeorm.d.ts +0 -2
  154. package/dist/db/adapters/typeorm.d.ts.map +0 -1
  155. package/dist/db/migrator/index.d.ts.map +0 -1
  156. package/dist/db/registry/consent-policy.d.ts +0 -23
  157. package/dist/db/registry/consent-policy.d.ts.map +0 -1
  158. package/dist/db/registry/consent-purpose.d.ts.map +0 -1
  159. package/dist/db/registry/domain.d.ts.map +0 -1
  160. package/dist/db/registry/index.d.ts +0 -57
  161. package/dist/db/registry/index.d.ts.map +0 -1
  162. package/dist/db/registry/subject.d.ts.map +0 -1
  163. package/dist/db/registry/types.d.ts.map +0 -1
  164. package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
  165. package/dist/db/registry/utils.d.ts.map +0 -1
  166. package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
  167. package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
  168. package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
  169. package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
  170. package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
  171. package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
  172. package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
  173. package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
  174. package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
  175. package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
  176. package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
  177. package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
  178. package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
  179. package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
  180. package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
  181. package/dist/db/schema/index.d.ts.map +0 -1
  182. package/dist/db/tenant-scope.d.ts.map +0 -1
  183. package/dist/define-config.d.ts +0 -5
  184. package/dist/define-config.d.ts.map +0 -1
  185. package/dist/handlers/consent/check.handler.d.ts.map +0 -1
  186. package/dist/handlers/consent/index.d.ts +0 -12
  187. package/dist/handlers/consent/index.d.ts.map +0 -1
  188. package/dist/handlers/init/geo.d.ts.map +0 -1
  189. package/dist/handlers/init/index.d.ts.map +0 -1
  190. package/dist/handlers/init/translations.d.ts +0 -28
  191. package/dist/handlers/init/translations.d.ts.map +0 -1
  192. package/dist/handlers/status/index.d.ts +0 -7
  193. package/dist/handlers/status/index.d.ts.map +0 -1
  194. package/dist/handlers/status/status.handler.d.ts.map +0 -1
  195. package/dist/handlers/subject/get.handler.d.ts.map +0 -1
  196. package/dist/handlers/subject/index.d.ts +0 -10
  197. package/dist/handlers/subject/index.d.ts.map +0 -1
  198. package/dist/handlers/subject/list.handler.d.ts.map +0 -1
  199. package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
  200. package/dist/handlers/subject/post.handler.d.ts.map +0 -1
  201. package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
  202. package/dist/init.d.ts.map +0 -1
  203. package/dist/middleware/auth/index.d.ts.map +0 -1
  204. package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
  205. package/dist/middleware/cors/cors.d.ts.map +0 -1
  206. package/dist/middleware/cors/index.d.ts +0 -30
  207. package/dist/middleware/cors/index.d.ts.map +0 -1
  208. package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
  209. package/dist/middleware/cors/process-cors.d.ts.map +0 -1
  210. package/dist/middleware/openapi/config.d.ts.map +0 -1
  211. package/dist/middleware/openapi/handlers.d.ts.map +0 -1
  212. package/dist/middleware/openapi/index.d.ts +0 -12
  213. package/dist/middleware/openapi/index.d.ts.map +0 -1
  214. package/dist/middleware/process-ip/index.d.ts.map +0 -1
  215. package/dist/router.d.ts.map +0 -1
  216. package/dist/routes/consent.d.ts.map +0 -1
  217. package/dist/routes/index.d.ts.map +0 -1
  218. package/dist/routes/init.d.ts.map +0 -1
  219. package/dist/routes/status.d.ts.map +0 -1
  220. package/dist/routes/subject.d.ts.map +0 -1
  221. package/dist/types/api.d.ts.map +0 -1
  222. package/dist/types/index.d.ts +0 -255
  223. package/dist/types/index.d.ts.map +0 -1
  224. package/dist/utils/create-telemetry-options.d.ts.map +0 -1
  225. package/dist/utils/env.d.ts.map +0 -1
  226. package/dist/utils/extract-error-message.d.ts.map +0 -1
  227. package/dist/utils/index.d.ts +0 -4
  228. package/dist/utils/index.d.ts.map +0 -1
  229. package/dist/utils/instrumentation.d.ts.map +0 -1
  230. package/dist/utils/logger.d.ts.map +0 -1
  231. package/dist/utils/metrics.d.ts.map +0 -1
  232. package/dist/version.d.ts +0 -2
  233. package/dist/version.d.ts.map +0 -1
  234. package/knip.json +0 -31
  235. package/rslib.config.ts +0 -93
  236. package/src/cache/adapters/cloudflare-kv.ts +0 -71
  237. package/src/cache/adapters/index.ts +0 -22
  238. package/src/cache/adapters/memory.ts +0 -111
  239. package/src/cache/adapters/upstash-redis.ts +0 -113
  240. package/src/cache/gvl-resolver.ts +0 -289
  241. package/src/cache/index.ts +0 -34
  242. package/src/cache/keys.ts +0 -68
  243. package/src/cache/types.ts +0 -66
  244. package/src/core.ts +0 -368
  245. package/src/db/migrator/index.ts +0 -80
  246. package/src/db/registry/consent-policy.test.ts +0 -451
  247. package/src/db/registry/consent-policy.ts +0 -82
  248. package/src/db/registry/consent-purpose.test.ts +0 -428
  249. package/src/db/registry/consent-purpose.ts +0 -61
  250. package/src/db/registry/domain.test.ts +0 -445
  251. package/src/db/registry/domain.ts +0 -91
  252. package/src/db/registry/index.ts +0 -14
  253. package/src/db/registry/subject.test.ts +0 -388
  254. package/src/db/registry/subject.ts +0 -129
  255. package/src/db/registry/types.ts +0 -10
  256. package/src/db/registry/utils/generate-id.test.ts +0 -216
  257. package/src/db/registry/utils/generate-id.ts +0 -133
  258. package/src/db/registry/utils.ts +0 -133
  259. package/src/db/schema/1.0.0/audit-log.ts +0 -15
  260. package/src/db/schema/1.0.0/consent-policy.ts +0 -14
  261. package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
  262. package/src/db/schema/1.0.0/consent-record.ts +0 -10
  263. package/src/db/schema/1.0.0/consent.ts +0 -20
  264. package/src/db/schema/1.0.0/domain.ts +0 -12
  265. package/src/db/schema/1.0.0/index.ts +0 -48
  266. package/src/db/schema/1.0.0/subject.ts +0 -12
  267. package/src/db/schema/2.0.0/audit-log.ts +0 -18
  268. package/src/db/schema/2.0.0/consent-policy.ts +0 -28
  269. package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
  270. package/src/db/schema/2.0.0/consent.ts +0 -26
  271. package/src/db/schema/2.0.0/domain.ts +0 -12
  272. package/src/db/schema/2.0.0/index.ts +0 -47
  273. package/src/db/schema/2.0.0/subject.ts +0 -14
  274. package/src/db/schema/index.ts +0 -15
  275. package/src/db/tenant-scope.test.ts +0 -750
  276. package/src/db/tenant-scope.ts +0 -103
  277. package/src/define-config.ts +0 -5
  278. package/src/handlers/consent/check.handler.ts +0 -126
  279. package/src/handlers/init/geo.test.ts +0 -317
  280. package/src/handlers/init/geo.ts +0 -195
  281. package/src/handlers/init/index.test.ts +0 -205
  282. package/src/handlers/init/index.ts +0 -114
  283. package/src/handlers/init/translations.test.ts +0 -121
  284. package/src/handlers/init/translations.ts +0 -72
  285. package/src/handlers/status/status.handler.test.ts +0 -155
  286. package/src/handlers/status/status.handler.ts +0 -51
  287. package/src/handlers/subject/get.handler.ts +0 -93
  288. package/src/handlers/subject/list.handler.ts +0 -93
  289. package/src/handlers/subject/patch.handler.ts +0 -122
  290. package/src/handlers/subject/post.handler.test.ts +0 -294
  291. package/src/handlers/subject/post.handler.ts +0 -254
  292. package/src/handlers/utils/consent-enrichment.test.ts +0 -380
  293. package/src/handlers/utils/consent-enrichment.ts +0 -218
  294. package/src/init.test.ts +0 -126
  295. package/src/init.ts +0 -87
  296. package/src/middleware/auth/index.ts +0 -11
  297. package/src/middleware/auth/validate-api-key.test.ts +0 -86
  298. package/src/middleware/auth/validate-api-key.ts +0 -107
  299. package/src/middleware/cors/cors.test.ts +0 -135
  300. package/src/middleware/cors/cors.ts +0 -186
  301. package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
  302. package/src/middleware/cors/is-origin-trusted.ts +0 -130
  303. package/src/middleware/cors/process-cors.ts +0 -91
  304. package/src/middleware/openapi/config.ts +0 -29
  305. package/src/middleware/openapi/handlers.ts +0 -34
  306. package/src/middleware/process-ip/index.test.ts +0 -195
  307. package/src/middleware/process-ip/index.ts +0 -199
  308. package/src/router.ts +0 -15
  309. package/src/routes/consent.ts +0 -52
  310. package/src/routes/index.ts +0 -10
  311. package/src/routes/init.ts +0 -102
  312. package/src/routes/status.ts +0 -46
  313. package/src/routes/subject.ts +0 -152
  314. package/src/types/api.ts +0 -48
  315. package/src/types/index.ts +0 -288
  316. package/src/utils/create-telemetry-options.test.ts +0 -302
  317. package/src/utils/create-telemetry-options.ts +0 -229
  318. package/src/utils/env.ts +0 -84
  319. package/src/utils/extract-error-message.ts +0 -21
  320. package/src/utils/instrumentation.test.ts +0 -185
  321. package/src/utils/instrumentation.ts +0 -196
  322. package/src/utils/logger.ts +0 -41
  323. package/src/utils/metrics.test.ts +0 -323
  324. package/src/utils/metrics.ts +0 -402
  325. package/src/utils/telemetry-pii.test.ts +0 -325
  326. package/src/version.ts +0 -2
  327. package/tsconfig.json +0 -11
  328. package/vitest.config.ts +0 -28
  329. /package/dist/{types.js → types/index.js} +0 -0
  330. /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
  331. /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
  332. /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
  333. /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
  334. /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
  335. /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
  336. /package/{src/utils/index.ts → dist-types/utils/index.d.ts} +0 -0
@@ -1,195 +0,0 @@
1
- import type { C15TOptions } from '~/types';
2
- import type { JurisdictionCode } from '~/types/api';
3
-
4
- /**
5
- * Normalizes a header value to a string or null.
6
- */
7
- function normalizeHeader(
8
- value: string | string[] | null | undefined
9
- ): string | null {
10
- if (!value) {
11
- return null;
12
- }
13
- return Array.isArray(value) ? (value[0] ?? null) : value;
14
- }
15
-
16
- /**
17
- * Gets geo-related headers from the request.
18
- */
19
- function getGeoHeaders(headers: Headers) {
20
- const countryCode =
21
- normalizeHeader(headers.get('x-c15t-country')) ??
22
- normalizeHeader(headers.get('cf-ipcountry')) ??
23
- normalizeHeader(headers.get('x-vercel-ip-country')) ??
24
- normalizeHeader(headers.get('x-amz-cf-ipcountry')) ??
25
- normalizeHeader(headers.get('x-country-code'));
26
-
27
- const regionCode =
28
- normalizeHeader(headers.get('x-c15t-region')) ??
29
- normalizeHeader(headers.get('x-vercel-ip-country-region')) ??
30
- normalizeHeader(headers.get('x-region-code'));
31
-
32
- return { countryCode, regionCode };
33
- }
34
-
35
- /**
36
- * Determines the applicable jurisdiction based on country and region codes.
37
- *
38
- * @remarks
39
- * - EU/EEA/UK map to GDPR-style regimes.
40
- * - Specific countries map to their local laws (CH, BR, CA, AU, JP, KR).
41
- * - CCPA is applied for certain US regions (e.g. California).
42
- */
43
- export function checkJurisdiction(
44
- countryCode: string | null,
45
- regionCode?: string | null
46
- ): JurisdictionCode {
47
- // Country/region sets for different jurisdictions
48
- const jurisdictions = {
49
- EU: new Set([
50
- 'AT',
51
- 'BE',
52
- 'BG',
53
- 'HR',
54
- 'CY',
55
- 'CZ',
56
- 'DK',
57
- 'EE',
58
- 'FI',
59
- 'FR',
60
- 'DE',
61
- 'GR',
62
- 'HU',
63
- 'IE',
64
- 'IT',
65
- 'LV',
66
- 'LT',
67
- 'LU',
68
- 'MT',
69
- 'NL',
70
- 'PL',
71
- 'PT',
72
- 'RO',
73
- 'SK',
74
- 'SI',
75
- 'ES',
76
- 'SE',
77
- ]),
78
- EEA: new Set(['IS', 'NO', 'LI']),
79
- UK: new Set(['GB']),
80
- CH: new Set(['CH']),
81
- BR: new Set(['BR']),
82
- CA: new Set(['CA']),
83
- AU: new Set(['AU']),
84
- JP: new Set(['JP']),
85
- KR: new Set(['KR']),
86
- US_CCPA_REGIONS: new Set([
87
- // California (CCPA/CPRA)
88
- 'CA',
89
- ]),
90
- CA_QC_REGIONS: new Set([
91
- // Quebec (Law 25)
92
- 'QC',
93
- ]),
94
- };
95
-
96
- // Default to no jurisdiction
97
- let jurisdiction: JurisdictionCode = 'NONE';
98
-
99
- // Check country/region codes against jurisdiction sets
100
- if (countryCode) {
101
- // Normalize for case-insensitive comparison
102
- const normalizedCountryCode = countryCode.toUpperCase();
103
- // Normalize region code: handle dash-separated formats like "CA-QC" or "US-CA"
104
- // by extracting the last segment as the subdivision code
105
- const normalizedRegionCode =
106
- regionCode && typeof regionCode === 'string'
107
- ? (regionCode.includes('-')
108
- ? regionCode.split('-').pop()!
109
- : regionCode
110
- ).toUpperCase()
111
- : null;
112
-
113
- // CCPA-style rules: currently applied for certain US regions only
114
- if (
115
- normalizedCountryCode === 'US' &&
116
- normalizedRegionCode &&
117
- jurisdictions.US_CCPA_REGIONS.has(normalizedRegionCode)
118
- ) {
119
- return 'CCPA';
120
- }
121
-
122
- // Quebec (Law 25): opt-in consent required
123
- if (
124
- normalizedCountryCode === 'CA' &&
125
- normalizedRegionCode &&
126
- jurisdictions.CA_QC_REGIONS.has(normalizedRegionCode)
127
- ) {
128
- return 'QC_LAW25';
129
- }
130
-
131
- const jurisdictionMap = [
132
- {
133
- sets: [jurisdictions.UK],
134
- code: 'UK_GDPR' as const,
135
- },
136
- {
137
- sets: [jurisdictions.EU, jurisdictions.EEA],
138
- code: 'GDPR' as const,
139
- },
140
- { sets: [jurisdictions.CH], code: 'CH' as const },
141
- { sets: [jurisdictions.BR], code: 'BR' as const },
142
- { sets: [jurisdictions.CA], code: 'PIPEDA' as const },
143
- { sets: [jurisdictions.AU], code: 'AU' as const },
144
- { sets: [jurisdictions.JP], code: 'APPI' as const },
145
- { sets: [jurisdictions.KR], code: 'PIPA' as const },
146
- ];
147
-
148
- // Find matching jurisdiction
149
- for (const { sets, code } of jurisdictionMap) {
150
- if (sets.some((set) => set.has(normalizedCountryCode))) {
151
- jurisdiction = code;
152
- break;
153
- }
154
- }
155
- }
156
-
157
- return jurisdiction;
158
- }
159
-
160
- /**
161
- * Gets the location from the request headers.
162
- *
163
- * @param request - The incoming request
164
- * @param options - The C15T options
165
- * @returns The location object with countryCode and regionCode
166
- */
167
- export async function getLocation(
168
- request: Request,
169
- options: C15TOptions
170
- ): Promise<{ countryCode: string | null; regionCode: string | null }> {
171
- if (options.advanced?.disableGeoLocation) {
172
- return { countryCode: null, regionCode: null };
173
- }
174
-
175
- const { countryCode, regionCode } = getGeoHeaders(request.headers);
176
- return { countryCode, regionCode };
177
- }
178
-
179
- /**
180
- * Gets the jurisdiction based on location and options.
181
- *
182
- * @param location - The location object
183
- * @param options - The C15T options
184
- * @returns The jurisdiction code
185
- */
186
- export function getJurisdiction(
187
- location: { countryCode: string | null; regionCode: string | null },
188
- options: C15TOptions
189
- ): JurisdictionCode {
190
- if (options.advanced?.disableGeoLocation) {
191
- return 'GDPR';
192
- }
193
-
194
- return checkJurisdiction(location.countryCode, location.regionCode);
195
- }
@@ -1,205 +0,0 @@
1
- import { baseTranslations } from '@c15t/translations';
2
- import { describe, expect, it } from 'vitest';
3
- import type { C15TOptions } from '~/types';
4
- import { checkJurisdiction, getJurisdiction, getLocation } from './geo';
5
- import {
6
- buildResponse,
7
- getHeaders,
8
- getTranslationsData,
9
- parseAcceptLanguage,
10
- } from './index';
11
-
12
- describe('Init Handler Utilities', () => {
13
- describe('getHeaders', () => {
14
- it('extracts country code from cf-ipcountry header', () => {
15
- const headers = new Headers({ 'cf-ipcountry': 'DE' });
16
- const result = getHeaders(headers);
17
- expect(result.countryCode).toBe('DE');
18
- });
19
-
20
- it('falls back to alternative country code headers', () => {
21
- const headers = new Headers({ 'x-vercel-ip-country': 'FR' });
22
- const result = getHeaders(headers);
23
- expect(result.countryCode).toBe('FR');
24
- });
25
-
26
- it('prioritizes cf-ipcountry over other headers', () => {
27
- const headers = new Headers({
28
- 'cf-ipcountry': 'DE',
29
- 'x-vercel-ip-country': 'FR',
30
- });
31
- const result = getHeaders(headers);
32
- expect(result.countryCode).toBe('DE');
33
- });
34
-
35
- it('extracts region code from headers', () => {
36
- const headers = new Headers({
37
- 'cf-ipcountry': 'US',
38
- 'x-vercel-ip-country-region': 'CA',
39
- });
40
- const result = getHeaders(headers);
41
- expect(result.countryCode).toBe('US');
42
- expect(result.regionCode).toBe('CA');
43
- });
44
-
45
- it('handles missing headers gracefully', () => {
46
- const headers = new Headers({});
47
- const result = getHeaders(headers);
48
- expect(result.countryCode).toBeNull();
49
- expect(result.regionCode).toBeNull();
50
- });
51
-
52
- it('handles undefined headers', () => {
53
- const result = getHeaders(undefined);
54
- expect(result.countryCode).toBeNull();
55
- expect(result.regionCode).toBeNull();
56
- expect(result.acceptLanguage).toBeNull();
57
- });
58
- });
59
-
60
- describe('checkJurisdiction', () => {
61
- it('returns GDPR for EU countries', () => {
62
- expect(checkJurisdiction('DE', null)).toBe('GDPR');
63
- expect(checkJurisdiction('FR', null)).toBe('GDPR');
64
- expect(checkJurisdiction('IT', null)).toBe('GDPR');
65
- });
66
-
67
- it('returns UK_GDPR for UK', () => {
68
- expect(checkJurisdiction('GB', null)).toBe('UK_GDPR');
69
- });
70
-
71
- it('returns CCPA for California', () => {
72
- expect(checkJurisdiction('US', 'CA')).toBe('CCPA');
73
- });
74
-
75
- it('returns NONE for non-regulated regions', () => {
76
- expect(checkJurisdiction('US', null)).toBe('NONE');
77
- expect(checkJurisdiction('US', 'TX')).toBe('NONE');
78
- });
79
-
80
- it('returns PIPEDA for Canada', () => {
81
- expect(checkJurisdiction('CA', null)).toBe('PIPEDA');
82
- });
83
-
84
- it('returns CH for Switzerland', () => {
85
- expect(checkJurisdiction('CH', null)).toBe('CH');
86
- });
87
-
88
- it('handles null country code', () => {
89
- expect(checkJurisdiction(null, null)).toBe('NONE');
90
- });
91
- });
92
-
93
- describe('parseAcceptLanguage', () => {
94
- it('returns en for null input', () => {
95
- expect(parseAcceptLanguage(null)).toBe('en');
96
- });
97
-
98
- it('parses simple language code', () => {
99
- expect(parseAcceptLanguage('de')).toBe('de');
100
- });
101
-
102
- it('parses language with region', () => {
103
- expect(parseAcceptLanguage('de-DE')).toBe('de');
104
- });
105
-
106
- it('parses Accept-Language with quality factors', () => {
107
- expect(parseAcceptLanguage('de-DE,de;q=0.9,en;q=0.8')).toBe('de');
108
- });
109
- });
110
-
111
- describe('getTranslationsData', () => {
112
- it('returns en translations for null Accept-Language', () => {
113
- const result = getTranslationsData(null);
114
- expect(result.language).toBe('en');
115
- expect(result.translations.cookieBanner.title).toBe(
116
- baseTranslations.en.cookieBanner.title
117
- );
118
- });
119
-
120
- it('returns de translations for de-DE', () => {
121
- const result = getTranslationsData('de-DE,de;q=0.9,en;q=0.8');
122
- expect(result.language).toBe('de');
123
- expect(result.translations.cookieBanner.title).toBe(
124
- baseTranslations.de.cookieBanner.title
125
- );
126
- });
127
-
128
- it('merges custom translations', () => {
129
- const customTranslations = {
130
- en: { cookieBanner: { title: 'Custom Title' } },
131
- };
132
- const result = getTranslationsData('en-US', customTranslations);
133
- expect(result.translations.cookieBanner.title).toBe('Custom Title');
134
- });
135
- });
136
-
137
- describe('buildResponse', () => {
138
- it('returns properly structured response', () => {
139
- const result = buildResponse({
140
- jurisdiction: 'GDPR',
141
- location: { countryCode: 'DE', regionCode: null },
142
- acceptLanguage: 'en',
143
- customTranslations: undefined,
144
- branding: 'c15t',
145
- });
146
-
147
- expect(result).toHaveProperty('jurisdiction');
148
- expect(result).toHaveProperty('location');
149
- expect(result).toHaveProperty('translations');
150
- expect(result).toHaveProperty('branding');
151
- expect(result.jurisdiction).toBe('GDPR');
152
- expect(result.branding).toBe('c15t');
153
- });
154
- });
155
-
156
- describe('getLocation', () => {
157
- it('returns null location when geo is disabled', async () => {
158
- const request = new Request('http://localhost', {
159
- headers: { 'cf-ipcountry': 'DE' },
160
- });
161
- const options: C15TOptions = {
162
- trustedOrigins: [],
163
- adapter: {} as C15TOptions['adapter'],
164
- advanced: { disableGeoLocation: true },
165
- };
166
-
167
- const result = await getLocation(request, options);
168
- expect(result.countryCode).toBeNull();
169
- expect(result.regionCode).toBeNull();
170
- });
171
- });
172
-
173
- describe('getJurisdiction', () => {
174
- it('returns GDPR when geo is disabled', () => {
175
- const options: C15TOptions = {
176
- trustedOrigins: [],
177
- adapter: {} as C15TOptions['adapter'],
178
- advanced: { disableGeoLocation: true },
179
- };
180
-
181
- const result = getJurisdiction(
182
- { countryCode: 'US', regionCode: null },
183
- options
184
- );
185
- expect(result).toBe('GDPR');
186
- });
187
-
188
- it('returns appropriate jurisdiction based on location', () => {
189
- const options: C15TOptions = {
190
- trustedOrigins: [],
191
- adapter: {} as C15TOptions['adapter'],
192
- };
193
-
194
- expect(
195
- getJurisdiction({ countryCode: 'DE', regionCode: null }, options)
196
- ).toBe('GDPR');
197
- expect(
198
- getJurisdiction({ countryCode: 'US', regionCode: 'CA' }, options)
199
- ).toBe('CCPA');
200
- expect(
201
- getJurisdiction({ countryCode: 'GB', regionCode: null }, options)
202
- ).toBe('UK_GDPR');
203
- });
204
- });
205
- });
@@ -1,114 +0,0 @@
1
- import type { GlobalVendorList, NonIABVendor } from '@c15t/schema/types';
2
- import type { Translations } from '@c15t/translations';
3
- import type { Branding } from '~/types';
4
- import type { JurisdictionCode } from '~/types/api';
5
- import { getTranslationsData } from './translations';
6
-
7
- /**
8
- * Gets the headers from the context.
9
- *
10
- * @param headers - The headers to get the headers from.
11
- * @returns The headers or null if the headers are not present.
12
- */
13
- export function getHeaders(headers: Headers | undefined) {
14
- if (!headers) {
15
- return {
16
- countryCode: null,
17
- regionCode: null,
18
- acceptLanguage: null,
19
- };
20
- }
21
-
22
- // Add this conversion to ensure headers are always string or null
23
- const normalizeHeader = (
24
- value: string | string[] | null | undefined
25
- ): string | null => {
26
- if (!value) {
27
- return null;
28
- }
29
-
30
- return Array.isArray(value) ? (value[0] ?? null) : value;
31
- };
32
-
33
- const countryCode =
34
- normalizeHeader(headers.get('x-c15t-country')) ??
35
- normalizeHeader(headers.get('cf-ipcountry')) ??
36
- normalizeHeader(headers.get('x-vercel-ip-country')) ??
37
- normalizeHeader(headers.get('x-amz-cf-ipcountry')) ??
38
- normalizeHeader(headers.get('x-country-code'));
39
-
40
- const regionCode =
41
- normalizeHeader(headers.get('x-c15t-region')) ??
42
- normalizeHeader(headers.get('x-vercel-ip-country-region')) ??
43
- normalizeHeader(headers.get('x-region-code'));
44
-
45
- // Get preferred language from Accept-Language header
46
- const acceptLanguage = normalizeHeader(headers.get('accept-language'));
47
-
48
- return {
49
- countryCode,
50
- regionCode,
51
- acceptLanguage,
52
- };
53
- }
54
-
55
- /**
56
- * Parse Accept-Language header to get the primary language code.
57
- *
58
- * @param acceptLanguage - The Accept-Language header value
59
- * @returns The primary language code (e.g., "en", "de"), defaults to "en"
60
- */
61
- export function parseAcceptLanguage(acceptLanguage: string | null): string {
62
- if (!acceptLanguage) {
63
- return 'en';
64
- }
65
-
66
- // Parse "en-US,en;q=0.9,de;q=0.8" format
67
- // Split by comma, take the first part, then extract language code
68
- const firstLanguage = acceptLanguage.split(',')[0];
69
- if (!firstLanguage) {
70
- return 'en';
71
- }
72
-
73
- // Remove quality factor if present (e.g., "en;q=0.9" -> "en")
74
- const languageWithRegion = firstLanguage.split(';')[0]?.trim();
75
- if (!languageWithRegion) {
76
- return 'en';
77
- }
78
-
79
- // Extract language code without region (e.g., "en-US" -> "en")
80
- const languageCode = languageWithRegion.split('-')[0]?.toLowerCase();
81
-
82
- return languageCode ?? 'en';
83
- }
84
-
85
- export function buildResponse({
86
- jurisdiction,
87
- location,
88
- acceptLanguage,
89
- customTranslations,
90
- branding = 'c15t',
91
- gvl,
92
- customVendors,
93
- }: {
94
- jurisdiction: JurisdictionCode;
95
- location: { countryCode: string | null; regionCode: string | null };
96
- acceptLanguage: string | null;
97
- customTranslations: Record<string, Partial<Translations>> | undefined;
98
- branding?: Branding;
99
- gvl?: GlobalVendorList;
100
- customVendors?: NonIABVendor[];
101
- }) {
102
- return {
103
- jurisdiction,
104
- location,
105
- translations: getTranslationsData(acceptLanguage, customTranslations),
106
- branding: branding,
107
- gvl: gvl ?? null,
108
- customVendors: customVendors ?? undefined,
109
- };
110
- }
111
-
112
- export { checkJurisdiction, getJurisdiction, getLocation } from './geo';
113
- // Re-export translations functions
114
- export { getTranslations, getTranslationsData } from './translations';
@@ -1,121 +0,0 @@
1
- import { baseTranslations } from '@c15t/translations';
2
- import { describe, expect, it } from 'vitest';
3
- import { getTranslationsData } from './translations';
4
-
5
- describe('showBanner > getTranslationsData', () => {
6
- it("should return 'en' translations when Accept-Language is null", () => {
7
- const { translations, language } = getTranslationsData(null);
8
- expect(language).toBe('en');
9
- expect(translations.cookieBanner.title).toBe(
10
- baseTranslations.en.cookieBanner.title
11
- );
12
- });
13
-
14
- it("should return 'en' translations for unsupported language", () => {
15
- const { translations, language } = getTranslationsData('xx-XX,en;q=0.9');
16
- expect(language).toBe('en');
17
- expect(translations.cookieBanner.title).toBe(
18
- baseTranslations.en.cookieBanner.title
19
- );
20
- });
21
-
22
- it("should return 'de' translations for 'de-DE'", () => {
23
- const { translations, language } = getTranslationsData(
24
- 'de-DE,de;q=0.9,en;q=0.8'
25
- );
26
- expect(language).toBe('de');
27
- expect(translations.cookieBanner.title).toBe(
28
- baseTranslations.de.cookieBanner.title
29
- );
30
- });
31
-
32
- it('should merge custom translations for the preferred language', () => {
33
- const customTranslations = {
34
- en: {
35
- cookieBanner: {
36
- title: 'My Custom Cookie Title',
37
- },
38
- },
39
- };
40
- const { translations, language } = getTranslationsData(
41
- 'en-US,en;q=0.9',
42
- customTranslations
43
- );
44
- expect(language).toBe('en');
45
- expect(translations.cookieBanner.title).toBe('My Custom Cookie Title');
46
- // Check if other properties from base translations are still there
47
- expect(translations.cookieBanner.description).toBeDefined();
48
- });
49
-
50
- it('should not merge custom translations for a different language', () => {
51
- const customTranslations = {
52
- de: {
53
- cookieBanner: {
54
- title: 'Meine benutzerdefinierte Cookie-Überschrift',
55
- },
56
- },
57
- };
58
- const { translations, language } = getTranslationsData(
59
- 'en-US,en;q=0.9',
60
- customTranslations
61
- );
62
- expect(language).toBe('en');
63
- expect(translations.cookieBanner.title).toBe(
64
- baseTranslations.en.cookieBanner.title
65
- );
66
- });
67
-
68
- it('should handle partially provided custom translations', () => {
69
- const customTranslations = {
70
- en: {
71
- cookieBanner: {
72
- // Title is NOT provided, description is.
73
- description: 'My custom description.',
74
- },
75
- },
76
- };
77
- const { translations, language } = getTranslationsData(
78
- 'en-US,en;q=0.9',
79
- customTranslations
80
- );
81
- expect(language).toBe('en');
82
- // Title should come from base
83
- expect(translations.cookieBanner.title).toBe(
84
- baseTranslations.en.cookieBanner.title
85
- );
86
- // Description should be from custom
87
- expect(translations.cookieBanner.description).toBe(
88
- 'My custom description.'
89
- );
90
- });
91
-
92
- it('should return base translations if custom translations are empty for the language', () => {
93
- const customTranslations = {
94
- de: {},
95
- };
96
- const { translations, language } = getTranslationsData(
97
- 'de-DE,de;q=0.9',
98
- customTranslations
99
- );
100
- expect(language).toBe('de');
101
- expect(translations.cookieBanner.title).toBe(
102
- baseTranslations.de.cookieBanner.title
103
- );
104
- });
105
-
106
- it('should return custom translations for unsupported base language', () => {
107
- const { translations, language } = getTranslationsData('xx-XX,en;q=0.9', {
108
- xx: {
109
- cookieBanner: {
110
- title: 'XX Title',
111
- },
112
- },
113
- });
114
-
115
- expect(language).toBe('xx');
116
- expect(translations.cookieBanner.title).toBe('XX Title');
117
- expect(translations.cookieBanner.description).toBe(
118
- baseTranslations.en.cookieBanner.description
119
- );
120
- });
121
- });
@@ -1,72 +0,0 @@
1
- import {
2
- baseTranslations,
3
- type CompleteTranslations,
4
- deepMergeTranslations,
5
- selectLanguage,
6
- type Translations,
7
- } from '@c15t/translations';
8
-
9
- type SupportedBaseLanguage = keyof typeof baseTranslations;
10
-
11
- function isSupportedBaseLanguage(lang: string): lang is SupportedBaseLanguage {
12
- return lang in baseTranslations;
13
- }
14
-
15
- /**
16
- * Gets the translations data for a given language, merging custom translations if provided.
17
- *
18
- * @param acceptLanguage - The `Accept-Language` header from the request.
19
- * @param customTranslations - An object containing custom translations to merge with the base translations.
20
- * @returns An object containing the final translations and the determined language.
21
- */
22
- export function getTranslationsData(
23
- acceptLanguage: string | null,
24
- customTranslations?: Record<string, Partial<Translations>>
25
- ) {
26
- const supportedDefaultLanguages = Object.keys(baseTranslations);
27
- const supportedCustomLanguages = Object.keys(customTranslations || {});
28
-
29
- const supportedLanguages = [
30
- ...supportedDefaultLanguages,
31
- ...supportedCustomLanguages,
32
- ];
33
-
34
- const preferredLanguage = selectLanguage(supportedLanguages, {
35
- header: acceptLanguage,
36
- fallback: 'en',
37
- });
38
-
39
- const base = isSupportedBaseLanguage(preferredLanguage)
40
- ? baseTranslations[preferredLanguage]
41
- : baseTranslations.en;
42
-
43
- const custom = supportedCustomLanguages.includes(preferredLanguage)
44
- ? customTranslations?.[preferredLanguage]
45
- : {};
46
-
47
- const translations = custom ? deepMergeTranslations(base, custom) : base;
48
-
49
- return {
50
- translations: translations as CompleteTranslations,
51
- language: preferredLanguage,
52
- };
53
- }
54
-
55
- /**
56
- * Gets the translations for a given language from options.
57
- *
58
- * @param acceptLanguage - The `Accept-Language` header from the request.
59
- * @param options - The C15T options containing custom translations.
60
- * @returns An object containing the final translations and the determined language.
61
- */
62
- export async function getTranslations(
63
- acceptLanguage: string,
64
- options: {
65
- advanced?: { customTranslations?: Record<string, Partial<Translations>> };
66
- }
67
- ) {
68
- return getTranslationsData(
69
- acceptLanguage,
70
- options.advanced?.customTranslations
71
- );
72
- }