@friggframework/core 2.0.0-next.8 → 2.0.0-next.81

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 (305) hide show
  1. package/CLAUDE.md +702 -0
  2. package/README.md +959 -50
  3. package/application/commands/README.md +451 -0
  4. package/application/commands/credential-commands.js +245 -0
  5. package/application/commands/entity-commands.js +336 -0
  6. package/application/commands/integration-commands.js +210 -0
  7. package/application/commands/scheduler-commands.js +263 -0
  8. package/application/commands/user-commands.js +283 -0
  9. package/application/index.js +73 -0
  10. package/assertions/index.js +0 -3
  11. package/core/CLAUDE.md +690 -0
  12. package/core/Worker.js +60 -24
  13. package/core/create-handler.js +79 -8
  14. package/credential/repositories/credential-repository-documentdb.js +304 -0
  15. package/credential/repositories/credential-repository-factory.js +54 -0
  16. package/credential/repositories/credential-repository-interface.js +98 -0
  17. package/credential/repositories/credential-repository-mongo.js +269 -0
  18. package/credential/repositories/credential-repository-postgres.js +287 -0
  19. package/credential/repositories/credential-repository.js +300 -0
  20. package/credential/use-cases/get-credential-for-user.js +25 -0
  21. package/credential/use-cases/update-authentication-status.js +15 -0
  22. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  23. package/database/adapters/lambda-invoker.js +97 -0
  24. package/database/config.js +154 -0
  25. package/database/documentdb-encryption-service.js +330 -0
  26. package/database/documentdb-utils.js +136 -0
  27. package/database/encryption/README.md +839 -0
  28. package/database/encryption/documentdb-encryption-service.md +3575 -0
  29. package/database/encryption/encryption-schema-registry.js +268 -0
  30. package/database/encryption/field-encryption-service.js +226 -0
  31. package/database/encryption/logger.js +79 -0
  32. package/database/encryption/prisma-encryption-extension.js +222 -0
  33. package/database/index.js +21 -21
  34. package/database/prisma.js +182 -0
  35. package/database/repositories/health-check-repository-documentdb.js +138 -0
  36. package/database/repositories/health-check-repository-factory.js +48 -0
  37. package/database/repositories/health-check-repository-interface.js +82 -0
  38. package/database/repositories/health-check-repository-mongodb.js +89 -0
  39. package/database/repositories/health-check-repository-postgres.js +82 -0
  40. package/database/repositories/migration-status-repository-s3.js +137 -0
  41. package/database/use-cases/check-database-health-use-case.js +29 -0
  42. package/database/use-cases/check-database-state-use-case.js +81 -0
  43. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  44. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  45. package/database/use-cases/get-migration-status-use-case.js +93 -0
  46. package/database/use-cases/run-database-migration-use-case.js +139 -0
  47. package/database/use-cases/test-encryption-use-case.js +253 -0
  48. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  49. package/database/utils/mongodb-collection-utils.js +94 -0
  50. package/database/utils/mongodb-schema-init.js +108 -0
  51. package/database/utils/prisma-runner.js +477 -0
  52. package/database/utils/prisma-schema-parser.js +182 -0
  53. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  54. package/encrypt/Cryptor.js +34 -168
  55. package/encrypt/index.js +1 -2
  56. package/errors/client-safe-error.js +26 -0
  57. package/errors/fetch-error.js +15 -7
  58. package/errors/index.js +2 -0
  59. package/generated/prisma-mongodb/client.d.ts +1 -0
  60. package/generated/prisma-mongodb/client.js +4 -0
  61. package/generated/prisma-mongodb/default.d.ts +1 -0
  62. package/generated/prisma-mongodb/default.js +4 -0
  63. package/generated/prisma-mongodb/edge.d.ts +1 -0
  64. package/generated/prisma-mongodb/edge.js +335 -0
  65. package/generated/prisma-mongodb/index-browser.js +317 -0
  66. package/generated/prisma-mongodb/index.d.ts +22955 -0
  67. package/generated/prisma-mongodb/index.js +360 -0
  68. package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  69. package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  70. package/generated/prisma-mongodb/package.json +183 -0
  71. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  72. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  73. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  74. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  75. package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
  76. package/generated/prisma-mongodb/runtime/library.js +146 -0
  77. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  78. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  79. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  80. package/generated/prisma-mongodb/schema.prisma +368 -0
  81. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  82. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  83. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  84. package/generated/prisma-mongodb/wasm.js +342 -0
  85. package/generated/prisma-postgresql/client.d.ts +1 -0
  86. package/generated/prisma-postgresql/client.js +4 -0
  87. package/generated/prisma-postgresql/default.d.ts +1 -0
  88. package/generated/prisma-postgresql/default.js +4 -0
  89. package/generated/prisma-postgresql/edge.d.ts +1 -0
  90. package/generated/prisma-postgresql/edge.js +357 -0
  91. package/generated/prisma-postgresql/index-browser.js +339 -0
  92. package/generated/prisma-postgresql/index.d.ts +25135 -0
  93. package/generated/prisma-postgresql/index.js +382 -0
  94. package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  95. package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  96. package/generated/prisma-postgresql/package.json +183 -0
  97. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  98. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  99. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  100. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  101. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  102. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  103. package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
  104. package/generated/prisma-postgresql/runtime/library.js +146 -0
  105. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  106. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  107. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  108. package/generated/prisma-postgresql/schema.prisma +351 -0
  109. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  110. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  111. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  112. package/generated/prisma-postgresql/wasm.js +364 -0
  113. package/handlers/WEBHOOKS.md +653 -0
  114. package/handlers/app-definition-loader.js +38 -0
  115. package/handlers/app-handler-helpers.js +57 -0
  116. package/handlers/backend-utils.js +262 -0
  117. package/handlers/database-migration-handler.js +227 -0
  118. package/handlers/integration-event-dispatcher.js +54 -0
  119. package/handlers/routers/HEALTHCHECK.md +342 -0
  120. package/handlers/routers/auth.js +15 -0
  121. package/handlers/routers/db-migration.handler.js +29 -0
  122. package/handlers/routers/db-migration.js +326 -0
  123. package/handlers/routers/health.js +516 -0
  124. package/handlers/routers/integration-defined-routers.js +45 -0
  125. package/handlers/routers/integration-webhook-routers.js +67 -0
  126. package/handlers/routers/user.js +63 -0
  127. package/handlers/routers/websocket.js +57 -0
  128. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  129. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  130. package/handlers/workers/db-migration.js +352 -0
  131. package/handlers/workers/dlq-processor.js +63 -0
  132. package/handlers/workers/integration-defined-workers.js +23 -0
  133. package/index.js +82 -46
  134. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
  135. package/infrastructure/scheduler/index.js +33 -0
  136. package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
  137. package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
  138. package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
  139. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  140. package/integrations/index.js +12 -10
  141. package/integrations/integration-base.js +364 -55
  142. package/integrations/integration-router.js +376 -179
  143. package/integrations/options.js +1 -1
  144. package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
  145. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  146. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  147. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  148. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  149. package/integrations/repositories/integration-mapping-repository.js +156 -0
  150. package/integrations/repositories/integration-repository-documentdb.js +219 -0
  151. package/integrations/repositories/integration-repository-factory.js +51 -0
  152. package/integrations/repositories/integration-repository-interface.js +144 -0
  153. package/integrations/repositories/integration-repository-mongo.js +330 -0
  154. package/integrations/repositories/integration-repository-postgres.js +385 -0
  155. package/integrations/repositories/process-repository-documentdb.js +311 -0
  156. package/integrations/repositories/process-repository-factory.js +53 -0
  157. package/integrations/repositories/process-repository-interface.js +136 -0
  158. package/integrations/repositories/process-repository-mongo.js +262 -0
  159. package/integrations/repositories/process-repository-postgres.js +380 -0
  160. package/integrations/repositories/process-update-ops-shared.js +112 -0
  161. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  162. package/integrations/tests/doubles/dummy-integration-class.js +105 -0
  163. package/integrations/tests/doubles/test-integration-repository.js +112 -0
  164. package/integrations/use-cases/create-integration.js +83 -0
  165. package/integrations/use-cases/create-process.js +128 -0
  166. package/integrations/use-cases/delete-integration-for-user.js +101 -0
  167. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  168. package/integrations/use-cases/get-integration-for-user.js +78 -0
  169. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  170. package/integrations/use-cases/get-integration-instance.js +83 -0
  171. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  172. package/integrations/use-cases/get-possible-integrations.js +27 -0
  173. package/integrations/use-cases/get-process.js +87 -0
  174. package/integrations/use-cases/index.js +19 -0
  175. package/integrations/use-cases/load-integration-context.js +71 -0
  176. package/integrations/use-cases/update-integration-messages.js +44 -0
  177. package/integrations/use-cases/update-integration-status.js +32 -0
  178. package/integrations/use-cases/update-integration.js +92 -0
  179. package/integrations/use-cases/update-process-metrics.js +205 -0
  180. package/integrations/use-cases/update-process-state.js +158 -0
  181. package/integrations/utils/map-integration-dto.js +37 -0
  182. package/jest-global-setup-noop.js +3 -0
  183. package/jest-global-teardown-noop.js +3 -0
  184. package/logs/logger.js +0 -4
  185. package/{module-plugin → modules}/index.js +0 -10
  186. package/modules/module-factory.js +56 -0
  187. package/modules/module.js +258 -0
  188. package/modules/repositories/module-repository-documentdb.js +335 -0
  189. package/modules/repositories/module-repository-factory.js +40 -0
  190. package/modules/repositories/module-repository-interface.js +129 -0
  191. package/modules/repositories/module-repository-mongo.js +408 -0
  192. package/modules/repositories/module-repository-postgres.js +453 -0
  193. package/modules/repositories/module-repository.js +345 -0
  194. package/modules/requester/api-key.js +52 -0
  195. package/modules/requester/oauth-2.js +396 -0
  196. package/modules/requester/requester.js +275 -0
  197. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  198. package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
  199. package/modules/tests/doubles/test-module-factory.js +16 -0
  200. package/modules/tests/doubles/test-module-repository.js +39 -0
  201. package/modules/use-cases/get-entities-for-user.js +32 -0
  202. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  203. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  204. package/modules/use-cases/get-module-instance-from-type.js +34 -0
  205. package/modules/use-cases/get-module.js +74 -0
  206. package/modules/use-cases/process-authorization-callback.js +177 -0
  207. package/modules/use-cases/refresh-entity-options.js +72 -0
  208. package/modules/use-cases/test-module-auth.js +72 -0
  209. package/modules/utils/map-module-dto.js +18 -0
  210. package/package.json +82 -50
  211. package/prisma-mongodb/schema.prisma +368 -0
  212. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  213. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  214. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  215. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  216. package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
  217. package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
  218. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  219. package/prisma-postgresql/schema.prisma +351 -0
  220. package/queues/queuer-util.js +103 -21
  221. package/syncs/manager.js +468 -443
  222. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  223. package/syncs/repositories/sync-repository-factory.js +43 -0
  224. package/syncs/repositories/sync-repository-interface.js +109 -0
  225. package/syncs/repositories/sync-repository-mongo.js +239 -0
  226. package/syncs/repositories/sync-repository-postgres.js +319 -0
  227. package/syncs/sync.js +0 -1
  228. package/token/repositories/token-repository-documentdb.js +137 -0
  229. package/token/repositories/token-repository-factory.js +40 -0
  230. package/token/repositories/token-repository-interface.js +131 -0
  231. package/token/repositories/token-repository-mongo.js +219 -0
  232. package/token/repositories/token-repository-postgres.js +264 -0
  233. package/token/repositories/token-repository.js +219 -0
  234. package/types/associations/index.d.ts +0 -17
  235. package/types/core/index.d.ts +12 -4
  236. package/types/database/index.d.ts +10 -2
  237. package/types/encrypt/index.d.ts +5 -3
  238. package/types/integrations/index.d.ts +3 -8
  239. package/types/module-plugin/index.d.ts +17 -69
  240. package/types/syncs/index.d.ts +0 -17
  241. package/user/repositories/user-repository-documentdb.js +441 -0
  242. package/user/repositories/user-repository-factory.js +52 -0
  243. package/user/repositories/user-repository-interface.js +201 -0
  244. package/user/repositories/user-repository-mongo.js +308 -0
  245. package/user/repositories/user-repository-postgres.js +360 -0
  246. package/user/tests/doubles/test-user-repository.js +72 -0
  247. package/user/use-cases/authenticate-user.js +127 -0
  248. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  249. package/user/use-cases/create-individual-user.js +61 -0
  250. package/user/use-cases/create-organization-user.js +47 -0
  251. package/user/use-cases/create-token-for-user-id.js +30 -0
  252. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  253. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  254. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  255. package/user/use-cases/login-user.js +122 -0
  256. package/user/user.js +125 -0
  257. package/utils/backend-path.js +38 -0
  258. package/utils/index.js +6 -0
  259. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  260. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  261. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  262. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  263. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  264. package/websocket/repositories/websocket-connection-repository.js +161 -0
  265. package/assertions/is-equal.js +0 -17
  266. package/associations/model.js +0 -54
  267. package/database/models/IndividualUser.js +0 -76
  268. package/database/models/OrganizationUser.js +0 -29
  269. package/database/models/State.js +0 -9
  270. package/database/models/Token.js +0 -70
  271. package/database/models/UserModel.js +0 -7
  272. package/database/models/WebsocketConnection.js +0 -49
  273. package/database/mongo.js +0 -45
  274. package/database/mongoose.js +0 -5
  275. package/encrypt/Cryptor.test.js +0 -32
  276. package/encrypt/encrypt.js +0 -132
  277. package/encrypt/encrypt.test.js +0 -1069
  278. package/encrypt/test-encrypt.js +0 -107
  279. package/errors/base-error.test.js +0 -32
  280. package/errors/fetch-error.test.js +0 -79
  281. package/errors/halt-error.test.js +0 -11
  282. package/errors/validation-errors.test.js +0 -120
  283. package/integrations/create-frigg-backend.js +0 -31
  284. package/integrations/integration-factory.js +0 -251
  285. package/integrations/integration-mapping.js +0 -43
  286. package/integrations/integration-model.js +0 -46
  287. package/integrations/integration-user.js +0 -144
  288. package/integrations/test/integration-base.test.js +0 -144
  289. package/lambda/TimeoutCatcher.test.js +0 -68
  290. package/logs/logger.test.js +0 -76
  291. package/module-plugin/auther.js +0 -393
  292. package/module-plugin/credential.js +0 -22
  293. package/module-plugin/entity-manager.js +0 -70
  294. package/module-plugin/entity.js +0 -46
  295. package/module-plugin/manager.js +0 -169
  296. package/module-plugin/module-factory.js +0 -61
  297. package/module-plugin/requester/api-key.js +0 -36
  298. package/module-plugin/requester/oauth-2.js +0 -219
  299. package/module-plugin/requester/requester.js +0 -165
  300. package/module-plugin/requester/requester.test.js +0 -28
  301. package/module-plugin/test/auther.test.js +0 -97
  302. package/syncs/model.js +0 -62
  303. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  304. /package/{module-plugin → modules}/requester/basic.js +0 -0
  305. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,351 @@
