@friggframework/core 2.0.0-next.10 → 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 (344) 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 +3 -3
  129. package/handlers/backend-utils.js +271 -42
  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 +4 -15
  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 +85 -10
  139. package/handlers/routers/integration-webhook-routers.js +67 -0
  140. package/handlers/routers/reporting.js +9 -0
  141. package/handlers/routers/user.js +27 -5
  142. package/handlers/routers/websocket.js +5 -3
  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 +13 -7
  148. package/index.js +91 -46
  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 +398 -180
  160. package/integrations/options.js +1 -1
  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/handlers/routers/middleware/loadUser.js +0 -15
  321. package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
  322. package/integrations/create-frigg-backend.js +0 -31
  323. package/integrations/integration-factory.js +0 -251
  324. package/integrations/integration-mapping.js +0 -43
  325. package/integrations/integration-model.js +0 -46
  326. package/integrations/integration-user.js +0 -144
  327. package/integrations/test/integration-base.test.js +0 -144
  328. package/lambda/TimeoutCatcher.test.js +0 -68
  329. package/logs/logger.test.js +0 -76
  330. package/module-plugin/auther.js +0 -393
  331. package/module-plugin/credential.js +0 -22
  332. package/module-plugin/entity-manager.js +0 -70
  333. package/module-plugin/entity.js +0 -46
  334. package/module-plugin/manager.js +0 -169
  335. package/module-plugin/module-factory.js +0 -61
  336. package/module-plugin/requester/api-key.js +0 -36
  337. package/module-plugin/requester/oauth-2.js +0 -219
  338. package/module-plugin/requester/requester.js +0 -165
  339. package/module-plugin/requester/requester.test.js +0 -28
  340. package/module-plugin/test/auther.test.js +0 -97
  341. package/syncs/model.js +0 -62
  342. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  343. /package/{module-plugin → modules}/requester/basic.js +0 -0
  344. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,452 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const {
3
+ IntegrationRepositoryInterface,
4
+ } = require('./integration-repository-interface');
5
+ const { validateConfigPatch } = require('./config-patch-shared');
6
+
7
+ /**
8
+ * PostgreSQL Integration Repository Adapter
9
+ * Handles integration persistence using Prisma with PostgreSQL
10
+ *
11
+ * PostgreSQL-specific characteristics:
12
+ * - Uses nested relations for foreign keys (user, entities)
13
+ * - Uses Int IDs with autoincrement
14
+ * - Requires ID conversion: String (app layer) ↔ Int (database)
15
+ * - All returned IDs are converted to strings for application layer consistency
16
+ * - Implicit join tables for many-to-many relationships (_EntityToIntegration)
17
+ * - Uses connect/disconnect syntax for relations
18
+ */
19
+ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
20
+ constructor() {
21
+ super();
22
+ this.prisma = prisma;
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 integration object IDs to strings
43
+ * @private
44
+ * @param {Object|null} integration - Integration object from database
45
+ * @returns {Object|null} Integration with string IDs
46
+ */
47
+ _convertIntegrationIds(integration) {
48
+ if (!integration) return integration;
49
+ return {
50
+ ...integration,
51
+ id: integration.id?.toString(),
52
+ userId: integration.userId?.toString(),
53
+ entities: integration.entities?.map(e => ({
54
+ ...e,
55
+ id: e.id?.toString(),
56
+ userId: e.userId?.toString(),
57
+ credentialId: e.credentialId?.toString()
58
+ }))
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Find all integrations for a user
64
+ *
65
+ * @param {string} userId - User ID (string from application layer)
66
+ * @returns {Promise<Array>} Array of integration objects with string IDs
67
+ */
68
+ async findIntegrationsByUserId(userId) {
69
+ const intUserId = this._convertId(userId);
70
+ const integrations = await this.prisma.integration.findMany({
71
+ where: { userId: intUserId },
72
+ include: {
73
+ entities: true,
74
+ },
75
+ });
76
+
77
+ // Map to domain objects with string IDs
78
+ return integrations.map((integration) => {
79
+ const converted = this._convertIntegrationIds(integration);
80
+ return {
81
+ id: converted.id,
82
+ entitiesIds: converted.entities.map((e) => e.id),
83
+ userId: converted.userId,
84
+ config: converted.config,
85
+ version: converted.version,
86
+ status: converted.status,
87
+ messages: converted.messages,
88
+ createdAt: converted.createdAt,
89
+ };
90
+ });
91
+ }
92
+
93
+ /**
94
+ * Find integrations, optionally filtered by config type and/or status.
95
+ *
96
+ * @param {Object} [filter={}]
97
+ * @param {string} [filter.type] - Integration type (config.type)
98
+ * @param {string} [filter.status] - Integration status
99
+ * @returns {Promise<Array>} Array of integration objects (possibly empty)
100
+ */
101
+ async findIntegrations({ type, status } = {}) {
102
+ const where = {};
103
+ if (type) {
104
+ where.config = { path: ['type'], equals: type };
105
+ }
106
+ if (status) {
107
+ where.status = status;
108
+ }
109
+
110
+ const integrations = await this.prisma.integration.findMany({
111
+ where,
112
+ include: {
113
+ entities: true,
114
+ },
115
+ });
116
+
117
+ return integrations.map((integration) => {
118
+ const converted = this._convertIntegrationIds(integration);
119
+ return {
120
+ id: converted.id,
121
+ entitiesIds: converted.entities.map((e) => e.id),
122
+ userId: converted.userId,
123
+ config: converted.config,
124
+ version: converted.version,
125
+ status: converted.status,
126
+ messages: converted.messages,
127
+ createdAt: converted.createdAt,
128
+ };
129
+ });
130
+ }
131
+
132
+ /**
133
+ * Delete integration by ID
134
+ *
135
+ * @param {string} integrationId - Integration ID (string from application layer)
136
+ * @returns {Promise<Object>} Deletion result
137
+ */
138
+ async deleteIntegrationById(integrationId) {
139
+ const intId = this._convertId(integrationId);
140
+ await this.prisma.integration.delete({
141
+ where: { id: intId },
142
+ });
143
+
144
+ // Return Mongoose-compatible result
145
+ return { acknowledged: true, deletedCount: 1 };
146
+ }
147
+
148
+ /**
149
+ * Find integration by name
150
+ *
151
+ * @param {string} name - Integration type name
152
+ * @returns {Promise<Object>} Integration object with string IDs
153
+ */
154
+ async findIntegrationByName(name) {
155
+ const integration = await this.prisma.integration.findFirst({
156
+ where: {
157
+ config: {
158
+ path: ['type'],
159
+ equals: name,
160
+ },
161
+ },
162
+ include: {
163
+ entities: true,
164
+ },
165
+ });
166
+
167
+ if (!integration) {
168
+ throw new Error(`Integration with name ${name} not found`);
169
+ }
170
+
171
+ const converted = this._convertIntegrationIds(integration);
172
+ return {
173
+ id: converted.id,
174
+ entitiesIds: converted.entities.map((e) => e.id),
175
+ userId: converted.userId,
176
+ config: converted.config,
177
+ version: converted.version,
178
+ status: converted.status,
179
+ messages: converted.messages,
180
+ };
181
+ }
182
+
183
+ /**
184
+ * Find integration by ID
185
+ *
186
+ * @param {string} id - Integration ID (string from application layer)
187
+ * @returns {Promise<Object>} Integration object with string IDs
188
+ */
189
+ async findIntegrationById(id) {
190
+ const intId = this._convertId(id);
191
+ const integration = await this.prisma.integration.findUnique({
192
+ where: { id: intId },
193
+ include: {
194
+ entities: true,
195
+ },
196
+ });
197
+
198
+ if (!integration) {
199
+ throw new Error(`Integration with id ${id} not found`);
200
+ }
201
+
202
+ const converted = this._convertIntegrationIds(integration);
203
+ return {
204
+ id: converted.id,
205
+ entitiesIds: converted.entities.map((e) => e.id),
206
+ userId: converted.userId,
207
+ config: converted.config,
208
+ version: converted.version,
209
+ status: converted.status,
210
+ messages: converted.messages,
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Update integration status
216
+ *
217
+ * @param {string} integrationId - Integration ID (string from application layer)
218
+ * @param {string} status - New status
219
+ * @returns {Promise<boolean>} Success indicator
220
+ */
221
+ async updateIntegrationStatus(integrationId, status) {
222
+ const intId = this._convertId(integrationId);
223
+ await this.prisma.integration.update({
224
+ where: { id: intId },
225
+ data: { status },
226
+ });
227
+
228
+ return true; // Mongoose compatibility
229
+ }
230
+
231
+ /**
232
+ * Update integration messages
233
+ *
234
+ * @param {string} integrationId - Integration ID (string from application layer)
235
+ * @param {string} messageType - Type of message (errors, warnings, info, logs)
236
+ * @param {string} messageTitle - Message title
237
+ * @param {string} messageBody - Message body
238
+ * @param {Date} messageTimestamp - Message timestamp
239
+ * @returns {Promise<boolean>} Success indicator
240
+ */
241
+ async updateIntegrationMessages(
242
+ integrationId,
243
+ messageType,
244
+ messageTitle,
245
+ messageBody,
246
+ messageTimestamp
247
+ ) {
248
+ const intId = this._convertId(integrationId);
249
+
250
+ // Get current integration
251
+ const integration = await this.prisma.integration.findUnique({
252
+ where: { id: intId },
253
+ });
254
+
255
+ if (!integration) {
256
+ throw new Error(`Integration ${integrationId} not found`);
257
+ }
258
+
259
+ // Parse existing messages (JSON field)
260
+ const messages = integration.messages || {};
261
+ const messageArray = Array.isArray(messages[messageType])
262
+ ? messages[messageType]
263
+ : [];
264
+
265
+ // Add new message
266
+ messageArray.push({
267
+ title: messageTitle,
268
+ message: messageBody,
269
+ timestamp: messageTimestamp,
270
+ });
271
+
272
+ // Update messages
273
+ await this.prisma.integration.update({
274
+ where: { id: intId },
275
+ data: {
276
+ [messageType]: messageArray,
277
+ },
278
+ });
279
+
280
+ return true; // Mongoose compatibility
281
+ }
282
+
283
+ /**
284
+ * Create a new integration
285
+ *
286
+ * PostgreSQL-specific: Uses nested relations with connect syntax
287
+ *
288
+ * @param {Array<string>} entities - Array of entity IDs (strings from application layer)
289
+ * @param {string} userId - User ID (string from application layer)
290
+ * @param {Object} config - Integration configuration
291
+ * @returns {Promise<Object>} Created integration object with string IDs
292
+ */
293
+ async createIntegration(entities, userId, config) {
294
+ const data = {
295
+ config,
296
+ version: '0.0.0',
297
+ };
298
+
299
+ // PostgreSQL: use nested relations with ID conversion
300
+ if (userId) {
301
+ data.user = { connect: { id: this._convertId(userId) } };
302
+ }
303
+ if (entities && entities.length > 0) {
304
+ data.entities = {
305
+ connect: entities.map((id) => ({ id: this._convertId(id) })),
306
+ };
307
+ }
308
+
309
+ const integration = await this.prisma.integration.create({
310
+ data,
311
+ include: {
312
+ entities: true,
313
+ },
314
+ });
315
+
316
+ const converted = this._convertIntegrationIds(integration);
317
+ return {
318
+ id: converted.id,
319
+ entitiesIds: converted.entities.map((e) => e.id),
320
+ userId: converted.userId,
321
+ config: converted.config,
322
+ version: converted.version,
323
+ status: converted.status,
324
+ messages: converted.messages,
325
+ };
326
+ }
327
+
328
+ /**
329
+ * Find integration by user ID (returns single integration)
330
+ *
331
+ * @param {string} userId - User ID (string from application layer)
332
+ * @returns {Promise<Object|null>} Integration object with string IDs or null
333
+ */
334
+ async findIntegrationByUserId(userId) {
335
+ const intUserId = this._convertId(userId);
336
+ const integration = await this.prisma.integration.findFirst({
337
+ where: { userId: intUserId },
338
+ include: {
339
+ entities: true,
340
+ },
341
+ });
342
+
343
+ if (!integration) {
344
+ return null;
345
+ }
346
+
347
+ const converted = this._convertIntegrationIds(integration);
348
+ return {
349
+ id: converted.id,
350
+ entitiesIds: converted.entities.map((e) => e.id),
351
+ userId: converted.userId,
352
+ config: converted.config,
353
+ version: converted.version,
354
+ status: converted.status,
355
+ messages: converted.messages,
356
+ };
357
+ }
358
+
359
+ /**
360
+ * Update integration configuration
361
+ *
362
+ * @param {string} integrationId - Integration ID (string from application layer)
363
+ * @param {Object} config - Updated configuration object
364
+ * @returns {Promise<Object>} Updated integration object with string IDs
365
+ */
366
+ async updateIntegrationConfig(integrationId, config) {
367
+ if (config === null || config === undefined) {
368
+ throw new Error('Config parameter is required');
369
+ }
370
+
371
+ const intId = this._convertId(integrationId);
372
+ const integration = await this.prisma.integration.update({
373
+ where: { id: intId },
374
+ data: { config },
375
+ include: {
376
+ entities: true,
377
+ },
378
+ });
379
+
380
+ const converted = this._convertIntegrationIds(integration);
381
+ return {
382
+ id: converted.id,
383
+ entitiesIds: converted.entities.map((e) => e.id),
384
+ userId: converted.userId,
385
+ config: converted.config,
386
+ version: converted.version,
387
+ status: converted.status,
388
+ messages: converted.messages,
389
+ };
390
+ }
391
+
392
+ /**
393
+ * Atomically merge a patch into the existing config with a single
394
+ * jsonb concatenation — the database does the merge, so there is no
395
+ * JS-side read-modify-write for concurrent callers to race on.
396
+ *
397
+ * @param {string} integrationId - Integration ID (string from application layer)
398
+ * @param {Object} patch - Keys to merge into the existing config
399
+ * @returns {Promise<Object>} Updated integration object with string IDs
400
+ */
401
+ async patchIntegrationConfig(integrationId, patch) {
402
+ validateConfigPatch(patch);
403
+
404
+ const intId = this._convertId(integrationId);
405
+ const affectedCount = await this.prisma.$executeRawUnsafe(
406
+ 'UPDATE "Integration" SET "config" = COALESCE("config", \'{}\'::jsonb) || $1::jsonb, "updatedAt" = NOW() WHERE "id" = $2',
407
+ JSON.stringify(patch),
408
+ intId
409
+ );
410
+
411
+ if (!affectedCount) {
412
+ throw new Error(`Integration with id ${integrationId} not found`);
413
+ }
414
+
415
+ return this.findIntegrationById(integrationId);
416
+ }
417
+
418
+ /**
419
+ * Find all integrations whose entity set includes the given entity ID.
420
+ *
421
+ * @param {string|number} entityId - Entity ID (string from application layer)
422
+ * @returns {Promise<Array>} Array of integration objects with string IDs (possibly empty)
423
+ */
424
+ async findIntegrationsByEntityId(entityId) {
425
+ const intEntityId = this._convertId(entityId);
426
+ const integrations = await this.prisma.integration.findMany({
427
+ where: {
428
+ entities: {
429
+ some: { id: intEntityId },
430
+ },
431
+ },
432
+ include: {
433
+ entities: true,
434
+ },
435
+ });
436
+
437
+ return integrations.map((integration) => {
438
+ const converted = this._convertIntegrationIds(integration);
439
+ return {
440
+ id: converted.id,
441
+ entitiesIds: converted.entities.map((e) => e.id),
442
+ userId: converted.userId,
443
+ config: converted.config,
444
+ version: converted.version,
445
+ status: converted.status,
446
+ messages: converted.messages,
447
+ };
448
+ });
449
+ }
450
+ }
451
+
452
+ module.exports = { IntegrationRepositoryPostgres };