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

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 +184 -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 +91 -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
@@ -0,0 +1,103 @@
1
+ const { findNearestBackendPackageJson } = require('@friggframework/core/utils');
2
+ const path = require('node:path');
3
+ const fs = require('fs-extra');
4
+ const { resolveTelemetryConfig } = require('../telemetry/telemetry-config');
5
+ const { registerDeniedKeys } = require('../logs/denied-keys');
6
+ const {
7
+ extractCredentialFieldsFromModules,
8
+ } = require('../database/encryption/encryption-schema-registry');
9
+ const {
10
+ getModulesDefinitionFromIntegrationClasses,
11
+ } = require('../integrations/utils/map-integration-dto');
12
+
13
+ // Handlers without a database never load the encryption registry, so the
14
+ // credential leaf keys are registered for redaction here too.
15
+ function registerCredentialLogKeys(appDefinition, integrations) {
16
+ try {
17
+ extractCredentialFieldsFromModules(
18
+ getModulesDefinitionFromIntegrationClasses(integrations)
19
+ );
20
+ } catch {
21
+ // An odd integration shape must not stop the app from loading.
22
+ }
23
+ const schema = appDefinition.encryption?.schema;
24
+ if (schema && typeof schema === 'object') {
25
+ for (const config of Object.values(schema)) {
26
+ registerDeniedKeys(config?.fields);
27
+ }
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Loads the App definition from the nearest backend package
33
+ * @function loadAppDefinition
34
+ * @description Searches for the nearest backend package.json, loads the corresponding index.js file,
35
+ * and extracts the application definition containing integrations and user configuration.
36
+ * @returns {{integrations: Array<object>, userConfig: object | null, adminScripts: Array<object>, reports: Array<object>, admin: object, telemetry: object, logging: object | null}} An object containing the application definition.
37
+ * @throws {Error} Throws error if backend package.json cannot be found.
38
+ * @throws {Error} Throws error if index.js file cannot be found in the backend directory.
39
+ * @example
40
+ * const { integrations, userConfig, telemetry } = loadAppDefinition();
41
+ * console.log(`Found ${integrations.length} integrations`);
42
+ */
43
+ function loadAppDefinition() {
44
+ const backendPath = findNearestBackendPackageJson();
45
+ if (!backendPath) {
46
+ throw new Error('Could not find backend package.json');
47
+ }
48
+
49
+ const backendDir = path.dirname(backendPath);
50
+ const backendFilePath = path.join(backendDir, 'index.js');
51
+ if (!fs.existsSync(backendFilePath)) {
52
+ throw new Error('Could not find index.js');
53
+ }
54
+
55
+ const backendJsFile = require(backendFilePath);
56
+ const appDefinition = backendJsFile.Definition;
57
+
58
+ const {
59
+ integrations = [],
60
+ user: userConfig = null,
61
+ adminScripts = [],
62
+ reports = [],
63
+ admin = {},
64
+ logging = null,
65
+ } = appDefinition;
66
+
67
+ registerCredentialLogKeys(appDefinition, integrations);
68
+
69
+ // Degrade consistently: an invalid telemetry block must never take down a
70
+ // router bundle that loads the app definition at module scope (telemetry is
71
+ // never allowed to break a handler). The singletons apply the same
72
+ // fall-back, so all consumers behave identically.
73
+ let telemetry;
74
+ try {
75
+ telemetry = resolveTelemetryConfig(appDefinition);
76
+ } catch (error) {
77
+ console.warn(
78
+ `[Frigg][telemetry] invalid telemetry config, defaulting to disabled: ${
79
+ error && error.message
80
+ }`
81
+ );
82
+ telemetry = {
83
+ exporter: { type: 'none' },
84
+ northStar: null,
85
+ sampleRatio: 1,
86
+ subscribers: [],
87
+ };
88
+ }
89
+
90
+ return {
91
+ integrations,
92
+ userConfig,
93
+ adminScripts,
94
+ reports,
95
+ admin,
96
+ telemetry,
97
+ logging,
98
+ };
99
+ }
100
+
101
+ module.exports = {
102
+ loadAppDefinition,
103
+ };
@@ -1,11 +1,14 @@
1
- const { createHandler, flushDebugLog } = require('@friggframework/core');
1
+ const { createHandler } = require('@friggframework/core');
2
+ const { getLogger } = require('../logs');
3
+ const { summarizeExpressRequest } = require('../logs/summarize-event');
2
4
  const express = require('express');
3
5
  const bodyParser = require('body-parser');
4
6
  const cors = require('cors');
5
7
  const Boom = require('@hapi/boom');
6
- const loadUserManager = require('./routers/middleware/loadUser');
7
8
  const serverlessHttp = require('serverless-http');
8
9
 
10
+ const log = getLogger('frigg.http');
11
+
9
12
  const createApp = (applyMiddleware) => {
10
13
  const app = express();
11
14
 
@@ -14,15 +17,15 @@ const createApp = (applyMiddleware) => {
14
17
  app.use(
15
18
  cors({
16
19
  origin: '*',
20
+ allowedHeaders: '*',
21
+ methods: '*',
17
22
  credentials: true,
18
23
  })
19
24
  );
20
25
 
21
- app.use(loadUserManager);
22
-
23
26
  if (applyMiddleware) applyMiddleware(app);
24
27
 
25
- // Handle sending error response and logging server errors to console
28
+ // The express boundary: send the error response and log it one time.
26
29
  app.use((err, req, res, next) => {
27
30
  const boomError = err.isBoom ? err : Boom.boomify(err);
28
31
  const {
@@ -30,9 +33,20 @@ const createApp = (applyMiddleware) => {
30
33
  } = boomError;
31
34
 
32
35
  if (statusCode >= 500) {
33
- flushDebugLog(boomError);
36
+ log.error('Request failed', {
37
+ eventName: 'frigg.http.request_failed',
38
+ statusCode,
39
+ invocation: summarizeExpressRequest(req),
40
+ error: boomError,
41
+ });
34
42
  res.status(statusCode).json({ error: 'Internal Server Error' });
35
43
  } else {
44
+ // A client error needs no stack; the logger scrubs the reason.
45
+ log.warn('Request rejected', {
46
+ eventName: 'frigg.http.request_rejected',
47
+ statusCode,
48
+ reason: boomError.message,
49
+ });
36
50
  res.status(statusCode).json({ error: err.message });
37
51
  }
38
52
  });
@@ -1,38 +1,49 @@
1
- const { createFriggBackend, Worker } = require('@friggframework/core');
2
- const {
3
- findNearestBackendPackageJson,
4
- } = require('../../devtools/frigg-cli/utils/backend-path');
5
- const path = require('node:path');
6
- const fs = require('fs-extra');
7
-
8
- const backendPath = findNearestBackendPackageJson();
9
- if (!backendPath) {
10
- throw new Error('Could not find backend package.json');
11
- }
12
-
13
- const backendDir = path.dirname(backendPath);
14
- const backendFilePath = path.join(backendDir, 'index.js');
15
- if (!fs.existsSync(backendFilePath)) {
16
- throw new Error('Could not find index.js');
17
- }
18
-
19
- const backendJsFile = require(backendFilePath);
20
1
  const { Router } = require('express');
21
- const appDefinition = backendJsFile.Definition;
2
+ const { runInContext, LOGGER_SCOPE_KEY } = require('../logs/context');
3
+ const { Worker } = require('@friggframework/core');
4
+ const {
5
+ IntegrationEventDispatcher,
6
+ } = require('./integration-event-dispatcher');
7
+ const {
8
+ GetIntegrationInstance,
9
+ } = require('../integrations/use-cases/get-integration-instance');
10
+ const { ModuleFactory } = require('../modules/module-factory');
11
+ const {
12
+ createProcessRepository,
13
+ } = require('../integrations/repositories/process-repository-factory');
14
+ const {
15
+ createIntegrationRepository,
16
+ } = require('../integrations/repositories/integration-repository-factory');
17
+ const {
18
+ createModuleRepository,
19
+ } = require('../modules/repositories/module-repository-factory');
20
+ const {
21
+ getModulesDefinitionFromIntegrationClasses,
22
+ } = require('../integrations/utils/map-integration-dto');
23
+ const { processNotFound } = require('../integrations/use-cases/process-errors');
22
24
 
23
- const backend = createFriggBackend(appDefinition);
24
25
  const loadRouterFromObject = (IntegrationClass, routerObject) => {
25
26
  const router = Router();
26
27
  const { path, method, event } = routerObject;
28
+
27
29
  console.log(
28
30
  `Registering ${method} ${path} for ${IntegrationClass.Definition.name}`
29
31
  );
32
+
30
33
  router[method.toLowerCase()](path, async (req, res, next) => {
31
34
  try {
32
- const integration = new IntegrationClass({});
33
- await integration.loadModules();
34
- await integration.registerEventHandlers();
35
- const result = await integration.send(event, {req, res, next});
35
+ const integrationInstance = new IntegrationClass();
36
+ // initialize() registers dynamic user actions AND merges any Tier 3
37
+ // Integration Extension events into instance.events before dispatch.
38
+ await integrationInstance.initialize();
39
+ const dispatcher = new IntegrationEventDispatcher(
40
+ integrationInstance
41
+ );
42
+ // Logs only: a route :integrationId is unauthenticated input.
43
+ const result = await runInContext(
44
+ { [LOGGER_SCOPE_KEY]: { integrationId: req.params?.integrationId } },
45
+ () => dispatcher.dispatchHttp({ event, req, res, next })
46
+ );
36
47
  res.json(result);
37
48
  } catch (error) {
38
49
  next(error);
@@ -41,38 +52,252 @@ const loadRouterFromObject = (IntegrationClass, routerObject) => {
41
52
 
42
53
  return router;
43
54
  };
55
+
56
+ const initializeRepositories = () => {
57
+ const processRepository = createProcessRepository();
58
+ const integrationRepository = createIntegrationRepository();
59
+ const moduleRepository = createModuleRepository();
60
+
61
+ return { processRepository, integrationRepository, moduleRepository };
62
+ };
63
+
64
+ const createModuleFactoryWithDefinitions = (
65
+ moduleRepository,
66
+ integrationClasses
67
+ ) => {
68
+ const moduleDefinitions =
69
+ getModulesDefinitionFromIntegrationClasses(integrationClasses);
70
+
71
+ return new ModuleFactory({
72
+ moduleRepository,
73
+ moduleDefinitions,
74
+ });
75
+ };
76
+
77
+ const loadIntegrationForWebhook = async (integrationId) => {
78
+ const { loadAppDefinition } = require('./app-definition-loader');
79
+ const { integrations: integrationClasses } = loadAppDefinition();
80
+
81
+ const { integrationRepository, moduleRepository } =
82
+ initializeRepositories();
83
+
84
+ const moduleFactory = createModuleFactoryWithDefinitions(
85
+ moduleRepository,
86
+ integrationClasses
87
+ );
88
+
89
+ const getIntegrationInstance = new GetIntegrationInstance({
90
+ integrationRepository,
91
+ integrationClasses,
92
+ moduleFactory,
93
+ });
94
+
95
+ let integrationRecord;
96
+ try {
97
+ integrationRecord = await integrationRepository.findIntegrationById(
98
+ integrationId
99
+ );
100
+ } catch (error) {
101
+ if (error.message?.includes('not found')) {
102
+ return null;
103
+ }
104
+ throw error;
105
+ }
106
+
107
+ const instance = await getIntegrationInstance.execute(
108
+ integrationId,
109
+ integrationRecord.userId
110
+ );
111
+
112
+ return instance;
113
+ };
114
+
115
+ // Returns false only when the integration is confirmed gone; a transient
116
+ // lookup failure returns true so genuine errors keep their normal retry path.
117
+ const integrationExists = async (integrationId) => {
118
+ const integrationRepository = createIntegrationRepository();
119
+ try {
120
+ const record = await integrationRepository.findIntegrationById(
121
+ integrationId
122
+ );
123
+ return Boolean(record);
124
+ } catch (error) {
125
+ if (error.message?.includes('not found')) {
126
+ return false;
127
+ }
128
+ return true;
129
+ }
130
+ };
131
+
132
+ const loadIntegrationForProcess = async (processId, integrationClass) => {
133
+ const { processRepository, integrationRepository, moduleRepository } =
134
+ initializeRepositories();
135
+
136
+ const moduleFactory = createModuleFactoryWithDefinitions(moduleRepository, [
137
+ integrationClass,
138
+ ]);
139
+
140
+ const getIntegrationInstance = new GetIntegrationInstance({
141
+ integrationRepository,
142
+ integrationClasses: [integrationClass],
143
+ moduleFactory,
144
+ });
145
+
146
+ if (!processId) {
147
+ throw new Error('processId is required in queue message data');
148
+ }
149
+
150
+ const process = await processRepository.findById(processId);
151
+
152
+ if (!process) {
153
+ throw processNotFound(`Process not found: ${processId}`);
154
+ }
155
+
156
+ const instance = await getIntegrationInstance.execute(
157
+ process.integrationId,
158
+ process.userId
159
+ );
160
+
161
+ return instance;
162
+ };
163
+
44
164
  const createQueueWorker = (integrationClass) => {
165
+ const integrationName = integrationClass.Definition.name;
166
+
45
167
  class QueueWorker extends Worker {
46
- async _run(params, context) {
168
+ async _run(params, context, delivery) {
169
+ const logCtx = {
170
+ integration: integrationName,
171
+ event: params.event,
172
+ processId: params.data?.processId,
173
+ integrationId: params.data?.integrationId,
174
+ };
175
+
47
176
  try {
48
- let instance;
49
- if (!params.integrationId) {
50
- instance = new integrationClass({});
51
- await instance.loadModules();
52
- // await instance.loadUserActions();
53
- await instance.registerEventHandlers();
177
+ let integrationInstance;
178
+
179
+ // Prioritize processId first (for sync handler compatibility),
180
+ // then integrationId (for ANY event type that needs hydration),
181
+ // fallback to unhydrated instance
182
+ if (params.data?.processId) {
183
+ console.log(`[QueueWorker] hydrating by processId`, logCtx);
184
+ integrationInstance = await loadIntegrationForProcess(
185
+ params.data.processId,
186
+ integrationClass
187
+ );
188
+ console.log(`[QueueWorker] hydrated`, {
189
+ ...logCtx,
190
+ integrationStatus: integrationInstance?.status,
191
+ hydratedIntegrationId: integrationInstance?.id,
192
+ });
193
+ if (
194
+ ['DISABLED', 'ERROR', 'IN_DELETION'].includes(
195
+ integrationInstance?.status
196
+ )
197
+ ) {
198
+ console.warn(
199
+ `[${integrationName}] Integration for process ${params.data.processId} is ${integrationInstance.status}. Discarding ${params.event} message.`
200
+ );
201
+ return;
202
+ }
203
+ } else if (params.data?.integrationId) {
54
204
  console.log(
55
- `${params.event} for ${integrationClass.Definition.name} integration with no integrationId`
205
+ `[QueueWorker] hydrating by integrationId`,
206
+ logCtx
207
+ );
208
+ integrationInstance = await loadIntegrationForWebhook(
209
+ params.data.integrationId
56
210
  );
211
+ if (!integrationInstance) {
212
+ console.warn(
213
+ `[${integrationName}] Integration ${params.data.integrationId} no longer exists. Discarding ${params.event} message.`
214
+ );
215
+ return;
216
+ }
217
+ console.log(`[QueueWorker] hydrated`, {
218
+ ...logCtx,
219
+ integrationStatus: integrationInstance?.status,
220
+ });
221
+ if (
222
+ ['DISABLED', 'ERROR', 'IN_DELETION'].includes(
223
+ integrationInstance.status
224
+ )
225
+ ) {
226
+ console.warn(
227
+ `[${integrationName}] Integration ${params.data.integrationId} is ${integrationInstance.status}. Discarding ${params.event} message.`
228
+ );
229
+ return;
230
+ }
57
231
  } else {
58
- instance =
59
- await integrationClass.getInstanceFromIntegrationId({
60
- integrationId: params.integrationId,
61
- });
232
+ // Instantiates a DRY integration class without database records.
233
+ // There will be cases where we need to use helpers that the api modules can export.
234
+ // Like for HubSpot, the answer is to do a reverse lookup for the integration by the entity external ID (HubSpot Portal ID),
235
+ // and then you'll have the integration ID available to hydrate from.
62
236
  console.log(
63
- `${params.event} for ${instance.integration.config.type} of integrationId: ${params.integrationId}`
237
+ `[QueueWorker] no processId/integrationId — running dry instance`,
238
+ logCtx
64
239
  );
240
+ integrationInstance = new integrationClass();
241
+ // Merge Tier 3 Integration Extension events into instance.events
242
+ // so extension-contributed queue events can be dispatched.
243
+ await integrationInstance.initialize();
65
244
  }
66
- const res = await instance.send(params.event, {
245
+
246
+ const dispatcher = new IntegrationEventDispatcher(
247
+ integrationInstance
248
+ );
249
+
250
+ console.log(
251
+ `[QueueWorker] dispatching ${params.event}`,
252
+ logCtx
253
+ );
254
+ const result = await dispatcher.dispatchJob({
255
+ event: params.event,
67
256
  data: params.data,
68
- context,
257
+ context: context,
258
+ delivery,
69
259
  });
70
- return res;
71
- } catch (error) {
72
- console.error(
73
- `Error in ${params.event} for ${integrationClass.Definition.name}:`,
74
- error
260
+ console.log(
261
+ `[QueueWorker] ${params.event} dispatched ok`,
262
+ logCtx
75
263
  );
264
+ return result;
265
+ } catch (error) {
266
+ // Integration deleted mid-flight: no retry can succeed once
267
+ // it's gone, so discard instead of sending it to the DLQ.
268
+ if (
269
+ params.data?.integrationId &&
270
+ !(await integrationExists(params.data.integrationId))
271
+ ) {
272
+ console.warn(
273
+ `[${integrationName}] Integration ${params.data.integrationId} was deleted mid-flight — discarding ${params.event} message (no retry)`
274
+ );
275
+ return;
276
+ }
277
+
278
+ // 4xx HTTP errors are permanent — the requester already
279
+ // attempted token refresh (401) and backoff (429/5xx).
280
+ // By the time a 4xx reaches here, retrying won't help.
281
+ // 408 (timeout) and 429 (rate limit) are excluded — both are transient.
282
+ const status = error.statusCode;
283
+ if (
284
+ status &&
285
+ status >= 400 &&
286
+ status < 500 &&
287
+ status !== 408 &&
288
+ status !== 429
289
+ ) {
290
+ error.isHaltError = true;
291
+ console.warn(
292
+ `[${integrationName}] Permanent ${status} error for ${params.event} — message will be discarded (no retry)`,
293
+ {
294
+ ...logCtx,
295
+ errorName: error.name,
296
+ errorMessage: error.message,
297
+ }
298
+ );
299
+ }
300
+
76
301
  throw error;
77
302
  }
78
303
  }
@@ -81,7 +306,8 @@ const createQueueWorker = (integrationClass) => {
81
306
  };
82
307
 
83
308
  module.exports = {
84
- ...backend,
85
309
  loadRouterFromObject,
86
310
  createQueueWorker,
311
+ integrationExists,
312
+ loadIntegrationForWebhook,
87
313
  };