@friggframework/core 2.0.0-next.12 → 2.0.0-next.121

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 (410) hide show
  1. package/.eslintrc.json +18 -1
  2. package/CLAUDE.md +830 -0
  3. package/README.md +996 -50
  4. package/admin-scripts/index.js +52 -0
  5. package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
  6. package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
  7. package/admin-scripts/repositories/admin-script-execution-repository-interface.js +171 -0
  8. package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +238 -0
  9. package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +278 -0
  10. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  11. package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
  12. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  13. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  14. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  15. package/application/commands/README.md +451 -0
  16. package/application/commands/admin-script-commands.js +359 -0
  17. package/application/commands/credential-commands.js +262 -0
  18. package/application/commands/entity-commands.js +294 -0
  19. package/application/commands/integration-commands.js +419 -0
  20. package/application/commands/integration-mapping-commands.js +25 -0
  21. package/application/commands/process-commands.js +135 -0
  22. package/application/commands/report-commands.js +188 -0
  23. package/application/commands/scheduler-commands.js +263 -0
  24. package/application/commands/usage-commands.js +56 -0
  25. package/application/commands/user-commands.js +312 -0
  26. package/application/index.js +83 -0
  27. package/artifacts/repositories/artifact-repository-factory.js +19 -0
  28. package/artifacts/repositories/artifact-repository-interface.js +27 -0
  29. package/artifacts/repositories/artifact-repository-local.js +42 -0
  30. package/artifacts/repositories/artifact-repository-s3.js +61 -0
  31. package/assertions/index.js +0 -3
  32. package/core/CLAUDE.md +715 -0
  33. package/core/Worker.js +55 -24
  34. package/core/create-handler.js +121 -38
  35. package/core/index.js +8 -1
  36. package/core/invocation-scope.js +225 -0
  37. package/core/parameters-to-env.js +257 -0
  38. package/core/secrets-to-env.js +7 -6
  39. package/core/ssm-preload.mjs +34 -0
  40. package/credential/repositories/credential-active-type.js +32 -0
  41. package/credential/repositories/credential-repository-documentdb.js +355 -0
  42. package/credential/repositories/credential-repository-factory.js +54 -0
  43. package/credential/repositories/credential-repository-interface.js +113 -0
  44. package/credential/repositories/credential-repository-mongo.js +294 -0
  45. package/credential/repositories/credential-repository-postgres.js +312 -0
  46. package/credential/repositories/credential-repository.js +300 -0
  47. package/credential/use-cases/get-credential-for-user.js +25 -0
  48. package/credential/use-cases/update-authentication-status.js +15 -0
  49. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  50. package/database/adapters/lambda-invoker.js +97 -0
  51. package/database/config.js +147 -0
  52. package/database/documentdb-encryption-service.js +334 -0
  53. package/database/documentdb-utils.js +196 -0
  54. package/database/encryption/README.md +863 -0
  55. package/database/encryption/documentdb-encryption-service.md +3575 -0
  56. package/database/encryption/encryption-schema-registry.js +404 -0
  57. package/database/encryption/field-encryption-service.js +254 -0
  58. package/database/encryption/integration-mapping-encryption.js +97 -0
  59. package/database/encryption/logger.js +82 -0
  60. package/database/encryption/prisma-encryption-extension.js +241 -0
  61. package/database/index.js +21 -21
  62. package/database/prisma.js +182 -0
  63. package/database/repositories/health-check-repository-documentdb.js +138 -0
  64. package/database/repositories/health-check-repository-factory.js +48 -0
  65. package/database/repositories/health-check-repository-interface.js +82 -0
  66. package/database/repositories/health-check-repository-mongodb.js +89 -0
  67. package/database/repositories/health-check-repository-postgres.js +82 -0
  68. package/database/repositories/migration-status-repository-s3.js +137 -0
  69. package/database/use-cases/check-database-health-use-case.js +29 -0
  70. package/database/use-cases/check-database-state-use-case.js +81 -0
  71. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  72. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  73. package/database/use-cases/get-migration-status-use-case.js +93 -0
  74. package/database/use-cases/resolve-migration-via-worker-use-case.js +49 -0
  75. package/database/use-cases/run-database-migration-use-case.js +139 -0
  76. package/database/use-cases/test-encryption-use-case.js +253 -0
  77. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  78. package/database/utils/mongodb-collection-utils.js +94 -0
  79. package/database/utils/mongodb-schema-init.js +108 -0
  80. package/database/utils/prisma-runner.js +491 -0
  81. package/database/utils/prisma-schema-parser.js +182 -0
  82. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  83. package/encrypt/Cryptor.js +34 -168
  84. package/encrypt/index.js +1 -2
  85. package/errors/client-safe-error.js +26 -0
  86. package/errors/fetch-error.js +59 -57
  87. package/errors/index.js +6 -0
  88. package/errors/mapping-already-exists-error.js +14 -0
  89. package/generated/prisma-mongodb/client.d.ts +1 -0
  90. package/generated/prisma-mongodb/client.js +5 -0
  91. package/generated/prisma-mongodb/default.d.ts +1 -0
  92. package/generated/prisma-mongodb/default.js +5 -0
  93. package/generated/prisma-mongodb/edge.d.ts +1 -0
  94. package/generated/prisma-mongodb/edge.js +386 -0
  95. package/generated/prisma-mongodb/index-browser.js +368 -0
  96. package/generated/prisma-mongodb/index.d.ts +27065 -0
  97. package/generated/prisma-mongodb/index.js +411 -0
  98. package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  99. package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  100. package/generated/prisma-mongodb/package.json +183 -0
  101. package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
  102. package/generated/prisma-mongodb/runtime/edge.js +35 -0
  103. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  104. package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
  105. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  106. package/generated/prisma-mongodb/runtime/library.js +147 -0
  107. package/generated/prisma-mongodb/runtime/react-native.js +84 -0
  108. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
  109. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
  110. package/generated/prisma-mongodb/schema.prisma +455 -0
  111. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
  112. package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
  113. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  114. package/generated/prisma-mongodb/wasm.js +393 -0
  115. package/generated/prisma-postgresql/client.d.ts +1 -0
  116. package/generated/prisma-postgresql/client.js +5 -0
  117. package/generated/prisma-postgresql/default.d.ts +1 -0
  118. package/generated/prisma-postgresql/default.js +5 -0
  119. package/generated/prisma-postgresql/edge.d.ts +1 -0
  120. package/generated/prisma-postgresql/edge.js +407 -0
  121. package/generated/prisma-postgresql/index-browser.js +389 -0
  122. package/generated/prisma-postgresql/index.d.ts +29904 -0
  123. package/generated/prisma-postgresql/index.js +432 -0
  124. package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  125. package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  126. package/generated/prisma-postgresql/package.json +183 -0
  127. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  128. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  129. package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
  130. package/generated/prisma-postgresql/runtime/edge.js +35 -0
  131. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  132. package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
  133. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  134. package/generated/prisma-postgresql/runtime/library.js +147 -0
  135. package/generated/prisma-postgresql/runtime/react-native.js +84 -0
  136. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
  137. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
  138. package/generated/prisma-postgresql/schema.prisma +436 -0
  139. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
  140. package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
  141. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  142. package/generated/prisma-postgresql/wasm.js +414 -0
  143. package/handlers/WEBHOOKS.md +682 -0
  144. package/handlers/app-definition-loader.js +103 -0
  145. package/handlers/app-handler-helpers.js +20 -6
  146. package/handlers/backend-utils.js +272 -46
  147. package/handlers/database-migration-handler.js +236 -0
  148. package/handlers/integration-event-dispatcher.js +68 -0
  149. package/handlers/middleware/admin-auth.js +73 -0
  150. package/handlers/routers/HEALTHCHECK.md +342 -0
  151. package/handlers/routers/auth.js +4 -15
  152. package/handlers/routers/db-migration.handler.js +29 -0
  153. package/handlers/routers/db-migration.js +329 -0
  154. package/handlers/routers/health.js +518 -0
  155. package/handlers/routers/integration-defined-routers.js +85 -10
  156. package/handlers/routers/integration-webhook-routers.js +87 -0
  157. package/handlers/routers/user.js +27 -5
  158. package/handlers/routers/websocket.js +31 -9
  159. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  160. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  161. package/handlers/workers/db-migration.js +438 -0
  162. package/handlers/workers/dlq-processor.js +89 -0
  163. package/handlers/workers/integration-defined-workers.js +13 -7
  164. package/index.js +123 -47
  165. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +238 -0
  166. package/infrastructure/scheduler/index.js +33 -0
  167. package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
  168. package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
  169. package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
  170. package/integrations/EXTENSIONS.md +240 -0
  171. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  172. package/integrations/extension.js +254 -0
  173. package/integrations/index.js +20 -10
  174. package/integrations/integration-base.js +777 -74
  175. package/integrations/integration-router.js +399 -180
  176. package/integrations/options.js +1 -1
  177. package/integrations/repositories/config-patch-shared.js +43 -0
  178. package/integrations/repositories/integration-mapping-query-pipeline.js +152 -0
  179. package/integrations/repositories/integration-mapping-query.js +193 -0
  180. package/integrations/repositories/integration-mapping-repository-documentdb.js +406 -0
  181. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  182. package/integrations/repositories/integration-mapping-repository-interface.js +183 -0
  183. package/integrations/repositories/integration-mapping-repository-mongo.js +284 -0
  184. package/integrations/repositories/integration-mapping-repository-postgres.js +403 -0
  185. package/integrations/repositories/integration-mapping-repository.js +185 -0
  186. package/integrations/repositories/integration-repository-documentdb.js +354 -0
  187. package/integrations/repositories/integration-repository-factory.js +51 -0
  188. package/integrations/repositories/integration-repository-interface.js +219 -0
  189. package/integrations/repositories/integration-repository-mongo.js +502 -0
  190. package/integrations/repositories/integration-repository-postgres.js +550 -0
  191. package/integrations/repositories/process-repository-documentdb.js +311 -0
  192. package/integrations/repositories/process-repository-factory.js +53 -0
  193. package/integrations/repositories/process-repository-interface.js +136 -0
  194. package/integrations/repositories/process-repository-mongo.js +262 -0
  195. package/integrations/repositories/process-repository-postgres.js +380 -0
  196. package/integrations/repositories/process-update-ops-shared.js +112 -0
  197. package/integrations/repositories/report-id.js +13 -0
  198. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  199. package/integrations/tests/doubles/dummy-integration-class.js +113 -0
  200. package/integrations/tests/doubles/test-integration-repository.js +146 -0
  201. package/integrations/use-cases/add-entities-to-integration.js +92 -0
  202. package/integrations/use-cases/create-integration.js +253 -0
  203. package/integrations/use-cases/create-process.js +130 -0
  204. package/integrations/use-cases/delete-integration-for-user.js +130 -0
  205. package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
  206. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
  207. package/integrations/use-cases/get-integration-for-user.js +78 -0
  208. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  209. package/integrations/use-cases/get-integration-instance.js +83 -0
  210. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  211. package/integrations/use-cases/get-possible-integrations.js +27 -0
  212. package/integrations/use-cases/get-process.js +89 -0
  213. package/integrations/use-cases/index.js +19 -0
  214. package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
  215. package/integrations/use-cases/load-integration-context.js +71 -0
  216. package/integrations/use-cases/patch-integration-config.js +39 -0
  217. package/integrations/use-cases/process-errors.js +28 -0
  218. package/integrations/use-cases/remove-entities-from-integration.js +70 -0
  219. package/integrations/use-cases/update-integration-config.js +32 -0
  220. package/integrations/use-cases/update-integration-messages.js +44 -0
  221. package/integrations/use-cases/update-integration-status.js +32 -0
  222. package/integrations/use-cases/update-integration.js +92 -0
  223. package/integrations/use-cases/update-process-metrics.js +222 -0
  224. package/integrations/use-cases/update-process-state.js +163 -0
  225. package/integrations/utils/assert-entity-ids-input.js +42 -0
  226. package/integrations/utils/map-integration-dto.js +37 -0
  227. package/jest-global-setup-noop.js +3 -0
  228. package/jest-global-teardown-noop.js +3 -0
  229. package/jest.config.js +2 -0
  230. package/logs/.eslintrc.json +26 -1
  231. package/logs/__fixtures__/events.js +82 -0
  232. package/logs/__fixtures__/matchers.js +45 -0
  233. package/logs/__fixtures__/secrets.js +59 -0
  234. package/logs/__fixtures__/vectors.js +329 -0
  235. package/logs/__fixtures__/with-env.js +45 -0
  236. package/logs/context.js +109 -0
  237. package/logs/contract-keys.js +67 -0
  238. package/logs/debug-shims.js +46 -0
  239. package/logs/denied-keys.js +21 -0
  240. package/logs/index.js +14 -3
  241. package/logs/jest-logger-setup.js +25 -0
  242. package/logs/levels.js +71 -0
  243. package/logs/logger-runtime.js +137 -0
  244. package/logs/logger.js +134 -46
  245. package/logs/record.js +276 -0
  246. package/logs/redact.js +328 -0
  247. package/logs/serialize.js +329 -0
  248. package/logs/sinks.js +93 -0
  249. package/logs/summarize-event.js +163 -0
  250. package/{module-plugin → modules}/index.js +0 -10
  251. package/modules/module-factory.js +56 -0
  252. package/modules/module.js +320 -0
  253. package/modules/repositories/module-repository-documentdb.js +350 -0
  254. package/modules/repositories/module-repository-factory.js +40 -0
  255. package/modules/repositories/module-repository-interface.js +145 -0
  256. package/modules/repositories/module-repository-mongo.js +436 -0
  257. package/modules/repositories/module-repository-postgres.js +481 -0
  258. package/modules/repositories/module-repository.js +369 -0
  259. package/modules/requester/api-key.js +52 -0
  260. package/modules/requester/oauth-2.js +561 -0
  261. package/modules/requester/requester.js +605 -0
  262. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  263. package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
  264. package/modules/tests/doubles/test-module-factory.js +16 -0
  265. package/modules/tests/doubles/test-module-repository.js +39 -0
  266. package/modules/use-cases/get-entities-for-user.js +32 -0
  267. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  268. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  269. package/modules/use-cases/get-module-instance-from-type.js +34 -0
  270. package/modules/use-cases/get-module.js +74 -0
  271. package/modules/use-cases/process-authorization-callback.js +294 -0
  272. package/modules/use-cases/refresh-entity-options.js +72 -0
  273. package/modules/use-cases/test-module-auth.js +72 -0
  274. package/modules/utils/map-module-dto.js +20 -0
  275. package/modules/utils/to-credential-id.js +14 -0
  276. package/package.json +92 -52
  277. package/prisma-mongodb/schema.prisma +455 -0
  278. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  279. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  280. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  281. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  282. package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
  283. package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
  284. package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
  285. package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
  286. package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
  287. package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +26 -0
  288. package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
  289. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  290. package/prisma-postgresql/schema.prisma +436 -0
  291. package/queues/queue-delivery.js +41 -0
  292. package/queues/queuer-util.js +89 -21
  293. package/reporting/README.md +154 -0
  294. package/reporting/builtin-reports.js +6 -0
  295. package/reporting/index.js +13 -0
  296. package/reporting/report-base.js +49 -0
  297. package/reporting/reports/integrations-report.js +221 -0
  298. package/syncs/manager.js +468 -443
  299. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  300. package/syncs/repositories/sync-repository-factory.js +43 -0
  301. package/syncs/repositories/sync-repository-interface.js +109 -0
  302. package/syncs/repositories/sync-repository-mongo.js +239 -0
  303. package/syncs/repositories/sync-repository-postgres.js +319 -0
  304. package/syncs/sync.js +0 -1
  305. package/telemetry/README.md +331 -0
  306. package/telemetry/bind-telemetry-context.js +77 -0
  307. package/telemetry/canonical-counters.js +52 -0
  308. package/telemetry/exporters.js +85 -0
  309. package/telemetry/index.js +26 -0
  310. package/telemetry/instrument-handler.js +104 -0
  311. package/telemetry/no-op-telemetry.js +71 -0
  312. package/telemetry/north-star.js +103 -0
  313. package/telemetry/otel-telemetry.js +232 -0
  314. package/telemetry/plugin-subscribers.js +77 -0
  315. package/telemetry/telemetry-config.js +120 -0
  316. package/telemetry/telemetry-context.js +51 -0
  317. package/telemetry/telemetry-event-bus.js +58 -0
  318. package/telemetry/telemetry-runtime.js +164 -0
  319. package/telemetry/telemetry-service.js +52 -0
  320. package/telemetry/usage-rollup-subscriber.js +116 -0
  321. package/token/repositories/token-repository-documentdb.js +137 -0
  322. package/token/repositories/token-repository-factory.js +40 -0
  323. package/token/repositories/token-repository-interface.js +131 -0
  324. package/token/repositories/token-repository-mongo.js +219 -0
  325. package/token/repositories/token-repository-postgres.js +264 -0
  326. package/token/repositories/token-repository.js +219 -0
  327. package/types/associations/index.d.ts +0 -17
  328. package/types/core/index.d.ts +12 -4
  329. package/types/database/index.d.ts +10 -2
  330. package/types/encrypt/index.d.ts +5 -3
  331. package/types/errors/index.d.ts +24 -10
  332. package/types/integrations/index.d.ts +3 -8
  333. package/types/logs/index.d.ts +61 -2
  334. package/types/module-plugin/index.d.ts +20 -69
  335. package/types/syncs/index.d.ts +0 -17
  336. package/usage/README.md +54 -0
  337. package/usage/index.js +17 -0
  338. package/usage/repositories/usage-repository-documentdb.js +194 -0
  339. package/usage/repositories/usage-repository-factory.js +25 -0
  340. package/usage/repositories/usage-repository-interface.js +37 -0
  341. package/usage/repositories/usage-repository-prisma.js +146 -0
  342. package/usage/tracked-metrics.js +38 -0
  343. package/usage/usage-windows.js +24 -0
  344. package/user/repositories/user-repository-documentdb.js +426 -0
  345. package/user/repositories/user-repository-factory.js +52 -0
  346. package/user/repositories/user-repository-interface.js +214 -0
  347. package/user/repositories/user-repository-mongo.js +323 -0
  348. package/user/repositories/user-repository-postgres.js +377 -0
  349. package/user/tests/doubles/test-user-repository.js +72 -0
  350. package/user/use-cases/authenticate-user.js +143 -0
  351. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  352. package/user/use-cases/create-individual-user.js +61 -0
  353. package/user/use-cases/create-organization-user.js +47 -0
  354. package/user/use-cases/create-token-for-user-id.js +30 -0
  355. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  356. package/user/use-cases/get-user-from-bearer-token.js +82 -0
  357. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  358. package/user/use-cases/login-user.js +122 -0
  359. package/user/user.js +125 -0
  360. package/utils/backend-path.js +38 -0
  361. package/utils/index.js +6 -0
  362. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  363. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  364. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  365. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  366. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  367. package/websocket/repositories/websocket-connection-repository.js +161 -0
  368. package/assertions/is-equal.js +0 -17
  369. package/associations/model.js +0 -54
  370. package/database/models/IndividualUser.js +0 -76
  371. package/database/models/OrganizationUser.js +0 -29
  372. package/database/models/State.js +0 -9
  373. package/database/models/Token.js +0 -70
  374. package/database/models/UserModel.js +0 -7
  375. package/database/models/WebsocketConnection.js +0 -49
  376. package/database/mongo.js +0 -45
  377. package/database/mongoose.js +0 -5
  378. package/encrypt/Cryptor.test.js +0 -32
  379. package/encrypt/encrypt.js +0 -132
  380. package/encrypt/encrypt.test.js +0 -1069
  381. package/encrypt/test-encrypt.js +0 -107
  382. package/errors/base-error.test.js +0 -32
  383. package/errors/fetch-error.test.js +0 -79
  384. package/errors/halt-error.test.js +0 -11
  385. package/errors/validation-errors.test.js +0 -120
  386. package/handlers/routers/middleware/loadUser.js +0 -15
  387. package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
  388. package/integrations/create-frigg-backend.js +0 -31
  389. package/integrations/integration-factory.js +0 -251
  390. package/integrations/integration-mapping.js +0 -43
  391. package/integrations/integration-model.js +0 -46
  392. package/integrations/integration-user.js +0 -144
  393. package/integrations/test/integration-base.test.js +0 -144
  394. package/lambda/TimeoutCatcher.test.js +0 -68
  395. package/logs/logger.test.js +0 -76
  396. package/module-plugin/auther.js +0 -393
  397. package/module-plugin/credential.js +0 -22
  398. package/module-plugin/entity-manager.js +0 -70
  399. package/module-plugin/entity.js +0 -46
  400. package/module-plugin/manager.js +0 -169
  401. package/module-plugin/module-factory.js +0 -61
  402. package/module-plugin/requester/api-key.js +0 -36
  403. package/module-plugin/requester/oauth-2.js +0 -219
  404. package/module-plugin/requester/requester.js +0 -165
  405. package/module-plugin/requester/requester.test.js +0 -28
  406. package/module-plugin/test/auther.test.js +0 -97
  407. package/syncs/model.js +0 -62
  408. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  409. /package/{module-plugin → modules}/requester/basic.js +0 -0
  410. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
