@c15t/backend 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (489) hide show
  1. package/.turbo/turbo-build.log +63 -37
  2. package/CHANGELOG.md +4 -209
  3. package/README.md +86 -164
  4. package/dist/contracts/consent/index.d.ts +103 -615
  5. package/dist/contracts/consent/index.d.ts.map +1 -1
  6. package/dist/contracts/consent/post.contract.d.ts +42 -140
  7. package/dist/contracts/consent/post.contract.d.ts.map +1 -1
  8. package/dist/contracts/consent/show-banner.contract.d.ts +28 -376
  9. package/dist/contracts/consent/show-banner.contract.d.ts.map +1 -1
  10. package/dist/contracts/consent/verify.contract.d.ts +33 -99
  11. package/dist/contracts/consent/verify.contract.d.ts.map +1 -1
  12. package/dist/contracts/index.d.ts +222 -1356
  13. package/dist/contracts/index.d.ts.map +1 -1
  14. package/dist/contracts/meta/index.d.ts +8 -63
  15. package/dist/contracts/meta/index.d.ts.map +1 -1
  16. package/dist/contracts/meta/status.contract.d.ts +8 -63
  17. package/dist/contracts/meta/status.contract.d.ts.map +1 -1
  18. package/dist/contracts/shared/jurisdiction.schema.d.ts +21 -9
  19. package/dist/contracts/shared/jurisdiction.schema.d.ts.map +1 -1
  20. package/dist/contracts.cjs +100 -106
  21. package/dist/contracts.js +100 -106
  22. package/dist/core.cjs +681 -681
  23. package/dist/core.d.ts +118 -678
  24. package/dist/core.d.ts.map +1 -1
  25. package/dist/core.js +634 -637
  26. package/dist/handlers/consent/index.d.ts +103 -615
  27. package/dist/handlers/consent/index.d.ts.map +1 -1
  28. package/dist/handlers/consent/post.handler.d.ts +42 -140
  29. package/dist/handlers/consent/post.handler.d.ts.map +1 -1
  30. package/dist/handlers/consent/show-banner/handler.d.ts +28 -376
  31. package/dist/handlers/consent/show-banner/handler.d.ts.map +1 -1
  32. package/dist/handlers/consent/show-banner/translations.d.ts.map +1 -1
  33. package/dist/handlers/consent/verify.handler.d.ts +33 -99
  34. package/dist/handlers/consent/verify.handler.d.ts.map +1 -1
  35. package/dist/handlers/meta/index.d.ts +8 -63
  36. package/dist/handlers/meta/index.d.ts.map +1 -1
  37. package/dist/handlers/meta/status.handler.d.ts +8 -63
  38. package/dist/handlers/meta/status.handler.d.ts.map +1 -1
  39. package/dist/init.d.ts.map +1 -1
  40. package/dist/middleware/openapi/index.d.ts +2 -2
  41. package/dist/middleware/openapi/index.d.ts.map +1 -1
  42. package/dist/pkgs/data-model/fields/index.cjs +14 -26
  43. package/dist/pkgs/data-model/fields/index.d.ts +4 -4
  44. package/dist/pkgs/data-model/fields/index.d.ts.map +1 -1
  45. package/dist/pkgs/data-model/fields/index.js +14 -26
  46. package/dist/pkgs/data-model/fields/zod-fields.d.ts +195 -871
  47. package/dist/pkgs/data-model/fields/zod-fields.d.ts.map +1 -1
  48. package/dist/pkgs/data-model/hooks/index.d.ts +2 -2
  49. package/dist/pkgs/data-model/hooks/index.d.ts.map +1 -1
  50. package/dist/pkgs/data-model/index.cjs +346 -358
  51. package/dist/pkgs/data-model/index.d.ts +1 -1
  52. package/dist/pkgs/data-model/index.d.ts.map +1 -1
  53. package/dist/pkgs/data-model/index.js +345 -357
  54. package/dist/pkgs/data-model/schema/index.cjs +346 -358
  55. package/dist/pkgs/data-model/schema/index.d.ts +1 -1
  56. package/dist/pkgs/data-model/schema/index.d.ts.map +1 -1
  57. package/dist/pkgs/data-model/schema/index.js +345 -357
  58. package/dist/pkgs/data-model/schema/schemas.d.ts +2 -2
  59. package/dist/pkgs/data-model/schema/schemas.d.ts.map +1 -1
  60. package/dist/pkgs/db-adapters/adapters/drizzle-adapter/drizzle-adapter.d.ts +3 -0
  61. package/dist/pkgs/db-adapters/adapters/drizzle-adapter/drizzle-adapter.d.ts.map +1 -1
  62. package/dist/pkgs/db-adapters/adapters/drizzle-adapter/index.cjs +158 -170
  63. package/dist/pkgs/db-adapters/adapters/drizzle-adapter/index.js +157 -169
  64. package/dist/pkgs/db-adapters/adapters/index.d.ts +2 -2
  65. package/dist/pkgs/db-adapters/adapters/index.d.ts.map +1 -1
  66. package/dist/pkgs/db-adapters/adapters/kysely-adapter/index.cjs +215 -227
  67. package/dist/pkgs/db-adapters/adapters/kysely-adapter/index.d.ts +2 -2
  68. package/dist/pkgs/db-adapters/adapters/kysely-adapter/index.d.ts.map +1 -1
  69. package/dist/pkgs/db-adapters/adapters/kysely-adapter/index.js +213 -225
  70. package/dist/pkgs/db-adapters/adapters/kysely-adapter/kysely-adapter.d.ts +2 -0
  71. package/dist/pkgs/db-adapters/adapters/kysely-adapter/kysely-adapter.d.ts.map +1 -1
  72. package/dist/pkgs/db-adapters/adapters/kysely-adapter/tests/test-utils.d.ts +1 -1
  73. package/dist/pkgs/db-adapters/adapters/kysely-adapter/tests/test-utils.d.ts.map +1 -1
  74. package/dist/pkgs/db-adapters/adapters/memory-adapter/index.cjs +158 -170
  75. package/dist/pkgs/db-adapters/adapters/memory-adapter/index.js +157 -169
  76. package/dist/pkgs/db-adapters/adapters/memory-adapter/memory-adapter.d.ts +3 -0
  77. package/dist/pkgs/db-adapters/adapters/memory-adapter/memory-adapter.d.ts.map +1 -1
  78. package/dist/pkgs/db-adapters/adapters/prisma-adapter/index.cjs +243 -255
  79. package/dist/pkgs/db-adapters/adapters/prisma-adapter/index.d.ts +1 -1
  80. package/dist/pkgs/db-adapters/adapters/prisma-adapter/index.d.ts.map +1 -1
  81. package/dist/pkgs/db-adapters/adapters/prisma-adapter/index.js +241 -253
  82. package/dist/pkgs/db-adapters/adapters/prisma-adapter/prisma-adapter.d.ts +3 -0
  83. package/dist/pkgs/db-adapters/adapters/prisma-adapter/prisma-adapter.d.ts.map +1 -1
  84. package/dist/pkgs/db-adapters/index.cjs +714 -726
  85. package/dist/pkgs/db-adapters/index.d.ts +6 -6
  86. package/dist/pkgs/db-adapters/index.d.ts.map +1 -1
  87. package/dist/pkgs/db-adapters/index.js +708 -720
  88. package/dist/pkgs/migrations/get-migration.d.ts.map +1 -1
  89. package/dist/pkgs/migrations/get-schema/get-schema.d.ts.map +1 -1
  90. package/dist/pkgs/migrations/get-schema/process-tables.d.ts.map +1 -1
  91. package/dist/pkgs/migrations/index.cjs +236 -248
  92. package/dist/pkgs/migrations/index.d.ts +4 -4
  93. package/dist/pkgs/migrations/index.d.ts.map +1 -1
  94. package/dist/pkgs/migrations/index.js +235 -247
  95. package/dist/pkgs/results/index.cjs +67 -67
  96. package/dist/pkgs/results/index.d.ts +5 -5
  97. package/dist/pkgs/results/index.d.ts.map +1 -1
  98. package/dist/pkgs/results/index.js +67 -67
  99. package/dist/pkgs/results/orpc-error-handler.d.ts +1 -1
  100. package/dist/pkgs/results/orpc-error-handler.d.ts.map +1 -1
  101. package/dist/pkgs/types/index.d.ts +1 -2
  102. package/dist/pkgs/types/index.d.ts.map +1 -1
  103. package/dist/pkgs/types/options.d.ts +9 -2
  104. package/dist/pkgs/types/options.d.ts.map +1 -1
  105. package/dist/pkgs/utils/index.d.ts +1 -1
  106. package/dist/pkgs/utils/index.d.ts.map +1 -1
  107. package/dist/pkgs/utils/logger.d.ts +1 -1
  108. package/dist/pkgs/utils/logger.d.ts.map +1 -1
  109. package/dist/router.cjs +114 -117
  110. package/dist/router.d.ts +111 -678
  111. package/dist/router.d.ts.map +1 -1
  112. package/dist/router.js +114 -117
  113. package/dist/schema/audit-log/schema.d.ts +2 -24
  114. package/dist/schema/audit-log/schema.d.ts.map +1 -1
  115. package/dist/schema/audit-log/table.d.ts +2 -24
  116. package/dist/schema/audit-log/table.d.ts.map +1 -1
  117. package/dist/schema/consent/registry.d.ts +8 -8
  118. package/dist/schema/consent/schema.d.ts +9 -33
  119. package/dist/schema/consent/schema.d.ts.map +1 -1
  120. package/dist/schema/consent/table.d.ts +9 -33
  121. package/dist/schema/consent/table.d.ts.map +1 -1
  122. package/dist/schema/consent-policy/registry.d.ts +20 -20
  123. package/dist/schema/consent-policy/schema.d.ts +22 -30
  124. package/dist/schema/consent-policy/schema.d.ts.map +1 -1
  125. package/dist/schema/consent-policy/table.d.ts +13 -29
  126. package/dist/schema/consent-policy/table.d.ts.map +1 -1
  127. package/dist/schema/consent-purpose/registry.d.ts +6 -6
  128. package/dist/schema/consent-purpose/schema.d.ts +5 -27
  129. package/dist/schema/consent-purpose/schema.d.ts.map +1 -1
  130. package/dist/schema/consent-purpose/table.d.ts +5 -27
  131. package/dist/schema/consent-purpose/table.d.ts.map +1 -1
  132. package/dist/schema/consent-record/schema.d.ts +3 -19
  133. package/dist/schema/consent-record/schema.d.ts.map +1 -1
  134. package/dist/schema/consent-record/table.d.ts +3 -19
  135. package/dist/schema/consent-record/table.d.ts.map +1 -1
  136. package/dist/schema/create-registry.d.ts +58 -58
  137. package/dist/schema/definition.d.ts +42 -176
  138. package/dist/schema/definition.d.ts.map +1 -1
  139. package/dist/schema/domain/registry.d.ts +20 -20
  140. package/dist/schema/domain/schema.d.ts +6 -24
  141. package/dist/schema/domain/schema.d.ts.map +1 -1
  142. package/dist/schema/domain/table.d.ts +6 -24
  143. package/dist/schema/domain/table.d.ts.map +1 -1
  144. package/dist/schema/index.cjs +426 -438
  145. package/dist/schema/index.d.ts +12 -12
  146. package/dist/schema/index.d.ts.map +1 -1
  147. package/dist/schema/index.js +426 -438
  148. package/dist/schema/schemas.d.ts +42 -176
  149. package/dist/schema/schemas.d.ts.map +1 -1
  150. package/dist/schema/subject/registry.d.ts +4 -4
  151. package/dist/schema/subject/schema.d.ts +4 -20
  152. package/dist/schema/subject/schema.d.ts.map +1 -1
  153. package/dist/schema/subject/table.d.ts +4 -20
  154. package/dist/schema/subject/table.d.ts.map +1 -1
  155. package/dist/schema/types.d.ts +1 -1
  156. package/dist/schema/types.d.ts.map +1 -1
  157. package/dist/testing/contract-testing.d.ts +3 -2
  158. package/dist/testing/contract-testing.d.ts.map +1 -1
  159. package/dist/types/index.d.ts +5 -4
  160. package/dist/types/index.d.ts.map +1 -1
  161. package/dist/types/options.d.ts +2 -2
  162. package/dist/types/options.d.ts.map +1 -1
  163. package/dist/v2/contracts/consent/index.d.ts +260 -0
  164. package/dist/v2/contracts/consent/index.d.ts.map +1 -0
  165. package/dist/v2/contracts/consent/index.test.d.ts +2 -0
  166. package/dist/v2/contracts/consent/index.test.d.ts.map +1 -0
  167. package/dist/v2/contracts/consent/post.contract.d.ts +114 -0
  168. package/dist/v2/contracts/consent/post.contract.d.ts.map +1 -0
  169. package/dist/v2/contracts/consent/post.contract.test.d.ts +2 -0
  170. package/dist/v2/contracts/consent/post.contract.test.d.ts.map +1 -0
  171. package/dist/v2/contracts/consent/show-banner.contract.d.ts +68 -0
  172. package/dist/v2/contracts/consent/show-banner.contract.d.ts.map +1 -0
  173. package/dist/v2/contracts/consent/show-banner.contract.test.d.ts +2 -0
  174. package/dist/v2/contracts/consent/show-banner.contract.test.d.ts.map +1 -0
  175. package/dist/v2/contracts/consent/verify.contract.d.ts +81 -0
  176. package/dist/v2/contracts/consent/verify.contract.d.ts.map +1 -0
  177. package/dist/v2/contracts/consent/verify.contract.test.d.ts +2 -0
  178. package/dist/v2/contracts/consent/verify.contract.test.d.ts.map +1 -0
  179. package/dist/v2/contracts/index.cjs +644 -0
  180. package/dist/v2/contracts/index.d.ts +563 -0
  181. package/dist/v2/contracts/index.d.ts.map +1 -0
  182. package/dist/v2/contracts/index.js +607 -0
  183. package/dist/v2/contracts/meta/index.d.ts +19 -0
  184. package/dist/v2/contracts/meta/index.d.ts.map +1 -0
  185. package/dist/v2/contracts/meta/index.test.d.ts +2 -0
  186. package/dist/v2/contracts/meta/index.test.d.ts.map +1 -0
  187. package/dist/v2/contracts/meta/status.contract.d.ts +18 -0
  188. package/dist/v2/contracts/meta/status.contract.d.ts.map +1 -0
  189. package/dist/v2/contracts/meta/status.contract.test.d.ts +2 -0
  190. package/dist/v2/contracts/meta/status.contract.test.d.ts.map +1 -0
  191. package/dist/v2/contracts/shared/jurisdiction.schema.d.ts +36 -0
  192. package/dist/v2/contracts/shared/jurisdiction.schema.d.ts.map +1 -0
  193. package/dist/v2/contracts/test.utils.d.ts +38 -0
  194. package/dist/v2/contracts/test.utils.d.ts.map +1 -0
  195. package/dist/v2/core.cjs +2181 -0
  196. package/dist/v2/core.d.ts +364 -0
  197. package/dist/v2/core.d.ts.map +1 -0
  198. package/dist/v2/core.js +2130 -0
  199. package/dist/v2/db/adapters/drizzle.cjs +36 -0
  200. package/dist/v2/db/adapters/drizzle.d.ts +2 -0
  201. package/dist/v2/db/adapters/drizzle.d.ts.map +1 -0
  202. package/dist/v2/db/adapters/drizzle.js +3 -0
  203. package/dist/v2/db/adapters/index.cjs +18 -0
  204. package/dist/v2/db/adapters/index.d.ts +2 -0
  205. package/dist/v2/db/adapters/index.d.ts.map +1 -0
  206. package/dist/v2/db/adapters/index.js +0 -0
  207. package/dist/v2/db/adapters/kysely.cjs +36 -0
  208. package/dist/v2/db/adapters/kysely.d.ts +2 -0
  209. package/dist/v2/db/adapters/kysely.d.ts.map +1 -0
  210. package/dist/v2/db/adapters/kysely.js +3 -0
  211. package/dist/v2/db/adapters/mongo.cjs +36 -0
  212. package/dist/v2/db/adapters/mongo.d.ts +2 -0
  213. package/dist/v2/db/adapters/mongo.d.ts.map +1 -0
  214. package/dist/v2/db/adapters/mongo.js +3 -0
  215. package/dist/v2/db/adapters/prisma.cjs +36 -0
  216. package/dist/v2/db/adapters/prisma.d.ts +2 -0
  217. package/dist/v2/db/adapters/prisma.d.ts.map +1 -0
  218. package/dist/v2/db/adapters/prisma.js +3 -0
  219. package/dist/v2/db/adapters/typeorm.cjs +36 -0
  220. package/dist/v2/db/adapters/typeorm.d.ts +2 -0
  221. package/dist/v2/db/adapters/typeorm.d.ts.map +1 -0
  222. package/dist/v2/db/adapters/typeorm.js +3 -0
  223. package/dist/v2/db/migrator/index.cjs +61 -0
  224. package/dist/v2/db/migrator/index.d.ts +29 -0
  225. package/dist/v2/db/migrator/index.d.ts.map +1 -0
  226. package/dist/v2/db/migrator/index.js +27 -0
  227. package/dist/v2/db/registry/audit-log.d.ts +21 -0
  228. package/dist/v2/db/registry/audit-log.d.ts.map +1 -0
  229. package/dist/v2/db/registry/audit-log.test.d.ts +2 -0
  230. package/dist/v2/db/registry/audit-log.test.d.ts.map +1 -0
  231. package/dist/v2/db/registry/consent-policy.d.ts +29 -0
  232. package/dist/v2/db/registry/consent-policy.d.ts.map +1 -0
  233. package/dist/v2/db/registry/consent-policy.test.d.ts +2 -0
  234. package/dist/v2/db/registry/consent-policy.test.d.ts.map +1 -0
  235. package/dist/v2/db/registry/consent-purpose.d.ts +16 -0
  236. package/dist/v2/db/registry/consent-purpose.d.ts.map +1 -0
  237. package/dist/v2/db/registry/consent-purpose.test.d.ts +2 -0
  238. package/dist/v2/db/registry/consent-purpose.test.d.ts.map +1 -0
  239. package/dist/v2/db/registry/consent.d.ts +20 -0
  240. package/dist/v2/db/registry/consent.d.ts.map +1 -0
  241. package/dist/v2/db/registry/consent.test.d.ts +2 -0
  242. package/dist/v2/db/registry/consent.test.d.ts.map +1 -0
  243. package/dist/v2/db/registry/domain.d.ts +24 -0
  244. package/dist/v2/db/registry/domain.d.ts.map +1 -0
  245. package/dist/v2/db/registry/domain.test.d.ts +2 -0
  246. package/dist/v2/db/registry/domain.test.d.ts.map +1 -0
  247. package/dist/v2/db/registry/index.d.ts +102 -0
  248. package/dist/v2/db/registry/index.d.ts.map +1 -0
  249. package/dist/v2/db/registry/subject.d.ts +18 -0
  250. package/dist/v2/db/registry/subject.d.ts.map +1 -0
  251. package/dist/v2/db/registry/subject.test.d.ts +2 -0
  252. package/dist/v2/db/registry/subject.test.d.ts.map +1 -0
  253. package/dist/v2/db/registry/types.d.ts +10 -0
  254. package/dist/v2/db/registry/types.d.ts.map +1 -0
  255. package/dist/v2/db/registry/utils/generate-id.d.ts +25 -0
  256. package/dist/v2/db/registry/utils/generate-id.d.ts.map +1 -0
  257. package/dist/v2/db/registry/utils/generate-id.test.d.ts +2 -0
  258. package/dist/v2/db/registry/utils/generate-id.test.d.ts.map +1 -0
  259. package/dist/v2/db/registry/utils.d.ts +25 -0
  260. package/dist/v2/db/registry/utils.d.ts.map +1 -0
  261. package/dist/v2/db/schema/1.0.0/audit-log.d.ts +29 -0
  262. package/dist/v2/db/schema/1.0.0/audit-log.d.ts.map +1 -0
  263. package/dist/v2/db/schema/1.0.0/consent-policy.d.ts +45 -0
  264. package/dist/v2/db/schema/1.0.0/consent-policy.d.ts.map +1 -0
  265. package/dist/v2/db/schema/1.0.0/consent-purpose.d.ts +27 -0
  266. package/dist/v2/db/schema/1.0.0/consent-purpose.d.ts.map +1 -0
  267. package/dist/v2/db/schema/1.0.0/consent-record.d.ts +19 -0
  268. package/dist/v2/db/schema/1.0.0/consent-record.d.ts.map +1 -0
  269. package/dist/v2/db/schema/1.0.0/consent.d.ts +42 -0
  270. package/dist/v2/db/schema/1.0.0/consent.d.ts.map +1 -0
  271. package/dist/v2/db/schema/1.0.0/domain.d.ts +23 -0
  272. package/dist/v2/db/schema/1.0.0/domain.d.ts.map +1 -0
  273. package/dist/v2/db/schema/1.0.0/index.d.ts +1513 -0
  274. package/dist/v2/db/schema/1.0.0/index.d.ts.map +1 -0
  275. package/dist/v2/db/schema/1.0.0/subject.d.ts +23 -0
  276. package/dist/v2/db/schema/1.0.0/subject.d.ts.map +1 -0
  277. package/dist/v2/db/schema/index.cjs +326 -0
  278. package/dist/v2/db/schema/index.d.ts +1507 -0
  279. package/dist/v2/db/schema/index.d.ts.map +1 -0
  280. package/dist/v2/db/schema/index.js +241 -0
  281. package/dist/v2/define-config.cjs +36 -0
  282. package/dist/v2/define-config.d.ts +5 -0
  283. package/dist/v2/define-config.d.ts.map +1 -0
  284. package/dist/v2/define-config.js +2 -0
  285. package/dist/v2/handlers/consent/index.d.ts +260 -0
  286. package/dist/v2/handlers/consent/index.d.ts.map +1 -0
  287. package/dist/v2/handlers/consent/post.handler.d.ts +136 -0
  288. package/dist/v2/handlers/consent/post.handler.d.ts.map +1 -0
  289. package/dist/v2/handlers/consent/show-banner/geo.d.ts +10 -0
  290. package/dist/v2/handlers/consent/show-banner/geo.d.ts.map +1 -0
  291. package/dist/v2/handlers/consent/show-banner/geo.test.d.ts +2 -0
  292. package/dist/v2/handlers/consent/show-banner/geo.test.d.ts.map +1 -0
  293. package/dist/v2/handlers/consent/show-banner/handler.d.ts +71 -0
  294. package/dist/v2/handlers/consent/show-banner/handler.d.ts.map +1 -0
  295. package/dist/v2/handlers/consent/show-banner/handler.test.d.ts +2 -0
  296. package/dist/v2/handlers/consent/show-banner/handler.test.d.ts.map +1 -0
  297. package/dist/v2/handlers/consent/show-banner/translations.d.ts +13 -0
  298. package/dist/v2/handlers/consent/show-banner/translations.d.ts.map +1 -0
  299. package/dist/v2/handlers/consent/show-banner/translations.test.d.ts +2 -0
  300. package/dist/v2/handlers/consent/show-banner/translations.test.d.ts.map +1 -0
  301. package/dist/v2/handlers/consent/verify.handler.d.ts +103 -0
  302. package/dist/v2/handlers/consent/verify.handler.d.ts.map +1 -0
  303. package/dist/v2/handlers/meta/index.d.ts +19 -0
  304. package/dist/v2/handlers/meta/index.d.ts.map +1 -0
  305. package/dist/v2/handlers/meta/status.handler.d.ts +17 -0
  306. package/dist/v2/handlers/meta/status.handler.d.ts.map +1 -0
  307. package/dist/v2/init.d.ts +3 -0
  308. package/dist/v2/init.d.ts.map +1 -0
  309. package/dist/v2/init.test.d.ts +2 -0
  310. package/dist/v2/init.test.d.ts.map +1 -0
  311. package/dist/v2/middleware/cors/cors.d.ts +37 -0
  312. package/dist/v2/middleware/cors/cors.d.ts.map +1 -0
  313. package/dist/v2/middleware/cors/cors.test.d.ts +2 -0
  314. package/dist/v2/middleware/cors/cors.test.d.ts.map +1 -0
  315. package/dist/v2/middleware/cors/index.d.ts +30 -0
  316. package/dist/v2/middleware/cors/index.d.ts.map +1 -0
  317. package/dist/v2/middleware/cors/is-origin-trusted.d.ts +49 -0
  318. package/dist/v2/middleware/cors/is-origin-trusted.d.ts.map +1 -0
  319. package/dist/v2/middleware/cors/is-origin-trusted.test.d.ts +2 -0
  320. package/dist/v2/middleware/cors/is-origin-trusted.test.d.ts.map +1 -0
  321. package/dist/v2/middleware/cors/process-cors.d.ts +31 -0
  322. package/dist/v2/middleware/cors/process-cors.d.ts.map +1 -0
  323. package/dist/v2/middleware/openapi/config.d.ts +28 -0
  324. package/dist/v2/middleware/openapi/config.d.ts.map +1 -0
  325. package/dist/v2/middleware/openapi/handlers.d.ts +29 -0
  326. package/dist/v2/middleware/openapi/handlers.d.ts.map +1 -0
  327. package/dist/v2/middleware/openapi/index.d.ts +11 -0
  328. package/dist/v2/middleware/openapi/index.d.ts.map +1 -0
  329. package/dist/v2/middleware/process-ip/index.d.ts +3 -0
  330. package/dist/v2/middleware/process-ip/index.d.ts.map +1 -0
  331. package/dist/v2/router.cjs +1275 -0
  332. package/dist/v2/router.d.ts +280 -0
  333. package/dist/v2/router.d.ts.map +1 -0
  334. package/dist/v2/router.js +1231 -0
  335. package/dist/v2/types/api.d.ts +27 -0
  336. package/dist/v2/types/api.d.ts.map +1 -0
  337. package/dist/v2/types/index.cjs +40 -0
  338. package/dist/v2/types/index.d.ts +104 -0
  339. package/dist/v2/types/index.d.ts.map +1 -0
  340. package/dist/v2/types/index.js +6 -0
  341. package/dist/v2/utils/create-telemetry-options.d.ts +28 -0
  342. package/dist/v2/utils/create-telemetry-options.d.ts.map +1 -0
  343. package/dist/v2/utils/env.d.ts +60 -0
  344. package/dist/v2/utils/env.d.ts.map +1 -0
  345. package/dist/v2/utils/index.d.ts +3 -0
  346. package/dist/v2/utils/index.d.ts.map +1 -0
  347. package/dist/v2/utils/logger.d.ts +16 -0
  348. package/dist/v2/utils/logger.d.ts.map +1 -0
  349. package/dist/version.d.ts +1 -1
  350. package/package.json +106 -15
  351. package/readme.json +30 -0
  352. package/rslib.config.ts +13 -14
  353. package/src/__tests__/server.test.ts +1 -1
  354. package/src/contracts/consent/post.contract.test.ts +3 -8
  355. package/src/contracts/consent/post.contract.ts +13 -13
  356. package/src/contracts/consent/show-banner.contract.test.ts +9 -0
  357. package/src/contracts/consent/show-banner.contract.ts +2 -0
  358. package/src/contracts/consent/verify.contract.ts +19 -23
  359. package/src/core.ts +7 -0
  360. package/src/handlers/consent/show-banner/handler.ts +12 -9
  361. package/src/handlers/consent/show-banner/translations.ts +2 -2
  362. package/src/init.ts +9 -6
  363. package/src/middleware/openapi/index.ts +2 -2
  364. package/src/pkgs/api-router/hooks/__tests__/processor.test.ts +1 -1
  365. package/src/pkgs/data-model/fields/index.ts +17 -22
  366. package/src/pkgs/data-model/fields/zod-fields.ts +14 -26
  367. package/src/pkgs/data-model/hooks/index.ts +3 -2
  368. package/src/pkgs/data-model/index.ts +2 -4
  369. package/src/pkgs/data-model/schema/index.ts +6 -7
  370. package/src/pkgs/data-model/schema/schemas.ts +3 -3
  371. package/src/pkgs/db-adapters/adapters/drizzle-adapter/drizzle-adapter.ts +4 -1
  372. package/src/pkgs/db-adapters/adapters/index.ts +2 -2
  373. package/src/pkgs/db-adapters/adapters/kysely-adapter/index.ts +4 -4
  374. package/src/pkgs/db-adapters/adapters/kysely-adapter/kysely-adapter.ts +4 -5
  375. package/src/pkgs/db-adapters/adapters/kysely-adapter/tests/postgres.test.ts +2 -4
  376. package/src/pkgs/db-adapters/adapters/kysely-adapter/tests/sqlite.test.ts +2 -3
  377. package/src/pkgs/db-adapters/adapters/kysely-adapter/tests/test-utils.ts +1 -6
  378. package/src/pkgs/db-adapters/adapters/memory-adapter/memory-adapter.ts +4 -1
  379. package/src/pkgs/db-adapters/adapters/prisma-adapter/index.ts +1 -1
  380. package/src/pkgs/db-adapters/adapters/prisma-adapter/prisma-adapter.ts +5 -2
  381. package/src/pkgs/db-adapters/index.ts +12 -13
  382. package/src/pkgs/migrations/get-migration.ts +4 -2
  383. package/src/pkgs/migrations/get-schema/get-schema.ts +0 -1
  384. package/src/pkgs/migrations/get-schema/process-fields.ts +1 -1
  385. package/src/pkgs/migrations/get-schema/process-tables.ts +0 -2
  386. package/src/pkgs/migrations/index.ts +7 -8
  387. package/src/pkgs/results/__tests__/error-codes.test.ts +2 -2
  388. package/src/pkgs/results/index.ts +22 -27
  389. package/src/pkgs/results/orpc-error-handler.ts +1 -1
  390. package/src/pkgs/results/results/result-helpers.ts +1 -1
  391. package/src/pkgs/types/index.ts +4 -4
  392. package/src/pkgs/types/options.ts +10 -3
  393. package/src/pkgs/utils/index.ts +1 -1
  394. package/src/pkgs/utils/logger.ts +1 -1
  395. package/src/schema/audit-log/schema.ts +3 -3
  396. package/src/schema/consent/schema.ts +4 -4
  397. package/src/schema/consent-policy/schema.ts +3 -3
  398. package/src/schema/consent-purpose/schema.ts +4 -4
  399. package/src/schema/consent-record/schema.ts +3 -3
  400. package/src/schema/definition.ts +1 -1
  401. package/src/schema/domain/schema.ts +5 -5
  402. package/src/schema/index.ts +14 -17
  403. package/src/schema/subject/schema.ts +3 -3
  404. package/src/schema/types.ts +1 -1
  405. package/src/testing/contract-testing.ts +15 -52
  406. package/src/types/index.ts +8 -8
  407. package/src/types/options.ts +2 -3
  408. package/src/v2/contracts/consent/index.test.ts +5 -0
  409. package/src/v2/contracts/consent/index.ts +9 -0
  410. package/src/v2/contracts/consent/post.contract.test.ts +521 -0
  411. package/src/v2/contracts/consent/post.contract.ts +155 -0
  412. package/src/v2/contracts/consent/show-banner.contract.test.ts +252 -0
  413. package/src/v2/contracts/consent/show-banner.contract.ts +73 -0
  414. package/src/v2/contracts/consent/verify.contract.test.ts +185 -0
  415. package/src/v2/contracts/consent/verify.contract.ts +122 -0
  416. package/src/v2/contracts/index.ts +20 -0
  417. package/src/v2/contracts/meta/index.test.ts +5 -0
  418. package/src/v2/contracts/meta/index.ts +5 -0
  419. package/src/v2/contracts/meta/status.contract.test.ts +226 -0
  420. package/src/v2/contracts/meta/status.contract.ts +34 -0
  421. package/src/v2/contracts/shared/jurisdiction.schema.ts +30 -0
  422. package/src/v2/contracts/test.utils.ts +400 -0
  423. package/src/v2/core.ts +379 -0
  424. package/src/v2/db/adapters/drizzle.ts +1 -0
  425. package/src/v2/db/adapters/index.ts +1 -0
  426. package/src/v2/db/adapters/kysely.ts +1 -0
  427. package/src/v2/db/adapters/mongo.ts +1 -0
  428. package/src/v2/db/adapters/prisma.ts +1 -0
  429. package/src/v2/db/adapters/typeorm.ts +1 -0
  430. package/src/v2/db/migrator/index.ts +80 -0
  431. package/src/v2/db/registry/audit-log.test.ts +77 -0
  432. package/src/v2/db/registry/audit-log.ts +46 -0
  433. package/src/v2/db/registry/consent-policy.test.ts +778 -0
  434. package/src/v2/db/registry/consent-policy.ts +74 -0
  435. package/src/v2/db/registry/consent-purpose.test.ts +485 -0
  436. package/src/v2/db/registry/consent-purpose.ts +41 -0
  437. package/src/v2/db/registry/consent.test.ts +843 -0
  438. package/src/v2/db/registry/consent.ts +42 -0
  439. package/src/v2/db/registry/domain.test.ts +463 -0
  440. package/src/v2/db/registry/domain.ts +51 -0
  441. package/src/v2/db/registry/index.ts +18 -0
  442. package/src/v2/db/registry/subject.test.ts +497 -0
  443. package/src/v2/db/registry/subject.ts +101 -0
  444. package/src/v2/db/registry/types.ts +10 -0
  445. package/src/v2/db/registry/utils/generate-id.test.ts +217 -0
  446. package/src/v2/db/registry/utils/generate-id.ts +134 -0
  447. package/src/v2/db/registry/utils.ts +134 -0
  448. package/src/v2/db/schema/1.0.0/audit-log.ts +32 -0
  449. package/src/v2/db/schema/1.0.0/consent-policy.ts +41 -0
  450. package/src/v2/db/schema/1.0.0/consent-purpose.ts +30 -0
  451. package/src/v2/db/schema/1.0.0/consent-record.ts +22 -0
  452. package/src/v2/db/schema/1.0.0/consent.ts +38 -0
  453. package/src/v2/db/schema/1.0.0/domain.ts +26 -0
  454. package/src/v2/db/schema/1.0.0/index.ts +56 -0
  455. package/src/v2/db/schema/1.0.0/subject.ts +26 -0
  456. package/src/v2/db/schema/index.ts +9 -0
  457. package/src/v2/define-config.ts +5 -0
  458. package/src/v2/handlers/consent/index.ts +9 -0
  459. package/src/v2/handlers/consent/post.handler.ts +254 -0
  460. package/src/v2/handlers/consent/show-banner/geo.test.ts +281 -0
  461. package/src/v2/handlers/consent/show-banner/geo.ts +96 -0
  462. package/src/v2/handlers/consent/show-banner/handler.test.ts +374 -0
  463. package/src/v2/handlers/consent/show-banner/handler.ts +123 -0
  464. package/src/v2/handlers/consent/show-banner/translations.test.ts +121 -0
  465. package/src/v2/handlers/consent/show-banner/translations.ts +79 -0
  466. package/src/v2/handlers/consent/verify.handler.ts +288 -0
  467. package/src/v2/handlers/meta/index.ts +5 -0
  468. package/src/v2/handlers/meta/status.handler.ts +43 -0
  469. package/src/v2/init.test.ts +114 -0
  470. package/src/v2/init.ts +126 -0
  471. package/src/v2/middleware/cors/cors.test.ts +111 -0
  472. package/src/v2/middleware/cors/cors.ts +192 -0
  473. package/src/v2/middleware/cors/index.ts +30 -0
  474. package/src/v2/middleware/cors/is-origin-trusted.test.ts +104 -0
  475. package/src/v2/middleware/cors/is-origin-trusted.ts +126 -0
  476. package/src/v2/middleware/cors/process-cors.ts +91 -0
  477. package/src/v2/middleware/openapi/config.ts +27 -0
  478. package/src/v2/middleware/openapi/handlers.ts +132 -0
  479. package/src/v2/middleware/openapi/index.ts +11 -0
  480. package/src/v2/middleware/process-ip/index.ts +39 -0
  481. package/src/v2/router.ts +8 -0
  482. package/src/v2/types/api.ts +32 -0
  483. package/src/v2/types/index.ts +121 -0
  484. package/src/v2/utils/create-telemetry-options.ts +115 -0
  485. package/src/v2/utils/env.ts +84 -0
  486. package/src/v2/utils/index.ts +2 -0
  487. package/src/v2/utils/logger.ts +38 -0
  488. package/src/version.ts +1 -1
  489. package/vitest.config.ts +11 -2