1
+ // Frigg Framework - Prisma Schema (PostgreSQL)
2
+ // PostgreSQL database schema for enterprise integration platform
3
+ // Converted from MongoDB schema for relational database support
4
+
5
+ generator client {
6
+ provider = "prisma-client-js"
7
+ output = "../generated/prisma-postgresql"
8
+ binaryTargets = ["native", "rhel-openssl-3.0.x"] // native for local dev, rhel for Lambda deployment
9
+ // Library engine (default since Prisma 3.x): Rust query engine loads as a
10
+ // Node-API addon inside the same process. The binary engine forks a child
11
+ // query-engine subprocess and communicates over a local HTTP/IPC pipe with
12
+ // NO client-side timeout — a zombied child wedges the Node process until
13
+ // Lambda's 900s cap. Switching to library eliminates that entire class of
14
+ // silent hangs. See friggframework/frigg#580 for the investigation.
15
+ engineType = "library"
16
+ }
17
+
18
+ datasource db {
19
+ provider = "postgresql"
20
+ url = env("DATABASE_URL")
21
+ }
22
+
23
+ // ============================================================================
24
+ // USER MODELS
25
+ // ============================================================================
26
+
27
+ /// User model with discriminator pattern support
28
+ /// Replaces Mongoose discriminators (IndividualUser, OrganizationUser)
29
+ model User {
30
+ id Int @id @default(autoincrement())
31
+ type UserType
32
+
33
+ // Timestamps
34
+ createdAt DateTime @default(now())
35
+ updatedAt DateTime @updatedAt
36
+
37
+ // IndividualUser fields (nullable for organizations)
38
+ email String?
39
+ username String?
40
+ hashword String? // Bcrypt hashed password (handled in application layer)
41
+ appUserId String?
42
+ organizationId Int?
43
+
44
+ // Self-referential relation for organization membership
45
+ organization User? @relation("OrgMembers", fields: [organizationId], references: [id], onDelete: NoAction, onUpdate: NoAction)
46
+ members User[] @relation("OrgMembers")
47
+
48
+ // OrganizationUser fields (nullable for individuals)
49
+ appOrgId String?
50
+ name String?
51
+
52
+ // Relations
53
+ tokens Token[]
54
+ credentials Credential[]
55
+ entities Entity[]
56
+ integrations Integration[]
57
+ processes Process[]
58
+
59
+ @@unique([username, appUserId])
60
+ @@index([type])
61
+ @@index([appUserId])
62
+ }
63
+
64
+ enum UserType {
65
+ INDIVIDUAL
66
+ ORGANIZATION
67
+ }
68
+
69
+ // ============================================================================
70
+ // AUTHENTICATION MODELS
71
+ // ============================================================================
72
+
73
+ /// Authentication tokens with expiration
74
+ /// Bcrypt hashed tokens stored (handled in application layer)
75
+ model Token {
76
+ id Int @id @default(autoincrement())
77
+ token String // Bcrypt hashed
78
+ created DateTime @default(now())
79
+ expires DateTime?
80
+ userId Int
81
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
82
+
83
+ @@index([userId])
84
+ @@index([expires])
85
+ }
86
+
87
+ // ============================================================================
88
+ // CREDENTIAL & ENTITY MODELS
89
+ // ============================================================================
90
+
91
+ /// OAuth credentials and API tokens
92
+ /// All sensitive data encrypted with KMS at rest
93
+ model Credential {
94
+ id Int @id @default(autoincrement())
95
+ userId Int?
96
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
97
+ authIsValid Boolean?
98
+ externalId String?
99
+
100
+ // Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)
101
+ // Contains: access_token, refresh_token, domain, expires_in, token_type, etc.
102
+ data Json @default("{}")
103
+
104
+ createdAt DateTime @default(now())
105
+ updatedAt DateTime @updatedAt
106
+
107
+ // Relations
108
+ entities Entity[]
109
+
110
+ @@index([userId])
111
+ @@index([externalId])
112
+ }
113
+
114
+ /// External service entities (API connections)
115
+ model Entity {
116
+ id Int @id @default(autoincrement())
117
+ credentialId Int?
118
+ credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull)
119
+ userId Int?
120
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
121
+ name String?
122
+ moduleName String?
123
+ externalId String?
124
+
125
+ data Json @default("{}")
126
+
127
+ createdAt DateTime @default(now())
128
+ updatedAt DateTime @updatedAt
129
+
130
+ // Relations - many-to-many with implicit join tables
131
+ integrations Integration[]
132
+ syncs Sync[]
133
+
134
+ dataIdentifiers DataIdentifier[]
135
+ associationObjects AssociationObject[]
136
+
137
+ @@index([userId])
138
+ @@index([externalId])
139
+ @@index([moduleName])
140
+ @@index([credentialId])
141
+ }
142
+
143
+ // ============================================================================
144
+ // INTEGRATION MODELS
145
+ // ============================================================================
146
+
147
+ /// Main integration configuration and state
148
+ model Integration {
149
+ id Int @id @default(autoincrement())
150
+ userId Int?
151
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
152
+ status IntegrationStatus @default(ENABLED)
153
+
154
+ // Configuration and version
155
+ config Json? // Integration configuration object
156
+ version String?
157
+
158
+ // Entity references (many-to-many via implicit join table)
159
+ entities Entity[]
160
+
161
+ // Message arrays (stored as JSON)
162
+ errors Json @default("[]")
163
+ warnings Json @default("[]")
164
+ info Json @default("[]")
165
+ logs Json @default("[]")
166
+
167
+ createdAt DateTime @default(now())
168
+ updatedAt DateTime @updatedAt
169
+
170
+ // Relations
171
+ associations Association[]
172
+ syncs Sync[]
173
+ mappings IntegrationMapping[]
174
+ processes Process[]
175
+
176
+ @@index([userId])
177
+ @@index([status])
178
+ }
179
+
180
+ enum IntegrationStatus {
181
+ ENABLED
182
+ NEEDS_CONFIG
183
+ PROCESSING
184
+ DISABLED
185
+ ERROR
186
+ }
187
+
188
+ /// Integration-specific data mappings
189
+ /// All mapping data encrypted with KMS
190
+ model IntegrationMapping {
191
+ id Int @id @default(autoincrement())
192
+ integrationId Int
193
+ integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)
194
+ sourceId String?
195
+
196
+ // Encrypted mapping data (handled via Prisma middleware)
197
+ mapping Json?
198
+
199
+ createdAt DateTime @default(now())
200
+ updatedAt DateTime @updatedAt
201
+
202
+ @@unique([integrationId, sourceId])
203
+ @@index([integrationId])
204
+ @@index([sourceId])
205
+ }
206
+
207
+ // ============================================================================
208
+ // SYNC MODELS
209
+ // ============================================================================
210
+
211
+ /// Bidirectional data synchronization tracking
212
+ model Sync {
213
+ id Int @id @default(autoincrement())
214
+ integrationId Int?
215
+ integration Integration? @relation(fields: [integrationId], references: [id], onDelete: Cascade)
216
+
217
+ // Entity references (many-to-many via implicit join table)
218
+ entities Entity[]
219
+
220
+ hash String
221
+ name String
222
+
223
+ // Data identifiers (extracted to separate model)
224
+ dataIdentifiers DataIdentifier[]
225
+
226
+ @@index([integrationId])
227
+ @@index([hash])
228
+ @@index([name])
229
+ }
230
+
231
+ /// Data identifier for sync operations
232
+ /// Replaces nested array structure in Mongoose
233
+ model DataIdentifier {
234
+ id Int @id @default(autoincrement())
235
+ syncId Int?
236
+ sync Sync? @relation(fields: [syncId], references: [id], onDelete: Cascade)
237
+ entityId Int
238
+ entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
239
+
240
+ // Identifier data (can be any structure)
241
+ idData Json
242
+
243
+ hash String
244
+
245
+ @@index([syncId])
246
+ @@index([entityId])
247
+ @@index([hash])
248
+ }
249
+
250
+ // ============================================================================
251
+ // ASSOCIATION MODELS
252
+ // ============================================================================
253
+
254
+ /// Entity associations with cardinality tracking
255
+ model Association {
256
+ id Int @id @default(autoincrement())
257
+ integrationId Int
258
+ integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)
259
+ name String
260
+ type AssociationType
261
+ primaryObject String
262
+
263
+ // Associated objects (extracted to separate model)
264
+ objects AssociationObject[]
265
+
266
+ @@index([integrationId])
267
+ @@index([name])
268
+ }
269
+
270
+ /// Association object entry
271
+ /// Replaces nested array structure in Mongoose
272
+ model AssociationObject {
273
+ id Int @id @default(autoincrement())
274
+ associationId Int
275
+ association Association @relation(fields: [associationId], references: [id], onDelete: Cascade)
276
+ entityId Int
277
+ entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
278
+ objectType String
279
+ objId String
280
+ metadata Json? // Optional metadata
281
+
282
+ @@index([associationId])
283
+ @@index([entityId])
284
+ }
285
+
286
+ enum AssociationType {
287
+ ONE_TO_MANY
288
+ ONE_TO_ONE
289
+ MANY_TO_ONE
290
+ }
291
+
292
+ // ============================================================================
293
+ // PROCESS MODELS
294
+ // ============================================================================
295
+
296
+ /// Generic Process Model - tracks any long-running operation
297
+ /// Used for: CRM syncs, data migrations, bulk operations, etc.
298
+ model Process {
299
+ id Int @id @default(autoincrement())
300
+
301
+ // Core references
302
+ userId Int
303
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
304
+ integrationId Int
305
+ integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)
306
+
307
+ // Process identification
308
+ name String // e.g., "zoho-crm-contact-sync", "pipedrive-lead-sync"
309
+ type String // e.g., "CRM_SYNC", "DATA_MIGRATION", "BULK_OPERATION"
310
+
311
+ // State machine
312
+ state String // Current state (integration-defined states)
313
+
314
+ // Flexible storage
315
+ context Json @default("{}") // Process-specific data (pagination, metadata, etc.)
316
+ results Json @default("{}") // Process results and metrics
317
+
318
+ // Hierarchy support - self-referential relation
319
+ parentProcessId Int?
320
+ parentProcess Process? @relation("ProcessHierarchy", fields: [parentProcessId], references: [id], onDelete: SetNull)
321
+ childProcesses Process[] @relation("ProcessHierarchy")
322
+
323
+ // Timestamps
324
+ createdAt DateTime @default(now())
325
+ updatedAt DateTime @updatedAt
326
+
327
+ @@index([userId])
328
+ @@index([integrationId])
329
+ @@index([type])
330
+ @@index([state])
331
+ @@index([name])
332
+ @@index([parentProcessId])
333
+ }
334
+
335
+ // ============================================================================
336
+ // UTILITY MODELS
337
+ // ============================================================================
338
+
339
+ /// Generic state storage
340
+ model State {
341
+ id Int @id @default(autoincrement())
342
+ state Json?
343
+ }
344
+
345
+ /// AWS API Gateway WebSocket connection tracking
346
+ model WebsocketConnection {
347
+ id Int @id @default(autoincrement())
348
+ connectionId String?
349
+
350
+ @@index([connectionId])
351
+ }
@@ -1,23 +1,108 @@
1
1
  const { v4: uuid } = require('uuid');
