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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/README.md +3 -3
  2. package/dist/302.js +473 -0
  3. package/dist/583.js +540 -0
  4. package/dist/915.js +1771 -0
  5. package/dist/cache.cjs +5 -5
  6. package/dist/cache.js +4 -415
  7. package/dist/core.cjs +1356 -120
  8. package/dist/core.js +163 -1981
  9. package/dist/db/adapters/drizzle.cjs +1 -1
  10. package/dist/db/adapters/drizzle.js +1 -2
  11. package/dist/db/adapters/kysely.cjs +1 -1
  12. package/dist/db/adapters/kysely.js +1 -2
  13. package/dist/db/adapters/mongo.cjs +1 -1
  14. package/dist/db/adapters/mongo.js +1 -2
  15. package/dist/db/adapters/prisma.cjs +1 -1
  16. package/dist/db/adapters/prisma.js +1 -2
  17. package/dist/db/adapters/typeorm.cjs +1 -1
  18. package/dist/db/adapters/typeorm.js +1 -2
  19. package/dist/db/adapters.cjs +1 -1
  20. package/dist/db/migrator.cjs +1 -1
  21. package/dist/db/schema.cjs +43 -3
  22. package/dist/db/schema.js +35 -4
  23. package/dist/define-config.cjs +1 -1
  24. package/dist/edge.cjs +1106 -0
  25. package/dist/edge.js +190 -0
  26. package/dist/router.cjs +885 -123
  27. package/dist/router.js +1 -1507
  28. package/dist/{types.cjs → types/index.cjs} +1 -1
  29. package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
  30. package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
  31. package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
  32. package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
  33. package/{dist → dist-types}/cache/gvl-resolver.d.ts +0 -1
  34. package/{dist → dist-types}/cache/index.d.ts +0 -1
  35. package/{dist → dist-types}/cache/keys.d.ts +0 -1
  36. package/{dist → dist-types}/cache/types.d.ts +0 -1
  37. package/{dist → dist-types}/core.d.ts +8 -1
  38. package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
  39. package/dist-types/db/registry/consent-policy.d.ts +78 -0
  40. package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
  41. package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
  42. package/dist-types/db/registry/index.d.ts +118 -0
  43. package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
  44. package/{dist → dist-types}/db/registry/subject.d.ts +0 -2
  45. package/{dist → dist-types}/db/registry/types.d.ts +1 -1
  46. package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
  47. package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
  48. package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
  49. package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
  50. package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
  51. package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
  52. package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +1 -2
  53. package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
  54. package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -32
  55. package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -2
  56. package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +1 -2
  57. package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +3 -3
  58. package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +1 -2
  59. package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +7 -2
  60. package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +1 -2
  61. package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +455 -28
  62. package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
  63. package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +1 -3
  64. package/{dist → dist-types}/db/schema/index.d.ts +908 -86
  65. package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
  66. package/dist-types/define-config.d.ts +17 -0
  67. package/dist-types/edge/index.d.ts +5 -0
  68. package/dist-types/edge/init-handler.d.ts +40 -0
  69. package/dist-types/edge/resolve-consent.d.ts +80 -0
  70. package/dist-types/edge/types.d.ts +13 -0
  71. package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
  72. package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
  73. package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
  74. package/{dist → dist-types}/handlers/init/index.d.ts +2 -3
  75. package/dist-types/handlers/init/policy.d.ts +26 -0
  76. package/dist-types/handlers/init/resolve-init.d.ts +44 -0
  77. package/dist-types/handlers/init/translations.d.ts +48 -0
  78. package/dist-types/handlers/legal-document/current.handler.d.ts +11 -0
  79. package/dist-types/handlers/legal-document/snapshot.d.ts +39 -0
  80. package/dist-types/handlers/policy/snapshot.d.ts +99 -0
  81. package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
  82. package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
  83. package/{dist → dist-types}/handlers/subject/get.handler.d.ts +3 -2
  84. package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
  85. package/{dist → dist-types}/handlers/subject/list.handler.d.ts +3 -2
  86. package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -2
  87. package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
  88. package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +3 -1
  89. package/{dist → dist-types}/init.d.ts +4 -7
  90. package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
  91. package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
  92. package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
  93. package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
  94. package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +0 -1
  95. package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
  96. package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
  97. package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
  98. package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
  99. package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
  100. package/dist-types/policies/builder.d.ts +127 -0
  101. package/dist-types/policies/defaults.d.ts +2 -0
  102. package/dist-types/policies/matchers.d.ts +3 -0
  103. package/{dist → dist-types}/router.d.ts +0 -1
  104. package/{dist → dist-types}/routes/consent.d.ts +0 -1
  105. package/{dist → dist-types}/routes/index.d.ts +1 -1
  106. package/{dist → dist-types}/routes/init.d.ts +0 -1
  107. package/dist-types/routes/legal-document.d.ts +7 -0
  108. package/{dist → dist-types}/routes/status.d.ts +0 -1
  109. package/{dist → dist-types}/routes/subject.d.ts +0 -1
  110. package/{dist → dist-types}/types/api.d.ts +0 -1
  111. package/dist-types/types/index.d.ts +464 -0
  112. package/dist-types/utils/background.d.ts +6 -0
  113. package/{dist → dist-types}/utils/create-telemetry-options.d.ts +1 -2
  114. package/{dist → dist-types}/utils/env.d.ts +0 -1
  115. package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
  116. package/{dist → dist-types}/utils/instrumentation.d.ts +2 -3
  117. package/{dist → dist-types}/utils/logger.d.ts +0 -1
  118. package/{dist → dist-types}/utils/metrics.d.ts +0 -1
  119. package/dist-types/version.d.ts +1 -0
  120. package/docs/README.md +49 -0
  121. package/docs/api/configuration.md +208 -0
  122. package/docs/api/endpoints.md +211 -0
  123. package/docs/guides/caching.md +85 -0
  124. package/docs/guides/database-setup.md +128 -0
  125. package/docs/guides/edge-deployment.md +251 -0
  126. package/docs/guides/framework-integration.md +142 -0
  127. package/docs/guides/iab-tcf.md +89 -0
  128. package/docs/guides/observability.md +96 -0
  129. package/docs/guides/policy-packs.md +396 -0
  130. package/docs/quickstart.md +129 -0
  131. package/package.json +51 -37
  132. package/.turbo/turbo-build.log +0 -49
  133. package/CHANGELOG.md +0 -99
  134. package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
  135. package/dist/cache/adapters/index.d.ts.map +0 -1
  136. package/dist/cache/adapters/memory.d.ts.map +0 -1
  137. package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
  138. package/dist/cache/gvl-resolver.d.ts.map +0 -1
  139. package/dist/cache/index.d.ts.map +0 -1
  140. package/dist/cache/keys.d.ts.map +0 -1
  141. package/dist/cache/types.d.ts.map +0 -1
  142. package/dist/core.d.ts.map +0 -1
  143. package/dist/db/adapters/drizzle.d.ts +0 -2
  144. package/dist/db/adapters/drizzle.d.ts.map +0 -1
  145. package/dist/db/adapters/index.d.ts +0 -2
  146. package/dist/db/adapters/index.d.ts.map +0 -1
  147. package/dist/db/adapters/kysely.d.ts +0 -2
  148. package/dist/db/adapters/kysely.d.ts.map +0 -1
  149. package/dist/db/adapters/mongo.d.ts +0 -2
  150. package/dist/db/adapters/mongo.d.ts.map +0 -1
  151. package/dist/db/adapters/prisma.d.ts +0 -2
  152. package/dist/db/adapters/prisma.d.ts.map +0 -1
  153. package/dist/db/adapters/typeorm.d.ts +0 -2
  154. package/dist/db/adapters/typeorm.d.ts.map +0 -1
  155. package/dist/db/migrator/index.d.ts.map +0 -1
  156. package/dist/db/registry/consent-policy.d.ts +0 -23
  157. package/dist/db/registry/consent-policy.d.ts.map +0 -1
  158. package/dist/db/registry/consent-purpose.d.ts.map +0 -1
  159. package/dist/db/registry/domain.d.ts.map +0 -1
  160. package/dist/db/registry/index.d.ts +0 -57
  161. package/dist/db/registry/index.d.ts.map +0 -1
  162. package/dist/db/registry/subject.d.ts.map +0 -1
  163. package/dist/db/registry/types.d.ts.map +0 -1
  164. package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
  165. package/dist/db/registry/utils.d.ts.map +0 -1
  166. package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
  167. package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
  168. package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
  169. package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
  170. package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
  171. package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
  172. package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
  173. package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
  174. package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
  175. package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
  176. package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
  177. package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
  178. package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
  179. package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
  180. package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
  181. package/dist/db/schema/index.d.ts.map +0 -1
  182. package/dist/db/tenant-scope.d.ts.map +0 -1
  183. package/dist/define-config.d.ts +0 -5
  184. package/dist/define-config.d.ts.map +0 -1
  185. package/dist/handlers/consent/check.handler.d.ts.map +0 -1
  186. package/dist/handlers/consent/index.d.ts +0 -12
  187. package/dist/handlers/consent/index.d.ts.map +0 -1
  188. package/dist/handlers/init/geo.d.ts.map +0 -1
  189. package/dist/handlers/init/index.d.ts.map +0 -1
  190. package/dist/handlers/init/translations.d.ts +0 -28
  191. package/dist/handlers/init/translations.d.ts.map +0 -1
  192. package/dist/handlers/status/index.d.ts +0 -7
  193. package/dist/handlers/status/index.d.ts.map +0 -1
  194. package/dist/handlers/status/status.handler.d.ts.map +0 -1
  195. package/dist/handlers/subject/get.handler.d.ts.map +0 -1
  196. package/dist/handlers/subject/index.d.ts +0 -10
  197. package/dist/handlers/subject/index.d.ts.map +0 -1
  198. package/dist/handlers/subject/list.handler.d.ts.map +0 -1
  199. package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
  200. package/dist/handlers/subject/post.handler.d.ts.map +0 -1
  201. package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
  202. package/dist/init.d.ts.map +0 -1
  203. package/dist/middleware/auth/index.d.ts.map +0 -1
  204. package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
  205. package/dist/middleware/cors/cors.d.ts.map +0 -1
  206. package/dist/middleware/cors/index.d.ts +0 -30
  207. package/dist/middleware/cors/index.d.ts.map +0 -1
  208. package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
  209. package/dist/middleware/cors/process-cors.d.ts.map +0 -1
  210. package/dist/middleware/openapi/config.d.ts.map +0 -1
  211. package/dist/middleware/openapi/handlers.d.ts.map +0 -1
  212. package/dist/middleware/openapi/index.d.ts +0 -12
  213. package/dist/middleware/openapi/index.d.ts.map +0 -1
  214. package/dist/middleware/process-ip/index.d.ts.map +0 -1
  215. package/dist/router.d.ts.map +0 -1
  216. package/dist/routes/consent.d.ts.map +0 -1
  217. package/dist/routes/index.d.ts.map +0 -1
  218. package/dist/routes/init.d.ts.map +0 -1
  219. package/dist/routes/status.d.ts.map +0 -1
  220. package/dist/routes/subject.d.ts.map +0 -1
  221. package/dist/types/api.d.ts.map +0 -1
  222. package/dist/types/index.d.ts +0 -255
  223. package/dist/types/index.d.ts.map +0 -1
  224. package/dist/utils/create-telemetry-options.d.ts.map +0 -1
  225. package/dist/utils/env.d.ts.map +0 -1
  226. package/dist/utils/extract-error-message.d.ts.map +0 -1
  227. package/dist/utils/index.d.ts +0 -4
  228. package/dist/utils/index.d.ts.map +0 -1
  229. package/dist/utils/instrumentation.d.ts.map +0 -1
  230. package/dist/utils/logger.d.ts.map +0 -1
  231. package/dist/utils/metrics.d.ts.map +0 -1
  232. package/dist/version.d.ts +0 -2
  233. package/dist/version.d.ts.map +0 -1
  234. package/knip.json +0 -31
  235. package/rslib.config.ts +0 -93
  236. package/src/cache/adapters/cloudflare-kv.ts +0 -71
  237. package/src/cache/adapters/index.ts +0 -22
  238. package/src/cache/adapters/memory.ts +0 -111
  239. package/src/cache/adapters/upstash-redis.ts +0 -113
  240. package/src/cache/gvl-resolver.ts +0 -289
  241. package/src/cache/index.ts +0 -34
  242. package/src/cache/keys.ts +0 -68
  243. package/src/cache/types.ts +0 -66
  244. package/src/core.ts +0 -368
  245. package/src/db/migrator/index.ts +0 -80
  246. package/src/db/registry/consent-policy.test.ts +0 -451
  247. package/src/db/registry/consent-policy.ts +0 -82
  248. package/src/db/registry/consent-purpose.test.ts +0 -428
  249. package/src/db/registry/consent-purpose.ts +0 -61
  250. package/src/db/registry/domain.test.ts +0 -445
  251. package/src/db/registry/domain.ts +0 -91
  252. package/src/db/registry/index.ts +0 -14
  253. package/src/db/registry/subject.test.ts +0 -388
  254. package/src/db/registry/subject.ts +0 -129
  255. package/src/db/registry/types.ts +0 -10
  256. package/src/db/registry/utils/generate-id.test.ts +0 -216
  257. package/src/db/registry/utils/generate-id.ts +0 -133
  258. package/src/db/registry/utils.ts +0 -133
  259. package/src/db/schema/1.0.0/audit-log.ts +0 -15
  260. package/src/db/schema/1.0.0/consent-policy.ts +0 -14
  261. package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
  262. package/src/db/schema/1.0.0/consent-record.ts +0 -10
  263. package/src/db/schema/1.0.0/consent.ts +0 -20
  264. package/src/db/schema/1.0.0/domain.ts +0 -12
  265. package/src/db/schema/1.0.0/index.ts +0 -48
  266. package/src/db/schema/1.0.0/subject.ts +0 -12
  267. package/src/db/schema/2.0.0/audit-log.ts +0 -18
  268. package/src/db/schema/2.0.0/consent-policy.ts +0 -28
  269. package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
  270. package/src/db/schema/2.0.0/consent.ts +0 -26
  271. package/src/db/schema/2.0.0/domain.ts +0 -12
  272. package/src/db/schema/2.0.0/index.ts +0 -47
  273. package/src/db/schema/2.0.0/subject.ts +0 -14
  274. package/src/db/schema/index.ts +0 -15
  275. package/src/db/tenant-scope.test.ts +0 -750
  276. package/src/db/tenant-scope.ts +0 -103
  277. package/src/define-config.ts +0 -5
  278. package/src/handlers/consent/check.handler.ts +0 -126
  279. package/src/handlers/init/geo.test.ts +0 -317
  280. package/src/handlers/init/geo.ts +0 -195
  281. package/src/handlers/init/index.test.ts +0 -205
  282. package/src/handlers/init/index.ts +0 -114
  283. package/src/handlers/init/translations.test.ts +0 -121
  284. package/src/handlers/init/translations.ts +0 -72
  285. package/src/handlers/status/status.handler.test.ts +0 -155
  286. package/src/handlers/status/status.handler.ts +0 -51
  287. package/src/handlers/subject/get.handler.ts +0 -93
  288. package/src/handlers/subject/list.handler.ts +0 -93
  289. package/src/handlers/subject/patch.handler.ts +0 -122
  290. package/src/handlers/subject/post.handler.test.ts +0 -294
  291. package/src/handlers/subject/post.handler.ts +0 -254
  292. package/src/handlers/utils/consent-enrichment.test.ts +0 -380
  293. package/src/handlers/utils/consent-enrichment.ts +0 -218
  294. package/src/init.test.ts +0 -126
  295. package/src/init.ts +0 -87
  296. package/src/middleware/auth/index.ts +0 -11
  297. package/src/middleware/auth/validate-api-key.test.ts +0 -86
  298. package/src/middleware/auth/validate-api-key.ts +0 -107
  299. package/src/middleware/cors/cors.test.ts +0 -135
  300. package/src/middleware/cors/cors.ts +0 -186
  301. package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
  302. package/src/middleware/cors/is-origin-trusted.ts +0 -130
  303. package/src/middleware/cors/process-cors.ts +0 -91
  304. package/src/middleware/openapi/config.ts +0 -29
  305. package/src/middleware/openapi/handlers.ts +0 -34
  306. package/src/middleware/process-ip/index.test.ts +0 -195
  307. package/src/middleware/process-ip/index.ts +0 -199
  308. package/src/router.ts +0 -15
  309. package/src/routes/consent.ts +0 -52
  310. package/src/routes/index.ts +0 -10
  311. package/src/routes/init.ts +0 -102
  312. package/src/routes/status.ts +0 -46
  313. package/src/routes/subject.ts +0 -152
  314. package/src/types/api.ts +0 -48
  315. package/src/types/index.ts +0 -288
  316. package/src/utils/create-telemetry-options.test.ts +0 -302
  317. package/src/utils/create-telemetry-options.ts +0 -229
  318. package/src/utils/env.ts +0 -84
  319. package/src/utils/extract-error-message.ts +0 -21
  320. package/src/utils/instrumentation.test.ts +0 -185
  321. package/src/utils/instrumentation.ts +0 -196
  322. package/src/utils/logger.ts +0 -41
  323. package/src/utils/metrics.test.ts +0 -323
  324. package/src/utils/metrics.ts +0 -402
  325. package/src/utils/telemetry-pii.test.ts +0 -325
  326. package/src/version.ts +0 -2
  327. package/tsconfig.json +0 -11
  328. package/vitest.config.ts +0 -28
  329. /package/dist/{types.js → types/index.js} +0 -0
  330. /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
  331. /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
  332. /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
  333. /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
  334. /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
  335. /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
  336. /package/{src/utils/index.ts → dist-types/utils/index.d.ts} +0 -0
