@c15t/backend 2.0.0-rc.4 → 2.0.0-rc.6

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 (327) hide show
  1. package/dist/302.js +473 -0
  2. package/dist/364.js +1140 -0
  3. package/dist/583.js +540 -0
  4. package/dist/cache.cjs +1 -1
  5. package/dist/cache.js +4 -415
  6. package/dist/core.cjs +849 -96
  7. package/dist/core.js +147 -1817
  8. package/dist/db/adapters/drizzle.cjs +1 -1
  9. package/dist/db/adapters/drizzle.js +1 -2
  10. package/dist/db/adapters/kysely.cjs +1 -1
  11. package/dist/db/adapters/kysely.js +1 -2
  12. package/dist/db/adapters/mongo.cjs +1 -1
  13. package/dist/db/adapters/mongo.js +1 -2
  14. package/dist/db/adapters/prisma.cjs +1 -1
  15. package/dist/db/adapters/prisma.js +1 -2
  16. package/dist/db/adapters/typeorm.cjs +1 -1
  17. package/dist/db/adapters/typeorm.js +1 -2
  18. package/dist/db/adapters.cjs +1 -1
  19. package/dist/db/migrator.cjs +1 -1
  20. package/dist/db/schema.cjs +38 -1
  21. package/dist/db/schema.js +33 -2
  22. package/dist/define-config.cjs +1 -1
  23. package/dist/edge.cjs +1106 -0
  24. package/dist/edge.js +190 -0
  25. package/dist/router.cjs +629 -81
  26. package/dist/router.js +1 -1509
  27. package/dist/types/index.cjs +1 -1
  28. package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
  29. package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
  30. package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
  31. package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
  32. package/{dist → dist-types}/cache/gvl-resolver.d.ts +1 -2
  33. package/{dist → dist-types}/cache/index.d.ts +0 -1
  34. package/{dist → dist-types}/cache/keys.d.ts +0 -1
  35. package/{dist → dist-types}/cache/types.d.ts +0 -1
  36. package/{dist → dist-types}/core.d.ts +8 -1
  37. package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
  38. package/{dist → dist-types}/db/registry/consent-policy.d.ts +0 -1
  39. package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
  40. package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
  41. package/{dist → dist-types}/db/registry/index.d.ts +22 -2
  42. package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
  43. package/{dist → dist-types}/db/registry/subject.d.ts +0 -1
  44. package/{dist → dist-types}/db/registry/types.d.ts +1 -2
  45. package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
  46. package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
  47. package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
  48. package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
  49. package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
  50. package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
  51. package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +2 -3
  52. package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
  53. package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -1
  54. package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -1
  55. package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +2 -3
  56. package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +2 -3
  57. package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +2 -3
  58. package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +6 -3
  59. package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +2 -3
  60. package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +432 -17
  61. package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
  62. package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +2 -3
  63. package/{dist → dist-types}/db/schema/index.d.ts +862 -33
  64. package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
  65. package/{dist → dist-types}/define-config.d.ts +0 -1
  66. package/dist-types/edge/index.d.ts +5 -0
  67. package/dist-types/edge/init-handler.d.ts +38 -0
  68. package/dist-types/edge/resolve-consent.d.ts +80 -0
  69. package/dist-types/edge/types.d.ts +13 -0
  70. package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
  71. package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
  72. package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
  73. package/{dist → dist-types}/handlers/init/index.d.ts +4 -5
  74. package/dist-types/handlers/init/policy.d.ts +26 -0
  75. package/dist-types/handlers/init/resolve-init.d.ts +44 -0
  76. package/dist-types/handlers/init/translations.d.ts +48 -0
  77. package/dist-types/handlers/policy/snapshot.d.ts +99 -0
  78. package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
  79. package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
  80. package/{dist → dist-types}/handlers/subject/get.handler.d.ts +0 -1
  81. package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
  82. package/{dist → dist-types}/handlers/subject/list.handler.d.ts +0 -1
  83. package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -1
  84. package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
  85. package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +0 -1
  86. package/{dist → dist-types}/init.d.ts +0 -1
  87. package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
  88. package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
  89. package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
  90. package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
  91. package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +1 -2
  92. package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
  93. package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
  94. package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
  95. package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
  96. package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
  97. package/dist-types/policies/builder.d.ts +127 -0
  98. package/dist-types/policies/defaults.d.ts +2 -0
  99. package/dist-types/policies/matchers.d.ts +3 -0
  100. package/{dist → dist-types}/router.d.ts +0 -1
  101. package/{dist → dist-types}/routes/consent.d.ts +0 -1
  102. package/{src/routes/index.ts → dist-types/routes/index.d.ts} +0 -1
  103. package/{dist → dist-types}/routes/init.d.ts +0 -1
  104. package/{dist → dist-types}/routes/status.d.ts +0 -1
  105. package/{dist → dist-types}/routes/subject.d.ts +0 -1
  106. package/{dist → dist-types}/types/api.d.ts +0 -1
  107. package/{dist → dist-types}/types/index.d.ts +110 -6
  108. package/dist-types/utils/background.d.ts +6 -0
  109. package/{dist → dist-types}/utils/create-telemetry-options.d.ts +0 -1
  110. package/{dist → dist-types}/utils/env.d.ts +0 -1
  111. package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
  112. package/{dist → dist-types}/utils/instrumentation.d.ts +0 -1
  113. package/{dist → dist-types}/utils/logger.d.ts +1 -2
  114. package/{dist → dist-types}/utils/metrics.d.ts +0 -1
  115. package/dist-types/version.d.ts +1 -0
  116. package/docs/README.md +49 -0
  117. package/docs/api/configuration.md +197 -0
  118. package/docs/api/endpoints.md +211 -0
  119. package/docs/guides/caching.md +85 -0
  120. package/docs/guides/database-setup.md +128 -0
  121. package/docs/guides/edge-deployment.md +248 -0
  122. package/docs/guides/framework-integration.md +142 -0
  123. package/docs/guides/iab-tcf.md +89 -0
  124. package/docs/guides/observability.md +96 -0
  125. package/docs/guides/policy-packs.md +396 -0
  126. package/docs/quickstart.md +129 -0
  127. package/package.json +45 -31
  128. package/.turbo/turbo-build.log +0 -49
  129. package/CHANGELOG.md +0 -123
  130. package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
  131. package/dist/cache/adapters/index.d.ts.map +0 -1
  132. package/dist/cache/adapters/memory.d.ts.map +0 -1
  133. package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
  134. package/dist/cache/gvl-resolver.d.ts.map +0 -1
  135. package/dist/cache/index.d.ts.map +0 -1
  136. package/dist/cache/keys.d.ts.map +0 -1
  137. package/dist/cache/types.d.ts.map +0 -1
  138. package/dist/core.d.ts.map +0 -1
  139. package/dist/db/adapters/drizzle.d.ts +0 -2
  140. package/dist/db/adapters/drizzle.d.ts.map +0 -1
  141. package/dist/db/adapters/index.d.ts +0 -2
  142. package/dist/db/adapters/index.d.ts.map +0 -1
  143. package/dist/db/adapters/kysely.d.ts +0 -2
  144. package/dist/db/adapters/kysely.d.ts.map +0 -1
  145. package/dist/db/adapters/mongo.d.ts +0 -2
  146. package/dist/db/adapters/mongo.d.ts.map +0 -1
  147. package/dist/db/adapters/prisma.d.ts +0 -2
  148. package/dist/db/adapters/prisma.d.ts.map +0 -1
  149. package/dist/db/adapters/typeorm.d.ts +0 -2
  150. package/dist/db/adapters/typeorm.d.ts.map +0 -1
  151. package/dist/db/migrator/index.d.ts.map +0 -1
  152. package/dist/db/registry/consent-policy.d.ts.map +0 -1
  153. package/dist/db/registry/consent-purpose.d.ts.map +0 -1
  154. package/dist/db/registry/domain.d.ts.map +0 -1
  155. package/dist/db/registry/index.d.ts.map +0 -1
  156. package/dist/db/registry/subject.d.ts.map +0 -1
  157. package/dist/db/registry/types.d.ts.map +0 -1
  158. package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
  159. package/dist/db/registry/utils.d.ts.map +0 -1
  160. package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
  161. package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
  162. package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
  163. package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
  164. package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
  165. package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
  166. package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
  167. package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
  168. package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
  169. package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
  170. package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
  171. package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
  172. package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
  173. package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
  174. package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
  175. package/dist/db/schema/index.d.ts.map +0 -1
  176. package/dist/db/tenant-scope.d.ts.map +0 -1
  177. package/dist/define-config.d.ts.map +0 -1
  178. package/dist/handlers/consent/check.handler.d.ts.map +0 -1
  179. package/dist/handlers/consent/index.d.ts +0 -12
  180. package/dist/handlers/consent/index.d.ts.map +0 -1
  181. package/dist/handlers/init/geo.d.ts.map +0 -1
  182. package/dist/handlers/init/index.d.ts.map +0 -1
  183. package/dist/handlers/init/translations.d.ts +0 -26
  184. package/dist/handlers/init/translations.d.ts.map +0 -1
  185. package/dist/handlers/status/index.d.ts +0 -7
  186. package/dist/handlers/status/index.d.ts.map +0 -1
  187. package/dist/handlers/status/status.handler.d.ts.map +0 -1
  188. package/dist/handlers/subject/get.handler.d.ts.map +0 -1
  189. package/dist/handlers/subject/index.d.ts +0 -10
  190. package/dist/handlers/subject/index.d.ts.map +0 -1
  191. package/dist/handlers/subject/list.handler.d.ts.map +0 -1
  192. package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
  193. package/dist/handlers/subject/post.handler.d.ts.map +0 -1
  194. package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
  195. package/dist/init.d.ts.map +0 -1
  196. package/dist/middleware/auth/index.d.ts.map +0 -1
  197. package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
  198. package/dist/middleware/cors/cors.d.ts.map +0 -1
  199. package/dist/middleware/cors/index.d.ts +0 -30
  200. package/dist/middleware/cors/index.d.ts.map +0 -1
  201. package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
  202. package/dist/middleware/cors/process-cors.d.ts.map +0 -1
  203. package/dist/middleware/openapi/config.d.ts.map +0 -1
  204. package/dist/middleware/openapi/handlers.d.ts.map +0 -1
  205. package/dist/middleware/openapi/index.d.ts +0 -12
  206. package/dist/middleware/openapi/index.d.ts.map +0 -1
  207. package/dist/middleware/process-ip/index.d.ts.map +0 -1
  208. package/dist/router.d.ts.map +0 -1
  209. package/dist/routes/consent.d.ts.map +0 -1
  210. package/dist/routes/index.d.ts +0 -10
  211. package/dist/routes/index.d.ts.map +0 -1
  212. package/dist/routes/init.d.ts.map +0 -1
  213. package/dist/routes/status.d.ts.map +0 -1
  214. package/dist/routes/subject.d.ts.map +0 -1
  215. package/dist/types/api.d.ts.map +0 -1
  216. package/dist/types/index.d.ts.map +0 -1
  217. package/dist/utils/create-telemetry-options.d.ts.map +0 -1
  218. package/dist/utils/env.d.ts.map +0 -1
  219. package/dist/utils/extract-error-message.d.ts.map +0 -1
  220. package/dist/utils/index.d.ts +0 -4
  221. package/dist/utils/index.d.ts.map +0 -1
  222. package/dist/utils/instrumentation.d.ts.map +0 -1
  223. package/dist/utils/logger.d.ts.map +0 -1
  224. package/dist/utils/metrics.d.ts.map +0 -1
  225. package/dist/version.d.ts +0 -2
  226. package/dist/version.d.ts.map +0 -1
  227. package/knip.json +0 -31
  228. package/rslib.config.ts +0 -93
  229. package/src/cache/adapters/cloudflare-kv.ts +0 -71
  230. package/src/cache/adapters/index.ts +0 -22
  231. package/src/cache/adapters/memory.ts +0 -111
  232. package/src/cache/adapters/upstash-redis.ts +0 -113
  233. package/src/cache/gvl-resolver.ts +0 -289
  234. package/src/cache/index.ts +0 -34
  235. package/src/cache/keys.ts +0 -68
  236. package/src/cache/types.ts +0 -66
  237. package/src/core.ts +0 -369
  238. package/src/db/migrator/index.ts +0 -80
  239. package/src/db/registry/consent-policy.test.ts +0 -451
  240. package/src/db/registry/consent-policy.ts +0 -82
  241. package/src/db/registry/consent-purpose.test.ts +0 -428
  242. package/src/db/registry/consent-purpose.ts +0 -61
  243. package/src/db/registry/domain.test.ts +0 -445
  244. package/src/db/registry/domain.ts +0 -91
  245. package/src/db/registry/index.ts +0 -14
  246. package/src/db/registry/subject.test.ts +0 -371
  247. package/src/db/registry/subject.ts +0 -126
  248. package/src/db/registry/types.ts +0 -10
  249. package/src/db/registry/utils/generate-id.test.ts +0 -216
  250. package/src/db/registry/utils/generate-id.ts +0 -133
  251. package/src/db/registry/utils.ts +0 -133
  252. package/src/db/schema/1.0.0/audit-log.ts +0 -15
  253. package/src/db/schema/1.0.0/consent-policy.ts +0 -14
  254. package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
  255. package/src/db/schema/1.0.0/consent-record.ts +0 -10
  256. package/src/db/schema/1.0.0/consent.ts +0 -20
  257. package/src/db/schema/1.0.0/domain.ts +0 -12
  258. package/src/db/schema/1.0.0/index.ts +0 -48
  259. package/src/db/schema/1.0.0/subject.ts +0 -11
  260. package/src/db/schema/2.0.0/audit-log.ts +0 -18
  261. package/src/db/schema/2.0.0/consent-policy.ts +0 -28
  262. package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
  263. package/src/db/schema/2.0.0/consent.ts +0 -28
  264. package/src/db/schema/2.0.0/domain.ts +0 -12
  265. package/src/db/schema/2.0.0/index.ts +0 -47
  266. package/src/db/schema/2.0.0/subject.ts +0 -13
  267. package/src/db/schema/index.ts +0 -15
  268. package/src/db/tenant-scope.test.ts +0 -747
  269. package/src/db/tenant-scope.ts +0 -103
  270. package/src/define-config.ts +0 -19
  271. package/src/handlers/consent/check.handler.ts +0 -126
  272. package/src/handlers/init/geo.test.ts +0 -317
  273. package/src/handlers/init/geo.ts +0 -195
  274. package/src/handlers/init/index.test.ts +0 -205
  275. package/src/handlers/init/index.ts +0 -114
  276. package/src/handlers/init/translations.test.ts +0 -121
  277. package/src/handlers/init/translations.ts +0 -69
  278. package/src/handlers/status/status.handler.test.ts +0 -155
  279. package/src/handlers/status/status.handler.ts +0 -51
  280. package/src/handlers/subject/get.handler.ts +0 -92
  281. package/src/handlers/subject/list.handler.ts +0 -92
  282. package/src/handlers/subject/patch.handler.ts +0 -119
  283. package/src/handlers/subject/post.handler.test.ts +0 -294
  284. package/src/handlers/subject/post.handler.ts +0 -268
  285. package/src/handlers/utils/consent-enrichment.test.ts +0 -380
  286. package/src/handlers/utils/consent-enrichment.ts +0 -218
  287. package/src/init.test.ts +0 -122
  288. package/src/init.ts +0 -88
  289. package/src/middleware/auth/index.ts +0 -11
  290. package/src/middleware/auth/validate-api-key.test.ts +0 -86
  291. package/src/middleware/auth/validate-api-key.ts +0 -107
  292. package/src/middleware/cors/cors.test.ts +0 -135
  293. package/src/middleware/cors/cors.ts +0 -186
  294. package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
  295. package/src/middleware/cors/is-origin-trusted.ts +0 -130
  296. package/src/middleware/cors/process-cors.ts +0 -91
  297. package/src/middleware/openapi/config.ts +0 -29
  298. package/src/middleware/openapi/handlers.ts +0 -34
  299. package/src/middleware/process-ip/index.test.ts +0 -193
  300. package/src/middleware/process-ip/index.ts +0 -199
  301. package/src/router.ts +0 -15
  302. package/src/routes/consent.ts +0 -52
  303. package/src/routes/init.ts +0 -105
  304. package/src/routes/status.ts +0 -46
  305. package/src/routes/subject.ts +0 -152
  306. package/src/types/api.ts +0 -48
  307. package/src/types/index.ts +0 -391
  308. package/src/utils/create-telemetry-options.test.ts +0 -286
  309. package/src/utils/create-telemetry-options.ts +0 -229
  310. package/src/utils/env.ts +0 -84
  311. package/src/utils/extract-error-message.ts +0 -21
  312. package/src/utils/instrumentation.test.ts +0 -183
  313. package/src/utils/instrumentation.ts +0 -194
  314. package/src/utils/logger.ts +0 -41
  315. package/src/utils/metrics.test.ts +0 -311
  316. package/src/utils/metrics.ts +0 -402
  317. package/src/utils/telemetry-pii.test.ts +0 -323
  318. package/src/version.ts +0 -2
  319. package/tsconfig.json +0 -11
  320. package/vitest.config.ts +0 -28
  321. /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
  322. /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
  323. /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
  324. /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
  325. /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
  326. /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
  327. /package/{src/utils/index.ts → dist-types/utils/index.d.ts} +0 -0
