@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
@@ -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,122 +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((options) => options?.telemetry?.enabled === true),
40
- }));
41
-
42
- vi.mock('./db/registry', () => ({
43
- createRegistry: vi.fn().mockReturnValue({}),
44
- }));
45
-
46
- vi.mock('./db/schema', () => ({
47
- DB: {
48
- client: mockClient,
49
- },
50
- }));
51
-
52
- beforeEach(() => {
53
- vi.clearAllMocks();
54
- });
55
-
56
- afterEach(() => {
57
- vi.clearAllMocks();
58
- });
59
-
60
- function createOptions(overrides: Partial<C15TOptions> = {}): C15TOptions {
61
- return {
62
- trustedOrigins: [],
63
- adapter: {} as C15TOptions['adapter'],
64
- ...overrides,
65
- };
66
- }
67
-
68
- describe('init', () => {
69
- it('uses "c15t" as default appName when none is provided', () => {
70
- const options = createOptions();
71
- const context = init(options);
72
-
73
- expect(context.appName).toBe('c15t');
74
- expect(mockClient).toHaveBeenCalledTimes(1);
75
- });
76
-
77
- it('uses the provided appName', () => {
78
- const options = createOptions({ appName: 'MyAmazingApp' });
79
- const context = init(options);
80
-
81
- expect(context.appName).toBe('MyAmazingApp');
82
- });
83
-
84
- it('telemetry is disabled by default (opt-in)', () => {
85
- const options = createOptions();
86
- init(options);
87
-
88
- // Check that logger was called with telemetry disabled message
89
- expect(mockLogger.debug).toHaveBeenCalledWith(
90
- 'Telemetry is disabled (opt-in required)'
91
- );
92
- });
93
-
94
- it('logs telemetry enabled when explicitly enabled', () => {
95
- const options = createOptions({
96
- telemetry: {
97
- enabled: true,
98
- },
99
- });
100
- init(options);
101
-
102
- // Check that logger was called with telemetry enabled message
103
- expect(mockLogger.debug).toHaveBeenCalledWith(
104
- 'Telemetry is enabled',
105
- expect.objectContaining({
106
- hasTracer: false,
107
- hasMeter: false,
108
- })
109
- );
110
- });
111
-
112
- it('creates context with required properties', () => {
113
- const options = createOptions();
114
- const context = init(options);
115
-
116
- expect(context).toHaveProperty('appName');
117
- expect(context).toHaveProperty('logger');
118
- expect(context).toHaveProperty('db');
119
- expect(context).toHaveProperty('registry');
120
- expect(context).toHaveProperty('trustedOrigins');
121
- });
122
- });