@friggframework/core 2.0.0-next.1 → 2.0.0-next.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/CLAUDE.md +702 -0
  2. package/README.md +959 -50
  3. package/admin-scripts/index.js +52 -0
  4. package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
  5. package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
  6. package/admin-scripts/repositories/admin-script-execution-repository-interface.js +166 -0
  7. package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +228 -0
  8. package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +268 -0
  9. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  10. package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
  11. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  12. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  13. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  14. package/application/commands/README.md +451 -0
  15. package/application/commands/admin-script-commands.js +358 -0
  16. package/application/commands/credential-commands.js +245 -0
  17. package/application/commands/entity-commands.js +336 -0
  18. package/application/commands/integration-commands.js +343 -0
  19. package/application/commands/process-commands.js +135 -0
  20. package/application/commands/scheduler-commands.js +263 -0
  21. package/application/commands/user-commands.js +312 -0
  22. package/application/index.js +82 -0
  23. package/assertions/index.js +0 -3
  24. package/core/CLAUDE.md +690 -0
  25. package/core/Worker.js +60 -24
  26. package/core/create-handler.js +84 -6
  27. package/credential/repositories/credential-repository-documentdb.js +304 -0
  28. package/credential/repositories/credential-repository-factory.js +54 -0
  29. package/credential/repositories/credential-repository-interface.js +98 -0
  30. package/credential/repositories/credential-repository-mongo.js +269 -0
  31. package/credential/repositories/credential-repository-postgres.js +287 -0
  32. package/credential/repositories/credential-repository.js +300 -0
  33. package/credential/use-cases/get-credential-for-user.js +25 -0
  34. package/credential/use-cases/update-authentication-status.js +15 -0
  35. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  36. package/database/adapters/lambda-invoker.js +97 -0
  37. package/database/config.js +154 -0
  38. package/database/documentdb-encryption-service.js +330 -0
  39. package/database/documentdb-utils.js +136 -0
  40. package/database/encryption/README.md +839 -0
  41. package/database/encryption/documentdb-encryption-service.md +3575 -0
  42. package/database/encryption/encryption-schema-registry.js +401 -0
  43. package/database/encryption/field-encryption-service.js +254 -0
  44. package/database/encryption/logger.js +79 -0
  45. package/database/encryption/prisma-encryption-extension.js +230 -0
  46. package/database/index.js +21 -21
  47. package/database/prisma.js +182 -0
  48. package/database/repositories/health-check-repository-documentdb.js +138 -0
  49. package/database/repositories/health-check-repository-factory.js +48 -0
  50. package/database/repositories/health-check-repository-interface.js +82 -0
  51. package/database/repositories/health-check-repository-mongodb.js +89 -0
  52. package/database/repositories/health-check-repository-postgres.js +82 -0
  53. package/database/repositories/migration-status-repository-s3.js +137 -0
  54. package/database/use-cases/check-database-health-use-case.js +29 -0
  55. package/database/use-cases/check-database-state-use-case.js +81 -0
  56. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  57. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  58. package/database/use-cases/get-migration-status-use-case.js +93 -0
  59. package/database/use-cases/run-database-migration-use-case.js +139 -0
  60. package/database/use-cases/test-encryption-use-case.js +253 -0
  61. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  62. package/database/utils/mongodb-collection-utils.js +94 -0
  63. package/database/utils/mongodb-schema-init.js +108 -0
  64. package/database/utils/prisma-runner.js +477 -0
  65. package/database/utils/prisma-schema-parser.js +182 -0
  66. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  67. package/encrypt/Cryptor.js +34 -168
  68. package/encrypt/index.js +1 -2
  69. package/errors/client-safe-error.js +26 -0
  70. package/errors/fetch-error.js +15 -7
  71. package/errors/index.js +2 -0
  72. package/generated/prisma-mongodb/client.d.ts +1 -0
  73. package/generated/prisma-mongodb/client.js +5 -0
  74. package/generated/prisma-mongodb/default.d.ts +1 -0
  75. package/generated/prisma-mongodb/default.js +5 -0
  76. package/generated/prisma-mongodb/edge.d.ts +1 -0
  77. package/generated/prisma-mongodb/edge.js +374 -0
  78. package/generated/prisma-mongodb/index-browser.js +356 -0
  79. package/generated/prisma-mongodb/index.d.ts +25667 -0
  80. package/generated/prisma-mongodb/index.js +399 -0
  81. package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  82. package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  83. package/generated/prisma-mongodb/package.json +183 -0
  84. package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
  85. package/generated/prisma-mongodb/runtime/edge.js +35 -0
  86. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  87. package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
  88. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  89. package/generated/prisma-mongodb/runtime/library.js +147 -0
  90. package/generated/prisma-mongodb/runtime/react-native.js +84 -0
  91. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
  92. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
  93. package/generated/prisma-mongodb/schema.prisma +432 -0
  94. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
  95. package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
  96. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  97. package/generated/prisma-mongodb/wasm.js +381 -0
  98. package/generated/prisma-postgresql/client.d.ts +1 -0
  99. package/generated/prisma-postgresql/client.js +5 -0
  100. package/generated/prisma-postgresql/default.d.ts +1 -0
  101. package/generated/prisma-postgresql/default.js +5 -0
  102. package/generated/prisma-postgresql/edge.d.ts +1 -0
  103. package/generated/prisma-postgresql/edge.js +395 -0
  104. package/generated/prisma-postgresql/index-browser.js +377 -0
  105. package/generated/prisma-postgresql/index.d.ts +28455 -0
  106. package/generated/prisma-postgresql/index.js +420 -0
  107. package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  108. package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  109. package/generated/prisma-postgresql/package.json +183 -0
  110. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  111. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  112. package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
  113. package/generated/prisma-postgresql/runtime/edge.js +35 -0
  114. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  115. package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
  116. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  117. package/generated/prisma-postgresql/runtime/library.js +147 -0
  118. package/generated/prisma-postgresql/runtime/react-native.js +84 -0
  119. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
  120. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
  121. package/generated/prisma-postgresql/schema.prisma +414 -0
  122. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
  123. package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
  124. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  125. package/generated/prisma-postgresql/wasm.js +402 -0
  126. package/handlers/WEBHOOKS.md +653 -0
  127. package/handlers/app-definition-loader.js +43 -0
  128. package/handlers/app-handler-helpers.js +57 -0
  129. package/handlers/backend-utils.js +316 -0
  130. package/handlers/database-migration-handler.js +227 -0
  131. package/handlers/integration-event-dispatcher.js +54 -0
  132. package/handlers/middleware/admin-auth.js +73 -0
  133. package/handlers/routers/HEALTHCHECK.md +342 -0
  134. package/handlers/routers/auth.js +15 -0
  135. package/handlers/routers/db-migration.handler.js +29 -0
  136. package/handlers/routers/db-migration.js +311 -0
  137. package/handlers/routers/health.js +518 -0
  138. package/handlers/routers/integration-defined-routers.js +117 -0
  139. package/handlers/routers/integration-webhook-routers.js +67 -0
  140. package/handlers/routers/reporting.js +9 -0
  141. package/handlers/routers/user.js +63 -0
  142. package/handlers/routers/websocket.js +57 -0
  143. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  144. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  145. package/handlers/workers/db-migration.js +352 -0
  146. package/handlers/workers/dlq-processor.js +63 -0
  147. package/handlers/workers/integration-defined-workers.js +30 -0
  148. package/index.js +111 -50
  149. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
  150. package/infrastructure/scheduler/index.js +33 -0
  151. package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
  152. package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
  153. package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
  154. package/integrations/EXTENSIONS.md +240 -0
  155. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  156. package/integrations/extension.js +254 -0
  157. package/integrations/index.js +20 -10
  158. package/integrations/integration-base.js +619 -72
  159. package/integrations/integration-router.js +460 -218
  160. package/integrations/options.js +3 -4
  161. package/integrations/repositories/config-patch-shared.js +43 -0
  162. package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
  163. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  164. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  165. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  166. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  167. package/integrations/repositories/integration-mapping-repository.js +156 -0
  168. package/integrations/repositories/integration-repository-documentdb.js +284 -0
  169. package/integrations/repositories/integration-repository-factory.js +51 -0
  170. package/integrations/repositories/integration-repository-interface.js +173 -0
  171. package/integrations/repositories/integration-repository-mongo.js +414 -0
  172. package/integrations/repositories/integration-repository-postgres.js +452 -0
  173. package/integrations/repositories/process-repository-documentdb.js +311 -0
  174. package/integrations/repositories/process-repository-factory.js +53 -0
  175. package/integrations/repositories/process-repository-interface.js +136 -0
  176. package/integrations/repositories/process-repository-mongo.js +262 -0
  177. package/integrations/repositories/process-repository-postgres.js +380 -0
  178. package/integrations/repositories/process-update-ops-shared.js +112 -0
  179. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  180. package/integrations/tests/doubles/dummy-integration-class.js +113 -0
  181. package/integrations/tests/doubles/test-integration-repository.js +146 -0
  182. package/integrations/use-cases/create-integration.js +215 -0
  183. package/integrations/use-cases/create-process.js +130 -0
  184. package/integrations/use-cases/delete-integration-for-user.js +119 -0
  185. package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
  186. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
  187. package/integrations/use-cases/get-integration-for-user.js +78 -0
  188. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  189. package/integrations/use-cases/get-integration-instance.js +83 -0
  190. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  191. package/integrations/use-cases/get-possible-integrations.js +27 -0
  192. package/integrations/use-cases/get-process.js +89 -0
  193. package/integrations/use-cases/index.js +19 -0
  194. package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
  195. package/integrations/use-cases/load-integration-context.js +71 -0
  196. package/integrations/use-cases/patch-integration-config.js +39 -0
  197. package/integrations/use-cases/process-errors.js +28 -0
  198. package/integrations/use-cases/update-integration-config.js +32 -0
  199. package/integrations/use-cases/update-integration-messages.js +44 -0
  200. package/integrations/use-cases/update-integration-status.js +32 -0
  201. package/integrations/use-cases/update-integration.js +92 -0
  202. package/integrations/use-cases/update-process-metrics.js +216 -0
  203. package/integrations/use-cases/update-process-state.js +163 -0
  204. package/integrations/utils/map-integration-dto.js +37 -0
  205. package/jest-global-setup-noop.js +3 -0
  206. package/jest-global-teardown-noop.js +3 -0
  207. package/logs/logger.js +0 -4
  208. package/{module-plugin → modules}/index.js +0 -10
  209. package/modules/module-factory.js +56 -0
  210. package/modules/module.js +274 -0
  211. package/modules/repositories/module-repository-documentdb.js +350 -0
  212. package/modules/repositories/module-repository-factory.js +40 -0
  213. package/modules/repositories/module-repository-interface.js +145 -0
  214. package/modules/repositories/module-repository-mongo.js +436 -0
  215. package/modules/repositories/module-repository-postgres.js +481 -0
  216. package/modules/repositories/module-repository.js +369 -0
  217. package/modules/requester/api-key.js +52 -0
  218. package/modules/requester/oauth-2.js +396 -0
  219. package/modules/requester/requester.js +285 -0
  220. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  221. package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
  222. package/modules/tests/doubles/test-module-factory.js +16 -0
  223. package/modules/tests/doubles/test-module-repository.js +39 -0
  224. package/modules/use-cases/get-entities-for-user.js +32 -0
  225. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  226. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  227. package/modules/use-cases/get-module-instance-from-type.js +34 -0
  228. package/modules/use-cases/get-module.js +74 -0
  229. package/modules/use-cases/process-authorization-callback.js +243 -0
  230. package/modules/use-cases/refresh-entity-options.js +72 -0
  231. package/modules/use-cases/test-module-auth.js +72 -0
  232. package/modules/utils/map-module-dto.js +18 -0
  233. package/package.json +82 -50
  234. package/prisma-mongodb/schema.prisma +432 -0
  235. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  236. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  237. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  238. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  239. package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
  240. package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
  241. package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
  242. package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
  243. package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
  244. package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
  245. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  246. package/prisma-postgresql/schema.prisma +414 -0
  247. package/queues/queuer-util.js +103 -21
  248. package/reporting/README.md +86 -0
  249. package/reporting/index.js +17 -0
  250. package/reporting/reporting-router.js +77 -0
  251. package/reporting/repositories/reporting-repository-documentdb.js +127 -0
  252. package/reporting/repositories/reporting-repository-factory.js +35 -0
  253. package/reporting/repositories/reporting-repository-interface.js +16 -0
  254. package/reporting/repositories/reporting-repository-mongo.js +54 -0
  255. package/reporting/repositories/reporting-repository-postgres.js +70 -0
  256. package/reporting/use-cases/index.js +6 -0
  257. package/reporting/use-cases/list-integrations-report.js +142 -0
  258. package/syncs/manager.js +468 -443
  259. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  260. package/syncs/repositories/sync-repository-factory.js +43 -0
  261. package/syncs/repositories/sync-repository-interface.js +109 -0
  262. package/syncs/repositories/sync-repository-mongo.js +239 -0
  263. package/syncs/repositories/sync-repository-postgres.js +319 -0
  264. package/syncs/sync.js +0 -1
  265. package/token/repositories/token-repository-documentdb.js +137 -0
  266. package/token/repositories/token-repository-factory.js +40 -0
  267. package/token/repositories/token-repository-interface.js +131 -0
  268. package/token/repositories/token-repository-mongo.js +219 -0
  269. package/token/repositories/token-repository-postgres.js +264 -0
  270. package/token/repositories/token-repository.js +219 -0
  271. package/types/associations/index.d.ts +0 -17
  272. package/types/core/index.d.ts +12 -4
  273. package/types/database/index.d.ts +10 -2
  274. package/types/encrypt/index.d.ts +5 -3
  275. package/types/integrations/index.d.ts +3 -8
  276. package/types/module-plugin/index.d.ts +17 -69
  277. package/types/syncs/index.d.ts +0 -17
  278. package/user/repositories/user-repository-documentdb.js +458 -0
  279. package/user/repositories/user-repository-factory.js +52 -0
  280. package/user/repositories/user-repository-interface.js +214 -0
  281. package/user/repositories/user-repository-mongo.js +323 -0
  282. package/user/repositories/user-repository-postgres.js +377 -0
  283. package/user/tests/doubles/test-user-repository.js +72 -0
  284. package/user/use-cases/authenticate-user.js +127 -0
  285. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  286. package/user/use-cases/create-individual-user.js +61 -0
  287. package/user/use-cases/create-organization-user.js +47 -0
  288. package/user/use-cases/create-token-for-user-id.js +30 -0
  289. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  290. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  291. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  292. package/user/use-cases/login-user.js +122 -0
  293. package/user/user.js +125 -0
  294. package/utils/backend-path.js +38 -0
  295. package/utils/index.js +6 -0
  296. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  297. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  298. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  299. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  300. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  301. package/websocket/repositories/websocket-connection-repository.js +161 -0
  302. package/assertions/is-equal.js +0 -17
  303. package/associations/model.js +0 -54
  304. package/database/models/IndividualUser.js +0 -76
  305. package/database/models/OrganizationUser.js +0 -29
  306. package/database/models/State.js +0 -9
  307. package/database/models/Token.js +0 -70
  308. package/database/models/UserModel.js +0 -7
  309. package/database/models/WebsocketConnection.js +0 -49
  310. package/database/mongo.js +0 -45
  311. package/database/mongoose.js +0 -5
  312. package/encrypt/Cryptor.test.js +0 -32
  313. package/encrypt/encrypt.js +0 -132
  314. package/encrypt/encrypt.test.js +0 -1069
  315. package/encrypt/test-encrypt.js +0 -107
  316. package/errors/base-error.test.js +0 -32
  317. package/errors/fetch-error.test.js +0 -79
  318. package/errors/halt-error.test.js +0 -11
  319. package/errors/validation-errors.test.js +0 -120
  320. package/integrations/create-frigg-backend.js +0 -31
  321. package/integrations/integration-factory.js +0 -251
  322. package/integrations/integration-mapping.js +0 -43
  323. package/integrations/integration-model.js +0 -46
  324. package/integrations/integration-user.js +0 -144
  325. package/integrations/test/integration-base.test.js +0 -144
  326. package/lambda/TimeoutCatcher.test.js +0 -68
  327. package/logs/logger.test.js +0 -76
  328. package/module-plugin/auther.js +0 -350
  329. package/module-plugin/credential.js +0 -22
  330. package/module-plugin/entity-manager.js +0 -70
  331. package/module-plugin/entity.js +0 -46
  332. package/module-plugin/manager.js +0 -169
  333. package/module-plugin/module-factory.js +0 -61
  334. package/module-plugin/requester/api-key.js +0 -36
  335. package/module-plugin/requester/oauth-2.js +0 -219
  336. package/module-plugin/requester/requester.js +0 -165
  337. package/module-plugin/requester/requester.test.js +0 -28
  338. package/module-plugin/test/auther.test.js +0 -97
  339. package/syncs/model.js +0 -62
  340. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  341. /package/{module-plugin → modules}/requester/basic.js +0 -0
  342. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,414 @@
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
+ @@index([organizationId])
63
+ }
64
+
65
+ enum UserType {
66
+ INDIVIDUAL
67
+ ORGANIZATION
68
+ }
69
+
70
+ // ============================================================================
71
+ // AUTHENTICATION MODELS
72
+ // ============================================================================
73
+
74
+ /// Authentication tokens with expiration
75
+ /// Bcrypt hashed tokens stored (handled in application layer)
76
+ model Token {
77
+ id Int @id @default(autoincrement())
78
+ token String // Bcrypt hashed
79
+ created DateTime @default(now())
80
+ expires DateTime?
81
+ userId Int
82
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
83
+
84
+ @@index([userId])
85
+ @@index([expires])
86
+ }
87
+
88
+ // ============================================================================
89
+ // CREDENTIAL & ENTITY MODELS
90
+ // ============================================================================
91
+
92
+ /// OAuth credentials and API tokens
93
+ /// All sensitive data encrypted with KMS at rest
94
+ model Credential {
95
+ id Int @id @default(autoincrement())
96
+ userId Int?
97
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
98
+ authIsValid Boolean?
99
+ externalId String?
100
+
101
+ // Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)
102
+ // Contains: access_token, refresh_token, domain, expires_in, token_type, etc.
103
+ data Json @default("{}")
104
+
105
+ createdAt DateTime @default(now())
106
+ updatedAt DateTime @updatedAt
107
+
108
+ // Relations
109
+ entities Entity[]
110
+
111
+ @@index([userId])
112
+ @@index([externalId])
113
+ }
114
+
115
+ /// External service entities (API connections)
116
+ model Entity {
117
+ id Int @id @default(autoincrement())
118
+ credentialId Int?
119
+ credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull)
120
+ userId Int?
121
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
122
+ name String?
123
+ moduleName String?
124
+ externalId String?
125
+
126
+ data Json @default("{}")
127
+
128
+ createdAt DateTime @default(now())
129
+ updatedAt DateTime @updatedAt
130
+
131
+ // Relations - many-to-many with implicit join tables
132
+ integrations Integration[]
133
+ syncs Sync[]
134
+
135
+ dataIdentifiers DataIdentifier[]
136
+ associationObjects AssociationObject[]
137
+
138
+ @@index([userId])
139
+ @@index([externalId])
140
+ @@index([moduleName])
141
+ @@index([credentialId])
142
+ }
143
+
144
+ // ============================================================================
145
+ // INTEGRATION MODELS
146
+ // ============================================================================
147
+
148
+ /// Main integration configuration and state
149
+ model Integration {
150
+ id Int @id @default(autoincrement())
151
+ userId Int?
152
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
153
+ status IntegrationStatus @default(IN_CREATION)
154
+
155
+ // Configuration and version
156
+ config Json? // Integration configuration object
157
+ version String?
158
+
159
+ // Entity references (many-to-many via implicit join table)
160
+ entities Entity[]
161
+
162
+ // Message arrays (stored as JSON)
163
+ errors Json @default("[]")
164
+ warnings Json @default("[]")
165
+ info Json @default("[]")
166
+ logs Json @default("[]")
167
+
168
+ createdAt DateTime @default(now())
169
+ updatedAt DateTime @updatedAt
170
+
171
+ // Relations
172
+ associations Association[]
173
+ syncs Sync[]
174
+ mappings IntegrationMapping[]
175
+ processes Process[]
176
+
177
+ @@index([userId])
178
+ @@index([status])
179
+ }
180
+
181
+ enum IntegrationStatus {
182
+ ENABLED
183
+ NEEDS_CONFIG
184
+ PROCESSING
185
+ DISABLED
186
+ ERROR
187
+ IN_CREATION
188
+ IN_DELETION
189
+ }
190
+
191
+ /// Integration-specific data mappings
192
+ /// All mapping data encrypted with KMS
193
+ model IntegrationMapping {
194
+ id Int @id @default(autoincrement())
195
+ integrationId Int
196
+ integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)
197
+ sourceId String?
198
+
199
+ // Encrypted mapping data (handled via Prisma middleware)
200
+ mapping Json?
201
+
202
+ createdAt DateTime @default(now())
203
+ updatedAt DateTime @updatedAt
204
+
205
+ @@unique([integrationId, sourceId])
206
+ @@index([integrationId])
207
+ @@index([sourceId])
208
+ }
209
+
210
+ // ============================================================================
211
+ // SYNC MODELS
212
+ // ============================================================================
213
+
214
+ /// Bidirectional data synchronization tracking
215
+ model Sync {
216
+ id Int @id @default(autoincrement())
217
+ integrationId Int?
218
+ integration Integration? @relation(fields: [integrationId], references: [id], onDelete: Cascade)
219
+
220
+ // Entity references (many-to-many via implicit join table)
221
+ entities Entity[]
222
+
223
+ hash String
224
+ name String
225
+
226
+ // Data identifiers (extracted to separate model)
227
+ dataIdentifiers DataIdentifier[]
228
+
229
+ @@index([integrationId])
230
+ @@index([hash])
231
+ @@index([name])
232
+ }
233
+
234
+ /// Data identifier for sync operations
235
+ /// Replaces nested array structure in Mongoose
236
+ model DataIdentifier {
237
+ id Int @id @default(autoincrement())
238
+ syncId Int?
239
+ sync Sync? @relation(fields: [syncId], references: [id], onDelete: Cascade)
240
+ entityId Int
241
+ entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
242
+
243
+ // Identifier data (can be any structure)
244
+ idData Json
245
+
246
+ hash String
247
+
248
+ @@index([syncId])
249
+ @@index([entityId])
250
+ @@index([hash])
251
+ }
252
+
253
+ // ============================================================================
254
+ // ASSOCIATION MODELS
255
+ // ============================================================================
256
+
257
+ /// Entity associations with cardinality tracking
258
+ model Association {
259
+ id Int @id @default(autoincrement())
260
+ integrationId Int
261
+ integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)
262
+ name String
263
+ type AssociationType
264
+ primaryObject String
265
+
266
+ // Associated objects (extracted to separate model)
267
+ objects AssociationObject[]
268
+
269
+ @@index([integrationId])
270
+ @@index([name])
271
+ }
272
+
273
+ /// Association object entry
274
+ /// Replaces nested array structure in Mongoose
275
+ model AssociationObject {
276
+ id Int @id @default(autoincrement())
277
+ associationId Int
278
+ association Association @relation(fields: [associationId], references: [id], onDelete: Cascade)
279
+ entityId Int
280
+ entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
281
+ objectType String
282
+ objId String
283
+ metadata Json? // Optional metadata
284
+
285
+ @@index([associationId])
286
+ @@index([entityId])
287
+ }
288
+
289
+ enum AssociationType {
290
+ ONE_TO_MANY
291
+ ONE_TO_ONE
292
+ MANY_TO_ONE
293
+ }
294
+
295
+ // ============================================================================
296
+ // PROCESS MODELS
297
+ // ============================================================================
298
+
299
+ /// Generic Process Model - tracks any long-running operation
300
+ /// Used for: CRM syncs, data migrations, bulk operations, etc.
301
+ model Process {
302
+ id Int @id @default(autoincrement())
303
+
304
+ // Core references
305
+ userId Int
306
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
307
+ integrationId Int
308
+ integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)
309
+
310
+ // Process identification
311
+ name String // e.g., "zoho-crm-contact-sync", "pipedrive-lead-sync"
312
+ type String // e.g., "CRM_SYNC", "DATA_MIGRATION", "BULK_OPERATION"
313
+
314
+ // State machine
315
+ state String // Current state (integration-defined states)
316
+
317
+ // Flexible storage
318
+ context Json @default("{}") // Process-specific data (pagination, metadata, etc.)
319
+ results Json @default("{}") // Process results and metrics
320
+
321
+ // Hierarchy support - self-referential relation
322
+ parentProcessId Int?
323
+ parentProcess Process? @relation("ProcessHierarchy", fields: [parentProcessId], references: [id], onDelete: SetNull)
324
+ childProcesses Process[] @relation("ProcessHierarchy")
325
+
326
+ // Timestamps
327
+ createdAt DateTime @default(now())
328
+ updatedAt DateTime @updatedAt
329
+
330
+ @@index([userId])
331
+ @@index([integrationId])
332
+ @@index([type])
333
+ @@index([state])
334
+ @@index([name])
335
+ @@index([parentProcessId])
336
+ }
337
+
338
+ // ============================================================================
339
+ // UTILITY MODELS
340
+ // ============================================================================
341
+
342
+ /// Generic state storage
343
+ model State {
344
+ id Int @id @default(autoincrement())
345
+ state Json?
346
+ }
347
+
348
+ /// AWS API Gateway WebSocket connection tracking
349
+ model WebsocketConnection {
350
+ id Int @id @default(autoincrement())
351
+ connectionId String?
352
+
353
+ @@index([connectionId])
354
+ }
355
+
356
+ // ============================================================================
357
+ // ADMIN PROCESS MODELS
358
+ // ============================================================================
359
+
360
+ /// Admin process state machine
361
+ enum AdminScriptExecutionState {
362
+ PENDING
363
+ RUNNING
364
+ COMPLETED
365
+ FAILED
366
+ }
367
+
368
+ /// Admin process tracking (like Process but without user/integration FK)
369
+ /// Used for: admin scripts, db migrations, system maintenance tasks
370
+ model AdminScriptExecution {
371
+ id Int @id @default(autoincrement())
372
+ name String // e.g., "oauth-token-refresh", "db-migration-xyz"
373
+ type String // e.g., "ADMIN_SCRIPT", "DB_MIGRATION"
374
+
375
+ // State machine
376
+ state AdminScriptExecutionState @default(PENDING)
377
+
378
+ // Flexible storage (mirrors Process model pattern)
379
+ context Json @default("{}") // input, trigger, audit info, script version
380
+ results Json @default("{}") // output, logs, metrics, errors
381
+
382
+ // Hierarchy support - self-referential relation
383
+ parentExecutionId Int?
384
+ parentExecution AdminScriptExecution? @relation("AdminScriptExecutionHierarchy", fields: [parentExecutionId], references: [id], onDelete: SetNull)
385
+ childExecutions AdminScriptExecution[] @relation("AdminScriptExecutionHierarchy")
386
+
387
+ // Timestamps
388
+ createdAt DateTime @default(now())
389
+ updatedAt DateTime @updatedAt
390
+
391
+ @@index([name, createdAt(sort: Desc)])
392
+ @@index([state])
393
+ @@index([type])
394
+ }
395
+
396
+ /// Script scheduling configuration for hybrid scheduling (SQS + EventBridge)
397
+ model ScriptSchedule {
398
+ id Int @id @default(autoincrement())
399
+ scriptName String @unique
400
+ enabled Boolean @default(false)
401
+ cronExpression String?
402
+ timezone String @default("UTC")
403
+ lastTriggeredAt DateTime?
404
+ nextTriggerAt DateTime?
405
+
406
+ // External Scheduler (e.g., AWS EventBridge Scheduler)
407
+ externalScheduleId String?
408
+ externalScheduleName String?
409
+
410
+ createdAt DateTime @default(now())
411
+ updatedAt DateTime @updatedAt
412
+
413
+ @@index([enabled])
414
+ }
@@ -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
@@ -0,0 +1,86 @@
1
+ # Reporting
2
+
3
+ Read-only, deployment-wide reporting endpoints for `@friggframework/core`.
4
+ Gated by a dedicated reporting API key — **not** the per-user auth used by the
5
+ rest of the Management API.
6
+
7
+ ## Auth
8
+
9
+ Send the reporting key in the `x-frigg-reporting-api-key` header. It is validated
10
+ against the `REPORTING_API_KEY` environment variable. Missing or wrong key → `401`.
11
+
12
+ ## Endpoints
13
+
14
+ | Method | Path | Description |
15
+ | --- | --- | --- |
16
+ | `GET` | `/api/v2/reports` | Index of available reports. |
17
+ | `GET` | `/api/v2/reports/integrations` | Integrations report (see below). |
18
+
19
+ ### `GET /api/v2/reports/integrations`
20
+
21
+ Optional query params (all strings): `status` (an `IntegrationStatus`), `type`
22
+ (`config.type` slug), `userId`.
23
+
24
+ ## Response shape
25
+
26
+ ```jsonc
27
+ {
28
+ "schemaVersion": 1,
29
+ "service": "frigg-core-api",
30
+ "generatedAt": "2026-06-29T20:45:05.142Z",
31
+ "filters": { "status": null, "type": null, "userId": null },
32
+ "metrics": {
33
+ "total": 12,
34
+ "byStatus": { "ENABLED": 9, "ERROR": 2, "NEEDS_CONFIG": 1, "PROCESSING": 0, "DISABLED": 0 },
35
+ "byType": [
36
+ {
37
+ "type": "hubspot",
38
+ "label": "HubSpot CRM",
39
+ "total": 5,
40
+ "byStatus": { "ENABLED": 4, "ERROR": 1, "NEEDS_CONFIG": 0, "PROCESSING": 0, "DISABLED": 0 }
41
+ }
42
+ ],
43
+ "typeLabels": { "hubspot": "HubSpot CRM" },
44
+ "integrations": [ /* lightweight per-integration rows */ ]
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## Field reference
50
+
51
+ - `schemaVersion` — contract version; branch on it. Additive changes do **not** bump it.
52
+ - `filters` — echoes the applied filters (nulls when omitted).
53
+ - `metrics.total` — count of integrations matching the filters.
54
+ - `metrics.byStatus` — counts keyed by `IntegrationStatus` value.
55
+ - `metrics.byType[]` — per `type` breakdown: `{ type, label, total, byStatus }`.
56
+ - `type` — the `config.type` slug. Integrations with no type bucket as `"unknown"`.
57
+ - `label` — human-readable name from the integration class's
58
+ `Definition.display.label`. Falls back to the `type` slug when no registered
59
+ class supplies a label (e.g. an integration that was removed, or run on an
60
+ older app that doesn't register it). Additive — `type` is unchanged.
61
+ - `metrics.typeLabels` — map of `type` slug → human-readable label, so callers can
62
+ label `integrations[].type` rows without bloating each row. Contains only types
63
+ whose registered class supplies a non-default `display.label` (classes still
64
+ carrying the IntegrationBase default `'Integration Name'` are excluded).
65
+ - `metrics.integrations[]` — lightweight per-integration rows (`id`, `type`,
66
+ `status`, `userId`, `version`, `moduleCount`, `errorCount`, `mappedRecordCount`,
67
+ `createdAt`, `updatedAt`). These rows carry `type` only — resolve display names
68
+ via `metrics.typeLabels`.
69
+
70
+ ## How labels are sourced
71
+
72
+ `createReportingRouter()` loads the app's registered integration classes via
73
+ `loadAppDefinition()` and builds a `{ slug → label }` map from each class's
74
+ `Definition.display.label`. Loading is wrapped in try/catch, so reporting still
75
+ works (labels fall back to slugs) if the app definition can't be loaded. The
76
+ `ListIntegrationsReport` use case stays storage-agnostic — it just reads the
77
+ injected `typeLabels` map, so all database adapters (PostgreSQL, MongoDB,
78
+ DocumentDB) get labels with zero adapter changes.
79
+
80
+ ## Caveats
81
+
82
+ - The response is sensitive (exposes integration types, counts, user IDs, versions).
83
+ Treat the reporting key as an admin secret.
84
+ - Read-only — no mutation endpoints.
85
+ - Labels only appear once the deployment runs a `@friggframework/core` version that
86
+ includes this field and the app registers the integration classes.
@@ -0,0 +1,17 @@
1
+ const { createReportingRouter } = require('./reporting-router');
2
+ const {
3
+ createReportingRepository,
4
+ ReportingRepositoryMongo,
5
+ ReportingRepositoryPostgres,
6
+ ReportingRepositoryDocumentDB,
7
+ } = require('./repositories/reporting-repository-factory');
8
+ const { ListIntegrationsReport } = require('./use-cases');
9
+
10
+ module.exports = {
11
+ createReportingRouter,
12
+ createReportingRepository,
13
+ ReportingRepositoryMongo,
14
+ ReportingRepositoryPostgres,
15
+ ReportingRepositoryDocumentDB,
16
+ ListIntegrationsReport,
17
+ };