@c15t/backend 2.0.0-rc.4 → 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 (308) hide show
  1. package/dist/core.cjs +830 -74
  2. package/dist/core.js +807 -75
  3. package/dist/db/schema.cjs +37 -0
  4. package/dist/db/schema.js +33 -2
  5. package/dist/edge.cjs +1106 -0
  6. package/dist/edge.js +1069 -0
  7. package/dist/router.cjs +613 -64
  8. package/dist/router.js +613 -64
  9. package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
  10. package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
  11. package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
  12. package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
  13. package/{dist → dist-types}/cache/gvl-resolver.d.ts +1 -2
  14. package/{dist → dist-types}/cache/index.d.ts +0 -1
  15. package/{dist → dist-types}/cache/keys.d.ts +0 -1
  16. package/{dist → dist-types}/cache/types.d.ts +0 -1
  17. package/{dist → dist-types}/core.d.ts +8 -1
  18. package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
  19. package/{dist → dist-types}/db/registry/consent-policy.d.ts +0 -1
  20. package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
  21. package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
  22. package/{dist → dist-types}/db/registry/index.d.ts +22 -2
  23. package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
  24. package/{dist → dist-types}/db/registry/subject.d.ts +0 -1
  25. package/{dist → dist-types}/db/registry/types.d.ts +1 -2
  26. package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
  27. package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
  28. package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
  29. package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
  30. package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
  31. package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
  32. package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +1 -2
  33. package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
  34. package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -1
  35. package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -1
  36. package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +1 -2
  37. package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +1 -2
  38. package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +1 -2
  39. package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +5 -2
  40. package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +1 -2
  41. package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +432 -17
  42. package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
  43. package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +1 -2
  44. package/{dist → dist-types}/db/schema/index.d.ts +862 -33
  45. package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
  46. package/{dist → dist-types}/define-config.d.ts +0 -1
  47. package/dist-types/edge/index.d.ts +5 -0
  48. package/dist-types/edge/init-handler.d.ts +38 -0
  49. package/dist-types/edge/resolve-consent.d.ts +80 -0
  50. package/dist-types/edge/types.d.ts +13 -0
  51. package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
  52. package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
  53. package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
  54. package/{dist → dist-types}/handlers/init/index.d.ts +4 -5
  55. package/dist-types/handlers/init/policy.d.ts +26 -0
  56. package/dist-types/handlers/init/resolve-init.d.ts +44 -0
  57. package/dist-types/handlers/init/translations.d.ts +48 -0
  58. package/dist-types/handlers/policy/snapshot.d.ts +99 -0
  59. package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
  60. package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
  61. package/{dist → dist-types}/handlers/subject/get.handler.d.ts +0 -1
  62. package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
  63. package/{dist → dist-types}/handlers/subject/list.handler.d.ts +0 -1
  64. package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -1
  65. package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
  66. package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +0 -1
  67. package/{dist → dist-types}/init.d.ts +0 -1
  68. package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
  69. package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
  70. package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
  71. package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
  72. package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +1 -2
  73. package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
  74. package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
  75. package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
  76. package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
  77. package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
  78. package/dist-types/policies/builder.d.ts +127 -0
  79. package/dist-types/policies/defaults.d.ts +2 -0
  80. package/dist-types/policies/matchers.d.ts +3 -0
  81. package/{dist → dist-types}/router.d.ts +0 -1
  82. package/{dist → dist-types}/routes/consent.d.ts +0 -1
  83. package/{src/routes/index.ts → dist-types/routes/index.d.ts} +0 -1
  84. package/{dist → dist-types}/routes/init.d.ts +0 -1
  85. package/{dist → dist-types}/routes/status.d.ts +0 -1
  86. package/{dist → dist-types}/routes/subject.d.ts +0 -1
  87. package/{dist → dist-types}/types/api.d.ts +0 -1
  88. package/{dist → dist-types}/types/index.d.ts +110 -6
  89. package/dist-types/utils/background.d.ts +6 -0
  90. package/{dist → dist-types}/utils/create-telemetry-options.d.ts +0 -1
  91. package/{dist → dist-types}/utils/env.d.ts +0 -1
  92. package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
  93. package/{dist → dist-types}/utils/instrumentation.d.ts +0 -1
  94. package/{dist → dist-types}/utils/logger.d.ts +1 -2
  95. package/{dist → dist-types}/utils/metrics.d.ts +0 -1
  96. package/dist-types/version.d.ts +1 -0
  97. package/docs/README.md +49 -0
  98. package/docs/api/configuration.md +197 -0
  99. package/docs/api/endpoints.md +211 -0
  100. package/docs/guides/caching.md +85 -0
  101. package/docs/guides/database-setup.md +128 -0
  102. package/docs/guides/edge-deployment.md +248 -0
  103. package/docs/guides/framework-integration.md +142 -0
  104. package/docs/guides/iab-tcf.md +89 -0
  105. package/docs/guides/observability.md +96 -0
  106. package/docs/guides/policy-packs.md +396 -0
  107. package/docs/quickstart.md +129 -0
  108. package/package.json +33 -19
  109. package/.turbo/turbo-build.log +0 -49
  110. package/CHANGELOG.md +0 -123
  111. package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
  112. package/dist/cache/adapters/index.d.ts.map +0 -1
  113. package/dist/cache/adapters/memory.d.ts.map +0 -1
  114. package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
  115. package/dist/cache/gvl-resolver.d.ts.map +0 -1
  116. package/dist/cache/index.d.ts.map +0 -1
  117. package/dist/cache/keys.d.ts.map +0 -1
  118. package/dist/cache/types.d.ts.map +0 -1
  119. package/dist/core.d.ts.map +0 -1
  120. package/dist/db/adapters/drizzle.d.ts +0 -2
  121. package/dist/db/adapters/drizzle.d.ts.map +0 -1
  122. package/dist/db/adapters/index.d.ts +0 -2
  123. package/dist/db/adapters/index.d.ts.map +0 -1
  124. package/dist/db/adapters/kysely.d.ts +0 -2
  125. package/dist/db/adapters/kysely.d.ts.map +0 -1
  126. package/dist/db/adapters/mongo.d.ts +0 -2
  127. package/dist/db/adapters/mongo.d.ts.map +0 -1
  128. package/dist/db/adapters/prisma.d.ts +0 -2
  129. package/dist/db/adapters/prisma.d.ts.map +0 -1
  130. package/dist/db/adapters/typeorm.d.ts +0 -2
  131. package/dist/db/adapters/typeorm.d.ts.map +0 -1
  132. package/dist/db/migrator/index.d.ts.map +0 -1
  133. package/dist/db/registry/consent-policy.d.ts.map +0 -1
  134. package/dist/db/registry/consent-purpose.d.ts.map +0 -1
  135. package/dist/db/registry/domain.d.ts.map +0 -1
  136. package/dist/db/registry/index.d.ts.map +0 -1
  137. package/dist/db/registry/subject.d.ts.map +0 -1
  138. package/dist/db/registry/types.d.ts.map +0 -1
  139. package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
  140. package/dist/db/registry/utils.d.ts.map +0 -1
  141. package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
  142. package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
  143. package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
  144. package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
  145. package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
  146. package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
  147. package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
  148. package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
  149. package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
  150. package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
  151. package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
  152. package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
  153. package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
  154. package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
  155. package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
  156. package/dist/db/schema/index.d.ts.map +0 -1
  157. package/dist/db/tenant-scope.d.ts.map +0 -1
  158. package/dist/define-config.d.ts.map +0 -1
  159. package/dist/handlers/consent/check.handler.d.ts.map +0 -1
  160. package/dist/handlers/consent/index.d.ts +0 -12
  161. package/dist/handlers/consent/index.d.ts.map +0 -1
  162. package/dist/handlers/init/geo.d.ts.map +0 -1
  163. package/dist/handlers/init/index.d.ts.map +0 -1
  164. package/dist/handlers/init/translations.d.ts +0 -26
  165. package/dist/handlers/init/translations.d.ts.map +0 -1
  166. package/dist/handlers/status/index.d.ts +0 -7
  167. package/dist/handlers/status/index.d.ts.map +0 -1
  168. package/dist/handlers/status/status.handler.d.ts.map +0 -1
  169. package/dist/handlers/subject/get.handler.d.ts.map +0 -1
  170. package/dist/handlers/subject/index.d.ts +0 -10
  171. package/dist/handlers/subject/index.d.ts.map +0 -1
  172. package/dist/handlers/subject/list.handler.d.ts.map +0 -1
  173. package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
  174. package/dist/handlers/subject/post.handler.d.ts.map +0 -1
  175. package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
  176. package/dist/init.d.ts.map +0 -1
  177. package/dist/middleware/auth/index.d.ts.map +0 -1
  178. package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
  179. package/dist/middleware/cors/cors.d.ts.map +0 -1
  180. package/dist/middleware/cors/index.d.ts +0 -30
  181. package/dist/middleware/cors/index.d.ts.map +0 -1
  182. package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
  183. package/dist/middleware/cors/process-cors.d.ts.map +0 -1
  184. package/dist/middleware/openapi/config.d.ts.map +0 -1
  185. package/dist/middleware/openapi/handlers.d.ts.map +0 -1
  186. package/dist/middleware/openapi/index.d.ts +0 -12
  187. package/dist/middleware/openapi/index.d.ts.map +0 -1
  188. package/dist/middleware/process-ip/index.d.ts.map +0 -1
  189. package/dist/router.d.ts.map +0 -1
  190. package/dist/routes/consent.d.ts.map +0 -1
  191. package/dist/routes/index.d.ts +0 -10
  192. package/dist/routes/index.d.ts.map +0 -1
  193. package/dist/routes/init.d.ts.map +0 -1
  194. package/dist/routes/status.d.ts.map +0 -1
  195. package/dist/routes/subject.d.ts.map +0 -1
  196. package/dist/types/api.d.ts.map +0 -1
  197. package/dist/types/index.d.ts.map +0 -1
  198. package/dist/utils/create-telemetry-options.d.ts.map +0 -1
  199. package/dist/utils/env.d.ts.map +0 -1
  200. package/dist/utils/extract-error-message.d.ts.map +0 -1
  201. package/dist/utils/index.d.ts +0 -4
  202. package/dist/utils/index.d.ts.map +0 -1
  203. package/dist/utils/instrumentation.d.ts.map +0 -1
  204. package/dist/utils/logger.d.ts.map +0 -1
  205. package/dist/utils/metrics.d.ts.map +0 -1
  206. package/dist/version.d.ts +0 -2
  207. package/dist/version.d.ts.map +0 -1
  208. package/knip.json +0 -31
  209. package/rslib.config.ts +0 -93
  210. package/src/cache/adapters/cloudflare-kv.ts +0 -71
  211. package/src/cache/adapters/index.ts +0 -22
  212. package/src/cache/adapters/memory.ts +0 -111
  213. package/src/cache/adapters/upstash-redis.ts +0 -113
  214. package/src/cache/gvl-resolver.ts +0 -289
  215. package/src/cache/index.ts +0 -34
  216. package/src/cache/keys.ts +0 -68
  217. package/src/cache/types.ts +0 -66
  218. package/src/core.ts +0 -369
  219. package/src/db/migrator/index.ts +0 -80
  220. package/src/db/registry/consent-policy.test.ts +0 -451
  221. package/src/db/registry/consent-policy.ts +0 -82
  222. package/src/db/registry/consent-purpose.test.ts +0 -428
  223. package/src/db/registry/consent-purpose.ts +0 -61
  224. package/src/db/registry/domain.test.ts +0 -445
  225. package/src/db/registry/domain.ts +0 -91
  226. package/src/db/registry/index.ts +0 -14
  227. package/src/db/registry/subject.test.ts +0 -371
  228. package/src/db/registry/subject.ts +0 -126
  229. package/src/db/registry/types.ts +0 -10
  230. package/src/db/registry/utils/generate-id.test.ts +0 -216
  231. package/src/db/registry/utils/generate-id.ts +0 -133
  232. package/src/db/registry/utils.ts +0 -133
  233. package/src/db/schema/1.0.0/audit-log.ts +0 -15
  234. package/src/db/schema/1.0.0/consent-policy.ts +0 -14
  235. package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
  236. package/src/db/schema/1.0.0/consent-record.ts +0 -10
  237. package/src/db/schema/1.0.0/consent.ts +0 -20
  238. package/src/db/schema/1.0.0/domain.ts +0 -12
  239. package/src/db/schema/1.0.0/index.ts +0 -48
  240. package/src/db/schema/1.0.0/subject.ts +0 -11
  241. package/src/db/schema/2.0.0/audit-log.ts +0 -18
  242. package/src/db/schema/2.0.0/consent-policy.ts +0 -28
  243. package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
  244. package/src/db/schema/2.0.0/consent.ts +0 -28
  245. package/src/db/schema/2.0.0/domain.ts +0 -12
  246. package/src/db/schema/2.0.0/index.ts +0 -47
  247. package/src/db/schema/2.0.0/subject.ts +0 -13
  248. package/src/db/schema/index.ts +0 -15
  249. package/src/db/tenant-scope.test.ts +0 -747
  250. package/src/db/tenant-scope.ts +0 -103
  251. package/src/define-config.ts +0 -19
  252. package/src/handlers/consent/check.handler.ts +0 -126
  253. package/src/handlers/init/geo.test.ts +0 -317
  254. package/src/handlers/init/geo.ts +0 -195
  255. package/src/handlers/init/index.test.ts +0 -205
  256. package/src/handlers/init/index.ts +0 -114
  257. package/src/handlers/init/translations.test.ts +0 -121
  258. package/src/handlers/init/translations.ts +0 -69
  259. package/src/handlers/status/status.handler.test.ts +0 -155
  260. package/src/handlers/status/status.handler.ts +0 -51
  261. package/src/handlers/subject/get.handler.ts +0 -92
  262. package/src/handlers/subject/list.handler.ts +0 -92
  263. package/src/handlers/subject/patch.handler.ts +0 -119
  264. package/src/handlers/subject/post.handler.test.ts +0 -294
  265. package/src/handlers/subject/post.handler.ts +0 -268
  266. package/src/handlers/utils/consent-enrichment.test.ts +0 -380
  267. package/src/handlers/utils/consent-enrichment.ts +0 -218
  268. package/src/init.test.ts +0 -122
  269. package/src/init.ts +0 -88
  270. package/src/middleware/auth/index.ts +0 -11
  271. package/src/middleware/auth/validate-api-key.test.ts +0 -86
  272. package/src/middleware/auth/validate-api-key.ts +0 -107
  273. package/src/middleware/cors/cors.test.ts +0 -135
  274. package/src/middleware/cors/cors.ts +0 -186
  275. package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
  276. package/src/middleware/cors/is-origin-trusted.ts +0 -130
  277. package/src/middleware/cors/process-cors.ts +0 -91
  278. package/src/middleware/openapi/config.ts +0 -29
  279. package/src/middleware/openapi/handlers.ts +0 -34
  280. package/src/middleware/process-ip/index.test.ts +0 -193
  281. package/src/middleware/process-ip/index.ts +0 -199
  282. package/src/router.ts +0 -15
  283. package/src/routes/consent.ts +0 -52
  284. package/src/routes/init.ts +0 -105
  285. package/src/routes/status.ts +0 -46
  286. package/src/routes/subject.ts +0 -152
  287. package/src/types/api.ts +0 -48
  288. package/src/types/index.ts +0 -391
  289. package/src/utils/create-telemetry-options.test.ts +0 -286
  290. package/src/utils/create-telemetry-options.ts +0 -229
  291. package/src/utils/env.ts +0 -84
  292. package/src/utils/extract-error-message.ts +0 -21
  293. package/src/utils/instrumentation.test.ts +0 -183
  294. package/src/utils/instrumentation.ts +0 -194
  295. package/src/utils/logger.ts +0 -41
  296. package/src/utils/metrics.test.ts +0 -311
  297. package/src/utils/metrics.ts +0 -402
  298. package/src/utils/telemetry-pii.test.ts +0 -323
  299. package/src/version.ts +0 -2
  300. package/tsconfig.json +0 -11
  301. package/vitest.config.ts +0 -28
  302. /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
  303. /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
  304. /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
  305. /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
  306. /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
  307. /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
  308. /package/{src/utils/index.ts → dist-types/utils/index.d.ts} +0 -0
