@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,311 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const {
3
+ toObjectId,
4
+ fromObjectId,
5
+ findMany,
6
+ findOne,
7
+ insertOne,
8
+ updateOne,
9
+ deleteOne,
10
+ } = require('../../database/documentdb-utils');
11
+ const {
12
+ ProcessRepositoryInterface,
13
+ } = require('./process-repository-interface');
14
+ const {
15
+ DocumentDBEncryptionService,
16
+ } = require('../../database/documentdb-encryption-service');
17
+ const { validateOps } = require('./process-update-ops-shared');
18
+
19
+ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
20
+ constructor() {
21
+ super();
22
+ this.prisma = prisma;
23
+ this.encryptionService = new DocumentDBEncryptionService();
24
+ }
25
+
26
+ async create(processData) {
27
+ const now = new Date();
28
+ const plainDocument = {
29
+ userId: toObjectId(processData.userId),
30
+ integrationId: toObjectId(processData.integrationId),
31
+ name: processData.name,
32
+ type: processData.type,
33
+ state: processData.state || 'INITIALIZING',
34
+ context: processData.context || {},
35
+ results: processData.results || {},
36
+ childProcesses: (processData.childProcesses || [])
37
+ .map((id) => toObjectId(id))
38
+ .filter(Boolean),
39
+ parentProcessId: processData.parentProcessId
40
+ ? toObjectId(processData.parentProcessId)
41
+ : null,
42
+ createdAt: now,
43
+ updatedAt: now,
44
+ };
45
+
46
+ const encryptedDocument = await this.encryptionService.encryptFields(
47
+ 'Process',
48
+ plainDocument
49
+ );
50
+
51
+ const insertedId = await insertOne(
52
+ this.prisma,
53
+ 'Process',
54
+ encryptedDocument
55
+ );
56
+
57
+ const created = await findOne(this.prisma, 'Process', {
58
+ _id: insertedId,
59
+ });
60
+ if (!created) {
61
+ console.error(
62
+ '[ProcessRepositoryDocumentDB] Process not found after insert',
63
+ {
64
+ insertedId: fromObjectId(insertedId),
65
+ processData: {
66
+ userId: processData.userId,
67
+ integrationId: processData.integrationId,
68
+ name: processData.name,
69
+ type: processData.type,
70
+ },
71
+ }
72
+ );
73
+ throw new Error(
74
+ 'Failed to create process: Document not found after insert. ' +
75
+ 'This indicates a database consistency issue.'
76
+ );
77
+ }
78
+ const decryptedProcess = await this.encryptionService.decryptFields(
79
+ 'Process',
80
+ created
81
+ );
82
+ return this._mapProcess(decryptedProcess);
83
+ }
84
+
85
+ async findById(processId) {
86
+ const objectId = toObjectId(processId);
87
+ if (!objectId) return null;
88
+ const doc = await findOne(this.prisma, 'Process', { _id: objectId });
89
+ if (!doc) return null;
90
+
91
+ const decryptedProcess = await this.encryptionService.decryptFields(
92
+ 'Process',
93
+ doc
94
+ );
95
+ return this._mapProcess(decryptedProcess);
96
+ }
97
+
98
+ async update(processId, updates) {
99
+ const objectId = toObjectId(processId);
100
+ if (!objectId) return null;
101
+
102
+ const existing = await findOne(this.prisma, 'Process', {
103
+ _id: objectId,
104
+ });
105
+ if (!existing) return null;
106
+
107
+ const updatePayload = {};
108
+ if (updates.state !== undefined) updatePayload.state = updates.state;
109
+ if (updates.context !== undefined)
110
+ updatePayload.context = updates.context;
111
+ if (updates.results !== undefined)
112
+ updatePayload.results = updates.results;
113
+ if (updates.childProcesses !== undefined) {
114
+ updatePayload.childProcesses = (updates.childProcesses || [])
115
+ .map((id) => toObjectId(id))
116
+ .filter(Boolean);
117
+ }
118
+ if (updates.parentProcessId !== undefined) {
119
+ updatePayload.parentProcessId = updates.parentProcessId
120
+ ? toObjectId(updates.parentProcessId)
121
+ : null;
122
+ }
123
+ updatePayload.updatedAt = new Date();
124
+
125
+ const encryptedUpdate = await this.encryptionService.encryptFields(
126
+ 'Process',
127
+ updatePayload
128
+ );
129
+
130
+ await updateOne(
131
+ this.prisma,
132
+ 'Process',
133
+ { _id: objectId },
134
+ { $set: encryptedUpdate }
135
+ );
136
+
137
+ const updated = await findOne(this.prisma, 'Process', {
138
+ _id: objectId,
139
+ });
140
+ if (!updated) {
141
+ console.error(
142
+ '[ProcessRepositoryDocumentDB] Process not found after update',
143
+ {
144
+ processId: fromObjectId(objectId),
145
+ }
146
+ );
147
+ throw new Error(
148
+ 'Failed to update process: Document not found after update. ' +
149
+ 'This indicates a database consistency issue.'
150
+ );
151
+ }
152
+ const decryptedProcess = await this.encryptionService.decryptFields(
153
+ 'Process',
154
+ updated
155
+ );
156
+ return this._mapProcess(decryptedProcess);
157
+ }
158
+
159
+ /**
160
+ * Atomic process update — race-safe counterpart to `update()`.
161
+ *
162
+ * Uses DocumentDB's native $inc / $set / $push operators (Mongo-wire
163
+ * compatible) via findAndModify so increments, sets, and pushes land
164
+ * in one server-side write. Contention on the same document
165
+ * serializes at the DB level.
166
+ *
167
+ * DocumentDB compatibility notes:
168
+ * - $inc: supported since v3.6.
169
+ * - $set with dot-path: supported.
170
+ * - $push with $each + negative $slice: supported since v4.0.
171
+ * Clusters still on v3.6 must upgrade before using pushSlice.
172
+ *
173
+ * Process documents have no encrypted fields today; if that changes,
174
+ * the set-by-path payload here MUST route through
175
+ * `encryptionService.encryptFields` for any affected paths.
176
+ *
177
+ * @param {string} processId
178
+ * @param {import('./process-repository-interface').ProcessUpdateOps} ops
179
+ * @returns {Promise<Object|null>}
180
+ */
181
+ async applyProcessUpdate(processId, ops) {
182
+ const normalized = validateOps(ops);
183
+ const objectId = toObjectId(processId);
184
+
185
+ const update = {};
186
+ const $set = {};
187
+
188
+ if (Object.keys(normalized.increment).length > 0) {
189
+ update.$inc = { ...normalized.increment };
190
+ }
191
+ for (const [path, value] of Object.entries(normalized.set)) {
192
+ $set[path] = value;
193
+ }
194
+ if (normalized.newState !== null) {
195
+ $set.state = normalized.newState;
196
+ }
197
+ $set.updatedAt = new Date();
198
+ update.$set = $set;
199
+
200
+ if (Object.keys(normalized.pushSlice).length > 0) {
201
+ update.$push = {};
202
+ for (const [path, spec] of Object.entries(normalized.pushSlice)) {
203
+ update.$push[path] = {
204
+ $each: spec.values,
205
+ $slice: -spec.keepLast,
206
+ };
207
+ }
208
+ }
209
+
210
+ const result = await this.prisma.$runCommandRaw({
211
+ findAndModify: 'Process',
212
+ query: { _id: objectId },
213
+ update,
214
+ new: true,
215
+ });
216
+
217
+ const doc = result && result.value;
218
+ if (!doc) return null;
219
+ const decrypted = await this.encryptionService.decryptFields(
220
+ 'Process',
221
+ doc
222
+ );
223
+ return this._mapProcess(decrypted);
224
+ }
225
+
226
+ async findByIntegrationAndType(integrationId, type) {
227
+ const integrationObjectId = toObjectId(integrationId);
228
+ const filter = {
229
+ integrationId: integrationObjectId,
230
+ type,
231
+ };
232
+ const docs = await findMany(this.prisma, 'Process', filter, {
233
+ sort: { createdAt: -1 },
234
+ });
235
+
236
+ const decryptedDocs = await Promise.all(
237
+ docs.map((doc) =>
238
+ this.encryptionService.decryptFields('Process', doc)
239
+ )
240
+ );
241
+
242
+ return decryptedDocs.map((doc) => this._mapProcess(doc));
243
+ }
244
+
245
+ async findActiveProcesses(
246
+ integrationId,
247
+ excludeStates = ['COMPLETED', 'ERROR']
248
+ ) {
249
+ const integrationObjectId = toObjectId(integrationId);
250
+ const filter = {
251
+ integrationId: integrationObjectId,
252
+ state: { $nin: excludeStates },
253
+ };
254
+ const docs = await findMany(this.prisma, 'Process', filter, {
255
+ sort: { createdAt: -1 },
256
+ });
257
+
258
+ const decryptedDocs = await Promise.all(
259
+ docs.map((doc) =>
260
+ this.encryptionService.decryptFields('Process', doc)
261
+ )
262
+ );
263
+
264
+ return decryptedDocs.map((doc) => this._mapProcess(doc));
265
+ }
266
+
267
+ async findByName(name) {
268
+ const doc = await findOne(
269
+ this.prisma,
270
+ 'Process',
271
+ { name },
272
+ { sort: { createdAt: -1 } }
273
+ );
274
+ if (!doc) return null;
275
+
276
+ const decryptedProcess = await this.encryptionService.decryptFields(
277
+ 'Process',
278
+ doc
279
+ );
280
+ return this._mapProcess(decryptedProcess);
281
+ }
282
+
283
+ async deleteById(processId) {
284
+ const objectId = toObjectId(processId);
285
+ if (!objectId) return;
286
+ await deleteOne(this.prisma, 'Process', { _id: objectId });
287
+ }
288
+
289
+ _mapProcess(doc) {
290
+ return {
291
+ id: fromObjectId(doc?._id),
292
+ userId: fromObjectId(doc?.userId),
293
+ integrationId: fromObjectId(doc?.integrationId),
294
+ name: doc?.name ?? null,
295
+ type: doc?.type ?? null,
296
+ state: doc?.state ?? null,
297
+ context: doc?.context ?? {},
298
+ results: doc?.results ?? {},
299
+ childProcesses: (doc?.childProcesses || []).map((id) =>
300
+ fromObjectId(id)
301
+ ),
302
+ parentProcessId: doc?.parentProcessId
303
+ ? fromObjectId(doc.parentProcessId)
304
+ : null,
305
+ createdAt: doc?.createdAt ? new Date(doc.createdAt) : null,
306
+ updatedAt: doc?.updatedAt ? new Date(doc.updatedAt) : null,
307
+ };
308
+ }
309
+ }
310
+
311
+ module.exports = { ProcessRepositoryDocumentDB };
@@ -0,0 +1,53 @@
1
+ const { ProcessRepositoryMongo } = require('./process-repository-mongo');
2
+ const { ProcessRepositoryPostgres } = require('./process-repository-postgres');
3
+ const {
4
+ ProcessRepositoryDocumentDB,
5
+ } = require('./process-repository-documentdb');
6
+ const config = require('../../database/config');
7
+
8
+ /**
9
+ * Process Repository Factory
10
+ * Creates the appropriate repository adapter based on database type
11
+ *
12
+ * This implements the Factory pattern for Hexagonal Architecture:
13
+ * - Reads database type from app definition (backend/index.js)
14
+ * - Returns correct adapter (MongoDB or PostgreSQL)
15
+ * - Provides clear error for unsupported databases
16
+ *
17
+ * Usage:
18
+ * ```javascript
19
+ * const repository = createProcessRepository();
20
+ * await repository.create({ userId, integrationId, name, type, state });
21
+ * ```
22
+ *
23
+ * @returns {ProcessRepositoryInterface} Configured repository adapter
24
+ * @throws {Error} If database type is not supported
25
+ */
26
+ function createProcessRepository() {
27
+ const dbType = config.DB_TYPE;
28
+
29
+ switch (dbType) {
30
+ case 'mongodb':
31
+ return new ProcessRepositoryMongo();
32
+
33
+ case 'postgresql':
34
+ return new ProcessRepositoryPostgres();
35
+
36
+ case 'documentdb':
37
+ return new ProcessRepositoryDocumentDB();
38
+
39
+ default:
40
+ throw new Error(
41
+ `Unsupported database type: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
42
+ );
43
+ }
44
+ }
45
+
46
+ module.exports = {
47
+ createProcessRepository,
48
+ // Export adapters for direct testing
49
+ ProcessRepositoryMongo,
50
+ ProcessRepositoryPostgres,
51
+ ProcessRepositoryDocumentDB,
52
+ };
53
+
@@ -0,0 +1,136 @@
1
+ /**
2
+ * ProcessRepository Interface
3
+ *
4
+ * Defines the contract for Process data access operations.
5
+ * Implementations must provide concrete methods for all operations.
6
+ *
7
+ * This interface supports the Hexagonal Architecture pattern by:
8
+ * - Defining clear boundaries between domain logic and data access
9
+ * - Allowing multiple implementations (MongoDB, PostgreSQL, in-memory)
10
+ * - Enabling dependency injection and testability
11
+ */
12
+ class ProcessRepositoryInterface {
13
+ /**
14
+ * Create a new process record
15
+ * @param {Object} processData - Process data to create
16
+ * @param {string} processData.userId - User ID
17
+ * @param {string} processData.integrationId - Integration ID
18
+ * @param {string} processData.name - Process name
19
+ * @param {string} processData.type - Process type
20
+ * @param {string} processData.state - Initial state
21
+ * @param {Object} [processData.context] - Process context
22
+ * @param {Object} [processData.results] - Process results
23
+ * @param {string[]} [processData.childProcesses] - Child process IDs
24
+ * @param {string} [processData.parentProcessId] - Parent process ID
25
+ * @returns {Promise<Object>} Created process record
26
+ */
27
+ async create(processData) {
28
+ throw new Error('Method create() must be implemented');
29
+ }
30
+
31
+ /**
32
+ * Find a process by ID
33
+ * @param {string} processId - Process ID to find
34
+ * @returns {Promise<Object|null>} Process record or null if not found
35
+ */
36
+ async findById(processId) {
37
+ throw new Error('Method findById() must be implemented');
38
+ }
39
+
40
+ /**
41
+ * Update a process record
42
+ * @param {string} processId - Process ID to update
43
+ * @param {Object} updates - Fields to update
44
+ * @returns {Promise<Object>} Updated process record
45
+ */
46
+ async update(processId, updates) {
47
+ throw new Error('Method update() must be implemented');
48
+ }
49
+
50
+ /**
51
+ * Apply atomic mutations to a process record.
52
+ *
53
+ * Race-safe counterpart to `update()`. Where `update()` takes full
54
+ * JSON blobs and does read-modify-write at the ORM layer (clobber-
55
+ * prone under concurrent writers), `applyProcessUpdate()` describes
56
+ * the intent declaratively and each backend uses its native atomic
57
+ * primitive:
58
+ * - PostgreSQL: `jsonb_set` chain inside a single UPDATE ... RETURNING
59
+ * - MongoDB: `$inc` / `$set` / `$push` via findAndModify
60
+ * - DocumentDB: same operator set as MongoDB (with version caveats)
61
+ *
62
+ * All paths are dot-delimited and rooted in `context` or `results`
63
+ * (e.g. `context.processedRecords`,
64
+ * `results.aggregateData.totalSynced`). Paths MUST match
65
+ * `^(context|results)(\\.[a-zA-Z_][a-zA-Z0-9_]*)+$` — validated by
66
+ * each adapter before any SQL/command generation.
67
+ *
68
+ * Intended primary callers: UpdateProcessMetrics and
69
+ * UpdateProcessState. Other callers can use this directly when they
70
+ * need race-free cumulative updates.
71
+ *
72
+ * @typedef {Object} ProcessUpdateOps
73
+ * @property {Object.<string, number>} [increment] - Atomic numeric
74
+ * increments keyed by dot-path. e.g.
75
+ * `{ 'context.processedRecords': 1, 'results.aggregateData.totalSynced': 1 }`
76
+ * @property {Object.<string, *>} [set] - Atomic whole-subtree set
77
+ * keyed by dot-path. Replaces the value at the path (NOT deep
78
+ * merge). e.g. `{ 'context.fetchDone': true }`
79
+ * @property {Object.<string, {values: Array, keepLast: number}>} [pushSlice]
80
+ * Atomic array push with bounded retention (sliding window of the
81
+ * last `keepLast` items). Keys are dot-paths pointing to arrays.
82
+ * e.g. `{ 'results.aggregateData.errors': { values: [err], keepLast: 100 } }`
83
+ * @property {string} [newState] - Top-level `state` column update.
84
+ * Written alongside the JSON mutations in the same UPDATE so state
85
+ * + counters move together.
86
+ *
87
+ * @param {string} processId - Process ID to update
88
+ * @param {ProcessUpdateOps} ops - Atomic operations to apply
89
+ * @returns {Promise<Object|null>} Updated process record (post-
90
+ * mutation) or null if the process does not exist.
91
+ */
92
+ async applyProcessUpdate(processId, ops) {
93
+ throw new Error('Method applyProcessUpdate() must be implemented');
94
+ }
95
+
96
+ /**
97
+ * Find processes by integration and type
98
+ * @param {string} integrationId - Integration ID
99
+ * @param {string} type - Process type
100
+ * @returns {Promise<Array>} Array of process records
101
+ */
102
+ async findByIntegrationAndType(integrationId, type) {
103
+ throw new Error('Method findByIntegrationAndType() must be implemented');
104
+ }
105
+
106
+ /**
107
+ * Find active processes (not in excluded states)
108
+ * @param {string} integrationId - Integration ID
109
+ * @param {string[]} [excludeStates=['COMPLETED', 'ERROR']] - States to exclude
110
+ * @returns {Promise<Array>} Array of active process records
111
+ */
112
+ async findActiveProcesses(integrationId, excludeStates = ['COMPLETED', 'ERROR']) {
113
+ throw new Error('Method findActiveProcesses() must be implemented');
114
+ }
115
+
116
+ /**
117
+ * Find a process by name (most recent)
118
+ * @param {string} name - Process name
119
+ * @returns {Promise<Object|null>} Most recent process with given name, or null
120
+ */
121
+ async findByName(name) {
122
+ throw new Error('Method findByName() must be implemented');
123
+ }
124
+
125
+ /**
126
+ * Delete a process by ID
127
+ * @param {string} processId - Process ID to delete
128
+ * @returns {Promise<void>}
129
+ */
130
+ async deleteById(processId) {
131
+ throw new Error('Method deleteById() must be implemented');
132
+ }
133
+ }
134
+
135
+ module.exports = { ProcessRepositoryInterface };
136
+