package/README.md CHANGED
@@ -1,83 +1,1029 @@
1
1
  # Frigg Core
2
2
 
3
- The `frigg-core` package is the heart of the Frigg Framework. It contains the core functionality and essential modules required to build and maintain integrations at scale.
4
-
3
+ The `@friggframework/core` package is the foundational layer of the Frigg Framework, implementing a hexagonal architecture pattern for building scalable, maintainable enterprise integrations. It provides the essential building blocks, domain logic, and infrastructure components that power the entire Frigg ecosystem.
5
4
 
6
5
  ## Table of Contents
7
6
 
8
- - [Introduction](#introduction)
9
- - [Features](#features)
7
+ - [Architecture Overview](#architecture-overview)
10
8
  - [Installation](#installation)
11
- - [Usage](#usage)
12
- - [Modules](#modules)
9
+ - [Quick Start](#quick-start)
10
+ - [Core Components](#core-components)
11
+ - [Hexagonal Architecture](#hexagonal-architecture)
12
+ - [Usage Examples](#usage-examples)
13
+ - [Testing](#testing)
14
+ - [Development](#development)
15
+ - [API Reference](#api-reference)
13
16
  - [Contributing](#contributing)
14
- - [License](#license)
15
-
16
- ## Introduction
17
17
 
18
- The Frigg Core package provides the foundational components and utilities for the Frigg Framework. It is designed to be modular, extensible, and easy to integrate with other packages in the Frigg ecosystem.
18
+ ## Architecture Overview
19
19
 
20
- ## Features
20
+ Frigg Core implements a **hexagonal architecture** (also known as ports and adapters) that separates business logic from external concerns:
21
21
 
22
- - **Associations**: Manage relationships between different entities.
23
- - **Database**: Database utilities and connectors.
24
- - **Encryption**: Secure data encryption and decryption.
25
- - **Error Handling**: Standardized error handling mechanisms.
26
- - **Integrations**: Tools for building and managing integrations.
27
- - **Lambda**: Utilities for AWS Lambda functions.
28
- - **Logging**: Structured logging utilities.
29
- - **Module Plugin**: Plugin system for extending core functionality.
30
- - **Syncs**: Synchronization utilities for data consistency.
31
- - **Infrastructure**: Frigg reads through your integration definitions and auto-generates the infrastructure your code needs to run smoothly.
22
+ ```
23
+ ┌─────────────────────────────────────────────────────────────┐
24
+ │ Inbound Adapters │
25
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
26
+ │ │ Express │ │ Lambda │ │ WebSocket │ │
27
+ │ │ Routes │ │ Handlers │ │ Handlers │ │
28
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
29
+ └─────────────────────────────────────────────────────────────┘
30
+ │
31
+ ┌─────────────────────────────────────────────────────────────┐
32
+ │ Application Layer │
33
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
34
+ │ │ Use Cases │ │ Services │ │ Coordinators│ │
35
+ │ │ (Business │ │ │ │ │ │
36
+ │ │ Logic) │ │ │ │ │ │
37
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
38
+ └─────────────────────────────────────────────────────────────┘
39
+ │
40
+ ┌─────────────────────────────────────────────────────────────┐
41
+ │ Domain Layer │
42
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
43
+ │ │ Integration │ │ Entities │ │ Value │ │
44
+ │ │ Aggregates │ │ │ │ Objects │ │
45
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
46
+ └─────────────────────────────────────────────────────────────┘
47
+ │
48
+ ┌─────────────────────────────────────────────────────────────┐
49
+ │ Outbound Adapters │
50
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
51
+ │ │ Database │ │ API Modules │ │ Event │ │
52
+ │ │ Repositories│ │ │ │ Publishers │ │
53
+ │ └─────────────┘ └─────────────┘ └─────────────┘ │
54
+ └─────────────────────────────────────────────────────────────┘
55
+ ```
32
56
 
33
57
  ## Installation
34
58
 
35
- To install the `frigg-core` package, use npm or yarn:
36
-
37
- ```sh
59
+ ```bash
38
60
  npm install @friggframework/core
39
61
  # or
40
62
  yarn add @friggframework/core
41
63
  ```
42
- ## Usage
43
- Here's a basic example of how to use the frigg-core package:
64
+
65
+ ### Prisma Support (Optional)
66
+
67
+ `@friggframework/core` supports both MongoDB and PostgreSQL via Prisma ORM. **Prisma is an optional peer dependency** - you only need to install it if you're using database features that require migrations or schema generation.
68
+
69
+ **When you need Prisma:**
70
+ - Running database migrations (`prisma migrate`, `prisma db push`)
71
+ - Generating Prisma clients for your application
72
+ - Using the migration Lambda function (`dbMigrate`)
73
+
74
+ **Installation:**
75
+ ```bash
76
+ # Install Prisma CLI and Client as dev dependencies
77
+ npm install --save-dev prisma @prisma/client
78
+
79
+ # Or with yarn
80
+ yarn add -D prisma @prisma/client
81
+ ```
82
+
83
+ **Generate Prisma Clients:**
84
+ ```bash
85
+ # From @friggframework/core directory
86
+ npm run prisma:generate:mongo # MongoDB only
87
+ npm run prisma:generate:postgres # PostgreSQL only
88
+ npm run prisma:generate # Both databases
89
+ ```
90
+
91
+ **Note:** The published npm package includes pre-generated Prisma clients, so you don't need to install Prisma just to use `@friggframework/core` in production. Prisma is only required if you're actively developing migrations or running the migration Lambda function.
92
+
93
+ ### Prerequisites
94
+
95
+ - Node.js 16+
96
+ - MongoDB 4.4+ (for data persistence)
97
+ - AWS credentials (for SQS, KMS, Lambda deployment)
98
+
99
+ ### Environment Variables
100
+
101
+ ```bash
102
+ # Database
103
+ MONGO_URI=mongodb://localhost:27017/frigg
104
+ FRIGG_ENCRYPTION_KEY=your-256-bit-encryption-key
105
+
106
+ # AWS (Optional - for production deployments)
107
+ AWS_REGION=us-east-1
108
+ AWS_ACCESS_KEY_ID=your-access-key
109
+ AWS_SECRET_ACCESS_KEY=your-secret-key
110
+ ```
111
+
112
+ ## Core Components
113
+
114
+ ### 1. Integrations (`/integrations`)
115
+
116
+ The heart of the framework - manages integration lifecycle and business logic.
117
+
118
+ **Key Classes:**
119
+ - `IntegrationBase` - Base class for all integrations
120
+ - `Integration` - Domain aggregate using Proxy pattern
121
+ - Use cases: `CreateIntegration`, `UpdateIntegration`, `DeleteIntegration`
122
+
123
+ **Usage:**
44
124
  ```javascript
45
- const { encrypt, decrypt } = require('@friggframework/core/encrypt');
46
- const { logInfo } = require('@friggframework/core/logs');
125
+ const { IntegrationBase } = require('@friggframework/core');
47
126
 
48
- const secret = 'mySecret';
49
- const encrypted = encrypt(secret);
50
- const decrypted = decrypt(encrypted);
127
+ class SlackHubSpotSync extends IntegrationBase {
128
+ static Definition = {
129
+ name: 'slack-hubspot-sync',
130
+ version: '2.1.0',
131
+ modules: {
132
+ slack: 'slack',
133
+ hubspot: 'hubspot'
134
+ }
135
+ };
51
136
 
52
- logInfo(`Encrypted: ${encrypted}`);
53
- logInfo(`Decrypted: ${decrypted}`);
137
+ async onCreate({ integrationId }) {
138
+ // Setup webhooks, initial sync, etc.
139
+ await this.slack.createWebhook(process.env.WEBHOOK_URL);
140
+ await this.hubspot.setupContactSync();
141
+ await super.onCreate({ integrationId });
142
+ }
143
+ }
54
144
  ```
55
145
 
56
- ## Modules
146
+ ### 3. Database (`/database`)
57
147
 
58
- The frigg-core package is organized into several modules:
148
+ MongoDB integration with Mongoose ODM.
59
149
 
60
- - **Associations**: @friggframework/core/associations
61
- - **Database**: @friggframework/core/database
62
- - **Encryption**: @friggframework/core/encrypt
63
- - **Errors**: @friggframework/core/errors
64
- - **Integrations**: @friggframework/core/integrations
65
- - **Lambda**: @friggframework/core/lambda
66
- - **Logs**: @friggframework/core/logs
67
- - **Module Plugin**: @friggframework/core/module-plugin
68
- - **Syncs**: @friggframework/core/syncs
69
- - **Infrastructure**: @friggframework/core/infrastructure
150
+ **Key Components:**
151
+ - Connection management
152
+ - Pre-built models (User, Integration, Credential, etc.)
153
+ - Schema definitions
70
154
 
155
+ **Usage:**
156
+ ```javascript
157
+ const {
158
+ connectToDatabase,
159
+ IntegrationModel,
160
+ UserModel
161
+ } = require('@friggframework/core');
71
162
 
72
- Each module provides specific functionality and can be imported individually as needed.
163
+ await connectToDatabase();
73
164
 
74
- ## Contributing
165
+ // Query integrations
166
+ const userIntegrations = await IntegrationModel.find({
167
+ userId: 'user-123',
168
+ status: 'ENABLED'
169
+ });
75
170
 
76
- We welcome contributions from the community! Please read our contributing guide to get started. Make sure to follow our code of conduct and use the provided pull request template.
171
+ // Create user
172
+ const user = new UserModel({
173
+ email: 'user@example.com',
174
+ name: 'John Doe'
175
+ });
176
+ await user.save();
177
+ ```
178
+
179
+ ### 4. Encryption (`/encrypt`)
180
+
181
+ AES-256-GCM encryption for sensitive data.
182
+
183
+ **Usage:**
184
+ ```javascript
185
+ const { Encrypt, Cryptor } = require('@friggframework/core');
186
+
187
+ // Simple encryption
188
+ const encrypted = Encrypt.encrypt('sensitive-data');
189
+ const decrypted = Encrypt.decrypt(encrypted);
190
+
191
+ // Advanced encryption with custom key
192
+ const cryptor = new Cryptor(process.env.CUSTOM_KEY);
193
+ const secureData = cryptor.encrypt(JSON.stringify({
194
+ accessToken: 'oauth-token',
195
+ refreshToken: 'refresh-token'
196
+ }));
197
+ ```
198
+
199
+ ### 4b. Telemetry & Usage Tracking (`/telemetry`, `/usage`)
200
+
201
+ Vendor-neutral OpenTelemetry observability plus durable, per-integration usage
202
+ counters (ADR-011). No-op by default (zero cold-start cost); framework seams are
203
+ auto-instrumented so integrations get handler/API-module/webhook metrics for free.
204
+
205
+ **Usage:**
206
+ ```javascript
207
+ // App definition — turn on export + declare a North Star:
208
+ const Definition = {
209
+ name: 'my-app',
210
+ telemetry: {
211
+ exporter: { type: 'otlp', endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT },
212
+ northStar: { default: { name: 'records.synced' } },
213
+ },
214
+ };
215
+
216
+ // Integration code — custom metrics/spans (this.telemetry is auto-tagged):
217
+ await this.telemetry.span('delta_sync', async () => {
218
+ this.telemetry.count('records.synced', batch.length, { entity: 'contact' });
219
+ });
220
+
221
+ // Declare which usage counters an integration reports (opts into reporting):
222
+ class HubSpotIntegration extends IntegrationBase {
223
+ static Definition = {
224
+ name: 'hubspot',
225
+ usage: { canonical: ['records.synced', 'api.requests'] },
226
+ };
227
+ }
228
+
229
+ // Read the durable usage store (never an APM):
230
+ const frigg = createFriggCommands({ integrationClass: HubSpotIntegration });
231
+ await frigg.usage.getTotalsByDimension({ metric: 'records.synced', groupBy: 'integrationType' });
232
+ ```
233
+
234
+ **See:** [`telemetry/README.md`](telemetry/README.md) for the full guide
235
+ (exporters, custom metrics, the Usage-Counter contract, North Star, the plugin
236
+ tap, cardinality rules, and caveats) and [`usage/README.md`](usage/README.md) for
237
+ the store internals.
238
+
239
+ ### 5. Error Handling (`/errors`)
240
+
241
+ Standardized error types with proper HTTP status codes.
242
+
243
+ **Usage:**
244
+ ```javascript
245
+ const {
246
+ BaseError,
247
+ RequiredPropertyError,
248
+ FetchError
249
+ } = require('@friggframework/core');
250
+
251
+ // Custom business logic error
252
+ throw new RequiredPropertyError('userId is required');
253
+
254
+ // API communication error. The message is built from the method, the
255
+ // sanitized URL and the status; the response body stays off the message.
256
+ throw await FetchError.create({
257
+ resource: 'https://api.example.com/contacts',
258
+ init: { method: 'GET' },
259
+ response,
260
+ });
261
+
262
+ // Base error with custom properties
263
+ throw new BaseError('Integration failed', {
264
+ integrationId: 'int-123',
265
+ errorCode: 'SYNC_FAILED'
266
+ });
267
+ ```
268
+
269
+ ### 6. Logging (`/logs`)
270
+
271
+ One redacted JSON record per line to stdout, with the correlation ids of
272
+ ADR-011. See the [Logging guide](../../docs/guides/LOGGING.md).
273
+
274
+ **Usage:**
275
+ ```javascript
276
+ // In an integration or an API module
277
+ this.logger.info('Contact batch started', {
278
+ eventName: 'integration.hubspot.batch_started',
279
+ batchSize: contacts.length,
280
+ });
281
+
282
+ // In core code
283
+ const { getLogger } = require('@friggframework/core');
284
+ const log = getLogger('frigg.core.sync');
285
+ log.warn('Sync skipped', { eventName: 'frigg.core.sync.skipped', processId });
286
+ ```
287
+
288
+ `debug`, `initDebugLog` and `flushDebugLog` are deprecated shims.
289
+
290
+ ### 7. User Management (`/user`)
291
+
292
+ Comprehensive user authentication and authorization system supporting both individual and organizational users.
293
+
294
+ **Key Classes:**
295
+ - `User` - Domain aggregate for user entities
296
+ - `UserRepository` - Data access for user operations
297
+ - Use cases: `LoginUser`, `CreateIndividualUser`, `CreateOrganizationUser`, `GetUserFromBearerToken`
298
+
299
+ **User Types:**
300
+ - **Individual Users**: Personal accounts with email/username authentication
301
+ - **Organization Users**: Business accounts with organization-level access
302
+ - **Hybrid Mode**: Support for both user types simultaneously
303
+
304
+ **Authentication Methods:**
305
+ - **Password-based**: Traditional username/password authentication
306
+ - **Token-based**: Bearer token authentication with session management
307
+ - **App-based**: External app user ID authentication (passwordless)
308
+
309
+ **Usage:**
310
+ ```javascript
311
+ const {
312
+ LoginUser,
313
+ CreateIndividualUser,
314
+ GetUserFromBearerToken,
315
+ UserRepository
316
+ } = require('@friggframework/core');
317
+
318
+ // Configure user behavior in app definition
319
+ const userConfig = {
320
+ usePassword: true,
321
+ primary: 'individual', // or 'organization'
322
+ individualUserRequired: true,
323
+ organizationUserRequired: false
324
+ };
325
+
326
+ const userRepository = new UserRepository({ userConfig });
327
+
328
+ // Create individual user
329
+ const createUser = new CreateIndividualUser({ userRepository, userConfig });
330
+ const user = await createUser.execute({
331
+ email: 'user@example.com',
332
+ username: 'john_doe',
333
+ password: 'secure_password',
334
+ appUserId: 'external_user_123' // Optional external reference
335
+ });
336
+
337
+ // Login user
338
+ const loginUser = new LoginUser({ userRepository, userConfig });
339
+ const authenticatedUser = await loginUser.execute({
340
+ username: 'john_doe',
341
+ password: 'secure_password'
342
+ });
343
+
344
+ // Token-based authentication
345
+ const getUserFromToken = new GetUserFromBearerToken({ userRepository, userConfig });
346
+ const user = await getUserFromToken.execute('Bearer eyJhbGciOiJIUzI1NiIs...');
347
+
348
+ // Access user properties
349
+ console.log('User ID:', user.getId());
350
+ console.log('Primary user:', user.getPrimaryUser());
351
+ console.log('Individual user:', user.getIndividualUser());
352
+ console.log('Organization user:', user.getOrganizationUser());
353
+ ```
354
+
355
+ ### 8. Lambda Utilities (`/lambda`)
356
+
357
+ AWS Lambda-specific utilities and helpers.
358
+
359
+ **Usage:**
360
+ ```javascript
361
+ const { TimeoutCatcher } = require('@friggframework/core');
362
+
363
+ exports.handler = async (event, context) => {
364
+ const timeoutCatcher = new TimeoutCatcher(context);
365
+
366
+ try {
367
+ // Long-running integration process
368
+ const result = await processIntegrationSync(event);
369
+ return { statusCode: 200, body: JSON.stringify(result) };
370
+ } catch (error) {
371
+ if (timeoutCatcher.isNearTimeout()) {
372
+ // Handle graceful shutdown
373
+ await saveProgressState(event);
374
+ return { statusCode: 202, body: 'Processing continues...' };
375
+ }
376
+ throw error;
377
+ }
378
+ };
379
+ ```
380
+
381
+ ## User Management & Behavior
382
+
383
+ Frigg Core provides a flexible user management system that supports various authentication patterns and user types. The system is designed around the concept of **Individual Users** (personal accounts) and **Organization Users** (business accounts), with configurable authentication methods.
384
+
385
+ ### User Configuration
386
+
387
+ User behavior is configured in the app definition, allowing you to customize authentication requirements:
388
+
389
+ ```javascript
390
+ // App Definition with User Configuration
391
+ const appDefinition = {
392
+ integrations: [HubSpotIntegration],
393
+ user: {
394
+ usePassword: true, // Enable password authentication
395
+ primary: 'individual', // Primary user type: 'individual' or 'organization'
396
+ organizationUserRequired: true, // Require organization user
397
+ individualUserRequired: true, // Require individual user
398
+ }
399
+ };
400
+ ```
401
+
402
+ ### User Domain Model
403
+
404
+ The `User` class provides a rich domain model with behavior:
405
+
406
+ ```javascript
407
+ const { User } = require('@friggframework/core');
408
+
409
+ // User instance methods
410
+ const user = new User(individualUser, organizationUser, usePassword, primary);
411
+
412
+ // Access methods
413
+ user.getId() // Get primary user ID
414
+ user.getPrimaryUser() // Get primary user based on config
415
+ user.getIndividualUser() // Get individual user
416
+ user.getOrganizationUser() // Get organization user
417
+
418
+ // Validation methods
419
+ user.isPasswordRequired() // Check if password is required
420
+ user.isPasswordValid(password) // Validate password
421
+ user.isIndividualUserRequired() // Check individual user requirement
422
+ user.isOrganizationUserRequired() // Check organization user requirement
423
+
424
+ // Configuration methods
425
+ user.setIndividualUser(individualUser)
426
+ user.setOrganizationUser(organizationUser)
427
+ ```
428
+
429
+ ### Database Models
430
+
431
+ The user system uses MongoDB with Mongoose for data persistence:
432
+
433
+ ```javascript
434
+ // Individual User Schema
435
+ {
436
+ email: String,
437
+ username: { type: String, unique: true },
438
+ hashword: String, // Encrypted password
439
+ appUserId: String, // External app reference
440
+ organizationUser: ObjectId // Reference to organization
441
+ }
442
+
443
+ // Organization User Schema
444
+ {
445
+ name: String,
446
+ appOrgId: String, // External organization reference
447
+ domain: String,
448
+ settings: Object
449
+ }
450
+
451
+ // Session Token Schema
452
+ {
453
+ user: ObjectId, // Reference to user
454
+ token: String, // Encrypted token
455
+ expires: Date,
456
+ created: Date
457
+ }
458
+ ```
459
+
460
+ ### Security Features
461
+
462
+ - **Password Hashing**: Uses bcrypt with configurable salt rounds
463
+ - **Token Management**: Secure session tokens with expiration
464
+ - **Unique Constraints**: Enforced username and email uniqueness
465
+ - **External References**: Support for external app user/org IDs
466
+ - **Flexible Authentication**: Multiple authentication methods
467
+
468
+ ## Hexagonal Architecture
469
+
470
+ ### Use Case Pattern
471
+
472
+ Each business operation is encapsulated in a use case class:
473
+
474
+ ```javascript
475
+ class UpdateIntegrationStatus {
476
+ constructor({ integrationRepository }) {
477
+ this.integrationRepository = integrationRepository;
478
+ }
479
+
480
+ async execute(integrationId, newStatus) {
481
+ // Business logic validation
482
+ if (!['ENABLED', 'DISABLED', 'ERROR'].includes(newStatus)) {
483
+ throw new Error('Invalid status');
484
+ }
485
+
486
+ // Domain operation
487
+ const integration = await this.integrationRepository.findById(integrationId);
488
+ if (!integration) {
489
+ throw new Error('Integration not found');
490
+ }
491
+
492
+ // Update and persist
493
+ integration.status = newStatus;
494
+ integration.updatedAt = new Date();
495
+
496
+ return await this.integrationRepository.save(integration);
497
+ }
498
+ }
499
+ ```
500
+
501
+ ### Repository Pattern
502
+
503
+ Data access is abstracted through repositories:
504
+
505
+ ```javascript
506
+ class IntegrationRepository {
507
+ async findById(id) {
508
+ return await IntegrationModel.findById(id);
509
+ }
510
+
511
+ async findByUserId(userId) {
512
+ return await IntegrationModel.find({ userId, deletedAt: null });
513
+ }
514
+
515
+ async save(integration) {
516
+ return await integration.save();
517
+ }
518
+
519
+ async createIntegration(entities, userId, config) {
520
+ const integration = new IntegrationModel({
521
+ entitiesIds: entities,
522
+ userId,
523
+ config,
524
+ status: 'NEW',
525
+ createdAt: new Date()
526
+ });
527
+ return await integration.save();
528
+ }
529
+ }
530
+ ```
531
+
532
+ ### Domain Aggregates
533
+
534
+ Complex business objects with behavior:
535
+
536
+ ```javascript
537
+ const Integration = new Proxy(class {}, {
538
+ construct(target, args) {
539
+ const [params] = args;
540
+ const instance = new params.integrationClass(params);
541
+
542
+ // Attach domain properties
543
+ Object.assign(instance, {
544
+ id: params.id,
545
+ userId: params.userId,
546
+ entities: params.entities,
547
+ config: params.config,
548
+ status: params.status,
549
+ modules: params.modules
550
+ });
551
+
552
+ return instance;
553
+ }
554
+ });
555
+ ```
556
+
557
+ ## Usage Examples
558
+
559
+ ### Real-World HubSpot Integration Example
560
+
561
+ Here's a complete, production-ready HubSpot integration that demonstrates advanced Frigg features:
562
+
563
+ ```javascript
564
+ const {
565
+ get,
566
+ IntegrationBase,
567
+ WebsocketConnection,
568
+ } = require('@friggframework/core');
569
+ const FriggConstants = require('../utils/constants');
570
+ const hubspot = require('@friggframework/api-module-hubspot');
571
+ const testRouter = require('../testRouter');
572
+ const extensions = require('../extensions');
573
+
574
+ class HubSpotIntegration extends IntegrationBase {
575
+ static Definition = {
576
+ name: 'hubspot',
577
+ version: '1.0.0',
578
+ supportedVersions: ['1.0.0'],
579
+ hasUserConfig: true,
580
+
581
+ display: {
582
+ label: 'HubSpot',
583
+ description: hubspot.Config.description,
584
+ category: 'Sales & CRM, Marketing',
585
+ detailsUrl: 'https://hubspot.com',
586
+ icon: hubspot.Config.logoUrl,
587
+ },
588
+ modules: {
589
+ hubspot: {
590
+ definition: hubspot.Definition,
591
+ },
592
+ },
593
+ // Express routes for webhook endpoints and custom APIs
594
+ routes: [
595
+ {
596
+ path: '/hubspot/webhooks',
597
+ method: 'POST',
598
+ event: 'HUBSPOT_WEBHOOK',
599
+ },
600
+ testRouter,
601
+ ],
602
+ };
603
+
604
+ constructor() {
605
+ super();
606
+
607
+ // Define event handlers for various integration actions
608
+ this.events = {
609
+ // Webhook handler with real-time WebSocket broadcasting
610
+ HUBSPOT_WEBHOOK: {
611
+ handler: async ({ data, context }) => {
612
+ console.log('Received HubSpot webhook:', data);
613
+
614
+ // Broadcast to all connected WebSocket clients
615
+ const activeConnections = await WebsocketConnection.getActiveConnections();
616
+ const message = JSON.stringify({
617
+ type: 'HUBSPOT_WEBHOOK',
618
+ data,
619
+ });
620
+
621
+ activeConnections.forEach((connection) => {
622
+ connection.send(message);
623
+ });
624
+ },
625
+ },
626
+
627
+ // User action: Get sample data with formatted table output
628
+ [FriggConstants.defaultEvents.GET_SAMPLE_DATA]: {
629
+ type: FriggConstants.eventTypes.USER_ACTION,
630
+ handler: this.getSampleData,
631
+ title: 'Get Sample Data',
632
+ description: 'Get sample data from HubSpot and display in a formatted table',
633
+ userActionType: 'QUICK_ACTION',
634
+ },
635
+
636
+ // User action: List available objects
637
+ GET_OBJECT_LIST: {
638
+ type: FriggConstants.eventTypes.USER_ACTION,
639
+ handler: this.getObjectList,
640
+ title: 'Get Object List',
641
+ description: 'Get list of available HubSpot objects',
642
+ userActionType: 'DATA',
643
+ },
644
+
645
+ // User action: Create records with dynamic forms
646
+ CREATE_RECORD: {
647
+ type: FriggConstants.eventTypes.USER_ACTION,
648
+ handler: this.createRecord,
649
+ title: 'Create Record',
650
+ description: 'Create a new record in HubSpot',
651
+ userActionType: 'DATA',
652
+ },
653
+ };
654
+
655
+ // Extension system for modular functionality
656
+ this.extensions = {
657
+ hubspotWebhooks: {
658
+ extension: extensions.hubspotWebhooks,
659
+ handlers: {
660
+ WEBHOOK_EVENT: this.handleWebhookEvent,
661
+ },
662
+ },
663
+ };
664
+ }
665
+
666
+ // Business logic: Fetch and format sample data
667
+ async getSampleData({ objectName }) {
668
+ let res;
669
+ switch (objectName) {
670
+ case 'deals':
671
+ res = await this.hubspot.api.searchDeals({
672
+ properties: ['dealname,amount,closedate'],
673
+ });
674
+ break;
675
+ case 'contacts':
676
+ res = await this.hubspot.api.listContacts({
677
+ after: 0,
678
+ properties: 'firstname,lastname,email',
679
+ });
680
+ break;
681
+ case 'companies':
682
+ res = await this.hubspot.api.searchCompanies({
683
+ properties: ['name,website,email'],
684
+ limit: 100,
685
+ });
686
+ break;
687
+ default:
688
+ throw new Error(`Unsupported object type: ${objectName}`);
689
+ }
690
+
691
+ const portalId = this.hubspot.entity.externalId;
692
+
693
+ // Format data with HubSpot record links
694
+ const formatted = res.results.map((item) => {
695
+ const formattedItem = {
696
+ linkToRecord: `https://app.hubspot.com/contacts/${portalId}/${objectName}/${item.id}/`,
697
+ id: item.id,
698
+ };
699
+
700
+ // Clean and format properties
701
+ for (const [key, value] of Object.entries(item.properties)) {
702
+ if (value !== null && value !== undefined && value !== '') {
703
+ formattedItem[key] = value;
704
+ }
705
+ }
706
+ delete formattedItem.hs_object_id;
707
+
708
+ return formattedItem;
709
+ });
710
+
711
+ return { label: objectName, data: formatted };
712
+ }
713
+
714
+ // Return available HubSpot object types
715
+ async getObjectList() {
716
+ return [
717
+ { key: 'deals', label: 'Deals' },
718
+ { key: 'contacts', label: 'Contacts' },
719
+ { key: 'companies', label: 'Companies' },
720
+ ];
721
+ }
722
+
723
+ // Create records based on object type
724
+ async createRecord(args) {
725
+ let res;
726
+ const objectType = args.objectType;
727
+ delete args.objectType;
728
+
729
+ switch (objectType.toLowerCase()) {
730
+ case 'deal':
731
+ res = await this.hubspot.api.createDeal({ ...args });
732
+ break;
733
+ case 'company':
734
+ res = await this.hubspot.api.createCompany({ ...args });
735
+ break;
736
+ case 'contact':
737
+ res = await this.hubspot.api.createContact({ ...args });
738
+ break;
739
+ default:
740
+ throw new Error(`Unsupported object type: ${objectType}`);
741
+ }
742
+ return { data: res };
743
+ }
744
+
745
+ // Dynamic form generation based on action and context
746
+ async getActionOptions({ actionId, data }) {
747
+ switch (actionId) {
748
+ case 'CREATE_RECORD':
749
+ let jsonSchema = {
750
+ type: 'object',
751
+ properties: {
752
+ objectType: {
753
+ type: 'string',
754
+ title: 'Object Type',
755
+ },
756
+ },
757
+ required: [],
758
+ };
759
+
760
+ let uiSchema = {
761
+ type: 'HorizontalLayout',
762
+ elements: [
763
+ {
764
+ type: 'Control',
765
+ scope: '#/properties/objectType',
766
+ rule: { effect: 'HIDE', condition: {} },
767
+ },
768
+ ],
769
+ };
770
+
771
+ // Generate form fields based on object type
772
+ switch (data.name.toLowerCase()) {
773
+ case 'deal':
774
+ jsonSchema.properties = {
775
+ ...jsonSchema.properties,
776
+ dealname: { type: 'string', title: 'Deal Name' },
777
+ amount: { type: 'number', title: 'Amount' },
778
+ };
779
+ jsonSchema.required = ['dealname', 'amount'];
780
+ uiSchema.elements.push(
781
+ { type: 'Control', scope: '#/properties/dealname' },
782
+ { type: 'Control', scope: '#/properties/amount' }
783
+ );
784
+ break;
785
+
786
+ case 'company':
787
+ jsonSchema.properties = {
788
+ ...jsonSchema.properties,
789
+ name: { type: 'string', title: 'Company Name' },
790
+ website: { type: 'string', title: 'Website URL' },
791
+ };
792
+ jsonSchema.required = ['name', 'website'];
793
+ uiSchema.elements.push(
794
+ { type: 'Control', scope: '#/properties/name' },
795
+ { type: 'Control', scope: '#/properties/website' }
796
+ );
797
+ break;
798
+
799
+ case 'contact':
800
+ jsonSchema.properties = {
801
+ ...jsonSchema.properties,
802
+ firstname: { type: 'string', title: 'First Name' },
803
+ lastname: { type: 'string', title: 'Last Name' },
804
+ email: { type: 'string', title: 'Email Address' },
805
+ };
806
+ jsonSchema.required = ['firstname', 'lastname', 'email'];
807
+ uiSchema.elements.push(
808
+ { type: 'Control', scope: '#/properties/firstname' },
809
+ { type: 'Control', scope: '#/properties/lastname' },
810
+ { type: 'Control', scope: '#/properties/email' }
811
+ );
812
+ break;
813
+
814
+ default:
815
+ throw new Error(`Unsupported object type: ${data.name}`);
816
+ }
817
+
818
+ return {
819
+ jsonSchema,
820
+ uiSchema,
821
+ data: { objectType: data.name },
822
+ };
823
+ }
824
+ return null;
825
+ }
826
+
827
+ async getConfigOptions() {
828
+ // Return configuration options for the integration
829
+ return {};
830
+ }
831
+ }
832
+
833
+ module.exports = HubSpotIntegration;
834
+ ```
835
+
836
+ index.js
837
+ ```js
838
+ const HubSpotIntegration = require('./src/integrations/HubSpotIntegration');
839
+
840
+ const appDefinition = {
841
+ integrations: [
842
+ HubSpotIntegration,
843
+ ],
844
+ user: {
845
+ usePassword: true,
846
+ primary: 'individual',
847
+ organizationUserRequired: true,
848
+ individualUserRequired: true,
849
+ }
850
+ }
851
+
852
+ module.exports = {
853
+ Definition: appDefinition,
854
+ }
855
+
856
+ ```
857
+
858
+
859
+ ### Key Features Demonstrated
860
+
861
+ This real-world example showcases:
862
+
863
+ **🔄 Webhook Integration**: Real-time event processing with WebSocket broadcasting
864
+ **📊 User Actions**: Interactive data operations with dynamic form generation
865
+ **🎯 API Module Integration**: Direct use of `@friggframework/api-module-hubspot`
866
+ **🛠 Extension System**: Modular functionality through extensions
867
+ **📝 Dynamic Forms**: JSON Schema-based form generation for different object types
868
+ **🔗 Deep Linking**: Direct links to HubSpot records in formatted data
869
+ **⚡ Real-time Updates**: WebSocket connections for live data streaming
870
+
871
+
872
+ ## Testing
873
+
874
+ ### Running Tests
875
+
876
+ ```bash
877
+ # Run all tests
878
+ npm test
879
+
880
+ # Run specific test file
881
+ npm test -- --testPathPattern="integration.test.js"
882
+ ```
883
+
884
+ ### Test Structure
885
+
886
+ The core package uses a comprehensive testing approach:
887
+
888
+ ```javascript
889
+ // Example test structure
890
+ describe('CreateIntegration Use-Case', () => {
891
+ let integrationRepository;
892
+ let moduleFactory;
893
+ let useCase;
894
+
895
+ beforeEach(() => {
896
+ integrationRepository = new TestIntegrationRepository();
897
+ moduleFactory = new TestModuleFactory();
898
+ useCase = new CreateIntegration({
899
+ integrationRepository,
900
+ integrationClasses: [TestIntegration],
901
+ moduleFactory
902
+ });
903
+ });
904
+
905
+ describe('happy path', () => {
906
+ it('creates an integration and returns DTO', async () => {
907
+ const result = await useCase.execute(['entity-1'], 'user-1', { type: 'test' });
908
+ expect(result.id).toBeDefined();
909
+ expect(result.status).toBe('NEW');
910
+ });
911
+ });
912
+
913
+ describe('error cases', () => {
914
+ it('throws error for unknown integration type', async () => {
915
+ await expect(useCase.execute(['entity-1'], 'user-1', { type: 'unknown' }))
916
+ .rejects.toThrow('No integration class found for type: unknown');
917
+ });
918
+ });
919
+ });
920
+ ```
921
+
922
+ ### Test Doubles
923
+
924
+ The framework provides test doubles for external dependencies:
925
+
926
+ ```javascript
927
+ const { TestIntegrationRepository, TestModuleFactory } = require('@friggframework/core/test');
928
+
929
+ // Mock repository for testing
930
+ const testRepo = new TestIntegrationRepository();
931
+ testRepo.addMockIntegration({ id: 'test-123', userId: 'user-1' });
932
+
933
+ // Mock module factory
934
+ const testFactory = new TestModuleFactory();
935
+ testFactory.addMockModule('hubspot', mockHubSpotModule);
936
+ ```
937
+
938
+ ## Development
939
+
940
+ ### Project Structure
941
+
942
+ ```
943
+ packages/core/
944
+ ├── integrations/ # Integration domain logic
945
+ │ ├── use-cases/ # Business use cases
946
+ │ ├── tests/ # Integration tests
947
+ │ └── integration-base.js # Base integration class
948
+ ├── modules/ # API module system
949
+ │ ├── requester/ # HTTP clients
950
+ │ └── use-cases/ # Module management
951
+ ├── database/ # Data persistence
952
+ ├── encrypt/ # Encryption utilities
953
+ ├── errors/ # Error definitions
954
+ ├── logs/ # Logging system
955
+ └── lambda/ # Serverless utilities
956
+ ```
957
+
958
+ ### Adding New Components
959
+
960
+ 1. **Create the component**: Follow the established patterns
961
+ 2. **Add tests**: Comprehensive test coverage required
962
+ 3. **Export from index.js**: Make it available to consumers
963
+ 4. **Update documentation**: Keep README current
964
+
965
+ ### Code Style
966
+
967
+ ```bash
968
+ # Format code
969
+ npm run lint:fix
970
+
971
+ # Check linting
972
+ npm run lint
973
+ ```
974
+
975
+ ## API Reference
976
+
977
+ ### Core Exports
978
+
979
+ ```javascript
980
+ const {
981
+ // Integrations
982
+ IntegrationBase,
983
+ IntegrationModel,
984
+ CreateIntegration,
985
+ UpdateIntegration,
986
+ DeleteIntegration,
987
+
988
+ // Modules
989
+ OAuth2Requester,
990
+ ApiKeyRequester,
991
+ Credential,
992
+ Entity,
993
+ // Database
994
+ connectToDatabase,
995
+ mongoose,
996
+ UserModel,
997
+
998
+ // Utilities
999
+ Encrypt,
1000
+ Cryptor,
1001
+ BaseError,
1002
+ debug,
1003
+ TimeoutCatcher
1004
+ } = require('@friggframework/core');
1005
+ ```
1006
+
1007
+ ### Environment Configuration
1008
+
1009
+ | Variable | Required | Description |
1010
+ |----------|----------|-------------|
1011
+ | `MONGO_URI` | Yes | MongoDB connection string |
1012
+ | `FRIGG_ENCRYPTION_KEY` | Yes | 256-bit encryption key |
1013
+ | `AWS_REGION` | No | AWS region for services |
1014
+ | `FRIGG_LOG_LEVEL` | No | Minimum log level: `TRACE`, `DEBUG`, `INFO` (default), `WARN`, `ERROR`, `FATAL`. Local runs default to `DEBUG` |
77
1015
 
78
1016
  ## License
79
1017
 
80
- This project is licensed under the MIT License. See the LICENSE.md file for details.
1018
+ This project is licensed under the MIT License - see the [LICENSE.md](../../LICENSE.md) file for details.
81
1019
 
82
1020
  ---
83
- Thank you for using Frigg Core! If you have any questions or need further assistance, feel free to reach out to our community on Slack or check out our GitHub issues page.
1021
+
1022
+ ## Support
1023
+
1024
+ - 📖 [Documentation](https://docs.friggframework.org)
1025
+ - 💬 [Community Slack](https://friggframework.slack.com)
1026
+ - 🐛 [Issue Tracker](https://github.com/friggframework/frigg/issues)
1027
+ - 📧 [Email Support](mailto:support@friggframework.org)
1028
+
1029
+ Built with ❤️ by the Frigg Framework team.