@c15t/backend 2.0.0-rc.3 → 2.0.0-rc.5

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 (314) hide show
  1. package/dist/cache.cjs +4 -4
  2. package/dist/cache.js +4 -4
  3. package/dist/core.cjs +845 -87
  4. package/dist/core.js +821 -87
  5. package/dist/db/schema.cjs +37 -0
  6. package/dist/db/schema.js +33 -2
  7. package/dist/edge.cjs +1106 -0
  8. package/dist/edge.js +1069 -0
  9. package/dist/router.cjs +621 -71
  10. package/dist/router.js +621 -71
  11. package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
  12. package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
  13. package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
  14. package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
  15. package/{dist → dist-types}/cache/gvl-resolver.d.ts +1 -2
  16. package/{dist → dist-types}/cache/index.d.ts +0 -1
  17. package/{dist → dist-types}/cache/keys.d.ts +0 -1
  18. package/{dist → dist-types}/cache/types.d.ts +0 -1
  19. package/{dist → dist-types}/core.d.ts +8 -1
  20. package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
  21. package/{dist → dist-types}/db/registry/consent-policy.d.ts +0 -1
  22. package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
  23. package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
  24. package/{dist → dist-types}/db/registry/index.d.ts +22 -2
  25. package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
  26. package/{dist → dist-types}/db/registry/subject.d.ts +0 -1
  27. package/{dist → dist-types}/db/registry/types.d.ts +1 -2
  28. package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
  29. package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
  30. package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
  31. package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
  32. package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
  33. package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
  34. package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +1 -2
  35. package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
  36. package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -1
  37. package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -1
  38. package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +1 -2
  39. package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +1 -2
  40. package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +1 -2
  41. package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +5 -2
  42. package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +1 -2
  43. package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +432 -17
  44. package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
  45. package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +1 -2
  46. package/{dist → dist-types}/db/schema/index.d.ts +862 -33
  47. package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
  48. package/dist-types/define-config.d.ts +17 -0
  49. package/dist-types/edge/index.d.ts +5 -0
  50. package/dist-types/edge/init-handler.d.ts +38 -0
  51. package/dist-types/edge/resolve-consent.d.ts +80 -0
  52. package/dist-types/edge/types.d.ts +13 -0
  53. package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
  54. package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
  55. package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
  56. package/{dist → dist-types}/handlers/init/index.d.ts +4 -5
  57. package/dist-types/handlers/init/policy.d.ts +26 -0
  58. package/dist-types/handlers/init/resolve-init.d.ts +44 -0
  59. package/dist-types/handlers/init/translations.d.ts +48 -0
  60. package/dist-types/handlers/policy/snapshot.d.ts +99 -0
  61. package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
  62. package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
  63. package/{dist → dist-types}/handlers/subject/get.handler.d.ts +0 -1
  64. package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
  65. package/{dist → dist-types}/handlers/subject/list.handler.d.ts +0 -1
  66. package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -1
  67. package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
  68. package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +0 -1
  69. package/{dist → dist-types}/init.d.ts +4 -7
  70. package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
  71. package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
  72. package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
  73. package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
  74. package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +1 -2
  75. package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
  76. package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
  77. package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
  78. package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
  79. package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
  80. package/dist-types/policies/builder.d.ts +127 -0
  81. package/dist-types/policies/defaults.d.ts +2 -0
  82. package/dist-types/policies/matchers.d.ts +3 -0
  83. package/{dist → dist-types}/router.d.ts +0 -1
  84. package/{dist → dist-types}/routes/consent.d.ts +0 -1
  85. package/{src/routes/index.ts → dist-types/routes/index.d.ts} +0 -1
  86. package/{dist → dist-types}/routes/init.d.ts +0 -1
  87. package/{dist → dist-types}/routes/status.d.ts +0 -1
  88. package/{dist → dist-types}/routes/subject.d.ts +0 -1
  89. package/{dist → dist-types}/types/api.d.ts +0 -1
  90. package/dist-types/types/index.d.ts +443 -0
  91. package/dist-types/utils/background.d.ts +6 -0
  92. package/{dist → dist-types}/utils/create-telemetry-options.d.ts +1 -2
  93. package/{dist → dist-types}/utils/env.d.ts +0 -1
  94. package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
  95. package/{dist → dist-types}/utils/instrumentation.d.ts +0 -1
  96. package/{dist → dist-types}/utils/logger.d.ts +1 -2
  97. package/{dist → dist-types}/utils/metrics.d.ts +0 -1
  98. package/dist-types/version.d.ts +1 -0
  99. package/docs/README.md +49 -0
  100. package/docs/api/configuration.md +197 -0
  101. package/docs/api/endpoints.md +211 -0
  102. package/docs/guides/caching.md +85 -0
  103. package/docs/guides/database-setup.md +128 -0
  104. package/docs/guides/edge-deployment.md +248 -0
  105. package/docs/guides/framework-integration.md +142 -0
  106. package/docs/guides/iab-tcf.md +89 -0
  107. package/docs/guides/observability.md +96 -0
  108. package/docs/guides/policy-packs.md +396 -0
  109. package/docs/quickstart.md +129 -0
  110. package/package.json +37 -23
  111. package/.turbo/turbo-build.log +0 -49
  112. package/CHANGELOG.md +0 -115
  113. package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
  114. package/dist/cache/adapters/index.d.ts.map +0 -1
  115. package/dist/cache/adapters/memory.d.ts.map +0 -1
  116. package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
  117. package/dist/cache/gvl-resolver.d.ts.map +0 -1
  118. package/dist/cache/index.d.ts.map +0 -1
  119. package/dist/cache/keys.d.ts.map +0 -1
  120. package/dist/cache/types.d.ts.map +0 -1
  121. package/dist/core.d.ts.map +0 -1
  122. package/dist/db/adapters/drizzle.d.ts +0 -2
  123. package/dist/db/adapters/drizzle.d.ts.map +0 -1
  124. package/dist/db/adapters/index.d.ts +0 -2
  125. package/dist/db/adapters/index.d.ts.map +0 -1
  126. package/dist/db/adapters/kysely.d.ts +0 -2
  127. package/dist/db/adapters/kysely.d.ts.map +0 -1
  128. package/dist/db/adapters/mongo.d.ts +0 -2
  129. package/dist/db/adapters/mongo.d.ts.map +0 -1
  130. package/dist/db/adapters/prisma.d.ts +0 -2
  131. package/dist/db/adapters/prisma.d.ts.map +0 -1
  132. package/dist/db/adapters/typeorm.d.ts +0 -2
  133. package/dist/db/adapters/typeorm.d.ts.map +0 -1
  134. package/dist/db/migrator/index.d.ts.map +0 -1
  135. package/dist/db/registry/consent-policy.d.ts.map +0 -1
  136. package/dist/db/registry/consent-purpose.d.ts.map +0 -1
  137. package/dist/db/registry/domain.d.ts.map +0 -1
  138. package/dist/db/registry/index.d.ts.map +0 -1
  139. package/dist/db/registry/subject.d.ts.map +0 -1
  140. package/dist/db/registry/types.d.ts.map +0 -1
  141. package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
  142. package/dist/db/registry/utils.d.ts.map +0 -1
  143. package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
  144. package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
  145. package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
  146. package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
  147. package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
  148. package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
  149. package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
  150. package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
  151. package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
  152. package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
  153. package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
  154. package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
  155. package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
  156. package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
  157. package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
  158. package/dist/db/schema/index.d.ts.map +0 -1
  159. package/dist/db/tenant-scope.d.ts.map +0 -1
  160. package/dist/define-config.d.ts +0 -5
  161. package/dist/define-config.d.ts.map +0 -1
  162. package/dist/handlers/consent/check.handler.d.ts.map +0 -1
  163. package/dist/handlers/consent/index.d.ts +0 -12
  164. package/dist/handlers/consent/index.d.ts.map +0 -1
  165. package/dist/handlers/init/geo.d.ts.map +0 -1
  166. package/dist/handlers/init/index.d.ts.map +0 -1
  167. package/dist/handlers/init/translations.d.ts +0 -28
  168. package/dist/handlers/init/translations.d.ts.map +0 -1
  169. package/dist/handlers/status/index.d.ts +0 -7
  170. package/dist/handlers/status/index.d.ts.map +0 -1
  171. package/dist/handlers/status/status.handler.d.ts.map +0 -1
  172. package/dist/handlers/subject/get.handler.d.ts.map +0 -1
  173. package/dist/handlers/subject/index.d.ts +0 -10
  174. package/dist/handlers/subject/index.d.ts.map +0 -1
  175. package/dist/handlers/subject/list.handler.d.ts.map +0 -1
  176. package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
  177. package/dist/handlers/subject/post.handler.d.ts.map +0 -1
  178. package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
  179. package/dist/init.d.ts.map +0 -1
  180. package/dist/middleware/auth/index.d.ts.map +0 -1
  181. package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
  182. package/dist/middleware/cors/cors.d.ts.map +0 -1
  183. package/dist/middleware/cors/index.d.ts +0 -30
  184. package/dist/middleware/cors/index.d.ts.map +0 -1
  185. package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
  186. package/dist/middleware/cors/process-cors.d.ts.map +0 -1
  187. package/dist/middleware/openapi/config.d.ts.map +0 -1
  188. package/dist/middleware/openapi/handlers.d.ts.map +0 -1
  189. package/dist/middleware/openapi/index.d.ts +0 -12
  190. package/dist/middleware/openapi/index.d.ts.map +0 -1
  191. package/dist/middleware/process-ip/index.d.ts.map +0 -1
  192. package/dist/router.d.ts.map +0 -1
  193. package/dist/routes/consent.d.ts.map +0 -1
  194. package/dist/routes/index.d.ts +0 -10
  195. package/dist/routes/index.d.ts.map +0 -1
  196. package/dist/routes/init.d.ts.map +0 -1
  197. package/dist/routes/status.d.ts.map +0 -1
  198. package/dist/routes/subject.d.ts.map +0 -1
  199. package/dist/types/api.d.ts.map +0 -1
  200. package/dist/types/index.d.ts +0 -263
  201. package/dist/types/index.d.ts.map +0 -1
  202. package/dist/utils/create-telemetry-options.d.ts.map +0 -1
  203. package/dist/utils/env.d.ts.map +0 -1
  204. package/dist/utils/extract-error-message.d.ts.map +0 -1
  205. package/dist/utils/index.d.ts +0 -4
  206. package/dist/utils/index.d.ts.map +0 -1
  207. package/dist/utils/instrumentation.d.ts.map +0 -1
  208. package/dist/utils/logger.d.ts.map +0 -1
  209. package/dist/utils/metrics.d.ts.map +0 -1
  210. package/dist/version.d.ts +0 -2
  211. package/dist/version.d.ts.map +0 -1
  212. package/knip.json +0 -31
  213. package/rslib.config.ts +0 -93
  214. package/src/cache/adapters/cloudflare-kv.ts +0 -71
  215. package/src/cache/adapters/index.ts +0 -22
  216. package/src/cache/adapters/memory.ts +0 -111
  217. package/src/cache/adapters/upstash-redis.ts +0 -113
  218. package/src/cache/gvl-resolver.ts +0 -289
  219. package/src/cache/index.ts +0 -34
  220. package/src/cache/keys.ts +0 -68
  221. package/src/cache/types.ts +0 -66
  222. package/src/core.ts +0 -369
  223. package/src/db/migrator/index.ts +0 -80
  224. package/src/db/registry/consent-policy.test.ts +0 -451
  225. package/src/db/registry/consent-policy.ts +0 -82
  226. package/src/db/registry/consent-purpose.test.ts +0 -428
  227. package/src/db/registry/consent-purpose.ts +0 -61
  228. package/src/db/registry/domain.test.ts +0 -445
  229. package/src/db/registry/domain.ts +0 -91
  230. package/src/db/registry/index.ts +0 -14
  231. package/src/db/registry/subject.test.ts +0 -371
  232. package/src/db/registry/subject.ts +0 -126
  233. package/src/db/registry/types.ts +0 -10
  234. package/src/db/registry/utils/generate-id.test.ts +0 -216
  235. package/src/db/registry/utils/generate-id.ts +0 -133
  236. package/src/db/registry/utils.ts +0 -133
  237. package/src/db/schema/1.0.0/audit-log.ts +0 -15
  238. package/src/db/schema/1.0.0/consent-policy.ts +0 -14
  239. package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
  240. package/src/db/schema/1.0.0/consent-record.ts +0 -10
  241. package/src/db/schema/1.0.0/consent.ts +0 -20
  242. package/src/db/schema/1.0.0/domain.ts +0 -12
  243. package/src/db/schema/1.0.0/index.ts +0 -48
  244. package/src/db/schema/1.0.0/subject.ts +0 -11
  245. package/src/db/schema/2.0.0/audit-log.ts +0 -18
  246. package/src/db/schema/2.0.0/consent-policy.ts +0 -28
  247. package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
  248. package/src/db/schema/2.0.0/consent.ts +0 -28
  249. package/src/db/schema/2.0.0/domain.ts +0 -12
  250. package/src/db/schema/2.0.0/index.ts +0 -47
  251. package/src/db/schema/2.0.0/subject.ts +0 -13
  252. package/src/db/schema/index.ts +0 -15
  253. package/src/db/tenant-scope.test.ts +0 -747
  254. package/src/db/tenant-scope.ts +0 -103
  255. package/src/define-config.ts +0 -5
  256. package/src/handlers/consent/check.handler.ts +0 -126
  257. package/src/handlers/init/geo.test.ts +0 -317
  258. package/src/handlers/init/geo.ts +0 -195
  259. package/src/handlers/init/index.test.ts +0 -205
  260. package/src/handlers/init/index.ts +0 -114
  261. package/src/handlers/init/translations.test.ts +0 -121
  262. package/src/handlers/init/translations.ts +0 -72
  263. package/src/handlers/status/status.handler.test.ts +0 -155
  264. package/src/handlers/status/status.handler.ts +0 -51
  265. package/src/handlers/subject/get.handler.ts +0 -92
  266. package/src/handlers/subject/list.handler.ts +0 -92
  267. package/src/handlers/subject/patch.handler.ts +0 -119
  268. package/src/handlers/subject/post.handler.test.ts +0 -294
  269. package/src/handlers/subject/post.handler.ts +0 -268
  270. package/src/handlers/utils/consent-enrichment.test.ts +0 -380
  271. package/src/handlers/utils/consent-enrichment.ts +0 -218
  272. package/src/init.test.ts +0 -126
  273. package/src/init.ts +0 -87
  274. package/src/middleware/auth/index.ts +0 -11
  275. package/src/middleware/auth/validate-api-key.test.ts +0 -86
  276. package/src/middleware/auth/validate-api-key.ts +0 -107
  277. package/src/middleware/cors/cors.test.ts +0 -135
  278. package/src/middleware/cors/cors.ts +0 -186
  279. package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
  280. package/src/middleware/cors/is-origin-trusted.ts +0 -130
  281. package/src/middleware/cors/process-cors.ts +0 -91
  282. package/src/middleware/openapi/config.ts +0 -29
  283. package/src/middleware/openapi/handlers.ts +0 -34
  284. package/src/middleware/process-ip/index.test.ts +0 -195
  285. package/src/middleware/process-ip/index.ts +0 -199
  286. package/src/router.ts +0 -15
  287. package/src/routes/consent.ts +0 -52
  288. package/src/routes/init.ts +0 -105
  289. package/src/routes/status.ts +0 -46
  290. package/src/routes/subject.ts +0 -152
  291. package/src/types/api.ts +0 -48
  292. package/src/types/index.ts +0 -297
  293. package/src/utils/create-telemetry-options.test.ts +0 -302
  294. package/src/utils/create-telemetry-options.ts +0 -229
  295. package/src/utils/env.ts +0 -84
  296. package/src/utils/extract-error-message.ts +0 -21
  297. package/src/utils/instrumentation.test.ts +0 -185
  298. package/src/utils/instrumentation.ts +0 -196
  299. package/src/utils/logger.ts +0 -41
  300. package/src/utils/metrics.test.ts +0 -323
  301. package/src/utils/metrics.ts +0 -402
  302. package/src/utils/telemetry-pii.test.ts +0 -325
  303. package/src/version.ts +0 -2
  304. package/tsconfig.json +0 -11
  305. package/vitest.config.ts +0 -28
  306. /package/dist/{types.cjs → types/index.cjs} +0 -0
  307. /package/dist/{types.js → types/index.js} +0 -0
  308. /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
  309. /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
  310. /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
  311. /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
  312. /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
  313. /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
  314. /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
- });