@friggframework/core 2.0.0-next.1 → 2.0.0-next.100

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 (342) hide show
  1. package/CLAUDE.md +702 -0
  2. package/README.md +959 -50
  3. package/admin-scripts/index.js +52 -0
  4. package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
  5. package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
  6. package/admin-scripts/repositories/admin-script-execution-repository-interface.js +166 -0
  7. package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +228 -0
  8. package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +268 -0
  9. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  10. package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
  11. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  12. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  13. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  14. package/application/commands/README.md +451 -0
  15. package/application/commands/admin-script-commands.js +358 -0
  16. package/application/commands/credential-commands.js +245 -0
  17. package/application/commands/entity-commands.js +336 -0
  18. package/application/commands/integration-commands.js +343 -0
  19. package/application/commands/process-commands.js +135 -0
  20. package/application/commands/scheduler-commands.js +263 -0
  21. package/application/commands/user-commands.js +312 -0
  22. package/application/index.js +82 -0
  23. package/assertions/index.js +0 -3
  24. package/core/CLAUDE.md +690 -0
  25. package/core/Worker.js +60 -24
  26. package/core/create-handler.js +84 -6
  27. package/credential/repositories/credential-repository-documentdb.js +304 -0
  28. package/credential/repositories/credential-repository-factory.js +54 -0
  29. package/credential/repositories/credential-repository-interface.js +98 -0
  30. package/credential/repositories/credential-repository-mongo.js +269 -0
  31. package/credential/repositories/credential-repository-postgres.js +287 -0
  32. package/credential/repositories/credential-repository.js +300 -0
  33. package/credential/use-cases/get-credential-for-user.js +25 -0
  34. package/credential/use-cases/update-authentication-status.js +15 -0
  35. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  36. package/database/adapters/lambda-invoker.js +97 -0
  37. package/database/config.js +154 -0
  38. package/database/documentdb-encryption-service.js +330 -0
  39. package/database/documentdb-utils.js +136 -0
  40. package/database/encryption/README.md +839 -0
  41. package/database/encryption/documentdb-encryption-service.md +3575 -0
  42. package/database/encryption/encryption-schema-registry.js +401 -0
  43. package/database/encryption/field-encryption-service.js +254 -0
  44. package/database/encryption/logger.js +79 -0
  45. package/database/encryption/prisma-encryption-extension.js +230 -0
  46. package/database/index.js +21 -21
  47. package/database/prisma.js +182 -0
  48. package/database/repositories/health-check-repository-documentdb.js +138 -0
  49. package/database/repositories/health-check-repository-factory.js +48 -0
  50. package/database/repositories/health-check-repository-interface.js +82 -0
  51. package/database/repositories/health-check-repository-mongodb.js +89 -0
  52. package/database/repositories/health-check-repository-postgres.js +82 -0
  53. package/database/repositories/migration-status-repository-s3.js +137 -0
  54. package/database/use-cases/check-database-health-use-case.js +29 -0
  55. package/database/use-cases/check-database-state-use-case.js +81 -0
  56. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  57. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  58. package/database/use-cases/get-migration-status-use-case.js +93 -0
  59. package/database/use-cases/run-database-migration-use-case.js +139 -0
  60. package/database/use-cases/test-encryption-use-case.js +253 -0
  61. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  62. package/database/utils/mongodb-collection-utils.js +94 -0
  63. package/database/utils/mongodb-schema-init.js +108 -0
  64. package/database/utils/prisma-runner.js +477 -0
  65. package/database/utils/prisma-schema-parser.js +182 -0
  66. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  67. package/encrypt/Cryptor.js +34 -168
  68. package/encrypt/index.js +1 -2
  69. package/errors/client-safe-error.js +26 -0
  70. package/errors/fetch-error.js +15 -7
  71. package/errors/index.js +2 -0
  72. package/generated/prisma-mongodb/client.d.ts +1 -0
  73. package/generated/prisma-mongodb/client.js +5 -0
  74. package/generated/prisma-mongodb/default.d.ts +1 -0
  75. package/generated/prisma-mongodb/default.js +5 -0
  76. package/generated/prisma-mongodb/edge.d.ts +1 -0
  77. package/generated/prisma-mongodb/edge.js +374 -0
  78. package/generated/prisma-mongodb/index-browser.js +356 -0
  79. package/generated/prisma-mongodb/index.d.ts +25667 -0
  80. package/generated/prisma-mongodb/index.js +399 -0
  81. package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  82. package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  83. package/generated/prisma-mongodb/package.json +183 -0
  84. package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
  85. package/generated/prisma-mongodb/runtime/edge.js +35 -0
  86. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  87. package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
  88. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  89. package/generated/prisma-mongodb/runtime/library.js +147 -0
  90. package/generated/prisma-mongodb/runtime/react-native.js +84 -0
  91. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
  92. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
  93. package/generated/prisma-mongodb/schema.prisma +432 -0
  94. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
  95. package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
  96. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  97. package/generated/prisma-mongodb/wasm.js +381 -0
  98. package/generated/prisma-postgresql/client.d.ts +1 -0
  99. package/generated/prisma-postgresql/client.js +5 -0
  100. package/generated/prisma-postgresql/default.d.ts +1 -0
  101. package/generated/prisma-postgresql/default.js +5 -0
  102. package/generated/prisma-postgresql/edge.d.ts +1 -0
  103. package/generated/prisma-postgresql/edge.js +395 -0
  104. package/generated/prisma-postgresql/index-browser.js +377 -0
  105. package/generated/prisma-postgresql/index.d.ts +28455 -0
  106. package/generated/prisma-postgresql/index.js +420 -0
  107. package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  108. package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  109. package/generated/prisma-postgresql/package.json +183 -0
  110. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  111. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  112. package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
  113. package/generated/prisma-postgresql/runtime/edge.js +35 -0
  114. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  115. package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
  116. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  117. package/generated/prisma-postgresql/runtime/library.js +147 -0
  118. package/generated/prisma-postgresql/runtime/react-native.js +84 -0
  119. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
  120. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
  121. package/generated/prisma-postgresql/schema.prisma +414 -0
  122. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
  123. package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
  124. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  125. package/generated/prisma-postgresql/wasm.js +402 -0
  126. package/handlers/WEBHOOKS.md +653 -0
  127. package/handlers/app-definition-loader.js +43 -0
  128. package/handlers/app-handler-helpers.js +57 -0
  129. package/handlers/backend-utils.js +316 -0
  130. package/handlers/database-migration-handler.js +227 -0
  131. package/handlers/integration-event-dispatcher.js +54 -0
  132. package/handlers/middleware/admin-auth.js +73 -0
  133. package/handlers/routers/HEALTHCHECK.md +342 -0
  134. package/handlers/routers/auth.js +15 -0
  135. package/handlers/routers/db-migration.handler.js +29 -0
  136. package/handlers/routers/db-migration.js +311 -0
  137. package/handlers/routers/health.js +518 -0
  138. package/handlers/routers/integration-defined-routers.js +117 -0
  139. package/handlers/routers/integration-webhook-routers.js +67 -0
  140. package/handlers/routers/reporting.js +9 -0
  141. package/handlers/routers/user.js +63 -0
  142. package/handlers/routers/websocket.js +57 -0
  143. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  144. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  145. package/handlers/workers/db-migration.js +352 -0
  146. package/handlers/workers/dlq-processor.js +63 -0
  147. package/handlers/workers/integration-defined-workers.js +30 -0
  148. package/index.js +111 -50
  149. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
  150. package/infrastructure/scheduler/index.js +33 -0
  151. package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
  152. package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
  153. package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
  154. package/integrations/EXTENSIONS.md +240 -0
  155. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  156. package/integrations/extension.js +254 -0
  157. package/integrations/index.js +20 -10
  158. package/integrations/integration-base.js +619 -72
  159. package/integrations/integration-router.js +460 -218
  160. package/integrations/options.js +3 -4
  161. package/integrations/repositories/config-patch-shared.js +43 -0
  162. package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
  163. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  164. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  165. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  166. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  167. package/integrations/repositories/integration-mapping-repository.js +156 -0
  168. package/integrations/repositories/integration-repository-documentdb.js +284 -0
  169. package/integrations/repositories/integration-repository-factory.js +51 -0
  170. package/integrations/repositories/integration-repository-interface.js +173 -0
  171. package/integrations/repositories/integration-repository-mongo.js +414 -0
  172. package/integrations/repositories/integration-repository-postgres.js +452 -0
  173. package/integrations/repositories/process-repository-documentdb.js +311 -0
  174. package/integrations/repositories/process-repository-factory.js +53 -0
  175. package/integrations/repositories/process-repository-interface.js +136 -0
  176. package/integrations/repositories/process-repository-mongo.js +262 -0
  177. package/integrations/repositories/process-repository-postgres.js +380 -0
  178. package/integrations/repositories/process-update-ops-shared.js +112 -0
  179. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  180. package/integrations/tests/doubles/dummy-integration-class.js +113 -0
  181. package/integrations/tests/doubles/test-integration-repository.js +146 -0
  182. package/integrations/use-cases/create-integration.js +215 -0
  183. package/integrations/use-cases/create-process.js +130 -0
  184. package/integrations/use-cases/delete-integration-for-user.js +119 -0
  185. package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
  186. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
  187. package/integrations/use-cases/get-integration-for-user.js +78 -0
  188. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  189. package/integrations/use-cases/get-integration-instance.js +83 -0
  190. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  191. package/integrations/use-cases/get-possible-integrations.js +27 -0
  192. package/integrations/use-cases/get-process.js +89 -0
  193. package/integrations/use-cases/index.js +19 -0
  194. package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
  195. package/integrations/use-cases/load-integration-context.js +71 -0
  196. package/integrations/use-cases/patch-integration-config.js +39 -0
  197. package/integrations/use-cases/process-errors.js +28 -0
  198. package/integrations/use-cases/update-integration-config.js +32 -0
  199. package/integrations/use-cases/update-integration-messages.js +44 -0
  200. package/integrations/use-cases/update-integration-status.js +32 -0
  201. package/integrations/use-cases/update-integration.js +92 -0
  202. package/integrations/use-cases/update-process-metrics.js +216 -0
  203. package/integrations/use-cases/update-process-state.js +163 -0
  204. package/integrations/utils/map-integration-dto.js +37 -0
  205. package/jest-global-setup-noop.js +3 -0
  206. package/jest-global-teardown-noop.js +3 -0
  207. package/logs/logger.js +0 -4
  208. package/{module-plugin → modules}/index.js +0 -10
  209. package/modules/module-factory.js +56 -0
  210. package/modules/module.js +274 -0
  211. package/modules/repositories/module-repository-documentdb.js +350 -0
  212. package/modules/repositories/module-repository-factory.js +40 -0
  213. package/modules/repositories/module-repository-interface.js +145 -0
  214. package/modules/repositories/module-repository-mongo.js +436 -0
  215. package/modules/repositories/module-repository-postgres.js +481 -0
  216. package/modules/repositories/module-repository.js +369 -0
  217. package/modules/requester/api-key.js +52 -0
  218. package/modules/requester/oauth-2.js +396 -0
  219. package/modules/requester/requester.js +285 -0
  220. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  221. package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
  222. package/modules/tests/doubles/test-module-factory.js +16 -0
  223. package/modules/tests/doubles/test-module-repository.js +39 -0
  224. package/modules/use-cases/get-entities-for-user.js +32 -0
  225. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  226. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  227. package/modules/use-cases/get-module-instance-from-type.js +34 -0
  228. package/modules/use-cases/get-module.js +74 -0
  229. package/modules/use-cases/process-authorization-callback.js +243 -0
  230. package/modules/use-cases/refresh-entity-options.js +72 -0
  231. package/modules/use-cases/test-module-auth.js +72 -0
  232. package/modules/utils/map-module-dto.js +18 -0
  233. package/package.json +82 -50
  234. package/prisma-mongodb/schema.prisma +432 -0
  235. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  236. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  237. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  238. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  239. package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
  240. package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
  241. package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
  242. package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
  243. package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
  244. package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
  245. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  246. package/prisma-postgresql/schema.prisma +414 -0
  247. package/queues/queuer-util.js +103 -21
  248. package/reporting/README.md +86 -0
  249. package/reporting/index.js +17 -0
  250. package/reporting/reporting-router.js +77 -0
  251. package/reporting/repositories/reporting-repository-documentdb.js +127 -0
  252. package/reporting/repositories/reporting-repository-factory.js +35 -0
  253. package/reporting/repositories/reporting-repository-interface.js +16 -0
  254. package/reporting/repositories/reporting-repository-mongo.js +54 -0
  255. package/reporting/repositories/reporting-repository-postgres.js +70 -0
  256. package/reporting/use-cases/index.js +6 -0
  257. package/reporting/use-cases/list-integrations-report.js +142 -0
  258. package/syncs/manager.js +468 -443
  259. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  260. package/syncs/repositories/sync-repository-factory.js +43 -0
  261. package/syncs/repositories/sync-repository-interface.js +109 -0
  262. package/syncs/repositories/sync-repository-mongo.js +239 -0
  263. package/syncs/repositories/sync-repository-postgres.js +319 -0
  264. package/syncs/sync.js +0 -1
  265. package/token/repositories/token-repository-documentdb.js +137 -0
  266. package/token/repositories/token-repository-factory.js +40 -0
  267. package/token/repositories/token-repository-interface.js +131 -0
  268. package/token/repositories/token-repository-mongo.js +219 -0
  269. package/token/repositories/token-repository-postgres.js +264 -0
  270. package/token/repositories/token-repository.js +219 -0
  271. package/types/associations/index.d.ts +0 -17
  272. package/types/core/index.d.ts +12 -4
  273. package/types/database/index.d.ts +10 -2
  274. package/types/encrypt/index.d.ts +5 -3
  275. package/types/integrations/index.d.ts +3 -8
  276. package/types/module-plugin/index.d.ts +17 -69
  277. package/types/syncs/index.d.ts +0 -17
  278. package/user/repositories/user-repository-documentdb.js +458 -0
  279. package/user/repositories/user-repository-factory.js +52 -0
  280. package/user/repositories/user-repository-interface.js +214 -0
  281. package/user/repositories/user-repository-mongo.js +323 -0
  282. package/user/repositories/user-repository-postgres.js +377 -0
  283. package/user/tests/doubles/test-user-repository.js +72 -0
  284. package/user/use-cases/authenticate-user.js +127 -0
  285. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  286. package/user/use-cases/create-individual-user.js +61 -0
  287. package/user/use-cases/create-organization-user.js +47 -0
  288. package/user/use-cases/create-token-for-user-id.js +30 -0
  289. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  290. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  291. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  292. package/user/use-cases/login-user.js +122 -0
  293. package/user/user.js +125 -0
  294. package/utils/backend-path.js +38 -0
  295. package/utils/index.js +6 -0
  296. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  297. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  298. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  299. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  300. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  301. package/websocket/repositories/websocket-connection-repository.js +161 -0
  302. package/assertions/is-equal.js +0 -17
  303. package/associations/model.js +0 -54
  304. package/database/models/IndividualUser.js +0 -76
  305. package/database/models/OrganizationUser.js +0 -29
  306. package/database/models/State.js +0 -9
  307. package/database/models/Token.js +0 -70
  308. package/database/models/UserModel.js +0 -7
  309. package/database/models/WebsocketConnection.js +0 -49
  310. package/database/mongo.js +0 -45
  311. package/database/mongoose.js +0 -5
  312. package/encrypt/Cryptor.test.js +0 -32
  313. package/encrypt/encrypt.js +0 -132
  314. package/encrypt/encrypt.test.js +0 -1069
  315. package/encrypt/test-encrypt.js +0 -107
  316. package/errors/base-error.test.js +0 -32
  317. package/errors/fetch-error.test.js +0 -79
  318. package/errors/halt-error.test.js +0 -11
  319. package/errors/validation-errors.test.js +0 -120
  320. package/integrations/create-frigg-backend.js +0 -31
  321. package/integrations/integration-factory.js +0 -251
  322. package/integrations/integration-mapping.js +0 -43
  323. package/integrations/integration-model.js +0 -46
  324. package/integrations/integration-user.js +0 -144
  325. package/integrations/test/integration-base.test.js +0 -144
  326. package/lambda/TimeoutCatcher.test.js +0 -68
  327. package/logs/logger.test.js +0 -76
  328. package/module-plugin/auther.js +0 -350
  329. package/module-plugin/credential.js +0 -22
  330. package/module-plugin/entity-manager.js +0 -70
  331. package/module-plugin/entity.js +0 -46
  332. package/module-plugin/manager.js +0 -169
  333. package/module-plugin/module-factory.js +0 -61
  334. package/module-plugin/requester/api-key.js +0 -36
  335. package/module-plugin/requester/oauth-2.js +0 -219
  336. package/module-plugin/requester/requester.js +0 -165
  337. package/module-plugin/requester/requester.test.js +0 -28
  338. package/module-plugin/test/auther.test.js +0 -97
  339. package/syncs/model.js +0 -62
  340. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  341. /package/{module-plugin → modules}/requester/basic.js +0 -0
  342. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,458 @@
