@friggframework/core 2.0.0-next.5 → 2.0.0-next.50

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 (267) hide show
  1. package/CLAUDE.md +693 -0
  2. package/README.md +959 -50
  3. package/application/commands/README.md +421 -0
  4. package/application/commands/credential-commands.js +224 -0
  5. package/application/commands/entity-commands.js +315 -0
  6. package/application/commands/integration-commands.js +179 -0
  7. package/application/commands/user-commands.js +213 -0
  8. package/application/index.js +69 -0
  9. package/core/CLAUDE.md +690 -0
  10. package/core/Worker.js +8 -21
  11. package/core/create-handler.js +2 -7
  12. package/credential/repositories/credential-repository-factory.js +47 -0
  13. package/credential/repositories/credential-repository-interface.js +98 -0
  14. package/credential/repositories/credential-repository-mongo.js +307 -0
  15. package/credential/repositories/credential-repository-postgres.js +313 -0
  16. package/credential/repositories/credential-repository.js +302 -0
  17. package/credential/use-cases/get-credential-for-user.js +21 -0
  18. package/credential/use-cases/update-authentication-status.js +15 -0
  19. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  20. package/database/adapters/lambda-invoker.js +97 -0
  21. package/database/config.js +154 -0
  22. package/database/encryption/README.md +684 -0
  23. package/database/encryption/encryption-schema-registry.js +141 -0
  24. package/database/encryption/field-encryption-service.js +226 -0
  25. package/database/encryption/logger.js +79 -0
  26. package/database/encryption/prisma-encryption-extension.js +222 -0
  27. package/database/index.js +25 -12
  28. package/database/models/WebsocketConnection.js +16 -10
  29. package/database/models/readme.md +1 -0
  30. package/database/prisma.js +222 -0
  31. package/database/repositories/health-check-repository-factory.js +43 -0
  32. package/database/repositories/health-check-repository-interface.js +87 -0
  33. package/database/repositories/health-check-repository-mongodb.js +91 -0
  34. package/database/repositories/health-check-repository-postgres.js +82 -0
  35. package/database/repositories/health-check-repository.js +108 -0
  36. package/database/repositories/migration-status-repository-s3.js +137 -0
  37. package/database/use-cases/check-database-health-use-case.js +29 -0
  38. package/database/use-cases/check-database-state-use-case.js +81 -0
  39. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  40. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  41. package/database/use-cases/get-migration-status-use-case.js +93 -0
  42. package/database/use-cases/run-database-migration-use-case.js +137 -0
  43. package/database/use-cases/test-encryption-use-case.js +253 -0
  44. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  45. package/database/utils/mongodb-collection-utils.js +91 -0
  46. package/database/utils/mongodb-schema-init.js +106 -0
  47. package/database/utils/prisma-runner.js +400 -0
  48. package/database/utils/prisma-schema-parser.js +182 -0
  49. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  50. package/encrypt/Cryptor.js +34 -168
  51. package/encrypt/index.js +1 -2
  52. package/encrypt/test-encrypt.js +0 -2
  53. package/generated/prisma-mongodb/client.d.ts +1 -0
  54. package/generated/prisma-mongodb/client.js +4 -0
  55. package/generated/prisma-mongodb/default.d.ts +1 -0
  56. package/generated/prisma-mongodb/default.js +4 -0
  57. package/generated/prisma-mongodb/edge.d.ts +1 -0
  58. package/generated/prisma-mongodb/edge.js +334 -0
  59. package/generated/prisma-mongodb/index-browser.js +316 -0
  60. package/generated/prisma-mongodb/index.d.ts +22898 -0
  61. package/generated/prisma-mongodb/index.js +359 -0
  62. package/generated/prisma-mongodb/package.json +183 -0
  63. package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
  64. package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
  65. package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
  66. package/generated/prisma-mongodb/runtime/binary.js +289 -0
  67. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  68. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  69. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  70. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  71. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  72. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  73. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  74. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  75. package/generated/prisma-mongodb/schema.prisma +362 -0
  76. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  77. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  78. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  79. package/generated/prisma-mongodb/wasm.js +341 -0
  80. package/generated/prisma-postgresql/client.d.ts +1 -0
  81. package/generated/prisma-postgresql/client.js +4 -0
  82. package/generated/prisma-postgresql/default.d.ts +1 -0
  83. package/generated/prisma-postgresql/default.js +4 -0
  84. package/generated/prisma-postgresql/edge.d.ts +1 -0
  85. package/generated/prisma-postgresql/edge.js +356 -0
  86. package/generated/prisma-postgresql/index-browser.js +338 -0
  87. package/generated/prisma-postgresql/index.d.ts +25072 -0
  88. package/generated/prisma-postgresql/index.js +381 -0
  89. package/generated/prisma-postgresql/package.json +183 -0
  90. package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
  91. package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
  92. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  93. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  94. package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
  95. package/generated/prisma-postgresql/runtime/binary.js +289 -0
  96. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  97. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  98. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  99. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  100. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  101. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  102. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  103. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  104. package/generated/prisma-postgresql/schema.prisma +345 -0
  105. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  106. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  107. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  108. package/generated/prisma-postgresql/wasm.js +363 -0
  109. package/handlers/WEBHOOKS.md +653 -0
  110. package/handlers/app-definition-loader.js +38 -0
  111. package/handlers/app-handler-helpers.js +56 -0
  112. package/handlers/backend-utils.js +180 -0
  113. package/handlers/database-migration-handler.js +227 -0
  114. package/handlers/integration-event-dispatcher.js +54 -0
  115. package/handlers/routers/HEALTHCHECK.md +342 -0
  116. package/handlers/routers/auth.js +15 -0
  117. package/handlers/routers/db-migration.handler.js +29 -0
  118. package/handlers/routers/db-migration.js +256 -0
  119. package/handlers/routers/health.js +519 -0
  120. package/handlers/routers/integration-defined-routers.js +45 -0
  121. package/handlers/routers/integration-webhook-routers.js +67 -0
  122. package/handlers/routers/user.js +63 -0
  123. package/handlers/routers/websocket.js +57 -0
  124. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  125. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  126. package/handlers/workers/db-migration.js +352 -0
  127. package/handlers/workers/integration-defined-workers.js +27 -0
  128. package/index.js +77 -22
  129. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  130. package/integrations/index.js +12 -10
  131. package/integrations/integration-base.js +296 -54
  132. package/integrations/integration-router.js +381 -182
  133. package/integrations/options.js +1 -1
  134. package/integrations/repositories/integration-mapping-repository-factory.js +50 -0
  135. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  136. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  137. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  138. package/integrations/repositories/integration-mapping-repository.js +156 -0
  139. package/integrations/repositories/integration-repository-factory.js +44 -0
  140. package/integrations/repositories/integration-repository-interface.js +127 -0
  141. package/integrations/repositories/integration-repository-mongo.js +303 -0
  142. package/integrations/repositories/integration-repository-postgres.js +352 -0
  143. package/integrations/repositories/process-repository-factory.js +46 -0
  144. package/integrations/repositories/process-repository-interface.js +90 -0
  145. package/integrations/repositories/process-repository-mongo.js +190 -0
  146. package/integrations/repositories/process-repository-postgres.js +217 -0
  147. package/integrations/tests/doubles/dummy-integration-class.js +83 -0
  148. package/integrations/tests/doubles/test-integration-repository.js +99 -0
  149. package/integrations/use-cases/create-integration.js +83 -0
  150. package/integrations/use-cases/create-process.js +128 -0
  151. package/integrations/use-cases/delete-integration-for-user.js +101 -0
  152. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  153. package/integrations/use-cases/get-integration-for-user.js +78 -0
  154. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  155. package/integrations/use-cases/get-integration-instance.js +83 -0
  156. package/integrations/use-cases/get-integrations-for-user.js +87 -0
  157. package/integrations/use-cases/get-possible-integrations.js +27 -0
  158. package/integrations/use-cases/get-process.js +87 -0
  159. package/integrations/use-cases/index.js +19 -0
  160. package/integrations/use-cases/load-integration-context.js +71 -0
  161. package/integrations/use-cases/update-integration-messages.js +44 -0
  162. package/integrations/use-cases/update-integration-status.js +32 -0
  163. package/integrations/use-cases/update-integration.js +93 -0
  164. package/integrations/use-cases/update-process-metrics.js +201 -0
  165. package/integrations/use-cases/update-process-state.js +119 -0
  166. package/integrations/utils/map-integration-dto.js +36 -0
  167. package/jest-global-setup-noop.js +3 -0
  168. package/jest-global-teardown-noop.js +3 -0
  169. package/logs/logger.js +0 -4
  170. package/{module-plugin → modules}/entity.js +1 -1
  171. package/{module-plugin → modules}/index.js +0 -8
  172. package/modules/module-factory.js +56 -0
  173. package/modules/module.js +221 -0
  174. package/modules/repositories/module-repository-factory.js +33 -0
  175. package/modules/repositories/module-repository-interface.js +129 -0
  176. package/modules/repositories/module-repository-mongo.js +377 -0
  177. package/modules/repositories/module-repository-postgres.js +426 -0
  178. package/modules/repositories/module-repository.js +316 -0
  179. package/{module-plugin → modules}/requester/requester.js +1 -0
  180. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  181. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  182. package/modules/tests/doubles/test-module-factory.js +16 -0
  183. package/modules/tests/doubles/test-module-repository.js +39 -0
  184. package/modules/use-cases/get-entities-for-user.js +32 -0
  185. package/modules/use-cases/get-entity-options-by-id.js +59 -0
  186. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  187. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  188. package/modules/use-cases/get-module.js +55 -0
  189. package/modules/use-cases/process-authorization-callback.js +122 -0
  190. package/modules/use-cases/refresh-entity-options.js +59 -0
  191. package/modules/use-cases/test-module-auth.js +55 -0
  192. package/modules/utils/map-module-dto.js +18 -0
  193. package/package.json +82 -50
  194. package/prisma-mongodb/schema.prisma +362 -0
  195. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  196. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  197. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  198. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  199. package/prisma-postgresql/schema.prisma +345 -0
  200. package/queues/queuer-util.js +28 -15
  201. package/syncs/manager.js +468 -443
  202. package/syncs/repositories/sync-repository-factory.js +38 -0
  203. package/syncs/repositories/sync-repository-interface.js +109 -0
  204. package/syncs/repositories/sync-repository-mongo.js +239 -0
  205. package/syncs/repositories/sync-repository-postgres.js +319 -0
  206. package/syncs/sync.js +0 -1
  207. package/token/repositories/token-repository-factory.js +33 -0
  208. package/token/repositories/token-repository-interface.js +131 -0
  209. package/token/repositories/token-repository-mongo.js +212 -0
  210. package/token/repositories/token-repository-postgres.js +257 -0
  211. package/token/repositories/token-repository.js +219 -0
  212. package/types/core/index.d.ts +2 -2
  213. package/types/integrations/index.d.ts +2 -6
  214. package/types/module-plugin/index.d.ts +5 -59
  215. package/types/syncs/index.d.ts +0 -2
  216. package/user/repositories/user-repository-factory.js +46 -0
  217. package/user/repositories/user-repository-interface.js +198 -0
  218. package/user/repositories/user-repository-mongo.js +291 -0
  219. package/user/repositories/user-repository-postgres.js +350 -0
  220. package/user/tests/doubles/test-user-repository.js +72 -0
  221. package/user/use-cases/authenticate-user.js +127 -0
  222. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  223. package/user/use-cases/create-individual-user.js +61 -0
  224. package/user/use-cases/create-organization-user.js +47 -0
  225. package/user/use-cases/create-token-for-user-id.js +30 -0
  226. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  227. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  228. package/user/use-cases/get-user-from-x-frigg-headers.js +106 -0
  229. package/user/use-cases/login-user.js +122 -0
  230. package/user/user.js +93 -0
  231. package/utils/backend-path.js +38 -0
  232. package/utils/index.js +6 -0
  233. package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
  234. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  235. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  236. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  237. package/websocket/repositories/websocket-connection-repository.js +161 -0
  238. package/database/models/State.js +0 -9
  239. package/database/models/Token.js +0 -70
  240. package/database/mongo.js +0 -45
  241. package/encrypt/Cryptor.test.js +0 -32
  242. package/encrypt/encrypt.js +0 -132
  243. package/encrypt/encrypt.test.js +0 -1069
  244. package/errors/base-error.test.js +0 -32
  245. package/errors/fetch-error.test.js +0 -79
  246. package/errors/halt-error.test.js +0 -11
  247. package/errors/validation-errors.test.js +0 -120
  248. package/integrations/create-frigg-backend.js +0 -31
  249. package/integrations/integration-factory.js +0 -251
  250. package/integrations/integration-mapping.js +0 -43
  251. package/integrations/integration-model.js +0 -46
  252. package/integrations/integration-user.js +0 -144
  253. package/integrations/test/integration-base.test.js +0 -144
  254. package/lambda/TimeoutCatcher.test.js +0 -68
  255. package/logs/logger.test.js +0 -76
  256. package/module-plugin/auther.js +0 -393
  257. package/module-plugin/credential.js +0 -22
  258. package/module-plugin/entity-manager.js +0 -70
  259. package/module-plugin/manager.js +0 -169
  260. package/module-plugin/module-factory.js +0 -61
  261. package/module-plugin/requester/requester.test.js +0 -28
  262. package/module-plugin/test/auther.test.js +0 -97
  263. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  264. /package/{module-plugin → modules}/requester/api-key.js +0 -0
  265. /package/{module-plugin → modules}/requester/basic.js +0 -0
  266. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  267. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,38 @@
