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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/README.md +3 -3
  2. package/dist/302.js +473 -0
  3. package/dist/583.js +540 -0
  4. package/dist/915.js +1771 -0
  5. package/dist/cache.cjs +5 -5
  6. package/dist/cache.js +4 -415
  7. package/dist/core.cjs +1356 -120
  8. package/dist/core.js +163 -1981
  9. package/dist/db/adapters/drizzle.cjs +1 -1
  10. package/dist/db/adapters/drizzle.js +1 -2
  11. package/dist/db/adapters/kysely.cjs +1 -1
  12. package/dist/db/adapters/kysely.js +1 -2
  13. package/dist/db/adapters/mongo.cjs +1 -1
  14. package/dist/db/adapters/mongo.js +1 -2
  15. package/dist/db/adapters/prisma.cjs +1 -1
  16. package/dist/db/adapters/prisma.js +1 -2
  17. package/dist/db/adapters/typeorm.cjs +1 -1
  18. package/dist/db/adapters/typeorm.js +1 -2
  19. package/dist/db/adapters.cjs +1 -1
  20. package/dist/db/migrator.cjs +1 -1
  21. package/dist/db/schema.cjs +43 -3
  22. package/dist/db/schema.js +35 -4
  23. package/dist/define-config.cjs +1 -1
  24. package/dist/edge.cjs +1106 -0
  25. package/dist/edge.js +190 -0
  26. package/dist/router.cjs +885 -123
  27. package/dist/router.js +1 -1507
  28. package/dist/{types.cjs → types/index.cjs} +1 -1
  29. package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
  30. package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
  31. package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
  32. package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
  33. package/{dist → dist-types}/cache/gvl-resolver.d.ts +0 -1
  34. package/{dist → dist-types}/cache/index.d.ts +0 -1
  35. package/{dist → dist-types}/cache/keys.d.ts +0 -1
  36. package/{dist → dist-types}/cache/types.d.ts +0 -1
  37. package/{dist → dist-types}/core.d.ts +8 -1
  38. package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
  39. package/dist-types/db/registry/consent-policy.d.ts +78 -0
  40. package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
  41. package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
  42. package/dist-types/db/registry/index.d.ts +118 -0
  43. package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
  44. package/{dist → dist-types}/db/registry/subject.d.ts +0 -2
  45. package/{dist → dist-types}/db/registry/types.d.ts +1 -1
  46. package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
  47. package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
  48. package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
  49. package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
  50. package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
  51. package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
  52. package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +1 -2
  53. package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
  54. package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -32
  55. package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -2
  56. package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +1 -2
  57. package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +3 -3
  58. package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +1 -2
  59. package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +7 -2
  60. package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +1 -2
  61. package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +455 -28
  62. package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
  63. package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +1 -3
  64. package/{dist → dist-types}/db/schema/index.d.ts +908 -86
  65. package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
  66. package/dist-types/define-config.d.ts +17 -0
  67. package/dist-types/edge/index.d.ts +5 -0
  68. package/dist-types/edge/init-handler.d.ts +40 -0
  69. package/dist-types/edge/resolve-consent.d.ts +80 -0
  70. package/dist-types/edge/types.d.ts +13 -0
  71. package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
  72. package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
  73. package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
  74. package/{dist → dist-types}/handlers/init/index.d.ts +2 -3
  75. package/dist-types/handlers/init/policy.d.ts +26 -0
  76. package/dist-types/handlers/init/resolve-init.d.ts +44 -0
  77. package/dist-types/handlers/init/translations.d.ts +48 -0
  78. package/dist-types/handlers/legal-document/current.handler.d.ts +11 -0
  79. package/dist-types/handlers/legal-document/snapshot.d.ts +39 -0
  80. package/dist-types/handlers/policy/snapshot.d.ts +99 -0
  81. package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
  82. package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
  83. package/{dist → dist-types}/handlers/subject/get.handler.d.ts +3 -2
  84. package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
  85. package/{dist → dist-types}/handlers/subject/list.handler.d.ts +3 -2
  86. package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -2
  87. package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
  88. package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +3 -1
  89. package/{dist → dist-types}/init.d.ts +4 -7
  90. package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
  91. package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
  92. package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
  93. package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
  94. package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +0 -1
  95. package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
  96. package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
  97. package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
  98. package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
  99. package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
  100. package/dist-types/policies/builder.d.ts +127 -0
  101. package/dist-types/policies/defaults.d.ts +2 -0
  102. package/dist-types/policies/matchers.d.ts +3 -0
  103. package/{dist → dist-types}/router.d.ts +0 -1
  104. package/{dist → dist-types}/routes/consent.d.ts +0 -1
  105. package/{dist → dist-types}/routes/index.d.ts +1 -1
  106. package/{dist → dist-types}/routes/init.d.ts +0 -1
  107. package/dist-types/routes/legal-document.d.ts +7 -0
  108. package/{dist → dist-types}/routes/status.d.ts +0 -1
  109. package/{dist → dist-types}/routes/subject.d.ts +0 -1
  110. package/{dist → dist-types}/types/api.d.ts +0 -1
  111. package/dist-types/types/index.d.ts +464 -0
  112. package/dist-types/utils/background.d.ts +6 -0
  113. package/{dist → dist-types}/utils/create-telemetry-options.d.ts +1 -2
  114. package/{dist → dist-types}/utils/env.d.ts +0 -1
  115. package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
  116. package/{dist → dist-types}/utils/instrumentation.d.ts +2 -3
  117. package/{dist → dist-types}/utils/logger.d.ts +0 -1
  118. package/{dist → dist-types}/utils/metrics.d.ts +0 -1
  119. package/dist-types/version.d.ts +1 -0
  120. package/docs/README.md +49 -0
  121. package/docs/api/configuration.md +208 -0
  122. package/docs/api/endpoints.md +211 -0
  123. package/docs/guides/caching.md +85 -0
  124. package/docs/guides/database-setup.md +128 -0
  125. package/docs/guides/edge-deployment.md +251 -0
  126. package/docs/guides/framework-integration.md +142 -0
  127. package/docs/guides/iab-tcf.md +89 -0
  128. package/docs/guides/observability.md +96 -0
  129. package/docs/guides/policy-packs.md +396 -0
  130. package/docs/quickstart.md +129 -0
  131. package/package.json +53 -39
  132. package/.turbo/turbo-build.log +0 -49
  133. package/CHANGELOG.md +0 -89
  134. package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
  135. package/dist/cache/adapters/index.d.ts.map +0 -1
  136. package/dist/cache/adapters/memory.d.ts.map +0 -1
  137. package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
  138. package/dist/cache/gvl-resolver.d.ts.map +0 -1
  139. package/dist/cache/index.d.ts.map +0 -1
  140. package/dist/cache/keys.d.ts.map +0 -1
  141. package/dist/cache/types.d.ts.map +0 -1
  142. package/dist/core.d.ts.map +0 -1
  143. package/dist/db/adapters/drizzle.d.ts +0 -2
  144. package/dist/db/adapters/drizzle.d.ts.map +0 -1
  145. package/dist/db/adapters/index.d.ts +0 -2
  146. package/dist/db/adapters/index.d.ts.map +0 -1
  147. package/dist/db/adapters/kysely.d.ts +0 -2
  148. package/dist/db/adapters/kysely.d.ts.map +0 -1
  149. package/dist/db/adapters/mongo.d.ts +0 -2
  150. package/dist/db/adapters/mongo.d.ts.map +0 -1
  151. package/dist/db/adapters/prisma.d.ts +0 -2
  152. package/dist/db/adapters/prisma.d.ts.map +0 -1
  153. package/dist/db/adapters/typeorm.d.ts +0 -2
  154. package/dist/db/adapters/typeorm.d.ts.map +0 -1
  155. package/dist/db/migrator/index.d.ts.map +0 -1
  156. package/dist/db/registry/consent-policy.d.ts +0 -23
  157. package/dist/db/registry/consent-policy.d.ts.map +0 -1
  158. package/dist/db/registry/consent-purpose.d.ts.map +0 -1
  159. package/dist/db/registry/domain.d.ts.map +0 -1
  160. package/dist/db/registry/index.d.ts +0 -57
  161. package/dist/db/registry/index.d.ts.map +0 -1
  162. package/dist/db/registry/subject.d.ts.map +0 -1
  163. package/dist/db/registry/types.d.ts.map +0 -1
  164. package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
  165. package/dist/db/registry/utils.d.ts.map +0 -1
  166. package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
  167. package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
  168. package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
  169. package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
  170. package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
  171. package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
  172. package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
  173. package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
  174. package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
  175. package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
  176. package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
  177. package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
  178. package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
  179. package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
  180. package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
  181. package/dist/db/schema/index.d.ts.map +0 -1
  182. package/dist/db/tenant-scope.d.ts.map +0 -1
  183. package/dist/define-config.d.ts +0 -5
  184. package/dist/define-config.d.ts.map +0 -1
  185. package/dist/handlers/consent/check.handler.d.ts.map +0 -1
  186. package/dist/handlers/consent/index.d.ts +0 -12
  187. package/dist/handlers/consent/index.d.ts.map +0 -1
  188. package/dist/handlers/init/geo.d.ts.map +0 -1
  189. package/dist/handlers/init/index.d.ts.map +0 -1
  190. package/dist/handlers/init/translations.d.ts +0 -28
  191. package/dist/handlers/init/translations.d.ts.map +0 -1
  192. package/dist/handlers/status/index.d.ts +0 -7
  193. package/dist/handlers/status/index.d.ts.map +0 -1
  194. package/dist/handlers/status/status.handler.d.ts.map +0 -1
  195. package/dist/handlers/subject/get.handler.d.ts.map +0 -1
  196. package/dist/handlers/subject/index.d.ts +0 -10
  197. package/dist/handlers/subject/index.d.ts.map +0 -1
  198. package/dist/handlers/subject/list.handler.d.ts.map +0 -1
  199. package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
  200. package/dist/handlers/subject/post.handler.d.ts.map +0 -1
  201. package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
  202. package/dist/init.d.ts.map +0 -1
  203. package/dist/middleware/auth/index.d.ts.map +0 -1
  204. package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
  205. package/dist/middleware/cors/cors.d.ts.map +0 -1
  206. package/dist/middleware/cors/index.d.ts +0 -30
  207. package/dist/middleware/cors/index.d.ts.map +0 -1
  208. package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
  209. package/dist/middleware/cors/process-cors.d.ts.map +0 -1
  210. package/dist/middleware/openapi/config.d.ts.map +0 -1
  211. package/dist/middleware/openapi/handlers.d.ts.map +0 -1
  212. package/dist/middleware/openapi/index.d.ts +0 -12
  213. package/dist/middleware/openapi/index.d.ts.map +0 -1
  214. package/dist/middleware/process-ip/index.d.ts.map +0 -1
  215. package/dist/router.d.ts.map +0 -1
  216. package/dist/routes/consent.d.ts.map +0 -1
  217. package/dist/routes/index.d.ts.map +0 -1
  218. package/dist/routes/init.d.ts.map +0 -1
  219. package/dist/routes/status.d.ts.map +0 -1
  220. package/dist/routes/subject.d.ts.map +0 -1
  221. package/dist/types/api.d.ts.map +0 -1
  222. package/dist/types/index.d.ts +0 -255
  223. package/dist/types/index.d.ts.map +0 -1
  224. package/dist/utils/create-telemetry-options.d.ts.map +0 -1
  225. package/dist/utils/env.d.ts.map +0 -1
  226. package/dist/utils/extract-error-message.d.ts.map +0 -1
  227. package/dist/utils/index.d.ts +0 -4
  228. package/dist/utils/index.d.ts.map +0 -1
  229. package/dist/utils/instrumentation.d.ts.map +0 -1
  230. package/dist/utils/logger.d.ts.map +0 -1
  231. package/dist/utils/metrics.d.ts.map +0 -1
  232. package/dist/version.d.ts +0 -2
  233. package/dist/version.d.ts.map +0 -1
  234. package/knip.json +0 -31
  235. package/rslib.config.ts +0 -93
  236. package/src/cache/adapters/cloudflare-kv.ts +0 -71
  237. package/src/cache/adapters/index.ts +0 -22
  238. package/src/cache/adapters/memory.ts +0 -111
  239. package/src/cache/adapters/upstash-redis.ts +0 -113
  240. package/src/cache/gvl-resolver.ts +0 -289
  241. package/src/cache/index.ts +0 -34
  242. package/src/cache/keys.ts +0 -68
  243. package/src/cache/types.ts +0 -66
  244. package/src/core.ts +0 -368
  245. package/src/db/migrator/index.ts +0 -80
  246. package/src/db/registry/consent-policy.test.ts +0 -451
  247. package/src/db/registry/consent-policy.ts +0 -82
  248. package/src/db/registry/consent-purpose.test.ts +0 -428
  249. package/src/db/registry/consent-purpose.ts +0 -61
  250. package/src/db/registry/domain.test.ts +0 -445
  251. package/src/db/registry/domain.ts +0 -91
  252. package/src/db/registry/index.ts +0 -14
  253. package/src/db/registry/subject.test.ts +0 -388
  254. package/src/db/registry/subject.ts +0 -129
  255. package/src/db/registry/types.ts +0 -10
  256. package/src/db/registry/utils/generate-id.test.ts +0 -216
  257. package/src/db/registry/utils/generate-id.ts +0 -133
  258. package/src/db/registry/utils.ts +0 -133
  259. package/src/db/schema/1.0.0/audit-log.ts +0 -15
  260. package/src/db/schema/1.0.0/consent-policy.ts +0 -14
  261. package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
  262. package/src/db/schema/1.0.0/consent-record.ts +0 -10
  263. package/src/db/schema/1.0.0/consent.ts +0 -20
  264. package/src/db/schema/1.0.0/domain.ts +0 -12
  265. package/src/db/schema/1.0.0/index.ts +0 -48
  266. package/src/db/schema/1.0.0/subject.ts +0 -12
  267. package/src/db/schema/2.0.0/audit-log.ts +0 -18
  268. package/src/db/schema/2.0.0/consent-policy.ts +0 -28
  269. package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
  270. package/src/db/schema/2.0.0/consent.ts +0 -26
  271. package/src/db/schema/2.0.0/domain.ts +0 -12
  272. package/src/db/schema/2.0.0/index.ts +0 -47
  273. package/src/db/schema/2.0.0/subject.ts +0 -14
  274. package/src/db/schema/index.ts +0 -15
  275. package/src/db/tenant-scope.test.ts +0 -750
  276. package/src/db/tenant-scope.ts +0 -103
  277. package/src/define-config.ts +0 -5
  278. package/src/handlers/consent/check.handler.ts +0 -126
  279. package/src/handlers/init/geo.test.ts +0 -317
  280. package/src/handlers/init/geo.ts +0 -195
  281. package/src/handlers/init/index.test.ts +0 -205
  282. package/src/handlers/init/index.ts +0 -114
  283. package/src/handlers/init/translations.test.ts +0 -121
  284. package/src/handlers/init/translations.ts +0 -72
  285. package/src/handlers/status/status.handler.test.ts +0 -155
  286. package/src/handlers/status/status.handler.ts +0 -51
  287. package/src/handlers/subject/get.handler.ts +0 -93
  288. package/src/handlers/subject/list.handler.ts +0 -93
  289. package/src/handlers/subject/patch.handler.ts +0 -122
  290. package/src/handlers/subject/post.handler.test.ts +0 -294
  291. package/src/handlers/subject/post.handler.ts +0 -254
  292. package/src/handlers/utils/consent-enrichment.test.ts +0 -380
  293. package/src/handlers/utils/consent-enrichment.ts +0 -218
  294. package/src/init.test.ts +0 -126
  295. package/src/init.ts +0 -87
  296. package/src/middleware/auth/index.ts +0 -11
  297. package/src/middleware/auth/validate-api-key.test.ts +0 -86
  298. package/src/middleware/auth/validate-api-key.ts +0 -107
  299. package/src/middleware/cors/cors.test.ts +0 -135
  300. package/src/middleware/cors/cors.ts +0 -186
  301. package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
  302. package/src/middleware/cors/is-origin-trusted.ts +0 -130
  303. package/src/middleware/cors/process-cors.ts +0 -91
  304. package/src/middleware/openapi/config.ts +0 -29
  305. package/src/middleware/openapi/handlers.ts +0 -34
  306. package/src/middleware/process-ip/index.test.ts +0 -195
  307. package/src/middleware/process-ip/index.ts +0 -199
  308. package/src/router.ts +0 -15
  309. package/src/routes/consent.ts +0 -52
  310. package/src/routes/index.ts +0 -10
  311. package/src/routes/init.ts +0 -102
  312. package/src/routes/status.ts +0 -46
  313. package/src/routes/subject.ts +0 -152
  314. package/src/types/api.ts +0 -48
  315. package/src/types/index.ts +0 -288
  316. package/src/utils/create-telemetry-options.test.ts +0 -302
  317. package/src/utils/create-telemetry-options.ts +0 -229
  318. package/src/utils/env.ts +0 -84
  319. package/src/utils/extract-error-message.ts +0 -21
  320. package/src/utils/instrumentation.test.ts +0 -185
  321. package/src/utils/instrumentation.ts +0 -196
  322. package/src/utils/logger.ts +0 -41
  323. package/src/utils/metrics.test.ts +0 -323
  324. package/src/utils/metrics.ts +0 -402
  325. package/src/utils/telemetry-pii.test.ts +0 -325
  326. package/src/version.ts +0 -2
  327. package/tsconfig.json +0 -11
  328. package/vitest.config.ts +0 -28
  329. /package/dist/{types.js → types/index.js} +0 -0
  330. /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
  331. /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
  332. /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
  333. /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
  334. /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
  335. /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
  336. /package/{src/utils/index.ts → dist-types/utils/index.d.ts} +0 -0