@@ -0,0 +1,96 @@
1
+ import {
2
+ type JurisdictionCode,
3
+ JurisdictionMessages,
4
+ } from '~/v2/contracts/shared/jurisdiction.schema';
5
+
6
+ /**
7
+ * Determines if a consent banner should be shown based on country code
8
+ * and returns appropriate jurisdiction information
9
+ */
10
+ export function checkJurisdiction(countryCode: string | null) {
11
+ // Country code sets for different jurisdictions
12
+ const jurisdictions = {
13
+ EU: new Set([
14
+ 'AT',
15
+ 'BE',
16
+ 'BG',
17
+ 'HR',
18
+ 'CY',
19
+ 'CZ',
20
+ 'DK',
21
+ 'EE',
22
+ 'FI',
23
+ 'FR',
24
+ 'DE',
25
+ 'GR',
26
+ 'HU',
27
+ 'IE',
28
+ 'IT',
29
+ 'LV',
30
+ 'LT',
31
+ 'LU',
32
+ 'MT',
33
+ 'NL',
34
+ 'PL',
35
+ 'PT',
36
+ 'RO',
37
+ 'SK',
38
+ 'SI',
39
+ 'ES',
40
+ 'SE',
41
+ ]),
42
+ EEA: new Set(['IS', 'NO', 'LI']),
43
+ UK: new Set(['GB']),
44
+ CH: new Set(['CH']),
45
+ BR: new Set(['BR']),
46
+ CA: new Set(['CA']),
47
+ AU: new Set(['AU']),
48
+ JP: new Set(['JP']),
49
+ KR: new Set(['KR']),
50
+ };
51
+
52
+ // Default to no jurisdiction, but show banner
53
+ let showConsentBanner = true;
54
+ let jurisdictionCode: JurisdictionCode = 'NONE';
55
+
56
+ // Check country code against jurisdiction sets
57
+ if (countryCode) {
58
+ // Normalize country code to uppercase for case-insensitive comparison
59
+ const normalizedCountryCode = countryCode.toUpperCase();
60
+
61
+ // Default to false as we don't know if it fits any jurisdiction yet
62
+ showConsentBanner = false;
63
+
64
+ // Map jurisdiction sets to their respective codes
65
+ const jurisdictionMap = [
66
+ {
67
+ sets: [jurisdictions.EU, jurisdictions.EEA, jurisdictions.UK],
68
+ code: 'GDPR',
69
+ },
70
+ { sets: [jurisdictions.CH], code: 'CH' },
71
+ { sets: [jurisdictions.BR], code: 'BR' },
72
+ { sets: [jurisdictions.CA], code: 'PIPEDA' },
73
+ { sets: [jurisdictions.AU], code: 'AU' },
74
+ { sets: [jurisdictions.JP], code: 'APPI' },
75
+ { sets: [jurisdictions.KR], code: 'PIPA' },
76
+ ] as const;
77
+
78
+ // Find matching jurisdiction
79
+ for (const { sets, code } of jurisdictionMap) {
80
+ if (sets.some((set) => set.has(normalizedCountryCode))) {
81
+ jurisdictionCode = code;
82
+ showConsentBanner = true;
83
+ break;
84
+ }
85
+ }
86
+ }
87
+
88
+ // Get corresponding message from shared schema
89
+ const message = JurisdictionMessages[jurisdictionCode];
90
+
91
+ return {
92
+ showConsentBanner,
93
+ jurisdictionCode,
94
+ message,
95
+ };
96
+ }
@@ -0,0 +1,374 @@
1
+ import { baseTranslations } from '@c15t/translations';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import type { C15TContext } from '~/v2/types';
4
+ import { showConsentBanner } from './handler';
5
+
6
+ // First, mock the oRPC handler
7
+ vi.mock('~/v2/contracts', () => ({
8
+ os: {
9
+ consent: {
10
+ showBanner: {
11
+ handler: (fn: typeof showConsentBanner) => fn,
12
+ },
13
+ },
14
+ },
15
+ }));
16
+
17
+ describe('Show Consent Banner Handler', () => {
18
+ // Helper to create mock context with headers
19
+ const createMockContext = (
20
+ headers: Record<string, string>,
21
+ advanced?: Partial<C15TContext['advanced']>
22
+ ) => {
23
+ return {
24
+ context: {
25
+ headers: new Headers(headers),
26
+ advanced: {
27
+ ...(advanced ?? {}),
28
+ },
29
+ },
30
+ };
31
+ };
32
+
33
+ describe('Header extraction', () => {
34
+ it('extracts country code from cf-ipcountry header', async () => {
35
+ //@ts-expect-error
36
+ const result = await showConsentBanner(
37
+ createMockContext({ 'cf-ipcountry': 'DE' })
38
+ );
39
+
40
+ expect(result.location.countryCode).toBe('DE');
41
+ });
42
+
43
+ it('falls back to alternative country code headers', async () => {
44
+ //@ts-expect-error
45
+ const result = await showConsentBanner(
46
+ createMockContext({ 'x-vercel-ip-country': 'FR' })
47
+ );
48
+
49
+ expect(result.location.countryCode).toBe('FR');
50
+ });
51
+
52
+ it('prioritizes cf-ipcountry over other headers when multiple are present', async () => {
53
+ //@ts-expect-error
54
+ const result = await showConsentBanner(
55
+ createMockContext({
56
+ 'cf-ipcountry': 'DE',
57
+ 'x-vercel-ip-country': 'FR',
58
+ })
59
+ );
60
+
61
+ expect(result.location.countryCode).toBe('DE');
62
+ });
63
+
64
+ it('extracts region code from headers', async () => {
65
+ //@ts-expect-error
66
+ const result = await showConsentBanner(
67
+ createMockContext({
68
+ 'cf-ipcountry': 'US',
69
+ 'x-vercel-ip-country-region': 'CA',
70
+ })
71
+ );
72
+
73
+ expect(result.location.countryCode).toBe('US');
74
+ expect(result.location.regionCode).toBe('CA');
75
+ });
76
+
77
+ it('handles missing headers gracefully', async () => {
78
+ //@ts-expect-error
79
+ const result = await showConsentBanner(createMockContext({}));
80
+
81
+ expect(result.location.countryCode).toBeNull();
82
+ expect(result.location.regionCode).toBeNull();
83
+ });
84
+
85
+ it('handles case-insensitive header names', async () => {
86
+ //@ts-expect-error
87
+ const result = await showConsentBanner(
88
+ createMockContext({ 'CF-IPCountry': 'GB' })
89
+ );
90
+
91
+ expect(result.location.countryCode).toBe('GB');
92
+ });
93
+ });
94
+
95
+ describe('Integration and response structure', () => {
96
+ it('returns properly structured response with all required fields', async () => {
97
+ //@ts-expect-error
98
+ const result = await showConsentBanner(
99
+ createMockContext({ 'cf-ipcountry': 'DE' })
100
+ );
101
+
102
+ // Verify structure
103
+ expect(result).toHaveProperty('showConsentBanner');
104
+ expect(result).toHaveProperty('jurisdiction');
105
+ expect(result).toHaveProperty('location');
106
+ expect(result).toHaveProperty('translations');
107
+
108
+ // Verify types
109
+ expect(typeof result.showConsentBanner).toBe('boolean');
110
+ expect(typeof result.jurisdiction.code).toBe('string');
111
+ expect(typeof result.jurisdiction.message).toBe('string');
112
+ expect(result.location.countryCode).toBe('DE');
113
+ expect(typeof result.translations.language).toBe('string');
114
+ expect(typeof result.translations.translations).toBe('object');
115
+ });
116
+
117
+ it('integrates geo logic correctly for regulated countries', async () => {
118
+ //@ts-expect-error
119
+ const result = await showConsentBanner(
120
+ createMockContext({ 'cf-ipcountry': 'DE' })
121
+ );
122
+
123
+ // Should show banner for regulated country
124
+ expect(result.showConsentBanner).toBe(true);
125
+ expect(result.jurisdiction.code).not.toBe('NONE');
126
+ });
127
+
128
+ it('integrates geo logic correctly for non-regulated countries', async () => {
129
+ //@ts-expect-error
130
+ const result = await showConsentBanner(
131
+ createMockContext({ 'cf-ipcountry': 'US' })
132
+ );
133
+
134
+ // Should not show banner for non-regulated country
135
+ expect(result.showConsentBanner).toBe(false);
136
+ expect(result.jurisdiction.code).toBe('NONE');
137
+ });
138
+
139
+ it('integrates translations correctly', async () => {
140
+ //@ts-expect-error
141
+ const result = await showConsentBanner(createMockContext({}));
142
+
143
+ expect(result.translations.translations).toStrictEqual(
144
+ baseTranslations.en
145
+ );
146
+ expect(result.translations.language).toBe('en');
147
+ });
148
+
149
+ it('handles custom translations when provided', async () => {
150
+ const customTranslations = {
151
+ en: { cookieBanner: { title: 'Custom Title' } },
152
+ };
153
+
154
+ //@ts-expect-error
155
+ const result = await showConsentBanner(
156
+ createMockContext({}, { customTranslations })
157
+ );
158
+
159
+ expect(result.translations.translations.cookieBanner.title).toBe(
160
+ 'Custom Title'
161
+ );
162
+ });
163
+
164
+ it('maintains consistency between location and jurisdiction', async () => {
165
+ // Test that the country code extracted matches the jurisdiction determination
166
+ //@ts-expect-error
167
+ const result = await showConsentBanner(
168
+ createMockContext({ 'cf-ipcountry': 'CH' })
169
+ );
170
+
171
+ expect(result.location.countryCode).toBe('CH');
172
+ expect(result.jurisdiction.code).toBe('CH');
173
+ expect(result.showConsentBanner).toBe(true);
174
+ });
175
+ });
176
+
177
+ describe('Geo location disabling', () => {
178
+ it('disables geo logic when disableGeoLocation is true for regulated country', async () => {
179
+ //@ts-expect-error
180
+ const result = await showConsentBanner(
181
+ createMockContext(
182
+ { 'cf-ipcountry': 'DE' }, // Normally would show banner
183
+ { disableGeoLocation: true }
184
+ )
185
+ );
186
+
187
+ // Should show banner despite being in a regulated country when geo-location is disabled
188
+ expect(result.showConsentBanner).toBe(true);
189
+ expect(result.jurisdiction.code).toBe('NONE');
190
+ expect(result.location.countryCode).toBeNull();
191
+ expect(result.location.regionCode).toBeNull();
192
+ });
193
+
194
+ it('disables geo logic when disableGeoLocation is true for non-regulated country', async () => {
195
+ //@ts-expect-error
196
+ const result = await showConsentBanner(
197
+ createMockContext(
198
+ { 'cf-ipcountry': 'US' }, // Normally would not show banner
199
+ { disableGeoLocation: true }
200
+ )
201
+ );
202
+
203
+ // Should still not show banner and have consistent response
204
+ expect(result.showConsentBanner).toBe(true);
205
+ expect(result.jurisdiction.code).toBe('NONE');
206
+ expect(result.location.countryCode).toBeNull();
207
+ expect(result.location.regionCode).toBeNull();
208
+ });
209
+
210
+ it('still provides translations when geo location is disabled', async () => {
211
+ //@ts-expect-error
212
+ const result = await showConsentBanner(
213
+ createMockContext(
214
+ { 'cf-ipcountry': 'DE' },
215
+ { disableGeoLocation: true }
216
+ )
217
+ );
218
+
219
+ // Translations should still be provided
220
+ expect(result.translations.translations).toStrictEqual(
221
+ baseTranslations.en
222
+ );
223
+ expect(result.translations.language).toBe('en');
224
+ });
225
+
226
+ it('respects custom translations when geo location is disabled', async () => {
227
+ const customTranslations = {
228
+ en: { cookieBanner: { title: 'Custom Disabled Title' } },
229
+ };
230
+
231
+ //@ts-expect-error
232
+ const result = await showConsentBanner(
233
+ createMockContext(
234
+ { 'cf-ipcountry': 'DE' },
235
+ { disableGeoLocation: true, customTranslations }
236
+ )
237
+ );
238
+
239
+ expect(result.translations.translations.cookieBanner.title).toBe(
240
+ 'Custom Disabled Title'
241
+ );
242
+ expect(result.showConsentBanner).toBe(true);
243
+ expect(result.jurisdiction.code).toBe('NONE');
244
+ });
245
+
246
+ it('ignores all geo headers when geo location is disabled', async () => {
247
+ //@ts-expect-error
248
+ const result = await showConsentBanner(
249
+ createMockContext(
250
+ {
251
+ 'cf-ipcountry': 'DE',
252
+ 'x-vercel-ip-country': 'FR',
253
+ 'x-vercel-ip-country-region': 'IDF',
254
+ },
255
+ { disableGeoLocation: true }
256
+ )
257
+ );
258
+
259
+ // Should ignore all geo headers
260
+ expect(result.location.countryCode).toBeNull();
261
+ expect(result.location.regionCode).toBeNull();
262
+ expect(result.showConsentBanner).toBe(true);
263
+ expect(result.jurisdiction.code).toBe('NONE');
264
+ });
265
+
266
+ it('applies normal geo logic when disableGeoLocation is false', async () => {
267
+ //@ts-expect-error
268
+ const result = await showConsentBanner(
269
+ createMockContext(
270
+ { 'cf-ipcountry': 'DE' },
271
+ { disableGeoLocation: false }
272
+ )
273
+ );
274
+
275
+ // Should apply normal geo logic
276
+ expect(result.showConsentBanner).toBe(true);
277
+ expect(result.jurisdiction.code).not.toBe('NONE');
278
+ expect(result.location.countryCode).toBe('DE');
279
+ });
280
+
281
+ it('applies normal geo logic when disableGeoLocation is undefined', async () => {
282
+ //@ts-expect-error
283
+ const result = await showConsentBanner(
284
+ createMockContext(
285
+ { 'cf-ipcountry': 'DE' },
286
+ { disableGeoLocation: undefined }
287
+ )
288
+ );
289
+
290
+ // Should apply normal geo logic when undefined
291
+ expect(result.showConsentBanner).toBe(true);
292
+ expect(result.jurisdiction.code).not.toBe('NONE');
293
+ expect(result.location.countryCode).toBe('DE');
294
+ });
295
+
296
+ it('maintains consistent response structure when geo is disabled', async () => {
297
+ //@ts-expect-error
298
+ const result = await showConsentBanner(
299
+ createMockContext(
300
+ { 'cf-ipcountry': 'DE' },
301
+ { disableGeoLocation: true }
302
+ )
303
+ );
304
+
305
+ // Verify the response has the same structure as normal responses
306
+ expect(result).toHaveProperty('showConsentBanner');
307
+ expect(result).toHaveProperty('jurisdiction');
308
+ expect(result).toHaveProperty('location');
309
+ expect(result).toHaveProperty('translations');
310
+
311
+ // Verify specific disabled values
312
+ expect(typeof result.showConsentBanner).toBe('boolean');
313
+ expect(typeof result.jurisdiction.code).toBe('string');
314
+ expect(typeof result.jurisdiction.message).toBe('string');
315
+ expect(result.location.countryCode).toBeNull();
316
+ expect(result.location.regionCode).toBeNull();
317
+ expect(typeof result.translations.language).toBe('string');
318
+ expect(typeof result.translations.translations).toBe('object');
319
+ });
320
+ });
321
+
322
+ describe('Edge cases and error handling', () => {
323
+ it('handles malformed headers gracefully', async () => {
324
+ //@ts-expect-error
325
+ const result = await showConsentBanner(
326
+ createMockContext({
327
+ 'cf-ipcountry': '', // Empty string
328
+ 'x-vercel-ip-country-region': ' ', // Whitespace
329
+ })
330
+ );
331
+
332
+ // Should handle gracefully without throwing
333
+ expect(result).toHaveProperty('showConsentBanner');
334
+ expect(result).toHaveProperty('jurisdiction');
335
+ expect(result).toHaveProperty('location');
336
+ expect(result).toHaveProperty('translations');
337
+ });
338
+
339
+ it('handles undefined context options gracefully', async () => {
340
+ const contextWithoutOptions = {
341
+ context: {
342
+ headers: new Headers({ 'cf-ipcountry': 'DE' }),
343
+ options: {}, // No advanced options
344
+ },
345
+ };
346
+
347
+ //@ts-expect-error
348
+ const result = await showConsentBanner(contextWithoutOptions);
349
+
350
+ expect(result.translations.translations).toStrictEqual(
351
+ baseTranslations.en
352
+ );
353
+ });
354
+
355
+ it('handles malformed headers gracefully when geo is disabled', async () => {
356
+ //@ts-expect-error
357
+ const result = await showConsentBanner(
358
+ createMockContext(
359
+ {
360
+ 'cf-ipcountry': '', // Empty string
361
+ 'x-vercel-ip-country-region': ' ', // Whitespace
362
+ },
363
+ { disableGeoLocation: true }
364
+ )
365
+ );
366
+
367
+ // Should handle gracefully and ignore malformed headers
368
+ expect(result.showConsentBanner).toBe(true);
369
+ expect(result.jurisdiction.code).toBe('NONE');
370
+ expect(result.location.countryCode).toBeNull();
371
+ expect(result.location.regionCode).toBeNull();
372
+ });
373
+ });
374
+ });
@@ -0,0 +1,123 @@
1
+ import type { Translations } from '@c15t/translations';
2
+ import { os } from '~/v2/contracts';
3
+ import {
4
+ type JurisdictionCode,
5
+ JurisdictionMessages,
6
+ } from '~/v2/contracts/shared/jurisdiction.schema';
7
+ import type { Branding, C15TContext } from '~/v2/types';
8
+ import { checkJurisdiction } from './geo';
9
+ import { getTranslations } from './translations';
10
+
11
+ function getHeaders(headers: Headers | undefined) {
12
+ if (!headers) {
13
+ return {
14
+ countryCode: null,
15
+ regionCode: null,
16
+ acceptLanguage: null,
17
+ };
18
+ }
19
+
20
+ // Add this conversion to ensure headers are always string or null
21
+ const normalizeHeader = (
22
+ value: string | string[] | null | undefined
23
+ ): string | null => {
24
+ if (!value) {
25
+ return null;
26
+ }
27
+
28
+ return Array.isArray(value) ? (value[0] ?? null) : value;
29
+ };
30
+
31
+ const countryCode =
32
+ normalizeHeader(headers.get('x-c15t-country')) ??
33
+ normalizeHeader(headers.get('cf-ipcountry')) ??
34
+ normalizeHeader(headers.get('x-vercel-ip-country')) ??
35
+ normalizeHeader(headers.get('x-amz-cf-ipcountry')) ??
36
+ normalizeHeader(headers.get('x-country-code'));
37
+
38
+ const regionCode =
39
+ normalizeHeader(headers.get('x-c15t-region')) ??
40
+ normalizeHeader(headers.get('x-vercel-ip-country-region')) ??
41
+ normalizeHeader(headers.get('x-region-code'));
42
+
43
+ // Get preferred language from Accept-Language header
44
+ const acceptLanguage = normalizeHeader(headers.get('accept-language'));
45
+
46
+ return {
47
+ countryCode,
48
+ regionCode,
49
+ acceptLanguage,
50
+ };
51
+ }
52
+
53
+ function buildResponse({
54
+ shouldShowBanner,
55
+ jurisdiction,
56
+ location,
57
+ acceptLanguage,
58
+ customTranslations,
59
+ branding = 'c15t',
60
+ }: {
61
+ shouldShowBanner: boolean;
62
+
63
+ jurisdiction: {
64
+ code: JurisdictionCode;
65
+ message: string;
66
+ };
67
+ location: { countryCode: string | null; regionCode: string | null };
68
+ acceptLanguage: string | null;
69
+ customTranslations: Record<string, Partial<Translations>> | undefined;
70
+ branding?: Branding;
71
+ }) {
72
+ return {
73
+ showConsentBanner: shouldShowBanner,
74
+ jurisdiction,
75
+ location,
76
+ translations: getTranslations(acceptLanguage, customTranslations),
77
+ branding: branding,
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Handler for the show consent banner endpoint
83
+ * Determines if a user should see a consent banner based on their location
84
+ */
85
+ export const showConsentBanner = os.consent.showBanner.handler(
86
+ ({ context }) => {
87
+ const typedContext = context as C15TContext;
88
+ const { customTranslations, disableGeoLocation, branding } =
89
+ typedContext.advanced ?? {};
90
+ const { countryCode, regionCode, acceptLanguage } = getHeaders(
91
+ typedContext.headers
92
+ );
93
+
94
+ if (disableGeoLocation) {
95
+ return buildResponse({
96
+ shouldShowBanner: true,
97
+ jurisdiction: {
98
+ code: 'NONE',
99
+ message: JurisdictionMessages.NONE,
100
+ },
101
+ location: { countryCode: null, regionCode: null },
102
+ acceptLanguage,
103
+ customTranslations,
104
+ branding,
105
+ });
106
+ }
107
+
108
+ const { showConsentBanner, jurisdictionCode, message } =
109
+ checkJurisdiction(countryCode);
110
+
111
+ return buildResponse({
112
+ shouldShowBanner: showConsentBanner,
113
+ jurisdiction: {
114
+ code: jurisdictionCode,
115
+ message,
116
+ },
117
+ location: { countryCode, regionCode },
118
+ acceptLanguage,
119
+ customTranslations,
120
+ branding,
121
+ });
122
+ }
123
+ );
@@ -0,0 +1,121 @@
1
+ import { baseTranslations } from '@c15t/translations';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { getTranslations } from './translations';
4
+
5
+ describe('showBanner > getTranslations', () => {
6
+ it("should return 'en' translations when Accept-Language is null", () => {
7
+ const { translations, language } = getTranslations(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 } = getTranslations('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 } = getTranslations(
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 } = getTranslations(
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 } = getTranslations(
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 } = getTranslations(
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 } = getTranslations(
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 } = getTranslations('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
+ });