1
+ const bcrypt = require('bcryptjs');
2
+ const { prisma } = require('../../database/prisma');
3
+ const {
4
+ toObjectId,
5
+ fromObjectId,
6
+ findOne,
7
+ findMany,
8
+ insertOne,
9
+ updateOne,
10
+ deleteOne,
11
+ } = require('../../database/documentdb-utils');
12
+ const {
13
+ createTokenRepository,
14
+ } = require('../../token/repositories/token-repository-factory');
15
+ const { UserRepositoryInterface } = require('./user-repository-interface');
16
+ const { ClientSafeError } = require('../../errors');
17
+ const {
18
+ DocumentDBEncryptionService,
19
+ } = require('../../database/documentdb-encryption-service');
20
+
21
+ /**
22
+ * User repository for DocumentDB.
23
+ * Uses DocumentDBEncryptionService for field-level encryption.
24
+ *
25
+ * Encrypted fields: User.hashword
26
+ *
27
+ * @see DocumentDBEncryptionService
28
+ * @see encryption-schema-registry.js
29
+ */
30
+ class UserRepositoryDocumentDB extends UserRepositoryInterface {
31
+ constructor() {
32
+ super();
33
+ this.prisma = prisma;
34
+ this.tokenRepository = createTokenRepository();
35
+ this.encryptionService = new DocumentDBEncryptionService();
36
+ }
37
+
38
+ async getSessionToken(token) {
39
+ const jsonToken =
40
+ this.tokenRepository.getJSONTokenFromBase64BufferToken(token);
41
+ const sessionToken = await this.tokenRepository.validateAndGetToken(
42
+ jsonToken
43
+ );
44
+ return sessionToken;
45
+ }
46
+
47
+ async findOrganizationUserById(userId) {
48
+ const doc = await findOne(this.prisma, 'User', {
49
+ _id: toObjectId(userId),
50
+ type: 'ORGANIZATION',
51
+ });
52
+ const decrypted = await this.encryptionService.decryptFields(
53
+ 'User',
54
+ doc
55
+ );
56
+ return this._mapUser(decrypted);
57
+ }
58
+
59
+ async findIndividualUserById(userId) {
60
+ const doc = await findOne(this.prisma, 'User', {
61
+ _id: toObjectId(userId),
62
+ type: 'INDIVIDUAL',
63
+ });
64
+ const decrypted = await this.encryptionService.decryptFields(
65
+ 'User',
66
+ doc
67
+ );
68
+ return this._mapUser(decrypted);
69
+ }
70
+
71
+ async findIndividualUsersByOrganizationId(organizationUserId) {
72
+ const docs = await findMany(this.prisma, 'User', {
73
+ organizationId: toObjectId(organizationUserId),
74
+ type: 'INDIVIDUAL',
75
+ });
76
+ return Promise.all(
77
+ docs.map(async (doc) => {
78
+ const decrypted = await this.encryptionService.decryptFields(
79
+ 'User',
80
+ doc
81
+ );
82
+ return this._mapUser(decrypted);
83
+ })
84
+ );
85
+ }
86
+
87
+ async createToken(userId, rawToken, minutes = 120) {
88
+ const createdToken = await this.tokenRepository.createTokenWithExpire(
89
+ fromObjectId(toObjectId(userId)),
90
+ rawToken,
91
+ minutes
92
+ );
93
+ return this.tokenRepository.createBase64BufferToken(
94
+ createdToken,
95
+ rawToken
96
+ );
97
+ }
98
+
99
+ async createIndividualUser(params) {
100
+ const now = new Date();
101
+ const document = {
102
+ type: 'INDIVIDUAL',
103
+ email: params.email ?? null,
104
+ username: params.username ?? null,
105
+ appUserId: params.appUserId ?? null,
106
+ organizationId: params.organization
107
+ ? toObjectId(params.organization)
108
+ : params.organizationId
109
+ ? toObjectId(params.organizationId)
110
+ : null,
111
+ createdAt: now,
112
+ updatedAt: now,
113
+ };
114
+
115
+ if (
116
+ params.hashword !== undefined &&
117
+ params.hashword !== null &&
118
+ params.hashword !== ''
119
+ ) {
120
+ if (typeof params.hashword !== 'string') {
121
+ throw new ClientSafeError('Password must be a string', 400);
122
+ }
123
+
124
+ if (params.hashword.startsWith('$2')) {
125
+ throw new Error(
126
+ 'Password appears to be already hashed. Pass plain text password only.'
127
+ );
128
+ }
129
+
130
+ // Bcrypt hash the password
131
+ document.hashword = await bcrypt.hash(params.hashword, 10);
132
+ }
133
+
134
+ // Encrypt sensitive fields before insert
135
+ const encryptedDocument = await this.encryptionService.encryptFields(
136
+ 'User',
137
+ document
138
+ );
139
+ const insertedId = await insertOne(
140
+ this.prisma,
141
+ 'User',
142
+ encryptedDocument
143
+ );
144
+ const created = await findOne(this.prisma, 'User', { _id: insertedId });
145
+
146
+ // Defensive check: verify document was found after insert
147
+ if (!created) {
148
+ console.error(
149
+ '[UserRepositoryDocumentDB] User not found after insert',
150
+ {
151
+ insertedId: fromObjectId(insertedId),
152
+ params: {
153
+ username: params.username,
154
+ appUserId: params.appUserId,
155
+ email: params.email,
156
+ },
157
+ }
158
+ );
159
+ throw new Error(
160
+ 'Failed to create individual user: Document not found after insert. ' +
161
+ 'This indicates a database consistency issue.'
162
+ );
163
+ }
164
+
165
+ // Decrypt sensitive fields after read
166
+ const decrypted = await this.encryptionService.decryptFields(
167
+ 'User',
168
+ created
169
+ );
170
+
171
+ return this._mapUser(decrypted);
172
+ }
173
+
174
+ async createOrganizationUser(params) {
175
+ const now = new Date();
176
+ const document = {
177
+ type: 'ORGANIZATION',
178
+ appOrgId: params.appOrgId ?? null,
179
+ name: params.name ?? null,
180
+ createdAt: now,
181
+ updatedAt: now,
182
+ };
183
+
184
+ // Encrypt sensitive fields before insert (consistency with individual user)
185
+ const encryptedDocument = await this.encryptionService.encryptFields(
186
+ 'User',
187
+ document
188
+ );
189
+ const insertedId = await insertOne(
190
+ this.prisma,
191
+ 'User',
192
+ encryptedDocument
193
+ );
194
+ const created = await findOne(this.prisma, 'User', { _id: insertedId });
195
+
196
+ // Defensive check: verify document was found after insert
197
+ if (!created) {
198
+ console.error(
199
+ '[UserRepositoryDocumentDB] Organization user not found after insert',
200
+ {
201
+ insertedId: fromObjectId(insertedId),
202
+ params: {
203
+ appOrgId: params.appOrgId,
204
+ name: params.name,
205
+ },
206
+ }
207
+ );
208
+ throw new Error(
209
+ 'Failed to create organization user: Document not found after insert. ' +
210
+ 'This indicates a database consistency issue.'
211
+ );
212
+ }
213
+
214
+ // Decrypt sensitive fields after read
215
+ const decrypted = await this.encryptionService.decryptFields(
216
+ 'User',
217
+ created
218
+ );
219
+ return this._mapUser(decrypted);
220
+ }
221
+
222
+ async findIndividualUserByUsername(username) {
223
+ const doc = await findOne(this.prisma, 'User', {
224
+ type: 'INDIVIDUAL',
225
+ username,
226
+ });
227
+ const decrypted = await this.encryptionService.decryptFields(
228
+ 'User',
229
+ doc
230
+ );
231
+ return this._mapUser(decrypted);
232
+ }
233
+
234
+ async findIndividualUserByAppUserId(appUserId) {
235
+ const doc = await findOne(this.prisma, 'User', {
236
+ type: 'INDIVIDUAL',
237
+ appUserId,
238
+ });
239
+ const decrypted = await this.encryptionService.decryptFields(
240
+ 'User',
241
+ doc
242
+ );
243
+ return this._mapUser(decrypted);
244
+ }
245
+
246
+ async findOrganizationUserByAppOrgId(appOrgId) {
247
+ const doc = await findOne(this.prisma, 'User', {
248
+ type: 'ORGANIZATION',
249
+ appOrgId,
250
+ });
251
+ const decrypted = await this.encryptionService.decryptFields(
252
+ 'User',
253
+ doc
254
+ );
255
+ return this._mapUser(decrypted);
256
+ }
257
+
258
+ async findIndividualUserByEmail(email) {
259
+ const doc = await findOne(this.prisma, 'User', {
260
+ type: 'INDIVIDUAL',
261
+ email,
262
+ });
263
+ const decrypted = await this.encryptionService.decryptFields(
264
+ 'User',
265
+ doc
266
+ );
267
+ return this._mapUser(decrypted);
268
+ }
269
+
270
+ async updateIndividualUser(userId, updates) {
271
+ const objectId = toObjectId(userId);
272
+ if (!objectId) return null;
273
+
274
+ const payload = await this._prepareUpdatePayload(updates);
275
+ payload.updatedAt = new Date();
276
+
277
+ // Encrypt sensitive fields before update
278
+ const encryptedPayload = await this.encryptionService.encryptFields(
279
+ 'User',
280
+ payload
281
+ );
282
+
283
+ await updateOne(
284
+ this.prisma,
285
+ 'User',
286
+ { _id: objectId, type: 'INDIVIDUAL' },
287
+ { $set: encryptedPayload }
288
+ );
289
+
290
+ const updated = await findOne(this.prisma, 'User', { _id: objectId });
291
+
292
+ // Defensive check: verify document was found after update
293
+ if (!updated) {
294
+ console.error(
295
+ '[UserRepositoryDocumentDB] Individual user not found after update',
296
+ {
297
+ userId: fromObjectId(objectId),
298
+ updates,
299
+ }
300
+ );
301
+ throw new Error(
302
+ 'Failed to update individual user: Document not found after update. ' +
303
+ 'This indicates a database consistency issue.'
304
+ );
305
+ }
306
+
307
+ const decrypted = await this.encryptionService.decryptFields(
308
+ 'User',
309
+ updated
310
+ );
311
+ return this._mapUser(decrypted);
312
+ }
313
+
314
+ async updateOrganizationUser(userId, updates) {
315
+ const objectId = toObjectId(userId);
316
+ if (!objectId) return null;
317
+
318
+ const payload = { ...updates, updatedAt: new Date() };
319
+
320
+ const encryptedPayload = await this.encryptionService.encryptFields(
321
+ 'User',
322
+ payload
323
+ );
324
+
325
+ await updateOne(
326
+ this.prisma,
327
+ 'User',
328
+ { _id: objectId, type: 'ORGANIZATION' },
329
+ { $set: encryptedPayload }
330
+ );
331
+
332
+ const updated = await findOne(this.prisma, 'User', { _id: objectId });
333
+
334
+ if (!updated) {
335
+ console.error(
336
+ '[UserRepositoryDocumentDB] Organization user not found after update',
337
+ {
338
+ userId: fromObjectId(objectId),
339
+ updates,
340
+ }
341
+ );
342
+ throw new Error(
343
+ 'Failed to update organization user: Document not found after update. ' +
344
+ 'This indicates a database consistency issue.'
345
+ );
346
+ }
347
+
348
+ const decrypted = await this.encryptionService.decryptFields(
349
+ 'User',
350
+ updated
351
+ );
352
+ return this._mapUser(decrypted);
353
+ }
354
+
355
+ async deleteUser(userId) {
356
+ const objectId = toObjectId(userId);
357
+ if (!objectId) return false;
358
+
359
+ const result = await deleteOne(this.prisma, 'User', { _id: objectId });
360
+ const deleted = result?.n ?? 0;
361
+ return deleted > 0;
362
+ }
363
+
364
+ _mapUser(doc) {
365
+ if (!doc) {
366
+ console.warn(
367
+ '[UserRepositoryDocumentDB] _mapUser received null/undefined document'
368
+ );
369
+ return null;
370
+ }
371
+
372
+ // Use optional chaining for robustness
373
+ return {
374
+ id: fromObjectId(doc?._id),
375
+ type: doc?.type ?? null,
376
+ email: doc?.email ?? null,
377
+ username: doc?.username ?? null,
378
+ hashword: doc?.hashword ?? null,
379
+ appUserId: doc?.appUserId ?? null,
380
+ organizationId: doc?.organizationId
381
+ ? fromObjectId(doc.organizationId)
382
+ : null,
383
+ appOrgId: doc?.appOrgId ?? null,
384
+ name: doc?.name ?? null,
385
+ createdAt: this._parseDate(doc?.createdAt),
386
+ updatedAt: this._parseDate(doc?.updatedAt),
387
+ };
388
+ }
389
+
390
+ async _prepareUpdatePayload(updates = {}) {
391
+ const payload = { ...updates };
392
+
393
+ if (
394
+ payload.hashword !== undefined &&
395
+ payload.hashword !== null &&
396
+ payload.hashword !== ''
397
+ ) {
398
+ if (typeof payload.hashword !== 'string') {
399
+ throw new ClientSafeError('Password must be a string', 400);
400
+ }
401
+
402
+ if (payload.hashword.startsWith('$2')) {
403
+ throw new Error(
404
+ 'Password appears to be already hashed. Pass plain text password only.'
405
+ );
406
+ }
407
+
408
+ payload.hashword = await bcrypt.hash(payload.hashword, 10);
409
+ }
410
+
411
+ if (payload.organization !== undefined) {
412
+ payload.organizationId = toObjectId(payload.organization);
413
+ delete payload.organization;
414
+ }
415
+
416
+ if (payload.organizationId !== undefined) {
417
+ payload.organizationId = payload.organizationId
418
+ ? toObjectId(payload.organizationId)
419
+ : null;
420
+ }
421
+
422
+ return payload;
423
+ }
424
+
425
+ /**
426
+ * Parse date value safely, returning undefined for invalid dates
427
+ * @private
428
+ * @param {*} value - Date value from database
429
+ * @returns {Date|undefined} Valid Date object or undefined
430
+ */
431
+ _parseDate(value) {
432
+ if (!value) return undefined;
433
+ const date = new Date(value);
434
+ return isNaN(date.getTime()) ? undefined : date;
435
+ }
436
+
437
+ /**
438
+ * Link an individual user to an organization user
439
+ * @param {string} individualUserId - Individual user ID (MongoDB ObjectId string)
440
+ * @param {string} organizationUserId - Organization user ID (MongoDB ObjectId string)
441
+ * @returns {Promise<Object>} Updated individual user object
442
+ */
443
+ async linkIndividualToOrganization(individualUserId, organizationUserId) {
444
+ const doc = await updateOne(
445
+ this.prisma,
446
+ 'User',
447
+ { _id: toObjectId(individualUserId), type: 'INDIVIDUAL' },
448
+ { $set: { organizationId: toObjectId(organizationUserId) } }
449
+ );
450
+ const decrypted = await this.encryptionService.decryptFields(
451
+ 'User',
452
+ doc
453
+ );
454
+ return this._mapUser(decrypted);
455
+ }
456
+ }
457
+
458
+ module.exports = { UserRepositoryDocumentDB };
@@ -0,0 +1,52 @@
1
+ const { UserRepositoryMongo } = require('./user-repository-mongo');
2
+ const { UserRepositoryPostgres } = require('./user-repository-postgres');
3
+ const { UserRepositoryDocumentDB } = require('./user-repository-documentdb');
4
+ const databaseConfig = require('../../database/config');
5
+
6
+ /**
7
+ * User Repository Factory
8
+ * Creates the appropriate repository adapter based on database type
9
+ *
10
+ * Database-specific implementations:
11
+ * - MongoDB: Uses String IDs (ObjectId), no conversion needed
12
+ * - PostgreSQL: Uses Int IDs, converts String ↔ Int
13
+ *
14
+ * All repository methods return String IDs regardless of database type,
15
+ * ensuring application layer consistency.
16
+ *
17
+ * Usage:
18
+ * ```javascript
19
+ * const repository = createUserRepository();
20
+ * const user = await repository.findIndividualUserById(id); // ID is string
21
+ * const orgUser = await repository.findOrganizationUserById(id); // ID is string
22
+ * ```
23
+ *
24
+ * @returns {UserRepositoryInterface} Configured repository adapter
25
+ */
26
+ function createUserRepository() {
27
+ const dbType = databaseConfig.DB_TYPE;
28
+
29
+ switch (dbType) {
30
+ case 'mongodb':
31
+ return new UserRepositoryMongo();
32
+
33
+ case 'postgresql':
34
+ return new UserRepositoryPostgres();
35
+
36
+ case 'documentdb':
37
+ return new UserRepositoryDocumentDB();
38
+
39
+ default:
40
+ throw new Error(
41
+ `Unsupported DB_TYPE: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
42
+ );
43
+ }
44
+ }
45
+
46
+ module.exports = {
47
+ createUserRepository,
48
+ // Export adapters for direct testing
49
+ UserRepositoryMongo,
50
+ UserRepositoryPostgres,
51
+ UserRepositoryDocumentDB,
52
+ };