@@ -1,103 +0,0 @@
1
- import type { InferFumaDB } from 'fumadb';
2
- import type { LatestDB } from './schema';
3
-
4
- type ORM = ReturnType<InferFumaDB<typeof LatestDB>['orm']>;
5
-
6
- const SCOPED_METHODS = new Set([
7
- 'create',
8
- 'createMany',
9
- 'findFirst',
10
- 'findMany',
11
- 'count',
12
- 'updateMany',
13
- 'deleteMany',
14
- 'upsert',
15
- 'transaction',
16
- ]);
17
-
18
- /**
19
- * Wraps a FumaDB ORM instance to automatically scope all queries to a specific tenant.
20
- *
21
- * Uses a Proxy so that any ORM method not explicitly handled will throw,
22
- * preventing future methods from silently bypassing tenant scoping.
23
- *
24
- * - `create`/`createMany`: Injects `tenantId` into the data
25
- * - `findFirst`/`findMany`/`count`: Adds `tenantId` filter to the `where` clause
26
- * - `updateMany`/`deleteMany`: Adds `tenantId` filter to the `where` clause
27
- * - `upsert`: Adds `tenantId` filter to the `where` clause and injects into create data
28
- * - `transaction`: Returns a tenant-scoped transaction
29
- *
30
- * When `tenantId` is not set, this function should not be called — the raw ORM is used directly.
31
- */
32
- export function withTenantScope(db: ORM, tenantId: string): ORM {
33
- const scopeWhere = (originalWhere: ((b: any) => any) | undefined, b: any) => {
34
- const tenantFilter = b('tenantId', '=', tenantId);
35
- return originalWhere ? b.and(originalWhere(b), tenantFilter) : tenantFilter;
36
- };
37
-
38
- const scopedMethods: Record<string, (...args: any[]) => any> = {
39
- create: (table: any, data: any) => db.create(table, { ...data, tenantId }),
40
-
41
- createMany: (table: any, items: any[]) =>
42
- db.createMany(
43
- table,
44
- items.map((d: any) => ({ ...d, tenantId }))
45
- ),
46
-
47
- findFirst: (table: any, opts: any) =>
48
- db.findFirst(table, {
49
- ...opts,
50
- where: (b: any) => scopeWhere(opts?.where, b),
51
- }),
52
-
53
- findMany: (table: any, opts?: any) =>
54
- db.findMany(table, {
55
- ...opts,
56
- where: (b: any) => scopeWhere(opts?.where, b),
57
- }),
58
-
59
- count: (table: any, opts?: any) =>
60
- db.count(table, {
61
- ...opts,
62
- where: (b: any) => scopeWhere(opts?.where, b),
63
- }),
64
-
65
- updateMany: (table: any, opts: any) =>
66
- db.updateMany(table, {
67
- ...opts,
68
- where: (b: any) => scopeWhere(opts?.where, b),
69
- }),
70
-
71
- deleteMany: (table: any, opts: any) =>
72
- db.deleteMany(table, {
73
- ...opts,
74
- where: (b: any) => scopeWhere(opts?.where, b),
75
- }),
76
-
77
- upsert: (table: any, opts: any) =>
78
- db.upsert(table, {
79
- ...opts,
80
- where: (b: any) => scopeWhere(opts?.where, b),
81
- create: { ...opts.create, tenantId },
82
- }),
83
-
84
- transaction: (fn: any) =>
85
- db.transaction((tx: any) => fn(withTenantScope(tx, tenantId))),
86
- };
87
-
88
- return new Proxy(db, {
89
- get(_target, prop, _receiver) {
90
- if (typeof prop === 'string' && SCOPED_METHODS.has(prop)) {
91
- return scopedMethods[prop];
92
- }
93
- // Allow symbol access (e.g. Symbol.toStringTag) and standard object props
94
- if (typeof prop === 'symbol') {
95
- return Reflect.get(db, prop);
96
- }
97
- throw new Error(
98
- `withTenantScope: method "${prop}" is not tenant-scoped. ` +
99
- 'Add an explicit scoped wrapper before using it.'
100
- );
101
- },
102
- }) as ORM;
103
- }
@@ -1,19 +0,0 @@
1
- import type { C15TOptions } from './types';
2
-
3
- /**
4
- * c15t backend config accepted by `defineConfig`.
5
- *
6
- * Keep this as an intersection with `C15TOptions` (instead of `Omit`) so
7
- * TypeScript preserves property-level JSDoc in editor completions.
8
- */
9
- export type C15TConfig = C15TOptions & {
10
- /**
11
- * Logger config is managed internally and is not supported via config files.
12
- */
13
- logger?: never;
14
- };
15
-
16
- /**
17
- * Helper for typed backend configuration in `c15t-backend.config.ts`.
18
- */
19
- export const defineConfig = (config: C15TConfig) => config;
@@ -1,126 +0,0 @@
1
- /**
2
- * GET /consents/check handler - Pre-banner cross-device consent check.
3
- *
4
- * @packageDocumentation
5
- */
6
-
7
- import type { Context } from 'hono';
8
- import { HTTPException } from 'hono/http-exception';
9
- import type { C15TContext } from '~/types';
10
- import { extractErrorMessage } from '~/utils/extract-error-message';
11
- import { getMetrics } from '~/utils/metrics';
12
- import { resolveConsentPolicies } from '../utils/consent-enrichment';
13
-
14
- /**
15
- * Handles checking if an externalId has consented to specific policies.
16
- *
17
- * Use this endpoint BEFORE showing consent banners to check if the user
18
- * has already consented on another device.
19
- *
20
- * Returns minimal data (just booleans) for privacy - no subject IDs,
21
- * no consent details, no PII.
22
- */
23
- export const checkConsentHandler = async (c: Context) => {
24
- const ctx = c.get('c15tContext') as C15TContext;
25
- const logger = ctx.logger;
26
- logger.info('Handling GET /consents/check request');
27
-
28
- const { db, registry } = ctx;
29
-
30
- const externalId = c.req.query('externalId');
31
- const type = c.req.query('type');
32
-
33
- if (!externalId) {
34
- throw new HTTPException(422, {
35
- message: 'externalId query parameter is required',
36
- cause: { code: 'EXTERNAL_ID_REQUIRED' },
37
- });
38
- }
39
-
40
- if (!type) {
41
- throw new HTTPException(422, {
42
- message: 'type query parameter is required',
43
- cause: { code: 'TYPE_REQUIRED' },
44
- });
45
- }
46
-
47
- const types = type.split(',').map((t) => t.trim());
48
-
49
- logger.debug('Request parameters', { externalId, types });
50
-
51
- try {
52
- // Find all subjects with this externalId
53
- const subjects = await db.findMany('subject', {
54
- where: (b) => b('externalId', '=', externalId),
55
- });
56
-
57
- const subjectIds = subjects.map((s) => s.id);
58
-
59
- // Initialize results
60
- const results: Record<
61
- string,
62
- { hasConsent: boolean; isLatestPolicy: boolean }
63
- > = {};
64
- for (const t of types) {
65
- results[t] = { hasConsent: false, isLatestPolicy: false };
66
- }
67
-
68
- // If no subjects found, return all false
69
- if (subjectIds.length === 0) {
70
- logger.debug('No subjects found for externalId', { externalId });
71
- return c.json({ results });
72
- }
73
-
74
- // Get all consents for these subjects
75
- const allConsents = await Promise.all(
76
- subjectIds.map((subjectId) =>
77
- db.findMany('consent', {
78
- where: (b) => b('subjectId', '=', subjectId),
79
- })
80
- )
81
- );
82
-
83
- const consents = allConsents.flat();
84
-
85
- const policyInfos = await resolveConsentPolicies(consents, {
86
- db,
87
- registry,
88
- });
89
- for (const info of policyInfos) {
90
- if (!types.includes(info.policyType)) continue;
91
- const entry = results[info.policyType];
92
- if (entry) {
93
- entry.hasConsent = true;
94
- if (info.isLatestPolicy) {
95
- entry.isLatestPolicy = true;
96
- }
97
- }
98
- }
99
-
100
- logger.debug('Consent check results', { externalId, results });
101
-
102
- // Record consent check metrics
103
- const metrics = getMetrics();
104
- if (metrics) {
105
- for (const [type, result] of Object.entries(results)) {
106
- metrics.recordConsentCheck(type, result.hasConsent);
107
- }
108
- }
109
-
110
- return c.json({ results });
111
- } catch (error) {
112
- logger.error('Error in GET /consents/check handler', {
113
- error: extractErrorMessage(error),
114
- errorType: error instanceof Error ? error.constructor.name : typeof error,
115
- });
116
-
117
- if (error instanceof HTTPException) {
118
- throw error;
119
- }
120
-
121
- throw new HTTPException(500, {
122
- message: 'Internal server error',
123
- cause: { code: 'INTERNAL_SERVER_ERROR' },
124
- });
125
- }
126
- };
@@ -1,317 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { checkJurisdiction } from './geo';
3
-
4
- describe('checkJurisdiction', () => {
5
- describe('GDPR jurisdiction (EU countries)', () => {
6
- const euCountries = [
7
- 'AT',
8
- 'BE',
9
- 'BG',
10
- 'HR',
11
- 'CY',
12
- 'CZ',
13
- 'DK',
14
- 'EE',
15
- 'FI',
16
- 'FR',
17
- 'DE',
18
- 'GR',
19
- 'HU',
20
- 'IE',
21
- 'IT',
22
- 'LV',
23
- 'LT',
24
- 'LU',
25
- 'MT',
26
- 'NL',
27
- 'PL',
28
- 'PT',
29
- 'RO',
30
- 'SK',
31
- 'SI',
32
- 'ES',
33
- 'SE',
34
- ];
35
-
36
- it.each(
37
- euCountries
38
- )('should identify %s as GDPR jurisdiction', (countryCode) => {
39
- const jurisdiction = checkJurisdiction(countryCode);
40
-
41
- expect(jurisdiction).toBe('GDPR');
42
- });
43
- });
44
-
45
- describe('GDPR jurisdiction (EEA countries)', () => {
46
- const eeaCountries = ['IS', 'NO', 'LI'];
47
-
48
- it.each(
49
- eeaCountries
50
- )('should identify %s as GDPR jurisdiction', (countryCode) => {
51
- const jurisdiction = checkJurisdiction(countryCode);
52
-
53
- expect(jurisdiction).toBe('GDPR');
54
- });
55
- });
56
-
57
- describe('GDPR jurisdiction (UK)', () => {
58
- it('should identify GB as GDPR jurisdiction', () => {
59
- const jurisdiction = checkJurisdiction('GB');
60
-
61
- expect(jurisdiction).toBe('UK_GDPR');
62
- });
63
- });
64
-
65
- describe('Other specific jurisdictions', () => {
66
- const jurisdictionCases = [
67
- { country: 'CH', code: 'CH' },
68
- { country: 'BR', code: 'BR' },
69
- { country: 'CA', code: 'PIPEDA' },
70
- { country: 'AU', code: 'AU' },
71
- { country: 'JP', code: 'APPI' },
72
- { country: 'KR', code: 'PIPA' },
73
- ] as const;
74
-
75
- it.each(
76
- jurisdictionCases
77
- )('should identify $country as $code jurisdiction', ({ country, code }) => {
78
- const jurisdiction = checkJurisdiction(country);
79
-
80
- expect(jurisdiction).toBe(code);
81
- });
82
- });
83
-
84
- describe('Non-regulated countries', () => {
85
- const nonRegulatedCountries = [
86
- 'US', // United States (outside CCPA regions)
87
- 'RU', // Russia
88
- 'CN', // China
89
- 'IN', // India
90
- 'MX', // Mexico
91
- 'AR', // Argentina
92
- 'EG', // Egypt
93
- 'ZA', // South Africa
94
- 'TH', // Thailand
95
- 'PH', // Philippines
96
- ];
97
-
98
- it.each(
99
- nonRegulatedCountries
100
- )('should identify %s as non-regulated (NONE jurisdiction)', (countryCode) => {
101
- const jurisdiction = checkJurisdiction(countryCode);
102
-
103
- expect(jurisdiction).toBe('NONE');
104
- });
105
- });
106
-
107
- describe('Edge cases', () => {
108
- it('should handle null country code by defaulting to show banner with NONE jurisdiction', () => {
109
- const jurisdiction = checkJurisdiction(null);
110
-
111
- expect(jurisdiction).toBe('NONE');
112
- });
113
-
114
- it('should handle empty string country code by defaulting to show banner with NONE jurisdiction', () => {
115
- const jurisdiction = checkJurisdiction('');
116
-
117
- expect(jurisdiction).toBe('NONE');
118
- });
119
-
120
- it('should handle lowercase country codes correctly', () => {
121
- const jurisdiction = checkJurisdiction('de');
122
-
123
- // Should now match because we normalize to uppercase
124
- expect(jurisdiction).toBe('GDPR');
125
- });
126
-
127
- it('should handle mixed case country codes across different jurisdictions', () => {
128
- const testCases = [
129
- { input: 'de', expectedJurisdiction: 'GDPR' },
130
- { input: 'De', expectedJurisdiction: 'GDPR' },
131
- { input: 'DE', expectedJurisdiction: 'GDPR' },
132
- { input: 'ch', expectedJurisdiction: 'CH' },
133
- { input: 'Ch', expectedJurisdiction: 'CH' },
134
- { input: 'CH', expectedJurisdiction: 'CH' },
135
- { input: 'ca', expectedJurisdiction: 'PIPEDA' },
136
- { input: 'Ca', expectedJurisdiction: 'PIPEDA' },
137
- { input: 'CA', expectedJurisdiction: 'PIPEDA' },
138
- ] as const;
139
-
140
- for (const { input, expectedJurisdiction } of testCases) {
141
- const jurisdiction = checkJurisdiction(input);
142
-
143
- expect(jurisdiction).toBe(expectedJurisdiction);
144
- }
145
- });
146
-
147
- it('should handle invalid country codes', () => {
148
- const invalidCodes = ['XX', 'ZZ', '123', 'ABC'];
149
-
150
- for (const code of invalidCodes) {
151
- const jurisdiction = checkJurisdiction(code);
152
-
153
- expect(jurisdiction).toBe('NONE');
154
- }
155
- });
156
- });
157
-
158
- describe('Return value structure', () => {
159
- it('should always return an object with required properties', () => {
160
- const jurisdiction = checkJurisdiction('DE');
161
-
162
- expect(jurisdiction).toBe('GDPR');
163
- });
164
-
165
- it('should return consistent types regardless of input', () => {
166
- const inputs = ['DE', 'US', 'GB', 'XX', '', null];
167
-
168
- for (const input of inputs) {
169
- const jurisdiction = checkJurisdiction(input);
170
-
171
- expect(typeof jurisdiction).toBe('string');
172
- }
173
- });
174
- });
175
-
176
- describe('Comprehensive jurisdiction mapping', () => {
177
- it('should correctly map all supported jurisdictions', () => {
178
- // Test one representative from each jurisdiction group
179
- const testCases = [
180
- {
181
- input: 'DE',
182
- expectedJurisdiction: 'GDPR' as const,
183
- },
184
- {
185
- input: 'NO',
186
- expectedJurisdiction: 'GDPR' as const,
187
- },
188
- {
189
- input: 'GB',
190
- expectedJurisdiction: 'UK_GDPR' as const,
191
- },
192
- {
193
- input: 'CH',
194
- expectedJurisdiction: 'CH' as const,
195
- },
196
- {
197
- input: 'BR',
198
- expectedJurisdiction: 'BR' as const,
199
- },
200
- {
201
- input: 'CA',
202
- expectedJurisdiction: 'PIPEDA' as const,
203
- },
204
- {
205
- input: 'AU',
206
- expectedJurisdiction: 'AU' as const,
207
- },
208
- {
209
- input: 'JP',
210
- expectedJurisdiction: 'APPI' as const,
211
- },
212
- {
213
- input: 'KR',
214
- expectedJurisdiction: 'PIPA' as const,
215
- },
216
- {
217
- input: 'US',
218
- expectedJurisdiction: 'NONE' as const,
219
- },
220
- {
221
- input: null,
222
- expectedJurisdiction: 'NONE' as const,
223
- },
224
- ];
225
-
226
- for (const { input, expectedJurisdiction } of testCases) {
227
- const jurisdiction = checkJurisdiction(input);
228
-
229
- expect(jurisdiction).toBe(expectedJurisdiction);
230
- }
231
- });
232
- });
233
-
234
- describe('Quebec Law 25 jurisdiction (CA regions)', () => {
235
- it('should identify CA-QC as QC_LAW25 jurisdiction (case-insensitive)', () => {
236
- const cases = ['QC', 'qc', 'Qc'];
237
-
238
- for (const region of cases) {
239
- const jurisdiction = checkJurisdiction('CA', region);
240
-
241
- expect(jurisdiction).toBe('QC_LAW25');
242
- }
243
- });
244
-
245
- it('should handle dash-separated region codes for Quebec', () => {
246
- const cases = ['CA-QC', 'ca-qc', 'Ca-Qc'];
247
-
248
- for (const region of cases) {
249
- const jurisdiction = checkJurisdiction('CA', region);
250
-
251
- expect(jurisdiction).toBe('QC_LAW25');
252
- }
253
- });
254
-
255
- it('should return PIPEDA for non-Quebec Canadian provinces', () => {
256
- const nonQuebecRegions = ['ON', 'BC', 'AB', null];
257
-
258
- for (const region of nonQuebecRegions) {
259
- const jurisdiction = checkJurisdiction('CA', region as string | null);
260
-
261
- expect(jurisdiction).toBe('PIPEDA');
262
- }
263
- });
264
-
265
- it('should return PIPEDA for dash-separated non-Quebec Canadian provinces', () => {
266
- const nonQuebecRegions = ['CA-ON', 'CA-BC', 'CA-AB'];
267
-
268
- for (const region of nonQuebecRegions) {
269
- const jurisdiction = checkJurisdiction('CA', region);
270
-
271
- expect(jurisdiction).toBe('PIPEDA');
272
- }
273
- });
274
- });
275
-
276
- describe('CCPA jurisdiction (US regions)', () => {
277
- it('should identify US-CA as CCPA jurisdiction (case-insensitive)', () => {
278
- const cases = ['CA', 'ca', 'Ca'];
279
-
280
- for (const region of cases) {
281
- const jurisdiction = checkJurisdiction('US', region);
282
-
283
- expect(jurisdiction).toBe('CCPA');
284
- }
285
- });
286
-
287
- it('should handle dash-separated region codes for California', () => {
288
- const cases = ['US-CA', 'us-ca', 'Us-Ca'];
289
-
290
- for (const region of cases) {
291
- const jurisdiction = checkJurisdiction('US', region);
292
-
293
- expect(jurisdiction).toBe('CCPA');
294
- }
295
- });
296
-
297
- it('should not apply CCPA for non-CCPA US regions', () => {
298
- const nonCcpaRegions = ['NY', 'TX', 'WA', 'FL', null];
299
-
300
- for (const region of nonCcpaRegions) {
301
- const jurisdiction = checkJurisdiction('US', region as string | null);
302
-
303
- expect(jurisdiction).toBe('NONE');
304
- }
305
- });
306
-
307
- it('should not apply CCPA for dash-separated non-CCPA US regions', () => {
308
- const nonCcpaRegions = ['US-NY', 'US-TX', 'US-WA'];
309
-
310
- for (const region of nonCcpaRegions) {
311
- const jurisdiction = checkJurisdiction('US', region);
312
-
313
- expect(jurisdiction).toBe('NONE');
314
- }
315
- });
316
- });
317
- });