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

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