@friggframework/core 2.0.0-next.12 → 2.0.0-next.121

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 (410) hide show
  1. package/.eslintrc.json +18 -1
  2. package/CLAUDE.md +830 -0
  3. package/README.md +996 -50
  4. package/admin-scripts/index.js +52 -0
  5. package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
  6. package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
  7. package/admin-scripts/repositories/admin-script-execution-repository-interface.js +171 -0
  8. package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +238 -0
  9. package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +278 -0
  10. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  11. package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
  12. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  13. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  14. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  15. package/application/commands/README.md +451 -0
  16. package/application/commands/admin-script-commands.js +359 -0
  17. package/application/commands/credential-commands.js +262 -0
  18. package/application/commands/entity-commands.js +294 -0
  19. package/application/commands/integration-commands.js +419 -0
  20. package/application/commands/integration-mapping-commands.js +25 -0
  21. package/application/commands/process-commands.js +135 -0
  22. package/application/commands/report-commands.js +188 -0
  23. package/application/commands/scheduler-commands.js +263 -0
  24. package/application/commands/usage-commands.js +56 -0
  25. package/application/commands/user-commands.js +312 -0
  26. package/application/index.js +83 -0
  27. package/artifacts/repositories/artifact-repository-factory.js +19 -0
  28. package/artifacts/repositories/artifact-repository-interface.js +27 -0
  29. package/artifacts/repositories/artifact-repository-local.js +42 -0
  30. package/artifacts/repositories/artifact-repository-s3.js +61 -0
  31. package/assertions/index.js +0 -3
  32. package/core/CLAUDE.md +715 -0
  33. package/core/Worker.js +55 -24
  34. package/core/create-handler.js +121 -38
  35. package/core/index.js +8 -1
  36. package/core/invocation-scope.js +225 -0
  37. package/core/parameters-to-env.js +257 -0
  38. package/core/secrets-to-env.js +7 -6
  39. package/core/ssm-preload.mjs +34 -0
  40. package/credential/repositories/credential-active-type.js +32 -0
  41. package/credential/repositories/credential-repository-documentdb.js +355 -0
  42. package/credential/repositories/credential-repository-factory.js +54 -0
  43. package/credential/repositories/credential-repository-interface.js +113 -0
  44. package/credential/repositories/credential-repository-mongo.js +294 -0
  45. package/credential/repositories/credential-repository-postgres.js +312 -0
  46. package/credential/repositories/credential-repository.js +300 -0
  47. package/credential/use-cases/get-credential-for-user.js +25 -0
  48. package/credential/use-cases/update-authentication-status.js +15 -0
  49. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  50. package/database/adapters/lambda-invoker.js +97 -0
  51. package/database/config.js +147 -0
  52. package/database/documentdb-encryption-service.js +334 -0
  53. package/database/documentdb-utils.js +196 -0
  54. package/database/encryption/README.md +863 -0
  55. package/database/encryption/documentdb-encryption-service.md +3575 -0
  56. package/database/encryption/encryption-schema-registry.js +404 -0
  57. package/database/encryption/field-encryption-service.js +254 -0
  58. package/database/encryption/integration-mapping-encryption.js +97 -0
  59. package/database/encryption/logger.js +82 -0
  60. package/database/encryption/prisma-encryption-extension.js +241 -0
  61. package/database/index.js +21 -21
  62. package/database/prisma.js +182 -0
  63. package/database/repositories/health-check-repository-documentdb.js +138 -0
  64. package/database/repositories/health-check-repository-factory.js +48 -0
  65. package/database/repositories/health-check-repository-interface.js +82 -0
  66. package/database/repositories/health-check-repository-mongodb.js +89 -0
  67. package/database/repositories/health-check-repository-postgres.js +82 -0
  68. package/database/repositories/migration-status-repository-s3.js +137 -0
  69. package/database/use-cases/check-database-health-use-case.js +29 -0
  70. package/database/use-cases/check-database-state-use-case.js +81 -0
  71. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  72. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  73. package/database/use-cases/get-migration-status-use-case.js +93 -0
  74. package/database/use-cases/resolve-migration-via-worker-use-case.js +49 -0
  75. package/database/use-cases/run-database-migration-use-case.js +139 -0
  76. package/database/use-cases/test-encryption-use-case.js +253 -0
  77. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  78. package/database/utils/mongodb-collection-utils.js +94 -0
  79. package/database/utils/mongodb-schema-init.js +108 -0
  80. package/database/utils/prisma-runner.js +491 -0
  81. package/database/utils/prisma-schema-parser.js +182 -0
  82. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  83. package/encrypt/Cryptor.js +34 -168
  84. package/encrypt/index.js +1 -2
  85. package/errors/client-safe-error.js +26 -0
  86. package/errors/fetch-error.js +59 -57
  87. package/errors/index.js +6 -0
  88. package/errors/mapping-already-exists-error.js +14 -0
  89. package/generated/prisma-mongodb/client.d.ts +1 -0
  90. package/generated/prisma-mongodb/client.js +5 -0
  91. package/generated/prisma-mongodb/default.d.ts +1 -0
  92. package/generated/prisma-mongodb/default.js +5 -0
  93. package/generated/prisma-mongodb/edge.d.ts +1 -0
  94. package/generated/prisma-mongodb/edge.js +386 -0
  95. package/generated/prisma-mongodb/index-browser.js +368 -0
  96. package/generated/prisma-mongodb/index.d.ts +27065 -0
  97. package/generated/prisma-mongodb/index.js +411 -0
  98. package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  99. package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  100. package/generated/prisma-mongodb/package.json +183 -0
  101. package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
  102. package/generated/prisma-mongodb/runtime/edge.js +35 -0
  103. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  104. package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
  105. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  106. package/generated/prisma-mongodb/runtime/library.js +147 -0
  107. package/generated/prisma-mongodb/runtime/react-native.js +84 -0
  108. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
  109. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
  110. package/generated/prisma-mongodb/schema.prisma +455 -0
  111. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
  112. package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
  113. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  114. package/generated/prisma-mongodb/wasm.js +393 -0
  115. package/generated/prisma-postgresql/client.d.ts +1 -0
  116. package/generated/prisma-postgresql/client.js +5 -0
  117. package/generated/prisma-postgresql/default.d.ts +1 -0
  118. package/generated/prisma-postgresql/default.js +5 -0
  119. package/generated/prisma-postgresql/edge.d.ts +1 -0
  120. package/generated/prisma-postgresql/edge.js +407 -0
  121. package/generated/prisma-postgresql/index-browser.js +389 -0
  122. package/generated/prisma-postgresql/index.d.ts +29904 -0
  123. package/generated/prisma-postgresql/index.js +432 -0
  124. package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  125. package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  126. package/generated/prisma-postgresql/package.json +183 -0
  127. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  128. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  129. package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
  130. package/generated/prisma-postgresql/runtime/edge.js +35 -0
  131. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  132. package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
  133. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  134. package/generated/prisma-postgresql/runtime/library.js +147 -0
  135. package/generated/prisma-postgresql/runtime/react-native.js +84 -0
  136. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
  137. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
  138. package/generated/prisma-postgresql/schema.prisma +436 -0
  139. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
  140. package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
  141. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  142. package/generated/prisma-postgresql/wasm.js +414 -0
  143. package/handlers/WEBHOOKS.md +682 -0
  144. package/handlers/app-definition-loader.js +103 -0
  145. package/handlers/app-handler-helpers.js +20 -6
  146. package/handlers/backend-utils.js +272 -46
  147. package/handlers/database-migration-handler.js +236 -0
  148. package/handlers/integration-event-dispatcher.js +68 -0
  149. package/handlers/middleware/admin-auth.js +73 -0
  150. package/handlers/routers/HEALTHCHECK.md +342 -0
  151. package/handlers/routers/auth.js +4 -15
  152. package/handlers/routers/db-migration.handler.js +29 -0
  153. package/handlers/routers/db-migration.js +329 -0
  154. package/handlers/routers/health.js +518 -0
  155. package/handlers/routers/integration-defined-routers.js +85 -10
  156. package/handlers/routers/integration-webhook-routers.js +87 -0
  157. package/handlers/routers/user.js +27 -5
  158. package/handlers/routers/websocket.js +31 -9
  159. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  160. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  161. package/handlers/workers/db-migration.js +438 -0
  162. package/handlers/workers/dlq-processor.js +89 -0
  163. package/handlers/workers/integration-defined-workers.js +13 -7
  164. package/index.js +123 -47
  165. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +238 -0
  166. package/infrastructure/scheduler/index.js +33 -0
  167. package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
  168. package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
  169. package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
  170. package/integrations/EXTENSIONS.md +240 -0
  171. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  172. package/integrations/extension.js +254 -0
  173. package/integrations/index.js +20 -10
  174. package/integrations/integration-base.js +777 -74
  175. package/integrations/integration-router.js +399 -180
  176. package/integrations/options.js +1 -1
  177. package/integrations/repositories/config-patch-shared.js +43 -0
  178. package/integrations/repositories/integration-mapping-query-pipeline.js +152 -0
  179. package/integrations/repositories/integration-mapping-query.js +193 -0
  180. package/integrations/repositories/integration-mapping-repository-documentdb.js +406 -0
  181. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  182. package/integrations/repositories/integration-mapping-repository-interface.js +183 -0
  183. package/integrations/repositories/integration-mapping-repository-mongo.js +284 -0
  184. package/integrations/repositories/integration-mapping-repository-postgres.js +403 -0
  185. package/integrations/repositories/integration-mapping-repository.js +185 -0
  186. package/integrations/repositories/integration-repository-documentdb.js +354 -0
  187. package/integrations/repositories/integration-repository-factory.js +51 -0
  188. package/integrations/repositories/integration-repository-interface.js +219 -0
  189. package/integrations/repositories/integration-repository-mongo.js +502 -0
  190. package/integrations/repositories/integration-repository-postgres.js +550 -0
  191. package/integrations/repositories/process-repository-documentdb.js +311 -0
  192. package/integrations/repositories/process-repository-factory.js +53 -0
  193. package/integrations/repositories/process-repository-interface.js +136 -0
  194. package/integrations/repositories/process-repository-mongo.js +262 -0
  195. package/integrations/repositories/process-repository-postgres.js +380 -0
  196. package/integrations/repositories/process-update-ops-shared.js +112 -0
  197. package/integrations/repositories/report-id.js +13 -0
  198. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  199. package/integrations/tests/doubles/dummy-integration-class.js +113 -0
  200. package/integrations/tests/doubles/test-integration-repository.js +146 -0
  201. package/integrations/use-cases/add-entities-to-integration.js +92 -0
  202. package/integrations/use-cases/create-integration.js +253 -0
  203. package/integrations/use-cases/create-process.js +130 -0
  204. package/integrations/use-cases/delete-integration-for-user.js +130 -0
  205. package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
  206. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
  207. package/integrations/use-cases/get-integration-for-user.js +78 -0
  208. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  209. package/integrations/use-cases/get-integration-instance.js +83 -0
  210. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  211. package/integrations/use-cases/get-possible-integrations.js +27 -0
  212. package/integrations/use-cases/get-process.js +89 -0
  213. package/integrations/use-cases/index.js +19 -0
  214. package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
  215. package/integrations/use-cases/load-integration-context.js +71 -0
  216. package/integrations/use-cases/patch-integration-config.js +39 -0
  217. package/integrations/use-cases/process-errors.js +28 -0
  218. package/integrations/use-cases/remove-entities-from-integration.js +70 -0
  219. package/integrations/use-cases/update-integration-config.js +32 -0
  220. package/integrations/use-cases/update-integration-messages.js +44 -0
  221. package/integrations/use-cases/update-integration-status.js +32 -0
  222. package/integrations/use-cases/update-integration.js +92 -0
  223. package/integrations/use-cases/update-process-metrics.js +222 -0
  224. package/integrations/use-cases/update-process-state.js +163 -0
  225. package/integrations/utils/assert-entity-ids-input.js +42 -0
  226. package/integrations/utils/map-integration-dto.js +37 -0
  227. package/jest-global-setup-noop.js +3 -0
  228. package/jest-global-teardown-noop.js +3 -0
  229. package/jest.config.js +2 -0
  230. package/logs/.eslintrc.json +26 -1
  231. package/logs/__fixtures__/events.js +82 -0
  232. package/logs/__fixtures__/matchers.js +45 -0
  233. package/logs/__fixtures__/secrets.js +59 -0
  234. package/logs/__fixtures__/vectors.js +329 -0
  235. package/logs/__fixtures__/with-env.js +45 -0
  236. package/logs/context.js +109 -0
  237. package/logs/contract-keys.js +67 -0
  238. package/logs/debug-shims.js +46 -0
  239. package/logs/denied-keys.js +21 -0
  240. package/logs/index.js +14 -3
  241. package/logs/jest-logger-setup.js +25 -0
  242. package/logs/levels.js +71 -0
  243. package/logs/logger-runtime.js +137 -0
  244. package/logs/logger.js +134 -46
  245. package/logs/record.js +276 -0
  246. package/logs/redact.js +328 -0
  247. package/logs/serialize.js +329 -0
  248. package/logs/sinks.js +93 -0
  249. package/logs/summarize-event.js +163 -0
  250. package/{module-plugin → modules}/index.js +0 -10
  251. package/modules/module-factory.js +56 -0
  252. package/modules/module.js +320 -0
  253. package/modules/repositories/module-repository-documentdb.js +350 -0
  254. package/modules/repositories/module-repository-factory.js +40 -0
  255. package/modules/repositories/module-repository-interface.js +145 -0
  256. package/modules/repositories/module-repository-mongo.js +436 -0
  257. package/modules/repositories/module-repository-postgres.js +481 -0
  258. package/modules/repositories/module-repository.js +369 -0
  259. package/modules/requester/api-key.js +52 -0
  260. package/modules/requester/oauth-2.js +561 -0
  261. package/modules/requester/requester.js +605 -0
  262. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  263. package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
  264. package/modules/tests/doubles/test-module-factory.js +16 -0
  265. package/modules/tests/doubles/test-module-repository.js +39 -0
  266. package/modules/use-cases/get-entities-for-user.js +32 -0
  267. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  268. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  269. package/modules/use-cases/get-module-instance-from-type.js +34 -0
  270. package/modules/use-cases/get-module.js +74 -0
  271. package/modules/use-cases/process-authorization-callback.js +294 -0
  272. package/modules/use-cases/refresh-entity-options.js +72 -0
  273. package/modules/use-cases/test-module-auth.js +72 -0
  274. package/modules/utils/map-module-dto.js +20 -0
  275. package/modules/utils/to-credential-id.js +14 -0
  276. package/package.json +92 -52
  277. package/prisma-mongodb/schema.prisma +455 -0
  278. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  279. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  280. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  281. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  282. package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
  283. package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
  284. package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
  285. package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
  286. package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
  287. package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +26 -0
  288. package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
  289. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  290. package/prisma-postgresql/schema.prisma +436 -0
  291. package/queues/queue-delivery.js +41 -0
  292. package/queues/queuer-util.js +89 -21
  293. package/reporting/README.md +154 -0
  294. package/reporting/builtin-reports.js +6 -0
  295. package/reporting/index.js +13 -0
  296. package/reporting/report-base.js +49 -0
  297. package/reporting/reports/integrations-report.js +221 -0
  298. package/syncs/manager.js +468 -443
  299. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  300. package/syncs/repositories/sync-repository-factory.js +43 -0
  301. package/syncs/repositories/sync-repository-interface.js +109 -0
  302. package/syncs/repositories/sync-repository-mongo.js +239 -0
  303. package/syncs/repositories/sync-repository-postgres.js +319 -0
  304. package/syncs/sync.js +0 -1
  305. package/telemetry/README.md +331 -0
  306. package/telemetry/bind-telemetry-context.js +77 -0
  307. package/telemetry/canonical-counters.js +52 -0
  308. package/telemetry/exporters.js +85 -0
  309. package/telemetry/index.js +26 -0
  310. package/telemetry/instrument-handler.js +104 -0
  311. package/telemetry/no-op-telemetry.js +71 -0
  312. package/telemetry/north-star.js +103 -0
  313. package/telemetry/otel-telemetry.js +232 -0
  314. package/telemetry/plugin-subscribers.js +77 -0
  315. package/telemetry/telemetry-config.js +120 -0
  316. package/telemetry/telemetry-context.js +51 -0
  317. package/telemetry/telemetry-event-bus.js +58 -0
  318. package/telemetry/telemetry-runtime.js +164 -0
  319. package/telemetry/telemetry-service.js +52 -0
  320. package/telemetry/usage-rollup-subscriber.js +116 -0
  321. package/token/repositories/token-repository-documentdb.js +137 -0
  322. package/token/repositories/token-repository-factory.js +40 -0
  323. package/token/repositories/token-repository-interface.js +131 -0
  324. package/token/repositories/token-repository-mongo.js +219 -0
  325. package/token/repositories/token-repository-postgres.js +264 -0
  326. package/token/repositories/token-repository.js +219 -0
  327. package/types/associations/index.d.ts +0 -17
  328. package/types/core/index.d.ts +12 -4
  329. package/types/database/index.d.ts +10 -2
  330. package/types/encrypt/index.d.ts +5 -3
  331. package/types/errors/index.d.ts +24 -10
  332. package/types/integrations/index.d.ts +3 -8
  333. package/types/logs/index.d.ts +61 -2
  334. package/types/module-plugin/index.d.ts +20 -69
  335. package/types/syncs/index.d.ts +0 -17
  336. package/usage/README.md +54 -0
  337. package/usage/index.js +17 -0
  338. package/usage/repositories/usage-repository-documentdb.js +194 -0
  339. package/usage/repositories/usage-repository-factory.js +25 -0
  340. package/usage/repositories/usage-repository-interface.js +37 -0
  341. package/usage/repositories/usage-repository-prisma.js +146 -0
  342. package/usage/tracked-metrics.js +38 -0
  343. package/usage/usage-windows.js +24 -0
  344. package/user/repositories/user-repository-documentdb.js +426 -0
  345. package/user/repositories/user-repository-factory.js +52 -0
  346. package/user/repositories/user-repository-interface.js +214 -0
  347. package/user/repositories/user-repository-mongo.js +323 -0
  348. package/user/repositories/user-repository-postgres.js +377 -0
  349. package/user/tests/doubles/test-user-repository.js +72 -0
  350. package/user/use-cases/authenticate-user.js +143 -0
  351. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  352. package/user/use-cases/create-individual-user.js +61 -0
  353. package/user/use-cases/create-organization-user.js +47 -0
  354. package/user/use-cases/create-token-for-user-id.js +30 -0
  355. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  356. package/user/use-cases/get-user-from-bearer-token.js +82 -0
  357. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  358. package/user/use-cases/login-user.js +122 -0
  359. package/user/user.js +125 -0
  360. package/utils/backend-path.js +38 -0
  361. package/utils/index.js +6 -0
  362. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  363. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  364. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  365. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  366. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  367. package/websocket/repositories/websocket-connection-repository.js +161 -0
  368. package/assertions/is-equal.js +0 -17
  369. package/associations/model.js +0 -54
  370. package/database/models/IndividualUser.js +0 -76
  371. package/database/models/OrganizationUser.js +0 -29
  372. package/database/models/State.js +0 -9
  373. package/database/models/Token.js +0 -70
  374. package/database/models/UserModel.js +0 -7
  375. package/database/models/WebsocketConnection.js +0 -49
  376. package/database/mongo.js +0 -45
  377. package/database/mongoose.js +0 -5
  378. package/encrypt/Cryptor.test.js +0 -32
  379. package/encrypt/encrypt.js +0 -132
  380. package/encrypt/encrypt.test.js +0 -1069
  381. package/encrypt/test-encrypt.js +0 -107
  382. package/errors/base-error.test.js +0 -32
  383. package/errors/fetch-error.test.js +0 -79
  384. package/errors/halt-error.test.js +0 -11
  385. package/errors/validation-errors.test.js +0 -120
  386. package/handlers/routers/middleware/loadUser.js +0 -15
  387. package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
  388. package/integrations/create-frigg-backend.js +0 -31
  389. package/integrations/integration-factory.js +0 -251
  390. package/integrations/integration-mapping.js +0 -43
  391. package/integrations/integration-model.js +0 -46
  392. package/integrations/integration-user.js +0 -144
  393. package/integrations/test/integration-base.test.js +0 -144
  394. package/lambda/TimeoutCatcher.test.js +0 -68
  395. package/logs/logger.test.js +0 -76
  396. package/module-plugin/auther.js +0 -393
  397. package/module-plugin/credential.js +0 -22
  398. package/module-plugin/entity-manager.js +0 -70
  399. package/module-plugin/entity.js +0 -46
  400. package/module-plugin/manager.js +0 -169
  401. package/module-plugin/module-factory.js +0 -61
  402. package/module-plugin/requester/api-key.js +0 -36
  403. package/module-plugin/requester/oauth-2.js +0 -219
  404. package/module-plugin/requester/requester.js +0 -165
  405. package/module-plugin/requester/requester.test.js +0 -28
  406. package/module-plugin/test/auther.test.js +0 -97
  407. package/syncs/model.js +0 -62
  408. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  409. /package/{module-plugin → modules}/requester/basic.js +0 -0
  410. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,323 @@