@@ -1,388 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
- import type { Subject } from '../schema';
3
- import { subjectRegistry } from './subject';
4
- import type { Registry } from './types';
5
-
6
- // Mock generateUniqueId to return a predictable value for assertions
7
- vi.mock('./utils/generate-id', () => ({
8
- generateUniqueId: vi.fn().mockResolvedValue('sub_test_123'),
9
- }));
10
-
11
- describe('subjectRegistry', () => {
12
- const mockLogger = {
13
- debug: vi.fn(),
14
- error: vi.fn(),
15
- info: vi.fn(),
16
- warn: vi.fn(),
17
- };
18
-
19
- const createMockSubject = (overrides: Partial<Subject> = {}): Subject => ({
20
- id: 'sub_test_123',
21
- isIdentified: false,
22
- externalId: null,
23
- identityProvider: 'anonymous',
24
- createdAt: new Date('2024-01-01T00:00:00.000Z'),
25
- updatedAt: new Date('2024-01-01T00:00:00.000Z'),
26
- ...overrides,
27
- });
28
-
29
- afterEach(() => {
30
- vi.clearAllMocks();
31
- });
32
-
33
- describe('findOrCreateSubject', () => {
34
- describe('when subjectId is provided', () => {
35
- it('should return existing subject when found', async () => {
36
- const mockSubject = createMockSubject({
37
- id: 'sub_existing',
38
- externalId: 'ext_123',
39
- isIdentified: true,
40
- });
41
-
42
- const db = {
43
- findFirst: vi.fn().mockResolvedValue(mockSubject),
44
- };
45
-
46
- const registry = subjectRegistry({
47
- db,
48
- ctx: { logger: mockLogger },
49
- } as unknown as Registry);
50
-
51
- const result = await registry.findOrCreateSubject({
52
- subjectId: 'sub_existing',
53
- externalSubjectId: 'ext_123',
54
- ipAddress: '192.168.1.1',
55
- });
56
-
57
- expect(db.findFirst).toHaveBeenCalledWith('subject', {
58
- where: expect.any(Function),
59
- });
60
-
61
- expect(result).toEqual(mockSubject);
62
- });
63
-
64
- it('should create new subject when subjectId not found', async () => {
65
- const createdSubject = createMockSubject({
66
- id: 'sub_new',
67
- externalId: null,
68
- identityProvider: 'anonymous',
69
- isIdentified: false,
70
- });
71
-
72
- const db = {
73
- findFirst: vi.fn().mockResolvedValue(null),
74
- create: vi.fn().mockResolvedValue(createdSubject),
75
- };
76
-
77
- const registry = subjectRegistry({
78
- db,
79
- ctx: { logger: mockLogger },
80
- } as unknown as Registry);
81
-
82
- const result = await registry.findOrCreateSubject({
83
- subjectId: 'sub_new',
84
- });
85
-
86
- expect(db.findFirst).toHaveBeenCalledWith('subject', {
87
- where: expect.any(Function),
88
- });
89
-
90
- expect(db.create).toHaveBeenCalledWith('subject', {
91
- id: 'sub_new',
92
- externalId: null,
93
- identityProvider: 'anonymous',
94
- isIdentified: false,
95
- });
96
-
97
- expect(result).toEqual(createdSubject);
98
- });
99
-
100
- it('should create new subject with externalId when both IDs provided but subject not found', async () => {
101
- const createdSubject = createMockSubject({
102
- id: 'sub_new',
103
- externalId: 'ext_123',
104
- identityProvider: 'external',
105
- isIdentified: true,
106
- });
107
-
108
- const db = {
109
- findFirst: vi.fn().mockResolvedValue(null),
110
- create: vi.fn().mockResolvedValue(createdSubject),
111
- };
112
-
113
- const registry = subjectRegistry({
114
- db,
115
- ctx: { logger: mockLogger },
116
- } as unknown as Registry);
117
-
118
- const result = await registry.findOrCreateSubject({
119
- subjectId: 'sub_new',
120
- externalSubjectId: 'ext_123',
121
- });
122
-
123
- expect(db.create).toHaveBeenCalledWith('subject', {
124
- id: 'sub_new',
125
- externalId: 'ext_123',
126
- identityProvider: 'external',
127
- isIdentified: true,
128
- });
129
-
130
- expect(result).toEqual(createdSubject);
131
- });
132
- });
133
-
134
- describe('when only externalSubjectId is provided', () => {
135
- it('should create a new subject with external ID', async () => {
136
- const mockSubject = createMockSubject({
137
- id: 'sub_test_123',
138
- externalId: 'ext_existing',
139
- identityProvider: 'external',
140
- isIdentified: true,
141
- });
142
-
143
- const db = {
144
- create: vi.fn().mockResolvedValue(mockSubject),
145
- };
146
-
147
- const registry = subjectRegistry({
148
- db,
149
- ctx: { logger: mockLogger },
150
- } as unknown as Registry);
151
-
152
- const result = await registry.findOrCreateSubject({
153
- externalSubjectId: 'ext_existing',
154
- ipAddress: '192.168.1.200',
155
- });
156
-
157
- expect(db.create).toHaveBeenCalledWith('subject', {
158
- id: 'sub_test_123',
159
- externalId: 'ext_existing',
160
- identityProvider: 'external',
161
- isIdentified: true,
162
- });
163
-
164
- expect(result).toEqual(mockSubject);
165
- expect(mockLogger.debug).toHaveBeenCalledWith(
166
- 'Creating subject with external ID (legacy flow)',
167
- { externalSubjectId: 'ext_existing' }
168
- );
169
- });
170
-
171
- it('should use custom identity provider when specified', async () => {
172
- const mockSubject = createMockSubject({
173
- id: 'sub_test_123',
174
- externalId: 'ext_new',
175
- identityProvider: 'google',
176
- isIdentified: true,
177
- });
178
-
179
- const db = {
180
- create: vi.fn().mockResolvedValue(mockSubject),
181
- };
182
-
183
- const registry = subjectRegistry({
184
- db,
185
- ctx: { logger: mockLogger },
186
- } as unknown as Registry);
187
-
188
- const result = await registry.findOrCreateSubject({
189
- externalSubjectId: 'ext_new',
190
- identityProvider: 'google',
191
- });
192
-
193
- expect(db.create).toHaveBeenCalledWith('subject', {
194
- id: 'sub_test_123',
195
- externalId: 'ext_new',
196
- identityProvider: 'google',
197
- isIdentified: true,
198
- });
199
-
200
- expect(result).toEqual(mockSubject);
201
- });
202
-
203
- it('should default identityProvider to "external"', async () => {
204
- const mockSubject = createMockSubject({
205
- externalId: 'ext_test',
206
- });
207
-
208
- const db = {
209
- create: vi.fn().mockResolvedValue(mockSubject),
210
- };
211
-
212
- const registry = subjectRegistry({
213
- db,
214
- ctx: { logger: mockLogger },
215
- } as unknown as Registry);
216
-
217
- await registry.findOrCreateSubject({
218
- externalSubjectId: 'ext_test',
219
- });
220
-
221
- expect(db.create).toHaveBeenCalledWith('subject', {
222
- id: 'sub_test_123',
223
- externalId: 'ext_test',
224
- identityProvider: 'external',
225
- isIdentified: true,
226
- });
227
- });
228
- });
229
-
230
- describe('when no identifiers are provided (anonymous subject)', () => {
231
- it('should create a new anonymous subject', async () => {
232
- const mockSubject = createMockSubject({
233
- externalId: null,
234
- isIdentified: false,
235
- });
236
-
237
- const db = {
238
- create: vi.fn().mockResolvedValue(mockSubject),
239
- };
240
-
241
- const registry = subjectRegistry({
242
- db,
243
- ctx: { logger: mockLogger },
244
- } as unknown as Registry);
245
-
246
- const result = await registry.findOrCreateSubject({
247
- ipAddress: '10.0.0.1',
248
- });
249
-
250
- expect(db.create).toHaveBeenCalledWith('subject', {
251
- id: 'sub_test_123',
252
- externalId: null,
253
- identityProvider: 'anonymous',
254
- isIdentified: false,
255
- });
256
-
257
- expect(result).toEqual(mockSubject);
258
- expect(mockLogger.debug).toHaveBeenCalledWith(
259
- 'Creating new anonymous subject'
260
- );
261
- });
262
-
263
- it('should create anonymous subject when no arguments provided', async () => {
264
- const mockSubject = createMockSubject({
265
- externalId: null,
266
- isIdentified: false,
267
- });
268
-
269
- const db = {
270
- create: vi.fn().mockResolvedValue(mockSubject),
271
- };
272
-
273
- const registry = subjectRegistry({
274
- db,
275
- ctx: { logger: mockLogger },
276
- } as unknown as Registry);
277
-
278
- const result = await registry.findOrCreateSubject({});
279
-
280
- expect(db.create).toHaveBeenCalledWith('subject', {
281
- id: 'sub_test_123',
282
- externalId: null,
283
- identityProvider: 'anonymous',
284
- isIdentified: false,
285
- });
286
-
287
- expect(result).toEqual(mockSubject);
288
- });
289
- });
290
-
291
- describe('edge cases and error handling', () => {
292
- it('should handle empty string externalSubjectId as falsy', async () => {
293
- const mockSubject = createMockSubject();
294
-
295
- const db = {
296
- create: vi.fn().mockResolvedValue(mockSubject),
297
- };
298
-
299
- const registry = subjectRegistry({
300
- db,
301
- ctx: { logger: mockLogger },
302
- } as unknown as Registry);
303
-
304
- await registry.findOrCreateSubject({
305
- externalSubjectId: '',
306
- });
307
-
308
- // Should create anonymous subject since empty string is falsy
309
- expect(db.create).toHaveBeenCalledWith('subject', {
310
- id: 'sub_test_123',
311
- externalId: null,
312
- identityProvider: 'anonymous',
313
- isIdentified: false,
314
- });
315
- });
316
-
317
- it('should handle empty string subjectId as falsy', async () => {
318
- const mockSubject = createMockSubject();
319
-
320
- const db = {
321
- create: vi.fn().mockResolvedValue(mockSubject),
322
- };
323
-
324
- const registry = subjectRegistry({
325
- db,
326
- ctx: { logger: mockLogger },
327
- } as unknown as Registry);
328
-
329
- await registry.findOrCreateSubject({
330
- subjectId: '',
331
- });
332
-
333
- // Should create anonymous subject since empty string is falsy
334
- expect(db.create).toHaveBeenCalledWith('subject', {
335
- id: 'sub_test_123',
336
- externalId: null,
337
- identityProvider: 'anonymous',
338
- isIdentified: false,
339
- });
340
- });
341
- });
342
-
343
- describe('database query construction', () => {
344
- it('should construct correct findFirst query for subjectId lookup', async () => {
345
- const mockSubject = createMockSubject();
346
- const db = {
347
- findFirst: vi.fn().mockResolvedValue(mockSubject),
348
- };
349
-
350
- const registry = subjectRegistry({
351
- db,
352
- ctx: { logger: mockLogger },
353
- } as unknown as Registry);
354
-
355
- await registry.findOrCreateSubject({
356
- subjectId: 'sub_test',
357
- });
358
-
359
- expect(db.findFirst).toHaveBeenCalledWith('subject', {
360
- where: expect.any(Function),
361
- });
362
- });
363
-
364
- it('should construct correct create call for externalSubjectId', async () => {
365
- const mockSubject = createMockSubject();
366
- const db = {
367
- create: vi.fn().mockResolvedValue(mockSubject),
368
- };
369
-
370
- const registry = subjectRegistry({
371
- db,
372
- ctx: { logger: mockLogger },
373
- } as unknown as Registry);
374
-
375
- await registry.findOrCreateSubject({
376
- externalSubjectId: 'ext_test',
377
- });
378
-
379
- expect(db.create).toHaveBeenCalledWith('subject', {
380
- id: 'sub_test_123',
381
- externalId: 'ext_test',
382
- identityProvider: 'external',
383
- isIdentified: true,
384
- });
385
- });
386
- });
387
- });
388
- });
@@ -1,129 +0,0 @@
1
- import { withDatabaseSpan } from '~/utils/instrumentation';
2
- import { getMetrics } from '~/utils/metrics';
3
- import type { Registry } from './types';
4
- import { generateUniqueId } from './utils/generate-id';
5
-
6
- export function subjectRegistry({ db, ctx }: Registry) {
7
- const { logger } = ctx;
8
- return {
9
- /**
10
- * Finds or creates a subject.
11
- *
12
- * For the subject-centric API (v2.0):
13
- * - subjectId is required and client-generated
14
- * - Creates subject if it doesn't exist with the provided ID
15
- * - externalSubjectId can be provided at creation time
16
- * - Multiple subjects can have the same externalId (1:many relationship)
17
- *
18
- * @param subjectId - Client-generated subject ID (required for v2.0)
19
- * @param externalSubjectId - Optional external user ID from auth system
20
- * @param identityProvider - Optional identity provider name
21
- * @param ipAddress - Client IP address
22
- */
23
- findOrCreateSubject: async ({
24
- subjectId,
25
- externalSubjectId,
26
- identityProvider,
27
- ipAddress = null,
28
- }: {
29
- subjectId?: string;
30
- externalSubjectId?: string;
31
- identityProvider?: string;
32
- ipAddress?: string | null;
33
- }) => {
34
- const start = Date.now();
35
- try {
36
- const result = await withDatabaseSpan(
37
- { operation: 'findOrCreate', entity: 'subject' },
38
- async () => {
39
- // If subjectId is provided (v2.0 flow), find or create with that ID
40
- if (subjectId) {
41
- logger.debug(
42
- 'Finding/Creating subject with client-generated ID',
43
- {
44
- subjectId,
45
- }
46
- );
47
-
48
- // Try to find existing subject
49
- const existingSubject = await db.findFirst('subject', {
50
- where: (b) => b('id', '=', subjectId),
51
- });
52
-
53
- if (existingSubject) {
54
- logger.debug('Found existing subject', {
55
- subjectId,
56
- });
57
- return existingSubject;
58
- }
59
-
60
- // Create new subject with client-provided ID
61
- logger.debug('Creating new subject with client-generated ID', {
62
- subjectId,
63
- });
64
-
65
- const newSubject = await db.create('subject', {
66
- id: subjectId,
67
- externalId: externalSubjectId ?? null,
68
- identityProvider: externalSubjectId
69
- ? (identityProvider ?? 'external')
70
- : 'anonymous',
71
- isIdentified: !!externalSubjectId,
72
- });
73
-
74
- logger.debug('Created new subject', {
75
- subject: newSubject,
76
- });
77
-
78
- return newSubject;
79
- }
80
-
81
- // Legacy flow: If only externalSubjectId provided, find or create
82
- // Note: This creates a new subject for each call since we don't have a subjectId
83
- // With 1:many relationship, we should not upsert by externalId anymore
84
- if (externalSubjectId) {
85
- logger.debug('Creating subject with external ID (legacy flow)', {
86
- externalSubjectId,
87
- });
88
-
89
- const subject = await db.create('subject', {
90
- id: await generateUniqueId(db, 'subject', ctx),
91
- externalId: externalSubjectId,
92
- identityProvider: identityProvider ?? 'external',
93
- isIdentified: true,
94
- });
95
-
96
- return subject;
97
- }
98
-
99
- // If no identifiers provided, create an anonymous subject
100
- logger?.debug('Creating new anonymous subject');
101
- const subject = await db.create('subject', {
102
- id: await generateUniqueId(db, 'subject', ctx),
103
- externalId: null,
104
- identityProvider: 'anonymous',
105
- isIdentified: false,
106
- });
107
-
108
- logger.debug('Created new anonymous subject', {
109
- subject,
110
- });
111
-
112
- return subject;
113
- }
114
- );
115
- getMetrics()?.recordDbQuery(
116
- { operation: 'findOrCreate', entity: 'subject' },
117
- Date.now() - start
118
- );
119
- return result;
120
- } catch (error) {
121
- getMetrics()?.recordDbError({
122
- operation: 'findOrCreate',
123
- entity: 'subject',
124
- });
125
- throw error;
126
- }
127
- },
128
- };
129
- }
@@ -1,10 +0,0 @@
1
- import type { createLogger } from '@c15t/logger';
2
- import type { InferFumaDB } from 'fumadb';
3
- import type { LatestDB } from '~/db/schema';
4
-
5
- export interface Registry {
6
- db: ReturnType<InferFumaDB<typeof LatestDB>['orm']>;
7
- ctx: {
8
- logger: ReturnType<typeof createLogger>;
9
- };
10
- }