@c15t/backend 2.0.0-rc.4 → 2.0.0-rc.6

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