2
- const AWS = require('aws-sdk');
2
+ const { SQSClient, SendMessageCommand, SendMessageBatchCommand } = require('@aws-sdk/client-sqs');
3
+
3
4
  const awsConfigOptions = () => {
4
5
  const config = {};
5
6
  if (process.env.IS_OFFLINE) {
6
- console.log('Running in offline mode');
7
+ config.credentials = {
8
+ accessKeyId: 'test-aws-key',
9
+ secretAccessKey: 'test-aws-secret',
10
+ };
11
+ config.region = 'us-east-1';
7
12
  }
8
13
  if (process.env.AWS_ENDPOINT) {
9
14
  config.endpoint = process.env.AWS_ENDPOINT;
10
15
  }
11
16
  return config;
12
17
  };
13
- AWS.config.update(awsConfigOptions());
14
- const sqs = new AWS.SQS();
18
+
19
+ const sqs = new SQSClient(awsConfigOptions());
20
+
21
+ // Best-effort extraction of the logical event/processId/integrationId from a
22
+ // JSON message body. Used only for log correlation — never throws.
23
+ const summarizeMessageBody = (bodyStr) => {
24
+ try {
25
+ const parsed = JSON.parse(bodyStr);
26
+ return {
27
+ event: parsed?.event,
28
+ processId: parsed?.data?.processId,
29
+ integrationId: parsed?.data?.integrationId,
30
+ };
31
+ } catch {
32
+ return {};
33
+ }
34
+ };
35
+
36
+ // Inspect SendMessageBatchResult for partial failures and log them.
37
+ // AWS SendMessageBatch can succeed at the HTTP level while individual entries
38
+ // are rejected (KMS errors, per-entry throttling, service errors). Callers that
39
+ // don't inspect result.Failed silently lose those messages. This logs the
40
+ // details — including the logical event/processId of the failed entry — so
41
+ // the loss is visible and correlatable in CloudWatch.
42
+ const inspectBatchResult = (result, queueUrl, buffer) => {
43
+ const bufferSize = buffer.length;
44
+ const failedCount = result?.Failed?.length ?? 0;
45
+ const successCount = result?.Successful?.length ?? 0;
46
+
47
+ // Index buffer by Id so we can attach event/processId to failures.
48
+ const bufferById = new Map(buffer.map((b) => [b.Id, b]));
49
+
50
+ if (failedCount > 0) {
51
+ console.error(
52
+ `[QueuerUtil] SendMessageBatch partial failure: ${failedCount}/${bufferSize} failed`,
53
+ {
54
+ queueUrl,
55
+ bufferSize,
56
+ successCount,
57
+ failedCount,
58
+ failed: result.Failed.map((f) => {
59
+ const bufEntry = bufferById.get(f.Id);
60
+ const summary = bufEntry
61
+ ? summarizeMessageBody(bufEntry.MessageBody)
62
+ : {};
63
+ return {
64
+ Id: f.Id,
65
+ Code: f.Code,
66
+ SenderFault: f.SenderFault,
67
+ Message: f.Message,
68
+ ...summary,
69
+ };
70
+ }),
71
+ }
72
+ );
73
+ } else if (successCount > 0) {
74
+ // Include a compact per-entry summary so operators can correlate
75
+ // "which send contained which logical message" during incident triage.
76
+ const entries = result.Successful.map((s) => {
77
+ const bufEntry = bufferById.get(s.Id);
78
+ const summary = bufEntry
79
+ ? summarizeMessageBody(bufEntry.MessageBody)
80
+ : {};
81
+ return { MessageId: s.MessageId, ...summary };
82
+ });
83
+ console.log(
84
+ `[QueuerUtil] SendMessageBatch ok: ${successCount}/${bufferSize} to ${queueUrl}`,
85
+ { entries }
86
+ );
87
+ }
88
+
89
+ return result;
90
+ };
15
91
 
