@friggframework/core 2.0.0-next.9 → 2.0.0-next.91

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 (309) hide show
  1. package/CLAUDE.md +702 -0
  2. package/README.md +959 -50
  3. package/application/commands/README.md +451 -0
  4. package/application/commands/credential-commands.js +245 -0
  5. package/application/commands/entity-commands.js +336 -0
  6. package/application/commands/integration-commands.js +271 -0
  7. package/application/commands/scheduler-commands.js +263 -0
  8. package/application/commands/user-commands.js +283 -0
  9. package/application/index.js +73 -0
  10. package/assertions/index.js +0 -3
  11. package/core/CLAUDE.md +690 -0
  12. package/core/Worker.js +60 -24
  13. package/core/create-handler.js +84 -6
  14. package/credential/repositories/credential-repository-documentdb.js +304 -0
  15. package/credential/repositories/credential-repository-factory.js +54 -0
  16. package/credential/repositories/credential-repository-interface.js +98 -0
  17. package/credential/repositories/credential-repository-mongo.js +269 -0
  18. package/credential/repositories/credential-repository-postgres.js +287 -0
  19. package/credential/repositories/credential-repository.js +300 -0
  20. package/credential/use-cases/get-credential-for-user.js +25 -0
  21. package/credential/use-cases/update-authentication-status.js +15 -0
  22. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  23. package/database/adapters/lambda-invoker.js +97 -0
  24. package/database/config.js +154 -0
  25. package/database/documentdb-encryption-service.js +330 -0
  26. package/database/documentdb-utils.js +136 -0
  27. package/database/encryption/README.md +839 -0
  28. package/database/encryption/documentdb-encryption-service.md +3575 -0
  29. package/database/encryption/encryption-schema-registry.js +401 -0
  30. package/database/encryption/field-encryption-service.js +254 -0
  31. package/database/encryption/logger.js +79 -0
  32. package/database/encryption/prisma-encryption-extension.js +230 -0
  33. package/database/index.js +21 -21
  34. package/database/prisma.js +182 -0
  35. package/database/repositories/health-check-repository-documentdb.js +138 -0
  36. package/database/repositories/health-check-repository-factory.js +48 -0
  37. package/database/repositories/health-check-repository-interface.js +82 -0
  38. package/database/repositories/health-check-repository-mongodb.js +89 -0
  39. package/database/repositories/health-check-repository-postgres.js +82 -0
  40. package/database/repositories/migration-status-repository-s3.js +137 -0
  41. package/database/use-cases/check-database-health-use-case.js +29 -0
  42. package/database/use-cases/check-database-state-use-case.js +81 -0
  43. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  44. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  45. package/database/use-cases/get-migration-status-use-case.js +93 -0
  46. package/database/use-cases/run-database-migration-use-case.js +139 -0
  47. package/database/use-cases/test-encryption-use-case.js +253 -0
  48. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  49. package/database/utils/mongodb-collection-utils.js +94 -0
  50. package/database/utils/mongodb-schema-init.js +108 -0
  51. package/database/utils/prisma-runner.js +477 -0
  52. package/database/utils/prisma-schema-parser.js +182 -0
  53. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  54. package/encrypt/Cryptor.js +34 -168
  55. package/encrypt/index.js +1 -2
  56. package/errors/client-safe-error.js +26 -0
  57. package/errors/fetch-error.js +15 -7
  58. package/errors/index.js +2 -0
  59. package/generated/prisma-mongodb/client.d.ts +1 -0
  60. package/generated/prisma-mongodb/client.js +4 -0
  61. package/generated/prisma-mongodb/default.d.ts +1 -0
  62. package/generated/prisma-mongodb/default.js +4 -0
  63. package/generated/prisma-mongodb/edge.d.ts +1 -0
  64. package/generated/prisma-mongodb/edge.js +335 -0
  65. package/generated/prisma-mongodb/index-browser.js +317 -0
  66. package/generated/prisma-mongodb/index.d.ts +22955 -0
  67. package/generated/prisma-mongodb/index.js +360 -0
  68. package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  69. package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  70. package/generated/prisma-mongodb/package.json +183 -0
  71. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  72. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  73. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  74. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  75. package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
  76. package/generated/prisma-mongodb/runtime/library.js +146 -0
  77. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  78. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  79. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  80. package/generated/prisma-mongodb/schema.prisma +368 -0
  81. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  82. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  83. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  84. package/generated/prisma-mongodb/wasm.js +342 -0
  85. package/generated/prisma-postgresql/client.d.ts +1 -0
  86. package/generated/prisma-postgresql/client.js +4 -0
  87. package/generated/prisma-postgresql/default.d.ts +1 -0
  88. package/generated/prisma-postgresql/default.js +4 -0
  89. package/generated/prisma-postgresql/edge.d.ts +1 -0
  90. package/generated/prisma-postgresql/edge.js +357 -0
  91. package/generated/prisma-postgresql/index-browser.js +339 -0
  92. package/generated/prisma-postgresql/index.d.ts +25135 -0
  93. package/generated/prisma-postgresql/index.js +382 -0
  94. package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  95. package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  96. package/generated/prisma-postgresql/package.json +183 -0
  97. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  98. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  99. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  100. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  101. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  102. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  103. package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
  104. package/generated/prisma-postgresql/runtime/library.js +146 -0
  105. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  106. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  107. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  108. package/generated/prisma-postgresql/schema.prisma +351 -0
  109. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  110. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  111. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  112. package/generated/prisma-postgresql/wasm.js +364 -0
  113. package/handlers/WEBHOOKS.md +653 -0
  114. package/handlers/app-definition-loader.js +38 -0
  115. package/handlers/app-handler-helpers.js +57 -0
  116. package/handlers/backend-utils.js +297 -0
  117. package/handlers/database-migration-handler.js +227 -0
  118. package/handlers/integration-event-dispatcher.js +54 -0
  119. package/handlers/routers/HEALTHCHECK.md +342 -0
  120. package/handlers/routers/auth.js +15 -0
  121. package/handlers/routers/db-migration.handler.js +29 -0
  122. package/handlers/routers/db-migration.js +326 -0
  123. package/handlers/routers/health.js +518 -0
  124. package/handlers/routers/integration-defined-routers.js +117 -0
  125. package/handlers/routers/integration-webhook-routers.js +67 -0
  126. package/handlers/routers/user.js +63 -0
  127. package/handlers/routers/websocket.js +57 -0
  128. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  129. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  130. package/handlers/workers/db-migration.js +352 -0
  131. package/handlers/workers/dlq-processor.js +63 -0
  132. package/handlers/workers/integration-defined-workers.js +30 -0
  133. package/index.js +82 -46
  134. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
  135. package/infrastructure/scheduler/index.js +33 -0
  136. package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
  137. package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
  138. package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
  139. package/integrations/EXTENSIONS.md +240 -0
  140. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  141. package/integrations/extension.js +254 -0
  142. package/integrations/index.js +20 -10
  143. package/integrations/integration-base.js +487 -55
  144. package/integrations/integration-router.js +396 -179
  145. package/integrations/options.js +1 -1
  146. package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
  147. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  148. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  149. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  150. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  151. package/integrations/repositories/integration-mapping-repository.js +156 -0
  152. package/integrations/repositories/integration-repository-documentdb.js +219 -0
  153. package/integrations/repositories/integration-repository-factory.js +51 -0
  154. package/integrations/repositories/integration-repository-interface.js +144 -0
  155. package/integrations/repositories/integration-repository-mongo.js +330 -0
  156. package/integrations/repositories/integration-repository-postgres.js +385 -0
  157. package/integrations/repositories/process-repository-documentdb.js +311 -0
  158. package/integrations/repositories/process-repository-factory.js +53 -0
  159. package/integrations/repositories/process-repository-interface.js +136 -0
  160. package/integrations/repositories/process-repository-mongo.js +262 -0
  161. package/integrations/repositories/process-repository-postgres.js +380 -0
  162. package/integrations/repositories/process-update-ops-shared.js +112 -0
  163. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  164. package/integrations/tests/doubles/dummy-integration-class.js +105 -0
  165. package/integrations/tests/doubles/test-integration-repository.js +112 -0
  166. package/integrations/use-cases/create-integration.js +83 -0
  167. package/integrations/use-cases/create-process.js +128 -0
  168. package/integrations/use-cases/delete-integration-for-user.js +101 -0
  169. package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
  170. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
  171. package/integrations/use-cases/get-integration-for-user.js +78 -0
  172. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  173. package/integrations/use-cases/get-integration-instance.js +83 -0
  174. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  175. package/integrations/use-cases/get-possible-integrations.js +27 -0
  176. package/integrations/use-cases/get-process.js +87 -0
  177. package/integrations/use-cases/index.js +19 -0
  178. package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
  179. package/integrations/use-cases/load-integration-context.js +71 -0
  180. package/integrations/use-cases/update-integration-messages.js +44 -0
  181. package/integrations/use-cases/update-integration-status.js +32 -0
  182. package/integrations/use-cases/update-integration.js +92 -0
  183. package/integrations/use-cases/update-process-metrics.js +214 -0
  184. package/integrations/use-cases/update-process-state.js +158 -0
  185. package/integrations/utils/map-integration-dto.js +37 -0
  186. package/jest-global-setup-noop.js +3 -0
  187. package/jest-global-teardown-noop.js +3 -0
  188. package/logs/logger.js +0 -4
  189. package/{module-plugin → modules}/index.js +0 -10
  190. package/modules/module-factory.js +56 -0
  191. package/modules/module.js +274 -0
  192. package/modules/repositories/module-repository-documentdb.js +350 -0
  193. package/modules/repositories/module-repository-factory.js +40 -0
  194. package/modules/repositories/module-repository-interface.js +145 -0
  195. package/modules/repositories/module-repository-mongo.js +436 -0
  196. package/modules/repositories/module-repository-postgres.js +481 -0
  197. package/modules/repositories/module-repository.js +369 -0
  198. package/modules/requester/api-key.js +52 -0
  199. package/modules/requester/oauth-2.js +396 -0
  200. package/modules/requester/requester.js +280 -0
  201. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  202. package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
  203. package/modules/tests/doubles/test-module-factory.js +16 -0
  204. package/modules/tests/doubles/test-module-repository.js +39 -0
  205. package/modules/use-cases/get-entities-for-user.js +32 -0
  206. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  207. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  208. package/modules/use-cases/get-module-instance-from-type.js +34 -0
  209. package/modules/use-cases/get-module.js +74 -0
  210. package/modules/use-cases/process-authorization-callback.js +243 -0
  211. package/modules/use-cases/refresh-entity-options.js +72 -0
  212. package/modules/use-cases/test-module-auth.js +72 -0
  213. package/modules/utils/map-module-dto.js +18 -0
  214. package/package.json +82 -50
  215. package/prisma-mongodb/schema.prisma +368 -0
  216. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  217. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  218. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  219. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  220. package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
  221. package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
  222. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  223. package/prisma-postgresql/schema.prisma +351 -0
  224. package/queues/queuer-util.js +103 -21
  225. package/syncs/manager.js +468 -443
  226. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  227. package/syncs/repositories/sync-repository-factory.js +43 -0
  228. package/syncs/repositories/sync-repository-interface.js +109 -0
  229. package/syncs/repositories/sync-repository-mongo.js +239 -0
  230. package/syncs/repositories/sync-repository-postgres.js +319 -0
  231. package/syncs/sync.js +0 -1
  232. package/token/repositories/token-repository-documentdb.js +137 -0
  233. package/token/repositories/token-repository-factory.js +40 -0
  234. package/token/repositories/token-repository-interface.js +131 -0
  235. package/token/repositories/token-repository-mongo.js +219 -0
  236. package/token/repositories/token-repository-postgres.js +264 -0
  237. package/token/repositories/token-repository.js +219 -0
  238. package/types/associations/index.d.ts +0 -17
  239. package/types/core/index.d.ts +12 -4
  240. package/types/database/index.d.ts +10 -2
  241. package/types/encrypt/index.d.ts +5 -3
  242. package/types/integrations/index.d.ts +3 -8
  243. package/types/module-plugin/index.d.ts +17 -69
  244. package/types/syncs/index.d.ts +0 -17
  245. package/user/repositories/user-repository-documentdb.js +441 -0
  246. package/user/repositories/user-repository-factory.js +52 -0
  247. package/user/repositories/user-repository-interface.js +201 -0
  248. package/user/repositories/user-repository-mongo.js +308 -0
  249. package/user/repositories/user-repository-postgres.js +360 -0
  250. package/user/tests/doubles/test-user-repository.js +72 -0
  251. package/user/use-cases/authenticate-user.js +127 -0
  252. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  253. package/user/use-cases/create-individual-user.js +61 -0
  254. package/user/use-cases/create-organization-user.js +47 -0
  255. package/user/use-cases/create-token-for-user-id.js +30 -0
  256. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  257. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  258. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  259. package/user/use-cases/login-user.js +122 -0
  260. package/user/user.js +125 -0
  261. package/utils/backend-path.js +38 -0
  262. package/utils/index.js +6 -0
  263. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  264. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  265. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  266. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  267. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  268. package/websocket/repositories/websocket-connection-repository.js +161 -0
  269. package/assertions/is-equal.js +0 -17
  270. package/associations/model.js +0 -54
  271. package/database/models/IndividualUser.js +0 -76
  272. package/database/models/OrganizationUser.js +0 -29
  273. package/database/models/State.js +0 -9
  274. package/database/models/Token.js +0 -70
  275. package/database/models/UserModel.js +0 -7
  276. package/database/models/WebsocketConnection.js +0 -49
  277. package/database/mongo.js +0 -45
  278. package/database/mongoose.js +0 -5
  279. package/encrypt/Cryptor.test.js +0 -32
  280. package/encrypt/encrypt.js +0 -132
  281. package/encrypt/encrypt.test.js +0 -1069
  282. package/encrypt/test-encrypt.js +0 -107
  283. package/errors/base-error.test.js +0 -32
  284. package/errors/fetch-error.test.js +0 -79
  285. package/errors/halt-error.test.js +0 -11
  286. package/errors/validation-errors.test.js +0 -120
  287. package/integrations/create-frigg-backend.js +0 -31
  288. package/integrations/integration-factory.js +0 -251
  289. package/integrations/integration-mapping.js +0 -43
  290. package/integrations/integration-model.js +0 -46
  291. package/integrations/integration-user.js +0 -144
  292. package/integrations/test/integration-base.test.js +0 -144
  293. package/lambda/TimeoutCatcher.test.js +0 -68
  294. package/logs/logger.test.js +0 -76
  295. package/module-plugin/auther.js +0 -393
  296. package/module-plugin/credential.js +0 -22
  297. package/module-plugin/entity-manager.js +0 -70
  298. package/module-plugin/entity.js +0 -46
  299. package/module-plugin/manager.js +0 -169
  300. package/module-plugin/module-factory.js +0 -61
  301. package/module-plugin/requester/api-key.js +0 -36
  302. package/module-plugin/requester/oauth-2.js +0 -219
  303. package/module-plugin/requester/requester.js +0 -165
  304. package/module-plugin/requester/requester.test.js +0 -28
  305. package/module-plugin/test/auther.test.js +0 -97
  306. package/syncs/model.js +0 -62
  307. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  308. /package/{module-plugin → modules}/requester/basic.js +0 -0
  309. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,240 @@