@@ -1,380 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import {
3
- enrichConsents,
4
- parsePurposeIds,
5
- resolveConsentPolicies,
6
- } from './consent-enrichment';
7
-
8
- describe('consent-enrichment', () => {
9
- // ── parsePurposeIds ─────────────────────────────────────────────────
10
- describe('parsePurposeIds', () => {
11
- it('extracts IDs from { json: [...] } wrapper', () => {
12
- expect(parsePurposeIds({ json: ['a', 'b'] })).toEqual(['a', 'b']);
13
- });
14
-
15
- it('passes through raw string[]', () => {
16
- expect(parsePurposeIds(['x', 'y'])).toEqual(['x', 'y']);
17
- });
18
-
19
- it('returns [] for null', () => {
20
- expect(parsePurposeIds(null)).toEqual([]);
21
- });
22
-
23
- it('returns [] for undefined', () => {
24
- expect(parsePurposeIds(undefined)).toEqual([]);
25
- });
26
-
27
- it('returns [] for empty array', () => {
28
- expect(parsePurposeIds([])).toEqual([]);
29
- });
30
-
31
- it('returns [] for empty { json: [] }', () => {
32
- expect(parsePurposeIds({ json: [] })).toEqual([]);
33
- });
34
-
35
- it('returns [] for non-array values', () => {
36
- expect(parsePurposeIds('not-an-array')).toEqual([]);
37
- expect(parsePurposeIds(42)).toEqual([]);
38
- });
39
- });
40
-
41
- // ── enrichConsents ──────────────────────────────────────────────────
42
- describe('enrichConsents', () => {
43
- function createMockCtx(
44
- policies: Array<{ id: string; type: string }> = [],
45
- purposes: Array<{ id: string; code: string }> = [],
46
- latestByType: Record<string, { id: string }> = {}
47
- ) {
48
- const db = {
49
- findMany: vi.fn((table: string, opts?: { where?: unknown }) => {
50
- if (table === 'consentPolicy') return Promise.resolve(policies);
51
- if (table === 'consentPurpose') return Promise.resolve(purposes);
52
- return Promise.resolve([]);
53
- }),
54
- };
55
-
56
- const registry = {
57
- findOrCreatePolicy: vi.fn((type: string) =>
58
- Promise.resolve(latestByType[type] ?? { id: `latest_${type}` })
59
- ),
60
- findConsentPolicyById: vi.fn(),
61
- };
62
-
63
- return { db, registry } as unknown as Parameters<
64
- typeof enrichConsents
65
- >[1];
66
- }
67
-
68
- it('returns [] for empty consents', async () => {
69
- const ctx = createMockCtx();
70
- const result = await enrichConsents([], ctx);
71
- expect(result).toEqual([]);
72
- // No DB calls should be made
73
- expect((ctx.db as any).findMany).not.toHaveBeenCalled();
74
- });
75
-
76
- it('enriches a single consent with policy and purposes', async () => {
77
- const ctx = createMockCtx(
78
- [{ id: 'pol_1', type: 'cookie_banner' }],
79
- [
80
- { id: 'pur_1', code: 'analytics' },
81
- { id: 'pur_2', code: 'marketing' },
82
- ],
83
- { cookie_banner: { id: 'pol_1' } }
84
- );
85
-
86
- const result = await enrichConsents(
87
- [
88
- {
89
- id: 'con_1',
90
- policyId: 'pol_1',
91
- purposeIds: ['pur_1', 'pur_2'],
92
- givenAt: new Date('2024-01-01'),
93
- },
94
- ],
95
- ctx
96
- );
97
-
98
- expect(result).toHaveLength(1);
99
- expect(result[0]).toEqual({
100
- id: 'con_1',
101
- type: 'cookie_banner',
102
- policyId: 'pol_1',
103
- isLatestPolicy: true,
104
- preferences: { analytics: true, marketing: true },
105
- givenAt: new Date('2024-01-01'),
106
- });
107
- });
108
-
109
- it('uses batch findMany with "in" operator (not N individual calls)', async () => {
110
- const policies = [
111
- { id: 'pol_1', type: 'cookie_banner' },
112
- { id: 'pol_2', type: 'privacy_policy' },
113
- ];
114
- const purposes = [
115
- { id: 'pur_1', code: 'analytics' },
116
- { id: 'pur_2', code: 'marketing' },
117
- ];
118
- const ctx = createMockCtx(policies, purposes, {
119
- cookie_banner: { id: 'pol_1' },
120
- privacy_policy: { id: 'pol_2' },
121
- });
122
-
123
- await enrichConsents(
124
- [
125
- {
126
- id: 'c1',
127
- policyId: 'pol_1',
128
- purposeIds: ['pur_1'],
129
- givenAt: new Date(),
130
- },
131
- {
132
- id: 'c2',
133
- policyId: 'pol_2',
134
- purposeIds: ['pur_2'],
135
- givenAt: new Date(),
136
- },
137
- ],
138
- ctx
139
- );
140
-
141
- const findManyCalls = (ctx.db as any).findMany.mock.calls;
142
- // Exactly 2 findMany calls: one for policies, one for purposes
143
- expect(findManyCalls).toHaveLength(2);
144
- expect(findManyCalls[0][0]).toBe('consentPolicy');
145
- expect(findManyCalls[1][0]).toBe('consentPurpose');
146
- });
147
-
148
- it('sets type = "unknown" and isLatestPolicy = false for null policyId', async () => {
149
- const ctx = createMockCtx([], [{ id: 'pur_1', code: 'analytics' }]);
150
-
151
- const result = await enrichConsents(
152
- [
153
- {
154
- id: 'con_1',
155
- policyId: null,
156
- purposeIds: ['pur_1'],
157
- givenAt: new Date('2024-01-01'),
158
- },
159
- ],
160
- ctx
161
- );
162
-
163
- expect(result[0]!.type).toBe('unknown');
164
- expect(result[0]!.isLatestPolicy).toBe(false);
165
- expect(result[0]!.policyId).toBeUndefined();
166
- });
167
-
168
- it('isLatestPolicy is false when consent has an older policy', async () => {
169
- const ctx = createMockCtx(
170
- [{ id: 'pol_old', type: 'cookie_banner' }],
171
- [],
172
- { cookie_banner: { id: 'pol_new' } } // latest is different
173
- );
174
-
175
- const result = await enrichConsents(
176
- [
177
- {
178
- id: 'con_1',
179
- policyId: 'pol_old',
180
- purposeIds: null,
181
- givenAt: new Date(),
182
- },
183
- ],
184
- ctx
185
- );
186
-
187
- expect(result[0]!.isLatestPolicy).toBe(false);
188
- });
189
-
190
- it('handles { json: [...] } wrapper for purposeIds', async () => {
191
- const ctx = createMockCtx(
192
- [{ id: 'pol_1', type: 'cookie_banner' }],
193
- [{ id: 'pur_1', code: 'analytics' }],
194
- { cookie_banner: { id: 'pol_1' } }
195
- );
196
-
197
- const result = await enrichConsents(
198
- [
199
- {
200
- id: 'con_1',
201
- policyId: 'pol_1',
202
- purposeIds: { json: ['pur_1'] },
203
- givenAt: new Date(),
204
- },
205
- ],
206
- ctx
207
- );
208
-
209
- expect(result[0]!.preferences).toEqual({ analytics: true });
210
- });
211
-
212
- it('skips purposes not found in DB', async () => {
213
- const ctx = createMockCtx(
214
- [{ id: 'pol_1', type: 'cookie_banner' }],
215
- [{ id: 'pur_1', code: 'analytics' }], // pur_2 intentionally missing
216
- { cookie_banner: { id: 'pol_1' } }
217
- );
218
-
219
- const result = await enrichConsents(
220
- [
221
- {
222
- id: 'con_1',
223
- policyId: 'pol_1',
224
- purposeIds: ['pur_1', 'pur_2_missing'],
225
- givenAt: new Date(),
226
- },
227
- ],
228
- ctx
229
- );
230
-
231
- expect(result[0]!.preferences).toEqual({ analytics: true });
232
- });
233
-
234
- it('leaves preferences undefined when purposeIds is empty', async () => {
235
- const ctx = createMockCtx([{ id: 'pol_1', type: 'cookie_banner' }], [], {
236
- cookie_banner: { id: 'pol_1' },
237
- });
238
-
239
- const result = await enrichConsents(
240
- [
241
- {
242
- id: 'con_1',
243
- policyId: 'pol_1',
244
- purposeIds: [],
245
- givenAt: new Date(),
246
- },
247
- ],
248
- ctx
249
- );
250
-
251
- expect(result[0]!.preferences).toBeUndefined();
252
- });
253
-
254
- it('does not call findMany for policies when no consents have policyId', async () => {
255
- const ctx = createMockCtx();
256
-
257
- await enrichConsents(
258
- [
259
- {
260
- id: 'con_1',
261
- policyId: null,
262
- purposeIds: null,
263
- givenAt: new Date(),
264
- },
265
- ],
266
- ctx
267
- );
268
-
269
- const findManyCalls = (ctx.db as any).findMany.mock.calls;
270
- // Should not call findMany for consentPolicy (empty policyIds set)
271
- const policyCalls = findManyCalls.filter(
272
- (c: unknown[]) => c[0] === 'consentPolicy'
273
- );
274
- expect(policyCalls).toHaveLength(0);
275
- });
276
- });
277
-
278
- // ── resolveConsentPolicies ──────────────────────────────────────────
279
- describe('resolveConsentPolicies', () => {
280
- function createMockCtx(
281
- policies: Array<{ id: string; type: string }> = [],
282
- latestByType: Record<string, { id: string }> = {}
283
- ) {
284
- const db = {
285
- findMany: vi.fn(() => Promise.resolve(policies)),
286
- };
287
-
288
- const registry = {
289
- findOrCreatePolicy: vi.fn((type: string) =>
290
- Promise.resolve(latestByType[type] ?? { id: `latest_${type}` })
291
- ),
292
- findConsentPolicyById: vi.fn(),
293
- };
294
-
295
- return { db, registry } as unknown as Parameters<
296
- typeof resolveConsentPolicies
297
- >[1];
298
- }
299
-
300
- it('returns [] for empty consents', async () => {
301
- const ctx = createMockCtx();
302
- const result = await resolveConsentPolicies([], ctx);
303
- expect(result).toEqual([]);
304
- });
305
-
306
- it('returns correct policy info with isLatestPolicy', async () => {
307
- const ctx = createMockCtx([{ id: 'pol_1', type: 'cookie_banner' }], {
308
- cookie_banner: { id: 'pol_1' },
309
- });
310
-
311
- const result = await resolveConsentPolicies(
312
- [{ id: 'con_1', policyId: 'pol_1' }],
313
- ctx
314
- );
315
-
316
- expect(result).toEqual([
317
- {
318
- consentId: 'con_1',
319
- policyType: 'cookie_banner',
320
- policyId: 'pol_1',
321
- isLatestPolicy: true,
322
- },
323
- ]);
324
- });
325
-
326
- it('returns unknown type for null policyId', async () => {
327
- const ctx = createMockCtx();
328
-
329
- const result = await resolveConsentPolicies(
330
- [{ id: 'con_1', policyId: null }],
331
- ctx
332
- );
333
-
334
- expect(result[0]).toEqual({
335
- consentId: 'con_1',
336
- policyType: 'unknown',
337
- policyId: undefined,
338
- isLatestPolicy: false,
339
- });
340
- });
341
-
342
- it('does not load purposes (no consentPurpose findMany call)', async () => {
343
- const ctx = createMockCtx([{ id: 'pol_1', type: 'cookie_banner' }], {
344
- cookie_banner: { id: 'pol_1' },
345
- });
346
-
347
- await resolveConsentPolicies([{ id: 'con_1', policyId: 'pol_1' }], ctx);
348
-
349
- const findManyCalls = (ctx.db as any).findMany.mock.calls;
350
- const purposeCalls = findManyCalls.filter(
351
- (c: unknown[]) => c[0] === 'consentPurpose'
352
- );
353
- expect(purposeCalls).toHaveLength(0);
354
- });
355
-
356
- it('handles multiple consents with different policy types', async () => {
357
- const ctx = createMockCtx(
358
- [
359
- { id: 'pol_1', type: 'cookie_banner' },
360
- { id: 'pol_2', type: 'privacy_policy' },
361
- ],
362
- {
363
- cookie_banner: { id: 'pol_1' },
364
- privacy_policy: { id: 'pol_latest' }, // different from pol_2
365
- }
366
- );
367
-
368
- const result = await resolveConsentPolicies(
369
- [
370
- { id: 'con_1', policyId: 'pol_1' },
371
- { id: 'con_2', policyId: 'pol_2' },
372
- ],
373
- ctx
374
- );
375
-
376
- expect(result[0]!.isLatestPolicy).toBe(true);
377
- expect(result[1]!.isLatestPolicy).toBe(false);
378
- });
379
- });
380
- });
@@ -1,218 +0,0 @@
1
- /**
2
- * Shared consent enrichment utilities.
3
- *
4
- * Extracts duplicated consent-building logic from get/list/check handlers
5
- * and batch-loads policies + purposes to eliminate N+1 queries.
6
- *
7
- * @packageDocumentation
8
- */
9
-
10
- import type { PolicyType } from '~/db/schema';
11
- import type { C15TContext } from '~/types';
12
-
13
- type EnrichmentContext = Pick<C15TContext, 'db' | 'registry'>;
14
-
15
- export interface EnrichedConsentItem {
16
- id: string;
17
- type: string;
18
- policyId: string | undefined;
19
- isLatestPolicy: boolean;
20
- preferences: Record<string, boolean> | undefined;
21
- givenAt: Date;
22
- }
23
-
24
- export interface ConsentPolicyInfo {
25
- consentId: string;
26
- policyType: string;
27
- policyId: string | undefined;
28
- isLatestPolicy: boolean;
29
- }
30
-
31
- /**
32
- * Parse purposeIds from DB — handles both `{ json: string[] }` wrapper
33
- * (some adapters) and raw `string[]` format.
34
- */
35
- export function parsePurposeIds(purposeIds: unknown): string[] {
36
- if (purposeIds == null) return [];
37
-
38
- const ids =
39
- typeof purposeIds === 'object' && 'json' in (purposeIds as object)
40
- ? (purposeIds as { json: unknown }).json
41
- : purposeIds;
42
-
43
- return Array.isArray(ids) ? ids : [];
44
- }
45
-
46
- /**
47
- * Batch-fetch all referenced policies and resolve the latest policy per type.
48
- *
49
- * 1 query for findMany('consentPolicy', { where: id IN ... })
50
- * ≤7 calls for findOrCreatePolicy (one per unique PolicyType)
51
- */
52
- async function batchLoadPolicies(
53
- policyIds: Set<string>,
54
- ctx: EnrichmentContext
55
- ) {
56
- const { db, registry } = ctx;
57
-
58
- // Fetch all referenced policies in one query
59
- const policyMap = new Map<
60
- string,
61
- { id: string; type: string; [key: string]: unknown }
62
- >();
63
-
64
- if (policyIds.size > 0) {
65
- const policies = await db.findMany('consentPolicy', {
66
- where: (b) => b('id', 'in', [...policyIds]),
67
- });
68
- for (const p of policies) {
69
- policyMap.set(p.id, p);
70
- }
71
- }
72
-
73
- // Resolve the latest policy for each unique type
74
- const uniqueTypes = new Set<string>();
75
- for (const p of policyMap.values()) {
76
- uniqueTypes.add(p.type);
77
- }
78
-
79
- const latestPolicyByType = new Map<string, string>();
80
- for (const type of uniqueTypes) {
81
- const latest = await registry.findOrCreatePolicy(type as PolicyType);
82
- if (latest) {
83
- latestPolicyByType.set(type, latest.id);
84
- }
85
- }
86
-
87
- return { policyMap, latestPolicyByType };
88
- }
89
-
90
- /**
91
- * Full consent enrichment for get/list handlers.
92
- *
93
- * Resolves policy types, isLatestPolicy, and purpose preferences
94
- * using batch queries instead of per-consent lookups.
95
- */
96
- export async function enrichConsents(
97
- consents: Array<{
98
- id: string;
99
- policyId: string | null;
100
- purposeIds: unknown;
101
- givenAt: Date;
102
- }>,
103
- ctx: EnrichmentContext
104
- ): Promise<EnrichedConsentItem[]> {
105
- if (consents.length === 0) return [];
106
-
107
- // Collect all policy IDs
108
- const policyIds = new Set<string>();
109
- for (const c of consents) {
110
- if (c.policyId) policyIds.add(c.policyId);
111
- }
112
-
113
- const { policyMap, latestPolicyByType } = await batchLoadPolicies(
114
- policyIds,
115
- ctx
116
- );
117
-
118
- // Collect all purpose IDs across all consents
119
- const allPurposeIds = new Set<string>();
120
- for (const c of consents) {
121
- for (const id of parsePurposeIds(c.purposeIds)) {
122
- allPurposeIds.add(id);
123
- }
124
- }
125
-
126
- // Batch-fetch all purposes in one query
127
- const purposeMap = new Map<string, string>(); // id → code
128
- if (allPurposeIds.size > 0) {
129
- const purposes = await ctx.db.findMany('consentPurpose', {
130
- where: (b) => b('id', 'in', [...allPurposeIds]),
131
- });
132
- for (const p of purposes) {
133
- purposeMap.set(p.id, p.code);
134
- }
135
- }
136
-
137
- // Map consents synchronously — zero additional queries
138
- return consents.map((consent) => {
139
- let policyType = 'unknown';
140
- let isLatestPolicy = false;
141
-
142
- if (consent.policyId) {
143
- const policy = policyMap.get(consent.policyId);
144
- if (policy) {
145
- policyType = policy.type;
146
- isLatestPolicy =
147
- latestPolicyByType.get(policyType) === consent.policyId;
148
- }
149
- }
150
-
151
- let preferences: Record<string, boolean> | undefined;
152
- const ids = parsePurposeIds(consent.purposeIds);
153
- if (ids.length > 0) {
154
- preferences = {};
155
- for (const purposeId of ids) {
156
- const code = purposeMap.get(purposeId);
157
- if (code) {
158
- preferences[code] = true;
159
- }
160
- }
161
- }
162
-
163
- return {
164
- id: consent.id,
165
- type: policyType,
166
- policyId: consent.policyId ?? undefined,
167
- isLatestPolicy,
168
- preferences,
169
- givenAt: consent.givenAt,
170
- };
171
- });
172
- }
173
-
174
- /**
175
- * Policy-only enrichment for check handler.
176
- *
177
- * Resolves policy type and isLatestPolicy without loading purposes.
178
- */
179
- export async function resolveConsentPolicies(
180
- consents: Array<{
181
- id: string;
182
- policyId: string | null;
183
- }>,
184
- ctx: EnrichmentContext
185
- ): Promise<ConsentPolicyInfo[]> {
186
- if (consents.length === 0) return [];
187
-
188
- const policyIds = new Set<string>();
189
- for (const c of consents) {
190
- if (c.policyId) policyIds.add(c.policyId);
191
- }
192
-
193
- const { policyMap, latestPolicyByType } = await batchLoadPolicies(
194
- policyIds,
195
- ctx
196
- );
197
-
198
- return consents.map((consent) => {
199
- let policyType = 'unknown';
200
- let isLatestPolicy = false;
201
-
202
- if (consent.policyId) {
203
- const policy = policyMap.get(consent.policyId);
204
- if (policy) {
205
- policyType = policy.type;
206
- isLatestPolicy =
207
- latestPolicyByType.get(policyType) === consent.policyId;
208
- }
209
- }
210
-
211
- return {
212
- consentId: consent.id,
213
- policyType,
214
- policyId: consent.policyId ?? undefined,
215
- isLatestPolicy,
216
- };
217
- });
218
- }
package/src/init.test.ts DELETED
@@ -1,126 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { init } from './init';
3
- import type { C15TOptions } from './types';
4
-
5
- // Use vi.hoisted so these are available inside vi.mock factories (which are hoisted)
6
- const { mockLogger, mockClient, mockClientOrm } = vi.hoisted(() => {
7
- const mockLogger = {
8
- debug: vi.fn(),
9
- info: vi.fn(),
10
- warn: vi.fn(),
11
- error: vi.fn(),
12
- success: vi.fn(),
13
- };
14
-
15
- const mockClientOrm = vi.fn().mockReturnValue({});
16
- const mockClient = vi.fn().mockReturnValue({
17
- orm: mockClientOrm,
18
- });
19
-
20
- return { mockLogger, mockClient, mockClientOrm };
21
- });
22
-
23
- // Mock local modules
24
- vi.mock('./utils/logger', () => ({
25
- initLogger: vi.fn(() => mockLogger),
26
- }));
27
-
28
- vi.mock('./utils/create-telemetry-options', () => ({
29
- createTelemetryOptions: vi.fn((appName, config) => ({
30
- enabled: config?.enabled ?? false,
31
- tracer: config?.tracer,
32
- meter: config?.meter,
33
- defaultAttributes: {
34
- 'service.name': appName,
35
- 'service.version': '1.0.0',
36
- ...config?.defaultAttributes,
37
- },
38
- })),
39
- isTelemetryEnabled: vi.fn(
40
- (options) => options?.advanced?.telemetry?.enabled === true
41
- ),
42
- }));
43
-
44
- vi.mock('./db/registry', () => ({
45
- createRegistry: vi.fn().mockReturnValue({}),
46
- }));
47
-
48
- vi.mock('./db/schema', () => ({
49
- DB: {
50
- client: mockClient,
51
- },
52
- }));
53
-
54
- beforeEach(() => {
55
- vi.clearAllMocks();
56
- });
57
-
58
- afterEach(() => {
59
- vi.clearAllMocks();
60
- });
61
-
62
- function createOptions(overrides: Partial<C15TOptions> = {}): C15TOptions {
63
- return {
64
- trustedOrigins: [],
65
- adapter: {} as C15TOptions['adapter'],
66
- ...overrides,
67
- };
68
- }
69
-
70
- describe('init', () => {
71
- it('uses "c15t" as default appName when none is provided', () => {
72
- const options = createOptions();
73
- const context = init(options);
74
-
75
- expect(context.appName).toBe('c15t');
76
- expect(mockClient).toHaveBeenCalledTimes(1);
77
- });
78
-
79
- it('uses the provided appName', () => {
80
- const options = createOptions({ appName: 'MyAmazingApp' });
81
- const context = init(options);
82
-
83
- expect(context.appName).toBe('MyAmazingApp');
84
- });
85
-
86
- it('telemetry is disabled by default (opt-in)', () => {
87
- const options = createOptions();
88
- init(options);
89
-
90
- // Check that logger was called with telemetry disabled message
91
- expect(mockLogger.debug).toHaveBeenCalledWith(
92
- 'Telemetry is disabled (opt-in required)'
93
- );
94
- });
95
-
96
- it('logs telemetry enabled when explicitly enabled', () => {
97
- const options = createOptions({
98
- advanced: {
99
- telemetry: {
100
- enabled: true,
101
- },
102
- },
103
- });
104
- init(options);
105
-
106
- // Check that logger was called with telemetry enabled message
107
- expect(mockLogger.debug).toHaveBeenCalledWith(
108
- 'Telemetry is enabled',
109
- expect.objectContaining({
110
- hasTracer: false,
111
- hasMeter: false,
112
- })
113
- );
114
- });
115
-
116
- it('creates context with required properties', () => {
117
- const options = createOptions();
118
- const context = init(options);
119
-
120
- expect(context).toHaveProperty('appName');
121
- expect(context).toHaveProperty('logger');
122
- expect(context).toHaveProperty('db');
123
- expect(context).toHaveProperty('registry');
124
- expect(context).toHaveProperty('trustedOrigins');
125
- });
126
- });