package/dist/core.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  var __webpack_modules__ = {
3
- "./src/db/schema/index.ts" (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3
+ "./src/db/schema/index.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
4
4
  __webpack_require__.d(__webpack_exports__, {
5
5
  DB: ()=>DB
6
6
  });
@@ -172,6 +172,8 @@ var __webpack_modules__ = {
172
172
  tcString: (0, schema_namespaceObject.column)('tcString', 'string').nullable(),
173
173
  uiSource: (0, schema_namespaceObject.column)('uiSource', 'string').nullable(),
174
174
  consentAction: (0, schema_namespaceObject.column)('consentAction', 'string').nullable(),
175
+ runtimePolicyDecisionId: (0, schema_namespaceObject.column)('runtimePolicyDecisionId', 'string').nullable(),
176
+ runtimePolicySource: (0, schema_namespaceObject.column)('runtimePolicySource', 'string').nullable(),
175
177
  tenantId: (0, schema_namespaceObject.column)('tenantId', 'string').nullable()
176
178
  });
177
179
  const consent_policy_consentPolicyTable = (0, schema_namespaceObject.table)('consentPolicy', {
@@ -197,6 +199,27 @@ var __webpack_modules__ = {
197
199
  updatedAt: (0, schema_namespaceObject.column)('updatedAt', 'timestamp').defaultTo$('now'),
198
200
  tenantId: (0, schema_namespaceObject.column)('tenantId', 'string').nullable()
199
201
  });
202
+ const runtimePolicyDecisionTable = (0, schema_namespaceObject.table)('runtimePolicyDecision', {
203
+ id: (0, schema_namespaceObject.idColumn)('id', 'varchar(255)'),
204
+ tenantId: (0, schema_namespaceObject.column)('tenantId', 'string').nullable(),
205
+ policyId: (0, schema_namespaceObject.column)('policyId', 'string'),
206
+ fingerprint: (0, schema_namespaceObject.column)('fingerprint', 'string'),
207
+ matchedBy: (0, schema_namespaceObject.column)('matchedBy', 'string'),
208
+ countryCode: (0, schema_namespaceObject.column)('countryCode', 'string').nullable(),
209
+ regionCode: (0, schema_namespaceObject.column)('regionCode', 'string').nullable(),
210
+ jurisdiction: (0, schema_namespaceObject.column)('jurisdiction', 'string'),
211
+ language: (0, schema_namespaceObject.column)('language', 'string').nullable(),
212
+ model: (0, schema_namespaceObject.column)('model', 'string'),
213
+ policyI18n: (0, schema_namespaceObject.column)('policyI18n', 'json').nullable(),
214
+ uiMode: (0, schema_namespaceObject.column)('uiMode', 'string').nullable(),
215
+ bannerUi: (0, schema_namespaceObject.column)('bannerUi', 'json').nullable(),
216
+ dialogUi: (0, schema_namespaceObject.column)('dialogUi', 'json').nullable(),
217
+ categories: (0, schema_namespaceObject.column)('categories', 'json').nullable(),
218
+ preselectedCategories: (0, schema_namespaceObject.column)('preselectedCategories', 'json').nullable(),
219
+ proofConfig: (0, schema_namespaceObject.column)('proofConfig', 'json').nullable(),
220
+ dedupeKey: (0, schema_namespaceObject.column)('dedupeKey', 'string').unique(),
221
+ createdAt: (0, schema_namespaceObject.column)('createdAt', 'timestamp').defaultTo$('now')
222
+ });
200
223
  const subject_subjectTable = (0, schema_namespaceObject.table)('subject', {
201
224
  id: (0, schema_namespaceObject.idColumn)('id', 'varchar(255)'),
202
225
  externalId: (0, schema_namespaceObject.column)('externalId', 'string').nullable(),
@@ -211,6 +234,7 @@ var __webpack_modules__ = {
211
234
  subject: subject_subjectTable,
212
235
  domain: domain_domainTable,
213
236
  consentPolicy: consent_policy_consentPolicyTable,
237
+ runtimePolicyDecision: runtimePolicyDecisionTable,
214
238
  consentPurpose: consent_purpose_consentPurposeTable,
215
239
  consent: consent_consentTable,
216
240
  auditLog: audit_log_auditLogTable
@@ -226,6 +250,9 @@ var __webpack_modules__ = {
226
250
  consentPolicy: ({ many })=>({
227
251
  consents: many('consent')
228
252
  }),
253
+ runtimePolicyDecision: ({ many })=>({
254
+ consents: many('consent')
255
+ }),
229
256
  consentPurpose: ()=>({}),
230
257
  consent: ({ one })=>({
231
258
  subject: one('subject', [
@@ -239,6 +266,10 @@ var __webpack_modules__ = {
239
266
  policy: one('consentPolicy', [
240
267
  'policyId',
241
268
  'id'
269
+ ]).foreignKey(),
270
+ runtimePolicyDecision: one('runtimePolicyDecision', [
271
+ 'runtimePolicyDecisionId',
272
+ 'id'
242
273
  ]).foreignKey()
243
274
  }),
244
275
  auditLog: ({ one })=>({
@@ -263,7 +294,7 @@ var __webpack_modules__ = {
263
294
  ]
264
295
  });
265
296
  },
266
- "./src/define-config.ts" (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
297
+ "./src/define-config.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
267
298
  __webpack_require__.d(__webpack_exports__, {
268
299
  defineConfig: ()=>defineConfig
269
300
  });
@@ -305,7 +336,7 @@ function __webpack_require__(moduleId) {
305
336
  })();
306
337
  (()=>{
307
338
  __webpack_require__.r = (exports1)=>{
308
- if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
339
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
309
340
  value: 'Module'
310
341
  });
311
342
  Object.defineProperty(exports1, '__esModule', {
@@ -319,7 +350,15 @@ var __webpack_exports__ = {};
319
350
  __webpack_require__.d(__webpack_exports__, {
320
351
  version: ()=>version_version,
321
352
  c15tInstance: ()=>c15tInstance,
322
- defineConfig: ()=>define_config.defineConfig
353
+ EEA_COUNTRY_CODES: ()=>types_namespaceObject.EEA_COUNTRY_CODES,
354
+ EU_COUNTRY_CODES: ()=>types_namespaceObject.EU_COUNTRY_CODES,
355
+ UK_COUNTRY_CODES: ()=>types_namespaceObject.UK_COUNTRY_CODES,
356
+ policyMatchers: ()=>types_namespaceObject.policyMatchers,
357
+ policyBuilder: ()=>policyBuilder,
358
+ policyPackPresets: ()=>schema_.policyPackPresets,
359
+ inspectPolicies: ()=>inspectPolicies,
360
+ defineConfig: ()=>define_config.defineConfig,
361
+ POLICY_MATCH_DATASET_VERSION: ()=>types_namespaceObject.POLICY_MATCH_DATASET_VERSION
323
362
  });
324
363
  const logger_namespaceObject = require("@c15t/logger");
325
364
  const api_namespaceObject = require("@opentelemetry/api");
@@ -568,7 +607,137 @@ var __webpack_exports__ = {};
568
607
  }
569
608
  return null;
570
609
  }
571
- const version_version = '2.0.0-rc.4';
610
+ const types_namespaceObject = require("@c15t/schema/types");
611
+ const translations_namespaceObject = require("@c15t/translations");
612
+ const all_namespaceObject = require("@c15t/translations/all");
613
+ const DEFAULT_PROFILE = 'default';
614
+ const warnedKeys = new Set();
615
+ function isSupportedBaseLanguage(lang) {
616
+ return lang in all_namespaceObject.baseTranslations;
617
+ }
618
+ function warnOnce(logger, key, message, metadata) {
619
+ if (!logger || warnedKeys.has(key)) return;
620
+ warnedKeys.add(key);
621
+ logger.warn(message, metadata);
622
+ }
623
+ function normalizeLanguage(value) {
624
+ if (!value) return;
625
+ const normalized = value.split(',')[0]?.split(';')[0]?.trim().toLowerCase();
626
+ if (!normalized) return;
627
+ return normalized.split('-')[0] ?? void 0;
628
+ }
629
+ function normalizeProfiles(params) {
630
+ const profiles = params.i18n?.messages;
631
+ const legacy = params.customTranslations;
632
+ if (profiles && Object.keys(profiles).length > 0) {
633
+ if (legacy && Object.keys(legacy).length > 0) warnOnce(params.logger, 'i18n.customTranslations.ignored', '`customTranslations` is deprecated and ignored when `i18n.messages` is configured.');
634
+ return profiles;
635
+ }
636
+ if (legacy && Object.keys(legacy).length > 0) {
637
+ warnOnce(params.logger, 'i18n.customTranslations.deprecated', '`customTranslations` is deprecated. Use `i18n.messages` instead.');
638
+ return {
639
+ [DEFAULT_PROFILE]: {
640
+ translations: legacy
641
+ }
642
+ };
643
+ }
644
+ return {};
645
+ }
646
+ function buildCandidates(input) {
647
+ const raw = [
648
+ {
649
+ language: input.language,
650
+ reason: 'profile_language'
651
+ },
652
+ {
653
+ language: input.fallbackLanguage,
654
+ reason: 'profile_fallback'
655
+ }
656
+ ];
657
+ const dedupe = new Set();
658
+ return raw.filter((candidate)=>{
659
+ const key = candidate.language;
660
+ if (dedupe.has(key)) return false;
661
+ dedupe.add(key);
662
+ return true;
663
+ });
664
+ }
665
+ function getProfileLanguages(profiles, profile) {
666
+ return Object.keys(profiles[profile]?.translations ?? {}).sort();
667
+ }
668
+ function getSelectableLanguages(input) {
669
+ return getProfileLanguages(input.profiles, input.profile);
670
+ }
671
+ function resolveFallbackLanguage(input) {
672
+ const configuredFallbackLanguage = normalizeLanguage(input.profile?.fallbackLanguage) ?? 'en';
673
+ const profileLanguages = Object.keys(input.profile?.translations ?? {}).sort();
674
+ if (profileLanguages.includes(configuredFallbackLanguage)) return configuredFallbackLanguage;
675
+ if (profileLanguages.includes('en')) return 'en';
676
+ return profileLanguages[0] ?? configuredFallbackLanguage;
677
+ }
678
+ function resolveActiveProfile(input) {
679
+ const requestedProfile = input.policyProfile ?? input.defaultProfile;
680
+ if (input.profiles[requestedProfile]) return requestedProfile;
681
+ if (input.policyProfile) warnOnce(input.logger, `i18n.profile.missing:${requestedProfile}`, `Policy i18n profile '${requestedProfile}' does not exist. Falling back to default profile '${input.defaultProfile}'.`);
682
+ return input.defaultProfile;
683
+ }
684
+ function validateMessages(options) {
685
+ return (0, types_namespaceObject.validatePolicyI18nConfig)({
686
+ customTranslations: options.customTranslations,
687
+ i18n: options.i18n,
688
+ policies: options.policies
689
+ });
690
+ }
691
+ function translations_getTranslationsData(acceptLanguage, customTranslations, options) {
692
+ const profiles = normalizeProfiles({
693
+ customTranslations,
694
+ i18n: options?.i18n,
695
+ logger: options?.logger
696
+ });
697
+ const defaultProfile = options?.i18n?.defaultProfile ?? DEFAULT_PROFILE;
698
+ const profile = resolveActiveProfile({
699
+ profiles,
700
+ defaultProfile,
701
+ policyProfile: options?.policyI18n?.messageProfile,
702
+ logger: options?.logger
703
+ });
704
+ const configuredLanguages = Object.keys(profiles).length > 0 ? getSelectableLanguages({
705
+ profiles,
706
+ profile
707
+ }) : Object.keys(all_namespaceObject.baseTranslations);
708
+ const fallbackLanguage = Object.keys(profiles).length > 0 ? resolveFallbackLanguage({
709
+ profile: profiles[profile]
710
+ }) : 'en';
711
+ const policyLanguage = normalizeLanguage(options?.policyI18n?.language);
712
+ const requestedLanguage = policyLanguage ?? (0, translations_namespaceObject.selectLanguage)(configuredLanguages, {
713
+ header: acceptLanguage,
714
+ fallback: fallbackLanguage
715
+ });
716
+ const candidates = buildCandidates({
717
+ language: requestedLanguage,
718
+ fallbackLanguage
719
+ });
720
+ const selectedCandidate = candidates.find((candidate)=>!!profiles[profile]?.translations[candidate.language]);
721
+ if (selectedCandidate && 'profile_language' !== selectedCandidate.reason) warnOnce(options?.logger, `i18n.fallback:${profile}:${requestedLanguage}:${selectedCandidate.language}`, `Policy translation fallback used (${selectedCandidate.reason}).`, {
722
+ requestedProfile: profile,
723
+ requestedLanguage,
724
+ resolvedProfile: profile,
725
+ resolvedLanguage: selectedCandidate.language
726
+ });
727
+ let language = selectedCandidate?.language ?? requestedLanguage;
728
+ if (!selectedCandidate && !isSupportedBaseLanguage(language)) {
729
+ warnOnce(options?.logger, `i18n.base-fallback:${language}`, `No translation found for '${language}'. Falling back to base English translations.`);
730
+ language = 'en';
731
+ }
732
+ const base = isSupportedBaseLanguage(language) ? all_namespaceObject.baseTranslations[language] : all_namespaceObject.baseTranslations.en;
733
+ const custom = selectedCandidate ? profiles[profile]?.translations[selectedCandidate.language] : void 0;
734
+ const translations = custom ? (0, translations_namespaceObject.deepMergeTranslations)(base, custom) : base;
735
+ return {
736
+ translations: translations,
737
+ language
738
+ };
739
+ }
740
+ const version_version = '2.0.0-rc.6';
572
741
  function extractErrorMessage(error) {
573
742
  if (error instanceof AggregateError && error.errors?.length > 0) {
574
743
  const inner = error.errors.map((e)=>e instanceof Error ? e.message : String(e)).join('; ');
@@ -893,6 +1062,7 @@ var __webpack_exports__ = {};
893
1062
  consentPolicy: 'pol',
894
1063
  consentPurpose: 'pur',
895
1064
  domain: 'dom',
1065
+ runtimePolicyDecision: 'rpd',
896
1066
  subject: 'sub'
897
1067
  };
898
1068
  const b58 = external_base_x_default()('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');
@@ -1164,6 +1334,54 @@ var __webpack_exports__ = {};
1164
1334
  }
1165
1335
  };
1166
1336
  }
1337
+ function runtimePolicyDecisionRegistry({ db, ctx }) {
1338
+ const { logger } = ctx;
1339
+ return {
1340
+ findOrCreateRuntimePolicyDecision: async (input)=>{
1341
+ const existing = await db.findFirst('runtimePolicyDecision', {
1342
+ where: (b)=>b('dedupeKey', '=', input.dedupeKey)
1343
+ });
1344
+ if (existing) return existing;
1345
+ logger.debug('Creating runtime policy decision', {
1346
+ policyId: input.policyId,
1347
+ fingerprint: input.fingerprint,
1348
+ matchedBy: input.matchedBy
1349
+ });
1350
+ return db.create('runtimePolicyDecision', {
1351
+ id: `rpd_${crypto.randomUUID().replaceAll('-', '')}`,
1352
+ tenantId: input.tenantId,
1353
+ policyId: input.policyId,
1354
+ fingerprint: input.fingerprint,
1355
+ matchedBy: input.matchedBy,
1356
+ countryCode: input.countryCode,
1357
+ regionCode: input.regionCode,
1358
+ jurisdiction: input.jurisdiction,
1359
+ language: input.language,
1360
+ model: input.model,
1361
+ policyI18n: input.policyI18n ? {
1362
+ json: input.policyI18n
1363
+ } : void 0,
1364
+ uiMode: input.uiMode,
1365
+ bannerUi: input.bannerUi ? {
1366
+ json: input.bannerUi
1367
+ } : void 0,
1368
+ dialogUi: input.dialogUi ? {
1369
+ json: input.dialogUi
1370
+ } : void 0,
1371
+ categories: input.categories ? {
1372
+ json: input.categories
1373
+ } : void 0,
1374
+ preselectedCategories: input.preselectedCategories ? {
1375
+ json: input.preselectedCategories
1376
+ } : void 0,
1377
+ proofConfig: input.proofConfig ? {
1378
+ json: input.proofConfig
1379
+ } : void 0,
1380
+ dedupeKey: input.dedupeKey
1381
+ });
1382
+ }
1383
+ };
1384
+ }
1167
1385
  function subjectRegistry({ db, ctx }) {
1168
1386
  const { logger } = ctx;
1169
1387
  return {
@@ -1241,7 +1459,8 @@ var __webpack_exports__ = {};
1241
1459
  ...subjectRegistry(ctx),
1242
1460
  ...consentPurposeRegistry(ctx),
1243
1461
  ...policyRegistry(ctx),
1244
- ...domainRegistry(ctx)
1462
+ ...domainRegistry(ctx),
1463
+ ...runtimePolicyDecisionRegistry(ctx)
1245
1464
  });
1246
1465
  var schema = __webpack_require__("./src/db/schema/index.ts");
1247
1466
  const SCOPED_METHODS = new Set([
@@ -1307,6 +1526,18 @@ var __webpack_exports__ = {};
1307
1526
  }
1308
1527
  });
1309
1528
  }
1529
+ function inspectPolicies(policies, options) {
1530
+ return (0, types_namespaceObject.inspectPolicies)(policies, options);
1531
+ }
1532
+ async function resolvePolicyDecision(params) {
1533
+ return (0, types_namespaceObject.resolvePolicyDecision)({
1534
+ policies: params.policies,
1535
+ countryCode: params.countryCode,
1536
+ regionCode: params.regionCode,
1537
+ jurisdiction: params.jurisdiction,
1538
+ iabEnabled: params.iabEnabled
1539
+ });
1540
+ }
1310
1541
  let globalLogger;
1311
1542
  function initLogger(options) {
1312
1543
  globalLogger = (0, logger_namespaceObject.createLogger)({
@@ -1333,6 +1564,18 @@ var __webpack_exports__ = {};
1333
1564
  const rawOrm = client.orm('2.0.0');
1334
1565
  const orm = options.tenantId ? withTenantScope(rawOrm, options.tenantId) : rawOrm;
1335
1566
  const { ipAddress: _ipAddressConfig, ...baseOptions } = options;
1567
+ const i18nValidation = validateMessages({
1568
+ i18n: options.i18n,
1569
+ customTranslations: options.customTranslations,
1570
+ policies: options.policyPacks
1571
+ });
1572
+ for (const warning of i18nValidation.warnings)logger.warn(`i18n: ${warning}`);
1573
+ if (i18nValidation.errors.length > 0) throw new Error(`Invalid i18n configuration:\n${i18nValidation.errors.map((error)=>`- ${error}`).join('\n')}`);
1574
+ const policyValidation = inspectPolicies(options.policyPacks ?? [], {
1575
+ iabEnabled: options.iab?.enabled === true
1576
+ });
1577
+ for (const warning of policyValidation.warnings)logger.warn(`policyPacks: ${warning}`);
1578
+ if (policyValidation.errors.length > 0) throw new Error(policyValidation.errors[0]);
1336
1579
  const context = {
1337
1580
  ...baseOptions,
1338
1581
  appName,
@@ -1529,11 +1772,7 @@ var __webpack_exports__ = {};
1529
1772
  const app = new external_hono_namespaceObject.Hono();
1530
1773
  app.get('/check', (0, external_hono_openapi_namespaceObject.describeRoute)({
1531
1774
  summary: 'Check consent by external user ID',
1532
- description: `Pre-banner cross-device consent check. Use to avoid showing the banner when the user has already consented on another device.
1533
-
1534
- **Query parameters:**
1535
- - \`externalId\` – External user ID to check
1536
- - \`type\` – Consent type(s) to check (comma-separated)`,
1775
+ description: "Pre-banner cross-device consent check. Use to avoid showing the banner when the user has already consented on another device.\n\n**Query parameters:**\n- `externalId` – External user ID to check\n- `type` – Consent type(s) to check (comma-separated)",
1537
1776
  tags: [
1538
1777
  'Consent'
1539
1778
  ],
@@ -1693,6 +1932,124 @@ var __webpack_exports__ = {};
1693
1932
  }
1694
1933
  };
1695
1934
  }
1935
+ const external_jose_namespaceObject = require("jose");
1936
+ const POLICY_SNAPSHOT_JWT_HEADER = {
1937
+ alg: 'HS256',
1938
+ typ: 'JWT'
1939
+ };
1940
+ const DEFAULT_POLICY_SNAPSHOT_ISSUER = 'c15t';
1941
+ const DEFAULT_POLICY_SNAPSHOT_AUDIENCE = 'c15t-policy-snapshot';
1942
+ function resolveSnapshotIssuer(options) {
1943
+ return options?.issuer?.trim() || DEFAULT_POLICY_SNAPSHOT_ISSUER;
1944
+ }
1945
+ function resolveSnapshotAudience(params) {
1946
+ const configuredAudience = params.options?.audience?.trim();
1947
+ if (configuredAudience) return configuredAudience;
1948
+ return params.tenantId ? `${DEFAULT_POLICY_SNAPSHOT_AUDIENCE}:${params.tenantId}` : DEFAULT_POLICY_SNAPSHOT_AUDIENCE;
1949
+ }
1950
+ function getSigningKey(secret) {
1951
+ return new TextEncoder().encode(secret);
1952
+ }
1953
+ function isPolicySnapshotPayload(payload) {
1954
+ return 'string' == typeof payload.policyId && 'string' == typeof payload.fingerprint && 'string' == typeof payload.matchedBy && 'string' == typeof payload.jurisdiction && 'string' == typeof payload.model && 'string' == typeof payload.iss && 'string' == typeof payload.aud && 'string' == typeof payload.sub && 'number' == typeof payload.iat && 'number' == typeof payload.exp;
1955
+ }
1956
+ async function createPolicySnapshotToken(params) {
1957
+ const { options } = params;
1958
+ if (!options?.signingKey) return;
1959
+ const iat = Math.floor(Date.now() / 1000);
1960
+ const ttlSeconds = options.ttlSeconds ?? 1800;
1961
+ const exp = iat + ttlSeconds;
1962
+ const iss = resolveSnapshotIssuer(options);
1963
+ const aud = resolveSnapshotAudience({
1964
+ options,
1965
+ tenantId: params.tenantId
1966
+ });
1967
+ const payload = {
1968
+ iss,
1969
+ aud,
1970
+ sub: params.policyId,
1971
+ tenantId: params.tenantId,
1972
+ policyId: params.policyId,
1973
+ fingerprint: params.fingerprint,
1974
+ matchedBy: params.matchedBy,
1975
+ country: params.country,
1976
+ region: params.region,
1977
+ jurisdiction: params.jurisdiction,
1978
+ language: params.language,
1979
+ model: params.model,
1980
+ policyI18n: params.policyI18n,
1981
+ expiryDays: params.expiryDays,
1982
+ scopeMode: params.scopeMode,
1983
+ uiMode: params.uiMode,
1984
+ bannerUi: params.bannerUi,
1985
+ dialogUi: params.dialogUi,
1986
+ categories: params.categories,
1987
+ preselectedCategories: params.preselectedCategories,
1988
+ gpc: params.gpc,
1989
+ proofConfig: params.proofConfig,
1990
+ iat,
1991
+ exp
1992
+ };
1993
+ const token = await new external_jose_namespaceObject.SignJWT(payload).setProtectedHeader(POLICY_SNAPSHOT_JWT_HEADER).setIssuedAt(iat).setExpirationTime(exp).sign(getSigningKey(options.signingKey));
1994
+ return {
1995
+ token,
1996
+ payload
1997
+ };
1998
+ }
1999
+ async function verifyPolicySnapshotToken(params) {
2000
+ const { token, options, tenantId } = params;
2001
+ if (!options?.signingKey) return {
2002
+ valid: false,
2003
+ reason: 'missing'
2004
+ };
2005
+ if (!token) return {
2006
+ valid: false,
2007
+ reason: 'missing'
2008
+ };
2009
+ if (3 !== token.split('.').length) return {
2010
+ valid: false,
2011
+ reason: 'malformed'
2012
+ };
2013
+ try {
2014
+ const { payload, protectedHeader } = await (0, external_jose_namespaceObject.jwtVerify)(token, getSigningKey(options.signingKey), {
2015
+ issuer: resolveSnapshotIssuer(options),
2016
+ audience: resolveSnapshotAudience({
2017
+ options,
2018
+ tenantId
2019
+ })
2020
+ });
2021
+ const header = protectedHeader;
2022
+ if ('HS256' !== header.alg || 'JWT' !== header.typ) return {
2023
+ valid: false,
2024
+ reason: 'invalid'
2025
+ };
2026
+ if (!isPolicySnapshotPayload(payload)) return {
2027
+ valid: false,
2028
+ reason: 'invalid'
2029
+ };
2030
+ if (payload.sub !== payload.policyId) return {
2031
+ valid: false,
2032
+ reason: 'invalid'
2033
+ };
2034
+ if ((tenantId ?? void 0) !== (payload.tenantId ?? void 0)) return {
2035
+ valid: false,
2036
+ reason: 'invalid'
2037
+ };
2038
+ return {
2039
+ valid: true,
2040
+ payload
2041
+ };
2042
+ } catch (error) {
2043
+ if (error instanceof external_jose_namespaceObject.errors.JWTExpired) return {
2044
+ valid: false,
2045
+ reason: 'expired'
2046
+ };
2047
+ return {
2048
+ valid: false,
2049
+ reason: 'invalid'
2050
+ };
2051
+ }
2052
+ }
1696
2053
  function geo_normalizeHeader(value) {
1697
2054
  if (!value) return null;
1698
2055
  return Array.isArray(value) ? value[0] ?? null : value;
@@ -1848,28 +2205,118 @@ var __webpack_exports__ = {};
1848
2205
  if (options.disableGeoLocation) return 'GDPR';
1849
2206
  return checkJurisdiction(location.countryCode, location.regionCode);
1850
2207
  }
1851
- const translations_namespaceObject = require("@c15t/translations");
1852
- const all_namespaceObject = require("@c15t/translations/all");
1853
- function isSupportedBaseLanguage(lang) {
1854
- return lang in all_namespaceObject.baseTranslations;
2208
+ function stripIabTranslations(translations) {
2209
+ const { iab: _iab, ...rest } = translations;
2210
+ return rest;
1855
2211
  }
1856
- function translations_getTranslationsData(acceptLanguage, customTranslations) {
1857
- const supportedDefaultLanguages = Object.keys(all_namespaceObject.baseTranslations);
1858
- const supportedCustomLanguages = Object.keys(customTranslations || {});
1859
- const supportedLanguages = [
1860
- ...supportedDefaultLanguages,
1861
- ...supportedCustomLanguages
1862
- ];
1863
- const preferredLanguage = (0, translations_namespaceObject.selectLanguage)(supportedLanguages, {
1864
- header: acceptLanguage,
1865
- fallback: 'en'
2212
+ function resolveNoPolicyFallback() {
2213
+ return {
2214
+ id: 'no_banner',
2215
+ model: 'none',
2216
+ ui: {
2217
+ mode: 'none'
2218
+ }
2219
+ };
2220
+ }
2221
+ async function resolveInitPayload(request, options, logger) {
2222
+ const acceptLanguage = request.headers.get('accept-language') || 'en';
2223
+ const location = await getLocation(request, options);
2224
+ const jurisdiction = getJurisdiction(location, options);
2225
+ const hasExplicitPolicyPack = void 0 !== options.policyPacks;
2226
+ const isExplicitEmptyPolicyPack = hasExplicitPolicyPack && (options.policyPacks?.length ?? 0) === 0;
2227
+ const policyDecision = isExplicitEmptyPolicyPack ? void 0 : await resolvePolicyDecision({
2228
+ policies: options.policyPacks,
2229
+ countryCode: location.countryCode,
2230
+ regionCode: location.regionCode,
2231
+ jurisdiction,
2232
+ iabEnabled: options.iab?.enabled === true
2233
+ });
2234
+ if (hasExplicitPolicyPack && !isExplicitEmptyPolicyPack && !policyDecision) logger?.warn('Policy packs configured but no policy matched', {
2235
+ country: location.countryCode,
2236
+ region: location.regionCode
2237
+ });
2238
+ const resolvedPolicy = hasExplicitPolicyPack ? policyDecision?.policy ?? resolveNoPolicyFallback() : void 0;
2239
+ const iabOptions = options.iab;
2240
+ const shouldIncludeIabPayload = iabOptions?.enabled === true && (!hasExplicitPolicyPack || resolvedPolicy?.model === 'iab');
2241
+ const translationsResult = translations_getTranslationsData(acceptLanguage, options.customTranslations, {
2242
+ i18n: options.i18n,
2243
+ policyI18n: resolvedPolicy?.i18n,
2244
+ logger
2245
+ });
2246
+ const responseTranslations = shouldIncludeIabPayload ? translationsResult : {
2247
+ ...translationsResult,
2248
+ translations: stripIabTranslations(translationsResult.translations)
2249
+ };
2250
+ let gvl = null;
2251
+ if (shouldIncludeIabPayload && iabOptions) {
2252
+ const language = translationsResult.language.split('-')[0] || 'en';
2253
+ const gvlResolver = createGVLResolver({
2254
+ appName: options.appName || 'c15t',
2255
+ bundled: iabOptions.bundled,
2256
+ cacheAdapter: options.cache?.adapter,
2257
+ vendorIds: iabOptions.vendorIds,
2258
+ endpoint: iabOptions.endpoint
2259
+ });
2260
+ gvl = await gvlResolver.get(language);
2261
+ }
2262
+ const customVendors = shouldIncludeIabPayload ? iabOptions?.customVendors : void 0;
2263
+ const snapshot = policyDecision ? await createPolicySnapshotToken({
2264
+ options: options.policySnapshot,
2265
+ tenantId: options.tenantId,
2266
+ policyId: policyDecision.policy.id,
2267
+ fingerprint: policyDecision.fingerprint,
2268
+ matchedBy: policyDecision.matchedBy,
2269
+ country: location?.countryCode ?? null,
2270
+ region: location?.regionCode ?? null,
2271
+ jurisdiction,
2272
+ language: translationsResult.language,
2273
+ model: policyDecision.policy.model,
2274
+ policyI18n: policyDecision.policy.i18n,
2275
+ expiryDays: policyDecision.policy.consent?.expiryDays,
2276
+ scopeMode: policyDecision.policy.consent?.scopeMode,
2277
+ uiMode: policyDecision.policy.ui?.mode,
2278
+ bannerUi: policyDecision.policy.ui?.banner,
2279
+ dialogUi: policyDecision.policy.ui?.dialog,
2280
+ categories: policyDecision.policy.consent?.categories,
2281
+ preselectedCategories: policyDecision.policy.consent?.preselectedCategories,
2282
+ gpc: policyDecision.policy.consent?.gpc,
2283
+ proofConfig: policyDecision.policy.proof
2284
+ }) : void 0;
2285
+ const gpc = '1' === request.headers.get('sec-gpc');
2286
+ getMetrics()?.recordInit({
2287
+ jurisdiction,
2288
+ country: location?.countryCode ?? void 0,
2289
+ region: location?.regionCode ?? void 0,
2290
+ gpc
1866
2291
  });
1867
- const base = isSupportedBaseLanguage(preferredLanguage) ? all_namespaceObject.baseTranslations[preferredLanguage] : all_namespaceObject.baseTranslations.en;
1868
- const custom = supportedCustomLanguages.includes(preferredLanguage) ? customTranslations?.[preferredLanguage] : {};
1869
- const translations = custom ? (0, translations_namespaceObject.deepMergeTranslations)(base, custom) : base;
1870
2292
  return {
1871
- translations: translations,
1872
- language: preferredLanguage
2293
+ jurisdiction,
2294
+ location,
2295
+ translations: responseTranslations,
2296
+ branding: options.branding || 'c15t',
2297
+ ...shouldIncludeIabPayload && {
2298
+ gvl,
2299
+ customVendors
2300
+ },
2301
+ ...resolvedPolicy && {
2302
+ policy: resolvedPolicy
2303
+ },
2304
+ ...policyDecision && {
2305
+ policyDecision: {
2306
+ policyId: policyDecision.policy.id,
2307
+ fingerprint: policyDecision.fingerprint,
2308
+ matchedBy: policyDecision.matchedBy,
2309
+ country: location.countryCode,
2310
+ region: location.regionCode,
2311
+ jurisdiction
2312
+ }
2313
+ },
2314
+ ...snapshot?.token && {
2315
+ policySnapshotToken: snapshot.token
2316
+ },
2317
+ ...shouldIncludeIabPayload && iabOptions?.cmpId != null && {
2318
+ cmpId: iabOptions.cmpId
2319
+ }
1873
2320
  };
1874
2321
  }
1875
2322
  const createInitRoute = (options)=>{
@@ -1882,7 +2329,7 @@ var __webpack_exports__ = {};
1882
2329
  - **Location** – User's location (null if geo-location is disabled)
1883
2330
  - **Translations** – Consent manager copy (from \`Accept-Language\` header)
1884
2331
  - **Branding** – Configured branding key
1885
- - **GVL** – Global Vendor List when enabled
2332
+ - **GVL** – Global Vendor List when IAB is active for the request
1886
2333
 
1887
2334
  Use for geo-targeted consent banners and regional compliance.`,
1888
2335
  tags: [
@@ -1899,42 +2346,9 @@ Use for geo-targeted consent banners and regional compliance.`,
1899
2346
  }
1900
2347
  }
1901
2348
  }), async (c)=>{
1902
- const request = c.req.raw;
1903
- const acceptLanguage = request.headers.get('accept-language') || 'en';
1904
- const location = await getLocation(request, options);
1905
- const jurisdiction = getJurisdiction(location, options);
1906
- const translationsResult = translations_getTranslationsData(acceptLanguage, options.customTranslations);
1907
- let gvl = null;
1908
- if (options.iab?.enabled) {
1909
- const language = translationsResult.language.split('-')[0] || 'en';
1910
- const gvlResolver = createGVLResolver({
1911
- appName: options.appName || 'c15t',
1912
- bundled: options.iab.bundled,
1913
- cacheAdapter: options.cache?.adapter,
1914
- vendorIds: options.iab.vendorIds,
1915
- endpoint: options.iab.endpoint
1916
- });
1917
- gvl = await gvlResolver.get(language);
1918
- }
1919
- const customVendors = options.iab?.customVendors;
1920
- const gpc = '1' === request.headers.get('sec-gpc');
1921
- getMetrics()?.recordInit({
1922
- jurisdiction,
1923
- country: location?.countryCode ?? void 0,
1924
- region: location?.regionCode ?? void 0,
1925
- gpc
1926
- });
1927
- return c.json({
1928
- jurisdiction,
1929
- location,
1930
- translations: translationsResult,
1931
- branding: options.branding || 'c15t',
1932
- gvl,
1933
- customVendors,
1934
- ...options.iab?.cmpId != null && {
1935
- cmpId: options.iab.cmpId
1936
- }
1937
- });
2349
+ const ctx = c.get('c15tContext');
2350
+ const payload = await resolveInitPayload(c.req.raw, options, ctx?.logger);
2351
+ return c.json(payload);
1938
2352
  });
1939
2353
  return app;
1940
2354
  };
@@ -2024,6 +2438,12 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2024
2438
  const subjectId = c.req.param('id');
2025
2439
  const type = c.req.query('type');
2026
2440
  const typeFilter = type?.split(',').map((t)=>t.trim()) || [];
2441
+ if (!subjectId) throw new http_exception_namespaceObject.HTTPException(400, {
2442
+ message: 'Subject ID is required',
2443
+ cause: {
2444
+ code: 'SUBJECT_ID_REQUIRED'
2445
+ }
2446
+ });
2027
2447
  logger.debug('Request parameters', {
2028
2448
  subjectId,
2029
2449
  typeFilter
@@ -2215,6 +2635,12 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2215
2635
  const subjectId = c.req.param('id');
2216
2636
  const body = await c.req.json();
2217
2637
  const { externalId, identityProvider = 'external' } = body;
2638
+ if (!subjectId) throw new http_exception_namespaceObject.HTTPException(400, {
2639
+ message: 'Subject ID is required',
2640
+ cause: {
2641
+ code: 'SUBJECT_ID_REQUIRED'
2642
+ }
2643
+ });
2218
2644
  logger.debug('Request parameters', {
2219
2645
  subjectId,
2220
2646
  externalId,
@@ -2291,6 +2717,119 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2291
2717
  });
2292
2718
  }
2293
2719
  };
2720
+ function buildRuntimeDecisionDedupeKey(input) {
2721
+ return [
2722
+ input.tenantId ?? 'default',
2723
+ input.fingerprint,
2724
+ input.matchedBy,
2725
+ input.countryCode ?? 'none',
2726
+ input.regionCode ?? 'none',
2727
+ input.jurisdiction,
2728
+ input.language ?? 'none'
2729
+ ].join('|');
2730
+ }
2731
+ function buildDecisionPayload(params) {
2732
+ const { tenantId, snapshot, decision, location, jurisdiction, language, proofConfig } = params;
2733
+ if (snapshot?.valid && snapshot.payload) {
2734
+ const sp = snapshot.payload;
2735
+ return {
2736
+ tenantId,
2737
+ policyId: sp.policyId,
2738
+ fingerprint: sp.fingerprint,
2739
+ matchedBy: sp.matchedBy,
2740
+ countryCode: sp.country,
2741
+ regionCode: sp.region,
2742
+ jurisdiction: sp.jurisdiction,
2743
+ language: sp.language,
2744
+ model: sp.model,
2745
+ policyI18n: sp.policyI18n,
2746
+ uiMode: sp.uiMode,
2747
+ bannerUi: sp.bannerUi,
2748
+ dialogUi: sp.dialogUi,
2749
+ categories: sp.categories,
2750
+ preselectedCategories: sp.preselectedCategories,
2751
+ proofConfig: sp.proofConfig,
2752
+ dedupeKey: buildRuntimeDecisionDedupeKey({
2753
+ tenantId,
2754
+ fingerprint: sp.fingerprint,
2755
+ matchedBy: sp.matchedBy,
2756
+ countryCode: sp.country,
2757
+ regionCode: sp.region,
2758
+ jurisdiction: sp.jurisdiction,
2759
+ language: sp.language
2760
+ }),
2761
+ source: 'snapshot_token'
2762
+ };
2763
+ }
2764
+ if (decision) return {
2765
+ tenantId,
2766
+ policyId: decision.policy.id,
2767
+ fingerprint: decision.fingerprint,
2768
+ matchedBy: decision.matchedBy,
2769
+ countryCode: location.countryCode,
2770
+ regionCode: location.regionCode,
2771
+ jurisdiction,
2772
+ language,
2773
+ model: decision.policy.model,
2774
+ policyI18n: decision.policy.i18n,
2775
+ uiMode: decision.policy.ui?.mode,
2776
+ bannerUi: decision.policy.ui?.banner,
2777
+ dialogUi: decision.policy.ui?.dialog,
2778
+ categories: decision.policy.consent?.categories,
2779
+ preselectedCategories: decision.policy.consent?.preselectedCategories,
2780
+ proofConfig,
2781
+ dedupeKey: buildRuntimeDecisionDedupeKey({
2782
+ tenantId,
2783
+ fingerprint: decision.fingerprint,
2784
+ matchedBy: decision.matchedBy,
2785
+ countryCode: location.countryCode,
2786
+ regionCode: location.regionCode,
2787
+ jurisdiction,
2788
+ language
2789
+ }),
2790
+ source: 'write_time_fallback'
2791
+ };
2792
+ }
2793
+ function parseLanguageFromHeader(header) {
2794
+ if (!header) return;
2795
+ const firstLanguage = header.split(',')[0]?.split(';')[0]?.trim();
2796
+ if (!firstLanguage) return;
2797
+ return firstLanguage.split('-')[0]?.toLowerCase();
2798
+ }
2799
+ function resolveSnapshotFailureMode(ctx) {
2800
+ return ctx.policySnapshot?.onValidationFailure ?? 'reject';
2801
+ }
2802
+ function buildSnapshotHttpException(reason) {
2803
+ switch(reason){
2804
+ case 'missing':
2805
+ return new http_exception_namespaceObject.HTTPException(409, {
2806
+ message: 'Policy snapshot token is required',
2807
+ cause: {
2808
+ code: 'POLICY_SNAPSHOT_REQUIRED'
2809
+ }
2810
+ });
2811
+ case 'expired':
2812
+ return new http_exception_namespaceObject.HTTPException(409, {
2813
+ message: 'Policy snapshot token has expired',
2814
+ cause: {
2815
+ code: 'POLICY_SNAPSHOT_EXPIRED'
2816
+ }
2817
+ });
2818
+ case 'malformed':
2819
+ case 'invalid':
2820
+ return new http_exception_namespaceObject.HTTPException(409, {
2821
+ message: 'Policy snapshot token is invalid',
2822
+ cause: {
2823
+ code: 'POLICY_SNAPSHOT_INVALID'
2824
+ }
2825
+ });
2826
+ default:
2827
+ {
2828
+ const _exhaustive = reason;
2829
+ throw new Error(`Unhandled policy snapshot verification failure reason: ${_exhaustive}`);
2830
+ }
2831
+ }
2832
+ }
2294
2833
  const postSubjectHandler = async (c)=>{
2295
2834
  const ctx = c.get('c15tContext');
2296
2835
  const logger = ctx.logger;
@@ -2302,9 +2841,6 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2302
2841
  const givenAt = new Date(givenAtEpoch);
2303
2842
  const rawConsentAction = 'consentAction' in input ? input.consentAction : void 0;
2304
2843
  let derivedConsentAction;
2305
- if ('all' === rawConsentAction) derivedConsentAction = 'accept_all';
2306
- else if ('necessary' === rawConsentAction) derivedConsentAction = 'opt-out' === input.jurisdictionModel ? 'opt_out' : 'reject_all';
2307
- else if ('custom' === rawConsentAction) derivedConsentAction = 'custom';
2308
2844
  logger.debug('Request parameters', {
2309
2845
  type,
2310
2846
  subjectId,
@@ -2313,6 +2849,50 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2313
2849
  domain
2314
2850
  });
2315
2851
  try {
2852
+ if ('cookie_banner' === type) logger.warn('`cookie_banner` policy type is deprecated in 2.0 RC and will be removed in 2.0 GA. Use backend runtime `policyPacks` for banner behavior.');
2853
+ const request = c.req.raw ?? new Request('https://c15t.local/subjects');
2854
+ const acceptLanguage = request.headers.get('accept-language');
2855
+ const requestLanguage = parseLanguageFromHeader(acceptLanguage);
2856
+ const location = await getLocation(request, ctx);
2857
+ const resolvedJurisdiction = getJurisdiction(location, ctx);
2858
+ const snapshotVerification = await verifyPolicySnapshotToken({
2859
+ token: input.policySnapshotToken,
2860
+ options: ctx.policySnapshot,
2861
+ tenantId: ctx.tenantId
2862
+ });
2863
+ const hasValidSnapshot = snapshotVerification.valid;
2864
+ const snapshotPayload = snapshotVerification.valid ? snapshotVerification.payload : null;
2865
+ const shouldRequireSnapshot = !!ctx.policySnapshot?.signingKey && 'reject' === resolveSnapshotFailureMode(ctx);
2866
+ if (!hasValidSnapshot && shouldRequireSnapshot) throw buildSnapshotHttpException(snapshotVerification.reason);
2867
+ const resolvedPolicyDecision = hasValidSnapshot ? void 0 : await resolvePolicyDecision({
2868
+ policies: ctx.policyPacks,
2869
+ countryCode: location.countryCode,
2870
+ regionCode: location.regionCode,
2871
+ jurisdiction: resolvedJurisdiction,
2872
+ iabEnabled: ctx.iab?.enabled === true
2873
+ });
2874
+ const effectivePolicy = hasValidSnapshot && snapshotPayload ? {
2875
+ id: snapshotPayload.policyId,
2876
+ model: snapshotPayload.model,
2877
+ i18n: snapshotPayload.policyI18n,
2878
+ consent: {
2879
+ expiryDays: snapshotPayload.expiryDays,
2880
+ scopeMode: snapshotPayload.scopeMode,
2881
+ categories: snapshotPayload.categories,
2882
+ preselectedCategories: snapshotPayload.preselectedCategories,
2883
+ gpc: snapshotPayload.gpc
2884
+ },
2885
+ ui: {
2886
+ mode: snapshotPayload.uiMode,
2887
+ banner: snapshotPayload.bannerUi,
2888
+ dialog: snapshotPayload.dialogUi
2889
+ },
2890
+ proof: snapshotPayload.proofConfig
2891
+ } : resolvedPolicyDecision?.policy;
2892
+ const effectiveModel = effectivePolicy?.model ?? ('opt-in' === input.jurisdictionModel || 'opt-out' === input.jurisdictionModel || 'iab' === input.jurisdictionModel ? input.jurisdictionModel : void 0);
2893
+ if ('all' === rawConsentAction) derivedConsentAction = 'accept_all';
2894
+ else if ('necessary' === rawConsentAction) derivedConsentAction = 'opt-out' === effectiveModel ? 'opt_out' : 'reject_all';
2895
+ else if ('custom' === rawConsentAction) derivedConsentAction = 'custom';
2316
2896
  const subject = await registry.findOrCreateSubject({
2317
2897
  subjectId,
2318
2898
  externalSubjectId,
@@ -2339,6 +2919,7 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2339
2919
  });
2340
2920
  let policyId;
2341
2921
  let purposeIds = [];
2922
+ let appliedPreferences;
2342
2923
  const inputPolicyId = 'policyId' in input ? input.policyId : void 0;
2343
2924
  if (inputPolicyId) {
2344
2925
  policyId = inputPolicyId;
@@ -2371,20 +2952,66 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2371
2952
  policyId = policy.id;
2372
2953
  }
2373
2954
  if (preferences) {
2374
- const consentedPurposes = Object.entries(preferences).filter(([_, isConsented])=>isConsented).map(([purposeCode])=>purposeCode);
2955
+ const allowedCategories = effectivePolicy?.consent?.categories;
2956
+ const effectiveScopeMode = effectivePolicy?.consent?.scopeMode ?? 'permissive';
2957
+ const hasWildcardCategoryScope = allowedCategories?.includes('*') === true;
2958
+ const appliedPreferenceEntries = Object.entries(preferences);
2959
+ let filteredAppliedPreferenceEntries = appliedPreferenceEntries;
2960
+ if (allowedCategories && allowedCategories.length > 0 && !hasWildcardCategoryScope) {
2961
+ const disallowed = appliedPreferenceEntries.map(([purpose])=>purpose).filter((purpose)=>!allowedCategories.includes(purpose));
2962
+ filteredAppliedPreferenceEntries = appliedPreferenceEntries.filter(([purpose])=>allowedCategories.includes(purpose));
2963
+ if (disallowed.length > 0 && 'strict' === effectiveScopeMode) throw new http_exception_namespaceObject.HTTPException(400, {
2964
+ message: 'Preferences include categories not allowed by policy',
2965
+ cause: {
2966
+ code: 'PURPOSE_NOT_ALLOWED',
2967
+ disallowed
2968
+ }
2969
+ });
2970
+ }
2971
+ appliedPreferences = Object.fromEntries(filteredAppliedPreferenceEntries);
2972
+ const filteredConsentedPurposeCodes = filteredAppliedPreferenceEntries.filter(([_, isConsented])=>isConsented).map(([purposeCode])=>purposeCode);
2375
2973
  logger.debug('Consented purposes', {
2376
- consentedPurposes
2974
+ consentedPurposes: filteredConsentedPurposeCodes
2377
2975
  });
2378
- const purposesRaw = await Promise.all(consentedPurposes.map((purposeCode)=>registry.findOrCreateConsentPurposeByCode(purposeCode)));
2976
+ const purposesRaw = await Promise.all(filteredConsentedPurposeCodes.map((purposeCode)=>registry.findOrCreateConsentPurposeByCode(purposeCode)));
2379
2977
  const purposes = purposesRaw.map((purpose)=>purpose?.id ?? null).filter((id)=>Boolean(id));
2380
2978
  logger.debug('Filtered purposes', {
2381
2979
  purposes
2382
2980
  });
2383
2981
  if (0 === purposes.length) logger.warn('No valid purpose IDs found after filtering. Using empty list.', {
2384
- consentedPurposes
2982
+ consentedPurposes: filteredConsentedPurposeCodes
2385
2983
  });
2386
2984
  purposeIds = purposes;
2387
2985
  }
2986
+ const expiryDays = effectivePolicy?.consent?.expiryDays;
2987
+ const validUntil = 'number' == typeof expiryDays && Number.isFinite(expiryDays) ? new Date(givenAt.getTime() + 86400000 * Math.max(0, expiryDays)) : void 0;
2988
+ const proofConfig = effectivePolicy?.proof;
2989
+ const shouldStoreIp = proofConfig?.storeIp ?? true;
2990
+ const shouldStoreUserAgent = proofConfig?.storeUserAgent ?? true;
2991
+ const shouldStoreLanguage = proofConfig?.storeLanguage ?? false;
2992
+ const effectiveLanguage = (snapshotPayload?.language && hasValidSnapshot ? snapshotPayload.language : requestLanguage) ?? void 0;
2993
+ const metadataWithPolicy = {
2994
+ ...metadata ?? {},
2995
+ ...shouldStoreLanguage && effectiveLanguage ? {
2996
+ policyLanguage: effectiveLanguage
2997
+ } : {}
2998
+ };
2999
+ const effectiveJurisdiction = hasValidSnapshot && snapshotPayload ? snapshotPayload.jurisdiction : resolvedJurisdiction;
3000
+ const decisionPayload = buildDecisionPayload({
3001
+ tenantId: ctx.tenantId,
3002
+ snapshot: hasValidSnapshot && snapshotPayload ? {
3003
+ valid: true,
3004
+ payload: snapshotPayload
3005
+ } : null,
3006
+ decision: resolvedPolicyDecision,
3007
+ location: {
3008
+ countryCode: location.countryCode,
3009
+ regionCode: location.regionCode
3010
+ },
3011
+ jurisdiction: resolvedJurisdiction,
3012
+ language: effectiveLanguage,
3013
+ proofConfig
3014
+ });
2388
3015
  const existingConsent = await db.findFirst('consent', {
2389
3016
  where: (b)=>b.and(b('subjectId', '=', subject.id), b('domainId', '=', domainRecord.id), b('policyId', '=', policyId), b('givenAt', '=', givenAt))
2390
3017
  });
@@ -2399,6 +3026,7 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2399
3026
  domain: domainRecord.name,
2400
3027
  type,
2401
3028
  metadata,
3029
+ appliedPreferences,
2402
3030
  uiSource: input.uiSource,
2403
3031
  givenAt: existingConsent.givenAt
2404
3032
  });
@@ -2410,6 +3038,42 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2410
3038
  policyId,
2411
3039
  purposeIds
2412
3040
  });
3041
+ const runtimePolicyDecision = decisionPayload ? await tx.findFirst('runtimePolicyDecision', {
3042
+ where: (b)=>b('dedupeKey', '=', decisionPayload.dedupeKey)
3043
+ }) ?? await tx.create('runtimePolicyDecision', {
3044
+ id: `rpd_${crypto.randomUUID().replaceAll('-', '')}`,
3045
+ tenantId: decisionPayload.tenantId,
3046
+ policyId: decisionPayload.policyId,
3047
+ fingerprint: decisionPayload.fingerprint,
3048
+ matchedBy: decisionPayload.matchedBy,
3049
+ countryCode: decisionPayload.countryCode,
3050
+ regionCode: decisionPayload.regionCode,
3051
+ jurisdiction: decisionPayload.jurisdiction,
3052
+ language: decisionPayload.language,
3053
+ model: decisionPayload.model,
3054
+ policyI18n: decisionPayload.policyI18n ? {
3055
+ json: decisionPayload.policyI18n
3056
+ } : void 0,
3057
+ uiMode: decisionPayload.uiMode,
3058
+ bannerUi: decisionPayload.bannerUi ? {
3059
+ json: decisionPayload.bannerUi
3060
+ } : void 0,
3061
+ dialogUi: decisionPayload.dialogUi ? {
3062
+ json: decisionPayload.dialogUi
3063
+ } : void 0,
3064
+ categories: decisionPayload.categories ? {
3065
+ json: decisionPayload.categories
3066
+ } : void 0,
3067
+ preselectedCategories: decisionPayload.preselectedCategories ? {
3068
+ json: decisionPayload.preselectedCategories
3069
+ } : void 0,
3070
+ proofConfig: decisionPayload.proofConfig ? {
3071
+ json: decisionPayload.proofConfig
3072
+ } : void 0,
3073
+ dedupeKey: decisionPayload.dedupeKey
3074
+ }).catch(async ()=>tx.findFirst('runtimePolicyDecision', {
3075
+ where: (b)=>b('dedupeKey', '=', decisionPayload.dedupeKey)
3076
+ })) : void 0;
2413
3077
  const consentRecord = await tx.create('consent', {
2414
3078
  id: await utils_generateUniqueId(tx, 'consent', ctx),
2415
3079
  subjectId: subject.id,
@@ -2418,17 +3082,20 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2418
3082
  purposeIds: {
2419
3083
  json: purposeIds
2420
3084
  },
2421
- metadata: metadata ? {
2422
- json: metadata
3085
+ metadata: Object.keys(metadataWithPolicy).length > 0 ? {
3086
+ json: metadataWithPolicy
2423
3087
  } : void 0,
2424
- ipAddress: ctx.ipAddress,
2425
- userAgent: ctx.userAgent,
2426
- jurisdiction: input.jurisdiction,
2427
- jurisdictionModel: input.jurisdictionModel,
3088
+ ipAddress: shouldStoreIp ? ctx.ipAddress : null,
3089
+ userAgent: shouldStoreUserAgent ? ctx.userAgent : null,
3090
+ jurisdiction: effectiveJurisdiction,
3091
+ jurisdictionModel: effectiveModel,
2428
3092
  tcString: input.tcString,
2429
3093
  uiSource: input.uiSource,
2430
3094
  consentAction: derivedConsentAction,
2431
- givenAt
3095
+ givenAt,
3096
+ validUntil,
3097
+ runtimePolicyDecisionId: runtimePolicyDecision?.id,
3098
+ runtimePolicySource: decisionPayload?.source
2432
3099
  });
2433
3100
  logger.debug('Created consent', {
2434
3101
  consentRecord: consentRecord.id
@@ -2447,7 +3114,7 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2447
3114
  });
2448
3115
  const metrics = getMetrics();
2449
3116
  if (metrics) {
2450
- const jurisdiction = input.jurisdiction;
3117
+ const jurisdiction = effectiveJurisdiction;
2451
3118
  metrics.recordConsentCreated({
2452
3119
  type,
2453
3120
  jurisdiction
@@ -2469,6 +3136,7 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2469
3136
  domain: domainRecord.name,
2470
3137
  type,
2471
3138
  metadata,
3139
+ appliedPreferences,
2472
3140
  uiSource: input.uiSource,
2473
3141
  givenAt: result.consent.givenAt
2474
3142
  });
@@ -2490,11 +3158,7 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2490
3158
  const app = new external_hono_namespaceObject.Hono();
2491
3159
  app.get('/:id', (0, external_hono_openapi_namespaceObject.describeRoute)({
2492
3160
  summary: 'Get subject consent status',
2493
- description: `Returns the subject's consent status for this device. Use to check if the subject has valid consent for given policy types.
2494
-
2495
- **Query:** \`type\` – Filter by consent type(s), comma-separated (e.g. \`privacy_policy,cookie_banner\`).
2496
-
2497
- **Response:** \`subject\`, \`consents\` (matching filter), \`isValid\` (valid consent for requested type(s)).`,
3161
+ description: "Returns the subject's consent status for this device. Use to check if the subject has valid consent for given policy types.\n\n**Query:** `type` – Filter by consent type(s), comma-separated (e.g. `privacy_policy,cookie_banner`).\n\n**Response:** `subject`, `consents` (matching filter), `isValid` (valid consent for requested type(s)).",
2498
3162
  tags: [
2499
3163
  'Subject',
2500
3164
  'Consent'
@@ -2515,12 +3179,7 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2515
3179
  }), (0, external_hono_openapi_namespaceObject.validator)('param', schema_.getSubjectInputSchema), getSubjectHandler);
2516
3180
  app.post('/', (0, external_hono_openapi_namespaceObject.describeRoute)({
2517
3181
  summary: 'Record consent for a subject',
2518
- description: `Creates a new consent record (append-only). Creates the subject if it does not exist.
2519
-
2520
- **Request body by \`type\`:**
2521
- - \`cookie_banner\` – Requires \`preferences\` object
2522
- - \`privacy_policy\`, \`dpa\`, \`terms_and_conditions\` – Optional \`policyId\`
2523
- - \`marketing_communications\`, \`age_verification\`, \`other\` – Optional \`preferences\``,
3182
+ description: "Creates a new consent record (append-only). Creates the subject if it does not exist.\n\n**Request body by `type`:**\n- `cookie_banner` – Requires `preferences` object\n- `privacy_policy`, `dpa`, `terms_and_conditions` – Optional `policyId`\n- `marketing_communications`, `age_verification`, `other` – Optional `preferences`",
2524
3183
  tags: [
2525
3184
  'Subject',
2526
3185
  'Consent'
@@ -2592,6 +3251,86 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2592
3251
  return app;
2593
3252
  };
2594
3253
  var define_config = __webpack_require__("./src/define-config.ts");
3254
+ const DEFAULT_FALLBACK_POLICY_INPUT = {
3255
+ id: 'world_no_banner',
3256
+ isDefault: true,
3257
+ model: 'none',
3258
+ uiMode: 'none'
3259
+ };
3260
+ function mergeMatch(input) {
3261
+ return types_namespaceObject.policyMatchers.merge(input.countries?.length ? types_namespaceObject.policyMatchers.countries(input.countries) : {}, input.regions?.length ? types_namespaceObject.policyMatchers.regions(input.regions) : {}, input.isDefault ? types_namespaceObject.policyMatchers["default"]() : {});
3262
+ }
3263
+ function compactUiSurface(value) {
3264
+ if (!value) return;
3265
+ return (0, schema_.compactDefined)({
3266
+ allowedActions: (0, schema_.dedupeTrimmedStrings)(value.allowedActions),
3267
+ primaryActions: value.primaryActions,
3268
+ layout: value.layout,
3269
+ direction: value.direction,
3270
+ uiProfile: value.uiProfile,
3271
+ scrollLock: value.scrollLock
3272
+ });
3273
+ }
3274
+ function buildPolicyConfig(input) {
3275
+ const categories = (0, schema_.dedupeTrimmedStrings)(input.categories);
3276
+ const preselectedCategories = (0, schema_.dedupeTrimmedStrings)(input.preselectedCategories);
3277
+ return {
3278
+ id: input.id,
3279
+ match: mergeMatch(input),
3280
+ i18n: input.i18n,
3281
+ consent: (0, schema_.compactDefined)({
3282
+ model: input.model,
3283
+ expiryDays: input.expiryDays,
3284
+ scopeMode: input.scopeMode,
3285
+ categories,
3286
+ preselectedCategories,
3287
+ gpc: input.gpc
3288
+ }),
3289
+ ui: (0, schema_.compactDefined)({
3290
+ mode: input.uiMode,
3291
+ banner: compactUiSurface(input.banner),
3292
+ dialog: compactUiSurface(input.dialog)
3293
+ }),
3294
+ proof: (0, schema_.compactDefined)({
3295
+ storeIp: input.proof?.storeIp,
3296
+ storeUserAgent: input.proof?.storeUserAgent,
3297
+ storeLanguage: input.proof?.storeLanguage
3298
+ })
3299
+ };
3300
+ }
3301
+ function buildPolicyPack(inputs) {
3302
+ return inputs.map((input)=>buildPolicyConfig(input));
3303
+ }
3304
+ function buildPolicyPackWithDefault(inputs, defaultPolicy) {
3305
+ const pack = buildPolicyPack(inputs);
3306
+ const hasDefault = pack.some((policy)=>policy.match.isDefault);
3307
+ if (hasDefault) return pack;
3308
+ const fallbackInput = defaultPolicy ? {
3309
+ ...defaultPolicy,
3310
+ isDefault: true,
3311
+ countries: void 0,
3312
+ regions: void 0
3313
+ } : DEFAULT_FALLBACK_POLICY_INPUT;
3314
+ return [
3315
+ ...pack,
3316
+ buildPolicyConfig(fallbackInput)
3317
+ ];
3318
+ }
3319
+ function composePacks(...packs) {
3320
+ const seen = new Set();
3321
+ const result = [];
3322
+ for (const pack of packs)for (const policy of pack)if (!seen.has(policy.id)) {
3323
+ seen.add(policy.id);
3324
+ result.push(policy);
3325
+ }
3326
+ return result;
3327
+ }
3328
+ const policyBuilder = {
3329
+ create: buildPolicyConfig,
3330
+ createPack: buildPolicyPack,
3331
+ createPackWithDefault: buildPolicyPackWithDefault,
3332
+ composePacks
3333
+ };
2595
3334
  const c15tInstance = (options)=>{
2596
3335
  const context = init(options);
2597
3336
  const logger = (0, logger_namespaceObject.createLogger)(options.logger);
@@ -2673,9 +3412,7 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2673
3412
  }));
2674
3413
  const publicSpecUrl = `${basePath}${openApiConfig.specPath}`.replace(/\/+/g, '/');
2675
3414
  app.get(openApiConfig.docsPath, (0, hono_api_reference_namespaceObject.apiReference)({
2676
- spec: {
2677
- url: publicSpecUrl
2678
- },
3415
+ url: publicSpecUrl,
2679
3416
  pageTitle: `${options.appName || 'c15t API'} Documentation`
2680
3417
  }));
2681
3418
  }
@@ -2769,12 +3506,28 @@ Use for health checks, load balancer probes, and debugging. Performs a lightweig
2769
3506
  };
2770
3507
  };
2771
3508
  })();
3509
+ exports.EEA_COUNTRY_CODES = __webpack_exports__.EEA_COUNTRY_CODES;
3510
+ exports.EU_COUNTRY_CODES = __webpack_exports__.EU_COUNTRY_CODES;
3511
+ exports.POLICY_MATCH_DATASET_VERSION = __webpack_exports__.POLICY_MATCH_DATASET_VERSION;
3512
+ exports.UK_COUNTRY_CODES = __webpack_exports__.UK_COUNTRY_CODES;
2772
3513
  exports.c15tInstance = __webpack_exports__.c15tInstance;
2773
3514
  exports.defineConfig = __webpack_exports__.defineConfig;
3515
+ exports.inspectPolicies = __webpack_exports__.inspectPolicies;
3516
+ exports.policyBuilder = __webpack_exports__.policyBuilder;
3517
+ exports.policyMatchers = __webpack_exports__.policyMatchers;
3518
+ exports.policyPackPresets = __webpack_exports__.policyPackPresets;
2774
3519
  exports.version = __webpack_exports__.version;
2775
3520
  for(var __rspack_i in __webpack_exports__)if (-1 === [
3521
+ "EEA_COUNTRY_CODES",
3522
+ "EU_COUNTRY_CODES",
3523
+ "POLICY_MATCH_DATASET_VERSION",
3524
+ "UK_COUNTRY_CODES",
2776
3525
  "c15tInstance",
2777
3526
  "defineConfig",
3527
+ "inspectPolicies",
3528
+ "policyBuilder",
3529
+ "policyMatchers",
3530
+ "policyPackPresets",
2778
3531
  "version"
2779
3532
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
2780
3533
  Object.defineProperty(exports, '__esModule', {