@friggframework/core 2.0.0-next.11 → 2.0.0-next.110

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