1
+ # Integration Extensions Quick Start
2
+
3
+ Tier 3 **Integration Extensions** let an API module ship reusable handler bundles — receiver routes, event handlers, queues, workers — that an integration consumes declaratively via `Definition.extensions`. See [ADR-EXTENSIONS](../../../docs/architecture/ADR-EXTENSIONS.md) for the full taxonomy.
4
+
5
+ ## When to use this vs `Definition.webhooks: true`
6
+
7
+ | Use `webhooks: true` ([WEBHOOK-QUICKSTART](./WEBHOOK-QUICKSTART.md)) | Use `extensions: {...}` |
8
+ |---|---|
9
+ | Per-account webhooks scoped to one integration record | App-level webhooks fanned out to many account records by external ID lookup |
10
+ | You'll write the receiver, signature check, and queue dispatch yourself | The API module ships the receiver, signature check, and queue dispatch already |
11
+ | One pattern, one endpoint | Multiple bundles (webhooks + CRM cards + timeline) declared together |
12
+
13
+ ## Step 1: Bind the extension on your Integration's Definition
14
+
15
+ ```javascript
16
+ const hubspot = require('@friggframework/api-module-hubspot');
17
+
18
+ class HubSpotIntegration extends IntegrationBase {
19
+ static Definition = {
20
+ name: 'hubspot',
21
+ version: '1.0.0',
22
+ modules: { hubspot: { definition: hubspot.Definition } },
23
+ extensions: {
24
+ hubspotWebhooks: {
25
+ extension: hubspot.extensions.webhooks,
26
+ handlers: { HUBSPOT_WEBHOOK: 'onHubSpotEvent' },
27
+ // optional: override the extension's declared useDatabase
28
+ // useDatabase: true,
29
+ },
30
+ },
31
+ };
32
+
33
+ async onHubSpotEvent({ data }) {
34
+ // pure business logic — signature verification, portalId lookup,
35
+ // and queue dispatch are all done by the extension's default handlers
36
+ const { subscriptionType, objectId } = data.body;
37
+ if (subscriptionType === 'contact.creation') {
38
+ await this.upsertContact(objectId);
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ The binding key (`hubspotWebhooks`) is your local name. It is also the **URL namespace** for the extension's routes (see below), so pick something readable — `hubspot` yields a cleaner URL than `hubspotWebhooks`. The extension reference (`hubspot.extensions.webhooks`) is whatever the API module exports.
45
+
46
+ ## Step 2: Deploy
47
+
48
+ Each extension binding is mounted under its **binding key**, on its own dedicated handler/Lambda function. This means two modules' extensions (e.g. a HubSpot and a Clockwork webhooks extension on the same integration) never collide — each lives at a distinct namespaced path. Boot logs show:
49
+
50
+ ```
51
+ │ Configuring routes for hubspot Integration:
52
+ │ POST /api/hubspot-integration/hubspotWebhooks/webhooks (extension: hubspot-webhooks, useDatabase: false)
53
+
54
+ ```
55
+
56
+ So the full URL is `/api/{integration-name}-integration/{bindingKey}{route.path}`. Register that URL with the upstream provider (e.g. paste it into your HubSpot app's webhook settings). Hit it and the bound method (`onHubSpotEvent`) fires on the resolved per-account integration instance.
57
+
58
+ > **⚠️ Breaking:** extension routes used to mount un-namespaced (`/api/{x}-integration/webhooks`). They are now namespaced under the binding key. Any provider webhook already registered against the old path must be re-pointed at the new `/{bindingKey}` URL — and for signature schemes that sign the full URL (e.g. HubSpot v3), the old registration will also fail verification until updated.
59
+
60
+ ## `useDatabase` — does the receiver open a DB connection?
61
+
62
+ Each extension declares whether its route handler should open a database connection:
63
+
64
+ ```javascript
65
+ // in the extension bundle (api-module side)
66
+ module.exports = {
67
+ name: 'hubspot-webhooks',
68
+ useDatabase: false, // default — the receiver is DB-free
69
+ routes: [ /* ... */ ],
70
+ events: { /* ... */ },
71
+ };
72
+ ```
73
+
74
+ - **Default is `false`** — a webhook receiver that only verifies a signature and enqueues should not pay for a DB connection (faster cold start; at build time its Lambda doesn't get the Prisma layer).
75
+ - Set `useDatabase: true` at the **extension level** if the receiver itself needs the DB. A binding may override it locally (`extensions: { x: { extension, useDatabase: true } }`), though that's rarely needed.
76
+ - Resolution order: `binding.useDatabase ?? extension.useDatabase ?? false`.
77
+ - Scope note: `false` is the default **for extension routes**. `createHandler` itself still defaults `shouldUseDatabase: true` for the integration's own catch-all handler and the legacy `Definition.webhooks: true` path — those connect as before. The `false` default applies only to the per-binding extension handler.
78
+
79
+ If `useDatabase` is `false`, the receiver must not touch the database. Work that needs the DB (e.g. resolving `portalId → integrationId`) belongs in the queue worker that processes the dispatched event, not in the receiver.
80
+
81
+ ## How handler binding works
82
+
83
+ Each binding can map extension event names to method names on your integration:
84
+
85
+ ```javascript
86
+ extensions: {
87
+ hubspotWebhooks: {
88
+ extension: hubspot.extensions.webhooks,
89
+ handlers: {
90
+ HUBSPOT_WEBHOOK: 'onHubSpotEvent', // method on `this`
91
+ },
92
+ },
93
+ },
94
+ ```
95
+
96
+ Resolution priority per event:
97
+
98
+ 1. `binding.handlers[eventName]` → resolves to `this[methodName]`, bound to the instance
99
+ 2. Extension's own default handler from `extension.events[eventName].handler`, bound to the instance
100
+ 3. Otherwise → `initialize()` throws with a message naming the integration, binding, and event
101
+
102
+ Strings (not function refs) are intentional: it dodges the `this`-in-static-Definition bootstrapping problem and centralizes binding inside the framework. The framework resolves the method against the live integration instance at startup.
103
+
104
+ ## Event-name conflicts (and binding the same extension twice)
105
+
106
+ If two bindings in your integration's `extensions` map declare the same event name, the framework **throws at `initialize()`** with a clear conflict error — no silent first/last-writer pick, no surprise routing. The fix is to use distinct event names per binding.
107
+
108
+ This means **binding the same extension twice only works if the extension itself defines disjoint event sets per use-case** (rare). For the common "two webhooks, two handlers" pattern, an API module should ship two distinct extensions instead — for example `hubspot.extensions.webhooks` and `hubspot.extensions.sandboxWebhooks`, each with its own event names.
109
+
110
+ Subclass overrides via `this.events[eventName]` (set in the constructor) take precedence over extension-declared events. If a binding tried to wire a handler that's now shadowed, the framework logs a warning naming the integration, binding, and ignored method.
111
+
112
+ **Routes do not collide across bindings** — each binding's routes are namespaced under its binding key (`/{bindingKey}{route.path}`), so two extensions can both declare `POST /webhooks` and live at distinct URLs. A route conflict only throws at boot if a *single* binding declares two routes with the same `method + path` (or a `Definition.routes` entry exactly matches an extension's namespaced path). Note this is independent of event-name conflicts above: namespacing disambiguates URLs, but two bindings still must use distinct **event** names since events are merged into one `this.events` map.
113
+
114
+ ## Authoring an extension (for API module authors)
115
+
116
+ An extension bundle is a plain object exported from your api-module:
117
+
118
+ ```javascript
119
+ // @friggframework/api-module-hubspot/extensions/webhooks/index.js
120
+ module.exports = {
121
+ name: 'hubspot-webhooks',
122
+ routes: [
123
+ { path: '/webhooks', method: 'POST', event: 'HUBSPOT_WEBHOOK_RECEIVED' },
124
+ ],
125
+ events: {
126
+ HUBSPOT_WEBHOOK_RECEIVED: {
127
+ type: 'LIFE_CYCLE_EVENT',
128
+ handler: async function ({ req, res }) {
129
+ // verify signature, look up integration by portalId, queue
130
+ await verifyHubSpotSignature(req);
131
+ for (const evt of req.body) {
132
+ // Reverse-lookup is exposed via friggCommands, the
133
+ // canonical access pattern for cross-cutting lookups.
134
+ // The HubSpot-named wrapper lives in the api-module's
135
+ // extension package.
136
+ const integrationId =
137
+ await this.commands.findIntegrationByEntityExternalId(
138
+ evt.portalId,
139
+ 'hubspot'
140
+ );
141
+ if (!integrationId) continue;
142
+ await this.queueWebhook({
143
+ integrationId,
144
+ body: evt,
145
+ event: 'HUBSPOT_WEBHOOK',
146
+ });
147
+ }
148
+ res.status(200).json({ received: req.body.length });
149
+ },
150
+ },
151
+ HUBSPOT_WEBHOOK: {
152
+ type: 'LIFE_CYCLE_EVENT',
153
+ handler: async function ({ data }) {
154
+ // default no-op; integrations override via binding.handlers
155
+ },
156
+ },
157
+ },
158
+ };
159
+ ```
160
+
161
+ Then expose it on your api-module's index:
162
+
163
+ ```javascript
164
+ // @friggframework/api-module-hubspot/index.js
165
+ module.exports = {
166
+ Definition: require('./api-module-definition'),
167
+ extensions: {
168
+ webhooks: require('./extensions/webhooks'),
169
+ crmCards: require('./extensions/crm-cards'),
170
+ timeline: require('./extensions/timeline'),
171
+ },
172
+ };
173
+ ```
174
+
175
+ ## Contract enforced by the framework
176
+
177
+ At `initialize()`, the framework validates each binding:
178
+
179
+ - `extension` must be an object with a `name`
180
+ - `extension.events` must be an object keyed by event name (if present)
181
+ - `extension.routes` must be an array (if present)
182
+ - Every route's `event` must exist in `extension.events`
183
+ - Every route's `method` must be a known HTTP verb
184
+ - For each event, either `binding.handlers[eventName]` resolves to an instance method, OR `extension.events[eventName].handler` is a function
185
+
186
+ Validation failures throw at boot with a message identifying the integration, binding, and field.
187
+
188
+ ## Reverse-lookup helpers
189
+
190
+ For app-level webhooks (HubSpot, Slack, Asana, Microsoft Teams, etc.) where one URL serves many accounts, extension default handlers need to resolve the inbound external ID (HubSpot `portalId`, Slack `team_id`, Asana `workspace_id`, Teams `tenant_id`) to a Frigg integration record. Two helpers are exposed via [`createFriggCommands`](../application/index.js) — the canonical access pattern for cross-cutting lookups:
191
+
192
+ ```javascript
193
+ // inside an integration class
194
+ this.commands = createFriggCommands({ integrationClass: MyIntegration });
195
+
196
+ // Throws on ambiguous resolution. Use when one externalId is expected
197
+ // to map to exactly one integration.
198
+ const integrationId = await this.commands.findIntegrationByEntityExternalId(
199
+ externalId,
200
+ 'hubspot' // optional moduleName
201
+ );
202
+
203
+ // Returns array. Use when one externalId may legitimately fan out to
204
+ // multiple integrations (e.g. one upstream account broadcasting to
205
+ // several Frigg integration records).
206
+ const integrationIds = await this.commands.listIntegrationsByEntityExternalId(
207
+ externalId,
208
+ 'hubspot'
209
+ );
210
+ ```
211
+
212
+ `findIntegrationByEntityExternalId` throws if:
213
+ - the (externalId, moduleName) tuple matches more than one Entity row, OR
214
+ - the matched entity is owned by more than one Integration record
215
+
216
+ A silent first-match at either layer is a cross-tenant routing risk; the command refuses to pick. The second argument (moduleName) disambiguates when multiple modules in the same app could carry colliding external IDs — pass it whenever an api-module knows its own moduleName.
217
+
218
+ ### Where platform-named wrappers belong
219
+
220
+ The commands are intentionally platform-neutral. Platform-vocabulary wrappers (`findIntegrationByPortalId`, `findIntegrationByTeamId`, `findIntegrationByWorkspaceId`, etc.) belong **inside the api-module's own extension**, not in core:
221
+
222
+ ```javascript
223
+ // inside @friggframework/api-module-hubspot/extensions/webhooks
224
+ async function findIntegrationByPortalId(integration, portalId) {
225
+ // thin wrapper — reads as self-documenting HubSpot code,
226
+ // delegates to the platform-neutral command
227
+ return integration.commands.findIntegrationByEntityExternalId(
228
+ portalId,
229
+ 'hubspot'
230
+ );
231
+ }
232
+ ```
233
+
234
+ This keeps core platform-neutral and reusable while keeping the api-module code self-documenting for the platform's developers. The same rule applies to any helper that can be named in a single platform's vocabulary.
235
+
236
+ ## See also
237
+
238
+ - [ADR-EXTENSIONS](../../../docs/architecture/ADR-EXTENSIONS.md) — the three-tier taxonomy (Core Plugins / Application Extensions / Integration Extensions)
239
+ - [WEBHOOK-QUICKSTART](./WEBHOOK-QUICKSTART.md) — per-account `Definition.webhooks: true` pattern
240
+ - `extension.js` — the validation + flattening helpers (`validateExtensionBinding`, `getExtensionRoutes`, `getExtensionWorkers`)
@@ -0,0 +1,151 @@
1
+ # Webhook Quick Start Guide
2
+
3
+ Get webhooks working in your Frigg integration in 3 simple steps.
4
+
5
+ ## Step 1: Enable Webhooks
6
+
7
+ Add `webhooks: true` to your Integration Definition:
8
+
9
+ ```javascript
10
+ class MyIntegration extends IntegrationBase {
11
+ static Definition = {
12
+ name: 'my-integration',
13
+ version: '1.0.0',
14
+ modules: {
15
+ myapi: { definition: MyApiDefinition },
16
+ },
17
+ webhooks: true, // ← Add this line
18
+ };
19
+ }
20
+ ```
21
+
22
+ ## Step 2: Handle Webhook Processing
23
+
24
+ Override the `onWebhook` handler to process webhooks:
25
+
26
+ ```javascript
27
+ class MyIntegration extends IntegrationBase {
28
+ // ... Definition ...
29
+
30
+ async onWebhook({ data }) {
31
+ const { body } = data;
32
+
33
+ // You have full access to:
34
+ // - this.myapi (your API modules)
35
+ // - this.config (integration config)
36
+ // - Database operations
37
+
38
+ if (body.event === 'item.created') {
39
+ await this.myapi.api.createItem(body.data);
40
+ }
41
+
42
+ return { processed: true };
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Step 3: Deploy
48
+
49
+ Deploy your Frigg app - webhook routes are automatically created:
50
+
51
+ ```bash
52
+ POST /api/my-integration-integration/webhooks/:integrationId
53
+ ```
54
+
55
+ ## That's It!
56
+
57
+ The default behavior handles:
58
+ - ✅ Receiving webhooks (instant 200 OK response)
59
+ - ✅ Queuing to SQS
60
+ - ✅ Loading your integration with DB and API modules
61
+ - ✅ Calling your `onWebhook` handler
62
+
63
+ ## Optional: Custom Signature Verification
64
+
65
+ Override `onWebhookReceived` for custom signature checks:
66
+
67
+ ```javascript
68
+ async onWebhookReceived({ req, res }) {
69
+ // Verify signature
70
+ const signature = req.headers['x-webhook-signature'];
71
+ if (!this.verifySignature(req.body, signature)) {
72
+ return res.status(401).json({ error: 'Invalid signature' });
73
+ }
74
+
75
+ // Queue for processing (default behavior)
76
+ await this.queueWebhook({
77
+ integrationId: req.params.integrationId,
78
+ body: req.body,
79
+ });
80
+
81
+ res.status(200).json({ received: true });
82
+ }
83
+ ```
84
+
85
+ ## Two Webhook Routes
86
+
87
+ ### With Integration ID (Recommended)
88
+ ```
89
+ POST /api/{name}-integration/webhooks/:integrationId
90
+ ```
91
+ - Full integration loaded in worker
92
+ - Access to DB, config, and API modules
93
+ - Use `this.myapi`, `this.config`, etc.
94
+
95
+ ### Without Integration ID
96
+ ```
97
+ POST /api/{name}-integration/webhooks
98
+ ```
99
+ - Unhydrated integration
100
+ - Useful for system-wide events
101
+ - Limited context
102
+
103
+ ## Need Help?
104
+
105
+ See full documentation: `packages/core/handlers/WEBHOOKS.md`
106
+
107
+ ## Common Patterns
108
+
109
+ ### Slack
110
+ ```javascript
111
+ async onWebhookReceived({ req, res }) {
112
+ if (req.body.type === 'url_verification') {
113
+ return res.json({ challenge: req.body.challenge });
114
+ }
115
+ // ... verify signature, queue ...
116
+ }
117
+ ```
118
+
119
+ ### Stripe
120
+ ```javascript
121
+ async onWebhookReceived({ req, res }) {
122
+ const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
123
+ const event = stripe.webhooks.constructEvent(
124
+ JSON.stringify(req.body),
125
+ req.headers['stripe-signature'],
126
+ process.env.STRIPE_WEBHOOK_SECRET
127
+ );
128
+ await this.queueWebhook({ body: event });
129
+ res.status(200).json({ received: true });
130
+ }
131
+ ```
132
+
133
+ ### GitHub
134
+ ```javascript
135
+ async onWebhookReceived({ req, res }) {
136
+ const crypto = require('crypto');
137
+ const signature = req.headers['x-hub-signature-256'];
138
+ const hash = crypto
139
+ .createHmac('sha256', process.env.GITHUB_WEBHOOK_SECRET)
140
+ .update(JSON.stringify(req.body))
141
+ .digest('hex');
142
+
143
+ if (`sha256=${hash}` !== signature) {
144
+ return res.status(401).json({ error: 'Invalid signature' });
145
+ }
146
+
147
+ await this.queueWebhook({ integrationId: req.params.integrationId, body: req.body });
148
+ res.status(200).json({ received: true });
149
+ }
150
+ ```
151
+
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Tier 3 — Integration Extensions
3
+ *
4
+ * An Integration Extension is a reusable bundle exported by an API module (or a
5
+ * shared extensions library) that contributes routes, events, queues, and workers
6
+ * to a consumer integration. The integration binds the bundle declaratively via
7
+ * `static Definition.extensions` and the framework merges its contributions into
8
+ * the integration's effective definition at instantiation time.
9
+ *
10
+ * Extension bundle shape:
11
+ *
12
+ * {
13
+ * name: string, // required, unique within the API module
14
+ * routes?: Array<{ // optional, mounted alongside Definition.routes
15
+ * path: string,
16
+ * method: 'GET' | 'POST' | 'PUT' | 'DELETE' | ...,
17
+ * event: string // must exist in `events` below
18
+ * }>,
19
+ * events?: { // optional, merged into instance.events
20
+ * [eventName]: {
21
+ * type?: string, // e.g. 'LIFE_CYCLE_EVENT'
22
+ * handler: Function // default handler; integration may override per-binding
23
+ * }
24
+ * },
25
+ * queues?: Array<Object>, // reserved — Phase 2
26
+ * workers?: Array<Object> // reserved — Phase 2
27
+ * }
28
+ *
29
+ * Integration binding shape (on the integration's static Definition.extensions):
30
+ *
31
+ * extensions: {
32
+ * hubspotWebhooks: { // local binding name (developer's choice)
33
+ * extension: hubspot.extensions.webhooks,
34
+ * handlers: { // optional override map
35
+ * HUBSPOT_WEBHOOK: 'onHubSpotEvent' // event → method name on the integration
36
+ * }
37
+ * }
38
+ * }
39
+ *
40
+ * The same extension may be bound multiple times under different local names; the
41
+ * binding key is the developer-controlled local handle, not a global registry key.
42
+ */
43
+
44
+ const KNOWN_HTTP_METHODS = new Set([
45
+ 'get',
46
+ 'post',
47
+ 'put',
48
+ 'patch',
49
+ 'delete',
50
+ 'options',
51
+ 'head',
52
+ ]);
53
+
54
+ /**
55
+ * Validate the shape of an extension bundle and its binding.
56
+ *
57
+ * @param {Object} extension - The extension bundle to validate.
58
+ * @param {string} bindingName - The local binding key (for error context).
59
+ * @param {string} integrationName - The integration's Definition.name (for error context).
60
+ * @param {Object} [binding] - Optional full binding object; if provided, also validates binding.handlers.
61
+ * @throws {Error} If the extension is missing required fields or is internally inconsistent.
62
+ */
63
+ function validateExtensionBinding(extension, bindingName, integrationName, binding) {
64
+ const ctx = `Integration "${integrationName}" extension binding "${bindingName}"`;
65
+
66
+ if (!extension || typeof extension !== 'object') {
67
+ throw new Error(`${ctx}: extension must be an object`);
68
+ }
69
+ if (!extension.name || typeof extension.name !== 'string') {
70
+ throw new Error(`${ctx}: extension is missing required "name" field`);
71
+ }
72
+
73
+ if (
74
+ extension.useDatabase !== undefined &&
75
+ typeof extension.useDatabase !== 'boolean'
76
+ ) {
77
+ throw new Error(
78
+ `${ctx}: extension "${extension.name}" "useDatabase" must be a boolean`
79
+ );
80
+ }
81
+ if (
82
+ binding &&
83
+ binding.useDatabase !== undefined &&
84
+ typeof binding.useDatabase !== 'boolean'
85
+ ) {
86
+ throw new Error(
87
+ `${ctx}: binding "useDatabase" must be a boolean`
88
+ );
89
+ }
90
+
91
+ const events = extension.events || {};
92
+ if (typeof events !== 'object' || Array.isArray(events)) {
93
+ throw new Error(
94
+ `${ctx}: extension "${extension.name}" "events" must be an object keyed by event name`
95
+ );
96
+ }
97
+
98
+ // Validate each event's shape — handler, when present, must be a function.
99
+ for (const [eventName, eventDef] of Object.entries(events)) {
100
+ if (!eventDef || typeof eventDef !== 'object') {
101
+ throw new Error(
102
+ `${ctx}: extension "${extension.name}" event "${eventName}" must be an object`
103
+ );
104
+ }
105
+ if (
106
+ eventDef.handler !== undefined &&
107
+ typeof eventDef.handler !== 'function'
108
+ ) {
109
+ throw new Error(
110
+ `${ctx}: extension "${extension.name}" event "${eventName}" "handler" must be a function`
111
+ );
112
+ }
113
+ }
114
+
115
+ const routes = extension.routes || [];
116
+ if (!Array.isArray(routes)) {
117
+ throw new Error(
118
+ `${ctx}: extension "${extension.name}" "routes" must be an array`
119
+ );
120
+ }
121
+
122
+ for (const route of routes) {
123
+ if (!route || typeof route !== 'object') {
124
+ throw new Error(
125
+ `${ctx}: extension "${extension.name}" has a malformed route entry`
126
+ );
127
+ }
128
+ if (typeof route.path !== 'string' || route.path.length === 0) {
129
+ throw new Error(
130
+ `${ctx}: extension "${extension.name}" route is missing "path"`
131
+ );
132
+ }
133
+ if (typeof route.method !== 'string') {
134
+ throw new Error(
135
+ `${ctx}: extension "${extension.name}" route "${route.path}" is missing "method"`
136
+ );
137
+ }
138
+ if (!KNOWN_HTTP_METHODS.has(route.method.toLowerCase())) {
139
+ throw new Error(
140
+ `${ctx}: extension "${extension.name}" route "${route.path}" has unsupported method "${route.method}"`
141
+ );
142
+ }
143
+ if (typeof route.event !== 'string' || route.event.length === 0) {
144
+ throw new Error(
145
+ `${ctx}: extension "${extension.name}" route "${route.path}" is missing "event"`
146
+ );
147
+ }
148
+ if (!Object.prototype.hasOwnProperty.call(events, route.event)) {
149
+ throw new Error(
150
+ `${ctx}: extension "${extension.name}" route "${route.path}" references event "${route.event}" which is not declared in extension.events`
151
+ );
152
+ }
153
+ }
154
+
155
+ // Validate binding.handlers if a binding was supplied.
156
+ if (binding && binding.handlers !== undefined) {
157
+ if (
158
+ typeof binding.handlers !== 'object' ||
159
+ Array.isArray(binding.handlers) ||
160
+ binding.handlers === null
161
+ ) {
162
+ throw new Error(
163
+ `${ctx}: "handlers" must be an object keyed by event name`
164
+ );
165
+ }
166
+ for (const [eventName, methodRef] of Object.entries(binding.handlers)) {
167
+ if (typeof methodRef !== 'string' || methodRef.length === 0) {
168
+ throw new Error(
169
+ `${ctx}: handler for event "${eventName}" must be a non-empty method name string (got ${typeof methodRef})`
170
+ );
171
+ }
172
+ if (!Object.prototype.hasOwnProperty.call(events, eventName)) {
173
+ throw new Error(
174
+ `${ctx}: binding.handlers references event "${eventName}" which is not declared in extension "${extension.name}".events — check for typos`
175
+ );
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Get the flattened list of extension-contributed routes for an integration class.
183
+ * Each route carries the binding name and extension name alongside the route fields
184
+ * so the router builder can produce useful boot-time logs.
185
+ *
186
+ * @param {Function} IntegrationClass - A class extending IntegrationBase.
187
+ * @returns {Array<{bindingName: string, extensionName: string, path: string, method: string, event: string}>}
188
+ */
189
+ function getExtensionRoutes(IntegrationClass) {
190
+ const extensions = IntegrationClass?.Definition?.extensions || {};
191
+ const integrationName = IntegrationClass?.Definition?.name;
192
+ const flat = [];
193
+ for (const [bindingName, binding] of Object.entries(extensions)) {
194
+ // Fail fast: surface bad bindings at boot, not at first request.
195
+ // Mirrors the validation that _mergeExtensions does at instance time.
196
+ validateExtensionBinding(
197
+ binding && binding.extension,
198
+ bindingName,
199
+ integrationName,
200
+ binding
201
+ );
202
+ const useDatabase =
203
+ binding.useDatabase ?? binding.extension.useDatabase ?? false;
204
+ const routes = binding.extension.routes || [];
205
+ for (const route of routes) {
206
+ flat.push({
207
+ bindingName,
208
+ extensionName: binding.extension.name,
209
+ path: route.path,
210
+ method: route.method,
211
+ event: route.event,
212
+ useDatabase,
213
+ });
214
+ }
215
+ }
216
+ return flat;
217
+ }
218
+
219
+ /**
220
+ * Get the flattened list of extension-contributed workers for an integration class.
221
+ * Reserved for Phase 2 — today the per-integration QueueWorker handles all events
222
+ * by name, so extension-contributed events flow through it without a dedicated worker.
223
+ *
224
+ * @param {Function} IntegrationClass - A class extending IntegrationBase.
225
+ * @returns {Array<Object>}
226
+ */
227
+ function getExtensionWorkers(IntegrationClass) {
228
+ const extensions = IntegrationClass?.Definition?.extensions || {};
229
+ const integrationName = IntegrationClass?.Definition?.name;
230
+ const flat = [];
231
+ for (const [bindingName, binding] of Object.entries(extensions)) {
232
+ validateExtensionBinding(
233
+ binding && binding.extension,
234
+ bindingName,
235
+ integrationName,
236
+ binding
237
+ );
238
+ const workers = binding.extension.workers || [];
239
+ for (const worker of workers) {
240
+ flat.push({
241
+ bindingName,
242
+ extensionName: binding.extension.name,
243
+ ...worker,
244
+ });
245
+ }
246
+ }
247
+ return flat;
248
+ }
249
+
250
+ module.exports = {
251
+ validateExtensionBinding,
252
+ getExtensionRoutes,
253
+ getExtensionWorkers,
254
+ };
@@ -1,19 +1,29 @@
1
1
  const { IntegrationBase } = require('./integration-base');
2
- const { IntegrationModel } = require('./integration-model');
3
2
  const { Options } = require('./options');
4
- const { IntegrationMapping } = require('./integration-mapping');
5
- const { IntegrationFactory, IntegrationHelper } = require('./integration-factory');
6
- const { createIntegrationRouter, checkRequiredParams } = require('./integration-router');
7
- const { createFriggBackend } = require('./create-frigg-backend');
3
+ const {
4
+ createIntegrationRouter,
5
+ checkRequiredParams,
6
+ } = require('./integration-router');
7
+ const {
8
+ getModulesDefinitionFromIntegrationClasses,
9
+ } = require('./utils/map-integration-dto');
10
+ const {
11
+ LoadIntegrationContextUseCase,
12
+ } = require('./use-cases/load-integration-context');
13
+ const {
14
+ validateExtensionBinding,
15
+ getExtensionRoutes,
16
+ getExtensionWorkers,
17
+ } = require('./extension');
8
18
 
9
19
  module.exports = {
10
20
  IntegrationBase,
11
- IntegrationModel,
12
21
  Options,
13
- IntegrationMapping,
14
- IntegrationFactory,
15
- IntegrationHelper,
16
22
  createIntegrationRouter,
17
23
  checkRequiredParams,
18
- createFriggBackend
24
+ getModulesDefinitionFromIntegrationClasses,
25
+ LoadIntegrationContextUseCase,
26
+ validateExtensionBinding,
27
+ getExtensionRoutes,
28
+ getExtensionWorkers,
19
29
  };