1
+ const { SyncRepositoryMongo } = require('./sync-repository-mongo');
2
+ const { SyncRepositoryPostgres } = require('./sync-repository-postgres');
3
+ const config = require('../../database/config');
4
+
5
+ /**
6
+ * Sync Repository Factory
7
+ * Creates the appropriate repository adapter based on database type
8
+ *
9
+ * Usage:
10
+ * ```javascript
11
+ * const repository = createSyncRepository();
12
+ * ```
13
+ *
14
+ * @returns {SyncRepositoryInterface} Configured repository adapter
15
+ */
16
+ function createSyncRepository() {
17
+ const dbType = config.DB_TYPE;
18
+
19
+ switch (dbType) {
20
+ case 'mongodb':
21
+ return new SyncRepositoryMongo();
22
+
23
+ case 'postgresql':
24
+ return new SyncRepositoryPostgres();
25
+
26
+ default:
27
+ throw new Error(
28
+ `Unsupported database type: ${dbType}. Supported values: 'mongodb', 'postgresql'`
29
+ );
30
+ }
31
+ }
32
+
33
+ module.exports = {
34
+ createSyncRepository,
35
+ // Export adapters for direct testing
36
+ SyncRepositoryMongo,
37
+ SyncRepositoryPostgres,
38
+ };
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Sync Repository Interface
3
+ * Abstract base class defining the contract for sync persistence adapters
4
+ *
5
+ * This follows the Port in Hexagonal Architecture:
6
+ * - Domain layer depends on this abstraction
7
+ * - Concrete adapters (MongoDB, PostgreSQL) implement this interface
8
+ * - Use cases receive repositories via dependency injection
9
+ *
10
+ * @abstract
11
+ */
12
+ class SyncRepositoryInterface {
13
+ /**
14
+ * Get a sync object by name, data identifier, and entity
15
+ *
16
+ * @param {string} name - The sync object name
17
+ * @param {Object} dataIdentifier - The data identifier object
18
+ * @param {string|number} entity - The entity ID
19
+ * @returns {Promise<Object|null>} The sync object or null
20
+ * @abstract
21
+ */
22
+ async getSyncObject(name, dataIdentifier, entity) {
23
+ throw new Error('Method getSyncObject must be implemented by subclass');
24
+ }
25
+
26
+ /**
27
+ * Create or update a sync object
28
+ *
29
+ * @param {Object} filter - Filter criteria for finding existing sync
30
+ * @param {Object} syncData - Sync data to create/update
31
+ * @returns {Promise<Object>} The created or updated sync object
32
+ * @abstract
33
+ */
34
+ async upsertSync(filter, syncData) {
35
+ throw new Error('Method upsertSync must be implemented by subclass');
36
+ }
37
+
38
+ /**
39
+ * Update a sync object by ID
40
+ *
41
+ * @param {string|number} id - The sync object ID
42
+ * @param {Object} updates - Updates to apply
43
+ * @returns {Promise<Object>} The updated sync object
44
+ * @abstract
45
+ */
46
+ async updateSync(id, updates) {
47
+ throw new Error('Method updateSync must be implemented by subclass');
48
+ }
49
+
50
+ /**
51
+ * Add a data identifier to a sync object
52
+ *
53
+ * @param {string|number} syncId - The sync object ID
54
+ * @param {Object} dataIdentifier - The data identifier to add
55
+ * @returns {Promise<Object>} The updated sync object
56
+ * @abstract
57
+ */
58
+ async addDataIdentifier(syncId, dataIdentifier) {
59
+ throw new Error('Method addDataIdentifier must be implemented by subclass');
60
+ }
61
+
62
+ /**
63
+ * Get entity object ID for entity ID from sync object
64
+ * This is a pure helper method (no database access)
65
+ *
66
+ * @param {Object} syncObj - The sync object
67
+ * @param {string|number} entityId - The entity ID
68
+ * @returns {Object} The entity object ID
69
+ * @abstract
70
+ */
71
+ getEntityObjIdForEntityIdFromObject(syncObj, entityId) {
72
+ throw new Error('Method getEntityObjIdForEntityIdFromObject must be implemented by subclass');
73
+ }
74
+
75
+ /**
76
+ * Find sync objects by filter
77
+ *
78
+ * @param {Object} filter - Filter criteria
79
+ * @returns {Promise<Array>} Array of sync objects
80
+ * @abstract
81
+ */
82
+ async findSyncs(filter) {
83
+ throw new Error('Method findSyncs must be implemented by subclass');
84
+ }
85
+
86
+ /**
87
+ * Find one sync object by filter
88
+ *
89
+ * @param {Object} filter - Filter criteria
90
+ * @returns {Promise<Object|null>} The sync object or null
91
+ * @abstract
92
+ */
93
+ async findOneSync(filter) {
94
+ throw new Error('Method findOneSync must be implemented by subclass');
95
+ }
96
+
97
+ /**
98
+ * Delete a sync object by ID
99
+ *
100
+ * @param {string|number} id - The sync object ID
101
+ * @returns {Promise<Object>} The deletion result
102
+ * @abstract
103
+ */
104
+ async deleteSync(id) {
105
+ throw new Error('Method deleteSync must be implemented by subclass');
106
+ }
107
+ }
108
+
109
+ module.exports = { SyncRepositoryInterface };
@@ -0,0 +1,239 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const { SyncRepositoryInterface } = require('./sync-repository-interface');
3
+
4
+ /**
5
+ * MongoDB Sync Repository Adapter
6
+ * Handles sync persistence using Prisma with MongoDB
7
+ *
8
+ * MongoDB-specific characteristics:
9
+ * - Uses scalar fields for entity relations (entityIds)
10
+ * - IDs are strings with @db.ObjectId
11
+ * - Arrays used for many-to-many relationships
12
+ *
13
+ * Migration from Mongoose:
14
+ * - Mongoose static methods → Repository instance methods
15
+ * - Mongoose populate() → Prisma include
16
+ * - Nested arrays → Separate DataIdentifier model
17
+ */
18
+ class SyncRepositoryMongo extends SyncRepositoryInterface {
19
+ constructor() {
20
+ super();
21
+ this.prisma = prisma;
22
+ }
23
+
24
+ /**
25
+ * Get a sync object by name, data identifier, and entity
26
+ * Replaces: Sync.getSyncObject(name, dataIdentifier, entity)
27
+ *
28
+ * @param {string} name - The sync object name
29
+ * @param {Object} dataIdentifier - The data identifier object
30
+ * @param {string} entity - The entity ID (MongoDB ObjectId)
31
+ * @returns {Promise<Object|null>} The sync object or null
32
+ */
33
+ async getSyncObject(name, dataIdentifier, entity) {
34
+ const syncList = await this.prisma.sync.findMany({
35
+ where: {
36
+ name,
37
+ dataIdentifiers: {
38
+ some: {
39
+ idData: dataIdentifier,
40
+ entityId: entity,
41
+ },
42
+ },
43
+ },
44
+ include: {
45
+ entities: true,
46
+ dataIdentifiers: {
47
+ include: {
48
+ entity: true,
49
+ },
50
+ },
51
+ },
52
+ });
53
+
54
+ if (syncList.length === 1) {
55
+ return syncList[0];
56
+ } else if (syncList.length === 0) {
57
+ return null;
58
+ } else {
59
+ throw new Error(
60
+ `There are multiple sync objects with the name ${name}, for entities [${syncList[0].entities}] [${syncList[1].entities}]`
61
+ );
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Create or update a sync object
67
+ * Replaces: Sync.upsert(filter, syncData)
68
+ *
69
+ * @param {Object} filter - Filter criteria for finding existing sync
70
+ * @param {Object} syncData - Sync data to create/update
71
+ * @returns {Promise<Object>} The created or updated sync object
72
+ */
73
+ async upsertSync(filter, syncData) {
74
+ // Find existing sync
75
+ const where = this._convertFilterToWhere(filter);
76
+ const existing = await this.prisma.sync.findFirst({ where });
77
+
78
+ if (existing) {
79
+ // Update existing
80
+ return await this.prisma.sync.update({
81
+ where: { id: existing.id },
82
+ data: syncData,
83
+ });
84
+ }
85
+
86
+ // Create new
87
+ return await this.prisma.sync.create({
88
+ data: syncData,
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Update a sync object by ID
94
+ * Replaces: Sync.update({ _id: id }, updates)
95
+ *
96
+ * @param {string} id - The sync object ID
97
+ * @param {Object} updates - Updates to apply
98
+ * @returns {Promise<Object>} The updated sync object
99
+ */
100
+ async updateSync(id, updates) {
101
+ return await this.prisma.sync.update({
102
+ where: { id },
103
+ data: updates,
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Add a data identifier to a sync object
109
+ * Replaces: Sync.addDataIdentifier(syncId, dataIdentifier)
110
+ *
111
+ * @param {string} syncId - The sync object ID
112
+ * @param {Object} dataIdentifier - The data identifier to add
113
+ * @returns {Promise<Object>} The updated sync object
114
+ */
115
+ async addDataIdentifier(syncId, dataIdentifier) {
116
+ // In Prisma, we create a new DataIdentifier record linked to the Sync
117
+ await this.prisma.dataIdentifier.create({
118
+ data: {
119
+ syncId,
120
+ entityId: dataIdentifier.entity,
121
+ idData: dataIdentifier.id,
122
+ hash: dataIdentifier.hash,
123
+ },
124
+ });
125
+
126
+ // Return updated sync object
127
+ return await this.prisma.sync.findUnique({
128
+ where: { id: syncId },
129
+ include: {
130
+ dataIdentifiers: true,
131
+ },
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Get entity object ID for entity ID from sync object
137
+ * Replaces: Sync.getEntityObjIdForEntityIdFromObject(syncObj, entityId)
138
+ *
139
+ * This is a pure helper method (no database access)
140
+ *
141
+ * @param {Object} syncObj - The sync object
142
+ * @param {string} entityId - The entity ID
143
+ * @returns {Object} The entity object ID
144
+ */
145
+ getEntityObjIdForEntityIdFromObject(syncObj, entityId) {
146
+ if (!syncObj.dataIdentifiers) {
147
+ throw new Error('Sync object must include dataIdentifiers');
148
+ }
149
+
150
+ for (let dataIdentifier of syncObj.dataIdentifiers) {
151
+ if (dataIdentifier.entityId === entityId) {
152
+ return dataIdentifier.idData;
153
+ }
154
+ }
155
+
156
+ throw new Error(
157
+ `Sync object ${syncObj.id} does not contain a data identifier for entity ${entityId}`
158
+ );
159
+ }
160
+
161
+ /**
162
+ * Find sync objects by filter
163
+ * Replaces: Sync.find(filter)
164
+ *
165
+ * @param {Object} filter - Filter criteria
166
+ * @returns {Promise<Array>} Array of sync objects
167
+ */
168
+ async findSyncs(filter) {
169
+ const where = this._convertFilterToWhere(filter);
170
+ return await this.prisma.sync.findMany({
171
+ where,
172
+ include: {
173
+ entities: true,
174
+ dataIdentifiers: {
175
+ include: {
176
+ entity: true,
177
+ },
178
+ },
179
+ },
180
+ });
181
+ }
182
+
183
+ /**
184
+ * Find one sync object by filter
185
+ * Replaces: Sync.findOne(filter)
186
+ *
187
+ * @param {Object} filter - Filter criteria
188
+ * @returns {Promise<Object|null>} The sync object or null
189
+ */
190
+ async findOneSync(filter) {
191
+ const where = this._convertFilterToWhere(filter);
192
+ return await this.prisma.sync.findFirst({
193
+ where,
194
+ include: {
195
+ entities: true,
196
+ dataIdentifiers: {
197
+ include: {
198
+ entity: true,
199
+ },
200
+ },
201
+ },
202
+ });
203
+ }
204
+
205
+ /**
206
+ * Delete a sync object by ID
207
+ * Replaces: Sync.deleteOne({ _id: id })
208
+ *
209
+ * @param {string} id - The sync object ID
210
+ * @returns {Promise<Object>} The deletion result
211
+ */
212
+ async deleteSync(id) {
213
+ // Prisma will cascade delete dataIdentifiers automatically
214
+ return await this.prisma.sync.delete({
215
+ where: { id },
216
+ });
217
+ }
218
+
219
+ /**
220
+ * Convert Mongoose-style filter to Prisma where clause
221
+ * @private
222
+ * @param {Object} filter - Mongoose filter
223
+ * @returns {Object} Prisma where clause
224
+ */
225
+ _convertFilterToWhere(filter) {
226
+ const where = {};
227
+
228
+ // Handle _id field (Mongoose uses _id, Prisma uses id)
229
+ if (filter._id) {
230
+ where.id = filter._id;
231
+ delete filter._id;
232
+ }
233
+
234
+ // Copy remaining filters
235
+ return { ...where, ...filter };
236
+ }
237
+ }
238
+
239
+ module.exports = { SyncRepositoryMongo };
@@ -0,0 +1,319 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const { SyncRepositoryInterface } = require('./sync-repository-interface');
3
+
4
+ /**
5
+ * PostgreSQL Sync Repository Adapter
6
+ * Handles sync persistence using Prisma with PostgreSQL
7
+ *
8
+ * PostgreSQL-specific characteristics:
9
+ * - Uses implicit join tables for entity relations (_EntityToSync)
10
+ * - Uses Int IDs with autoincrement
11
+ * - Requires ID conversion: String (app layer) ↔ Int (database)
12
+ * - All returned IDs are converted to strings for application layer consistency
13
+ * - Uses connect/disconnect syntax for relations
14
+ */
15
+ class SyncRepositoryPostgres extends SyncRepositoryInterface {
16
+ constructor() {
17
+ super();
18
+ this.prisma = prisma;
19
+ }
20
+
21
+ /**
22
+ * Convert string ID to integer for PostgreSQL queries
23
+ * @private
24
+ * @param {string|number|null|undefined} id - ID to convert
25
+ * @returns {number|null|undefined} Integer ID or null/undefined
26
+ * @throws {Error} If ID cannot be converted to integer
27
+ */
28
+ _convertId(id) {
29
+ if (id === null || id === undefined) return id;
30
+ const parsed = parseInt(id, 10);
31
+ if (isNaN(parsed)) {
32
+ throw new Error(`Invalid ID: ${id} cannot be converted to integer`);
33
+ }
34
+ return parsed;
35
+ }
36
+
37
+ /**
38
+ * Convert sync object IDs to strings
39
+ * @private
40
+ * @param {Object|null} sync - Sync object from database
41
+ * @returns {Object|null} Sync with string IDs
42
+ */
43
+ _convertSyncIds(sync) {
44
+ if (!sync) return sync;
45
+ return {
46
+ ...sync,
47
+ id: sync.id?.toString(),
48
+ integrationId: sync.integrationId?.toString(),
49
+ entities: sync.entities?.map(e => ({
50
+ ...e,
51
+ id: e.id?.toString(),
52
+ userId: e.userId?.toString(),
53
+ credentialId: e.credentialId?.toString()
54
+ })),
55
+ dataIdentifiers: sync.dataIdentifiers?.map(di => ({
56
+ ...di,
57
+ id: di.id?.toString(),
58
+ syncId: di.syncId?.toString(),
59
+ entityId: di.entityId?.toString(),
60
+ entity: di.entity ? {
61
+ ...di.entity,
62
+ id: di.entity.id?.toString(),
63
+ userId: di.entity.userId?.toString(),
64
+ credentialId: di.entity.credentialId?.toString()
65
+ } : di.entity
66
+ }))
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Get a sync object by name, data identifier, and entity
72
+ *
73
+ * @param {string} name - The sync object name
74
+ * @param {Object} dataIdentifier - The data identifier object
75
+ * @param {string} entity - The entity ID (string from application layer)
76
+ * @returns {Promise<Object|null>} The sync object with string IDs or null
77
+ */
78
+ async getSyncObject(name, dataIdentifier, entity) {
79
+ const intEntityId = this._convertId(entity);
80
+ const syncList = await this.prisma.sync.findMany({
81
+ where: {
82
+ name,
83
+ dataIdentifiers: {
84
+ some: {
85
+ idData: dataIdentifier,
86
+ entityId: intEntityId,
87
+ },
88
+ },
89
+ },
90
+ include: {
91
+ entities: true,
92
+ dataIdentifiers: {
93
+ include: {
94
+ entity: true,
95
+ },
96
+ },
97
+ },
98
+ });
99
+
100
+ if (syncList.length === 1) {
101
+ return this._convertSyncIds(syncList[0]);
102
+ } else if (syncList.length === 0) {
103
+ return null;
104
+ } else {
105
+ throw new Error(
106
+ `There are multiple sync objects with the name ${name}, for entities [${syncList[0].entities}] [${syncList[1].entities}]`
107
+ );
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Create or update a sync object
113
+ *
114
+ * @param {Object} filter - Filter criteria for finding existing sync
115
+ * @param {Object} syncData - Sync data to create/update (with string IDs from application layer)
116
+ * @returns {Promise<Object>} The created or updated sync object with string IDs
117
+ */
118
+ async upsertSync(filter, syncData) {
119
+ // Find existing sync
120
+ const where = this._convertFilterToWhere(filter);
121
+ const existing = await this.prisma.sync.findFirst({ where });
122
+
123
+ // Convert IDs in syncData if present
124
+ const convertedData = { ...syncData };
125
+ if (convertedData.integrationId) {
126
+ convertedData.integrationId = this._convertId(convertedData.integrationId);
127
+ }
128
+
129
+ if (existing) {
130
+ // Update existing
131
+ const updated = await this.prisma.sync.update({
132
+ where: { id: existing.id },
133
+ data: convertedData,
134
+ });
135
+ return this._convertSyncIds(updated);
136
+ }
137
+
138
+ // Create new
139
+ const created = await this.prisma.sync.create({
140
+ data: convertedData,
141
+ });
142
+ return this._convertSyncIds(created);
143
+ }
144
+
145
+ /**
146
+ * Update a sync object by ID
147
+ *
148
+ * @param {string} id - The sync object ID (string from application layer)
149
+ * @param {Object} updates - Updates to apply (with string IDs from application layer)
150
+ * @returns {Promise<Object>} The updated sync object with string IDs
151
+ */
152
+ async updateSync(id, updates) {
153
+ const intId = this._convertId(id);
154
+
155
+ // Convert IDs in updates if present
156
+ const convertedUpdates = { ...updates };
157
+ if (convertedUpdates.integrationId) {
158
+ convertedUpdates.integrationId = this._convertId(convertedUpdates.integrationId);
159
+ }
160
+
161
+ const updated = await this.prisma.sync.update({
162
+ where: { id: intId },
163
+ data: convertedUpdates,
164
+ });
165
+ return this._convertSyncIds(updated);
166
+ }
167
+
168
+ /**
169
+ * Add a data identifier to a sync object
170
+ *
171
+ * @param {string} syncId - The sync object ID (string from application layer)
172
+ * @param {Object} dataIdentifier - The data identifier to add (with string entity ID)
173
+ * @returns {Promise<Object>} The updated sync object with string IDs
174
+ */
175
+ async addDataIdentifier(syncId, dataIdentifier) {
176
+ const intSyncId = this._convertId(syncId);
177
+ const intEntityId = this._convertId(dataIdentifier.entity);
178
+
179
+ // In Prisma, we create a new DataIdentifier record linked to the Sync
180
+ await this.prisma.dataIdentifier.create({
181
+ data: {
182
+ syncId: intSyncId,
183
+ entityId: intEntityId,
184
+ idData: dataIdentifier.id,
185
+ hash: dataIdentifier.hash,
186
+ },
187
+ });
188
+
189
+ // Return updated sync object
190
+ const sync = await this.prisma.sync.findUnique({
191
+ where: { id: intSyncId },
192
+ include: {
193
+ dataIdentifiers: true,
194
+ },
195
+ });
196
+ return this._convertSyncIds(sync);
197
+ }
198
+
199
+ /**
200
+ * Get entity object ID for entity ID from sync object
201
+ *
202
+ * This is a pure helper method (no database access)
203
+ *
204
+ * @param {Object} syncObj - The sync object (with string IDs from application layer)
205
+ * @param {string} entityId - The entity ID (string from application layer)
206
+ * @returns {Object} The entity object ID
207
+ */
208
+ getEntityObjIdForEntityIdFromObject(syncObj, entityId) {
209
+ if (!syncObj.dataIdentifiers) {
210
+ throw new Error('Sync object must include dataIdentifiers');
211
+ }
212
+
213
+ for (let dataIdentifier of syncObj.dataIdentifiers) {
214
+ // Compare string IDs (both should be strings at this point)
215
+ if (dataIdentifier.entityId === entityId) {
216
+ return dataIdentifier.idData;
217
+ }
218
+ }
219
+
220
+ throw new Error(
221
+ `Sync object ${syncObj.id} does not contain a data identifier for entity ${entityId}`
222
+ );
223
+ }
224
+
225
+ /**
226
+ * Find sync objects by filter
227
+ *
228
+ * @param {Object} filter - Filter criteria (with string IDs from application layer)
229
+ * @returns {Promise<Array>} Array of sync objects with string IDs
230
+ */
231
+ async findSyncs(filter) {
232
+ const where = this._convertFilterToWhere(filter);
233
+ const syncs = await this.prisma.sync.findMany({
234
+ where,
235
+ include: {
236
+ entities: true,
237
+ dataIdentifiers: {
238
+ include: {
239
+ entity: true,
240
+ },
241
+ },
242
+ },
243
+ });
244
+ return syncs.map(sync => this._convertSyncIds(sync));
245
+ }
246
+
247
+ /**
248
+ * Find one sync object by filter
249
+ *
250
+ * @param {Object} filter - Filter criteria (with string IDs from application layer)
251
+ * @returns {Promise<Object|null>} The sync object with string IDs or null
252
+ */
253
+ async findOneSync(filter) {
254
+ const where = this._convertFilterToWhere(filter);
255
+ const sync = await this.prisma.sync.findFirst({
256
+ where,
257
+ include: {
258
+ entities: true,
259
+ dataIdentifiers: {
260
+ include: {
261
+ entity: true,
262
+ },
263
+ },
264
+ },
265
+ });
266
+ return this._convertSyncIds(sync);
267
+ }
268
+
269
+ /**
270
+ * Delete a sync object by ID
271
+ *
272
+ * @param {string} id - The sync object ID (string from application layer)
273
+ * @returns {Promise<Object>} The deletion result with string IDs
274
+ */
275
+ async deleteSync(id) {
276
+ const intId = this._convertId(id);
277
+ // Prisma will cascade delete dataIdentifiers automatically
278
+ const deleted = await this.prisma.sync.delete({
279
+ where: { id: intId },
280
+ });
281
+ return this._convertSyncIds(deleted);
282
+ }
283
+
284
+ /**
285
+ * Convert Mongoose-style filter to Prisma where clause (converting IDs to Int)
286
+ * @private
287
+ * @param {Object} filter - Mongoose filter (with string IDs from application layer)
288
+ * @returns {Object} Prisma where clause (with Int IDs for PostgreSQL)
289
+ */
290
+ _convertFilterToWhere(filter) {
291
+ const where = {};
292
+
293
+ // Handle _id field (Mongoose uses _id, Prisma uses id)
294
+ if (filter._id) {
295
+ where.id = this._convertId(filter._id);
296
+ }
297
+
298
+ // Handle id field
299
+ if (filter.id) {
300
+ where.id = this._convertId(filter.id);
301
+ }
302
+
303
+ // Handle integrationId field
304
+ if (filter.integrationId) {
305
+ where.integrationId = this._convertId(filter.integrationId);
306
+ }
307
+
308
+ // Handle integration field (Mongoose uses integration, Prisma uses integrationId)
309
+ if (filter.integration) {
310
+ where.integrationId = this._convertId(filter.integration);
311
+ }
312
+
313
+ // Copy non-ID fields
314
+ const { _id, id, integrationId, integration, ...rest } = filter;
315
+ return { ...where, ...rest };
316
+ }
317
+ }
318
+
319
+ module.exports = { SyncRepositoryPostgres };
package/syncs/sync.js CHANGED
@@ -1,5 +1,4 @@
1
1
  const md5 = require("md5");
2
- const ModuleManager = require('../module-plugin');
3
2
  const { debug } = require("packages/logs");
4
3
  const { get } = require("packages/assertions");
5
4