@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,682 @@
1
+ # Webhook Handling in Frigg
2
+
3
+ This document explains how to implement webhook handling for your Frigg integrations using the built-in webhook infrastructure.
4
+
5
+ ## Overview
6
+
7
+ Frigg provides a scalable webhook architecture that:
8
+ - **Receives webhooks without database connections** for fast response times
9
+ - **Queues webhooks to SQS** for async processing
10
+ - **Processes webhooks with fully hydrated integrations** (with DB and API modules loaded)
11
+ - **Supports custom signature verification** for security
12
+ - **Throttles database connections** using SQS to handle webhook bursts
13
+
14
+ ## Architecture
15
+
16
+ The webhook flow consists of two stages:
17
+
18
+ ### Stage 1: HTTP Webhook Receiver (No DB)
19
+ ```
20
+ Webhook → Lambda → WEBHOOK_RECEIVED event → Queue to SQS → 200 OK Response
21
+ ```
22
+ - Fast response (no database query)
23
+ - Optional signature verification
24
+ - Messages queued for processing
25
+
26
+ ### Stage 2: Queue Worker (DB-Connected)
27
+ ```
28
+ SQS Queue → Lambda Worker → ON_WEBHOOK event → Process with hydrated integration
29
+ ```
30
+ - Full database access
31
+ - API modules loaded
32
+ - Can use integration context
33
+
34
+ ## Enabling Webhooks
35
+
36
+ ### Simple Configuration
37
+
38
+ Add `webhooks: true` to your Integration Definition:
39
+
40
+ ```javascript
41
+ class MyIntegration extends IntegrationBase {
42
+ static Definition = {
43
+ name: 'my-integration',
44
+ version: '1.0.0',
45
+ modules: {
46
+ myapi: { definition: MyApiDefinition },
47
+ },
48
+ webhooks: true, // Enable webhook handling
49
+ };
50
+ }
51
+ ```
52
+
53
+ ### Advanced Configuration
54
+
55
+ For future extensibility, you can use object configuration:
56
+
57
+ ```javascript
58
+ class MyIntegration extends IntegrationBase {
59
+ static Definition = {
60
+ name: 'my-integration',
61
+ version: '1.0.0',
62
+ modules: { /* ... */ },
63
+ webhooks: {
64
+ enabled: true,
65
+ // Future options will be added here
66
+ },
67
+ };
68
+ }
69
+ ```
70
+
71
+ ## Webhook Routes
72
+
73
+ When webhooks are enabled, two routes are automatically created:
74
+
75
+ ### General Webhook
76
+ ```
77
+ POST /api/{integrationName}-integration/webhooks
78
+ ```
79
+ - No integration ID required
80
+ - Useful for system-wide events
81
+ - Creates unhydrated integration instance
82
+
83
+ ### Integration-Specific Webhook
84
+ ```
85
+ POST /api/{integrationName}-integration/webhooks/:integrationId
86
+ ```
87
+ - Includes integration ID in URL
88
+ - Worker loads full integration with DB and modules
89
+ - Recommended for most use cases
90
+
91
+ ## Event Handlers
92
+
93
+ ### WEBHOOK_RECEIVED Event
94
+
95
+ Triggered when a webhook HTTP request is received (no database connection).
96
+
97
+ #### Default Behavior
98
+ Queues the webhook to SQS and responds with `200 OK`:
99
+
100
+ ```javascript
101
+ // Default handler (automatic)
102
+ async onWebhookReceived({ req, res }) {
103
+ await this.queueWebhook({
104
+ integrationId: req.params.integrationId || null,
105
+ body: req.body,
106
+ headers: req.headers,
107
+ query: req.query,
108
+ });
109
+ res.status(200).json({ received: true });
110
+ }
111
+ ```
112
+
113
+ #### Custom Signature Verification
114
+
115
+ Override `onWebhookReceived` for custom signature verification:
116
+
117
+ ```javascript
118
+ class MyIntegration extends IntegrationBase {
119
+ static Definition = {
120
+ name: 'my-integration',
121
+ webhooks: true,
122
+ };
123
+
124
+ async onWebhookReceived({ req, res }) {
125
+ // Verify webhook signature
126
+ const signature = req.headers['x-webhook-signature'];
127
+ const expectedSignature = this.calculateSignature(req.body);
128
+
129
+ if (signature !== expectedSignature) {
130
+ return res.status(401).json({ error: 'Invalid signature' });
131
+ }
132
+
133
+ // Queue for processing
134
+ await this.queueWebhook({
135
+ integrationId: req.params.integrationId,
136
+ body: req.body,
137
+ headers: req.headers,
138
+ });
139
+
140
+ res.status(200).json({ received: true, verified: true });
141
+ }
142
+
143
+ calculateSignature(body) {
144
+ const crypto = require('crypto');
145
+ const secret = process.env.MY_WEBHOOK_SECRET;
146
+ return crypto
147
+ .createHmac('sha256', secret)
148
+ .update(JSON.stringify(body))
149
+ .digest('hex');
150
+ }
151
+ }
152
+ ```
153
+
154
+ ### ON_WEBHOOK Event
155
+
156
+ Triggered by the queue worker (with database connection and hydrated integration).
157
+
158
+ #### Default Behavior
159
+ Logs the webhook data (override this!):
160
+
161
+ ```javascript
162
+ // Default handler (logs only)
163
+ async onWebhook({ data }) {
164
+ console.log('Webhook received:', data);
165
+ }
166
+ ```
167
+
168
+ #### Custom Processing
169
+
170
+ Override `onWebhook` to process webhooks with full integration context:
171
+
172
+ ```javascript
173
+ class MyIntegration extends IntegrationBase {
174
+ static Definition = {
175
+ name: 'my-integration',
176
+ modules: {
177
+ myapi: { definition: MyApiDefinition },
178
+ },
179
+ webhooks: true,
180
+ };
181
+
182
+ async onWebhook({ data }) {
183
+ const { body, headers, integrationId } = data;
184
+
185
+ // Access hydrated API modules
186
+ if (body.event === 'item.created') {
187
+ await this.myapi.api.createRecord({
188
+ externalId: body.data.id,
189
+ name: body.data.name,
190
+ });
191
+ }
192
+
193
+ // Access integration config
194
+ const syncEnabled = this.config.syncEnabled;
195
+ if (syncEnabled) {
196
+ await this.performSync(body.data);
197
+ }
198
+
199
+ // Update integration mappings
200
+ await this.upsertMapping(body.data.id, {
201
+ externalId: body.data.id,
202
+ syncedAt: new Date(),
203
+ });
204
+
205
+ return { processed: true };
206
+ }
207
+
208
+ async performSync(data) {
209
+ // Custom sync logic
210
+ }
211
+ }
212
+ ```
213
+
214
+ ## Examples
215
+
216
+ ### Example 1: Slack Message Events
217
+
218
+ ```javascript
219
+ class SlackIntegration extends IntegrationBase {
220
+ static Definition = {
221
+ name: 'slack',
222
+ modules: {
223
+ slack: { definition: SlackApiDefinition },
224
+ },
225
+ webhooks: true,
226
+ };
227
+
228
+ async onWebhookReceived({ req, res }) {
229
+ // Slack URL verification challenge
230
+ if (req.body.type === 'url_verification') {
231
+ return res.json({ challenge: req.body.challenge });
232
+ }
233
+
234
+ // Verify Slack signature
235
+ const slackSignature = req.headers['x-slack-signature'];
236
+ if (!this.verifySlackSignature(req, slackSignature)) {
237
+ return res.status(401).json({ error: 'Invalid signature' });
238
+ }
239
+
240
+ await this.queueWebhook({
241
+ integrationId: req.params.integrationId,
242
+ body: req.body,
243
+ });
244
+
245
+ res.status(200).json({ ok: true });
246
+ }
247
+
248
+ async onWebhook({ data }) {
249
+ const { body } = data;
250
+
251
+ if (body.event.type === 'message') {
252
+ // Process message with API access
253
+ await this.slack.api.postMessage({
254
+ channel: body.event.channel,
255
+ text: `Received: ${body.event.text}`,
256
+ });
257
+ }
258
+ }
259
+
260
+ verifySlackSignature(req, signature) {
261
+ // Slack signature verification logic
262
+ const crypto = require('crypto');
263
+ const signingSecret = process.env.SLACK_SIGNING_SECRET;
264
+ const timestamp = req.headers['x-slack-request-timestamp'];
265
+
266
+ // Validate timestamp is recent (within 5 minutes)
267
+ const currentTime = Math.floor(Date.now() / 1000);
268
+ if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
269
+ return false; // Request is older than 5 minutes
270
+ }
271
+
272
+ const hmac = crypto.createHmac('sha256', signingSecret);
273
+ hmac.update(`v0:${timestamp}:${JSON.stringify(req.body)}`);
274
+ const expected = `v0=${hmac.digest('hex')}`;
275
+
276
+ // Check lengths first to avoid errors in timingSafeEqual
277
+ const expectedBuffer = Buffer.from(expected)
278
+ const signatureBuffer = Buffer.from(signature)
279
+
280
+ if (expectedBuffer.length !== signatureBuffer.length) {
281
+ return false
282
+ }
283
+
284
+ return crypto.timingSafeEqual(expectedBuffer, signatureBuffer)
285
+
286
+ }
287
+ }
288
+ ```
289
+
290
+ ### Example 2: Stripe Webhook Events
291
+
292
+ ```javascript
293
+ class StripeIntegration extends IntegrationBase {
294
+ static Definition = {
295
+ name: 'stripe',
296
+ modules: {
297
+ stripe: { definition: StripeApiDefinition },
298
+ },
299
+ webhooks: true,
300
+ };
301
+
302
+ async onWebhookReceived({ req, res }) {
303
+ const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
304
+ const sig = req.headers['stripe-signature'];
305
+
306
+ try {
307
+ // Stripe signature verification
308
+ const event = stripe.webhooks.constructEvent(
309
+ JSON.stringify(req.body),
310
+ sig,
311
+ process.env.STRIPE_WEBHOOK_SECRET
312
+ );
313
+
314
+ await this.queueWebhook({
315
+ integrationId: req.params.integrationId,
316
+ body: event,
317
+ });
318
+
319
+ res.status(200).json({ received: true });
320
+ } catch (err) {
321
+ res.status(400).json({ error: `Webhook Error: ${err.message}` });
322
+ }
323
+ }
324
+
325
+ async onWebhook({ data }) {
326
+ const event = data.body;
327
+
328
+ switch (event.type) {
329
+ case 'payment_intent.succeeded':
330
+ await this.handlePaymentSuccess(event.data.object);
331
+ break;
332
+ case 'customer.subscription.created':
333
+ await this.handleSubscriptionCreated(event.data.object);
334
+ break;
335
+ default:
336
+ console.log(`Unhandled event type: ${event.type}`);
337
+ }
338
+ }
339
+
340
+ async handlePaymentSuccess(paymentIntent) {
341
+ // Update your database, send notifications, etc.
342
+ await this.stripe.api.updatePaymentRecord(paymentIntent.id, {
343
+ status: 'succeeded',
344
+ amount: paymentIntent.amount,
345
+ });
346
+ }
347
+
348
+ async handleSubscriptionCreated(subscription) {
349
+ // Process new subscription
350
+ await this.upsertMapping(subscription.id, {
351
+ stripeSubscriptionId: subscription.id,
352
+ status: subscription.status,
353
+ createdAt: new Date(subscription.created * 1000),
354
+ });
355
+ }
356
+ }
357
+ ```
358
+
359
+ ### Example 3: General Webhook (No Integration ID)
360
+
361
+ ```javascript
362
+ class SystemWebhookIntegration extends IntegrationBase {
363
+ static Definition = {
364
+ name: 'system-webhook',
365
+ webhooks: true,
366
+ };
367
+
368
+ async onWebhook({ data }) {
369
+ const { body } = data;
370
+
371
+ // Process system-wide webhook without integration context
372
+ console.log('System webhook received:', body);
373
+
374
+ // Could trigger actions across multiple integrations
375
+ // or perform system-level operations
376
+ }
377
+ }
378
+ ```
379
+
380
+ ## Environment Variables
381
+
382
+ Webhook functionality requires queue URL environment variables:
383
+
384
+ ```bash
385
+ # Format: {INTEGRATION_NAME}_QUEUE_URL
386
+ SLACK_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789/slack-queue
387
+ STRIPE_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789/stripe-queue
388
+ ```
389
+
390
+ These are automatically configured by the Frigg infrastructure when using the serverless template.
391
+
392
+ ## Testing Webhooks
393
+
394
+ ### Unit Test Example
395
+
396
+ ```javascript
397
+ describe('MyIntegration Webhooks', () => {
398
+ it('should verify webhook signature', async () => {
399
+ const integration = new MyIntegration();
400
+ const dispatcher = new IntegrationEventDispatcher(integration);
401
+
402
+ const req = {
403
+ body: { event: 'test' },
404
+ params: {},
405
+ headers: { 'x-webhook-signature': 'valid-sig' },
406
+ query: {},
407
+ };
408
+ const res = {
409
+ status: jest.fn().mockReturnThis(),
410
+ json: jest.fn(),
411
+ };
412
+
413
+ await dispatcher.dispatchHttp({
414
+ event: 'WEBHOOK_RECEIVED',
415
+ req,
416
+ res,
417
+ next: jest.fn(),
418
+ });
419
+
420
+ expect(res.status).toHaveBeenCalledWith(200);
421
+ });
422
+
423
+ it('should process webhook with hydrated instance', async () => {
424
+ const integration = new MyIntegration({
425
+ id: 'int-123',
426
+ userId: 'user-456',
427
+ modules: [],
428
+ });
429
+ const dispatcher = new IntegrationEventDispatcher(integration);
430
+
431
+ const result = await dispatcher.dispatchJob({
432
+ event: 'ON_WEBHOOK',
433
+ data: {
434
+ integrationId: 'int-123',
435
+ body: { event: 'item.created' },
436
+ },
437
+ context: {},
438
+ });
439
+
440
+ expect(result.processed).toBe(true);
441
+ });
442
+ });
443
+ ```
444
+
445
+ ## Best Practices
446
+
447
+ ### 1. Always Verify Signatures
448
+ ```javascript
449
+ async onWebhookReceived({ req, res }) {
450
+ // Verify before queueing
451
+ if (!this.verifySignature(req)) {
452
+ return res.status(401).json({ error: 'Unauthorized' });
453
+ }
454
+ await this.queueWebhook({ /* ... */ });
455
+ res.status(200).json({ received: true });
456
+ }
457
+ ```
458
+
459
+ ### 2. Respond Quickly
460
+ The `WEBHOOK_RECEIVED` handler should complete in < 3 seconds:
461
+ - Verify signature
462
+ - Queue message
463
+ - Return 200 OK
464
+
465
+ Heavy processing goes in `ON_WEBHOOK`.
466
+
467
+ ### 3. Handle Idempotency
468
+ ```javascript
469
+ async onWebhook({ data }) {
470
+ const { body } = data;
471
+ const eventId = body.id;
472
+
473
+ // Check if already processed
474
+ const existing = await this.getMapping(eventId);
475
+ if (existing) {
476
+ console.log(`Event ${eventId} already processed`);
477
+ return { processed: false, duplicate: true };
478
+ }
479
+
480
+ // Process and mark as complete
481
+ await this.processEvent(body);
482
+ await this.upsertMapping(eventId, { processedAt: new Date() });
483
+ }
484
+ ```
485
+
486
+ `getMapping` followed by `upsertMapping` is not atomic. To claim an event so only one
487
+ concurrent worker proceeds, use the insert-only `createMapping`, which throws
488
+ `MappingAlreadyExistsError` (exported from `@friggframework/core`) when the
489
+ `sourceId` is already taken. `deleteMapping(sourceId)` releases the claim and is a
490
+ no-op when nothing exists.
491
+
492
+ ```javascript
493
+ const { MappingAlreadyExistsError } = require('@friggframework/core');
494
+
495
+ async onWebhook({ data }) {
496
+ const claimId = `claim:${data.body.id}`;
497
+ try {
498
+ await this.createMapping(claimId, { claimedAt: new Date() });
499
+ } catch (error) {
500
+ if (error instanceof MappingAlreadyExistsError) {
501
+ return { processed: false, duplicate: true };
502
+ }
503
+ throw error;
504
+ }
505
+
506
+ try {
507
+ await this.processEvent(data.body);
508
+ } catch (error) {
509
+ await this.deleteMapping(claimId);
510
+ throw error;
511
+ }
512
+ }
513
+ ```
514
+
515
+ ### 4. Error Handling
516
+ ```javascript
517
+ async onWebhook({ data }) {
518
+ try {
519
+ await this.processWebhookData(data.body);
520
+ } catch (error) {
521
+ // Log error - message will go to DLQ after retries
522
+ console.error('Webhook processing failed:', error);
523
+
524
+ // Update integration status if needed
525
+ await this.updateIntegrationMessages.execute(
526
+ this.id,
527
+ 'errors',
528
+ 'Webhook Processing Error',
529
+ error.message,
530
+ Date.now()
531
+ );
532
+
533
+ throw error; // Re-throw for retry/DLQ
534
+ }
535
+ }
536
+ ```
537
+
538
+ ## Infrastructure
539
+
540
+ ### Automatic Configuration
541
+
542
+ When `webhooks: true` is set, the Frigg infrastructure automatically creates:
543
+
544
+ 1. **HTTP Lambda Function**
545
+ - Handler: `integration-webhook-routers.js`
546
+ - No database connection
547
+ - Fast cold start
548
+
549
+ 2. **Webhook Routes**
550
+ - `POST /api/{name}-integration/webhooks`
551
+ - `POST /api/{name}-integration/webhooks/:integrationId`
552
+
553
+ 3. **Queue Worker**
554
+ - Processes from existing integration queue
555
+ - Handles `ON_WEBHOOK` events
556
+ - Full database access
557
+
558
+ ### Serverless Configuration (Automatic)
559
+
560
+ The following is generated automatically in `serverless.yml`:
561
+
562
+ ```yaml
563
+ functions:
564
+ myintegrationWebhook:
565
+ handler: node_modules/@friggframework/core/handlers/routers/integration-webhook-routers.handlers.myintegrationWebhook.handler
566
+ events:
567
+ - httpApi:
568
+ path: /api/myintegration-integration/webhooks
569
+ method: POST
570
+ - httpApi:
571
+ path: /api/myintegration-integration/webhooks/{integrationId}
572
+ method: POST
573
+
574
+ myintegrationQueueWorker:
575
+ handler: node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.myintegration.queueWorker
576
+ events:
577
+ - sqs:
578
+ arn: !GetAtt MyintegrationQueue.Arn
579
+ batchSize: 1
580
+ ```
581
+
582
+ ## Event Handler Reference
583
+
584
+ ### onWebhookReceived({ req, res })
585
+
586
+ **Called:** When webhook HTTP request is received
587
+ **Context:** Unhydrated integration (no DB, no modules loaded)
588
+ **Purpose:** Signature verification, quick response
589
+ **Must:** Respond to `res` with status code
590
+
591
+ **Parameters:**
592
+ - `req` - Express request object
593
+ - `req.body` - Webhook payload
594
+ - `req.params.integrationId` - Integration ID (if in URL)
595
+ - `req.headers` - HTTP headers
596
+ - `req.query` - Query parameters
597
+ - `res` - Express response object
598
+ - Call `res.status(code).json(data)` to respond
599
+
600
+ ### onWebhook({ data, context })
601
+
602
+ **Called:** When queue worker processes the webhook
603
+ **Context:** Hydrated integration (DB connected, modules loaded)
604
+ **Purpose:** Process webhook with full integration context
605
+ **Can:** Use `this.modules`, `this.config`, DB operations
606
+
607
+ **Parameters:**
608
+ - `data` - Queued webhook data
609
+ - `data.integrationId` - Integration ID (if provided)
610
+ - `data.body` - Original webhook payload
611
+ - `data.headers` - Original HTTP headers
612
+ - `data.query` - Original query parameters
613
+ - `context` - Lambda context object
614
+
615
+ ## Queue Helper
616
+
617
+ ### queueWebhook(data)
618
+
619
+ Utility method to queue webhook for processing:
620
+
621
+ ```javascript
622
+ await this.queueWebhook({
623
+ integrationId: 'int-123', // optional
624
+ body: webhookPayload,
625
+ headers: requestHeaders,
626
+ query: queryParams,
627
+ customField: 'any additional data',
628
+ });
629
+ ```
630
+
631
+ Automatically uses the correct SQS queue URL based on integration name.
632
+
633
+ ## Troubleshooting
634
+
635
+ ### Queue URL Not Found
636
+
637
+ **Error:** `Queue URL not found for {NAME}_QUEUE_URL`
638
+
639
+ **Solution:** Ensure environment variable is set:
640
+ ```bash
641
+ export MY_INTEGRATION_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/...
642
+ ```
643
+
644
+ ### Webhook Not Responding
645
+
646
+ **Check:**
647
+ 1. Is `webhooks: true` in Definition?
648
+ 2. Is webhook endpoint deployed?
649
+ 3. Are you sending POST requests?
650
+ 4. Check CloudWatch logs for errors
651
+
652
+ ### Worker Not Processing
653
+
654
+ **Check:**
655
+ 1. Is SQS queue receiving messages?
656
+ 2. Is queue worker Lambda function deployed?
657
+ 3. Check CloudWatch logs for worker errors
658
+ 4. Verify integration can be loaded from DB (for ID-specific webhooks)
659
+
660
+ ## Security Considerations
661
+
662
+ 1. **Always verify signatures** in production
663
+ 2. **Use HTTPS** for webhook endpoints
664
+ 3. **Validate webhook payloads** before processing
665
+ 4. **Rate limit** at API Gateway level if needed
666
+ 5. **Monitor** failed webhook processing in DLQ
667
+
668
+ ## Performance
669
+
670
+ - **HTTP Response:** < 100ms (signature check + queue)
671
+ - **Worker Processing:** Based on your logic
672
+ - **Concurrency:** Controlled by SQS worker `reservedConcurrency: 5`
673
+ - **Burst Handling:** Unlimited HTTP, throttled processing
674
+
675
+ ## Related Files
676
+
677
+ - `packages/core/integrations/integration-base.js` - Event definitions and default handlers
678
+ - `packages/core/handlers/routers/integration-webhook-routers.js` - HTTP webhook routes
679
+ - `packages/core/handlers/backend-utils.js` - Queue worker with hydration logic
680
+ - `packages/core/handlers/integration-event-dispatcher.js` - Event dispatching
681
+ - `packages/devtools/infrastructure/serverless-template.js` - Automatic infrastructure generation
682
+