1
+ const bcrypt = require('bcryptjs');
2
+ const { prisma } = require('../../database/prisma');
3
+ const {
4
+ createTokenRepository,
5
+ } = require('../../token/repositories/token-repository-factory');
6
+ const { UserRepositoryInterface } = require('./user-repository-interface');
7
+ const { ClientSafeError } = require('../../errors');
8
+
9
+ /**
10
+ * MongoDB User Repository Adapter
11
+ * Handles user operations with discriminator pattern support
12
+ *
13
+ * MongoDB-specific characteristics:
14
+ * - Uses String IDs (ObjectId)
15
+ * - No ID conversion needed (IDs are already strings)
16
+ * - IndividualUser/OrganizationUser discriminators → User model with type field
17
+ */
18
+ class UserRepositoryMongo extends UserRepositoryInterface {
19
+ constructor() {
20
+ super();
21
+ this.prisma = prisma;
22
+ this.tokenRepository = createTokenRepository();
23
+ }
24
+
25
+ /**
26
+ * Get session token from base64 buffer token
27
+ * Delegates to TokenRepository
28
+ *
29
+ * @param {string} token - Base64 buffer token
30
+ * @returns {Promise<Object>} Session token object with string IDs
31
+ */
32
+ async getSessionToken(token) {
33
+ const jsonToken =
34
+ this.tokenRepository.getJSONTokenFromBase64BufferToken(token);
35
+ const sessionToken = await this.tokenRepository.validateAndGetToken(
36
+ jsonToken
37
+ );
38
+ return sessionToken;
39
+ }
40
+
41
+ /**
42
+ * Find organization user by ID
43
+ * Replaces: OrganizationUser.findById(userId)
44
+ *
45
+ * @param {string} userId - User ID
46
+ * @returns {Promise<Object|null>} User object with string IDs or null
47
+ */
48
+ async findOrganizationUserById(userId) {
49
+ return await this.prisma.user.findFirst({
50
+ where: {
51
+ id: userId,
52
+ type: 'ORGANIZATION',
53
+ },
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Find individual user by ID
59
+ * Replaces: IndividualUser.findById(userId)
60
+ *
61
+ * @param {string} userId - User ID
62
+ * @returns {Promise<Object|null>} User object with string IDs or null
63
+ */
64
+ async findIndividualUserById(userId) {
65
+ return await this.prisma.user.findFirst({
66
+ where: {
67
+ id: userId,
68
+ type: 'INDIVIDUAL',
69
+ },
70
+ });
71
+ }
72
+
73
+ /**
74
+ * Find all individual users linked to an organization user
75
+ *
76
+ * @param {string} organizationUserId - Organization user ID
77
+ * @returns {Promise<Object[]>} Array of individual user records (empty if none)
78
+ */
79
+ async findIndividualUsersByOrganizationId(organizationUserId) {
80
+ return await this.prisma.user.findMany({
81
+ where: {
82
+ organizationId: organizationUserId,
83
+ type: 'INDIVIDUAL',
84
+ },
85
+ });
86
+ }
87
+
88
+ /**
89
+ * Create token with expiration
90
+ * Delegates to TokenRepository
91
+ *
92
+ * @param {string} userId - User ID
93
+ * @param {string} rawToken - Raw unhashed token
94
+ * @param {number} minutes - Minutes until expiration (default 120)
95
+ * @returns {Promise<string>} Base64 buffer token
96
+ */
97
+ async createToken(userId, rawToken, minutes = 120) {
98
+ const createdToken = await this.tokenRepository.createTokenWithExpire(
99
+ userId,
100
+ rawToken,
101
+ minutes
102
+ );
103
+ return this.tokenRepository.createBase64BufferToken(
104
+ createdToken,
105
+ rawToken
106
+ );
107
+ }
108
+
109
+ /**
110
+ * Create individual user
111
+ * Replaces: IndividualUser.create(params)
112
+ *
113
+ * @param {Object} params - User creation parameters
114
+ * @param {string} [params.hashword] - Plain text password (will be bcrypt hashed automatically)
115
+ * @returns {Promise<Object>} Created user object with string IDs
116
+ */
117
+ async createIndividualUser(params) {
118
+ const data = {
119
+ type: 'INDIVIDUAL',
120
+ email: params.email,
121
+ username: params.username,
122
+ appUserId: params.appUserId,
123
+ organizationId: params.organization || params.organizationId,
124
+ };
125
+
126
+ if (
127
+ params.hashword !== undefined &&
128
+ params.hashword !== null &&
129
+ params.hashword !== ''
130
+ ) {
131
+ if (typeof params.hashword !== 'string') {
132
+ throw new ClientSafeError('Password must be a string', 400);
133
+ }
134
+
135
+ // Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
136
+ if (params.hashword.startsWith('$2')) {
137
+ throw new Error(
138
+ 'Password appears to be already hashed. Pass plain text password only.'
139
+ );
140
+ }
141
+
142
+ data.hashword = await bcrypt.hash(params.hashword, 10);
143
+ }
144
+
145
+ return await this.prisma.user.create({ data });
146
+ }
147
+
148
+ /**
149
+ * Create organization user
150
+ * Replaces: OrganizationUser.create(params)
151
+ *
152
+ * @param {Object} params - Organization creation parameters
153
+ * @returns {Promise<Object>} Created organization object with string IDs
154
+ */
155
+ async createOrganizationUser(params) {
156
+ return await this.prisma.user.create({
157
+ data: {
158
+ type: 'ORGANIZATION',
159
+ appOrgId: params.appOrgId,
160
+ name: params.name,
161
+ },
162
+ });
163
+ }
164
+
165
+ /**
166
+ * Find individual user by username
167
+ * Replaces: IndividualUser.findOne({ username })
168
+ *
169
+ * @param {string} username - Username to search for
170
+ * @returns {Promise<Object|null>} User object with string IDs or null
171
+ */
172
+ async findIndividualUserByUsername(username) {
173
+ return await this.prisma.user.findFirst({
174
+ where: {
175
+ type: 'INDIVIDUAL',
176
+ username,
177
+ },
178
+ });
179
+ }
180
+
181
+ /**
182
+ * Find individual user by app user ID
183
+ * Replaces: IndividualUser.getUserByAppUserId(appUserId)
184
+ *
185
+ * @param {string} appUserId - App user ID to search for
186
+ * @returns {Promise<Object|null>} User object with string IDs or null
187
+ */
188
+ async findIndividualUserByAppUserId(appUserId) {
189
+ return await this.prisma.user.findFirst({
190
+ where: {
191
+ type: 'INDIVIDUAL',
192
+ appUserId,
193
+ },
194
+ });
195
+ }
196
+
197
+ /**
198
+ * Find organization user by app org ID
199
+ * Replaces: OrganizationUser.getUserByAppOrgId(appOrgId)
200
+ *
201
+ * @param {string} appOrgId - App organization ID to search for
202
+ * @returns {Promise<Object|null>} User object with string IDs or null
203
+ */
204
+ async findOrganizationUserByAppOrgId(appOrgId) {
205
+ return await this.prisma.user.findFirst({
206
+ where: {
207
+ type: 'ORGANIZATION',
208
+ appOrgId,
209
+ },
210
+ });
211
+ }
212
+
213
+ /**
214
+ * Find individual user by email
215
+ * @param {string} email - Email to search for
216
+ * @returns {Promise<Object|null>} User object with string IDs or null
217
+ */
218
+ async findIndividualUserByEmail(email) {
219
+ return await this.prisma.user.findFirst({
220
+ where: {
221
+ type: 'INDIVIDUAL',
222
+ email,
223
+ },
224
+ });
225
+ }
226
+
227
+ /**
228
+ * Update individual user
229
+ * @param {string} userId - User ID
230
+ * @param {Object} updates - Fields to update
231
+ * @param {string} [updates.hashword] - Plain text password (will be bcrypt hashed automatically)
232
+ * @returns {Promise<Object>} Updated user object with string IDs
233
+ */
234
+ async updateIndividualUser(userId, updates) {
235
+ const data = { ...updates };
236
+
237
+ if (
238
+ data.hashword !== undefined &&
239
+ data.hashword !== null &&
240
+ data.hashword !== ''
241
+ ) {
242
+ if (typeof data.hashword !== 'string') {
243
+ throw new ClientSafeError('Password must be a string', 400);
244
+ }
245
+
246
+ // Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
247
+ if (data.hashword.startsWith('$2')) {
248
+ throw new Error(
249
+ 'Password appears to be already hashed. Pass plain text password only.'
250
+ );
251
+ }
252
+
253
+ data.hashword = await bcrypt.hash(data.hashword, 10);
254
+ }
255
+
256
+ return await this.prisma.user.update({
257
+ where: { id: userId },
258
+ data,
259
+ });
260
+ }
261
+
262
+ /**
263
+ * Update organization user
264
+ * @param {string} userId - User ID
265
+ * @param {Object} updates - Fields to update
266
+ * @returns {Promise<Object>} Updated user object with string IDs
267
+ */
268
+ async updateOrganizationUser(userId, updates) {
269
+ return await this.prisma.user.update({
270
+ where: { id: userId },
271
+ data: updates,
272
+ });
273
+ }
274
+
275
+ /**
276
+ * Delete user by ID
277
+ *
278
+ * NOTE: This only deletes the user record itself.
279
+ * Prisma's onDelete: Cascade does NOT work reliably with MongoDB (no database-level referential integrity).
280
+ * Integration developers MUST manually cascade delete related records before calling this method:
281
+ * 1. Delete integrations (via deleteIntegrationById)
282
+ * 2. Delete entities (via deleteEntityById)
283
+ * 3. Delete credentials (via deleteCredentialById)
284
+ * 4. Finally delete user (via deleteUserById)
285
+ *
286
+ * @param {string} userId - User ID to delete
287
+ * @returns {Promise<boolean>} True if deleted successfully
288
+ */
289
+ async deleteUser(userId) {
290
+ try {
291
+ await this.prisma.user.delete({
292
+ where: { id: userId },
293
+ });
294
+ return true;
295
+ } catch (error) {
296
+ if (error.code === 'P2025') {
297
+ // Record not found
298
+ return false;
299
+ }
300
+ throw error;
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Link an individual user to an organization user
306
+ * @param {string} individualUserId - Individual user ID (MongoDB ObjectId string)
307
+ * @param {string} organizationUserId - Organization user ID (MongoDB ObjectId string)
308
+ * @returns {Promise<Object>} Updated individual user object
309
+ */
310
+ async linkIndividualToOrganization(individualUserId, organizationUserId) {
311
+ return await this.prisma.user.update({
312
+ where: {
313
+ id: individualUserId,
314
+ type: 'INDIVIDUAL',
315
+ },
316
+ data: {
317
+ organizationId: organizationUserId,
318
+ },
319
+ });
320
+ }
321
+ }
322
+
323
+ module.exports = { UserRepositoryMongo };
@@ -0,0 +1,377 @@
1
+ const bcrypt = require('bcryptjs');
2
+ const { prisma } = require('../../database/prisma');
3
+ const {
4
+ createTokenRepository,
5
+ } = require('../../token/repositories/token-repository-factory');
6
+ const { UserRepositoryInterface } = require('./user-repository-interface');
7
+ const { ClientSafeError } = require('../../errors');
8
+
9
+ /**
10
+ * PostgreSQL User Repository Adapter
11
+ * Handles user operations with discriminator pattern support
12
+ *
13
+ * PostgreSQL-specific characteristics:
14
+ * - Uses Int IDs with autoincrement
15
+ * - Requires ID conversion: String (app layer) ↔ Int (database)
16
+ * - All returned IDs are converted to strings for application layer consistency
17
+ */
18
+ class UserRepositoryPostgres extends UserRepositoryInterface {
19
+ constructor() {
20
+ super();
21
+ this.prisma = prisma;
22
+ this.tokenRepository = createTokenRepository();
23
+ }
24
+
25
+ /**
26
+ * Convert string ID to integer for PostgreSQL queries
27
+ * @private
28
+ * @param {string|number|null|undefined} id - ID to convert
29
+ * @returns {number|null|undefined} Integer ID or null/undefined
30
+ * @throws {Error} If ID cannot be converted to integer
31
+ */
32
+ _convertId(id) {
33
+ if (id === null || id === undefined) return id;
34
+ const parsed = parseInt(id, 10);
35
+ if (isNaN(parsed)) {
36
+ throw new Error(`Invalid ID: ${id} cannot be converted to integer`);
37
+ }
38
+ return parsed;
39
+ }
40
+
41
+ /**
42
+ * Convert user object IDs to strings
43
+ * @private
44
+ * @param {Object|null} user - User object from database
45
+ * @returns {Object|null} User with string IDs
46
+ */
47
+ _convertUserIds(user) {
48
+ if (!user) return user;
49
+ return {
50
+ ...user,
51
+ id: user.id?.toString(),
52
+ organizationId: user.organizationId?.toString(),
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Get session token from base64 buffer token
58
+ * Delegates to TokenRepository
59
+ *
60
+ * @param {string} token - Base64 buffer token
61
+ * @returns {Promise<Object>} Session token object with string IDs
62
+ */
63
+ async getSessionToken(token) {
64
+ const jsonToken =
65
+ this.tokenRepository.getJSONTokenFromBase64BufferToken(token);
66
+ const sessionToken = await this.tokenRepository.validateAndGetToken(
67
+ jsonToken
68
+ );
69
+ return sessionToken;
70
+ }
71
+
72
+ /**
73
+ * Find organization user by ID
74
+ * Replaces: OrganizationUser.findById(userId)
75
+ *
76
+ * @param {string} userId - User ID (string from application layer)
77
+ * @returns {Promise<Object|null>} User object with string IDs or null
78
+ */
79
+ async findOrganizationUserById(userId) {
80
+ const intId = this._convertId(userId);
81
+ const user = await this.prisma.user.findFirst({
82
+ where: {
83
+ id: intId,
84
+ type: 'ORGANIZATION',
85
+ },
86
+ });
87
+ return this._convertUserIds(user);
88
+ }
89
+
90
+ /**
91
+ * Find individual user by ID
92
+ * Replaces: IndividualUser.findById(userId)
93
+ *
94
+ * @param {string} userId - User ID (string from application layer)
95
+ * @returns {Promise<Object|null>} User object with string IDs or null
96
+ */
97
+ async findIndividualUserById(userId) {
98
+ const intId = this._convertId(userId);
99
+ const user = await this.prisma.user.findFirst({
100
+ where: {
101
+ id: intId,
102
+ type: 'INDIVIDUAL',
103
+ },
104
+ });
105
+ return this._convertUserIds(user);
106
+ }
107
+
108
+ /**
109
+ * Find all individual users linked to an organization user
110
+ *
111
+ * @param {string} organizationUserId - Organization user ID (string from application layer)
112
+ * @returns {Promise<Object[]>} Array of individual user objects with string IDs (empty if none)
113
+ */
114
+ async findIndividualUsersByOrganizationId(organizationUserId) {
115
+ const intId = this._convertId(organizationUserId);
116
+ const users = await this.prisma.user.findMany({
117
+ where: {
118
+ organizationId: intId,
119
+ type: 'INDIVIDUAL',
120
+ },
121
+ });
122
+ return users.map((user) => this._convertUserIds(user));
123
+ }
124
+
125
+ /**
126
+ * Create token with expiration
127
+ * Delegates to TokenRepository
128
+ *
129
+ * @param {string} userId - User ID (string from application layer)
130
+ * @param {string} rawToken - Raw unhashed token
131
+ * @param {number} minutes - Minutes until expiration (default 120)
132
+ * @returns {Promise<string>} Base64 buffer token
133
+ */
134
+ async createToken(userId, rawToken, minutes = 120) {
135
+ const createdToken = await this.tokenRepository.createTokenWithExpire(
136
+ userId,
137
+ rawToken,
138
+ minutes
139
+ );
140
+ return this.tokenRepository.createBase64BufferToken(
141
+ createdToken,
142
+ rawToken
143
+ );
144
+ }
145
+
146
+ /**
147
+ * Create individual user
148
+ * Replaces: IndividualUser.create(params)
149
+ *
150
+ * @param {Object} params - User creation parameters (with string IDs from application layer)
151
+ * @param {string} [params.hashword] - Plain text password (will be bcrypt hashed automatically)
152
+ * @returns {Promise<Object>} Created user object with string IDs
153
+ */
154
+ async createIndividualUser(params) {
155
+ const data = {
156
+ type: 'INDIVIDUAL',
157
+ email: params.email,
158
+ username: params.username,
159
+ appUserId: params.appUserId,
160
+ organizationId: this._convertId(
161
+ params.organization || params.organizationId
162
+ ),
163
+ };
164
+
165
+ if (
166
+ params.hashword !== undefined &&
167
+ params.hashword !== null &&
168
+ params.hashword !== ''
169
+ ) {
170
+ if (typeof params.hashword !== 'string') {
171
+ throw new ClientSafeError('Password must be a string', 400);
172
+ }
173
+
174
+ // Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
175
+ if (params.hashword.startsWith('$2')) {
176
+ throw new Error(
177
+ 'Password appears to be already hashed. Pass plain text password only.'
178
+ );
179
+ }
180
+
181
+ data.hashword = await bcrypt.hash(params.hashword, 10);
182
+ }
183
+
184
+ const user = await this.prisma.user.create({ data });
185
+ return this._convertUserIds(user);
186
+ }
187
+
188
+ /**
189
+ * Create organization user
190
+ * Replaces: OrganizationUser.create(params)
191
+ *
192
+ * @param {Object} params - Organization creation parameters
193
+ * @returns {Promise<Object>} Created organization object with string IDs
194
+ */
195
+ async createOrganizationUser(params) {
196
+ const user = await this.prisma.user.create({
197
+ data: {
198
+ type: 'ORGANIZATION',
199
+ appOrgId: params.appOrgId,
200
+ name: params.name,
201
+ },
202
+ });
203
+ return this._convertUserIds(user);
204
+ }
205
+
206
+ /**
207
+ * Find individual user by username
208
+ * Replaces: IndividualUser.findOne({ username })
209
+ *
210
+ * @param {string} username - Username to search for
211
+ * @returns {Promise<Object|null>} User object with string IDs or null
212
+ */
213
+ async findIndividualUserByUsername(username) {
214
+ const user = await this.prisma.user.findFirst({
215
+ where: {
216
+ type: 'INDIVIDUAL',
217
+ username,
218
+ },
219
+ });
220
+ return this._convertUserIds(user);
221
+ }
222
+
223
+ /**
224
+ * Find individual user by app user ID
225
+ * Replaces: IndividualUser.getUserByAppUserId(appUserId)
226
+ *
227
+ * @param {string} appUserId - App user ID to search for
228
+ * @returns {Promise<Object|null>} User object with string IDs or null
229
+ */
230
+ async findIndividualUserByAppUserId(appUserId) {
231
+ const user = await this.prisma.user.findFirst({
232
+ where: {
233
+ type: 'INDIVIDUAL',
234
+ appUserId,
235
+ },
236
+ });
237
+ return this._convertUserIds(user);
238
+ }
239
+
240
+ /**
241
+ * Find organization user by app org ID
242
+ * Replaces: OrganizationUser.getUserByAppOrgId(appOrgId)
243
+ *
244
+ * @param {string} appOrgId - App organization ID to search for
245
+ * @returns {Promise<Object|null>} User object with string IDs or null
246
+ */
247
+ async findOrganizationUserByAppOrgId(appOrgId) {
248
+ const user = await this.prisma.user.findFirst({
249
+ where: {
250
+ type: 'ORGANIZATION',
251
+ appOrgId,
252
+ },
253
+ });
254
+ return this._convertUserIds(user);
255
+ }
256
+
257
+ /**
258
+ * Find individual user by email
259
+ * @param {string} email - Email to search for
260
+ * @returns {Promise<Object|null>} User object with string IDs or null
261
+ */
262
+ async findIndividualUserByEmail(email) {
263
+ const user = await this.prisma.user.findFirst({
264
+ where: {
265
+ type: 'INDIVIDUAL',
266
+ email,
267
+ },
268
+ });
269
+ return this._convertUserIds(user);
270
+ }
271
+
272
+ /**
273
+ * Update individual user
274
+ * @param {string} userId - User ID (string from application layer)
275
+ * @param {Object} updates - Fields to update (with string IDs from application layer)
276
+ * @param {string} [updates.hashword] - Plain text password (will be bcrypt hashed automatically)
277
+ * @returns {Promise<Object>} Updated user object with string IDs
278
+ */
279
+ async updateIndividualUser(userId, updates) {
280
+ const intId = this._convertId(userId);
281
+
282
+ const data = { ...updates };
283
+
284
+ if (data.organizationId !== undefined) {
285
+ data.organizationId = this._convertId(data.organizationId);
286
+ }
287
+ if (data.organization !== undefined) {
288
+ data.organizationId = this._convertId(data.organization);
289
+ delete data.organization;
290
+ }
291
+
292
+ if (
293
+ data.hashword !== undefined &&
294
+ data.hashword !== null &&
295
+ data.hashword !== ''
296
+ ) {
297
+ if (typeof data.hashword !== 'string') {
298
+ throw new ClientSafeError('Password must be a string', 400);
299
+ }
300
+
301
+ // Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
302
+ if (data.hashword.startsWith('$2')) {
303
+ throw new Error(
304
+ 'Password appears to be already hashed. Pass plain text password only.'
305
+ );
306
+ }
307
+
308
+ data.hashword = await bcrypt.hash(data.hashword, 10);
309
+ }
310
+
311
+ const user = await this.prisma.user.update({
312
+ where: { id: intId },
313
+ data,
314
+ });
315
+ return this._convertUserIds(user);
316
+ }
317
+
318
+ /**
319
+ * Update organization user
320
+ * @param {string} userId - User ID (string from application layer)
321
+ * @param {Object} updates - Fields to update
322
+ * @returns {Promise<Object>} Updated user object with string IDs
323
+ */
324
+ async updateOrganizationUser(userId, updates) {
325
+ const intId = this._convertId(userId);
326
+ const user = await this.prisma.user.update({
327
+ where: { id: intId },
328
+ data: updates,
329
+ });
330
+ return this._convertUserIds(user);
331
+ }
332
+
333
+ /**
334
+ * Delete user by ID
335
+ * @param {string} userId - User ID to delete (string from application layer)
336
+ * @returns {Promise<boolean>} True if deleted successfully
337
+ */
338
+ async deleteUser(userId) {
339
+ try {
340
+ const intId = this._convertId(userId);
341
+ await this.prisma.user.delete({
342
+ where: { id: intId },
343
+ });
344
+ return true;
345
+ } catch (error) {
346
+ if (error.code === 'P2025') {
347
+ // Record not found
348
+ return false;
349
+ }
350
+ throw error;
351
+ }
352
+ }
353
+
354
+ /**
355
+ * Link an individual user to an organization user
356
+ * @param {string} individualUserId - Individual user ID (string from application layer)
357
+ * @param {string} organizationUserId - Organization user ID (string from application layer)
358
+ * @returns {Promise<Object>} Updated individual user with string IDs
359
+ */
360
+ async linkIndividualToOrganization(individualUserId, organizationUserId) {
361
+ const intIndividualId = this._convertId(individualUserId);
362
+ const intOrganizationId = this._convertId(organizationUserId);
363
+
364
+ const user = await this.prisma.user.update({
365
+ where: {
366
+ id: intIndividualId,
367
+ type: 'INDIVIDUAL',
368
+ },
369
+ data: {
370
+ organizationId: intOrganizationId,
371
+ },
372
+ });
373
+ return this._convertUserIds(user);
374
+ }
375
+ }
376
+
377
+ module.exports = { UserRepositoryPostgres };