16
92
  const QueuerUtil = {
17
- batchSend: async (entries = [], queueUrl) => {
93
+ send: async (message, queueUrl) => {
94
+ const command = new SendMessageCommand({
95
+ MessageBody: JSON.stringify(message),
96
+ QueueUrl: queueUrl,
97
+ });
98
+ const result = await sqs.send(command);
18
99
  console.log(
19
- `Enqueuing ${entries.length} entries on SQS to queue ${queueUrl}`
100
+ `[QueuerUtil] SendMessage ok: MessageId=${result?.MessageId} to ${queueUrl}`
20
101
  );
102
+ return result;
103
+ },
104
+
105
+ batchSend: async (entries = [], queueUrl) => {
21
106
  const buffer = [];
22
107
  const batchSize = 10;
23
108
 
@@ -28,28 +113,25 @@ const QueuerUtil = {
28
113
  });
29
114
  // Sends 10, then purges the buffer
30
115
  if (buffer.length === batchSize) {
31
- console.log('Buffer at 10, sending batch');
32
- await sqs
33
- .sendMessageBatch({
34
- Entries: buffer,
35
- QueueUrl: queueUrl,
36
- })
37
- .promise();
116
+ const command = new SendMessageBatchCommand({
117
+ Entries: buffer,
118
+ QueueUrl: queueUrl,
119
+ });
120
+ const result = await sqs.send(command);
121
+ inspectBatchResult(result, queueUrl, buffer);
38
122
  // Purge the buffer
39
123
  buffer.splice(0, buffer.length);
40
124
  }
41
125
  }
42
- console.log('Buffer at end, sending final batch');
43
126
 
44
127
  // If any remaining entries under 10 are left in the buffer, send and return
45
128
  if (buffer.length > 0) {
46
- console.log(buffer);
47
- return sqs
48
- .sendMessageBatch({
49
- Entries: buffer,
50
- QueueUrl: queueUrl,
51
- })
52
- .promise();
129
+ const command = new SendMessageBatchCommand({
130
+ Entries: buffer,
131
+ QueueUrl: queueUrl,
132
+ });
133
+ const result = await sqs.send(command);
134
+ return inspectBatchResult(result, queueUrl, buffer);
53
135
  }
54
136
 
55
137
  // If we're exact... just return an empty object for now